Split repository into two creates
* `radicle`: core library * `radicle-node`: node Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
parent
d1ccf1b807
commit
46501507be
|
|
@ -13,9 +13,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.64"
|
||||
version = "1.0.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9a8f622bcf6ff3df478e9deba3e03e4e04b300f8e6a139e192c05fa3490afc7"
|
||||
checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
|
|
@ -114,12 +114,6 @@ dependencies = [
|
|||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bs58"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.17"
|
||||
|
|
@ -277,9 +271,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.3"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
|
||||
checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
|
||||
dependencies = [
|
||||
"block-buffer 0.10.3",
|
||||
"crypto-common",
|
||||
|
|
@ -496,9 +490,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.132"
|
||||
version = "0.2.133"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
|
||||
checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
|
|
@ -646,9 +640,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.14.0"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
|
||||
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
|
|
@ -764,6 +758,31 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radicle"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"ed25519-compact",
|
||||
"fastrand",
|
||||
"git-ref-format",
|
||||
"git-url",
|
||||
"git2",
|
||||
"log",
|
||||
"multibase",
|
||||
"nonempty",
|
||||
"olpc-cjson",
|
||||
"once_cell",
|
||||
"quickcheck",
|
||||
"quickcheck_macros",
|
||||
"radicle-git-ext",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.6",
|
||||
"siphasher",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radicle-git-ext"
|
||||
version = "0.1.0"
|
||||
|
|
@ -784,30 +803,20 @@ version = "0.2.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"bloomy",
|
||||
"bs58",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"colored",
|
||||
"crossbeam-channel",
|
||||
"ed25519-compact",
|
||||
"fastrand",
|
||||
"git-ref-format",
|
||||
"git-url",
|
||||
"git2",
|
||||
"log",
|
||||
"multibase",
|
||||
"nakamoto-net",
|
||||
"nakamoto-net-poll",
|
||||
"nonempty",
|
||||
"olpc-cjson",
|
||||
"once_cell",
|
||||
"quickcheck",
|
||||
"quickcheck_macros",
|
||||
"radicle-git-ext",
|
||||
"radicle",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.5",
|
||||
"siphasher",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
]
|
||||
|
|
@ -828,9 +837,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.3"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
|
@ -919,13 +928,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.5"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf9db03534dff993187064c4e0c05a5708d2a9728ace9a8959b77bedf415dac5"
|
||||
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest 0.10.3",
|
||||
"digest 0.10.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -958,9 +967,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.99"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
|
||||
checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -983,18 +992,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.34"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c1b05ca9d106ba7d2e31a9dab4a64e7be2cce415321966ea3132c49a656e252"
|
||||
checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.34"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8f2591983642de85c921015f3f070c665a197ed69e417af436115e3a1407487"
|
||||
checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -1041,15 +1050,15 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
|||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
||||
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.21"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6"
|
||||
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
[workspace]
|
||||
members = ["radicle-node"]
|
||||
members = ["radicle", "radicle-node"]
|
||||
|
||||
[patch.crates-io.radicle-git-ext]
|
||||
git = "https://github.com/radicle-dev/radicle-link"
|
||||
|
|
|
|||
|
|
@ -7,32 +7,26 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
anyhow = { version = "1" }
|
||||
bs58 = { version = "0.4.0" }
|
||||
ed25519-compact = { version = "1.0.12", features = ["pem"] }
|
||||
byteorder = { version = "1" }
|
||||
bloomy = { version = "1.2" }
|
||||
chrono = { version = "0.4.0" }
|
||||
colored = { version = "1.9.0" }
|
||||
crossbeam-channel = { version = "0.5.6" }
|
||||
fastrand = { version = "1.8.0" }
|
||||
git-ref-format = { version = "0", features = ["serde", "macro"] }
|
||||
git2 = { version = "0.13" }
|
||||
git-url = { version = "0.3.5", features = ["serde1"] }
|
||||
multibase = { version = "0.9.1" }
|
||||
log = { version = "0.4.17", features = ["std"] }
|
||||
once_cell = { version = "1.13" }
|
||||
olpc-cjson = { version = "0.1.1" }
|
||||
sha2 = { version = "0.10.2" }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = { version = "1", features = ["preserve_order"] }
|
||||
siphasher = { version = "0.3.10" }
|
||||
radicle-git-ext = { version = "0", features = ["serde"] }
|
||||
nonempty = { version = "0.8.0", features = ["serialize"] }
|
||||
nakamoto-net = { version = "0.3.0" }
|
||||
nakamoto-net-poll = { version = "0.3.0" }
|
||||
tempfile = { version = "3.3.0" }
|
||||
thiserror = { version = "1" }
|
||||
|
||||
[dependencies.radicle]
|
||||
path = "../radicle"
|
||||
version = "0.2.0"
|
||||
|
||||
[dev-dependencies]
|
||||
radicle = { path = "../radicle", version = "*", features = ["test"] }
|
||||
quickcheck = { version = "1", default-features = false }
|
||||
quickcheck_macros = { version = "1", default-features = false }
|
||||
|
|
|
|||
|
|
@ -5,23 +5,17 @@ pub mod address_book;
|
|||
pub mod address_manager;
|
||||
pub mod client;
|
||||
pub mod clock;
|
||||
pub mod collections;
|
||||
pub mod control;
|
||||
pub mod crypto;
|
||||
pub mod decoder;
|
||||
pub mod git;
|
||||
pub mod hash;
|
||||
pub mod identity;
|
||||
pub mod logger;
|
||||
pub mod rad;
|
||||
pub mod serde_ext;
|
||||
pub mod service;
|
||||
pub mod storage;
|
||||
#[cfg(test)]
|
||||
pub mod test;
|
||||
pub mod transport;
|
||||
pub mod wire;
|
||||
|
||||
pub use radicle::{collections, crypto, git, hash, identity, rad, storage};
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::crypto::{PublicKey, Signature, Signer};
|
||||
pub use crate::decoder::Decoder;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ use std::{collections::VecDeque, fmt, net, net::IpAddr};
|
|||
|
||||
use crossbeam_channel as chan;
|
||||
use fastrand::Rng;
|
||||
use git_url::Url;
|
||||
use log::*;
|
||||
use nakamoto::{LocalDuration, LocalTime};
|
||||
use nakamoto_net as nakamoto;
|
||||
|
|
@ -22,6 +21,8 @@ use crate::address_manager::AddressManager;
|
|||
use crate::clock::RefClock;
|
||||
use crate::collections::{HashMap, HashSet};
|
||||
use crate::crypto;
|
||||
use crate::git;
|
||||
use crate::git::Url;
|
||||
use crate::identity::{Id, Project};
|
||||
use crate::service::config::ProjectTracking;
|
||||
use crate::service::message::{NodeAnnouncement, RefsAnnouncement};
|
||||
|
|
@ -81,7 +82,7 @@ pub enum Event {
|
|||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum FetchError {
|
||||
#[error(transparent)]
|
||||
Git(#[from] git2::Error),
|
||||
Git(#[from] git::raw::Error),
|
||||
#[error(transparent)]
|
||||
Storage(#[from] storage::Error),
|
||||
#[error(transparent)]
|
||||
|
|
@ -389,7 +390,7 @@ impl<'r, T: WriteStorage<'r>, S: address_book::Store, G: crypto::Signer> Service
|
|||
// TODO: Limit the number of seeds we fetch from? Randomize?
|
||||
for (_, peer) in seeds {
|
||||
match repo.fetch(&Url {
|
||||
scheme: git_url::Scheme::Git,
|
||||
scheme: git::url::Scheme::Git,
|
||||
host: Some(peer.addr.ip().to_string()),
|
||||
port: Some(peer.addr.port()),
|
||||
// TODO: Fix upstream crate so that it adds a `/` when needed.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
use std::net;
|
||||
|
||||
use git_url::Url;
|
||||
|
||||
use crate::collections::HashSet;
|
||||
use crate::git;
|
||||
use crate::git::Url;
|
||||
use crate::identity::{Id, PublicKey};
|
||||
use crate::service::message::{Address, Envelope, Message};
|
||||
|
||||
|
|
|
|||
|
|
@ -288,7 +288,7 @@ mod tests {
|
|||
use quickcheck_macros::quickcheck;
|
||||
|
||||
use crate::crypto::Signer;
|
||||
use crate::test::crypto::MockSigner;
|
||||
use crate::test::signer::MockSigner;
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_refs_announcement_signing(id: Id, refs: Refs) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
pub(crate) mod arbitrary;
|
||||
pub(crate) mod assert;
|
||||
pub(crate) mod crypto;
|
||||
pub(crate) mod fixtures;
|
||||
pub(crate) mod handle;
|
||||
pub(crate) mod logger;
|
||||
pub(crate) mod peer;
|
||||
pub(crate) mod simulator;
|
||||
pub(crate) mod storage;
|
||||
pub(crate) mod tests;
|
||||
|
||||
#[cfg(test)]
|
||||
pub use radicle::assert_matches;
|
||||
#[cfg(test)]
|
||||
pub use radicle::test::*;
|
||||
|
|
|
|||
|
|
@ -1,72 +1,20 @@
|
|||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::hash::Hash;
|
||||
use std::iter;
|
||||
use std::net;
|
||||
use std::ops::RangeBounds;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use bloomy::BloomFilter;
|
||||
use nonempty::NonEmpty;
|
||||
use quickcheck::Arbitrary;
|
||||
|
||||
use crate::collections::HashMap;
|
||||
use crate::crypto;
|
||||
use crate::crypto::{KeyPair, PublicKey, Seed, Signer, Unverified, Verified};
|
||||
use crate::git;
|
||||
use crate::hash;
|
||||
use crate::identity::{doc::Delegate, doc::Doc, Did, Id, Project};
|
||||
use crate::identity::Id;
|
||||
use crate::service::filter::{Filter, FILTER_SIZE};
|
||||
use crate::service::message::{
|
||||
Address, Envelope, InventoryAnnouncement, Message, NodeAnnouncement, RefsAnnouncement,
|
||||
Subscribe,
|
||||
};
|
||||
use crate::service::{NodeId, Timestamp};
|
||||
use crate::storage;
|
||||
use crate::storage::refs::{Refs, SignedRefs};
|
||||
use crate::test::storage::MockStorage;
|
||||
use crate::storage::refs::Refs;
|
||||
use crate::wire::message::MessageType;
|
||||
|
||||
use super::crypto::MockSigner;
|
||||
|
||||
pub fn set<T: Eq + Hash + Arbitrary>(range: impl RangeBounds<usize>) -> HashSet<T> {
|
||||
let size = fastrand::usize(range);
|
||||
let mut set = HashSet::with_capacity(size);
|
||||
let mut g = quickcheck::Gen::new(size);
|
||||
|
||||
while set.len() < size {
|
||||
set.insert(T::arbitrary(&mut g));
|
||||
}
|
||||
set
|
||||
}
|
||||
|
||||
pub fn gen<T: Arbitrary>(size: usize) -> T {
|
||||
let mut gen = quickcheck::Gen::new(size);
|
||||
|
||||
T::arbitrary(&mut gen)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ByteArray<const N: usize>([u8; N]);
|
||||
|
||||
impl<const N: usize> ByteArray<N> {
|
||||
pub fn into_inner(self) -> [u8; N] {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
self.0.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Arbitrary for ByteArray<N> {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let mut bytes: [u8; N] = [0; N];
|
||||
for byte in &mut bytes {
|
||||
*byte = u8::arbitrary(g);
|
||||
}
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
pub use radicle::test::arbitrary::*;
|
||||
|
||||
impl Arbitrary for Filter {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
|
|
@ -159,178 +107,3 @@ impl Arbitrary for Address {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for storage::Remotes<crypto::Verified> {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let remotes: HashMap<storage::RemoteId, storage::Remote<crypto::Verified>> =
|
||||
Arbitrary::arbitrary(g);
|
||||
|
||||
storage::Remotes::new(remotes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for MockStorage {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let inventory = Arbitrary::arbitrary(g);
|
||||
MockStorage::new(inventory)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Project {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let doc = Doc::<Verified>::arbitrary(g);
|
||||
let (oid, _) = doc.encode().unwrap();
|
||||
let id = Id::from(oid);
|
||||
let remotes = storage::Remotes::arbitrary(g);
|
||||
let path = PathBuf::arbitrary(g);
|
||||
|
||||
Self {
|
||||
id,
|
||||
doc,
|
||||
remotes,
|
||||
path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Did {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
Self::from(PublicKey::arbitrary(g))
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Delegate {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
Self {
|
||||
name: String::arbitrary(g),
|
||||
id: Did::arbitrary(g),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Doc<Unverified> {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let name = String::arbitrary(g);
|
||||
let description = String::arbitrary(g);
|
||||
let default_branch = String::arbitrary(g);
|
||||
let delegate = Delegate::arbitrary(g);
|
||||
|
||||
Self::initial(name, description, default_branch, delegate)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Doc<Verified> {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let rng = fastrand::Rng::with_seed(u64::arbitrary(g));
|
||||
let name = iter::repeat_with(|| rng.alphanumeric())
|
||||
.take(rng.usize(1..16))
|
||||
.collect();
|
||||
let description = iter::repeat_with(|| rng.alphanumeric())
|
||||
.take(rng.usize(0..32))
|
||||
.collect();
|
||||
let default_branch = iter::repeat_with(|| rng.alphanumeric())
|
||||
.take(rng.usize(1..16))
|
||||
.collect();
|
||||
let delegates: NonEmpty<_> = iter::repeat_with(|| Delegate {
|
||||
name: iter::repeat_with(|| rng.alphanumeric())
|
||||
.take(rng.usize(1..16))
|
||||
.collect(),
|
||||
id: Did::arbitrary(g),
|
||||
})
|
||||
.take(rng.usize(1..6))
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let threshold = delegates.len() / 2 + 1;
|
||||
let doc: Doc<Unverified> =
|
||||
Doc::new(name, description, default_branch, delegates, threshold);
|
||||
|
||||
doc.verified().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for SignedRefs<Unverified> {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let bytes: ByteArray<64> = Arbitrary::arbitrary(g);
|
||||
let signature = crypto::Signature::from(bytes.into_inner());
|
||||
let refs = Refs::arbitrary(g);
|
||||
|
||||
Self::new(refs, signature)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Refs {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let mut refs: BTreeMap<git::RefString, storage::Oid> = BTreeMap::new();
|
||||
let mut bytes: [u8; 20] = [0; 20];
|
||||
let names = &[
|
||||
"heads/master",
|
||||
"heads/feature/1",
|
||||
"heads/feature/2",
|
||||
"heads/feature/3",
|
||||
"heads/radicle/id",
|
||||
"tags/v1.0",
|
||||
"tags/v2.0",
|
||||
"notes/1",
|
||||
];
|
||||
|
||||
for _ in 0..g.size().min(names.len()) {
|
||||
if let Some(name) = g.choose(names) {
|
||||
for byte in &mut bytes {
|
||||
*byte = u8::arbitrary(g);
|
||||
}
|
||||
let oid = storage::Oid::try_from(&bytes[..]).unwrap();
|
||||
let name = git::RefString::try_from(*name).unwrap();
|
||||
|
||||
refs.insert(name, oid);
|
||||
}
|
||||
}
|
||||
Self::from(refs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for storage::Remote<crypto::Verified> {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let refs = Refs::arbitrary(g);
|
||||
let signer = MockSigner::arbitrary(g);
|
||||
let signed = refs.signed(&signer).unwrap();
|
||||
|
||||
storage::Remote::new(*signer.public_key(), signed)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for MockSigner {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let bytes: ByteArray<32> = Arbitrary::arbitrary(g);
|
||||
let seed = Seed::new(bytes.into_inner());
|
||||
let sk = KeyPair::from_seed(seed).sk;
|
||||
|
||||
MockSigner::from(sk)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Id {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let bytes = ByteArray::<20>::arbitrary(g);
|
||||
let oid = git::Oid::try_from(bytes.as_slice()).unwrap();
|
||||
|
||||
Id::from(oid)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for hash::Digest {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let bytes: Vec<u8> = Arbitrary::arbitrary(g);
|
||||
hash::Digest::new(&bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for PublicKey {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let bytes: ByteArray<32> = Arbitrary::arbitrary(g);
|
||||
let seed = Seed::new(bytes.into_inner());
|
||||
let keypair = KeyPair::from_seed(seed);
|
||||
|
||||
PublicKey(keypair.pk)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,116 +0,0 @@
|
|||
use std::path::Path;
|
||||
|
||||
use crate::crypto::{Signer, Verified};
|
||||
use crate::git;
|
||||
use crate::identity::Id;
|
||||
use crate::rad;
|
||||
use crate::storage::git::Storage;
|
||||
use crate::storage::refs::SignedRefs;
|
||||
use crate::storage::{BranchName, WriteStorage};
|
||||
use crate::test::arbitrary;
|
||||
use crate::test::crypto::MockSigner;
|
||||
|
||||
pub fn storage<P: AsRef<Path>>(path: P) -> Storage {
|
||||
let path = path.as_ref();
|
||||
let proj_ids = arbitrary::set::<Id>(3..=3);
|
||||
let signers = arbitrary::set::<MockSigner>(3..=3);
|
||||
let storage = Storage::open(path).unwrap();
|
||||
|
||||
crate::test::logger::init(log::Level::Debug);
|
||||
|
||||
for signer in signers {
|
||||
let remote = signer.public_key();
|
||||
|
||||
log::debug!("signer {}...", remote);
|
||||
|
||||
for proj in proj_ids.iter() {
|
||||
let repo = storage.repository(proj).unwrap();
|
||||
let raw = &repo.backend;
|
||||
let sig = git2::Signature::now(&remote.to_string(), "anonymous@radicle.xyz").unwrap();
|
||||
let head = git::initial_commit(raw, &sig).unwrap();
|
||||
|
||||
log::debug!("{}: creating {}...", remote, proj);
|
||||
|
||||
raw.reference(
|
||||
&format!("refs/remotes/{remote}/heads/radicle/id"),
|
||||
head.id(),
|
||||
false,
|
||||
"test",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let head = git::commit(
|
||||
raw,
|
||||
&head,
|
||||
&git::RefString::try_from(format!("refs/remotes/{remote}/heads/master")).unwrap(),
|
||||
"Second commit",
|
||||
&remote.to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
git::commit(
|
||||
raw,
|
||||
&head,
|
||||
&git::RefString::try_from(format!("refs/remotes/{remote}/heads/patch/3")).unwrap(),
|
||||
"Third commit",
|
||||
&remote.to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
storage.sign_refs(&repo, &signer).unwrap();
|
||||
}
|
||||
}
|
||||
storage
|
||||
}
|
||||
|
||||
/// Create a new repository at the given path, and initialize it into a project.
|
||||
pub fn project<'r, P: AsRef<Path>, S: WriteStorage<'r>, G: Signer>(
|
||||
path: P,
|
||||
storage: &'r S,
|
||||
signer: G,
|
||||
) -> Result<(Id, SignedRefs<Verified>, git2::Repository, git2::Oid), rad::InitError> {
|
||||
let (repo, head) = repository(path);
|
||||
let (id, refs) = rad::init(
|
||||
&repo,
|
||||
"acme",
|
||||
"Acme's repository",
|
||||
BranchName::from("master"),
|
||||
signer,
|
||||
storage,
|
||||
)?;
|
||||
|
||||
Ok((id, refs, repo, head))
|
||||
}
|
||||
|
||||
/// Creates a regular repository at the given path with a couple of commits.
|
||||
pub fn repository<P: AsRef<Path>>(path: P) -> (git2::Repository, git2::Oid) {
|
||||
let repo = git2::Repository::init(path).unwrap();
|
||||
let sig = git2::Signature::now("anonymous", "anonymous@radicle.xyz").unwrap();
|
||||
let head = git::initial_commit(&repo, &sig).unwrap();
|
||||
let oid = git::commit(
|
||||
&repo,
|
||||
&head,
|
||||
git::refname!("refs/heads/master").as_refstr(),
|
||||
"Second commit",
|
||||
"anonymous",
|
||||
)
|
||||
.unwrap()
|
||||
.id();
|
||||
|
||||
// Look, I don't really understand why we have to do this, but we do.
|
||||
drop(head);
|
||||
|
||||
(repo, oid)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
|
||||
storage(&tmp.path());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
use std::net;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use git_url::Url;
|
||||
use log::*;
|
||||
|
||||
use crate::address_book::{KnownAddress, Source};
|
||||
use crate::clock::RefClock;
|
||||
use crate::collections::HashMap;
|
||||
use crate::git::Url;
|
||||
use crate::service;
|
||||
use crate::service::config::*;
|
||||
use crate::service::message::*;
|
||||
use crate::service::*;
|
||||
use crate::storage::WriteStorage;
|
||||
use crate::test::crypto::MockSigner;
|
||||
use crate::test::signer::MockSigner;
|
||||
use crate::test::simulator;
|
||||
use crate::{Link, LocalTime};
|
||||
|
||||
|
|
|
|||
|
|
@ -11,15 +11,17 @@ use crate::service::peer::*;
|
|||
use crate::service::*;
|
||||
use crate::storage::git::Storage;
|
||||
use crate::storage::ReadStorage;
|
||||
use crate::test::assert_matches;
|
||||
use crate::test::fixtures;
|
||||
#[allow(unused)]
|
||||
use crate::test::logger;
|
||||
use crate::test::peer::Peer;
|
||||
use crate::test::signer::MockSigner;
|
||||
use crate::test::simulator;
|
||||
use crate::test::simulator::{Peer as _, Simulation};
|
||||
use crate::test::storage::MockStorage;
|
||||
use crate::{assert_matches, Link, LocalTime};
|
||||
use crate::{client, identity, rad, service, storage, test};
|
||||
use crate::{Link, LocalTime};
|
||||
|
||||
// NOTE
|
||||
//
|
||||
|
|
@ -136,7 +138,8 @@ fn test_inventory_sync() {
|
|||
[7, 7, 7, 7],
|
||||
Storage::open(tmp.path().join("alice")).unwrap(),
|
||||
);
|
||||
let bob_storage = fixtures::storage(tmp.path().join("bob"));
|
||||
let bob_signer = MockSigner::default();
|
||||
let bob_storage = fixtures::storage(tmp.path().join("bob"), bob_signer).unwrap();
|
||||
let bob = Peer::new("bob", [8, 8, 8, 8], bob_storage);
|
||||
let now = LocalTime::now().as_secs();
|
||||
let projs = bob.storage().inventory().unwrap();
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use nakamoto_net as nakamoto;
|
|||
use nakamoto_net::Link;
|
||||
|
||||
use crate::address_book;
|
||||
use crate::crypto::{PublicKey, Signature, Signer};
|
||||
use crate::crypto::{PublicKey, Signature, Signer, Unverified};
|
||||
use crate::decoder::Decoder;
|
||||
use crate::git;
|
||||
use crate::git::fmt;
|
||||
|
|
@ -21,6 +21,7 @@ use crate::identity::Id;
|
|||
use crate::service;
|
||||
use crate::service::filter;
|
||||
use crate::storage::refs::Refs;
|
||||
use crate::storage::refs::SignedRefs;
|
||||
use crate::storage::WriteStorage;
|
||||
|
||||
/// The default type we use to represent sizes.
|
||||
|
|
@ -266,7 +267,7 @@ impl Decode for git::Oid {
|
|||
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
|
||||
let len = usize::decode(reader)?;
|
||||
#[allow(non_upper_case_globals)]
|
||||
const expected: usize = mem::size_of::<git2::Oid>();
|
||||
const expected: usize = mem::size_of::<git::raw::Oid>();
|
||||
|
||||
if len != expected {
|
||||
return Err(Error::InvalidSize {
|
||||
|
|
@ -276,7 +277,7 @@ impl Decode for git::Oid {
|
|||
}
|
||||
|
||||
let buf: [u8; expected] = Decode::decode(reader)?;
|
||||
let oid = git2::Oid::from_bytes(&buf).expect("the buffer is exactly the right size");
|
||||
let oid = git::raw::Oid::from_bytes(&buf).expect("the buffer is exactly the right size");
|
||||
let oid = git::Oid::from(oid);
|
||||
|
||||
Ok(oid)
|
||||
|
|
@ -414,6 +415,26 @@ impl Decode for filter::Filter {
|
|||
}
|
||||
}
|
||||
|
||||
impl<V> Encode for SignedRefs<V> {
|
||||
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||
let mut n = 0;
|
||||
|
||||
n += self.refs.encode(writer)?;
|
||||
n += self.signature.encode(writer)?;
|
||||
|
||||
Ok(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for SignedRefs<Unverified> {
|
||||
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
|
||||
let refs = Refs::decode(reader)?;
|
||||
let signature = Signature::decode(reader)?;
|
||||
|
||||
Ok(Self::new(refs, signature))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Wire<S, T, G> {
|
||||
inboxes: HashMap<IpAddr, Decoder>,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
[package]
|
||||
name = "radicle"
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.2.0"
|
||||
authors = ["Alexis Sellier <alexis@radicle.xyz>"]
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
test = ["quickcheck"]
|
||||
|
||||
[dependencies]
|
||||
ed25519-compact = { version = "1.0.12", features = ["pem"] }
|
||||
fastrand = { version = "1.8.0" }
|
||||
git-ref-format = { version = "0", features = ["serde", "macro"] }
|
||||
git2 = { version = "0.13" }
|
||||
git-url = { version = "0.3.5", features = ["serde1"] }
|
||||
multibase = { version = "0.9.1" }
|
||||
log = { version = "0.4.17", features = ["std"] }
|
||||
once_cell = { version = "1.13" }
|
||||
olpc-cjson = { version = "0.1.1" }
|
||||
sha2 = { version = "0.10.2" }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = { version = "1", features = ["preserve_order"] }
|
||||
siphasher = { version = "0.3.10" }
|
||||
radicle-git-ext = { version = "0", features = ["serde"] }
|
||||
nonempty = { version = "0.8.0", features = ["serialize"] }
|
||||
tempfile = { version = "3.3.0" }
|
||||
thiserror = { version = "1" }
|
||||
|
||||
[dependencies.quickcheck]
|
||||
version = "1"
|
||||
default-features = false
|
||||
optional = true
|
||||
|
||||
[dev-dependencies]
|
||||
quickcheck_macros = { version = "1", default-features = false }
|
||||
|
|
@ -11,6 +11,7 @@ use crate::storage::RemoteId;
|
|||
|
||||
pub use ext::Error;
|
||||
pub use ext::Oid;
|
||||
pub use git2 as raw;
|
||||
pub use git_ref_format as fmt;
|
||||
pub use git_ref_format::{refname, RefStr, RefString};
|
||||
pub use git_url as url;
|
||||
|
|
@ -476,11 +476,12 @@ impl Identity<Untrusted> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::prelude::Signer;
|
||||
use crate::crypto::Signer;
|
||||
use crate::rad;
|
||||
use crate::storage::git::Storage;
|
||||
use crate::storage::{ReadStorage, WriteStorage};
|
||||
use crate::test::{crypto, fixtures};
|
||||
use crate::test::fixtures;
|
||||
use crate::test::signer::MockSigner;
|
||||
|
||||
use super::*;
|
||||
use quickcheck_macros::quickcheck;
|
||||
|
|
@ -490,9 +491,9 @@ mod test {
|
|||
let tempdir = tempfile::tempdir().unwrap();
|
||||
let mut rng = fastrand::Rng::new();
|
||||
|
||||
let alice = crypto::MockSigner::new(&mut rng);
|
||||
let bob = crypto::MockSigner::new(&mut rng);
|
||||
let eve = crypto::MockSigner::new(&mut rng);
|
||||
let alice = MockSigner::new(&mut rng);
|
||||
let bob = MockSigner::new(&mut rng);
|
||||
let eve = MockSigner::new(&mut rng);
|
||||
|
||||
let storage = Storage::open(tempdir.path().join("storage")).unwrap();
|
||||
let (id, _, _, _) =
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
pub mod collections;
|
||||
pub mod crypto;
|
||||
pub mod git;
|
||||
pub mod hash;
|
||||
pub mod identity;
|
||||
pub mod rad;
|
||||
pub mod serde_ext;
|
||||
pub mod storage;
|
||||
#[cfg(feature = "test")]
|
||||
pub mod test;
|
||||
|
|
@ -207,12 +207,12 @@ mod tests {
|
|||
use crate::identity::{Delegate, Did};
|
||||
use crate::storage::git::Storage;
|
||||
use crate::storage::{ReadStorage, WriteStorage};
|
||||
use crate::test::{crypto, fixtures};
|
||||
use crate::test::{fixtures, signer::MockSigner};
|
||||
|
||||
#[test]
|
||||
fn test_init() {
|
||||
let tempdir = tempfile::tempdir().unwrap();
|
||||
let signer = crypto::MockSigner::default();
|
||||
let signer = MockSigner::default();
|
||||
let public_key = *signer.public_key();
|
||||
let storage = Storage::open(tempdir.path().join("storage")).unwrap();
|
||||
let (repo, _) = fixtures::repository(tempdir.path().join("working"));
|
||||
|
|
@ -247,9 +247,9 @@ mod tests {
|
|||
fn test_fork() {
|
||||
let mut rng = fastrand::Rng::new();
|
||||
let tempdir = tempfile::tempdir().unwrap();
|
||||
let alice = crypto::MockSigner::new(&mut rng);
|
||||
let alice = MockSigner::new(&mut rng);
|
||||
let alice_id = alice.public_key();
|
||||
let bob = crypto::MockSigner::new(&mut rng);
|
||||
let bob = MockSigner::new(&mut rng);
|
||||
let bob_id = bob.public_key();
|
||||
let storage = Storage::open(tempdir.path().join("storage")).unwrap();
|
||||
let (original, _) = fixtures::repository(tempdir.path().join("original"));
|
||||
|
|
@ -280,7 +280,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_checkout() {
|
||||
let tempdir = tempfile::tempdir().unwrap();
|
||||
let signer = crypto::MockSigner::default();
|
||||
let signer = MockSigner::default();
|
||||
let remote_id = signer.public_key();
|
||||
let storage = Storage::open(tempdir.path().join("storage")).unwrap();
|
||||
let (original, _) = fixtures::repository(tempdir.path().join("original"));
|
||||
|
|
@ -5,12 +5,10 @@ use std::{fmt, fs, io};
|
|||
use git_ref_format::refspec;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
pub use radicle_git_ext::Oid;
|
||||
|
||||
use crate::crypto::{Signer, Unverified, Verified};
|
||||
use crate::git;
|
||||
use crate::identity::{self, Doc};
|
||||
use crate::identity::{Id, Project};
|
||||
use crate::identity;
|
||||
use crate::identity::{Doc, Id, Project};
|
||||
use crate::storage::refs;
|
||||
use crate::storage::refs::{Refs, SignedRefs};
|
||||
use crate::storage::{
|
||||
|
|
@ -18,6 +16,8 @@ use crate::storage::{
|
|||
WriteStorage,
|
||||
};
|
||||
|
||||
pub use crate::git::*;
|
||||
|
||||
use super::{RefUpdate, RemoteId};
|
||||
|
||||
pub static REMOTES_GLOB: Lazy<refspec::PatternString> =
|
||||
|
|
@ -50,12 +50,12 @@ impl fmt::Debug for Storage {
|
|||
}
|
||||
|
||||
impl ReadStorage for Storage {
|
||||
fn url(&self) -> git::Url {
|
||||
git::Url {
|
||||
fn url(&self) -> Url {
|
||||
Url {
|
||||
scheme: git_url::Scheme::File,
|
||||
host: None,
|
||||
path: self.path.to_string_lossy().to_string().into(),
|
||||
..git::Url::default()
|
||||
..Url::default()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -167,7 +167,7 @@ pub enum VerifyError {
|
|||
#[error("invalid remote `{0}`")]
|
||||
InvalidRemote(RemoteId),
|
||||
#[error("invalid target `{2}` for reference `{1}` of remote `{0}`")]
|
||||
InvalidRefTarget(RemoteId, git::RefString, git2::Oid),
|
||||
InvalidRefTarget(RemoteId, RefString, git2::Oid),
|
||||
#[error("invalid reference")]
|
||||
InvalidRef,
|
||||
#[error("ref error: {0}")]
|
||||
|
|
@ -185,7 +185,7 @@ pub enum VerifyError {
|
|||
impl Repository {
|
||||
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
|
||||
let backend = match git2::Repository::open_bare(path.as_ref()) {
|
||||
Err(e) if git::ext::is_not_found_err(&e) => {
|
||||
Err(e) if ext::is_not_found_err(&e) => {
|
||||
let backend = git2::Repository::init_opts(
|
||||
path,
|
||||
git2::RepositoryInitOptions::new()
|
||||
|
|
@ -239,7 +239,7 @@ impl Repository {
|
|||
.remove(&refname)
|
||||
.ok_or_else(|| VerifyError::UnknownRef(remote_id, refname.clone()))?;
|
||||
|
||||
if git::Oid::from(oid) != signed_oid {
|
||||
if Oid::from(oid) != signed_oid {
|
||||
return Err(VerifyError::InvalidRefTarget(remote_id, refname, oid));
|
||||
}
|
||||
}
|
||||
|
|
@ -278,7 +278,7 @@ impl Repository {
|
|||
}
|
||||
|
||||
/// Return the canonical identity [`git::Oid`] and document.
|
||||
pub fn identity(&self) -> Result<(git::Oid, identity::Doc<Unverified>), IdentityError> {
|
||||
pub fn identity(&self) -> Result<(Oid, identity::Doc<Unverified>), IdentityError> {
|
||||
let mut heads = Vec::new();
|
||||
for remote in self.remote_ids()? {
|
||||
let remote = remote?;
|
||||
|
|
@ -589,19 +589,20 @@ mod tests {
|
|||
use crate::assert_matches;
|
||||
use crate::git;
|
||||
use crate::storage::refs::SIGNATURE_REF;
|
||||
use crate::storage::{ReadStorage, RefUpdate, WriteRepository};
|
||||
use crate::storage::{ReadRepository, ReadStorage, RefUpdate, WriteRepository};
|
||||
use crate::test::arbitrary;
|
||||
use crate::test::crypto::MockSigner;
|
||||
use crate::test::fixtures;
|
||||
use crate::test::signer::MockSigner;
|
||||
|
||||
#[test]
|
||||
fn test_remote_refs() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let storage = fixtures::storage(dir.path());
|
||||
let signer = MockSigner::default();
|
||||
let storage = fixtures::storage(dir.path(), &signer).unwrap();
|
||||
let inv = storage.inventory().unwrap();
|
||||
let proj = inv.first().unwrap();
|
||||
let mut refs = git::remote_refs(&git::Url {
|
||||
host: Some(dir.path().to_string_lossy().to_string()),
|
||||
host: Some(storage.path().to_string_lossy().to_string()),
|
||||
scheme: git_url::Scheme::File,
|
||||
path: format!("/{}", proj).into(),
|
||||
..git::Url::default()
|
||||
|
|
@ -627,7 +628,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_fetch() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let alice = fixtures::storage(tmp.path().join("alice"));
|
||||
let alice_signer = MockSigner::default();
|
||||
let alice = fixtures::storage(tmp.path().join("alice"), alice_signer).unwrap();
|
||||
let bob = Storage::open(tmp.path().join("bob")).unwrap();
|
||||
let inventory = alice.inventory().unwrap();
|
||||
let proj = inventory.first().unwrap();
|
||||
|
|
@ -652,7 +654,7 @@ mod tests {
|
|||
.unwrap();
|
||||
|
||||
// Four refs are created for each remote.
|
||||
assert_eq!(updates.len(), remotes.len() * 4);
|
||||
assert_eq!(updates.len(), remotes.len() * 3);
|
||||
|
||||
for update in updates {
|
||||
assert_matches!(
|
||||
|
|
@ -17,7 +17,6 @@ use crate::git;
|
|||
use crate::git::Oid;
|
||||
use crate::storage;
|
||||
use crate::storage::{ReadRepository, RemoteId, WriteRepository};
|
||||
use crate::wire;
|
||||
|
||||
pub static SIGNATURE_REF: Lazy<git::RefString> = Lazy::new(|| git::refname!("radicle/signature"));
|
||||
pub const REFS_BLOB_PATH: &str = "refs";
|
||||
|
|
@ -177,8 +176,9 @@ impl DerefMut for Refs {
|
|||
/// [`Unverified`].
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SignedRefs<V> {
|
||||
refs: Refs,
|
||||
signature: Signature,
|
||||
pub refs: Refs,
|
||||
pub signature: Signature,
|
||||
|
||||
_verified: PhantomData<V>,
|
||||
}
|
||||
|
||||
|
|
@ -315,30 +315,6 @@ impl SignedRefs<Verified> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<V> wire::Encode for SignedRefs<V> {
|
||||
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||
let mut n = 0;
|
||||
|
||||
n += self.refs.encode(writer)?;
|
||||
n += self.signature.encode(writer)?;
|
||||
|
||||
Ok(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl wire::Decode for SignedRefs<Unverified> {
|
||||
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, wire::Error> {
|
||||
let refs = Refs::decode(reader)?;
|
||||
let signature = Signature::decode(reader)?;
|
||||
|
||||
Ok(Self {
|
||||
refs,
|
||||
signature,
|
||||
_verified: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Deref for SignedRefs<V> {
|
||||
type Target = Refs;
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
pub mod arbitrary;
|
||||
pub mod assert;
|
||||
pub mod fixtures;
|
||||
pub mod signer;
|
||||
pub mod storage;
|
||||
|
|
@ -0,0 +1,234 @@
|
|||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::hash::Hash;
|
||||
use std::iter;
|
||||
use std::ops::RangeBounds;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use nonempty::NonEmpty;
|
||||
use quickcheck::Arbitrary;
|
||||
|
||||
use crate::collections::HashMap;
|
||||
use crate::crypto;
|
||||
use crate::crypto::{KeyPair, PublicKey, Seed, Signer, Unverified, Verified};
|
||||
use crate::git;
|
||||
use crate::hash;
|
||||
use crate::identity::{doc::Delegate, doc::Doc, Did, Id, Project};
|
||||
use crate::storage;
|
||||
use crate::storage::refs::{Refs, SignedRefs};
|
||||
use crate::test::signer::MockSigner;
|
||||
use crate::test::storage::MockStorage;
|
||||
|
||||
pub fn set<T: Eq + Hash + Arbitrary>(range: impl RangeBounds<usize>) -> HashSet<T> {
|
||||
let size = fastrand::usize(range);
|
||||
let mut set = HashSet::with_capacity(size);
|
||||
let mut g = quickcheck::Gen::new(size);
|
||||
|
||||
while set.len() < size {
|
||||
set.insert(T::arbitrary(&mut g));
|
||||
}
|
||||
set
|
||||
}
|
||||
|
||||
pub fn gen<T: Arbitrary>(size: usize) -> T {
|
||||
let mut gen = quickcheck::Gen::new(size);
|
||||
|
||||
T::arbitrary(&mut gen)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ByteArray<const N: usize>([u8; N]);
|
||||
|
||||
impl<const N: usize> ByteArray<N> {
|
||||
pub fn into_inner(self) -> [u8; N] {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
self.0.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Arbitrary for ByteArray<N> {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let mut bytes: [u8; N] = [0; N];
|
||||
for byte in &mut bytes {
|
||||
*byte = u8::arbitrary(g);
|
||||
}
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for storage::Remotes<crypto::Verified> {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let remotes: HashMap<storage::RemoteId, storage::Remote<crypto::Verified>> =
|
||||
Arbitrary::arbitrary(g);
|
||||
|
||||
storage::Remotes::new(remotes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Project {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let doc = Doc::<Verified>::arbitrary(g);
|
||||
let (oid, _) = doc.encode().unwrap();
|
||||
let id = Id::from(oid);
|
||||
let remotes = storage::Remotes::arbitrary(g);
|
||||
let path = PathBuf::arbitrary(g);
|
||||
|
||||
Self {
|
||||
id,
|
||||
doc,
|
||||
remotes,
|
||||
path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Did {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
Self::from(PublicKey::arbitrary(g))
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Delegate {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
Self {
|
||||
name: String::arbitrary(g),
|
||||
id: Did::arbitrary(g),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Doc<Unverified> {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let name = String::arbitrary(g);
|
||||
let description = String::arbitrary(g);
|
||||
let default_branch = String::arbitrary(g);
|
||||
let delegate = Delegate::arbitrary(g);
|
||||
|
||||
Self::initial(name, description, default_branch, delegate)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Doc<Verified> {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let rng = fastrand::Rng::with_seed(u64::arbitrary(g));
|
||||
let name = iter::repeat_with(|| rng.alphanumeric())
|
||||
.take(rng.usize(1..16))
|
||||
.collect();
|
||||
let description = iter::repeat_with(|| rng.alphanumeric())
|
||||
.take(rng.usize(0..32))
|
||||
.collect();
|
||||
let default_branch = iter::repeat_with(|| rng.alphanumeric())
|
||||
.take(rng.usize(1..16))
|
||||
.collect();
|
||||
let delegates: NonEmpty<_> = iter::repeat_with(|| Delegate {
|
||||
name: iter::repeat_with(|| rng.alphanumeric())
|
||||
.take(rng.usize(1..16))
|
||||
.collect(),
|
||||
id: Did::arbitrary(g),
|
||||
})
|
||||
.take(rng.usize(1..6))
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let threshold = delegates.len() / 2 + 1;
|
||||
let doc: Doc<Unverified> =
|
||||
Doc::new(name, description, default_branch, delegates, threshold);
|
||||
|
||||
doc.verified().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for SignedRefs<Unverified> {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let bytes: ByteArray<64> = Arbitrary::arbitrary(g);
|
||||
let signature = crypto::Signature::from(bytes.into_inner());
|
||||
let refs = Refs::arbitrary(g);
|
||||
|
||||
Self::new(refs, signature)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Refs {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let mut refs: BTreeMap<git::RefString, storage::Oid> = BTreeMap::new();
|
||||
let mut bytes: [u8; 20] = [0; 20];
|
||||
let names = &[
|
||||
"heads/master",
|
||||
"heads/feature/1",
|
||||
"heads/feature/2",
|
||||
"heads/feature/3",
|
||||
"heads/radicle/id",
|
||||
"tags/v1.0",
|
||||
"tags/v2.0",
|
||||
"notes/1",
|
||||
];
|
||||
|
||||
for _ in 0..g.size().min(names.len()) {
|
||||
if let Some(name) = g.choose(names) {
|
||||
for byte in &mut bytes {
|
||||
*byte = u8::arbitrary(g);
|
||||
}
|
||||
let oid = storage::Oid::try_from(&bytes[..]).unwrap();
|
||||
let name = git::RefString::try_from(*name).unwrap();
|
||||
|
||||
refs.insert(name, oid);
|
||||
}
|
||||
}
|
||||
Self::from(refs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for MockSigner {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let bytes: ByteArray<32> = Arbitrary::arbitrary(g);
|
||||
let seed = Seed::new(bytes.into_inner());
|
||||
let sk = KeyPair::from_seed(seed).sk;
|
||||
|
||||
MockSigner::from(sk)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for MockStorage {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let inventory = Arbitrary::arbitrary(g);
|
||||
MockStorage::new(inventory)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for storage::Remote<crypto::Verified> {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let refs = Refs::arbitrary(g);
|
||||
let signer = MockSigner::arbitrary(g);
|
||||
let signed = refs.signed(&signer).unwrap();
|
||||
|
||||
storage::Remote::new(*signer.public_key(), signed)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Id {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let bytes = ByteArray::<20>::arbitrary(g);
|
||||
let oid = git::Oid::try_from(bytes.as_slice()).unwrap();
|
||||
|
||||
Id::from(oid)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for hash::Digest {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let bytes: Vec<u8> = Arbitrary::arbitrary(g);
|
||||
hash::Digest::new(&bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for PublicKey {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let bytes: ByteArray<32> = Arbitrary::arbitrary(g);
|
||||
let seed = Seed::new(bytes.into_inner());
|
||||
let keypair = KeyPair::from_seed(seed);
|
||||
|
||||
PublicKey(keypair.pk)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
use std::path::Path;
|
||||
|
||||
use crate::crypto::{Signer, Verified};
|
||||
use crate::git;
|
||||
use crate::identity::Id;
|
||||
use crate::rad;
|
||||
use crate::storage::git::Storage;
|
||||
use crate::storage::refs::SignedRefs;
|
||||
use crate::storage::{BranchName, WriteStorage};
|
||||
|
||||
/// Create a new storage with a project.
|
||||
pub fn storage<P: AsRef<Path>, G: Signer>(path: P, signer: G) -> Result<Storage, rad::InitError> {
|
||||
let path = path.as_ref();
|
||||
let storage = Storage::open(path.join("storage"))?;
|
||||
|
||||
for (name, desc) in [
|
||||
("acme", "Acme's repository"),
|
||||
("vim", "A text editor"),
|
||||
("rx", "A pixel editor"),
|
||||
] {
|
||||
let (repo, _) = repository(path.join("workdir").join(name));
|
||||
rad::init(
|
||||
&repo,
|
||||
name,
|
||||
desc,
|
||||
BranchName::from("master"),
|
||||
&signer,
|
||||
&storage,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(storage)
|
||||
}
|
||||
|
||||
/// Create a new repository at the given path, and initialize it into a project.
|
||||
pub fn project<'r, P: AsRef<Path>, S: WriteStorage<'r>, G: Signer>(
|
||||
path: P,
|
||||
storage: &'r S,
|
||||
signer: G,
|
||||
) -> Result<(Id, SignedRefs<Verified>, git2::Repository, git2::Oid), rad::InitError> {
|
||||
let (repo, head) = repository(path);
|
||||
let (id, refs) = rad::init(
|
||||
&repo,
|
||||
"acme",
|
||||
"Acme's repository",
|
||||
BranchName::from("master"),
|
||||
signer,
|
||||
storage,
|
||||
)?;
|
||||
|
||||
Ok((id, refs, repo, head))
|
||||
}
|
||||
|
||||
/// Creates a regular repository at the given path with a couple of commits.
|
||||
pub fn repository<P: AsRef<Path>>(path: P) -> (git2::Repository, git2::Oid) {
|
||||
let repo = git2::Repository::init(path).unwrap();
|
||||
let sig = git2::Signature::now("anonymous", "anonymous@radicle.xyz").unwrap();
|
||||
let head = git::initial_commit(&repo, &sig).unwrap();
|
||||
let oid = git::commit(
|
||||
&repo,
|
||||
&head,
|
||||
git::refname!("refs/heads/master").as_refstr(),
|
||||
"Second commit",
|
||||
"anonymous",
|
||||
)
|
||||
.unwrap()
|
||||
.id();
|
||||
|
||||
// Look, I don't really understand why we have to do this, but we do.
|
||||
drop(head);
|
||||
|
||||
(repo, oid)
|
||||
}
|
||||
|
|
@ -1,13 +1,9 @@
|
|||
use git_url::Url;
|
||||
|
||||
use crate::crypto::{Signer, Verified};
|
||||
use crate::git;
|
||||
use crate::identity::{Id, Project};
|
||||
use crate::storage::{refs, RefUpdate};
|
||||
use crate::storage::{
|
||||
Error, FetchError, Inventory, ReadRepository, ReadStorage, Remote, RemoteId, WriteRepository,
|
||||
WriteStorage,
|
||||
};
|
||||
|
||||
pub use crate::storage::*;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MockStorage {
|
||||
|
|
@ -90,11 +86,11 @@ impl ReadRepository<'_> for MockRepository {
|
|||
todo!()
|
||||
}
|
||||
|
||||
fn commit(&self, _oid: git::Oid) -> Result<Option<git2::Commit>, git2::Error> {
|
||||
fn commit(&self, _oid: Oid) -> Result<Option<git2::Commit>, git2::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn revwalk(&self, _head: git::Oid) -> Result<git2::Revwalk, git2::Error> {
|
||||
fn revwalk(&self, _head: Oid) -> Result<git2::Revwalk, git2::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue