node: Implement signed refs

Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
Alexis Sellier
2022-09-13 10:16:05 +02:00
parent 6e86218c36
commit cd7421eb86
14 changed files with 776 additions and 172 deletions
+45 -19
View File
@@ -1,15 +1,21 @@
use std::collections::HashSet;
use std::collections::{BTreeMap, HashSet};
use std::hash::Hash;
use std::ops::RangeBounds;
use quickcheck::Arbitrary;
use crate::collections::HashMap;
use crate::crypto::PublicKey;
use crate::crypto::{self, Signer};
use crate::crypto::{PublicKey, SecretKey};
use crate::hash;
use crate::identity::{ProjId, UserId};
use crate::identity::ProjId;
use crate::storage;
use crate::storage::refs::Refs;
use crate::test::storage::MockStorage;
pub fn set<T: Eq + Hash + quickcheck::Arbitrary>(range: impl RangeBounds<usize>) -> HashSet<T> {
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);
@@ -20,35 +26,34 @@ pub fn set<T: Eq + Hash + quickcheck::Arbitrary>(range: impl RangeBounds<usize>)
set
}
pub fn gen<T: quickcheck::Arbitrary>(size: usize) -> T {
pub fn gen<T: Arbitrary>(size: usize) -> T {
let mut gen = quickcheck::Gen::new(size);
T::arbitrary(&mut gen)
}
impl quickcheck::Arbitrary for storage::Remotes<storage::Unverified> {
impl Arbitrary for storage::Remotes<crypto::Verified> {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let remotes: HashMap<storage::RemoteId, storage::Remote<storage::Unverified>> =
quickcheck::Arbitrary::arbitrary(g);
let remotes: HashMap<storage::RemoteId, storage::Remote<crypto::Verified>> =
Arbitrary::arbitrary(g);
storage::Remotes::new(remotes)
}
}
impl quickcheck::Arbitrary for MockStorage {
impl Arbitrary for MockStorage {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let inventory = quickcheck::Arbitrary::arbitrary(g);
let inventory = Arbitrary::arbitrary(g);
MockStorage::new(inventory)
}
}
impl quickcheck::Arbitrary for storage::Remote<storage::Unverified> {
impl Arbitrary for Refs {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let rng = fastrand::Rng::with_seed(u64::arbitrary(g));
let mut refs: HashMap<storage::BranchName, storage::Oid> = HashMap::with_hasher(rng.into());
let mut refs: BTreeMap<storage::RefName, storage::Oid> = BTreeMap::new();
let mut bytes: [u8; 20] = [0; 20];
// TODO: Use refs other than branch names.
let names = &["master", "dev", "feature/1", "feature/2", "feature/3"];
let id = UserId::arbitrary(g);
for _ in 0..g.size().min(2) {
if let Some(name) = g.choose(names) {
@@ -59,25 +64,46 @@ impl quickcheck::Arbitrary for storage::Remote<storage::Unverified> {
refs.insert(name.to_string(), oid);
}
}
storage::Remote::new(id, refs)
Self::from(refs)
}
}
impl quickcheck::Arbitrary for ProjId {
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 mut bytes: [u8; 32] = [0; 32];
for byte in &mut bytes {
*byte = u8::arbitrary(g);
}
MockSigner::from(SecretKey::from(bytes))
}
}
impl Arbitrary for ProjId {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let digest = hash::Digest::arbitrary(g);
ProjId::from(digest)
}
}
impl quickcheck::Arbitrary for hash::Digest {
impl Arbitrary for hash::Digest {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let bytes: Vec<u8> = quickcheck::Arbitrary::arbitrary(g);
let bytes: Vec<u8> = Arbitrary::arbitrary(g);
hash::Digest::new(&bytes)
}
}
impl quickcheck::Arbitrary for PublicKey {
impl Arbitrary for PublicKey {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
use ed25519_consensus::SigningKey;
+22 -6
View File
@@ -2,7 +2,7 @@ use ed25519_consensus as ed25519;
use crate::crypto::{PublicKey, SecretKey, Signer};
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct MockSigner {
pk: PublicKey,
sk: SecretKey,
@@ -15,12 +15,14 @@ impl MockSigner {
for byte in &mut bytes {
*byte = rng.u8(..);
}
let sk = SecretKey::from(bytes);
Self::from(SecretKey::from(bytes))
}
}
Self {
pk: sk.verification_key().into(),
sk,
}
impl From<SecretKey> for MockSigner {
fn from(sk: SecretKey) -> Self {
let pk = sk.verification_key().into();
Self { sk, pk }
}
}
@@ -36,6 +38,20 @@ impl Default for MockSigner {
}
}
impl PartialEq for MockSigner {
fn eq(&self, other: &Self) -> bool {
self.pk == other.pk
}
}
impl Eq for MockSigner {}
impl std::hash::Hash for MockSigner {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.pk.hash(state)
}
}
impl Signer for MockSigner {
fn public_key(&self) -> &PublicKey {
&self.pk
+28 -21
View File
@@ -1,59 +1,66 @@
use std::path::Path;
use crate::git;
use crate::identity::{ProjId, UserId};
use crate::identity::ProjId;
use crate::storage::git::Storage;
use crate::storage::WriteStorage;
use crate::storage::{ReadStorage, 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 storage = Storage::open(path, MockSigner::default()).unwrap();
let proj_ids = arbitrary::set::<ProjId>(3..5);
let user_ids = arbitrary::set::<UserId>(1..3);
let signers = arbitrary::set::<MockSigner>(1..3);
let mut storages = signers
.into_iter()
.map(|s| Storage::open(path, s).unwrap())
.collect::<Vec<_>>();
crate::test::logger::init(log::Level::Debug);
for proj in proj_ids.iter() {
log::debug!("creating {}...", proj);
let repo = storage.repository(proj).unwrap();
for storage in &storages {
let remote = storage.user_id();
for user in user_ids.iter() {
let repo = &repo.backend;
let sig = git2::Signature::now(&user.to_string(), "anonymous@radicle.xyz").unwrap();
let head = git::initial_commit(repo, &sig).unwrap();
log::debug!("signer {}...", remote);
log::debug!("{}: creating {}...", proj, user);
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();
repo.reference(
&format!("refs/remotes/{user}/heads/radicle/id"),
log::debug!("{}: creating {}...", remote, proj);
raw.reference(
&format!("refs/remotes/{remote}/heads/radicle/id"),
head.id(),
false,
"test",
)
.unwrap();
let head = git::commit(repo, &head, "Second commit", &user.to_string()).unwrap();
repo.reference(
&format!("refs/remotes/{user}/heads/master"),
let head = git::commit(raw, &head, "Second commit", &remote.to_string()).unwrap();
raw.reference(
&format!("refs/remotes/{remote}/heads/master"),
head.id(),
false,
"test",
)
.unwrap();
let head = git::commit(repo, &head, "Third commit", &user.to_string()).unwrap();
repo.reference(
&format!("refs/remotes/{user}/heads/patch/3"),
let head = git::commit(raw, &head, "Third commit", &remote.to_string()).unwrap();
raw.reference(
&format!("refs/remotes/{remote}/heads/patch/3"),
head.id(),
false,
"test",
)
.unwrap();
storage.sign_refs(&repo).unwrap();
}
}
storage
storages.pop().unwrap()
}
#[cfg(test)]
+47 -7
View File
@@ -1,18 +1,19 @@
use git_url::Url;
use crate::crypto::Verified;
use crate::identity::{ProjId, UserId};
use crate::storage::refs;
use crate::storage::{
Error, Inventory, ReadRepository, ReadStorage, Remote, Remotes, Unverified, WriteRepository,
WriteStorage,
Error, Inventory, ReadRepository, ReadStorage, Remote, Remotes, WriteRepository, WriteStorage,
};
#[derive(Clone, Debug)]
pub struct MockStorage {
pub inventory: Vec<(ProjId, Remotes<Unverified>)>,
pub inventory: Vec<(ProjId, Remotes<Verified>)>,
}
impl MockStorage {
pub fn new(inventory: Vec<(ProjId, Remotes<Unverified>)>) -> Self {
pub fn new(inventory: Vec<(ProjId, Remotes<Verified>)>) -> Self {
Self { inventory }
}
@@ -36,7 +37,7 @@ impl ReadStorage for MockStorage {
}
}
fn get(&self, proj: &ProjId) -> Result<Option<Remotes<Unverified>>, Error> {
fn get(&self, proj: &ProjId) -> Result<Option<Remotes<Verified>>, Error> {
if let Some((_, refs)) = self.inventory.iter().find(|(id, _)| id == proj) {
return Ok(Some(refs.clone()));
}
@@ -60,6 +61,13 @@ impl WriteStorage for MockStorage {
fn repository(&self, _proj: &ProjId) -> Result<Self::Repository, Error> {
Ok(MockRepository {})
}
fn sign_refs(
&self,
_repository: &Self::Repository,
) -> Result<crate::storage::refs::SignedRefs<Verified>, Error> {
todo!()
}
}
pub struct MockRepository {}
@@ -69,11 +77,39 @@ impl ReadRepository for MockRepository {
todo!()
}
fn remote(&self, _user: &UserId) -> Result<Remote<Unverified>, Error> {
fn remote(&self, _user: &UserId) -> Result<Remote<Verified>, refs::Error> {
todo!()
}
fn remotes(&self) -> Result<Remotes<Unverified>, Error> {
fn remotes(&self) -> Result<Remotes<Verified>, refs::Error> {
todo!()
}
fn blob_at<'a>(
&'a self,
_oid: radicle_git_ext::Oid,
_path: &'a std::path::Path,
) -> Result<git2::Blob<'a>, radicle_git_ext::Error> {
todo!()
}
fn reference(
&self,
_user: &UserId,
_reference: &str,
) -> Result<Option<git2::Reference>, git2::Error> {
todo!()
}
fn reference_oid(
&self,
_user: &UserId,
_reference: &str,
) -> Result<Option<radicle_git_ext::Oid>, git2::Error> {
todo!()
}
fn references(&self, _user: &UserId) -> Result<crate::storage::refs::Refs, Error> {
todo!()
}
}
@@ -82,4 +118,8 @@ impl WriteRepository for MockRepository {
fn fetch(&mut self, _url: &Url) -> Result<(), git2::Error> {
Ok(())
}
fn raw(&self) -> &git2::Repository {
todo!()
}
}