simperby_repository/interpret/
works.rs1use super::*;
2use read::*;
3
4pub(crate) async fn advance_finalized_branch(
5 raw: &mut RawRepository,
6 to_be_finalized_block_commit_hash: CommitHash,
7 finalization_proof: LastFinalizationProof,
8) -> Result<(), Error> {
9 raw.checkout_clean().await?;
10 raw.move_branch(
11 FINALIZED_BRANCH_NAME.into(),
12 to_be_finalized_block_commit_hash,
13 )
14 .await?;
15 raw.move_branch(FP_BRANCH_NAME.into(), to_be_finalized_block_commit_hash)
16 .await?;
17 raw.checkout(FP_BRANCH_NAME.into()).await?;
18 raw.create_semantic_commit(format::fp_to_semantic_commit(&finalization_proof), true)
19 .await?;
20 raw.checkout_detach(to_be_finalized_block_commit_hash)
21 .await?;
22 Ok(())
23}
24
25pub async fn sync(
26 raw: &mut RawRepository,
27 tip_commit_hash: CommitHash,
28) -> Result<Result<(), String>, Error> {
29 let lfi = read_last_finalization_info(raw).await?;
30 let mut csv = CommitSequenceVerifier::new(lfi.header.clone(), lfi.reserved_state.clone())
31 .map_err(|e| {
32 IntegrityError::new(format!("finalized branch is not accepted by CSV: {e}"))
33 })?;
34
35 if raw
36 .find_merge_base(lfi.commit_hash, tip_commit_hash)
37 .await?
38 != lfi.commit_hash
39 {
40 return Ok(Err(
41 "the received branch tip commit is not a descendant of the last finalized block."
42 .to_owned(),
43 ));
44 }
45
46 if let Ok(last_finalization_proof) =
48 format::fp_from_semantic_commit(raw.read_semantic_commit(tip_commit_hash).await?)
49 {
50 let commit_hash = raw.list_ancestors(tip_commit_hash, Some(1)).await?[0];
53 if commit_hash == lfi.commit_hash {
54 return Ok(Err("the received commit is already finalized.".to_owned()));
55 }
56
57 let commits = match read_commits(raw, lfi.commit_hash, commit_hash).await {
59 Ok(x) => x,
60 Err(CommitError::Commit(error, commit)) => {
61 return Ok(Err(format!("failed to parse commit {commit}: {error}")));
62 }
63 Err(e) => return Err(e.into()),
64 };
65 for (commit, commit_hash) in &commits {
66 if let Err(e) = csv.apply_commit(commit) {
67 return Ok(Err(format!(
68 "commit sequence verification failed: {e} at {commit_hash}",
69 )));
70 }
71 }
72
73 let (last_commit, last_commit_hash) = commits.last().expect(
74 "already checked that the received commit is not same as the last finalized block",
75 );
76 if let Commit::Block(_) = last_commit {
77 if csv
78 .verify_last_header_finalization(&last_finalization_proof.proof)
79 .is_err()
80 {
81 return Ok(Err(
82 "finalization proof is invalid for the last block.".to_owned()
83 ));
84 }
85 advance_finalized_branch(raw, *last_commit_hash, last_finalization_proof).await?;
86 } else {
87 return Ok(Err("fp commit must be on top of a block commit.".to_owned()));
88 }
89 }
90 else {
92 if tip_commit_hash == lfi.commit_hash {
93 return Ok(Err("the received commit is already finalized.".to_owned()));
94 }
95
96 let commits = match read_commits(raw, lfi.commit_hash, tip_commit_hash).await {
98 Ok(x) => x,
99 Err(CommitError::Commit(error, commit)) => {
100 return Ok(Err(format!("failed to parse commit {commit}: {error}",)));
101 }
102 Err(e) => return Err(e.into()),
103 };
104 for (commit, commit_hash) in &commits {
105 if let Err(e) = csv.apply_commit(commit) {
106 return Ok(Err(format!(
107 "commit sequence verification failed: {e} at {commit_hash}",
108 )));
109 }
110 }
111
112 let headers = csv.get_block_headers();
114 if headers.len() > 2 {
115 let (last_header, _) = headers.last().expect(
116 "already checked that the received commit is not same as the last finalized block",
117 );
118 let (second_to_last_header, index) = headers[headers.len() - 2].clone();
119 advance_finalized_branch(
120 raw,
121 commits[index].1,
122 LastFinalizationProof {
123 height: second_to_last_header.height,
124 proof: last_header.prev_block_finalization_proof.clone(),
125 },
126 )
127 .await?;
128 }
129
130 let branch_name = match &commits
132 .last()
133 .expect("already checked that the received commit is not a finalization proof commit")
134 .0
135 {
136 Commit::Agenda(agenda) => {
137 let approved_agendas = read::read_governance_approved_agendas(raw).await?;
138 for (commit_hash, _) in approved_agendas {
139 if let Commit::AgendaProof(agenda_proof) =
140 read::read_commit(raw, commit_hash).await?
141 {
142 if agenda_proof.agenda_hash == agenda.to_hash256() {
143 return Ok(Err("agenda proof already exists.".to_owned()));
144 }
145 } else {
146 return Err(eyre!(IntegrityError::new(format!(
147 "commit {} is not an agenda proof",
148 commit_hash
149 ))));
150 }
151 }
152
153 format!(
154 "a-{}",
155 &agenda.to_hash256().to_string()[0..BRANCH_NAME_HASH_DIGITS]
156 )
157 }
158 Commit::AgendaProof(agenda_proof) => {
159 let agenda_name = match &commits[commits.len() - 2].0 {
160 Commit::Agenda(agenda) => {
161 format!(
162 "a-{}",
163 &agenda.to_hash256().to_string()[0..BRANCH_NAME_HASH_DIGITS]
164 )
165 }
166 _ => {
167 return Err(eyre!(IntegrityError::new(
168 "agenda proof's parent is not an agenda".to_string()
169 )))
170 }
171 };
172 if raw.locate_branch(agenda_name.clone()).await.is_ok() {
173 raw.delete_branch(agenda_name).await?;
174 }
175
176 format!(
177 "a-{}",
178 &agenda_proof.to_hash256().to_string()[0..BRANCH_NAME_HASH_DIGITS]
179 )
180 }
181 Commit::Block(block) => {
182 format!(
183 "b-{}",
184 &block.to_hash256().to_string()[0..BRANCH_NAME_HASH_DIGITS]
185 )
186 }
187 x => return Ok(Err(format!("commit sequence ends with: {x:?}"))),
188 };
189 if raw.locate_branch(branch_name.clone()).await.is_ok() {
190 return Ok(Err(format!("branch already exists: {branch_name}",)));
191 }
192 raw.create_branch(branch_name, tip_commit_hash).await?;
193 };
194 Ok(Ok(()))
195}
196
197pub async fn sync_all(raw: &mut RawRepository) -> Result<Vec<(String, Result<(), String>)>, Error> {
198 let local_branches: Vec<String> = raw
199 .list_branches()
200 .await?
201 .into_iter()
202 .filter(|s| {
203 s.as_str() != FINALIZED_BRANCH_NAME
204 && s.as_str() != FP_BRANCH_NAME
205 && !s.starts_with("a-")
206 && !s.starts_with("b-")
207 && s.as_str() != "p"
208 })
209 .collect();
210 let remote_tracking_branches = raw.list_remote_tracking_branches().await?;
211
212 let mut result = Vec::new();
213 for branch in local_branches {
214 result.push((
215 branch.to_owned(),
216 sync(raw, raw.locate_branch(branch.to_owned()).await?).await?,
217 ));
218 }
219 for (remote, branch, commit_hash) in remote_tracking_branches {
220 result.push((format!("{remote}/{branch}"), sync(raw, commit_hash).await?));
221 }
222 Ok(result)
223}
224
225pub async fn clean(raw: &mut RawRepository, hard: bool) -> Result<(), Error> {
226 let finalized_branch_commit_hash = raw
227 .locate_branch(FINALIZED_BRANCH_NAME.into())
228 .await
229 .map_err(|e| match e {
230 raw::Error::NotFound(_) => {
231 eyre!(IntegrityError::new(
232 "cannot locate `finalized` branch".to_string()
233 ))
234 }
235 _ => eyre!(e),
236 })?;
237 let branches = read_local_branches(raw).await?;
238 let last_header = read_last_finalized_block_header(raw).await?;
239 for (branch, branch_commit_hash) in branches {
240 if !(branch.as_str() == FINALIZED_BRANCH_NAME || branch.as_str() == FP_BRANCH_NAME) {
241 if hard {
242 raw.delete_branch(branch.to_string()).await?;
243 } else {
244 let find_merge_base_result = raw
246 .find_merge_base(branch_commit_hash, finalized_branch_commit_hash)
247 .await
248 .map_err(|e| match e {
249 raw::Error::NotFound(_) => {
250 eyre!(IntegrityError::new(format!(
251 "cannot find merge base for branch {branch} and finalized branch"
252 )))
253 }
254 _ => eyre!(e),
255 })?;
256
257 if finalized_branch_commit_hash != find_merge_base_result {
258 raw.delete_branch(branch.to_string()).await?;
259 }
260
261 raw.checkout(branch.to_string()).await?;
263 let reserved_state = raw.read_reserved_state().await?;
264 let commits =
265 read_commits(raw, finalized_branch_commit_hash, branch_commit_hash).await?;
266 let mut verifier =
267 CommitSequenceVerifier::new(last_header.clone(), reserved_state.clone())
268 .map_err(|e| eyre!("failed to create a commit sequence verifier: {}", e))?;
269 for (commit, _) in commits.iter() {
270 if verifier.apply_commit(commit).is_err() {
271 raw.delete_branch(branch.to_string()).await?;
272 }
273 }
274 }
275 }
276 }
277
278 let remote_list = raw.list_remotes().await?;
281 for (remote_name, _) in remote_list {
282 raw.remove_remote(remote_name).await?;
283 }
284
285 Ok(())
286}