1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
pub mod format;
pub mod interpret;
pub mod raw;
// TODO: integrate the server feature with `DistributedRepository`
pub mod server;

use eyre::eyre;
use format::*;
use futures::prelude::*;
use interpret::*;
use log::info;
use raw::{RawCommit, RawRepository};
use serde::{Deserialize, Serialize};
use simperby_core::reserved::ReservedState;
use simperby_core::utils::get_timestamp;
use simperby_core::verify::CommitSequenceVerifier;
use simperby_core::*;
use std::sync::Arc;
use std::{collections::HashSet, fmt};
use tokio::io::{AsyncBufReadExt, AsyncWriteExt};
use tokio::sync::RwLock;

pub type Branch = String;
pub type Tag = String;

pub const FINALIZED_BRANCH_NAME: &str = "finalized";
pub const FP_BRANCH_NAME: &str = "fp";
pub const COMMIT_TITLE_HASH_DIGITS: usize = 8;
pub const TAG_NAME_HASH_DIGITS: usize = 8;
pub const BRANCH_NAME_HASH_DIGITS: usize = 8;
pub const UNKNOWN_COMMIT_AUTHOR: &str = "unknown";

pub type Error = eyre::Error;

#[derive(thiserror::Error, Debug)]
#[error("repository integrity broken: {msg}")]
pub struct IntegrityError {
    pub msg: String,
}

impl IntegrityError {
    pub fn new(msg: String) -> Self {
        Self { msg }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
    /// The distance that if a commit is past this far,
    /// any forked branch starting from the commit
    /// will be considered a long range attack and thus ignored.
    ///
    /// If zero, fork can be detected only from the currently last-finalized commit.
    pub long_range_attack_distance: usize,
}

/// The local Simperby blockchain data repository.
///
/// It automatically locks the repository once created.
///
/// - It **verifies** all the incoming changes and applies them to the local repository
/// only if they are valid.
pub struct DistributedRepository {
    /// We keep the `RawRepository` in a `RwLock` for possible concurrent accesses in some operations.
    raw: Arc<RwLock<RawRepository>>,
    _config: Config,
    private_key: Option<PrivateKey>,
}

impl DistributedRepository {
    pub fn get_raw(&self) -> Arc<RwLock<RawRepository>> {
        Arc::clone(&self.raw)
    }

    pub async fn new(
        raw: Arc<RwLock<RawRepository>>,
        config: Config,
        private_key: Option<PrivateKey>,
    ) -> Result<Self, Error> {
        Ok(Self {
            raw,
            _config: config,
            private_key,
        })
    }

    /// Initializes the genesis repository, leaving a genesis header.
    ///
    /// It also
    /// - creates `fp` branch and its commit (for the genesis block).
    /// - creates the `finalized` branch.
    ///
    /// Note that `genesis` can be called on any commit except a merge commit.
    pub async fn genesis(mut raw: RawRepository) -> Result<(), Error> {
        genesis(&mut raw).await
    }

    // ---------------
    // Read-only operations
    // ---------------

    /// Reads the last finalization information from the repository.
    pub async fn read_last_finalization_info(&self) -> Result<FinalizationInfo, Error> {
        read_last_finalization_info(&*self.raw.read().await).await
    }

    /// Reads the finalization information at specific height.
    pub async fn read_finalization_info(
        &self,
        _height: BlockHeight,
    ) -> Result<FinalizationInfo, Error> {
        todo!()
    }

    /// Reads the given commit.
    pub async fn read_commit(&self, commit_hash: CommitHash) -> Result<Commit, Error> {
        read_commit(&*self.raw.read().await, commit_hash).await
    }

    /// Returns the currently valid and height-acceptable agendas in the repository.
    pub async fn read_agendas(&self) -> Result<Vec<(CommitHash, Hash256)>, Error> {
        read_agendas(&*self.raw.read().await).await
    }

    /// Returns governance-approved agendas in the repository.
    /// The result will be a list of agenda proofs, not just agendas.
    pub async fn read_governance_approved_agendas(
        &self,
    ) -> Result<Vec<(CommitHash, Hash256)>, Error> {
        read_governance_approved_agendas(&*self.raw.read().await).await
    }

    /// Returns the currently valid and height-acceptable blocks in the repository.
    pub async fn read_blocks(&self) -> Result<Vec<(CommitHash, Hash256)>, Error> {
        read_blocks(&*self.raw.read().await).await
    }

    /// Checks the validity of the repository, starting from the given height.
    ///
    /// It checks
    /// 1. all the reserved branches and tags
    /// 2. the finalization proof in the `fp` branch.
    /// 3. the existence of merge commits
    /// 4. the canonical history of the `finalized` branch.
    /// 5. the reserved state in a valid format.
    pub async fn check(&self, _starting_height: BlockHeight) -> Result<bool, Error> {
        // TODO
        Ok(true)
    }

    /// Checks the existence of `.gitignore` file and `.simperby/` entry in `.gitignore`.
    /// This returns true if both exist.
    pub async fn check_gitignore(&self) -> Result<bool, Error> {
        check_gitignore(&*self.raw.read().await).await
    }

    // ---------------
    // Operations that interact with possible local works
    // (manually added commits or remote tracking branches)
    // ---------------

