radicle: move to `signature` crate
The motivation of this change is to move away from tying signing to be specifically for ed25519. The reason being that the protocol will want move towards two different kinds of signing – the node signing artifacts, and an author signing artifacts. This change captures the former, node signing, by introducing a `Device` type that is the `NodeId` – a PublicKey underneath the hood – and a signing mechanism. This allows the replacement of the `Signer` trait being used – which always assumed a `PublicKey`. Instead, the `Device` is constructed with `NodeId`. In `radicle-cob`, a signer is expected to implement `signature::Signer<ExtendedSignature>`, and everywhere in `radicle`, `radicle-node`, `radicle-cli`, and `radicle-remote-helper` is expected the signer is expected to implement `signature::Signer<Signature>`. A `Device` implements both of these but only requires `signature::Signer<Signature>` to do so – since an `ExtendedSignature` is essentially `(PublicKey, Signature)`, and the `NodeId` of the `Device` can be used.
This commit is contained in:
parent
41f9048d96
commit
1f4fcc5e6a
|
|
@ -2284,6 +2284,7 @@ dependencies = [
|
|||
"radicle-git-ext",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"signature 2.2.0",
|
||||
"tempfile",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
|
@ -2316,6 +2317,7 @@ dependencies = [
|
|||
"radicle-git-ext",
|
||||
"radicle-ssh",
|
||||
"serde",
|
||||
"signature 2.2.0",
|
||||
"sqlite",
|
||||
"ssh-key",
|
||||
"tempfile",
|
||||
|
|
|
|||
|
|
@ -471,7 +471,7 @@ pub fn run(Options { op }: Options, ctx: impl term::Context) -> anyhow::Result<(
|
|||
let actions: Vec<cob::patch::Action> = read_jsonl(reader)?;
|
||||
let mut patches = profile.patches_mut(&repo)?;
|
||||
let mut patch = patches.get_mut(oid)?;
|
||||
patch.transaction(&message, &profile.signer()?, |tx| {
|
||||
patch.transaction(&message, &*profile.signer()?, |tx| {
|
||||
tx.extend(actions)?;
|
||||
tx.embed(embeds)?;
|
||||
Ok(())
|
||||
|
|
@ -481,7 +481,7 @@ pub fn run(Options { op }: Options, ctx: impl term::Context) -> anyhow::Result<(
|
|||
let actions: Vec<cob::issue::Action> = read_jsonl(reader)?;
|
||||
let mut issues = profile.issues_mut(&repo)?;
|
||||
let mut issue = issues.get_mut(oid)?;
|
||||
issue.transaction(&message, &profile.signer()?, |tx| {
|
||||
issue.transaction(&message, &*profile.signer()?, |tx| {
|
||||
tx.extend(actions)?;
|
||||
tx.embed(embeds)?;
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ use anyhow::{anyhow, Context};
|
|||
|
||||
use radicle::cob::identity::{self, IdentityMut, Revision, RevisionId};
|
||||
use radicle::identity::{doc, Doc, Identity, PayloadError, RawDoc, Visibility};
|
||||
use radicle::prelude::{Did, RepoId, Signer};
|
||||
use radicle::node::device::Device;
|
||||
use radicle::prelude::{Did, RepoId};
|
||||
use radicle::storage::refs;
|
||||
use radicle::storage::{ReadRepository, ReadStorage as _, WriteRepository};
|
||||
use radicle::{cob, Profile};
|
||||
use radicle::{cob, crypto, Profile};
|
||||
use radicle_surf::diff::Diff;
|
||||
use radicle_term::Element;
|
||||
use serde_json as json;
|
||||
|
|
@ -722,13 +723,17 @@ and description.
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
fn update<R: WriteRepository + cob::Store, G: Signer>(
|
||||
fn update<R, G>(
|
||||
title: Option<String>,
|
||||
description: Option<String>,
|
||||
doc: Doc,
|
||||
current: &mut IdentityMut<R>,
|
||||
signer: &G,
|
||||
) -> anyhow::Result<Revision> {
|
||||
signer: &Device<G>,
|
||||
) -> anyhow::Result<Revision>
|
||||
where
|
||||
R: WriteRepository + cob::Store,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
if let Some((title, description)) = edit_title_description(title, description)? {
|
||||
let id = current.update(title, description, &doc, signer)?;
|
||||
let revision = current
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@ use anyhow::{anyhow, Context as _};
|
|||
use radicle::cob::common::{Label, Reaction};
|
||||
use radicle::cob::issue::{CloseReason, State};
|
||||
use radicle::cob::{issue, thread};
|
||||
use radicle::crypto::Signer;
|
||||
use radicle::crypto;
|
||||
use radicle::issue::cache::Issues as _;
|
||||
use radicle::node::device::Device;
|
||||
use radicle::prelude::{Did, RepoId};
|
||||
use radicle::profile;
|
||||
use radicle::storage;
|
||||
|
|
@ -810,12 +811,12 @@ fn open<R, G>(
|
|||
assignees: Vec<Did>,
|
||||
options: &Options,
|
||||
cache: &mut issue::Cache<issue::Issues<'_, R>, cob::cache::StoreWriter>,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
profile: &Profile,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
R: ReadRepository + WriteRepository + cob::Store,
|
||||
G: Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let (title, description) = if let (Some(t), Some(d)) = (title.as_ref(), description.as_ref()) {
|
||||
(t.to_owned(), d.to_owned())
|
||||
|
|
@ -845,11 +846,11 @@ fn edit<'a, 'g, R, G>(
|
|||
id: Rev,
|
||||
title: Option<String>,
|
||||
description: Option<String>,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
) -> anyhow::Result<issue::IssueMut<'a, 'g, R, cob::cache::StoreWriter>>
|
||||
where
|
||||
R: WriteRepository + ReadRepository + cob::Store,
|
||||
G: radicle::crypto::Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let id = id.resolve(&repo.backend)?;
|
||||
let mut issue = issues.get_mut(&id)?;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ use std::ffi::OsString;
|
|||
use anyhow::{anyhow, Context as _};
|
||||
|
||||
use radicle::cob::job::{JobStore, Reason, State};
|
||||
use radicle::crypto::Signer;
|
||||
use radicle::crypto;
|
||||
use radicle::node::device::Device;
|
||||
use radicle::node::Handle;
|
||||
use radicle::storage::{WriteRepository, WriteStorage};
|
||||
use radicle::{cob, Node};
|
||||
|
|
@ -260,13 +261,17 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn trigger<R: WriteRepository + cob::Store, G: Signer>(
|
||||
fn trigger<R, G>(
|
||||
commit: &Rev,
|
||||
store: &mut JobStore<R>,
|
||||
repo: &radicle::storage::git::Repository,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
quiet: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
R: WriteRepository + cob::Store,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let commit = commit.resolve(&repo.backend)?;
|
||||
let job = store.create(commit, signer)?;
|
||||
if !quiet {
|
||||
|
|
@ -275,14 +280,18 @@ fn trigger<R: WriteRepository + cob::Store, G: Signer>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn start<R: WriteRepository + cob::Store, G: Signer>(
|
||||
fn start<R, G>(
|
||||
job_id: &Rev,
|
||||
run_id: &str,
|
||||
info_url: Option<String>,
|
||||
store: &mut JobStore<R>,
|
||||
repo: &radicle::storage::git::Repository,
|
||||
signer: &G,
|
||||
) -> anyhow::Result<()> {
|
||||
signer: &Device<G>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
R: WriteRepository + cob::Store,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let job_id = job_id.resolve(&repo.backend)?;
|
||||
let mut job = store.get_mut(&job_id)?;
|
||||
|
||||
|
|
@ -349,13 +358,17 @@ fn show<R: WriteRepository + cob::Store>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn finish<R: WriteRepository + cob::Store, G: Signer>(
|
||||
fn finish<R, G>(
|
||||
job_id: &Rev,
|
||||
reason: Reason,
|
||||
store: &mut JobStore<R>,
|
||||
repo: &radicle::storage::git::Repository,
|
||||
signer: &G,
|
||||
) -> anyhow::Result<()> {
|
||||
signer: &Device<G>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
R: WriteRepository + cob::Store,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let job_id = job_id.resolve(&repo.backend)?;
|
||||
let mut job = store.get_mut(&job_id)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use super::*;
|
|||
use radicle::cob;
|
||||
use radicle::cob::patch;
|
||||
use radicle::crypto;
|
||||
use radicle::node::device::Device;
|
||||
use radicle::prelude::*;
|
||||
use radicle::storage::git::Repository;
|
||||
|
||||
|
|
@ -32,10 +33,10 @@ fn edit_root<G>(
|
|||
mut patch: patch::PatchMut<'_, '_, Repository, cob::cache::StoreWriter>,
|
||||
title: String,
|
||||
description: String,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
G: crypto::Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let title = if title != patch.title() {
|
||||
Some(title)
|
||||
|
|
@ -75,10 +76,10 @@ fn edit_revision<G>(
|
|||
revision: patch::RevisionId,
|
||||
mut title: String,
|
||||
description: String,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
G: crypto::Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let embeds = patch.embeds().to_owned();
|
||||
let description = if description.is_empty() {
|
||||
|
|
|
|||
|
|
@ -89,10 +89,10 @@ pub fn run(
|
|||
.minimal(true)
|
||||
.context_lines(unified as u32);
|
||||
|
||||
builder::ReviewBuilder::new(patch_id, signer, repository)
|
||||
builder::ReviewBuilder::new(patch_id, repository)
|
||||
.hunk(hunk)
|
||||
.verdict(verdict)
|
||||
.run(revision, &mut opts)?;
|
||||
.run(revision, &mut opts, &signer)?;
|
||||
}
|
||||
Operation::Review { verdict, .. } => {
|
||||
let message = options.message.get(REVIEW_HELP_MSG)?;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@ use std::{fmt, io};
|
|||
use radicle::cob;
|
||||
use radicle::cob::patch::{PatchId, Revision, Verdict};
|
||||
use radicle::cob::{CodeLocation, CodeRange};
|
||||
use radicle::crypto;
|
||||
use radicle::git;
|
||||
use radicle::node::device::Device;
|
||||
use radicle::prelude::*;
|
||||
use radicle::storage::git::{cob::DraftStore, Repository};
|
||||
use radicle_git_ext::Oid;
|
||||
|
|
@ -569,11 +571,9 @@ impl<'a> Brain<'a> {
|
|||
}
|
||||
|
||||
/// Builds a patch review interactively, across multiple files.
|
||||
pub struct ReviewBuilder<'a, G> {
|
||||
pub struct ReviewBuilder<'a> {
|
||||
/// Patch being reviewed.
|
||||
patch_id: PatchId,
|
||||
/// Signer.
|
||||
signer: G,
|
||||
/// Stored copy of repository.
|
||||
repo: &'a Repository,
|
||||
/// Single hunk review.
|
||||
|
|
@ -582,12 +582,11 @@ pub struct ReviewBuilder<'a, G> {
|
|||
verdict: Option<Verdict>,
|
||||
}
|
||||
|
||||
impl<'a, G: Signer> ReviewBuilder<'a, G> {
|
||||
impl<'a> ReviewBuilder<'a> {
|
||||
/// Create a new review builder.
|
||||
pub fn new(patch_id: PatchId, signer: G, repo: &'a Repository) -> Self {
|
||||
pub fn new(patch_id: PatchId, repo: &'a Repository) -> Self {
|
||||
Self {
|
||||
patch_id,
|
||||
signer,
|
||||
repo,
|
||||
hunk: None,
|
||||
verdict: None,
|
||||
|
|
@ -607,9 +606,16 @@ impl<'a, G: Signer> ReviewBuilder<'a, G> {
|
|||
}
|
||||
|
||||
/// Run the review builder for the given revision.
|
||||
pub fn run(self, revision: &Revision, opts: &mut git::raw::DiffOptions) -> anyhow::Result<()> {
|
||||
pub fn run<G>(
|
||||
self,
|
||||
revision: &Revision,
|
||||
opts: &mut git::raw::DiffOptions,
|
||||
signer: &Device<G>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let repo = self.repo.raw();
|
||||
let signer = &self.signer;
|
||||
let base = repo.find_commit((*revision.base()).into())?;
|
||||
let patch_id = self.patch_id;
|
||||
let tree = {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ use radicle::cob::issue::Issue;
|
|||
use radicle::cob::thread::{Comment, CommentId};
|
||||
use radicle::cob::Reaction;
|
||||
use radicle::crypto::ssh::keystore::MemorySigner;
|
||||
use radicle::crypto::{ssh::Keystore, Signer};
|
||||
use radicle::crypto::ssh::Keystore;
|
||||
use radicle::node::device::{BoxedDevice, Device};
|
||||
use radicle::profile::env::RAD_PASSPHRASE;
|
||||
use radicle::profile::Profile;
|
||||
|
||||
|
|
@ -43,7 +44,7 @@ impl inquire::validator::StringValidator for PassphraseValidator {
|
|||
|
||||
/// Get the signer. First we try getting it from ssh-agent, otherwise we prompt the user,
|
||||
/// if we're connected to a TTY.
|
||||
pub fn signer(profile: &Profile) -> anyhow::Result<Box<dyn Signer>> {
|
||||
pub fn signer(profile: &Profile) -> anyhow::Result<BoxedDevice> {
|
||||
if let Ok(signer) = profile.signer() {
|
||||
return Ok(signer);
|
||||
}
|
||||
|
|
@ -62,7 +63,7 @@ pub fn signer(profile: &Profile) -> anyhow::Result<Box<dyn Signer>> {
|
|||
|
||||
spinner.finish();
|
||||
|
||||
Ok(signer.boxed())
|
||||
Ok(Device::from(signer).boxed())
|
||||
}
|
||||
|
||||
pub fn comment_select(issue: &Issue) -> anyhow::Result<(&CommentId, &Comment)> {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ nonempty = { version = "0.9.0", features = ["serialize"] }
|
|||
once_cell = { version = "1.13" }
|
||||
radicle-git-ext = { version = "0.8.0", features = ["serde"] }
|
||||
serde_json = { version = "1.0" }
|
||||
signature = { version = "2.2" }
|
||||
thiserror = { version = "1.0" }
|
||||
|
||||
[dependencies.git2]
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ impl change::Storage for git2::Repository {
|
|||
spec: store::Template<Self::ObjectId>,
|
||||
) -> Result<Entry, Self::StoreError>
|
||||
where
|
||||
Signer: crypto::Signer,
|
||||
Signer: signature::Signer<Self::Signatures>,
|
||||
{
|
||||
let change::Template {
|
||||
type_name,
|
||||
|
|
@ -115,11 +115,7 @@ impl change::Storage for git2::Repository {
|
|||
let manifest = store::Manifest::new(type_name, Version::default());
|
||||
let revision = write_manifest(self, &manifest, embeds, &contents)?;
|
||||
let tree = self.find_tree(revision)?;
|
||||
let signature = {
|
||||
let sig = signer.sign(revision.as_bytes());
|
||||
let key = signer.public_key();
|
||||
ExtendedSignature::new(*key, sig)
|
||||
};
|
||||
let signature = signer.sign(revision.as_bytes());
|
||||
|
||||
// Make sure there are no duplicates in the related list.
|
||||
related.sort();
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ pub trait Storage {
|
|||
template: Template<Self::ObjectId>,
|
||||
) -> Result<Entry<Self::Parent, Self::ObjectId, Self::Signatures>, Self::StoreError>
|
||||
where
|
||||
G: crypto::Signer;
|
||||
G: signature::Signer<Self::Signatures>;
|
||||
|
||||
/// Load a change entry.
|
||||
#[allow(clippy::type_complexity)]
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ pub fn create<T, S, G>(
|
|||
where
|
||||
T: Evaluate<S>,
|
||||
S: Store,
|
||||
G: crypto::Signer,
|
||||
G: signature::Signer<crate::ExtendedSignature>,
|
||||
{
|
||||
let type_name = args.type_name.clone();
|
||||
let version = args.version;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use radicle_crypto::PublicKey;
|
|||
|
||||
use crate::{
|
||||
change, change_graph::ChangeGraph, history::EntryId, CollaborativeObject, Embed, Evaluate,
|
||||
ObjectId, Store, TypeName,
|
||||
ExtendedSignature, ObjectId, Store, TypeName,
|
||||
};
|
||||
|
||||
use super::error;
|
||||
|
|
@ -67,7 +67,7 @@ pub fn update<T, S, G>(
|
|||
where
|
||||
T: Evaluate<S>,
|
||||
S: Store,
|
||||
G: crypto::Signer,
|
||||
G: signature::Signer<ExtendedSignature>,
|
||||
{
|
||||
let Update {
|
||||
type_name: ref typename,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use tempfile::TempDir;
|
|||
use crate::{
|
||||
change,
|
||||
object::{self, Reference},
|
||||
ObjectId, Store,
|
||||
signatures, ObjectId, Store,
|
||||
};
|
||||
|
||||
pub mod error {
|
||||
|
|
@ -76,7 +76,7 @@ impl change::Storage for Storage {
|
|||
Self::StoreError,
|
||||
>
|
||||
where
|
||||
Signer: crypto::Signer,
|
||||
Signer: signature::Signer<signatures::ExtendedSignature>,
|
||||
{
|
||||
self.as_raw().store(authority, parents, signer, spec)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ fastrand = { version = "2.0.0", default-features = false, optional = true }
|
|||
multibase = { version = "0.9.1" }
|
||||
ec25519 = { version = "0.1.0", features = [] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
signature = { version = "2.2" }
|
||||
sqlite = { version = "0.32.0", optional = true, features = ["bundled"] }
|
||||
thiserror = { version = "1" }
|
||||
zeroize = { version = "1.5.7" }
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ use thiserror::Error;
|
|||
|
||||
pub use ed25519::{edwards25519, Error, KeyPair, Seed};
|
||||
|
||||
pub extern crate signature;
|
||||
|
||||
#[cfg(feature = "ssh")]
|
||||
pub mod ssh;
|
||||
#[cfg(any(test, feature = "test"))]
|
||||
|
|
|
|||
|
|
@ -32,6 +32,12 @@ pub struct ExtendedSignature {
|
|||
pub sig: crypto::Signature,
|
||||
}
|
||||
|
||||
impl From<ExtendedSignature> for crypto::Signature {
|
||||
fn from(ExtendedSignature { sig, .. }: ExtendedSignature) -> Self {
|
||||
sig
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtendedSignature {
|
||||
/// Create a new extended signature.
|
||||
pub fn new(public_key: crypto::PublicKey, signature: crypto::Signature) -> Self {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ pub use std::net::TcpStream as Stream;
|
|||
#[cfg(unix)]
|
||||
pub use std::os::unix::net::UnixStream as Stream;
|
||||
|
||||
use super::ExtendedSignature;
|
||||
|
||||
pub struct Agent {
|
||||
client: AgentClient<Stream>,
|
||||
}
|
||||
|
|
@ -58,6 +60,21 @@ pub struct AgentSigner {
|
|||
public: PublicKey,
|
||||
}
|
||||
|
||||
impl signature::Signer<Signature> for AgentSigner {
|
||||
fn try_sign(&self, msg: &[u8]) -> Result<Signature, signature::Error> {
|
||||
Signer::try_sign(self, msg).map_err(signature::Error::from_source)
|
||||
}
|
||||
}
|
||||
|
||||
impl signature::Signer<ExtendedSignature> for AgentSigner {
|
||||
fn try_sign(&self, msg: &[u8]) -> Result<ExtendedSignature, signature::Error> {
|
||||
Ok(ExtendedSignature {
|
||||
key: self.public,
|
||||
sig: Signer::try_sign(self, msg).map_err(signature::Error::from_source)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl AgentSigner {
|
||||
pub fn new(agent: Agent, public: PublicKey) -> Self {
|
||||
let agent = RefCell::new(agent);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ use zeroize::Zeroizing;
|
|||
|
||||
use crate::{KeyPair, PublicKey, SecretKey, Signature, Signer, SignerError};
|
||||
|
||||
use super::ExtendedSignature;
|
||||
|
||||
/// A secret key passphrase.
|
||||
pub type Passphrase = Zeroizing<String>;
|
||||
|
||||
|
|
@ -186,6 +188,21 @@ pub struct MemorySigner {
|
|||
secret: Zeroizing<SecretKey>,
|
||||
}
|
||||
|
||||
impl signature::Signer<Signature> for MemorySigner {
|
||||
fn try_sign(&self, msg: &[u8]) -> Result<Signature, signature::Error> {
|
||||
Ok(Signer::sign(self, msg))
|
||||
}
|
||||
}
|
||||
|
||||
impl signature::Signer<ExtendedSignature> for MemorySigner {
|
||||
fn try_sign(&self, msg: &[u8]) -> Result<ExtendedSignature, signature::Error> {
|
||||
Ok(ExtendedSignature {
|
||||
key: self.public,
|
||||
sig: Signer::sign(self, msg),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Signer for MemorySigner {
|
||||
fn public_key(&self) -> &PublicKey {
|
||||
&self.public
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
use crate::{KeyPair, PublicKey, SecretKey, Seed, Signature, Signer, SignerError};
|
||||
use crate::{
|
||||
ssh::ExtendedSignature, KeyPair, PublicKey, SecretKey, Seed, Signature, Signer, SignerError,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MockSigner {
|
||||
|
|
@ -6,6 +8,21 @@ pub struct MockSigner {
|
|||
sk: SecretKey,
|
||||
}
|
||||
|
||||
impl signature::Signer<ExtendedSignature> for MockSigner {
|
||||
fn try_sign(&self, msg: &[u8]) -> Result<ExtendedSignature, signature::Error> {
|
||||
Ok(ExtendedSignature {
|
||||
key: self.pk,
|
||||
sig: signature::Signer::<Signature>::try_sign(self, msg)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl signature::Signer<Signature> for MockSigner {
|
||||
fn try_sign(&self, msg: &[u8]) -> Result<Signature, signature::Error> {
|
||||
Ok(Signature(self.sk.sign(msg, None)))
|
||||
}
|
||||
}
|
||||
|
||||
impl MockSigner {
|
||||
pub fn new(rng: &mut fastrand::Rng) -> Self {
|
||||
let mut seed: [u8; 32] = [0; 32];
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ pub const VERSION: Version = Version {
|
|||
|
||||
pub mod prelude {
|
||||
pub use crate::bounded::BoundedVec;
|
||||
pub use crate::crypto::{PublicKey, Signature, Signer};
|
||||
pub use crate::crypto::{PublicKey, Signature};
|
||||
pub use crate::deserializer::Deserializer;
|
||||
pub use crate::identity::{Did, RepoId};
|
||||
pub use crate::node::Address;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use anyhow::Context;
|
|||
use crossbeam_channel as chan;
|
||||
|
||||
use radicle::logger;
|
||||
use radicle::prelude::Signer;
|
||||
use radicle::node::device::Device;
|
||||
use radicle::profile;
|
||||
use radicle_node::crypto::ssh::keystore::{Keystore, MemorySigner};
|
||||
use radicle_node::{Runtime, VERSION};
|
||||
|
|
@ -99,7 +99,9 @@ fn execute() -> anyhow::Result<()> {
|
|||
|
||||
let passphrase = profile::env::passphrase();
|
||||
let keystore = Keystore::new(&home.keys());
|
||||
let signer = MemorySigner::load(&keystore, passphrase).context("couldn't load secret key")?;
|
||||
let signer = Device::from(
|
||||
MemorySigner::load(&keystore, passphrase).context("couldn't load secret key")?,
|
||||
);
|
||||
|
||||
log::info!(target: "node", "Node ID is {}", signer.public_key());
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ use crossbeam_channel as chan;
|
|||
use cyphernet::Ecdh;
|
||||
use netservices::resource::NetAccept;
|
||||
use radicle::cob::migrate;
|
||||
use radicle::crypto;
|
||||
use radicle::node::device::Device;
|
||||
use radicle_fetch::FetchLimit;
|
||||
use radicle_signals::Signal;
|
||||
use reactor::poller::popol;
|
||||
|
|
@ -25,7 +27,6 @@ use radicle::profile::Home;
|
|||
use radicle::{cob, git, storage, Storage};
|
||||
|
||||
use crate::control;
|
||||
use crate::crypto::Signer;
|
||||
use crate::node::{routing, NodeId};
|
||||
use crate::service::message::NodeAnnouncement;
|
||||
use crate::service::{gossip, policy, Event, INITIAL_SUBSCRIBE_BACKLOG_DELTA};
|
||||
|
|
@ -119,10 +120,10 @@ impl Runtime {
|
|||
config: service::Config,
|
||||
listen: Vec<net::SocketAddr>,
|
||||
signals: chan::Receiver<Signal>,
|
||||
signer: G,
|
||||
signer: Device<G>,
|
||||
) -> Result<Runtime, Error>
|
||||
where
|
||||
G: Signer + Ecdh<Pk = NodeId> + Clone + 'static,
|
||||
G: crypto::signature::Signer<crypto::Signature> + Ecdh<Pk = NodeId> + Clone + 'static,
|
||||
{
|
||||
let id = *signer.public_key();
|
||||
let alias = config.alias.clone();
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ use radicle::node::address;
|
|||
use radicle::node::address::Store as _;
|
||||
use radicle::node::address::{AddressBook, AddressType, KnownAddress};
|
||||
use radicle::node::config::PeerConfig;
|
||||
use radicle::node::device::Device;
|
||||
use radicle::node::refs::Store as _;
|
||||
use radicle::node::routing::Store as _;
|
||||
use radicle::node::seed;
|
||||
|
|
@ -37,7 +38,6 @@ use radicle::storage::refs::SIGREFS_BRANCH;
|
|||
use radicle::storage::RepositoryError;
|
||||
use radicle_fetch::policy::SeedingPolicy;
|
||||
|
||||
use crate::crypto::Signer;
|
||||
use crate::identity::RepoId;
|
||||
use crate::node::routing;
|
||||
use crate::node::routing::InsertResult;
|
||||
|
|
@ -390,7 +390,7 @@ pub struct Service<D, S, G> {
|
|||
/// Service configuration.
|
||||
config: Config,
|
||||
/// Our cryptographic signer and key.
|
||||
signer: G,
|
||||
signer: Device<G>,
|
||||
/// Project storage.
|
||||
storage: S,
|
||||
/// Node database.
|
||||
|
|
@ -444,10 +444,7 @@ pub struct Service<D, S, G> {
|
|||
metrics: Metrics,
|
||||
}
|
||||
|
||||
impl<D, S, G> Service<D, S, G>
|
||||
where
|
||||
G: crypto::Signer,
|
||||
{
|
||||
impl<D, S, G> Service<D, S, G> {
|
||||
/// Get the local node id.
|
||||
pub fn node_id(&self) -> NodeId {
|
||||
*self.signer.public_key()
|
||||
|
|
@ -467,14 +464,14 @@ impl<D, S, G> Service<D, S, G>
|
|||
where
|
||||
D: Store,
|
||||
S: ReadStorage + 'static,
|
||||
G: Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
pub fn new(
|
||||
config: Config,
|
||||
db: Stores<D>,
|
||||
storage: S,
|
||||
policies: policy::Config<Write>,
|
||||
signer: G,
|
||||
signer: Device<G>,
|
||||
rng: Rng,
|
||||
node: NodeAnnouncement,
|
||||
emitter: Emitter<Event>,
|
||||
|
|
@ -596,7 +593,7 @@ where
|
|||
}
|
||||
|
||||
/// Get the local signer.
|
||||
pub fn signer(&self) -> &G {
|
||||
pub fn signer(&self) -> &Device<G> {
|
||||
&self.signer
|
||||
}
|
||||
|
||||
|
|
@ -2658,7 +2655,7 @@ pub trait ServiceState {
|
|||
impl<D, S, G> ServiceState for Service<D, S, G>
|
||||
where
|
||||
D: routing::Store,
|
||||
G: Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
S: ReadStorage,
|
||||
{
|
||||
fn nid(&self) -> &NodeId {
|
||||
|
|
|
|||
|
|
@ -399,7 +399,7 @@ mod test {
|
|||
use crate::test::arbitrary;
|
||||
use localtime::LocalTime;
|
||||
use radicle::assert_matches;
|
||||
use radicle_crypto::test::signer::MockSigner;
|
||||
use radicle::node::device::Device;
|
||||
|
||||
#[test]
|
||||
fn test_announced() {
|
||||
|
|
@ -407,7 +407,7 @@ mod test {
|
|||
let nid = arbitrary::gen::<NodeId>(1);
|
||||
let rid = arbitrary::gen::<RepoId>(1);
|
||||
let timestamp = LocalTime::now().into();
|
||||
let signer = MockSigner::default();
|
||||
let signer = Device::mock();
|
||||
let refs = AnnouncementMessage::Refs(RefsAnnouncement {
|
||||
rid,
|
||||
refs: BoundedVec::new(),
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use std::{fmt, io, mem};
|
|||
|
||||
use nonempty::NonEmpty;
|
||||
use radicle::git;
|
||||
use radicle::node::device::Device;
|
||||
use radicle::storage::refs::RefsAt;
|
||||
|
||||
use crate::crypto;
|
||||
|
|
@ -260,7 +261,12 @@ pub enum AnnouncementMessage {
|
|||
|
||||
impl AnnouncementMessage {
|
||||
/// Sign this announcement message.
|
||||
pub fn signed<G: crypto::Signer>(self, signer: &G) -> Announcement {
|
||||
pub fn signed<G>(self, signer: &Device<G>) -> Announcement
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
use crypto::signature::Signer as _;
|
||||
|
||||
let msg = wire::serialize(&self);
|
||||
let signature = signer.sign(&msg);
|
||||
|
||||
|
|
@ -442,11 +448,17 @@ impl Message {
|
|||
.into()
|
||||
}
|
||||
|
||||
pub fn node<G: crypto::Signer>(message: NodeAnnouncement, signer: &G) -> Self {
|
||||
pub fn node<G: crypto::signature::Signer<crypto::Signature>>(
|
||||
message: NodeAnnouncement,
|
||||
signer: &Device<G>,
|
||||
) -> Self {
|
||||
AnnouncementMessage::from(message).signed(signer).into()
|
||||
}
|
||||
|
||||
pub fn inventory<G: crypto::Signer>(message: InventoryAnnouncement, signer: &G) -> Self {
|
||||
pub fn inventory<G: crypto::signature::Signer<crypto::Signature>>(
|
||||
message: InventoryAnnouncement,
|
||||
signer: &Device<G>,
|
||||
) -> Self {
|
||||
AnnouncementMessage::from(message).signed(signer).into()
|
||||
}
|
||||
|
||||
|
|
@ -587,7 +599,6 @@ mod tests {
|
|||
use radicle::git::raw;
|
||||
|
||||
use super::*;
|
||||
use crate::crypto::test::signer::MockSigner;
|
||||
use crate::prelude::*;
|
||||
use crate::test::arbitrary;
|
||||
use crate::wire::Encode;
|
||||
|
|
@ -595,7 +606,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_ref_remote_limit() {
|
||||
let mut refs = BoundedVec::<_, REF_REMOTE_LIMIT>::new();
|
||||
let signer = MockSigner::default();
|
||||
let signer = Device::mock();
|
||||
let at = raw::Oid::zero().into();
|
||||
|
||||
assert_eq!(refs.capacity(), REF_REMOTE_LIMIT);
|
||||
|
|
@ -613,7 +624,7 @@ mod tests {
|
|||
refs,
|
||||
timestamp: LocalTime::now().into(),
|
||||
})
|
||||
.signed(&MockSigner::default())
|
||||
.signed(&Device::mock())
|
||||
.into();
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
|
|
@ -633,7 +644,7 @@ mod tests {
|
|||
.expect("size within bounds limit"),
|
||||
timestamp: LocalTime::now().into(),
|
||||
},
|
||||
&MockSigner::default(),
|
||||
&Device::mock(),
|
||||
);
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
assert!(
|
||||
|
|
@ -655,7 +666,7 @@ mod tests {
|
|||
|
||||
#[quickcheck]
|
||||
fn prop_refs_announcement_signing(rid: RepoId) {
|
||||
let signer = MockSigner::new(&mut fastrand::Rng::new());
|
||||
let signer = Device::mock_rng(&mut fastrand::Rng::new());
|
||||
let timestamp = Timestamp::EPOCH;
|
||||
let at = raw::Oid::zero().into();
|
||||
let refs = BoundedVec::collect_from(
|
||||
|
|
|
|||
|
|
@ -11,12 +11,14 @@ use crossbeam_channel as chan;
|
|||
|
||||
use localtime::LocalTime;
|
||||
use radicle::cob::{issue, migrate};
|
||||
use radicle::crypto;
|
||||
use radicle::crypto::ssh::{keystore::MemorySigner, Keystore};
|
||||
use radicle::crypto::test::signer::MockSigner;
|
||||
use radicle::crypto::{KeyPair, Seed, Signer};
|
||||
use radicle::crypto::{KeyPair, Seed};
|
||||
use radicle::git::refname;
|
||||
use radicle::identity::{RepoId, Visibility};
|
||||
use radicle::node::config::ConnectAddress;
|
||||
use radicle::node::device::Device;
|
||||
use radicle::node::policy::store as policy;
|
||||
use radicle::node::seed::Store as _;
|
||||
use radicle::node::{Alias, Database, UserAgent, POLICIES_DB_FILE};
|
||||
|
|
@ -160,7 +162,7 @@ impl Environment {
|
|||
pub struct Node<G> {
|
||||
pub id: NodeId,
|
||||
pub home: Home,
|
||||
pub signer: G,
|
||||
pub signer: Device<G>,
|
||||
pub storage: Storage,
|
||||
pub config: Config,
|
||||
pub db: service::Stores<Database>,
|
||||
|
|
@ -169,7 +171,7 @@ pub struct Node<G> {
|
|||
|
||||
impl Node<MemorySigner> {
|
||||
pub fn new(profile: Profile) -> Self {
|
||||
let signer = MemorySigner::load(&profile.keystore, None).unwrap();
|
||||
let signer = Device::from(MemorySigner::load(&profile.keystore, None).unwrap());
|
||||
let id = *profile.id();
|
||||
let policies_db = profile.home.node().join(POLICIES_DB_FILE);
|
||||
let policies = policy::Store::open(policies_db).unwrap();
|
||||
|
|
@ -189,17 +191,19 @@ impl Node<MemorySigner> {
|
|||
}
|
||||
|
||||
/// Handle to a running node.
|
||||
pub struct NodeHandle<G: Signer + cyphernet::Ecdh + 'static> {
|
||||
pub struct NodeHandle<G: crypto::signature::Signer<crypto::Signature> + cyphernet::Ecdh + 'static> {
|
||||
pub id: NodeId,
|
||||
pub storage: Storage,
|
||||
pub signer: G,
|
||||
pub signer: Device<G>,
|
||||
pub home: Home,
|
||||
pub addr: net::SocketAddr,
|
||||
pub thread: ManuallyDrop<thread::JoinHandle<Result<(), runtime::Error>>>,
|
||||
pub handle: ManuallyDrop<Handle>,
|
||||
}
|
||||
|
||||
impl<G: Signer + cyphernet::Ecdh + 'static> Drop for NodeHandle<G> {
|
||||
impl<G: crypto::signature::Signer<crypto::Signature> + cyphernet::Ecdh + 'static> Drop
|
||||
for NodeHandle<G>
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
log::debug!(target: "test", "Node {} shutting down..", self.id);
|
||||
|
||||
|
|
@ -213,7 +217,7 @@ impl<G: Signer + cyphernet::Ecdh + 'static> Drop for NodeHandle<G> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<G: Signer + cyphernet::Ecdh> NodeHandle<G> {
|
||||
impl<G: crypto::signature::Signer<crypto::Signature> + cyphernet::Ecdh> NodeHandle<G> {
|
||||
/// Connect this node to another node, and wait for the connection to be established both ways.
|
||||
pub fn connect(&mut self, remote: &NodeHandle<G>) -> &mut Self {
|
||||
let local_events = self.handle.events();
|
||||
|
|
@ -481,7 +485,7 @@ impl Node<MockSigner> {
|
|||
.collect::<String>(),
|
||||
);
|
||||
let home = Home::new(home).unwrap();
|
||||
let signer = MockSigner::default();
|
||||
let signer = Device::mock();
|
||||
let storage = Storage::open(
|
||||
home.storage(),
|
||||
git::UserInfo {
|
||||
|
|
@ -507,7 +511,10 @@ impl Node<MockSigner> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<G: cyphernet::Ecdh<Pk = NodeId> + Signer + Clone> Node<G> {
|
||||
impl<G> Node<G>
|
||||
where
|
||||
G: cyphernet::Ecdh<Pk = NodeId> + crypto::signature::Signer<crypto::Signature> + Clone,
|
||||
{
|
||||
/// Spawn a node in its own thread.
|
||||
pub fn spawn(self) -> NodeHandle<G> {
|
||||
let listen = vec![([0, 0, 0, 0], 0).into()];
|
||||
|
|
@ -606,7 +613,7 @@ impl<G: cyphernet::Ecdh<Pk = NodeId> + Signer + Clone> Node<G> {
|
|||
|
||||
/// Checks whether the nodes have converged in their routing tables.
|
||||
#[track_caller]
|
||||
pub fn converge<'a, G: Signer + cyphernet::Ecdh + 'static>(
|
||||
pub fn converge<'a, G: crypto::signature::Signer<crypto::Signature> + cyphernet::Ecdh + 'static>(
|
||||
nodes: impl IntoIterator<Item = &'a NodeHandle<G>>,
|
||||
) -> BTreeSet<(RepoId, NodeId)> {
|
||||
let nodes = nodes.into_iter().collect::<Vec<_>>();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use radicle::crypto::test::signer::MockSigner;
|
||||
use radicle::node;
|
||||
use radicle::node::device::Device;
|
||||
use radicle::node::UserAgent;
|
||||
use radicle::test::fixtures::gen;
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ pub fn messages(count: usize, now: LocalTime, delta: LocalDuration) -> Vec<Messa
|
|||
let mut msgs = Vec::new();
|
||||
|
||||
for _ in 0..count {
|
||||
let signer = MockSigner::new(&mut rng);
|
||||
let signer = Device::mock_rng(&mut rng);
|
||||
let time = if delta == LocalDuration::from_secs(0) {
|
||||
now
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@ use std::str::FromStr;
|
|||
|
||||
use log::*;
|
||||
|
||||
use radicle::crypto;
|
||||
use radicle::identity::Visibility;
|
||||
use radicle::node::address::Store as _;
|
||||
use radicle::node::device::Device;
|
||||
use radicle::node::Database;
|
||||
use radicle::node::UserAgent;
|
||||
use radicle::node::{address, Alias, ConnectOptions};
|
||||
|
|
@ -18,7 +20,6 @@ use radicle::storage::{ReadRepository, RemoteRepository};
|
|||
use radicle::Storage;
|
||||
|
||||
use crate::crypto::test::signer::MockSigner;
|
||||
use crate::crypto::Signer;
|
||||
use crate::identity::RepoId;
|
||||
use crate::node;
|
||||
use crate::node::routing::Store as _;
|
||||
|
|
@ -56,7 +57,7 @@ pub struct Peer<S, G> {
|
|||
impl<S, G> simulator::Peer<S, G> for Peer<S, G>
|
||||
where
|
||||
S: WriteStorage + 'static,
|
||||
G: Signer + 'static,
|
||||
G: crypto::signature::Signer<crypto::Signature> + 'static,
|
||||
{
|
||||
fn init(&mut self) {}
|
||||
|
||||
|
|
@ -98,11 +99,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Config<G: Signer + 'static> {
|
||||
pub struct Config<G: crypto::signature::Signer<crypto::Signature> + 'static> {
|
||||
pub config: service::Config,
|
||||
pub local_time: LocalTime,
|
||||
pub policy: SeedingPolicy,
|
||||
pub signer: G,
|
||||
pub signer: Device<G>,
|
||||
pub rng: fastrand::Rng,
|
||||
pub tmp: tempfile::TempDir,
|
||||
}
|
||||
|
|
@ -110,7 +111,7 @@ pub struct Config<G: Signer + 'static> {
|
|||
impl Default for Config<MockSigner> {
|
||||
fn default() -> Self {
|
||||
let mut rng = fastrand::Rng::new();
|
||||
let signer = MockSigner::new(&mut rng);
|
||||
let signer = Device::mock_rng(&mut rng);
|
||||
let tmp = tempfile::TempDir::new().unwrap();
|
||||
let config = service::Config::test(Alias::from_str("mocky").unwrap());
|
||||
|
||||
|
|
@ -125,7 +126,7 @@ impl Default for Config<MockSigner> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<G: Signer> Peer<Storage, G> {
|
||||
impl<G: crypto::signature::Signer<crypto::Signature>> Peer<Storage, G> {
|
||||
pub fn project(&mut self, name: &str, description: &str) -> RepoId {
|
||||
radicle::storage::git::transport::local::register(self.storage().clone());
|
||||
|
||||
|
|
@ -148,7 +149,7 @@ impl<G: Signer> Peer<Storage, G> {
|
|||
impl<S, G> Peer<S, G>
|
||||
where
|
||||
S: WriteStorage + 'static,
|
||||
G: Signer + 'static,
|
||||
G: crypto::signature::Signer<crypto::Signature> + 'static,
|
||||
{
|
||||
pub fn config(
|
||||
name: &'static str,
|
||||
|
|
@ -387,7 +388,10 @@ where
|
|||
.expect("`inventory-announcement` must be sent");
|
||||
}
|
||||
|
||||
pub fn connect_to<T: WriteStorage + 'static, H: Signer + 'static>(
|
||||
pub fn connect_to<
|
||||
T: WriteStorage + 'static,
|
||||
H: crypto::signature::Signer<crypto::Signature> + 'static,
|
||||
>(
|
||||
&mut self,
|
||||
peer: &Peer<T, H>,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use std::{fmt, io, net};
|
|||
use localtime::{LocalDuration, LocalTime};
|
||||
use log::*;
|
||||
|
||||
use crate::crypto::Signer;
|
||||
use crate::crypto;
|
||||
use crate::prelude::{Address, RepoId};
|
||||
use crate::service::io::Io;
|
||||
use crate::service::{DisconnectReason, Event, Message, Metrics, NodeId};
|
||||
|
|
@ -202,7 +202,11 @@ pub struct Simulation<S, G> {
|
|||
signer: PhantomData<G>,
|
||||
}
|
||||
|
||||
impl<S: WriteStorage + 'static, G: Signer> Simulation<S, G> {
|
||||
impl<S, G> Simulation<S, G>
|
||||
where
|
||||
S: WriteStorage + 'static,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
/// Create a new simulation.
|
||||
pub fn new(time: LocalTime, rng: fastrand::Rng, opts: Options) -> Self {
|
||||
Self {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use netservices::Direction as Link;
|
|||
use once_cell::sync::Lazy;
|
||||
use radicle::identity::Visibility;
|
||||
use radicle::node::address::Store as _;
|
||||
use radicle::node::device::Device;
|
||||
use radicle::node::refs::Store as _;
|
||||
use radicle::node::routing::Store as _;
|
||||
use radicle::node::{ConnectOptions, DEFAULT_TIMEOUT};
|
||||
|
|
@ -21,7 +22,6 @@ use radicle::test::arbitrary::gen;
|
|||
use radicle::test::storage::MockRepository;
|
||||
|
||||
use crate::collections::{RandomMap, RandomSet};
|
||||
use crate::crypto::test::signer::MockSigner;
|
||||
use crate::identity::RepoId;
|
||||
use crate::node;
|
||||
use crate::node::config::*;
|
||||
|
|
@ -270,7 +270,7 @@ fn test_inventory_sync() {
|
|||
[7, 7, 7, 7],
|
||||
Storage::open(tmp.path().join("alice"), fixtures::user()).unwrap(),
|
||||
);
|
||||
let bob_signer = MockSigner::default();
|
||||
let bob_signer = Device::mock();
|
||||
let bob_storage = fixtures::storage(tmp.path().join("bob"), &bob_signer).unwrap();
|
||||
let bob = Peer::with_storage("bob", [8, 8, 8, 8], bob_storage);
|
||||
let now = LocalTime::now().into();
|
||||
|
|
@ -682,7 +682,7 @@ fn test_refs_announcement_relay_public() {
|
|||
|
||||
let bob = {
|
||||
let mut rng = fastrand::Rng::new();
|
||||
let signer = MockSigner::new(&mut rng);
|
||||
let signer = Device::mock_rng(&mut rng);
|
||||
let storage = fixtures::storage(tmp.path().join("bob"), &signer).unwrap();
|
||||
|
||||
Peer::config(
|
||||
|
|
@ -766,7 +766,7 @@ fn test_refs_announcement_relay_private() {
|
|||
|
||||
let bob = {
|
||||
let mut rng = fastrand::Rng::new();
|
||||
let signer = MockSigner::new(&mut rng);
|
||||
let signer = Device::mock_rng(&mut rng);
|
||||
let storage = fixtures::storage(tmp.path().join("bob"), &signer).unwrap();
|
||||
|
||||
Peer::config(
|
||||
|
|
@ -855,7 +855,7 @@ fn test_refs_announcement_fetch_trusted_no_inventory() {
|
|||
);
|
||||
let bob = {
|
||||
let mut rng = fastrand::Rng::new();
|
||||
let signer = MockSigner::new(&mut rng);
|
||||
let signer = Device::mock_rng(&mut rng);
|
||||
let storage = fixtures::storage(tmp.path().join("bob"), &signer).unwrap();
|
||||
|
||||
Peer::config(
|
||||
|
|
@ -967,7 +967,7 @@ fn test_refs_announcement_no_subscribe() {
|
|||
fn test_refs_announcement_offline() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let mut alice = {
|
||||
let signer = MockSigner::default();
|
||||
let signer = Device::mock();
|
||||
let storage = fixtures::storage(tmp.path().join("alice"), &signer).unwrap();
|
||||
|
||||
Peer::config(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{collections::HashSet, thread, time};
|
||||
|
||||
use radicle::crypto::{test::signer::MockSigner, Signer};
|
||||
use radicle::node::device::Device;
|
||||
use radicle::node::{Alias, ConnectResult, FetchResult, Handle as _, DEFAULT_TIMEOUT};
|
||||
use radicle::storage::{
|
||||
ReadRepository, ReadStorage, RefUpdate, RemoteRepository, SignRepository, ValidateRepository,
|
||||
|
|
@ -281,7 +281,7 @@ fn test_replication_invalid() {
|
|||
let tmp = tempfile::tempdir().unwrap();
|
||||
let alice = Node::init(tmp.path(), config::relay("alice"));
|
||||
let mut bob = Node::init(tmp.path(), config::relay("bob"));
|
||||
let carol = MockSigner::default();
|
||||
let carol = Device::mock();
|
||||
let acme = bob.project("acme", "");
|
||||
let repo = bob.storage.repository_mut(acme).unwrap();
|
||||
let (_, head) = repo.head().unwrap();
|
||||
|
|
@ -418,7 +418,7 @@ fn test_fetch_followed_remotes() {
|
|||
let mut signers = Vec::with_capacity(5);
|
||||
{
|
||||
for _ in 0..5 {
|
||||
let signer = MockSigner::default();
|
||||
let signer = Device::mock();
|
||||
rad::fork_remote(acme, &alice.id, &signer, &alice.storage).unwrap();
|
||||
signers.push(signer);
|
||||
}
|
||||
|
|
@ -473,7 +473,7 @@ fn test_missing_remote() {
|
|||
|
||||
let mut alice = alice.spawn();
|
||||
let mut bob = bob.spawn();
|
||||
let carol = MockSigner::default();
|
||||
let carol = Device::mock();
|
||||
|
||||
alice.connect(&bob);
|
||||
converge([&alice, &bob]);
|
||||
|
|
|
|||
|
|
@ -458,9 +458,9 @@ impl wire::Decode for ZeroBytes {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use qcheck_macros::quickcheck;
|
||||
use radicle::node::device::Device;
|
||||
use radicle::node::UserAgent;
|
||||
use radicle::storage::refs::RefsAt;
|
||||
use radicle_crypto::test::signer::MockSigner;
|
||||
|
||||
use crate::deserializer::Deserializer;
|
||||
use crate::test::arbitrary;
|
||||
|
|
@ -468,7 +468,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_refs_ann_max_size() {
|
||||
let signer = MockSigner::default();
|
||||
let signer = Device::mock();
|
||||
let refs: [RefsAt; REF_REMOTE_LIMIT] = arbitrary::gen(1);
|
||||
let ann = AnnouncementMessage::Refs(RefsAnnouncement {
|
||||
rid: arbitrary::gen(1),
|
||||
|
|
@ -484,7 +484,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_inv_ann_max_size() {
|
||||
let signer = MockSigner::default();
|
||||
let signer = Device::mock();
|
||||
let inv: [RepoId; INVENTORY_LIMIT] = arbitrary::gen(1);
|
||||
let ann = AnnouncementMessage::Inventory(InventoryAnnouncement {
|
||||
inventory: BoundedVec::collect_from(&mut inv.into_iter()),
|
||||
|
|
@ -499,7 +499,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_node_ann_max_size() {
|
||||
let signer = MockSigner::default();
|
||||
let signer = Device::mock();
|
||||
let addrs: [Address; ADDRESS_LIMIT] = arbitrary::gen(1);
|
||||
let alias = ['@'; radicle::node::MAX_ALIAS_LENGTH];
|
||||
let ann = AnnouncementMessage::Node(NodeAnnouncement {
|
||||
|
|
|
|||
|
|
@ -18,14 +18,15 @@ use localtime::LocalTime;
|
|||
use netservices::resource::{ListenerEvent, NetAccept, NetTransport, SessionEvent};
|
||||
use netservices::session::{NoiseSession, ProtocolArtifact, Socks5Session};
|
||||
use netservices::{NetConnection, NetReader, NetWriter};
|
||||
use radicle::node::device::Device;
|
||||
use reactor::{ResourceId, ResourceType, Timestamp};
|
||||
|
||||
use radicle::collections::RandomMap;
|
||||
use radicle::crypto;
|
||||
use radicle::node::config::AddressConfig;
|
||||
use radicle::node::NodeId;
|
||||
use radicle::storage::WriteStorage;
|
||||
|
||||
use crate::crypto::Signer;
|
||||
use crate::prelude::Deserializer;
|
||||
use crate::service;
|
||||
use crate::service::io::Io;
|
||||
|
|
@ -306,13 +307,13 @@ impl Peers {
|
|||
}
|
||||
|
||||
/// Wire protocol implementation for a set of peers.
|
||||
pub struct Wire<D, S, G: Signer + Ecdh> {
|
||||
pub struct Wire<D, S, G: crypto::signature::Signer<crypto::Signature> + Ecdh> {
|
||||
/// Backing service instance.
|
||||
service: Service<D, S, G>,
|
||||
/// Worker pool interface.
|
||||
worker: chan::Sender<Task>,
|
||||
/// Used for authentication.
|
||||
signer: G,
|
||||
signer: Device<G>,
|
||||
/// Node metrics.
|
||||
metrics: service::Metrics,
|
||||
/// Internal queue of actions to send to the reactor.
|
||||
|
|
@ -331,9 +332,9 @@ impl<D, S, G> Wire<D, S, G>
|
|||
where
|
||||
D: service::Store,
|
||||
S: WriteStorage + 'static,
|
||||
G: Signer + Ecdh<Pk = NodeId>,
|
||||
G: crypto::signature::Signer<crypto::Signature> + Ecdh<Pk = NodeId>,
|
||||
{
|
||||
pub fn new(service: Service<D, S, G>, worker: chan::Sender<Task>, signer: G) -> Self {
|
||||
pub fn new(service: Service<D, S, G>, worker: chan::Sender<Task>, signer: Device<G>) -> Self {
|
||||
assert!(service.started().is_some(), "Service must be initialized");
|
||||
|
||||
Self {
|
||||
|
|
@ -500,7 +501,7 @@ impl<D, S, G> reactor::Handler for Wire<D, S, G>
|
|||
where
|
||||
D: service::Store + Send,
|
||||
S: WriteStorage + Send + 'static,
|
||||
G: Signer + Ecdh<Pk = NodeId> + Clone + Send,
|
||||
G: crypto::signature::Signer<crypto::Signature> + Ecdh<Pk = NodeId> + Clone + Send,
|
||||
{
|
||||
type Listener = NetAccept<WireSession<G>>;
|
||||
type Transport = NetTransport<WireSession<G>>;
|
||||
|
|
@ -561,14 +562,17 @@ where
|
|||
return;
|
||||
}
|
||||
|
||||
let session =
|
||||
match accept::<G>(remote.clone().into(), connection, self.signer.clone()) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
log::error!(target: "wire", "Error creating session for {ip}: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let session = match accept::<G>(
|
||||
remote.clone().into(),
|
||||
connection,
|
||||
self.signer.clone().into_inner(),
|
||||
) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
log::error!(target: "wire", "Error creating session for {ip}: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let transport = match NetTransport::with_session(session, Link::Inbound) {
|
||||
Ok(transport) => transport,
|
||||
Err(err) => {
|
||||
|
|
@ -963,7 +967,7 @@ impl<D, S, G> Iterator for Wire<D, S, G>
|
|||
where
|
||||
D: service::Store,
|
||||
S: WriteStorage + 'static,
|
||||
G: Signer + Ecdh<Pk = NodeId>,
|
||||
G: crypto::signature::Signer<crypto::Signature> + Ecdh<Pk = NodeId> + Clone,
|
||||
{
|
||||
type Item = Action<G>;
|
||||
|
||||
|
|
@ -1016,7 +1020,7 @@ where
|
|||
match dial::<G>(
|
||||
addr.to_inner(),
|
||||
node_id,
|
||||
self.signer.clone(),
|
||||
self.signer.clone().into_inner(),
|
||||
self.service.config(),
|
||||
)
|
||||
.and_then(|session| {
|
||||
|
|
@ -1126,7 +1130,7 @@ where
|
|||
}
|
||||
|
||||
/// Establish a new outgoing connection.
|
||||
pub fn dial<G: Signer + Ecdh<Pk = NodeId>>(
|
||||
pub fn dial<G: Ecdh<Pk = NodeId>>(
|
||||
remote_addr: NetAddr<HostName>,
|
||||
remote_id: <G as EcSk>::Pk,
|
||||
signer: G,
|
||||
|
|
@ -1185,7 +1189,7 @@ pub fn dial<G: Signer + Ecdh<Pk = NodeId>>(
|
|||
}
|
||||
|
||||
/// Accept a new connection.
|
||||
pub fn accept<G: Signer + Ecdh<Pk = NodeId>>(
|
||||
pub fn accept<G: Ecdh<Pk = NodeId>>(
|
||||
remote_addr: NetAddr<HostName>,
|
||||
connection: net::TcpStream,
|
||||
signer: G,
|
||||
|
|
@ -1194,7 +1198,7 @@ pub fn accept<G: Signer + Ecdh<Pk = NodeId>>(
|
|||
}
|
||||
|
||||
/// Create a new [`WireSession`].
|
||||
fn session<G: Signer + Ecdh<Pk = NodeId>>(
|
||||
fn session<G: Ecdh<Pk = NodeId>>(
|
||||
remote_addr: NetAddr<HostName>,
|
||||
remote_id: Option<NodeId>,
|
||||
connection: net::TcpStream,
|
||||
|
|
|
|||
|
|
@ -5,13 +5,14 @@ use std::path::{Path, PathBuf};
|
|||
use std::str::FromStr;
|
||||
use std::{assert_eq, io};
|
||||
|
||||
use radicle::node::device::Device;
|
||||
use thiserror::Error;
|
||||
|
||||
use radicle::cob;
|
||||
use radicle::cob::object::ParseObjectId;
|
||||
use radicle::cob::patch;
|
||||
use radicle::cob::patch::cache::Patches as _;
|
||||
use radicle::crypto::Signer;
|
||||
use radicle::crypto;
|
||||
use radicle::explorer::ExplorerResource;
|
||||
use radicle::git::canonical;
|
||||
use radicle::git::canonical::Canonical;
|
||||
|
|
@ -389,7 +390,7 @@ pub fn run(
|
|||
}
|
||||
|
||||
/// Open a new patch.
|
||||
fn patch_open<G: Signer>(
|
||||
fn patch_open<G>(
|
||||
src: &git::RefStr,
|
||||
upstream: &git::RefString,
|
||||
nid: &NodeId,
|
||||
|
|
@ -399,10 +400,13 @@ fn patch_open<G: Signer>(
|
|||
patch::Patches<'_, storage::git::Repository>,
|
||||
cob::cache::StoreWriter,
|
||||
>,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
profile: &Profile,
|
||||
opts: Options,
|
||||
) -> Result<Option<ExplorerResource>, Error> {
|
||||
) -> Result<Option<ExplorerResource>, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let reference = working.find_reference(src.as_str())?;
|
||||
let commit = reference.peel_to_commit()?;
|
||||
let dst = git::refs::storage::staging::patch(nid, commit.id());
|
||||
|
|
@ -509,7 +513,7 @@ fn patch_open<G: Signer>(
|
|||
|
||||
/// Update an existing patch.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn patch_update<G: Signer>(
|
||||
fn patch_update<G>(
|
||||
src: &git::RefStr,
|
||||
dst: &git::Qualified,
|
||||
force: bool,
|
||||
|
|
@ -521,9 +525,12 @@ fn patch_update<G: Signer>(
|
|||
patch::Patches<'_, storage::git::Repository>,
|
||||
cob::cache::StoreWriter,
|
||||
>,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
opts: Options,
|
||||
) -> Result<Option<ExplorerResource>, Error> {
|
||||
) -> Result<Option<ExplorerResource>, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let reference = working.find_reference(src.as_str())?;
|
||||
let commit = reference.peel_to_commit()?;
|
||||
let patch_id = radicle::cob::ObjectId::from(oid);
|
||||
|
|
@ -592,7 +599,7 @@ fn patch_update<G: Signer>(
|
|||
Ok(Some(ExplorerResource::Patch { id: patch_id }))
|
||||
}
|
||||
|
||||
fn push<G: Signer>(
|
||||
fn push<G>(
|
||||
src: &git::RefStr,
|
||||
dst: &git::Qualified,
|
||||
force: bool,
|
||||
|
|
@ -603,8 +610,11 @@ fn push<G: Signer>(
|
|||
patch::Patches<'_, storage::git::Repository>,
|
||||
cob::cache::StoreWriter,
|
||||
>,
|
||||
signer: &G,
|
||||
) -> Result<Option<ExplorerResource>, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<Option<ExplorerResource>, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let head = match working.find_reference(src.as_str()) {
|
||||
Ok(obj) => obj.peel_to_commit()?,
|
||||
Err(e) => {
|
||||
|
|
@ -648,7 +658,7 @@ fn push<G: Signer>(
|
|||
}
|
||||
|
||||
/// Revert all patches that are no longer included in the base branch.
|
||||
fn patch_revert_all<G: Signer>(
|
||||
fn patch_revert_all<G>(
|
||||
old: git::Oid,
|
||||
new: git::Oid,
|
||||
stored: &git::raw::Repository,
|
||||
|
|
@ -656,8 +666,11 @@ fn patch_revert_all<G: Signer>(
|
|||
patch::Patches<'_, storage::git::Repository>,
|
||||
cob::cache::StoreWriter,
|
||||
>,
|
||||
_signer: &G,
|
||||
) -> Result<(), Error> {
|
||||
_signer: &Device<G>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
// Find all commits reachable from the old OID but not from the new OID.
|
||||
let mut revwalk = stored.revwalk()?;
|
||||
revwalk.push(*old)?;
|
||||
|
|
@ -710,7 +723,7 @@ fn patch_revert_all<G: Signer>(
|
|||
}
|
||||
|
||||
/// Merge all patches that have been included in the base branch.
|
||||
fn patch_merge_all<G: Signer>(
|
||||
fn patch_merge_all<G>(
|
||||
old: git::Oid,
|
||||
new: git::Oid,
|
||||
working: &git::raw::Repository,
|
||||
|
|
@ -718,8 +731,11 @@ fn patch_merge_all<G: Signer>(
|
|||
patch::Patches<'_, storage::git::Repository>,
|
||||
cob::cache::StoreWriter,
|
||||
>,
|
||||
signer: &G,
|
||||
) -> Result<(), Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let mut revwalk = working.revwalk()?;
|
||||
revwalk.push_range(&format!("{old}..{new}"))?;
|
||||
|
||||
|
|
@ -760,13 +776,17 @@ fn patch_merge_all<G: Signer>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn patch_merge<C: cob::cache::Update<patch::Patch>, G: Signer>(
|
||||
fn patch_merge<C, G>(
|
||||
mut patch: patch::PatchMut<storage::git::Repository, C>,
|
||||
revision: patch::RevisionId,
|
||||
commit: git::Oid,
|
||||
working: &git::raw::Repository,
|
||||
signer: &G,
|
||||
) -> Result<(), Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
C: cob::cache::Update<patch::Patch>,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let (latest, _) = patch.latest();
|
||||
let merged = patch.merge(revision, commit, signer)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ use std::{fmt, ops::Deref, str::FromStr};
|
|||
use crypto::{PublicKey, Signature};
|
||||
use once_cell::sync::Lazy;
|
||||
use radicle_cob::{Embed, ObjectId, TypeName};
|
||||
use radicle_crypto::Signer;
|
||||
use radicle_git_ext as git_ext;
|
||||
use radicle_git_ext::Oid;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::identity::doc::Doc;
|
||||
use crate::node::device::Device;
|
||||
use crate::{
|
||||
cob,
|
||||
cob::{
|
||||
|
|
@ -192,11 +192,14 @@ impl Identity {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn initialize<'a, R: WriteRepository + cob::Store, G: Signer>(
|
||||
pub fn initialize<'a, R: WriteRepository + cob::Store, G>(
|
||||
doc: &Doc,
|
||||
store: &'a R,
|
||||
signer: &G,
|
||||
) -> Result<IdentityMut<'a, R>, cob::store::Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<IdentityMut<'a, R>, cob::store::Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let mut store = cob::store::Store::open(store)?;
|
||||
let (id, identity) = Transaction::<Identity, _>::initial(
|
||||
"Initialize identity",
|
||||
|
|
@ -732,7 +735,10 @@ impl Revision {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn sign<G: Signer>(&self, signer: &G) -> Result<Signature, DocError> {
|
||||
pub fn sign<G: crypto::signature::Signer<crypto::Signature>>(
|
||||
&self,
|
||||
signer: &G,
|
||||
) -> Result<Signature, DocError> {
|
||||
self.doc.signature_of(signer)
|
||||
}
|
||||
}
|
||||
|
|
@ -839,14 +845,14 @@ impl<R: ReadRepository> store::Transaction<Identity, R> {
|
|||
}
|
||||
|
||||
impl<R: WriteRepository> store::Transaction<Identity, R> {
|
||||
pub fn revision<G: Signer>(
|
||||
pub fn revision<G: crypto::signature::Signer<crypto::Signature>>(
|
||||
&mut self,
|
||||
title: impl ToString,
|
||||
description: impl ToString,
|
||||
doc: &Doc,
|
||||
parent: Option<RevisionId>,
|
||||
repo: &R,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
) -> Result<(), store::Error> {
|
||||
let (blob, bytes, signature) = doc.sign(signer).map_err(store::Error::Identity)?;
|
||||
// Store document blob in repository.
|
||||
|
|
@ -901,11 +907,11 @@ where
|
|||
pub fn transaction<G, F>(
|
||||
&mut self,
|
||||
message: &str,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
operations: F,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
F: FnOnce(&mut Transaction<Identity, R>, &R) -> Result<(), store::Error>,
|
||||
{
|
||||
let mut tx = Transaction::default();
|
||||
|
|
@ -919,13 +925,16 @@ where
|
|||
|
||||
/// Update the identity by proposing a new revision.
|
||||
/// If the signer is the only delegate, the revision is accepted automatically.
|
||||
pub fn update<G: Signer>(
|
||||
pub fn update<G>(
|
||||
&mut self,
|
||||
title: impl ToString,
|
||||
description: impl ToString,
|
||||
doc: &Doc,
|
||||
signer: &G,
|
||||
) -> Result<RevisionId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<RevisionId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let parent = self.current;
|
||||
let id = self.transaction("Propose revision", signer, |tx, repo| {
|
||||
tx.revision(title, description, doc, Some(parent), repo, signer)
|
||||
|
|
@ -935,11 +944,10 @@ where
|
|||
}
|
||||
|
||||
/// Accept an active revision.
|
||||
pub fn accept<G: Signer>(
|
||||
&mut self,
|
||||
revision: &RevisionId,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
pub fn accept<G>(&mut self, revision: &RevisionId, signer: &Device<G>) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let id = *revision;
|
||||
let revision = self.revision(revision).ok_or(Error::NotFound(id))?;
|
||||
let signature = revision.sign(signer)?;
|
||||
|
|
@ -948,31 +956,32 @@ where
|
|||
}
|
||||
|
||||
/// Reject an active revision.
|
||||
pub fn reject<G: Signer>(
|
||||
&mut self,
|
||||
revision: RevisionId,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
pub fn reject<G>(&mut self, revision: RevisionId, signer: &Device<G>) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Reject revision", signer, |tx, _| tx.reject(revision))
|
||||
}
|
||||
|
||||
/// Redact a revision.
|
||||
pub fn redact<G: Signer>(
|
||||
&mut self,
|
||||
revision: RevisionId,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
pub fn redact<G>(&mut self, revision: RevisionId, signer: &Device<G>) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Redact revision", signer, |tx, _| tx.redact(revision))
|
||||
}
|
||||
|
||||
/// Edit an active revision's title or description.
|
||||
pub fn edit<G: Signer>(
|
||||
pub fn edit<G>(
|
||||
&mut self,
|
||||
revision: RevisionId,
|
||||
title: String,
|
||||
description: String,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Edit revision", signer, |tx, _| {
|
||||
tx.edit(revision, title, description)
|
||||
})
|
||||
|
|
@ -1021,8 +1030,6 @@ mod lookup {
|
|||
#[allow(clippy::unwrap_used)]
|
||||
mod test {
|
||||
use qcheck_macros::quickcheck;
|
||||
use radicle_crypto::test::signer::MockSigner;
|
||||
use radicle_crypto::Signer as _;
|
||||
|
||||
use crate::cob;
|
||||
use crate::crypto::PublicKey;
|
||||
|
|
@ -1052,7 +1059,7 @@ mod test {
|
|||
#[test]
|
||||
fn test_identity_updates() {
|
||||
let NodeWithRepo { node, repo } = NodeWithRepo::default();
|
||||
let bob = MockSigner::default();
|
||||
let bob = Device::mock();
|
||||
let signer = &node.signer;
|
||||
let mut identity = Identity::load_mut(&*repo).unwrap();
|
||||
let mut doc = identity.doc().clone().edit();
|
||||
|
|
@ -1112,8 +1119,8 @@ mod test {
|
|||
#[test]
|
||||
fn test_identity_update_rejected() {
|
||||
let NodeWithRepo { node, repo } = NodeWithRepo::default();
|
||||
let bob = MockSigner::default();
|
||||
let eve = MockSigner::default();
|
||||
let bob = Device::mock();
|
||||
let eve = Device::mock();
|
||||
let signer = &node.signer;
|
||||
let mut identity = Identity::load_mut(&*repo).unwrap();
|
||||
let mut doc = identity.doc().clone().edit();
|
||||
|
|
@ -1535,9 +1542,9 @@ mod test {
|
|||
let tempdir = tempfile::tempdir().unwrap();
|
||||
let mut rng = fastrand::Rng::new();
|
||||
|
||||
let alice = MockSigner::new(&mut rng);
|
||||
let bob = MockSigner::new(&mut rng);
|
||||
let eve = MockSigner::new(&mut rng);
|
||||
let alice = Device::mock_rng(&mut rng);
|
||||
let bob = Device::mock_rng(&mut rng);
|
||||
let eve = Device::mock_rng(&mut rng);
|
||||
|
||||
let storage = Storage::open(tempdir.path().join("storage"), fixtures::user()).unwrap();
|
||||
let (id, _, _, _) =
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ use crate::cob::store::{Cob, CobAction};
|
|||
use crate::cob::thread;
|
||||
use crate::cob::thread::{Comment, CommentId, Thread};
|
||||
use crate::cob::{op, store, ActorId, Embed, EntryId, ObjectId, TypeName};
|
||||
use crate::crypto::Signer;
|
||||
use crate::identity::doc::DocError;
|
||||
use crate::node::device::Device;
|
||||
use crate::prelude::{Did, Doc, ReadRepository, RepoId};
|
||||
use crate::storage::{HasRepoId, RepositoryError, WriteRepository};
|
||||
|
||||
|
|
@ -608,26 +608,35 @@ where
|
|||
}
|
||||
|
||||
/// Assign one or more actors to an issue.
|
||||
pub fn assign<G: Signer>(
|
||||
pub fn assign<G>(
|
||||
&mut self,
|
||||
assignees: impl IntoIterator<Item = Did>,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Assign", signer, |tx| tx.assign(assignees))
|
||||
}
|
||||
|
||||
/// Set the issue title.
|
||||
pub fn edit<G: Signer>(&mut self, title: impl ToString, signer: &G) -> Result<EntryId, Error> {
|
||||
pub fn edit<G>(&mut self, title: impl ToString, signer: &Device<G>) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Edit", signer, |tx| tx.edit(title))
|
||||
}
|
||||
|
||||
/// Set the issue description.
|
||||
pub fn edit_description<G: Signer>(
|
||||
pub fn edit_description<G>(
|
||||
&mut self,
|
||||
description: impl ToString,
|
||||
embeds: impl IntoIterator<Item = Embed<Uri>>,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let (id, _) = self.root();
|
||||
let id = *id;
|
||||
self.transaction("Edit description", signer, |tx| {
|
||||
|
|
@ -636,73 +645,89 @@ where
|
|||
}
|
||||
|
||||
/// Lifecycle an issue.
|
||||
pub fn lifecycle<G: Signer>(&mut self, state: State, signer: &G) -> Result<EntryId, Error> {
|
||||
pub fn lifecycle<G>(&mut self, state: State, signer: &Device<G>) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Lifecycle", signer, |tx| tx.lifecycle(state))
|
||||
}
|
||||
|
||||
/// Comment on an issue.
|
||||
pub fn comment<G: Signer, S: ToString>(
|
||||
pub fn comment<G, S>(
|
||||
&mut self,
|
||||
body: S,
|
||||
reply_to: CommentId,
|
||||
embeds: impl IntoIterator<Item = Embed<Uri>>,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
S: ToString,
|
||||
{
|
||||
self.transaction("Comment", signer, |tx| {
|
||||
tx.comment(body, reply_to, embeds.into_iter().collect())
|
||||
})
|
||||
}
|
||||
|
||||
/// Edit a comment.
|
||||
pub fn edit_comment<G: Signer, S: ToString>(
|
||||
pub fn edit_comment<G, S>(
|
||||
&mut self,
|
||||
id: CommentId,
|
||||
body: S,
|
||||
embeds: impl IntoIterator<Item = Embed<Uri>>,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
S: ToString,
|
||||
{
|
||||
self.transaction("Edit comment", signer, |tx| {
|
||||
tx.edit_comment(id, body, embeds.into_iter().collect())
|
||||
})
|
||||
}
|
||||
|
||||
/// Redact a comment.
|
||||
pub fn redact_comment<G: Signer>(
|
||||
&mut self,
|
||||
id: CommentId,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
pub fn redact_comment<G>(&mut self, id: CommentId, signer: &Device<G>) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Redact comment", signer, |tx| tx.redact_comment(id))
|
||||
}
|
||||
|
||||
/// Label an issue.
|
||||
pub fn label<G: Signer>(
|
||||
pub fn label<G>(
|
||||
&mut self,
|
||||
labels: impl IntoIterator<Item = Label>,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Label", signer, |tx| tx.label(labels))
|
||||
}
|
||||
|
||||
/// React to an issue comment.
|
||||
pub fn react<G: Signer>(
|
||||
pub fn react<G>(
|
||||
&mut self,
|
||||
to: CommentId,
|
||||
reaction: Reaction,
|
||||
active: bool,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("React", signer, |tx| tx.react(to, reaction, active))
|
||||
}
|
||||
|
||||
pub fn transaction<G, F>(
|
||||
&mut self,
|
||||
message: &str,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
operations: F,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
F: FnOnce(&mut Transaction<Issue, R>) -> Result<(), store::Error>,
|
||||
{
|
||||
let mut tx = Transaction::default();
|
||||
|
|
@ -791,10 +816,10 @@ where
|
|||
assignees: &[Did],
|
||||
embeds: impl IntoIterator<Item = Embed<Uri>>,
|
||||
cache: &'g mut C,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
) -> Result<IssueMut<'a, 'g, R, C>, Error>
|
||||
where
|
||||
G: Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
C: cob::cache::Update<Issue>,
|
||||
{
|
||||
let (id, issue) = Transaction::initial("Create issue", &mut self.raw, signer, |tx, _| {
|
||||
|
|
@ -822,9 +847,10 @@ where
|
|||
}
|
||||
|
||||
/// Remove an issue.
|
||||
pub fn remove<C, G: Signer>(&self, id: &ObjectId, signer: &G) -> Result<(), store::Error>
|
||||
pub fn remove<C, G>(&self, id: &ObjectId, signer: &Device<G>) -> Result<(), store::Error>
|
||||
where
|
||||
C: cob::cache::Remove<Issue>,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.raw.remove(id, signer)
|
||||
}
|
||||
|
|
@ -1717,13 +1743,12 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_cob() {
|
||||
use crate::crypto::test::signer::MockSigner;
|
||||
use cob::change::Storage as _;
|
||||
use cob::object::Storage as _;
|
||||
use nonempty::NonEmpty;
|
||||
|
||||
let test::setup::NodeWithRepo { node, repo, .. } = test::setup::NodeWithRepo::default();
|
||||
let eve = MockSigner::default();
|
||||
let eve = Device::mock();
|
||||
let identity = repo.identity().unwrap().head();
|
||||
let missing = arbitrary::oid();
|
||||
let type_name = Issue::type_name().clone();
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::cob::cache;
|
|||
use crate::cob::cache::{Remove, StoreReader, StoreWriter, Update};
|
||||
use crate::cob::store;
|
||||
use crate::cob::{Embed, Label, ObjectId, TypeName, Uri};
|
||||
use crate::crypto::Signer;
|
||||
use crate::node::device::Device;
|
||||
use crate::prelude::{Did, RepoId};
|
||||
use crate::storage::{HasRepoId, ReadRepository, RepositoryError, SignRepository, WriteRepository};
|
||||
|
||||
|
|
@ -104,11 +104,11 @@ impl<'a, R, C> Cache<super::Issues<'a, R>, C> {
|
|||
labels: &[Label],
|
||||
assignees: &[Did],
|
||||
embeds: impl IntoIterator<Item = Embed<Uri>>,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
) -> Result<IssueMut<'a, 'g, R, C>, super::Error>
|
||||
where
|
||||
R: ReadRepository + WriteRepository + cob::Store,
|
||||
G: Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
C: Update<Issue>,
|
||||
{
|
||||
self.store.create(
|
||||
|
|
@ -124,9 +124,9 @@ impl<'a, R, C> Cache<super::Issues<'a, R>, C> {
|
|||
|
||||
/// Remove the given `id` from the [`super::Issues`] storage, and
|
||||
/// removing the entry from the `cache`.
|
||||
pub fn remove<G>(&mut self, id: &IssueId, signer: &G) -> Result<(), super::Error>
|
||||
pub fn remove<G>(&mut self, id: &IssueId, signer: &Device<G>) -> Result<(), super::Error>
|
||||
where
|
||||
G: Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
R: ReadRepository + SignRepository + cob::Store,
|
||||
C: Remove<Issue>,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ use crate::cob::store;
|
|||
use crate::cob::store::{Cob, CobAction, Store, Transaction};
|
||||
use crate::cob::{EntryId, ObjectId, TypeName};
|
||||
use crate::crypto::ssh::ExtendedSignature;
|
||||
use crate::crypto::Signer;
|
||||
use crate::git;
|
||||
use crate::node::device::Device;
|
||||
use crate::prelude::ReadRepository;
|
||||
use crate::storage::{Oid, WriteRepository};
|
||||
|
||||
|
|
@ -332,12 +332,15 @@ where
|
|||
|
||||
/// Transition the `Job` into a running state, storing the provided
|
||||
/// metadata.
|
||||
pub fn start<G: Signer>(
|
||||
pub fn start<G>(
|
||||
&mut self,
|
||||
run_id: String,
|
||||
info_url: Option<String>,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Start", signer, |tx| {
|
||||
tx.start(run_id, info_url)?;
|
||||
Ok(())
|
||||
|
|
@ -345,18 +348,21 @@ where
|
|||
}
|
||||
|
||||
/// Transition the `Job` into a finished state, with the provided `reason`.
|
||||
pub fn finish<G: Signer>(&mut self, reason: Reason, signer: &G) -> Result<EntryId, Error> {
|
||||
pub fn finish<G>(&mut self, reason: Reason, signer: &Device<G>) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Finish", signer, |tx| tx.finish(reason))
|
||||
}
|
||||
|
||||
pub fn transaction<G, F>(
|
||||
&mut self,
|
||||
message: &str,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
operations: F,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
F: FnOnce(&mut Transaction<Job, R>) -> Result<(), store::Error>,
|
||||
{
|
||||
let mut tx = Transaction::default();
|
||||
|
|
@ -421,11 +427,14 @@ where
|
|||
}
|
||||
|
||||
/// Create a fresh `Job` with the provided `commit_id`.
|
||||
pub fn create<'g, G: Signer>(
|
||||
pub fn create<'g, G>(
|
||||
&'g mut self,
|
||||
commit_id: git::Oid,
|
||||
signer: &G,
|
||||
) -> Result<JobMut<'a, 'g, R>, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<JobMut<'a, 'g, R>, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let (id, job) = Transaction::initial("Create job", &mut self.raw, signer, |tx, _| {
|
||||
tx.trigger(commit_id)?;
|
||||
Ok(())
|
||||
|
|
@ -439,7 +448,10 @@ where
|
|||
}
|
||||
|
||||
/// Delete the `Job` identified by `id`.
|
||||
pub fn remove<G: Signer>(&self, id: &JobId, signer: &G) -> Result<(), store::Error> {
|
||||
pub fn remove<G>(&self, id: &JobId, signer: &Device<G>) -> Result<(), store::Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.raw.remove(id, signer)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,10 +21,11 @@ use crate::cob::thread;
|
|||
use crate::cob::thread::Thread;
|
||||
use crate::cob::thread::{Comment, CommentId, Edit, Reactions};
|
||||
use crate::cob::{op, store, ActorId, Embed, EntryId, ObjectId, TypeName, Uri};
|
||||
use crate::crypto::{PublicKey, Signer};
|
||||
use crate::crypto::PublicKey;
|
||||
use crate::git;
|
||||
use crate::identity::doc::{DocAt, DocError};
|
||||
use crate::identity::PayloadError;
|
||||
use crate::node::device::Device;
|
||||
use crate::prelude::*;
|
||||
use crate::storage;
|
||||
|
||||
|
|
@ -347,11 +348,14 @@ impl<R: WriteRepository> Merged<'_, R> {
|
|||
///
|
||||
/// This removes Git refs relating to the patch, both in the working copy,
|
||||
/// and the stored copy; and updates `rad/sigrefs`.
|
||||
pub fn cleanup<G: Signer>(
|
||||
pub fn cleanup<G>(
|
||||
self,
|
||||
working: &git::raw::Repository,
|
||||
signer: &G,
|
||||
) -> Result<(), storage::RepositoryError> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<(), storage::RepositoryError>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let nid = signer.public_key();
|
||||
let stored_ref = git::refs::patch(&self.patch).with_namespace(nid.into());
|
||||
let working_ref = git::refs::workdir::patch_upstream(&self.patch);
|
||||
|
|
@ -2000,11 +2004,11 @@ where
|
|||
pub fn transaction<G, F>(
|
||||
&mut self,
|
||||
message: &str,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
operations: F,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
F: FnOnce(&mut Transaction<Patch, R>) -> Result<(), store::Error>,
|
||||
{
|
||||
let mut tx = Transaction::default();
|
||||
|
|
@ -2023,57 +2027,72 @@ where
|
|||
}
|
||||
|
||||
/// Edit patch metadata.
|
||||
pub fn edit<G: Signer>(
|
||||
pub fn edit<G, S>(
|
||||
&mut self,
|
||||
title: String,
|
||||
target: MergeTarget,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
S: ToString,
|
||||
{
|
||||
self.transaction("Edit", signer, |tx| tx.edit(title, target))
|
||||
}
|
||||
|
||||
/// Edit revision metadata.
|
||||
pub fn edit_revision<G: Signer>(
|
||||
pub fn edit_revision<G, S>(
|
||||
&mut self,
|
||||
revision: RevisionId,
|
||||
description: String,
|
||||
description: S,
|
||||
embeds: impl IntoIterator<Item = Embed<Uri>>,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
S: ToString,
|
||||
{
|
||||
self.transaction("Edit revision", signer, |tx| {
|
||||
tx.edit_revision(revision, description, embeds.into_iter().collect())
|
||||
})
|
||||
}
|
||||
|
||||
/// Redact a revision.
|
||||
pub fn redact<G: Signer>(
|
||||
&mut self,
|
||||
revision: RevisionId,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
pub fn redact<G>(&mut self, revision: RevisionId, signer: &Device<G>) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Redact revision", signer, |tx| tx.redact(revision))
|
||||
}
|
||||
|
||||
/// Create a thread on a patch revision.
|
||||
pub fn thread<G: Signer, S: ToString>(
|
||||
pub fn thread<G, S>(
|
||||
&mut self,
|
||||
revision: RevisionId,
|
||||
body: S,
|
||||
signer: &G,
|
||||
) -> Result<CommentId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<CommentId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
S: ToString,
|
||||
{
|
||||
self.transaction("Create thread", signer, |tx| tx.thread(revision, body))
|
||||
}
|
||||
|
||||
/// Comment on a patch revision.
|
||||
pub fn comment<G: Signer, S: ToString>(
|
||||
pub fn comment<G, S>(
|
||||
&mut self,
|
||||
revision: RevisionId,
|
||||
body: S,
|
||||
reply_to: Option<CommentId>,
|
||||
location: Option<CodeLocation>,
|
||||
embeds: impl IntoIterator<Item = Embed<Uri>>,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
S: ToString,
|
||||
{
|
||||
self.transaction("Comment", signer, |tx| {
|
||||
tx.comment(
|
||||
revision,
|
||||
|
|
@ -2086,69 +2105,86 @@ where
|
|||
}
|
||||
|
||||
/// React on a patch revision.
|
||||
pub fn react<G: Signer>(
|
||||
pub fn react<G>(
|
||||
&mut self,
|
||||
revision: RevisionId,
|
||||
reaction: Reaction,
|
||||
location: Option<CodeLocation>,
|
||||
active: bool,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("React", signer, |tx| {
|
||||
tx.react(revision, reaction, location, active)
|
||||
})
|
||||
}
|
||||
|
||||
/// Edit a comment on a patch revision.
|
||||
pub fn comment_edit<G: Signer, S: ToString>(
|
||||
pub fn comment_edit<G, S>(
|
||||
&mut self,
|
||||
revision: RevisionId,
|
||||
comment: CommentId,
|
||||
body: S,
|
||||
embeds: impl IntoIterator<Item = Embed<Uri>>,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
S: ToString,
|
||||
{
|
||||
self.transaction("Edit comment", signer, |tx| {
|
||||
tx.comment_edit(revision, comment, body, embeds.into_iter().collect())
|
||||
})
|
||||
}
|
||||
|
||||
/// React to a comment on a patch revision.
|
||||
pub fn comment_react<G: Signer>(
|
||||
pub fn comment_react<G>(
|
||||
&mut self,
|
||||
revision: RevisionId,
|
||||
comment: CommentId,
|
||||
reaction: Reaction,
|
||||
active: bool,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("React comment", signer, |tx| {
|
||||
tx.comment_react(revision, comment, reaction, active)
|
||||
})
|
||||
}
|
||||
|
||||
/// Redact a comment on a patch revision.
|
||||
pub fn comment_redact<G: Signer>(
|
||||
pub fn comment_redact<G>(
|
||||
&mut self,
|
||||
revision: RevisionId,
|
||||
comment: CommentId,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Redact comment", signer, |tx| {
|
||||
tx.comment_redact(revision, comment)
|
||||
})
|
||||
}
|
||||
|
||||
/// Comment on a line of code as part of a review.
|
||||
pub fn review_comment<G: Signer, S: ToString>(
|
||||
pub fn review_comment<G, S>(
|
||||
&mut self,
|
||||
review: ReviewId,
|
||||
body: S,
|
||||
location: Option<CodeLocation>,
|
||||
reply_to: Option<CommentId>,
|
||||
embeds: impl IntoIterator<Item = Embed<Uri>>,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
S: ToString,
|
||||
{
|
||||
self.transaction("Review comment", signer, |tx| {
|
||||
tx.review_comment(
|
||||
review,
|
||||
|
|
@ -2161,54 +2197,67 @@ where
|
|||
}
|
||||
|
||||
/// Edit review comment.
|
||||
pub fn edit_review_comment<G: Signer, S: ToString>(
|
||||
pub fn edit_review_comment<G, S>(
|
||||
&mut self,
|
||||
review: ReviewId,
|
||||
comment: EntryId,
|
||||
body: S,
|
||||
embeds: impl IntoIterator<Item = Embed<Uri>>,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
S: ToString,
|
||||
{
|
||||
self.transaction("Edit review comment", signer, |tx| {
|
||||
tx.edit_review_comment(review, comment, body, embeds.into_iter().collect())
|
||||
})
|
||||
}
|
||||
|
||||
/// React to a review comment.
|
||||
pub fn react_review_comment<G: Signer>(
|
||||
pub fn react_review_comment<G>(
|
||||
&mut self,
|
||||
review: ReviewId,
|
||||
comment: EntryId,
|
||||
reaction: Reaction,
|
||||
active: bool,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("React to review comment", signer, |tx| {
|
||||
tx.react_review_comment(review, comment, reaction, active)
|
||||
})
|
||||
}
|
||||
|
||||
/// React to a review comment.
|
||||
pub fn redact_review_comment<G: Signer>(
|
||||
pub fn redact_review_comment<G>(
|
||||
&mut self,
|
||||
review: ReviewId,
|
||||
comment: EntryId,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Redact review comment", signer, |tx| {
|
||||
tx.redact_review_comment(review, comment)
|
||||
})
|
||||
}
|
||||
|
||||
/// Review a patch revision.
|
||||
pub fn review<G: Signer>(
|
||||
pub fn review<G>(
|
||||
&mut self,
|
||||
revision: RevisionId,
|
||||
verdict: Option<Verdict>,
|
||||
summary: Option<String>,
|
||||
labels: Vec<Label>,
|
||||
signer: &G,
|
||||
) -> Result<ReviewId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<ReviewId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
if verdict.is_none() && summary.is_none() {
|
||||
return Err(Error::EmptyReview);
|
||||
}
|
||||
|
|
@ -2219,59 +2268,74 @@ where
|
|||
}
|
||||
|
||||
/// Edit a review.
|
||||
pub fn review_edit<G: Signer>(
|
||||
pub fn review_edit<G>(
|
||||
&mut self,
|
||||
review: ReviewId,
|
||||
verdict: Option<Verdict>,
|
||||
summary: Option<String>,
|
||||
labels: Vec<Label>,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Edit review", signer, |tx| {
|
||||
tx.review_edit(review, verdict, summary, labels)
|
||||
})
|
||||
}
|
||||
|
||||
/// Redact a patch review.
|
||||
pub fn redact_review<G: Signer>(
|
||||
pub fn redact_review<G>(
|
||||
&mut self,
|
||||
review: ReviewId,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Redact review", signer, |tx| tx.redact_review(review))
|
||||
}
|
||||
|
||||
/// Resolve a patch review comment.
|
||||
pub fn resolve_review_comment<G: Signer>(
|
||||
pub fn resolve_review_comment<G>(
|
||||
&mut self,
|
||||
review: ReviewId,
|
||||
comment: CommentId,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Resolve review comment", signer, |tx| {
|
||||
tx.review_comment_resolve(review, comment)
|
||||
})
|
||||
}
|
||||
|
||||
/// Unresolve a patch review comment.
|
||||
pub fn unresolve_review_comment<G: Signer>(
|
||||
pub fn unresolve_review_comment<G>(
|
||||
&mut self,
|
||||
review: ReviewId,
|
||||
comment: CommentId,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Unresolve review comment", signer, |tx| {
|
||||
tx.review_comment_unresolve(review, comment)
|
||||
})
|
||||
}
|
||||
|
||||
/// Merge a patch revision.
|
||||
pub fn merge<G: Signer>(
|
||||
pub fn merge<G>(
|
||||
&mut self,
|
||||
revision: RevisionId,
|
||||
commit: git::Oid,
|
||||
signer: &G,
|
||||
) -> Result<Merged<R>, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<Merged<R>, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
// TODO: Don't allow merging the same revision twice?
|
||||
let entry = self.transaction("Merge revision", signer, |tx| tx.merge(revision, commit))?;
|
||||
|
||||
|
|
@ -2283,13 +2347,16 @@ where
|
|||
}
|
||||
|
||||
/// Update a patch with a new revision.
|
||||
pub fn update<G: Signer>(
|
||||
pub fn update<G>(
|
||||
&mut self,
|
||||
description: impl ToString,
|
||||
base: impl Into<git::Oid>,
|
||||
oid: impl Into<git::Oid>,
|
||||
signer: &G,
|
||||
) -> Result<RevisionId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<RevisionId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Add revision", signer, |tx| {
|
||||
tx.revision(description, base, oid)
|
||||
})
|
||||
|
|
@ -2297,21 +2364,30 @@ where
|
|||
}
|
||||
|
||||
/// Lifecycle a patch.
|
||||
pub fn lifecycle<G: Signer>(&mut self, state: Lifecycle, signer: &G) -> Result<EntryId, Error> {
|
||||
pub fn lifecycle<G>(&mut self, state: Lifecycle, signer: &Device<G>) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Lifecycle", signer, |tx| tx.lifecycle(state))
|
||||
}
|
||||
|
||||
/// Assign a patch.
|
||||
pub fn assign<G: Signer>(
|
||||
pub fn assign<G>(
|
||||
&mut self,
|
||||
assignees: BTreeSet<Did>,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Assign", signer, |tx| tx.assign(assignees))
|
||||
}
|
||||
|
||||
/// Archive a patch.
|
||||
pub fn archive<G: Signer>(&mut self, signer: &G) -> Result<bool, Error> {
|
||||
pub fn archive<G>(&mut self, signer: &Device<G>) -> Result<bool, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.lifecycle(Lifecycle::Archived, signer)?;
|
||||
|
||||
Ok(true)
|
||||
|
|
@ -2319,7 +2395,10 @@ where
|
|||
|
||||
/// Mark an archived patch as ready to be reviewed again.
|
||||
/// Returns `false` if the patch was not archived.
|
||||
pub fn unarchive<G: Signer>(&mut self, signer: &G) -> Result<bool, Error> {
|
||||
pub fn unarchive<G>(&mut self, signer: &Device<G>) -> Result<bool, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
if !self.is_archived() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
|
@ -2330,7 +2409,10 @@ where
|
|||
|
||||
/// Mark a patch as ready to be reviewed.
|
||||
/// Returns `false` if the patch was not a draft.
|
||||
pub fn ready<G: Signer>(&mut self, signer: &G) -> Result<bool, Error> {
|
||||
pub fn ready<G>(&mut self, signer: &Device<G>) -> Result<bool, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
if !self.is_draft() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
|
@ -2341,7 +2423,10 @@ where
|
|||
|
||||
/// Mark an open patch as a draft.
|
||||
/// Returns `false` if the patch was not open and free of merges.
|
||||
pub fn unready<G: Signer>(&mut self, signer: &G) -> Result<bool, Error> {
|
||||
pub fn unready<G>(&mut self, signer: &Device<G>) -> Result<bool, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
if !matches!(self.state(), State::Open { conflicts } if conflicts.is_empty()) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
|
@ -2351,11 +2436,14 @@ where
|
|||
}
|
||||
|
||||
/// Label a patch.
|
||||
pub fn label<G: Signer>(
|
||||
pub fn label<G>(
|
||||
&mut self,
|
||||
labels: impl IntoIterator<Item = Label>,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<EntryId, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self.transaction("Label", signer, |tx| tx.label(labels))
|
||||
}
|
||||
}
|
||||
|
|
@ -2514,11 +2602,11 @@ where
|
|||
oid: impl Into<git::Oid>,
|
||||
labels: &[Label],
|
||||
cache: &'g mut C,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
) -> Result<PatchMut<'a, 'g, R, C>, Error>
|
||||
where
|
||||
C: cob::cache::Update<Patch>,
|
||||
G: Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self._create(
|
||||
title,
|
||||
|
|
@ -2534,7 +2622,7 @@ where
|
|||
}
|
||||
|
||||
/// Draft a patch. This patch will be created in a [`State::Draft`] state.
|
||||
pub fn draft<'g, C, G: Signer>(
|
||||
pub fn draft<'g, C, G>(
|
||||
&'g mut self,
|
||||
title: impl ToString,
|
||||
description: impl ToString,
|
||||
|
|
@ -2543,10 +2631,11 @@ where
|
|||
oid: impl Into<git::Oid>,
|
||||
labels: &[Label],
|
||||
cache: &'g mut C,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
) -> Result<PatchMut<'a, 'g, R, C>, Error>
|
||||
where
|
||||
C: cob::cache::Update<Patch>,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
self._create(
|
||||
title,
|
||||
|
|
@ -2581,7 +2670,7 @@ where
|
|||
}
|
||||
|
||||
/// Create a patch. This is an internal function used by `create` and `draft`.
|
||||
fn _create<'g, C, G: Signer>(
|
||||
fn _create<'g, C, G>(
|
||||
&'g mut self,
|
||||
title: impl ToString,
|
||||
description: impl ToString,
|
||||
|
|
@ -2591,10 +2680,11 @@ where
|
|||
labels: &[Label],
|
||||
state: Lifecycle,
|
||||
cache: &'g mut C,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
) -> Result<PatchMut<'a, 'g, R, C>, Error>
|
||||
where
|
||||
C: cob::cache::Update<Patch>,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let (id, patch) = Transaction::initial("Create patch", &mut self.raw, signer, |tx, _| {
|
||||
tx.revision(description, base, oid)?;
|
||||
|
|
@ -3070,7 +3160,7 @@ mod test {
|
|||
fn test_revision_review_merge_redacted() {
|
||||
let base = git::Oid::from_str("cb18e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap();
|
||||
let oid = git::Oid::from_str("518d5069f94c03427f694bb494ac1cd7d1339380").unwrap();
|
||||
let mut alice = Actor::new(MockSigner::default());
|
||||
let mut alice = Actor::<MockSigner>::default();
|
||||
let rid = gen::<RepoId>(1);
|
||||
let doc = RawDoc::new(
|
||||
gen::<Project>(1),
|
||||
|
|
@ -3189,7 +3279,7 @@ mod test {
|
|||
fn test_revision_reaction() {
|
||||
let base = git::Oid::from_str("cb18e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap();
|
||||
let oid = git::Oid::from_str("518d5069f94c03427f694bb494ac1cd7d1339380").unwrap();
|
||||
let mut alice = Actor::new(MockSigner::default());
|
||||
let mut alice = Actor::<MockSigner>::default();
|
||||
let repo = gen::<MockRepository>(1);
|
||||
let reaction = Reaction::new('👍').expect("failed to create a reaction");
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ use crate::cob::cache::{self, StoreReader};
|
|||
use crate::cob::cache::{Remove, StoreWriter, Update};
|
||||
use crate::cob::store;
|
||||
use crate::cob::{Label, ObjectId, TypeName};
|
||||
use crate::crypto::Signer;
|
||||
use crate::git;
|
||||
use crate::node::device::Device;
|
||||
use crate::prelude::RepoId;
|
||||
use crate::storage::{HasRepoId, ReadRepository, RepositoryError, SignRepository, WriteRepository};
|
||||
|
||||
|
|
@ -116,11 +116,11 @@ impl<'a, R, C> Cache<super::Patches<'a, R>, C> {
|
|||
base: impl Into<git::Oid>,
|
||||
oid: impl Into<git::Oid>,
|
||||
labels: &[Label],
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
) -> Result<PatchMut<'a, 'g, R, C>, super::Error>
|
||||
where
|
||||
R: WriteRepository + cob::Store,
|
||||
G: Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
C: Update<Patch>,
|
||||
{
|
||||
self.store.create(
|
||||
|
|
@ -146,11 +146,11 @@ impl<'a, R, C> Cache<super::Patches<'a, R>, C> {
|
|||
base: impl Into<git::Oid>,
|
||||
oid: impl Into<git::Oid>,
|
||||
labels: &[Label],
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
) -> Result<PatchMut<'a, 'g, R, C>, super::Error>
|
||||
where
|
||||
R: WriteRepository + cob::Store,
|
||||
G: Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
C: Update<Patch>,
|
||||
{
|
||||
self.store.draft(
|
||||
|
|
@ -167,9 +167,9 @@ impl<'a, R, C> Cache<super::Patches<'a, R>, C> {
|
|||
|
||||
/// Remove the given `id` from the [`super::Patches`] storage, and
|
||||
/// removing the entry from the `cache`.
|
||||
pub fn remove<G>(&mut self, id: &PatchId, signer: &G) -> Result<(), super::Error>
|
||||
pub fn remove<G>(&mut self, id: &PatchId, signer: &Device<G>) -> Result<(), super::Error>
|
||||
where
|
||||
G: Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
R: ReadRepository + SignRepository + cob::Store,
|
||||
C: Remove<Patch>,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize};
|
|||
use crate::cob::op::Op;
|
||||
use crate::cob::{Create, Embed, EntryId, ObjectId, TypeName, Update, Updated, Uri, Version};
|
||||
use crate::git;
|
||||
use crate::node::device::Device;
|
||||
use crate::prelude::*;
|
||||
use crate::storage::git as storage;
|
||||
use crate::storage::SignRepository;
|
||||
|
|
@ -208,15 +209,18 @@ where
|
|||
T::Action: Serialize,
|
||||
{
|
||||
/// Update an object.
|
||||
pub fn update<G: Signer>(
|
||||
pub fn update<G>(
|
||||
&self,
|
||||
type_name: &TypeName,
|
||||
object_id: ObjectId,
|
||||
message: &str,
|
||||
actions: impl Into<NonEmpty<T::Action>>,
|
||||
embeds: Vec<Embed<Uri>>,
|
||||
signer: &G,
|
||||
) -> Result<Updated<T>, Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<Updated<T>, Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let actions = actions.into();
|
||||
let related = actions.iter().flat_map(T::Action::parents).collect();
|
||||
let changes = actions.try_map(encoding::encode)?;
|
||||
|
|
@ -251,13 +255,16 @@ where
|
|||
}
|
||||
|
||||
/// Create an object.
|
||||
pub fn create<G: Signer>(
|
||||
pub fn create<G>(
|
||||
&self,
|
||||
message: &str,
|
||||
actions: impl Into<NonEmpty<T::Action>>,
|
||||
embeds: Vec<Embed<Uri>>,
|
||||
signer: &G,
|
||||
) -> Result<(ObjectId, T), Error> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<(ObjectId, T), Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let actions = actions.into();
|
||||
let parents = actions.iter().flat_map(T::Action::parents).collect();
|
||||
let contents = actions.try_map(encoding::encode)?;
|
||||
|
|
@ -270,7 +277,7 @@ where
|
|||
})
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
let cob = cob::create::<T, _, G>(
|
||||
let cob = cob::create::<T, _, _>(
|
||||
self.repo,
|
||||
signer,
|
||||
self.identity,
|
||||
|
|
@ -296,7 +303,10 @@ where
|
|||
}
|
||||
|
||||
/// Remove an object.
|
||||
pub fn remove<G: Signer>(&self, id: &ObjectId, signer: &G) -> Result<(), Error> {
|
||||
pub fn remove<G>(&self, id: &ObjectId, signer: &Device<G>) -> Result<(), Error>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let name = git::refs::storage::cob(signer.public_key(), self.type_name, id);
|
||||
match self
|
||||
.repo
|
||||
|
|
@ -405,13 +415,13 @@ where
|
|||
pub fn initial<G, F, Tx>(
|
||||
message: &str,
|
||||
store: &mut Store<T, R>,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
operations: F,
|
||||
) -> Result<(ObjectId, T), Error>
|
||||
where
|
||||
Tx: From<Self>,
|
||||
Self: From<Tx>,
|
||||
G: Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
F: FnOnce(&mut Tx, &R) -> Result<(), Error>,
|
||||
R: ReadRepository + SignRepository + cob::Store,
|
||||
T::Action: Serialize + Clone,
|
||||
|
|
@ -469,16 +479,17 @@ where
|
|||
/// Commit transaction.
|
||||
///
|
||||
/// Returns an operation that can be applied onto an in-memory state.
|
||||
pub fn commit<G: Signer>(
|
||||
pub fn commit<G>(
|
||||
self,
|
||||
msg: &str,
|
||||
id: ObjectId,
|
||||
store: &mut Store<T, R>,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
) -> Result<(T, EntryId), Error>
|
||||
where
|
||||
R: ReadRepository + SignRepository + cob::Store,
|
||||
T::Action: Serialize + Clone,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let actions = NonEmpty::from_vec(self.actions)
|
||||
.expect("Transaction::commit: transaction must not be empty");
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use crate::git::ext::author::Author;
|
|||
use crate::git::ext::commit::headers::Headers;
|
||||
use crate::git::ext::commit::{trailers::OwnedTrailer, Commit};
|
||||
use crate::git::Oid;
|
||||
use crate::node::device::Device;
|
||||
use crate::prelude::Did;
|
||||
use crate::profile::env;
|
||||
use crate::storage::ReadRepository;
|
||||
|
|
@ -148,17 +149,17 @@ where
|
|||
|
||||
/// An object that can be used to create and sign operations.
|
||||
pub struct Actor<G> {
|
||||
pub signer: G,
|
||||
pub signer: Device<G>,
|
||||
}
|
||||
|
||||
impl<G: Default> Default for Actor<G> {
|
||||
impl<G: Default + Signer> Default for Actor<G> {
|
||||
fn default() -> Self {
|
||||
Self::new(G::default())
|
||||
Self::new(Device::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<G> Actor<G> {
|
||||
pub fn new(signer: G) -> Self {
|
||||
pub fn new(signer: Device<G>) -> Self {
|
||||
Self { signer }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -638,6 +638,7 @@ mod tests {
|
|||
use crate::cob::test;
|
||||
use crate::crypto::test::signer::MockSigner;
|
||||
use crate::crypto::Signer;
|
||||
use crate::node::device::Device;
|
||||
use crate::profile::env;
|
||||
use crate::test::arbitrary;
|
||||
use crate::test::arbitrary::gen;
|
||||
|
|
@ -651,13 +652,13 @@ mod tests {
|
|||
impl<G: Default + Signer> Default for Actor<G> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
inner: cob::test::Actor::new(G::default()),
|
||||
inner: cob::test::Actor::<G>::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Signer> Actor<G> {
|
||||
pub fn new(signer: G) -> Self {
|
||||
pub fn new(signer: Device<G>) -> Self {
|
||||
Self {
|
||||
inner: cob::test::Actor::new(signer),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,12 +249,11 @@ impl Canonical {
|
|||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
mod tests {
|
||||
use crypto::test::signer::MockSigner;
|
||||
use radicle_crypto::Signer;
|
||||
|
||||
use super::*;
|
||||
use crate::assert_matches;
|
||||
use crate::git;
|
||||
use crate::node::device::Device;
|
||||
use crate::test::fixtures;
|
||||
|
||||
/// Test helper to construct a Canonical and get the quorum
|
||||
|
|
@ -267,7 +266,7 @@ mod tests {
|
|||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, head)| {
|
||||
let signer = MockSigner::from_seed([(i + 1) as u8; 32]);
|
||||
let signer = Device::mock_from_seed([(i + 1) as u8; 32]);
|
||||
let did = Did::from(signer.public_key());
|
||||
(did, (*head).into())
|
||||
})
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ use crate::crypto;
|
|||
use crate::crypto::Signature;
|
||||
use crate::git;
|
||||
use crate::identity::{project::Project, Did};
|
||||
use crate::node::device::Device;
|
||||
use crate::storage;
|
||||
use crate::storage::{ReadRepository, RepositoryError};
|
||||
|
||||
|
|
@ -803,10 +804,10 @@ impl Doc {
|
|||
|
||||
/// [`Doc::encode`] and sign the [`Doc`], returning the set of bytes, its
|
||||
/// corresponding Git [`Oid`] and the [`Signature`] over the [`Oid`].
|
||||
pub fn sign<G: crypto::Signer>(
|
||||
&self,
|
||||
signer: &G,
|
||||
) -> Result<(git::Oid, Vec<u8>, Signature), DocError> {
|
||||
pub fn sign<G>(&self, signer: &G) -> Result<(git::Oid, Vec<u8>, Signature), DocError>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let (oid, bytes) = self.encode()?;
|
||||
let sig = signer.sign(oid.as_bytes());
|
||||
|
||||
|
|
@ -814,7 +815,10 @@ impl Doc {
|
|||
}
|
||||
|
||||
/// Similar to [`Doc::sign`], but only returning the [`Signature`].
|
||||
pub fn signature_of<G: crypto::Signer>(&self, signer: &G) -> Result<Signature, DocError> {
|
||||
pub fn signature_of<G>(&self, signer: &G) -> Result<Signature, DocError>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let (_, _, sig) = self.sign(signer)?;
|
||||
|
||||
Ok(sig)
|
||||
|
|
@ -835,11 +839,14 @@ impl Doc {
|
|||
|
||||
/// Initialize an [`identity::Identity`] with this [`Doc`] as the associated
|
||||
/// document.
|
||||
pub fn init<G: crypto::Signer>(
|
||||
pub fn init<G>(
|
||||
&self,
|
||||
repo: &storage::git::Repository,
|
||||
signer: &G,
|
||||
) -> Result<git::Oid, RepositoryError> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<git::Oid, RepositoryError>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let cob = identity::Identity::initialize(self, repo, signer)?;
|
||||
let id_ref = git::refs::storage::id(signer.public_key());
|
||||
let cob_ref = git::refs::storage::cob(
|
||||
|
|
@ -862,8 +869,6 @@ impl Doc {
|
|||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
mod test {
|
||||
use radicle_crypto::test::signer::MockSigner;
|
||||
use radicle_crypto::Signer as _;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::assert_matches;
|
||||
|
|
@ -880,7 +885,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_duplicate_dids() {
|
||||
let delegate = MockSigner::from_seed([0xff; 32]);
|
||||
let delegate = Device::mock_from_seed([0xff; 32]);
|
||||
let did = Did::from(delegate.public_key());
|
||||
let mut doc = RawDoc::new(gen::<Project>(1), vec![did], 1, Visibility::Public);
|
||||
doc.delegate(did);
|
||||
|
|
@ -1015,7 +1020,7 @@ mod test {
|
|||
|
||||
transport::local::register(storage.clone());
|
||||
|
||||
let delegate = MockSigner::from_seed([0xff; 32]);
|
||||
let delegate = Device::mock_from_seed([0xff; 32]);
|
||||
let (repo, _) = fixtures::repository(tempdir.path().join("working"));
|
||||
let (id, _, _) = rad::init(
|
||||
&repo,
|
||||
|
|
@ -1063,7 +1068,7 @@ mod test {
|
|||
|
||||
let (working, _) = fixtures::repository(tempdir.path().join("working"));
|
||||
|
||||
let delegate = MockSigner::from_seed([0xff; 32]);
|
||||
let delegate = Device::mock_from_seed([0xff; 32]);
|
||||
let (rid, doc, _) = rad::init(
|
||||
&working,
|
||||
"heartwood".try_into().unwrap(),
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ pub use storage::git::Storage;
|
|||
pub mod prelude {
|
||||
use super::*;
|
||||
|
||||
pub use crypto::{PublicKey, Signer, Verified};
|
||||
pub use crypto::{PublicKey, Verified};
|
||||
pub use identity::{project::Project, Did, Doc, RawDoc, RepoId};
|
||||
pub use node::{Alias, NodeId, Timestamp};
|
||||
pub use profile::Profile;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ mod features;
|
|||
pub mod address;
|
||||
pub mod config;
|
||||
pub mod db;
|
||||
pub mod device;
|
||||
pub mod events;
|
||||
pub mod notifications;
|
||||
pub mod policy;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,174 @@
|
|||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
|
||||
use crypto::{
|
||||
signature::{Signer, Verifier},
|
||||
ssh::ExtendedSignature,
|
||||
Signature,
|
||||
};
|
||||
|
||||
use crate::crypto;
|
||||
|
||||
use super::NodeId;
|
||||
|
||||
/// A `Device` identifies the local node through its [`NodeId`], and carries its
|
||||
/// signing mechanism.
|
||||
///
|
||||
/// The signing mechanism is for node specific cryptography, e.g. signing
|
||||
/// `rad/sigrefs`, COB commits, node messages, etc.
|
||||
///
|
||||
/// Note that a `Device` can create [`Signature`]s and [`ExtendedSignature`]s.
|
||||
/// It can achieve this as long as `S` implements `Signer<Signature>`.
|
||||
#[derive(Clone)]
|
||||
pub struct Device<S> {
|
||||
node: NodeId,
|
||||
signer: S,
|
||||
}
|
||||
|
||||
impl<S> fmt::Debug for Device<S> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Device")
|
||||
.field("node", &self.node.to_human())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: crypto::Signer + Default> Default for Device<S> {
|
||||
fn default() -> Self {
|
||||
Self::from(S::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Device<S> {
|
||||
/// Construct a new `Device`.
|
||||
pub fn new(node: NodeId, signer: S) -> Self {
|
||||
Self { node, signer }
|
||||
}
|
||||
|
||||
/// Return the [`NodeId`] of the `Device.`
|
||||
pub fn node_id(&self) -> &NodeId {
|
||||
&self.node
|
||||
}
|
||||
|
||||
/// Return the [`crypto::PublicKey`] of the `Device.`
|
||||
pub fn public_key(&self) -> &crypto::PublicKey {
|
||||
&self.node
|
||||
}
|
||||
|
||||
/// Convert the `Device` into its signer.
|
||||
///
|
||||
/// This consumes the `Device`.
|
||||
pub fn into_inner(self) -> S {
|
||||
self.signer
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test"))]
|
||||
impl Device<crypto::test::signer::MockSigner> {
|
||||
/// Construct a `Device` using a default `MockSigner` as the device signer.
|
||||
pub fn mock() -> Self {
|
||||
Device::from(crypto::test::signer::MockSigner::default())
|
||||
}
|
||||
|
||||
/// Construct a `Device`, constructing an RNG'd `MockSigner` for the signer.
|
||||
pub fn mock_rng(rng: &mut fastrand::Rng) -> Self {
|
||||
Device::from(crypto::test::signer::MockSigner::new(rng))
|
||||
}
|
||||
|
||||
/// Construct a `Device`, constructing a seeded `MockSigner` for the signer.
|
||||
pub fn mock_from_seed(seed: [u8; 32]) -> Self {
|
||||
Device::from(crypto::test::signer::MockSigner::from_seed(seed))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Signer<Signature> + 'static> Device<S> {
|
||||
/// Construct a [`BoxedDevice`] from a given `Device`.
|
||||
pub fn boxed(self) -> BoxedDevice {
|
||||
BoxedDevice(Device {
|
||||
node: self.node,
|
||||
signer: BoxedSigner(Box::new(self.signer)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Verifier<Signature> for Device<S> {
|
||||
fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), crypto::signature::Error> {
|
||||
self.node
|
||||
.verify(msg, signature)
|
||||
.map_err(crypto::signature::Error::from_source)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: crypto::Signer> From<S> for Device<S> {
|
||||
fn from(signer: S) -> Self {
|
||||
Self {
|
||||
node: *signer.public_key(),
|
||||
signer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: crypto::Signer + Clone> From<&S> for Device<S> {
|
||||
fn from(signer: &S) -> Self {
|
||||
Self::from(signer.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Signer<Signature>> Signer<Signature> for Device<S> {
|
||||
fn try_sign(&self, msg: &[u8]) -> Result<Signature, crypto::signature::Error> {
|
||||
self.signer.try_sign(msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Signer<Signature>> Signer<ExtendedSignature> for Device<S> {
|
||||
fn try_sign(&self, msg: &[u8]) -> Result<ExtendedSignature, crypto::signature::Error> {
|
||||
Ok(ExtendedSignature {
|
||||
key: *self.public_key(),
|
||||
sig: self.signer.try_sign(msg)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A `Signer<Signature>` that is packed in a [`Box`] for dynamic dispatch.
|
||||
pub struct BoxedSigner(Box<dyn Signer<Signature> + 'static>);
|
||||
|
||||
impl Signer<Signature> for BoxedSigner {
|
||||
fn try_sign(&self, msg: &[u8]) -> Result<Signature, crypto::signature::Error> {
|
||||
self.0.try_sign(msg)
|
||||
}
|
||||
}
|
||||
|
||||
/// A `Device` where the signer is a dynamic `Signer<Signature>`, in the form of
|
||||
/// a [`BoxedSigner`].
|
||||
///
|
||||
/// This can be constructed via [`Device::boxed`].
|
||||
pub struct BoxedDevice(Device<BoxedSigner>);
|
||||
|
||||
impl AsRef<Device<BoxedSigner>> for BoxedDevice {
|
||||
fn as_ref(&self) -> &Device<BoxedSigner> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for BoxedDevice {
|
||||
type Target = Device<BoxedSigner>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Signer<Signature> for BoxedDevice {
|
||||
fn try_sign(&self, msg: &[u8]) -> Result<Signature, crypto::signature::Error> {
|
||||
self.0.signer.try_sign(msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl Signer<ExtendedSignature> for BoxedDevice {
|
||||
fn try_sign(&self, msg: &[u8]) -> Result<ExtendedSignature, crypto::signature::Error> {
|
||||
Ok(ExtendedSignature {
|
||||
key: *self.0.public_key(),
|
||||
sig: self.0.signer.try_sign(msg)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,8 @@ use thiserror::Error;
|
|||
use crate::cob::migrate;
|
||||
use crate::crypto::ssh::agent::Agent;
|
||||
use crate::crypto::ssh::{keystore, Keystore, Passphrase};
|
||||
use crate::crypto::{PublicKey, Signer};
|
||||
use crate::crypto::PublicKey;
|
||||
use crate::node::device::{BoxedDevice, Device};
|
||||
use crate::node::policy::config::store::Read;
|
||||
use crate::node::{
|
||||
notifications, policy, policy::Scope, Alias, AliasStore, Handle as _, Node, UserAgent,
|
||||
|
|
@ -293,22 +294,22 @@ impl Profile {
|
|||
Did::from(self.public_key)
|
||||
}
|
||||
|
||||
pub fn signer(&self) -> Result<Box<dyn Signer>, Error> {
|
||||
pub fn signer(&self) -> Result<BoxedDevice, Error> {
|
||||
if !self.keystore.is_encrypted()? {
|
||||
let signer = keystore::MemorySigner::load(&self.keystore, None)?;
|
||||
return Ok(signer.boxed());
|
||||
return Ok(Device::from(signer).boxed());
|
||||
}
|
||||
|
||||
if let Some(passphrase) = env::passphrase() {
|
||||
let signer = keystore::MemorySigner::load(&self.keystore, Some(passphrase))?;
|
||||
return Ok(signer.boxed());
|
||||
return Ok(Device::from(signer).boxed());
|
||||
}
|
||||
|
||||
match Agent::connect() {
|
||||
Ok(agent) => {
|
||||
let signer = agent.signer(self.public_key);
|
||||
if signer.is_ready()? {
|
||||
Ok(signer.boxed())
|
||||
Ok(Device::from(signer).boxed())
|
||||
} else {
|
||||
Err(Error::KeyNotRegistered(self.public_key))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,12 @@ use once_cell::sync::Lazy;
|
|||
use thiserror::Error;
|
||||
|
||||
use crate::cob::ObjectId;
|
||||
use crate::crypto::{Signer, Verified};
|
||||
use crate::crypto::Verified;
|
||||
use crate::git;
|
||||
use crate::identity::doc;
|
||||
use crate::identity::doc::{DocError, RepoId, Visibility};
|
||||
use crate::identity::project::{Project, ProjectName};
|
||||
use crate::node::device::Device;
|
||||
use crate::storage::git::transport;
|
||||
use crate::storage::git::Repository;
|
||||
use crate::storage::refs::SignedRefs;
|
||||
|
|
@ -48,15 +49,19 @@ pub enum InitError {
|
|||
}
|
||||
|
||||
/// Initialize a new radicle project from a git repository.
|
||||
pub fn init<G: Signer, S: WriteStorage>(
|
||||
pub fn init<G, S>(
|
||||
repo: &git2::Repository,
|
||||
name: ProjectName,
|
||||
description: &str,
|
||||
default_branch: BranchName,
|
||||
visibility: Visibility,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
storage: S,
|
||||
) -> Result<(RepoId, identity::Doc, SignedRefs<Verified>), InitError> {
|
||||
) -> Result<(RepoId, identity::Doc, SignedRefs<Verified>), InitError>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
S: WriteStorage,
|
||||
{
|
||||
// TODO: Better error when project id already exists in storage, but remote doesn't.
|
||||
let delegate: identity::Did = signer.public_key().into();
|
||||
let proj = Project::new(
|
||||
|
|
@ -98,10 +103,10 @@ fn init_configure<G>(
|
|||
default_branch: &BranchName,
|
||||
url: &git::Url,
|
||||
identity: git::Oid,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
) -> Result<SignedRefs<Verified>, InitError>
|
||||
where
|
||||
G: crypto::Signer,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let pk = signer.public_key();
|
||||
|
||||
|
|
@ -162,12 +167,16 @@ pub enum ForkError {
|
|||
}
|
||||
|
||||
/// Create a local tree for an existing project, from an existing remote.
|
||||
pub fn fork_remote<G: Signer, S: storage::WriteStorage>(
|
||||
pub fn fork_remote<G, S>(
|
||||
proj: RepoId,
|
||||
remote: &RemoteId,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
storage: S,
|
||||
) -> Result<(), ForkError> {
|
||||
) -> Result<(), ForkError>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
S: storage::WriteStorage,
|
||||
{
|
||||
// TODO: Copy tags over?
|
||||
|
||||
// Creates or copies the following references:
|
||||
|
|
@ -197,11 +206,11 @@ pub fn fork_remote<G: Signer, S: storage::WriteStorage>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn fork<G: Signer, S: storage::WriteStorage>(
|
||||
rid: RepoId,
|
||||
signer: &G,
|
||||
storage: &S,
|
||||
) -> Result<(), ForkError> {
|
||||
pub fn fork<G, S>(rid: RepoId, signer: &Device<G>, storage: &S) -> Result<(), ForkError>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
S: storage::WriteStorage,
|
||||
{
|
||||
let me = signer.public_key();
|
||||
let repository = storage.repository_mut(rid)?;
|
||||
let (canonical_branch, canonical_head) = repository.head()?;
|
||||
|
|
@ -451,7 +460,6 @@ mod tests {
|
|||
use std::collections::HashMap;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use radicle_crypto::test::signer::MockSigner;
|
||||
|
||||
use crate::git::{name::component, qualified};
|
||||
use crate::identity::Did;
|
||||
|
|
@ -465,7 +473,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_init() {
|
||||
let tempdir = tempfile::tempdir().unwrap();
|
||||
let signer = MockSigner::default();
|
||||
let signer = Device::mock();
|
||||
let public_key = *signer.public_key();
|
||||
let storage = Storage::open(tempdir.path().join("storage"), fixtures::user()).unwrap();
|
||||
|
||||
|
|
@ -518,8 +526,8 @@ mod tests {
|
|||
fn test_fork() {
|
||||
let mut rng = fastrand::Rng::new();
|
||||
let tempdir = tempfile::tempdir().unwrap();
|
||||
let alice = MockSigner::new(&mut rng);
|
||||
let bob = MockSigner::new(&mut rng);
|
||||
let alice = Device::mock_rng(&mut rng);
|
||||
let bob = Device::mock_rng(&mut rng);
|
||||
let bob_id = bob.public_key();
|
||||
let storage = Storage::open(tempdir.path().join("storage"), fixtures::user()).unwrap();
|
||||
|
||||
|
|
@ -556,7 +564,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_checkout() {
|
||||
let tempdir = tempfile::tempdir().unwrap();
|
||||
let signer = MockSigner::default();
|
||||
let signer = Device::mock();
|
||||
let remote_id = signer.public_key();
|
||||
let storage = Storage::open(tempdir.path().join("storage"), fixtures::user()).unwrap();
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use nonempty::NonEmpty;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
use crypto::{PublicKey, Signer, Unverified, Verified};
|
||||
use crypto::{PublicKey, Unverified, Verified};
|
||||
pub use git::{Validation, Validations};
|
||||
pub use radicle_git_ext::Oid;
|
||||
|
||||
|
|
@ -21,6 +21,7 @@ use crate::git::{refspec::Refspec, PatternString, Qualified, RefError, RefStr, R
|
|||
use crate::identity::{Did, PayloadError};
|
||||
use crate::identity::{Doc, DocAt, DocError};
|
||||
use crate::identity::{Identity, RepoId};
|
||||
use crate::node::device::Device;
|
||||
use crate::node::SyncedAt;
|
||||
use crate::storage::git::NAMESPACES_GLOB;
|
||||
use crate::storage::refs::Refs;
|
||||
|
|
@ -657,7 +658,9 @@ pub trait WriteRepository: ReadRepository + SignRepository {
|
|||
/// Allows signing refs.
|
||||
pub trait SignRepository {
|
||||
/// Sign the repository's refs under the `refs/rad/sigrefs` branch.
|
||||
fn sign_refs<G: Signer>(&self, signer: &G) -> Result<SignedRefs<Verified>, RepositoryError>;
|
||||
fn sign_refs<G>(&self, signer: &Device<G>) -> Result<SignedRefs<Verified>, RepositoryError>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>;
|
||||
}
|
||||
|
||||
impl<T, S> ReadStorage for T
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use std::ops::{Deref, DerefMut};
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::{fs, io};
|
||||
|
||||
use crypto::{Signer, Verified};
|
||||
use crypto::Verified;
|
||||
use once_cell::sync::Lazy;
|
||||
use tempfile::TempDir;
|
||||
|
||||
|
|
@ -15,6 +15,7 @@ use crate::git::canonical::Canonical;
|
|||
use crate::identity::doc::DocError;
|
||||
use crate::identity::{Doc, DocAt, RepoId};
|
||||
use crate::identity::{Identity, Project};
|
||||
use crate::node::device::Device;
|
||||
use crate::node::SyncedAt;
|
||||
use crate::storage::refs;
|
||||
use crate::storage::refs::{Refs, SignedRefs, SignedRefsAt};
|
||||
|
|
@ -433,11 +434,15 @@ impl Repository {
|
|||
}
|
||||
|
||||
/// Create the repository's identity branch.
|
||||
pub fn init<G: Signer, S: WriteStorage>(
|
||||
pub fn init<G, S>(
|
||||
doc: &Doc,
|
||||
storage: &S,
|
||||
signer: &G,
|
||||
) -> Result<(Self, git::Oid), RepositoryError> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<(Self, git::Oid), RepositoryError>
|
||||
where
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
S: WriteStorage,
|
||||
{
|
||||
let (doc_oid, doc_bytes) = doc.encode()?;
|
||||
let id = RepoId::from(doc_oid);
|
||||
let repo = Self::create(paths::repository(storage, &id), id, storage.info())?;
|
||||
|
|
@ -885,7 +890,10 @@ impl WriteRepository for Repository {
|
|||
}
|
||||
|
||||
impl SignRepository for Repository {
|
||||
fn sign_refs<G: Signer>(&self, signer: &G) -> Result<SignedRefs<Verified>, RepositoryError> {
|
||||
fn sign_refs<G: crypto::signature::Signer<crypto::Signature>>(
|
||||
&self,
|
||||
signer: &Device<G>,
|
||||
) -> Result<SignedRefs<Verified>, RepositoryError> {
|
||||
let remote = signer.public_key();
|
||||
// Ensure the root reference is set, which is checked during sigref verification.
|
||||
if self.identity_root_of(remote).is_err() {
|
||||
|
|
@ -959,7 +967,6 @@ pub mod paths {
|
|||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
mod tests {
|
||||
use crypto::test::signer::MockSigner;
|
||||
|
||||
use super::*;
|
||||
use crate::git;
|
||||
|
|
@ -970,7 +977,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_remote_refs() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let signer = MockSigner::default();
|
||||
let signer = Device::mock();
|
||||
let storage = fixtures::storage(dir.path(), &signer).unwrap();
|
||||
let inv = storage.repositories().unwrap();
|
||||
let proj = inv.first().unwrap();
|
||||
|
|
@ -996,7 +1003,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_references_of() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let signer = MockSigner::default();
|
||||
let signer = Device::mock();
|
||||
let storage = Storage::open(tmp.path().join("storage"), fixtures::user()).unwrap();
|
||||
|
||||
transport::local::register(storage.clone());
|
||||
|
|
@ -1031,7 +1038,7 @@ mod tests {
|
|||
fn test_sign_refs() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let mut rng = fastrand::Rng::new();
|
||||
let signer = MockSigner::new(&mut rng);
|
||||
let signer = Device::mock_rng(&mut rng);
|
||||
let storage = Storage::open(tmp.path(), fixtures::user()).unwrap();
|
||||
let alice = *signer.public_key();
|
||||
let (rid, _, working, _) =
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use std::collections::BTreeMap;
|
|||
use std::path::Path;
|
||||
|
||||
use cob::object::Objects;
|
||||
use cob::signatures::ExtendedSignature;
|
||||
use radicle_cob as cob;
|
||||
use radicle_cob::change;
|
||||
use storage::RemoteRepository;
|
||||
|
|
@ -11,6 +12,9 @@ use storage::SignRepository;
|
|||
use storage::ValidateRepository;
|
||||
|
||||
use crate::git::*;
|
||||
use crate::identity;
|
||||
use crate::identity::doc::DocError;
|
||||
use crate::node::device::Device;
|
||||
use crate::storage;
|
||||
use crate::storage::Error;
|
||||
use crate::storage::{
|
||||
|
|
@ -68,7 +72,7 @@ impl change::Storage for Repository {
|
|||
spec: change::Template<Self::ObjectId>,
|
||||
) -> Result<cob::Entry, Self::StoreError>
|
||||
where
|
||||
Signer: crypto::Signer,
|
||||
Signer: crypto::signature::Signer<ExtendedSignature>,
|
||||
{
|
||||
self.backend.store(authority, parents, signer, spec)
|
||||
}
|
||||
|
|
@ -208,7 +212,7 @@ impl<R: storage::WriteRepository> change::Storage for DraftStore<'_, R> {
|
|||
spec: change::Template<Self::ObjectId>,
|
||||
) -> Result<cob::Entry, Self::StoreError>
|
||||
where
|
||||
Signer: crypto::Signer,
|
||||
Signer: crypto::signature::Signer<ExtendedSignature>,
|
||||
{
|
||||
self.repo.raw().store(authority, parents, signer, spec)
|
||||
}
|
||||
|
|
@ -222,10 +226,13 @@ impl<R: storage::WriteRepository> change::Storage for DraftStore<'_, R> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: storage::ReadRepository> SignRepository for DraftStore<'_, R> {
|
||||
fn sign_refs<G: crypto::Signer>(
|
||||
impl<R> SignRepository for DraftStore<'_, R>
|
||||
where
|
||||
R: storage::ReadRepository,
|
||||
{
|
||||
fn sign_refs<G: crypto::signature::Signer<crypto::Signature>>(
|
||||
&self,
|
||||
signer: &G,
|
||||
signer: &Device<G>,
|
||||
) -> Result<storage::refs::SignedRefs<Verified>, RepositoryError> {
|
||||
// Since this is a draft store, we do not actually want to sign the refs.
|
||||
// Instead, we just return the existing signed refs.
|
||||
|
|
|
|||
|
|
@ -7,13 +7,15 @@ use std::ops::{Deref, DerefMut};
|
|||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crypto::{PublicKey, Signature, Signer, SignerError, Unverified, Verified};
|
||||
use crypto::signature::Signer;
|
||||
use crypto::{PublicKey, Signature, Unverified, Verified};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::git;
|
||||
use crate::git::ext as git_ext;
|
||||
use crate::git::Oid;
|
||||
use crate::node::device::Device;
|
||||
use crate::profile::env;
|
||||
use crate::storage;
|
||||
use crate::storage::{ReadRepository, RemoteId, RepoId, WriteRepository};
|
||||
|
|
@ -39,7 +41,7 @@ pub enum Error {
|
|||
#[error("invalid signature: {0}")]
|
||||
InvalidSignature(#[from] crypto::Error),
|
||||
#[error("signer error: {0}")]
|
||||
Signer(#[from] SignerError),
|
||||
Signer(#[from] crypto::signature::Error),
|
||||
#[error("canonical refs: {0}")]
|
||||
Canonical(#[from] canonical::Error),
|
||||
#[error("invalid reference")]
|
||||
|
|
@ -88,15 +90,15 @@ impl Refs {
|
|||
}
|
||||
|
||||
/// Sign these refs with the given signer and return [`SignedRefs`].
|
||||
pub fn signed<G>(self, signer: &G) -> Result<SignedRefs<Unverified>, Error>
|
||||
pub fn signed<G>(self, device: &Device<G>) -> Result<SignedRefs<Unverified>, Error>
|
||||
where
|
||||
G: Signer,
|
||||
G: Signer<crypto::Signature>,
|
||||
{
|
||||
let refs = self;
|
||||
let msg = refs.canonical();
|
||||
let signature = signer.try_sign(&msg)?;
|
||||
let signature = device.try_sign(&msg)?;
|
||||
|
||||
Ok(SignedRefs::new(refs, *signer.public_key(), signature))
|
||||
Ok(SignedRefs::new(refs, *device.public_key(), signature))
|
||||
}
|
||||
|
||||
/// Get a particular ref.
|
||||
|
|
@ -464,7 +466,6 @@ pub mod canonical {
|
|||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
mod tests {
|
||||
use crypto::test::signer::MockSigner;
|
||||
use qcheck_macros::quickcheck;
|
||||
use storage::{git::transport, RemoteRepository, SignRepository, WriteStorage};
|
||||
|
||||
|
|
@ -490,8 +491,8 @@ mod tests {
|
|||
// `paris` repo. We also don't expected the signed refs to validate without error.
|
||||
fn test_rid_verification() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let alice = MockSigner::default();
|
||||
let bob = MockSigner::default();
|
||||
let alice = Device::mock();
|
||||
let bob = Device::mock();
|
||||
let storage = &Storage::open(tmp.path().join("storage"), fixtures::user()).unwrap();
|
||||
|
||||
transport::local::register(storage.clone());
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ pub mod setup {
|
|||
|
||||
use super::storage::{Namespaces, RefUpdate};
|
||||
use crate::crypto::test::signer::MockSigner;
|
||||
use crate::node::device::Device;
|
||||
use crate::storage::git::transport::remote;
|
||||
use crate::{
|
||||
git,
|
||||
|
|
@ -91,7 +92,7 @@ pub mod setup {
|
|||
pub tmp: TempDir,
|
||||
pub root: PathBuf,
|
||||
pub storage: Storage,
|
||||
pub signer: MockSigner,
|
||||
pub signer: Device<MockSigner>,
|
||||
}
|
||||
|
||||
impl Default for Node {
|
||||
|
|
@ -104,6 +105,7 @@ pub mod setup {
|
|||
|
||||
impl Node {
|
||||
pub fn new(tmp: TempDir, signer: MockSigner, alias: &str) -> Self {
|
||||
let signer = Device::from(signer);
|
||||
let root = tmp.path().to_path_buf();
|
||||
let home = root.join("home");
|
||||
let paths = Home::new(home.as_path()).unwrap();
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ impl Arbitrary for storage::Remote<crypto::Unverified> {
|
|||
fn arbitrary(g: &mut qcheck::Gen) -> Self {
|
||||
let refs = Refs::arbitrary(g);
|
||||
let signer = MockSigner::arbitrary(g);
|
||||
let signed = refs.signed(&signer).unwrap();
|
||||
let signed = refs.signed(&signer.into()).unwrap();
|
||||
|
||||
storage::Remote::<crypto::Unverified>::new(signed)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::crypto::{PublicKey, Signer, Verified};
|
||||
use crate::crypto::{PublicKey, Verified};
|
||||
use crate::git;
|
||||
use crate::identity::doc::Visibility;
|
||||
use crate::identity::RepoId;
|
||||
use crate::node::device::Device;
|
||||
use crate::node::Alias;
|
||||
use crate::rad;
|
||||
use crate::storage::git::transport;
|
||||
|
|
@ -23,7 +24,11 @@ pub fn user() -> git::UserInfo {
|
|||
}
|
||||
|
||||
/// Create a new storage with a project.
|
||||
pub fn storage<P: AsRef<Path>, G: Signer>(path: P, signer: &G) -> Result<Storage, rad::InitError> {
|
||||
pub fn storage<P, G>(path: P, signer: &Device<G>) -> Result<Storage, rad::InitError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
let path = path.as_ref();
|
||||
let storage = Storage::open(
|
||||
path.join("storage"),
|
||||
|
|
@ -57,11 +62,15 @@ pub fn storage<P: AsRef<Path>, G: Signer>(path: P, signer: &G) -> Result<Storage
|
|||
}
|
||||
|
||||
/// Create a new repository at the given path, and initialize it into a project.
|
||||
pub fn project<P: AsRef<Path>, G: Signer>(
|
||||
pub fn project<P, G>(
|
||||
path: P,
|
||||
storage: &Storage,
|
||||
signer: &G,
|
||||
) -> Result<(RepoId, SignedRefs<Verified>, git2::Repository, git2::Oid), rad::InitError> {
|
||||
signer: &Device<G>,
|
||||
) -> Result<(RepoId, SignedRefs<Verified>, git2::Repository, git2::Oid), rad::InitError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
transport::local::register(storage.clone());
|
||||
|
||||
let (working, head) = repository(path);
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@ use std::str::FromStr;
|
|||
|
||||
use git_ext::ref_format as fmt;
|
||||
|
||||
use crate::crypto::{Signer, Verified};
|
||||
use crate::crypto::Verified;
|
||||
use crate::identity::doc::{Doc, DocAt, DocError, RawDoc, RepoId};
|
||||
use crate::node::device::Device;
|
||||
use crate::node::NodeId;
|
||||
|
||||
pub use crate::storage::*;
|
||||
|
|
@ -347,9 +348,9 @@ impl WriteRepository for MockRepository {
|
|||
}
|
||||
|
||||
impl SignRepository for MockRepository {
|
||||
fn sign_refs<G: Signer>(
|
||||
fn sign_refs<G: crypto::signature::Signer<crypto::Signature>>(
|
||||
&self,
|
||||
_signer: &G,
|
||||
_signer: &Device<G>,
|
||||
) -> Result<crate::storage::refs::SignedRefs<Verified>, RepositoryError> {
|
||||
todo!()
|
||||
}
|
||||
|
|
@ -419,7 +420,7 @@ impl radicle_cob::change::Storage for MockRepository {
|
|||
Self::StoreError,
|
||||
>
|
||||
where
|
||||
G: radicle_crypto::Signer,
|
||||
G: radicle_crypto::signature::Signer<Self::Signatures>,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue