mirror of
https://seed.radicle.dev/rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5.git
synced 2026-11-16 00:00:37 +08:00
node: Implement signed refs
Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
+45
-19
@@ -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
@@ -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
@@ -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)]
|
||||
|
||||
@@ -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!()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user