    /// Synchronizes the repository with the given commit (interpreted as a branch tip).
    /// - Returns `Ok(Ok(()))` if the branch is successfully received.
    /// - Returns `Ok(Err(_))` if the branch is invalid and thus rejected, with a reason.
    /// - Returns `Err(_)` if an error occurs.
    ///
    /// 1. Finalization: move the `finalized` and `fp` branch to the last finalized block commit.
    /// 2. Block observed: add a `b-#` branch on the block candidate.
    /// 3. Agenda observed (either governance-approved or not): add an `a-#` branch on the agenda candidate.
    ///
    /// This will verify every commit along the way.
    /// If the given commit is not a descendant of the
    /// current `finalized` (i.e., cannot be fast-forwarded), it fails.
    pub async fn sync(&mut self, commit_hash: CommitHash) -> Result<Result<(), String>, Error> {
        sync(&mut *self.raw.write().await, commit_hash).await
    }

    /// Performs `sync()` on all local branches and remote tracking branches on the repository.
    ///
    /// Returns the list of `(branch name, result of sync())`.
    pub async fn sync_all(&mut self) -> Result<Vec<(String, Result<(), String>)>, Error> {
        sync_all(&mut *self.raw.write().await).await
    }

    /// Tests if the given push request is acceptable.
    pub async fn test_push_eligibility(
        &self,
        commit_hash: CommitHash,
        branch_name: String,
        timestamp: Timestamp,
        signature: TypedSignature<(CommitHash, String, Timestamp)>,
        _timestamp_to_test: Timestamp,
    ) -> Result<bool, Error> {
        test_push_eligibility(
            &*self.raw.read().await,
            commit_hash,
            branch_name,
            timestamp,
            signature,
            _timestamp_to_test,
        )
        .await
    }

    /// Cleans all the outdated commits, remote repositories and branches.
    ///
    /// It will leave only
    /// - the `finalized` branch
    /// - the `fp` branch
    /// when `hard` is `true`,
    ///
    /// and when `hard` is `false`,
    /// - the `p` branch
    /// - the `a-#` branches
    /// - the `b-#` branches
    /// will be left as well
    /// if only the branches have valid commit sequences
    /// and are not outdated (branched from the last finalized commit).
    pub async fn clean(&mut self, hard: bool) -> Result<(), Error> {
        clean(&mut *self.raw.write().await, hard).await
    }

    /// Broadcasts all the local messages.
    pub async fn broadcast(&mut self) -> Result<(), Error> {
        broadcast(&mut *self.raw.write().await, self.private_key.clone()).await
    }

    // ---------------
    // DMS-related operations
    // ---------------

    pub async fn flush(&mut self) -> Result<(), Error> {
        todo!()
    }

    pub async fn update(&mut self) -> Result<(), Error> {
        todo!()
    }

    // ---------------
    // Various operations that (might) create a commit
    // ---------------

    /// Informs that the given agenda has been approved.
    ///
    /// After verification, it will create an agenda-proof commit,
    /// and update the corresponding `a-#` branch to it
    ///
    /// TODO: get `AgendaProof` instead.
    pub async fn approve(
        &mut self,
        agenda_hash: &Hash256,
        proof: Vec<TypedSignature<Agenda>>,
        timestamp: Timestamp,
    ) -> Result<CommitHash, Error> {
        approve(&mut *self.raw.write().await, agenda_hash, proof, timestamp).await
    }

    /// Creates an agenda commit on top of the HEAD.
    pub async fn create_agenda(
        &mut self,
        author: MemberName,
    ) -> Result<(Agenda, CommitHash), Error> {
        create_agenda(&mut *self.raw.write().await, author).await
    }

    /// Creates a block commit on top of the HEAD.
    pub async fn create_block(
        &mut self,
        author: PublicKey,
    ) -> Result<(BlockHeader, CommitHash), Error> {
        create_block(&mut *self.raw.write().await, author).await
    }

    /// Creates an extra-agenda transaction commit on top of the HEAD.
    pub async fn create_extra_agenda_transaction(
        &mut self,
        transaction: &ExtraAgendaTransaction,
    ) -> Result<CommitHash, Error> {
        create_extra_agenda_transaction(&mut *self.raw.write().await, transaction).await
    }

    /// Finalizes the block with the given proof. Returns the commit hash of the updated `fp` branch.
    pub async fn finalize(
        &mut self,
        block_commit_hash: CommitHash,
        proof: FinalizationProof,
    ) -> Result<CommitHash, Error> {
        finalize(&mut *self.raw.write().await, block_commit_hash, proof).await
    }

    /// Creates a commit that adds `.simperby/` entry to `.gitignore`.
    /// It fails if it exists normally.
    pub async fn commit_gitignore(&mut self) -> Result<(), Error> {
        commit_gitignore(&mut *self.raw.write().await).await
    }

    // ---------------
    // Tag-related operations
    // ---------------

    /// Puts a 'vote' tag on the commit.
    pub async fn vote(&mut self, commit_hash: CommitHash) -> Result<(), Error> {
        vote(&mut *self.raw.write().await, commit_hash).await
    }

    /// Puts a 'veto' tag on the commit.
    pub async fn veto(&mut self, commit_hash: CommitHash) -> Result<(), Error> {
        veto(&mut *self.raw.write().await, commit_hash).await
    }
}