rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 heartwood9988b63bb204496cab00b584cca60ecb7beeb036
{
"request": "trigger",
"version": 1,
"event_type": "patch",
"repository": {
"id": "rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5",
"name": "heartwood",
"description": "Radicle Heartwood Protocol & Stack",
"private": false,
"default_branch": "master",
"delegates": [
"did:key:z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT",
"did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz"
]
},
"action": "Created",
"patch": {
"id": "8487370caba29949e561f8ad62ee4f086675abc6",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": null
},
"title": "Refactor signing and public key usage",
"state": {
"status": "merged",
"conflicts": []
},
"before": "41f9048d96596473600e6c77f041d29cd3de1d6f",
"after": "9988b63bb204496cab00b584cca60ecb7beeb036",
"commits": [
"9988b63bb204496cab00b584cca60ecb7beeb036",
"1f4fcc5e6a3dadef99284bc3845ada11c74ccd97"
],
"target": "3fc92f83fe089263c470b328414d923227cf0e4a",
"labels": [],
"assignees": [],
"revisions": [
{
"id": "8487370caba29949e561f8ad62ee4f086675abc6",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": null
},
"description": "This patch aims to refactor some of our usage of `PublicKey` and `Signer`. The\n\nmotivation for this is to help make way for thinking about agent repositories \u2013\nand, in the future, multi-device.\n\nThe main motivation is to help gain some separation when we want to think about\na node (or device) signing artifacts, and when will want to think about an agent\nsigning artifacts.\n\nThe reason for moving away from `radicle_crypto::Signer` and towards\n`signature::Signer` is because the former ties itself very closely to the\n`PublicKey`. In the future, we will want signers that are not necessarily a\nsingle `PublicKey` \u2013 but may come from many device, for example.\n\nHowever, there are places where we do want this association, and that is why the\n`Device` type is introduced.\n\nFurthermore, we also want to try help separate when a `PublicKey` is required\nfor verifying and when it used for namespacing in the Git repository. For now,\nthis separation is only done by abstracting away the namespacing for the COB\nstorage.\n\nSee the commits for more details on the changes.",
"base": "d9c76893a144fd787654613f2bfb919613014a71",
"oid": "bf6c0da0787df5851f3272374a6ac735f4f241d4",
"timestamp": 1733489271
},
{
"id": "7a593779505442edb1bfd200d0ca44451e32a3fa",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": null
},
"description": "Work in Erik's feedback.",
"base": "f30760d6bb86d2978a5ed4df8ee45b9aa97778b4",
"oid": "10de6a74beb82db60d131895e4b43c152e1bb2c1",
"timestamp": 1746036405
},
{
"id": "5dffeb1d1b107de6ec4b6ebb466dfd1361c466d9",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": null
},
"description": "Changes:\n- Rebased\n- Removed rogue `stream/error.rs` file",
"base": "41f9048d96596473600e6c77f041d29cd3de1d6f",
"oid": "9988b63bb204496cab00b584cca60ecb7beeb036",
"timestamp": 1747898222
}
]
}
}
{
"response": "triggered",
"run_id": {
"id": "e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0"
},
"info_url": "https://cci.rad.levitte.org//e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0.html"
}
Started at: 2026-09-07 23:56:10.281675+02:00
Commands:
$ rad clone rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 .
✓ Creating checkout in ./...
✓ Remote cloudhead@z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT added
✓ Remote-tracking branch cloudhead@z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT/master created for z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT
✓ Remote fintohaps@z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM added
✓ Remote-tracking branch fintohaps@z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM/master created for z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM
✓ Remote lorenz@z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz added
✓ Remote-tracking branch lorenz@z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz/master created for z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz
✓ Repository successfully cloned under /opt/radcis/ci.rad.levitte.org/cci/state/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/
╭────────────────────────────────────╮
│ heartwood │
│ Radicle Heartwood Protocol & Stack │
│ 189 issues · 38 patches │
╰────────────────────────────────────╯
Run `cd ./.` to go to the repository directory.
Exit code: 0
$ rad patch checkout 8487370caba29949e561f8ad62ee4f086675abc6
✓ Switched to branch patch/8487370 at revision 5dffeb1
✓ Branch patch/8487370 setup to track rad/patches/8487370caba29949e561f8ad62ee4f086675abc6
Exit code: 0
$ git config advice.detachedHead false
Exit code: 0
$ git checkout 9988b63bb204496cab00b584cca60ecb7beeb036
HEAD is now at 9988b63b cob: abstract namespace identifier
Exit code: 0
$ rad patch show 8487370caba29949e561f8ad62ee4f086675abc6 -p
╭──────────────────────────────────────────────────────────────────────────────────╮
│ Title Refactor signing and public key usage │
│ Patch 8487370caba29949e561f8ad62ee4f086675abc6 │
│ Author fintohaps z6Mkire…SQZ3voM │
│ Head 9988b63bb204496cab00b584cca60ecb7beeb036 │
│ Base 41f9048d96596473600e6c77f041d29cd3de1d6f │
│ Target master │
│ Branches patch/8487370 │
│ Commits ahead 0, behind 1067 │
│ Status merged │
│ │
│ This patch aims to refactor some of our usage of `PublicKey` and `Signer`. The │
│ │
│ motivation for this is to help make way for thinking about agent repositories – │
│ and, in the future, multi-device. │
│ │
│ The main motivation is to help gain some separation when we want to think about │
│ a node (or device) signing artifacts, and when will want to think about an agent │
│ signing artifacts. │
│ │
│ The reason for moving away from `radicle_crypto::Signer` and towards │
│ `signature::Signer` is because the former ties itself very closely to the │
│ `PublicKey`. In the future, we will want signers that are not necessarily a │
│ single `PublicKey` – but may come from many device, for example. │
│ │
│ However, there are places where we do want this association, and that is why the │
│ `Device` type is introduced. │
│ │
│ Furthermore, we also want to try help separate when a `PublicKey` is required │
│ for verifying and when it used for namespacing in the Git repository. For now, │
│ this separation is only done by abstracting away the namespacing for the COB │
│ storage. │
│ │
│ See the commits for more details on the changes. │
├──────────────────────────────────────────────────────────────────────────────────┤
│ 9988b63 cob: abstract namespace identifier │
│ 1f4fcc5 radicle: move to `signature` crate │
├──────────────────────────────────────────────────────────────────────────────────┤
│ ● Revision 8487370 @ d9c7689..bf6c0da by fintohaps z6Mkire…SQZ3voM 1 year ago │
│ └─ ⋄ reviewed by erikli z6MkgFq…FGAnBGz 1 year ago │
│ ↑ Revision 7a59377 @ f30760d..10de6a7 by lorenz z6MkkPv…WX5sTEz 1 year ago │
│ ↑ Revision 5dffeb1 @ 41f9048..9988b63 by fintohaps z6Mkire…SQZ3voM 1 year ago │
│ └─ ✓ merged by lorenz z6MkkPv…WX5sTEz │
╰──────────────────────────────────────────────────────────────────────────────────╯
commit 9988b63bb204496cab00b584cca60ecb7beeb036
Author: Fintan Halpenny <fintan.halpenny@gmail.com>
Date: Tue Dec 3 13:23:01 2024 +0000
cob: abstract namespace identifier
This change is to help separate the fact the namespacing by a `NodeId` is a
Radicle specific concern. The identifier can, in theory, be any kind of path
component, as long it is valid in Git.
The motivation for the change is to help separate the idea of a device's
`NodeId` vs an agent being an author – for future agent repository work.
diff --git a/radicle-cli/src/commands/id.rs b/radicle-cli/src/commands/id.rs
index 2b70f2930..9625cbdbb 100644
--- a/radicle-cli/src/commands/id.rs
+++ b/radicle-cli/src/commands/id.rs
@@ -7,6 +7,7 @@ use anyhow::{anyhow, Context};
use radicle::cob::identity::{self, IdentityMut, Revision, RevisionId};
use radicle::identity::{doc, Doc, Identity, PayloadError, RawDoc, Visibility};
use radicle::node::device::Device;
+use radicle::node::NodeId;
use radicle::prelude::{Did, RepoId};
use radicle::storage::refs;
use radicle::storage::{ReadRepository, ReadStorage as _, WriteRepository};
@@ -731,7 +732,7 @@ fn update<R, G>(
signer: &Device<G>,
) -> anyhow::Result<Revision>
where
- R: WriteRepository + cob::Store,
+ R: WriteRepository + cob::Store<Namespace = NodeId>,
G: crypto::signature::Signer<crypto::Signature>,
{
if let Some((title, description)) = edit_title_description(title, description)? {
diff --git a/radicle-cli/src/commands/inbox.rs b/radicle-cli/src/commands/inbox.rs
index 8edfcb609..4b91adec9 100644
--- a/radicle-cli/src/commands/inbox.rs
+++ b/radicle-cli/src/commands/inbox.rs
@@ -12,7 +12,7 @@ use radicle::issue::cache::Issues as _;
use radicle::node::notifications;
use radicle::node::notifications::*;
use radicle::patch::cache::Patches as _;
-use radicle::prelude::{Profile, RepoId};
+use radicle::prelude::{NodeId, Profile, RepoId};
use radicle::storage::{BranchName, ReadRepository, ReadStorage};
use radicle::{cob, git, Storage};
@@ -261,7 +261,7 @@ fn list_repo<'a, R: ReadStorage>(
profile: &Profile,
) -> anyhow::Result<Option<term::VStack<'a>>>
where
- <R as ReadStorage>::Repository: cob::Store,
+ <R as ReadStorage>::Repository: cob::Store<Namespace = NodeId>,
{
let mut table = term::Table::new(term::TableOptions {
spacing: 3,
diff --git a/radicle-cli/src/commands/issue.rs b/radicle-cli/src/commands/issue.rs
index 9332ace75..4a853d190 100644
--- a/radicle-cli/src/commands/issue.rs
+++ b/radicle-cli/src/commands/issue.rs
@@ -13,6 +13,7 @@ use radicle::cob::{issue, thread};
use radicle::crypto;
use radicle::issue::cache::Issues as _;
use radicle::node::device::Device;
+use radicle::node::NodeId;
use radicle::prelude::{Did, RepoId};
use radicle::profile;
use radicle::storage;
@@ -815,7 +816,7 @@ fn open<R, G>(
profile: &Profile,
) -> anyhow::Result<()>
where
- R: ReadRepository + WriteRepository + cob::Store,
+ R: ReadRepository + WriteRepository + cob::Store<Namespace = NodeId>,
G: crypto::signature::Signer<crypto::Signature>,
{
let (title, description) = if let (Some(t), Some(d)) = (title.as_ref(), description.as_ref()) {
@@ -849,7 +850,7 @@ fn edit<'a, 'g, R, G>(
signer: &Device<G>,
) -> anyhow::Result<issue::IssueMut<'a, 'g, R, cob::cache::StoreWriter>>
where
- R: WriteRepository + ReadRepository + cob::Store,
+ R: WriteRepository + ReadRepository + cob::Store<Namespace = NodeId>,
G: crypto::signature::Signer<crypto::Signature>,
{
let id = id.resolve(&repo.backend)?;
@@ -891,7 +892,7 @@ where
}
/// Get a comment from the user, by prompting.
-pub fn prompt_comment<R: WriteRepository + radicle::cob::Store>(
+pub fn prompt_comment<R: WriteRepository + radicle::cob::Store<Namespace = NodeId>>(
message: Message,
reply_to: Option<Rev>,
issue: &issue::Issue,
diff --git a/radicle-cli/src/commands/job.rs b/radicle-cli/src/commands/job.rs
index ce6f1f7a0..a8b36194d 100644
--- a/radicle-cli/src/commands/job.rs
+++ b/radicle-cli/src/commands/job.rs
@@ -6,7 +6,7 @@ use anyhow::{anyhow, Context as _};
use radicle::cob::job::{JobStore, Reason, State};
use radicle::crypto;
use radicle::node::device::Device;
-use radicle::node::Handle;
+use radicle::node::{Handle, NodeId};
use radicle::storage::{WriteRepository, WriteStorage};
use radicle::{cob, Node};
@@ -269,7 +269,7 @@ fn trigger<R, G>(
quiet: bool,
) -> anyhow::Result<()>
where
- R: WriteRepository + cob::Store,
+ R: WriteRepository + cob::Store<Namespace = NodeId>,
G: crypto::signature::Signer<crypto::Signature>,
{
let commit = commit.resolve(&repo.backend)?;
@@ -289,7 +289,7 @@ fn start<R, G>(
signer: &Device<G>,
) -> anyhow::Result<()>
where
- R: WriteRepository + cob::Store,
+ R: WriteRepository + cob::Store<Namespace = NodeId>,
G: crypto::signature::Signer<crypto::Signature>,
{
let job_id = job_id.resolve(&repo.backend)?;
@@ -301,7 +301,9 @@ where
}
// TODO: This should use the COB cache for performance.
-fn list<R: WriteRepository + cob::Store>(store: &JobStore<R>) -> anyhow::Result<()> {
+fn list<R: WriteRepository + cob::Store<Namespace = NodeId>>(
+ store: &JobStore<R>,
+) -> anyhow::Result<()> {
if store.is_empty()? {
term::print(term::format::italic("Nothing to show."));
return Ok(());
@@ -343,7 +345,7 @@ fn list<R: WriteRepository + cob::Store>(store: &JobStore<R>) -> anyhow::Result<
Ok(())
}
-fn show<R: WriteRepository + cob::Store>(
+fn show<R: WriteRepository + cob::Store<Namespace = NodeId>>(
job_id: &Rev,
store: &JobStore<R>,
repo: &radicle::storage::git::Repository,
@@ -366,7 +368,7 @@ fn finish<R, G>(
signer: &Device<G>,
) -> anyhow::Result<()>
where
- R: WriteRepository + cob::Store,
+ R: WriteRepository + cob::Store<Namespace = NodeId>,
G: crypto::signature::Signer<crypto::Signature>,
{
let job_id = job_id.resolve(&repo.backend)?;
diff --git a/radicle-cli/src/terminal/cob.rs b/radicle-cli/src/terminal/cob.rs
index ba628757f..d43f932cf 100644
--- a/radicle-cli/src/terminal/cob.rs
+++ b/radicle-cli/src/terminal/cob.rs
@@ -3,6 +3,7 @@ use radicle::{
self,
cache::{MigrateCallback, MigrateProgress},
},
+ prelude::NodeId,
profile,
storage::ReadRepository,
Profile,
@@ -61,7 +62,7 @@ pub fn patches<'a, R>(
repository: &'a R,
) -> Result<cob::patch::Cache<cob::patch::Patches<'a, R>, cob::cache::StoreReader>, anyhow::Error>
where
- R: ReadRepository + cob::Store,
+ R: ReadRepository + cob::Store<Namespace = NodeId>,
{
profile.patches(repository).map_err(with_hint)
}
@@ -72,7 +73,7 @@ pub fn patches_mut<'a, R>(
repository: &'a R,
) -> Result<cob::patch::Cache<cob::patch::Patches<'a, R>, cob::cache::StoreWriter>, anyhow::Error>
where
- R: ReadRepository + cob::Store,
+ R: ReadRepository + cob::Store<Namespace = NodeId>,
{
profile.patches_mut(repository).map_err(with_hint)
}
@@ -83,7 +84,7 @@ pub fn issues<'a, R>(
repository: &'a R,
) -> Result<cob::issue::Cache<cob::issue::Issues<'a, R>, cob::cache::StoreReader>, anyhow::Error>
where
- R: ReadRepository + cob::Store,
+ R: ReadRepository + cob::Store<Namespace = NodeId>,
{
profile.issues(repository).map_err(with_hint)
}
@@ -94,7 +95,7 @@ pub fn issues_mut<'a, R>(
repository: &'a R,
) -> Result<cob::issue::Cache<cob::issue::Issues<'a, R>, cob::cache::StoreWriter>, anyhow::Error>
where
- R: ReadRepository + cob::Store,
+ R: ReadRepository + cob::Store<Namespace = NodeId>,
{
profile.issues_mut(repository).map_err(with_hint)
}
diff --git a/radicle-cob/Cargo.toml b/radicle-cob/Cargo.toml
index 97847c72d..18bfcb639 100644
--- a/radicle-cob/Cargo.toml
+++ b/radicle-cob/Cargo.toml
@@ -55,4 +55,4 @@ qcheck-macros = { version = "1", default-features = false }
[dev-dependencies.radicle-crypto]
path = "../radicle-crypto"
version = "0"
-features = ["test"]
+features = ["test", "radicle-git-ext"]
diff --git a/radicle-cob/src/object/collaboration/create.rs b/radicle-cob/src/object/collaboration/create.rs
index ce16d6d64..c2a2b5ea5 100644
--- a/radicle-cob/src/object/collaboration/create.rs
+++ b/radicle-cob/src/object/collaboration/create.rs
@@ -1,7 +1,6 @@
// Copyright © 2022 The Radicle Link Contributors
use nonempty::NonEmpty;
-use radicle_crypto::PublicKey;
use crate::Embed;
use crate::Evaluate;
@@ -58,7 +57,7 @@ pub fn create<T, S, G>(
signer: &G,
resource: Option<Oid>,
related: Vec<Oid>,
- identifier: &PublicKey,
+ identifier: &S::Namespace,
args: Create,
) -> Result<CollaborativeObject<T>, error::Create>
where
diff --git a/radicle-cob/src/object/collaboration/remove.rs b/radicle-cob/src/object/collaboration/remove.rs
index 9bcbde694..0b0a798d7 100644
--- a/radicle-cob/src/object/collaboration/remove.rs
+++ b/radicle-cob/src/object/collaboration/remove.rs
@@ -1,7 +1,5 @@
// Copyright © 2022 The Radicle Link Contributors
-use radicle_crypto::PublicKey;
-
use crate::{ObjectId, Store, TypeName};
use super::error;
@@ -17,7 +15,7 @@ use super::error;
/// type.
pub fn remove<S>(
storage: &S,
- identifier: &PublicKey,
+ identifier: &S::Namespace,
typename: &TypeName,
oid: &ObjectId,
) -> Result<(), error::Remove>
diff --git a/radicle-cob/src/object/collaboration/update.rs b/radicle-cob/src/object/collaboration/update.rs
index 706197d50..3c6a7c431 100644
--- a/radicle-cob/src/object/collaboration/update.rs
+++ b/radicle-cob/src/object/collaboration/update.rs
@@ -3,7 +3,6 @@ use std::iter;
use git_ext::Oid;
use nonempty::NonEmpty;
-use radicle_crypto::PublicKey;
use crate::{
change, change_graph::ChangeGraph, history::EntryId, CollaborativeObject, Embed, Evaluate,
@@ -61,7 +60,7 @@ pub fn update<T, S, G>(
signer: &G,
resource: Option<Oid>,
related: Vec<Oid>,
- identifier: &PublicKey,
+ identifier: &S::Namespace,
args: Update,
) -> Result<Updated<T>, error::Update>
where
diff --git a/radicle-cob/src/object/storage.rs b/radicle-cob/src/object/storage.rs
index c8a11928f..6779f5a5b 100644
--- a/radicle-cob/src/object/storage.rs
+++ b/radicle-cob/src/object/storage.rs
@@ -4,7 +4,6 @@ use std::{collections::BTreeMap, error::Error};
use git_ext::ref_format::RefString;
use git_ext::Oid;
-use radicle_crypto::PublicKey;
use crate::change::EntryId;
use crate::{ObjectId, TypeName};
@@ -59,6 +58,8 @@ pub trait Storage {
type UpdateError: Error + Send + Sync + 'static;
type RemoveError: Error + Send + Sync + 'static;
+ type Namespace;
+
/// Get all references which point to a head of the change graph for a
/// particular object
fn objects(
@@ -74,7 +75,7 @@ pub trait Storage {
/// Update a ref to a particular collaborative object
fn update(
&self,
- identifier: &PublicKey,
+ namespace: &Self::Namespace,
typename: &TypeName,
object_id: &ObjectId,
entry: &EntryId,
@@ -83,7 +84,7 @@ pub trait Storage {
/// Remove a ref to a particular collaborative object
fn remove(
&self,
- identifier: &PublicKey,
+ namespace: &Self::Namespace,
typename: &TypeName,
object_id: &ObjectId,
) -> Result<(), Self::RemoveError>;
diff --git a/radicle-cob/src/test/storage.rs b/radicle-cob/src/test/storage.rs
index 107623096..887e131e7 100644
--- a/radicle-cob/src/test/storage.rs
+++ b/radicle-cob/src/test/storage.rs
@@ -1,6 +1,6 @@
use std::{collections::BTreeMap, convert::TryFrom as _};
-use radicle_crypto::PublicKey;
+use radicle_git_ext::ref_format::{refname, Component};
use tempfile::TempDir;
use crate::{
@@ -105,6 +105,8 @@ impl object::Storage for Storage {
type UpdateError = git2::Error;
type RemoveError = git2::Error;
+ type Namespace = crypto::PublicKey;
+
fn objects(
&self,
typename: &crate::TypeName,
@@ -148,12 +150,16 @@ impl object::Storage for Storage {
fn update(
&self,
- identifier: &PublicKey,
+ namespace: &Self::Namespace,
typename: &crate::TypeName,
object_id: &ObjectId,
entry: &change::EntryId,
) -> Result<(), Self::UpdateError> {
- let name = format!("refs/rad/{}/cobs/{}/{}", identifier, typename, object_id);
+ let name = refname!("refs/rad")
+ .join(Component::from(namespace))
+ .join(refname!("cobs"))
+ .join::<Component>(typename.into())
+ .join::<Component>(object_id.into());
self.raw
.reference(&name, (*entry).into(), true, "new change")?;
Ok(())
@@ -161,11 +167,15 @@ impl object::Storage for Storage {
fn remove(
&self,
- identifier: &PublicKey,
+ namespace: &Self::Namespace,
typename: &crate::TypeName,
object_id: &ObjectId,
) -> Result<(), Self::RemoveError> {
- let name = format!("refs/rad/{}/cobs/{}/{}", identifier, typename, object_id);
+ let name = refname!("refs/rad")
+ .join(Component::from(namespace))
+ .join(refname!("cobs"))
+ .join::<Component>(typename.into())
+ .join::<Component>(object_id.into());
self.raw.find_reference(&name)?.delete()?;
Ok(())
diff --git a/radicle-node/src/worker/fetch.rs b/radicle-node/src/worker/fetch.rs
index bf9bb3bc7..fa2df3bc7 100644
--- a/radicle-node/src/worker/fetch.rs
+++ b/radicle-node/src/worker/fetch.rs
@@ -8,6 +8,7 @@ use localtime::LocalTime;
use radicle::cob::TypedId;
use radicle::crypto::PublicKey;
use radicle::identity::DocAt;
+use radicle::prelude::NodeId;
use radicle::prelude::RepoId;
use radicle::storage::refs::RefsAt;
use radicle::storage::{
@@ -299,7 +300,7 @@ fn cache_cobs<S, C>(
cache: &mut C,
) -> Result<(), error::Cache>
where
- S: ReadRepository + cob::Store,
+ S: ReadRepository + cob::Store<Namespace = NodeId>,
C: cob::cache::Update<cob::issue::Issue> + cob::cache::Update<cob::patch::Patch>,
C: cob::cache::Remove<cob::issue::Issue> + cob::cache::Remove<cob::patch::Patch>,
{
diff --git a/radicle-remote-helper/src/lib.rs b/radicle-remote-helper/src/lib.rs
index 6c2d60a27..b24a4ed07 100644
--- a/radicle-remote-helper/src/lib.rs
+++ b/radicle-remote-helper/src/lib.rs
@@ -13,6 +13,7 @@ use std::{env, fmt, io};
use thiserror::Error;
+use radicle::prelude::NodeId;
use radicle::storage::git::transport::local::{Url, UrlError};
use radicle::storage::{ReadRepository, WriteStorage};
use radicle::{cob, profile};
@@ -262,7 +263,7 @@ pub(crate) fn warn(s: impl fmt::Display) {
}
/// Get the patch store.
-pub(crate) fn patches<'a, R: ReadRepository + cob::Store>(
+pub(crate) fn patches<'a, R: ReadRepository + cob::Store<Namespace = NodeId>>(
profile: &Profile,
repo: &'a R,
) -> Result<cob::patch::Cache<cob::patch::Patches<'a, R>, cob::cache::StoreReader>, list::Error> {
diff --git a/radicle-remote-helper/src/list.rs b/radicle-remote-helper/src/list.rs
index e4713089a..fef535d1a 100644
--- a/radicle-remote-helper/src/list.rs
+++ b/radicle-remote-helper/src/list.rs
@@ -4,6 +4,7 @@ use thiserror::Error;
use radicle::cob;
use radicle::git;
+use radicle::prelude::NodeId;
use radicle::storage::git::transport::local::Url;
use radicle::storage::ReadRepository;
use radicle::Profile;
@@ -34,7 +35,7 @@ pub enum Error {
}
/// List refs for fetching (`git fetch` and `git ls-remote`).
-pub fn for_fetch<R: ReadRepository + cob::Store + 'static>(
+pub fn for_fetch<R: ReadRepository + cob::Store<Namespace = NodeId> + 'static>(
url: &Url,
profile: &Profile,
stored: &R,
@@ -83,7 +84,7 @@ pub fn for_push<R: ReadRepository>(profile: &Profile, stored: &R) -> Result<(),
}
/// List canonical patch references. These are magic refs that can be used to pull patch updates.
-fn patch_refs<R: ReadRepository + cob::Store + 'static>(
+fn patch_refs<R: ReadRepository + cob::Store<Namespace = NodeId> + 'static>(
profile: &Profile,
stored: &R,
) -> Result<(), Error> {
diff --git a/radicle/src/cob/identity.rs b/radicle/src/cob/identity.rs
index 49f6090ff..f3383c596 100644
--- a/radicle/src/cob/identity.rs
+++ b/radicle/src/cob/identity.rs
@@ -11,6 +11,7 @@ use thiserror::Error;
use crate::identity::doc::Doc;
use crate::node::device::Device;
+use crate::node::NodeId;
use crate::{
cob,
cob::{
@@ -192,13 +193,14 @@ impl Identity {
}
}
- pub fn initialize<'a, R: WriteRepository + cob::Store, G>(
+ pub fn initialize<'a, R, G>(
doc: &Doc,
store: &'a R,
signer: &Device<G>,
) -> Result<IdentityMut<'a, R>, cob::store::Error>
where
G: crypto::signature::Signer<crypto::Signature>,
+ R: WriteRepository + cob::Store<Namespace = NodeId>,
{
let mut store = cob::store::Store::open(store)?;
let (id, identity) = Transaction::<Identity, _>::initial(
@@ -227,7 +229,7 @@ impl Identity {
}
/// Get a proposal mutably.
- pub fn get_mut<'a, R: WriteRepository + cob::Store>(
+ pub fn get_mut<'a, R: WriteRepository + cob::Store<Namespace = NodeId>>(
id: &ObjectId,
repo: &'a R,
) -> Result<IdentityMut<'a, R>, store::Error> {
@@ -248,7 +250,7 @@ impl Identity {
Self::get(&oid, repo).map_err(RepositoryError::from)
}
- pub fn load_mut<R: WriteRepository + cob::Store>(
+ pub fn load_mut<R: WriteRepository + cob::Store<Namespace = NodeId>>(
repo: &R,
) -> Result<IdentityMut<R>, RepositoryError> {
let oid = repo.identity_root()?;
@@ -892,7 +894,7 @@ impl<R> fmt::Debug for IdentityMut<'_, R> {
impl<R> IdentityMut<'_, R>
where
- R: WriteRepository + cob::Store,
+ R: WriteRepository + cob::Store<Namespace = NodeId>,
{
/// Reload the identity data from storage.
pub fn reload(&mut self) -> Result<(), store::Error> {
diff --git a/radicle/src/cob/issue.rs b/radicle/src/cob/issue.rs
index 9dc209f49..b351dce15 100644
--- a/radicle/src/cob/issue.rs
+++ b/radicle/src/cob/issue.rs
@@ -17,6 +17,7 @@ use crate::cob::thread::{Comment, CommentId, Thread};
use crate::cob::{op, store, ActorId, Embed, EntryId, ObjectId, TypeName};
use crate::identity::doc::DocError;
use crate::node::device::Device;
+use crate::node::NodeId;
use crate::prelude::{Did, Doc, ReadRepository, RepoId};
use crate::storage::{HasRepoId, RepositoryError, WriteRepository};
@@ -589,7 +590,7 @@ impl<R, C> std::fmt::Debug for IssueMut<'_, '_, R, C> {
impl<R, C> IssueMut<'_, '_, R, C>
where
- R: WriteRepository + cob::Store,
+ R: WriteRepository + cob::Store<Namespace = NodeId>,
C: cob::cache::Update<Issue>,
{
/// Reload the issue data from storage.
@@ -792,7 +793,7 @@ impl IssueCounts {
impl<'a, R> Issues<'a, R>
where
- R: ReadRepository + cob::Store,
+ R: ReadRepository + cob::Store<Namespace = NodeId>,
{
/// Open an issues store.
pub fn open(repository: &'a R) -> Result<Self, RepositoryError> {
@@ -805,7 +806,7 @@ where
impl<'a, R> Issues<'a, R>
where
- R: WriteRepository + cob::Store,
+ R: WriteRepository + cob::Store<Namespace = NodeId>,
{
/// Create a new issue.
pub fn create<'g, G, C>(
diff --git a/radicle/src/cob/issue/cache.rs b/radicle/src/cob/issue/cache.rs
index e13f0b911..3e7599824 100644
--- a/radicle/src/cob/issue/cache.rs
+++ b/radicle/src/cob/issue/cache.rs
@@ -10,6 +10,7 @@ use crate::cob::cache::{Remove, StoreReader, StoreWriter, Update};
use crate::cob::store;
use crate::cob::{Embed, Label, ObjectId, TypeName, Uri};
use crate::node::device::Device;
+use crate::node::NodeId;
use crate::prelude::{Did, RepoId};
use crate::storage::{HasRepoId, ReadRepository, RepositoryError, SignRepository, WriteRepository};
@@ -107,7 +108,7 @@ impl<'a, R, C> Cache<super::Issues<'a, R>, C> {
signer: &Device<G>,
) -> Result<IssueMut<'a, 'g, R, C>, super::Error>
where
- R: ReadRepository + WriteRepository + cob::Store,
+ R: ReadRepository + WriteRepository + cob::Store<Namespace = NodeId>,
G: crypto::signature::Signer<crypto::Signature>,
C: Update<Issue>,
{
@@ -127,7 +128,7 @@ impl<'a, R, C> Cache<super::Issues<'a, R>, C> {
pub fn remove<G>(&mut self, id: &IssueId, signer: &Device<G>) -> Result<(), super::Error>
where
G: crypto::signature::Signer<crypto::Signature>,
- R: ReadRepository + SignRepository + cob::Store,
+ R: ReadRepository + SignRepository + cob::Store<Namespace = NodeId>,
C: Remove<Issue>,
{
self.store.remove(id, signer)?;
@@ -199,7 +200,7 @@ impl<'a, R, C> Cache<super::Issues<'a, R>, C> {
impl<'a, R> Cache<super::Issues<'a, R>, cache::NoCache>
where
- R: ReadRepository + cob::Store,
+ R: ReadRepository + cob::Store<Namespace = NodeId>,
{
/// Get a `Cache` that does no write-through modifications and
/// uses the [`super::Issues`] store for all reads and writes.
diff --git a/radicle/src/cob/job.rs b/radicle/src/cob/job.rs
index d5a439751..0901e1d94 100644
--- a/radicle/src/cob/job.rs
+++ b/radicle/src/cob/job.rs
@@ -21,6 +21,7 @@ use crate::cob::{EntryId, ObjectId, TypeName};
use crate::crypto::ssh::ExtendedSignature;
use crate::git;
use crate::node::device::Device;
+use crate::node::NodeId;
use crate::prelude::ReadRepository;
use crate::storage::{Oid, WriteRepository};
@@ -315,7 +316,7 @@ impl<R> std::fmt::Debug for JobMut<'_, '_, R> {
impl<R> JobMut<'_, '_, R>
where
- R: WriteRepository + cob::Store,
+ R: WriteRepository + cob::Store<Namespace = NodeId>,
{
/// Reload the COB from storage.
pub fn reload(&mut self) -> Result<(), store::Error> {
@@ -397,7 +398,7 @@ impl<'a, R> Deref for JobStore<'a, R> {
impl<'a, R> JobStore<'a, R>
where
- R: WriteRepository + ReadRepository + cob::Store,
+ R: WriteRepository + ReadRepository + cob::Store<Namespace = NodeId>,
{
pub fn open(repository: &'a R) -> Result<Self, store::Error> {
let raw = store::Store::open(repository)?;
diff --git a/radicle/src/cob/patch.rs b/radicle/src/cob/patch.rs
index 5cc0fb9b0..ee40f3ba3 100644
--- a/radicle/src/cob/patch.rs
+++ b/radicle/src/cob/patch.rs
@@ -1976,7 +1976,7 @@ pub struct PatchMut<'a, 'g, R, C> {
impl<'a, 'g, R, C> PatchMut<'a, 'g, R, C>
where
C: cob::cache::Update<Patch>,
- R: ReadRepository + SignRepository + cob::Store,
+ R: ReadRepository + SignRepository + cob::Store<Namespace = NodeId>,
{
pub fn new(id: ObjectId, patch: Patch, cache: &'g mut Cache<Patches<'a, R>, C>) -> Self {
Self {
@@ -2507,7 +2507,7 @@ where
impl<'a, R> Patches<'a, R>
where
- R: ReadRepository + cob::Store,
+ R: ReadRepository + cob::Store<Namespace = NodeId>,
{
/// Open a patches store.
pub fn open(repository: &'a R) -> Result<Self, RepositoryError> {
@@ -2590,7 +2590,7 @@ where
impl<'a, R> Patches<'a, R>
where
- R: ReadRepository + SignRepository + cob::Store,
+ R: ReadRepository + SignRepository + cob::Store<Namespace = NodeId>,
{
/// Open a new patch.
pub fn create<'g, C, G>(
diff --git a/radicle/src/cob/patch/cache.rs b/radicle/src/cob/patch/cache.rs
index 305299094..ea10869dd 100644
--- a/radicle/src/cob/patch/cache.rs
+++ b/radicle/src/cob/patch/cache.rs
@@ -15,8 +15,8 @@ use crate::prelude::RepoId;
use crate::storage::{HasRepoId, ReadRepository, RepositoryError, SignRepository, WriteRepository};
use super::{
- ByRevision, MergeTarget, Patch, PatchCounts, PatchId, PatchMut, Revision, RevisionId, State,
- Status,
+ ByRevision, MergeTarget, NodeId, Patch, PatchCounts, PatchId, PatchMut, Revision, RevisionId,
+ State, Status,
};
/// A set of read-only methods for a [`Patch`] store.
@@ -119,7 +119,7 @@ impl<'a, R, C> Cache<super::Patches<'a, R>, C> {
signer: &Device<G>,
) -> Result<PatchMut<'a, 'g, R, C>, super::Error>
where
- R: WriteRepository + cob::Store,
+ R: WriteRepository + cob::Store<Namespace = NodeId>,
G: crypto::signature::Signer<crypto::Signature>,
C: Update<Patch>,
{
@@ -149,7 +149,7 @@ impl<'a, R, C> Cache<super::Patches<'a, R>, C> {
signer: &Device<G>,
) -> Result<PatchMut<'a, 'g, R, C>, super::Error>
where
- R: WriteRepository + cob::Store,
+ R: WriteRepository + cob::Store<Namespace = NodeId>,
G: crypto::signature::Signer<crypto::Signature>,
C: Update<Patch>,
{
@@ -170,7 +170,7 @@ impl<'a, R, C> Cache<super::Patches<'a, R>, C> {
pub fn remove<G>(&mut self, id: &PatchId, signer: &Device<G>) -> Result<(), super::Error>
where
G: crypto::signature::Signer<crypto::Signature>,
- R: ReadRepository + SignRepository + cob::Store,
+ R: ReadRepository + SignRepository + cob::Store<Namespace = NodeId>,
C: Remove<Patch>,
{
self.store.remove(id, signer)?;
@@ -276,7 +276,7 @@ where
impl<'a, R> Cache<super::Patches<'a, R>, cache::NoCache>
where
- R: ReadRepository + cob::Store,
+ R: ReadRepository + cob::Store<Namespace = NodeId>,
{
/// Get a `Cache` that does no write-through modifications and
/// uses the [`super::Patches`] store for all reads and writes.
@@ -488,7 +488,7 @@ impl Iterator for NoCacheIter<'_> {
impl<R> Patches for Cache<super::Patches<'_, R>, cache::NoCache>
where
- R: ReadRepository + cob::Store,
+ R: ReadRepository + cob::Store<Namespace = NodeId>,
{
type Error = super::Error;
type Iter<'b>
diff --git a/radicle/src/cob/store.rs b/radicle/src/cob/store.rs
index b6deabfa0..ddaf8bd1b 100644
--- a/radicle/src/cob/store.rs
+++ b/radicle/src/cob/store.rs
@@ -174,7 +174,7 @@ where
impl<'a, T, R> Store<'a, T, R>
where
- R: ReadRepository + cob::Store,
+ R: ReadRepository + cob::Store<Namespace = NodeId>,
T: CobWithType,
{
/// Open a new generic store.
@@ -190,7 +190,7 @@ where
impl<T, R> Store<'_, T, R>
where
- R: ReadRepository + cob::Store,
+ R: ReadRepository + cob::Store<Namespace = NodeId>,
T: Cob + cob::Evaluate<R>,
{
pub fn transaction(
@@ -204,7 +204,7 @@ where
impl<T, R> Store<'_, T, R>
where
- R: ReadRepository + SignRepository + cob::Store,
+ R: ReadRepository + SignRepository + cob::Store<Namespace = NodeId>,
T: Cob + cob::Evaluate<R>,
T::Action: Serialize,
{
@@ -423,7 +423,7 @@ where
Self: From<Tx>,
G: crypto::signature::Signer<crypto::Signature>,
F: FnOnce(&mut Tx, &R) -> Result<(), Error>,
- R: ReadRepository + SignRepository + cob::Store,
+ R: ReadRepository + SignRepository + cob::Store<Namespace = NodeId>,
T::Action: Serialize + Clone,
{
let mut tx = Tx::from(Transaction::default());
@@ -487,7 +487,7 @@ where
signer: &Device<G>,
) -> Result<(T, EntryId), Error>
where
- R: ReadRepository + SignRepository + cob::Store,
+ R: ReadRepository + SignRepository + cob::Store<Namespace = NodeId>,
T::Action: Serialize + Clone,
G: crypto::signature::Signer<crypto::Signature>,
{
diff --git a/radicle/src/profile.rs b/radicle/src/profile.rs
index d3a274098..c69ebc26c 100644
--- a/radicle/src/profile.rs
+++ b/radicle/src/profile.rs
@@ -605,7 +605,7 @@ impl Home {
repository: &'a R,
) -> Result<cob::issue::Cache<cob::issue::Issues<'a, R>, cob::cache::StoreReader>, Error>
where
- R: ReadRepository + cob::Store,
+ R: ReadRepository + cob::Store<Namespace = NodeId>,
{
let db = self.cobs_db()?;
let store = cob::issue::Issues::open(repository)?;
@@ -621,7 +621,7 @@ impl Home {
repository: &'a R,
) -> Result<cob::issue::Cache<cob::issue::Issues<'a, R>, cob::cache::StoreWriter>, Error>
where
- R: ReadRepository + cob::Store,
+ R: ReadRepository + cob::Store<Namespace = NodeId>,
{
let db = self.cobs_db_mut()?;
let store = cob::issue::Issues::open(repository)?;
@@ -637,7 +637,7 @@ impl Home {
repository: &'a R,
) -> Result<cob::patch::Cache<cob::patch::Patches<'a, R>, cob::cache::StoreReader>, Error>
where
- R: ReadRepository + cob::Store,
+ R: ReadRepository + cob::Store<Namespace = NodeId>,
{
let db = self.cobs_db()?;
let store = cob::patch::Patches::open(repository)?;
@@ -653,7 +653,7 @@ impl Home {
repository: &'a R,
) -> Result<cob::patch::Cache<cob::patch::Patches<'a, R>, cob::cache::StoreWriter>, Error>
where
- R: ReadRepository + cob::Store,
+ R: ReadRepository + cob::Store<Namespace = NodeId>,
{
let db = self.cobs_db_mut()?;
let store = cob::patch::Patches::open(repository)?;
diff --git a/radicle/src/storage/git/cob.rs b/radicle/src/storage/git/cob.rs
index 79287b175..a3bcce14b 100644
--- a/radicle/src/storage/git/cob.rs
+++ b/radicle/src/storage/git/cob.rs
@@ -11,20 +11,18 @@ use storage::RepositoryError;
use storage::SignRepository;
use storage::ValidateRepository;
+use crate::git;
use crate::git::*;
use crate::identity;
use crate::identity::doc::DocError;
use crate::node::device::Device;
+use crate::node::NodeId;
use crate::storage;
use crate::storage::Error;
use crate::storage::{
git::{Remote, Remotes, Validations},
ReadRepository, Verified,
};
-use crate::{
- git, identity,
- identity::{doc::DocError, PublicKey},
-};
use super::{RemoteId, Repository};
@@ -92,6 +90,8 @@ impl cob::object::Storage for Repository {
type UpdateError = git2::Error;
type RemoveError = git2::Error;
+ type Namespace = NodeId;
+
fn objects(
&self,
typename: &cob::TypeName,
@@ -146,13 +146,13 @@ impl cob::object::Storage for Repository {
fn update(
&self,
- identifier: &PublicKey,
+ namespace: &Self::Namespace,
typename: &cob::TypeName,
object_id: &cob::ObjectId,
entry: &cob::EntryId,
) -> Result<(), Self::UpdateError> {
self.backend.reference(
- git::refs::storage::cob(identifier, typename, object_id).as_str(),
+ git::refs::storage::cob(namespace, typename, object_id).as_str(),
(*entry).into(),
true,
&format!(
@@ -166,13 +166,13 @@ impl cob::object::Storage for Repository {
fn remove(
&self,
- identifier: &PublicKey,
+ namespace: &Self::Namespace,
typename: &cob::TypeName,
object_id: &cob::ObjectId,
) -> Result<(), Self::RemoveError> {
let mut reference = self
.backend
- .find_reference(git::refs::storage::cob(identifier, typename, object_id).as_str())?;
+ .find_reference(git::refs::storage::cob(namespace, typename, object_id).as_str())?;
reference.delete()
}
@@ -377,6 +377,8 @@ impl<R: storage::WriteRepository> cob::object::Storage for DraftStore<'_, R> {
type UpdateError = git2::Error;
type RemoveError = git2::Error;
+ type Namespace = NodeId;
+
fn objects(
&self,
typename: &cob::TypeName,
@@ -421,13 +423,13 @@ impl<R: storage::WriteRepository> cob::object::Storage for DraftStore<'_, R> {
fn update(
&self,
- identifier: &PublicKey,
+ namespace: &Self::Namespace,
typename: &cob::TypeName,
object_id: &cob::ObjectId,
entry: &cob::history::EntryId,
) -> Result<(), Self::UpdateError> {
self.repo.raw().reference(
- git::refs::storage::draft::cob(identifier, typename, object_id).as_str(),
+ git::refs::storage::draft::cob(namespace, typename, object_id).as_str(),
(*entry).into(),
true,
&format!(
@@ -441,12 +443,12 @@ impl<R: storage::WriteRepository> cob::object::Storage for DraftStore<'_, R> {
fn remove(
&self,
- identifier: &PublicKey,
+ namespace: &Self::Namespace,
typename: &cob::TypeName,
object_id: &cob::ObjectId,
) -> Result<(), Self::RemoveError> {
let mut reference = self.repo.raw().find_reference(
- git::refs::storage::draft::cob(identifier, typename, object_id).as_str(),
+ git::refs::storage::draft::cob(namespace, typename, object_id).as_str(),
)?;
reference.delete()
diff --git a/radicle/src/test/storage.rs b/radicle/src/test/storage.rs
index 4b00f8882..116d9cc94 100644
--- a/radicle/src/test/storage.rs
+++ b/radicle/src/test/storage.rs
@@ -364,6 +364,8 @@ impl radicle_cob::object::Storage for MockRepository {
type UpdateError = Infallible;
type RemoveError = Infallible;
+ type Namespace = crypto::PublicKey;
+
fn objects(
&self,
_typename: &radicle_cob::TypeName,
@@ -384,7 +386,7 @@ impl radicle_cob::object::Storage for MockRepository {
fn update(
&self,
- _identifier: &radicle_crypto::PublicKey,
+ _namespace: &Self::Namespace,
_typename: &radicle_cob::TypeName,
_object_id: &radicle_cob::ObjectId,
_entry: &radicle_cob::EntryId,
@@ -394,7 +396,7 @@ impl radicle_cob::object::Storage for MockRepository {
fn remove(
&self,
- _identifier: &radicle_crypto::PublicKey,
+ _namespace: &Self::Namespace,
_typename: &radicle_cob::TypeName,
_object_id: &radicle_cob::ObjectId,
) -> Result<(), Self::RemoveError> {
commit 1f4fcc5e6a3dadef99284bc3845ada11c74ccd97
Author: Fintan Halpenny <fintan.halpenny@gmail.com>
Date: Mon Dec 2 12:29:31 2024 +0000
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.
diff --git a/Cargo.lock b/Cargo.lock
index 981c3d3bc..ab61e6e16 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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",
diff --git a/radicle-cli/src/commands/cob.rs b/radicle-cli/src/commands/cob.rs
index 1e116ae81..5249fea23 100644
--- a/radicle-cli/src/commands/cob.rs
+++ b/radicle-cli/src/commands/cob.rs
@@ -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(())
diff --git a/radicle-cli/src/commands/id.rs b/radicle-cli/src/commands/id.rs
index f49eb74a6..2b70f2930 100644
--- a/radicle-cli/src/commands/id.rs
+++ b/radicle-cli/src/commands/id.rs
@@ -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
diff --git a/radicle-cli/src/commands/issue.rs b/radicle-cli/src/commands/issue.rs
index ed27284b5..9332ace75 100644
--- a/radicle-cli/src/commands/issue.rs
+++ b/radicle-cli/src/commands/issue.rs
@@ -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)?;
diff --git a/radicle-cli/src/commands/job.rs b/radicle-cli/src/commands/job.rs
index 365a19e29..ce6f1f7a0 100644
--- a/radicle-cli/src/commands/job.rs
+++ b/radicle-cli/src/commands/job.rs
@@ -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)?;
diff --git a/radicle-cli/src/commands/patch/edit.rs b/radicle-cli/src/commands/patch/edit.rs
index 8050b8113..70ec695f3 100644
--- a/radicle-cli/src/commands/patch/edit.rs
+++ b/radicle-cli/src/commands/patch/edit.rs
@@ -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() {
diff --git a/radicle-cli/src/commands/patch/review.rs b/radicle-cli/src/commands/patch/review.rs
index 08dcbd223..bf8436f13 100644
--- a/radicle-cli/src/commands/patch/review.rs
+++ b/radicle-cli/src/commands/patch/review.rs
@@ -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)?;
diff --git a/radicle-cli/src/commands/patch/review/builder.rs b/radicle-cli/src/commands/patch/review/builder.rs
index 8a5f91baa..fe7f24484 100644
--- a/radicle-cli/src/commands/patch/review/builder.rs
+++ b/radicle-cli/src/commands/patch/review/builder.rs
@@ -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 = {
diff --git a/radicle-cli/src/terminal/io.rs b/radicle-cli/src/terminal/io.rs
index 9b1f6dd64..79bcc37e8 100644
--- a/radicle-cli/src/terminal/io.rs
+++ b/radicle-cli/src/terminal/io.rs
@@ -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)> {
diff --git a/radicle-cob/Cargo.toml b/radicle-cob/Cargo.toml
index 601c0f2da..97847c72d 100644
--- a/radicle-cob/Cargo.toml
+++ b/radicle-cob/Cargo.toml
@@ -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]
diff --git a/radicle-cob/src/backend/git/change.rs b/radicle-cob/src/backend/git/change.rs
index 0d9695cf0..c36a7a155 100644
--- a/radicle-cob/src/backend/git/change.rs
+++ b/radicle-cob/src/backend/git/change.rs
@@ -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();
diff --git a/radicle-cob/src/change/store.rs b/radicle-cob/src/change/store.rs
index 3a5c24c6e..f5f9100c0 100644
--- a/radicle-cob/src/change/store.rs
+++ b/radicle-cob/src/change/store.rs
@@ -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)]
diff --git a/radicle-cob/src/object/collaboration/create.rs b/radicle-cob/src/object/collaboration/create.rs
index 68181754a..ce16d6d64 100644
--- a/radicle-cob/src/object/collaboration/create.rs
+++ b/radicle-cob/src/object/collaboration/create.rs
@@ -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;
diff --git a/radicle-cob/src/object/collaboration/update.rs b/radicle-cob/src/object/collaboration/update.rs
index 8a632cc1a..706197d50 100644
--- a/radicle-cob/src/object/collaboration/update.rs
+++ b/radicle-cob/src/object/collaboration/update.rs
@@ -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,
diff --git a/radicle-cob/src/test/storage.rs b/radicle-cob/src/test/storage.rs
index a5f56fb34..107623096 100644
--- a/radicle-cob/src/test/storage.rs
+++ b/radicle-cob/src/test/storage.rs
@@ -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)
}
diff --git a/radicle-crypto/Cargo.toml b/radicle-crypto/Cargo.toml
index ab902741a..8eac22988 100644
--- a/radicle-crypto/Cargo.toml
+++ b/radicle-crypto/Cargo.toml
@@ -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" }
diff --git a/radicle-crypto/src/lib.rs b/radicle-crypto/src/lib.rs
index 5417e3425..5a60a4b13 100644
--- a/radicle-crypto/src/lib.rs
+++ b/radicle-crypto/src/lib.rs
@@ -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"))]
diff --git a/radicle-crypto/src/ssh.rs b/radicle-crypto/src/ssh.rs
index 1f7e7e54d..474ac92b7 100644
--- a/radicle-crypto/src/ssh.rs
+++ b/radicle-crypto/src/ssh.rs
@@ -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 {
diff --git a/radicle-crypto/src/ssh/agent.rs b/radicle-crypto/src/ssh/agent.rs
index a4967a67d..804191408 100644
--- a/radicle-crypto/src/ssh/agent.rs
+++ b/radicle-crypto/src/ssh/agent.rs
@@ -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);
diff --git a/radicle-crypto/src/ssh/keystore.rs b/radicle-crypto/src/ssh/keystore.rs
index d7dfdd869..ab5f2d1a7 100644
--- a/radicle-crypto/src/ssh/keystore.rs
+++ b/radicle-crypto/src/ssh/keystore.rs
@@ -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
diff --git a/radicle-crypto/src/test/signer.rs b/radicle-crypto/src/test/signer.rs
index 997beb211..76c1fb2bf 100644
--- a/radicle-crypto/src/test/signer.rs
+++ b/radicle-crypto/src/test/signer.rs
@@ -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];
diff --git a/radicle-node/src/lib.rs b/radicle-node/src/lib.rs
index 768b3b9a7..269a63e7d 100644
--- a/radicle-node/src/lib.rs
+++ b/radicle-node/src/lib.rs
@@ -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;
diff --git a/radicle-node/src/main.rs b/radicle-node/src/main.rs
index f62a99882..08e19e01d 100644
--- a/radicle-node/src/main.rs
+++ b/radicle-node/src/main.rs
@@ -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());
diff --git a/radicle-node/src/runtime.rs b/radicle-node/src/runtime.rs
index 0abfcc132..ad2d5cc18 100644
--- a/radicle-node/src/runtime.rs
+++ b/radicle-node/src/runtime.rs
@@ -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();
diff --git a/radicle-node/src/service.rs b/radicle-node/src/service.rs
index db08f00dd..613dc1952 100644
--- a/radicle-node/src/service.rs
+++ b/radicle-node/src/service.rs
@@ -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 {
diff --git a/radicle-node/src/service/gossip/store.rs b/radicle-node/src/service/gossip/store.rs
index 9ae6dad82..49d9ba4ef 100644
--- a/radicle-node/src/service/gossip/store.rs
+++ b/radicle-node/src/service/gossip/store.rs
@@ -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(),
diff --git a/radicle-node/src/service/message.rs b/radicle-node/src/service/message.rs
index 45201b3b5..2b92e62d3 100644
--- a/radicle-node/src/service/message.rs
+++ b/radicle-node/src/service/message.rs
@@ -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(
diff --git a/radicle-node/src/test/environment.rs b/radicle-node/src/test/environment.rs
index 4fc6fb520..a68d11ce3 100644
--- a/radicle-node/src/test/environment.rs
+++ b/radicle-node/src/test/environment.rs
@@ -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<_>>();
diff --git a/radicle-node/src/test/gossip.rs b/radicle-node/src/test/gossip.rs
index 0184e0880..7c530170d 100644
--- a/radicle-node/src/test/gossip.rs
+++ b/radicle-node/src/test/gossip.rs
@@ -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 {
diff --git a/radicle-node/src/test/peer.rs b/radicle-node/src/test/peer.rs
index 5110289f1..a1eaf873e 100644
--- a/radicle-node/src/test/peer.rs
+++ b/radicle-node/src/test/peer.rs
@@ -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>,
) {
diff --git a/radicle-node/src/test/simulator.rs b/radicle-node/src/test/simulator.rs
index 5ce87a0d4..2f1648f9b 100644
--- a/radicle-node/src/test/simulator.rs
+++ b/radicle-node/src/test/simulator.rs
@@ -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 {
diff --git a/radicle-node/src/tests.rs b/radicle-node/src/tests.rs
index 5af183797..3d8ff039f 100644
--- a/radicle-node/src/tests.rs
+++ b/radicle-node/src/tests.rs
@@ -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(
diff --git a/radicle-node/src/tests/e2e.rs b/radicle-node/src/tests/e2e.rs
index ffa83c2a5..12b457a90 100644
--- a/radicle-node/src/tests/e2e.rs
+++ b/radicle-node/src/tests/e2e.rs
@@ -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]);
diff --git a/radicle-node/src/wire/message.rs b/radicle-node/src/wire/message.rs
index 69e54fd99..4e8062af7 100644
--- a/radicle-node/src/wire/message.rs
+++ b/radicle-node/src/wire/message.rs
@@ -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 {
diff --git a/radicle-node/src/wire/protocol.rs b/radicle-node/src/wire/protocol.rs
index a34a1ba22..3b0f93749 100644
--- a/radicle-node/src/wire/protocol.rs
+++ b/radicle-node/src/wire/protocol.rs
@@ -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,
diff --git a/radicle-remote-helper/src/push.rs b/radicle-remote-helper/src/push.rs
index 2ccdd8508..7459f6e68 100644
--- a/radicle-remote-helper/src/push.rs
+++ b/radicle-remote-helper/src/push.rs
@@ -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)?;
diff --git a/radicle/src/cob/identity.rs b/radicle/src/cob/identity.rs
index 73dc74e3a..49f6090ff 100644
--- a/radicle/src/cob/identity.rs
+++ b/radicle/src/cob/identity.rs
@@ -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, _, _, _) =
diff --git a/radicle/src/cob/issue.rs b/radicle/src/cob/issue.rs
index a53d20c67..9dc209f49 100644
--- a/radicle/src/cob/issue.rs
+++ b/radicle/src/cob/issue.rs
@@ -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();
diff --git a/radicle/src/cob/issue/cache.rs b/radicle/src/cob/issue/cache.rs
index 915d42a1a..e13f0b911 100644
--- a/radicle/src/cob/issue/cache.rs
+++ b/radicle/src/cob/issue/cache.rs
@@ -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>,
{
diff --git a/radicle/src/cob/job.rs b/radicle/src/cob/job.rs
index 84d849a7a..d5a439751 100644
--- a/radicle/src/cob/job.rs
+++ b/radicle/src/cob/job.rs
@@ -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)
}
}
diff --git a/radicle/src/cob/patch.rs b/radicle/src/cob/patch.rs
index 1666c1d39..5cc0fb9b0 100644
--- a/radicle/src/cob/patch.rs
+++ b/radicle/src/cob/patch.rs
@@ -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");
diff --git a/radicle/src/cob/patch/cache.rs b/radicle/src/cob/patch/cache.rs
index 784d1d4a4..305299094 100644
--- a/radicle/src/cob/patch/cache.rs
+++ b/radicle/src/cob/patch/cache.rs
@@ -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>,
{
diff --git a/radicle/src/cob/store.rs b/radicle/src/cob/store.rs
index bd67b2d3d..b6deabfa0 100644
--- a/radicle/src/cob/store.rs
+++ b/radicle/src/cob/store.rs
@@ -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");
diff --git a/radicle/src/cob/test.rs b/radicle/src/cob/test.rs
index 571812167..c2fb701cd 100644
--- a/radicle/src/cob/test.rs
+++ b/radicle/src/cob/test.rs
@@ -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 }
}
}
diff --git a/radicle/src/cob/thread.rs b/radicle/src/cob/thread.rs
index 7ae4c3bf1..64deb0b65 100644
--- a/radicle/src/cob/thread.rs
+++ b/radicle/src/cob/thread.rs
@@ -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),
}
diff --git a/radicle/src/git/canonical.rs b/radicle/src/git/canonical.rs
index cb794bd66..2f0db2fbc 100644
--- a/radicle/src/git/canonical.rs
+++ b/radicle/src/git/canonical.rs
@@ -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())
})
diff --git a/radicle/src/identity/doc.rs b/radicle/src/identity/doc.rs
index 1beb36007..bd34cc2e9 100644
--- a/radicle/src/identity/doc.rs
+++ b/radicle/src/identity/doc.rs
@@ -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(),
diff --git a/radicle/src/lib.rs b/radicle/src/lib.rs
index b1213ac59..04299cba7 100644
--- a/radicle/src/lib.rs
+++ b/radicle/src/lib.rs
@@ -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;
diff --git a/radicle/src/node.rs b/radicle/src/node.rs
index 669fa8322..6b9ab357b 100644
--- a/radicle/src/node.rs
+++ b/radicle/src/node.rs
@@ -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;
diff --git a/radicle/src/node/device.rs b/radicle/src/node/device.rs
new file mode 100644
index 000000000..1c49b4731
--- /dev/null
+++ b/radicle/src/node/device.rs
@@ -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)?,
+ })
+ }
+}
diff --git a/radicle/src/profile.rs b/radicle/src/profile.rs
index a854abaed..d3a274098 100644
--- a/radicle/src/profile.rs
+++ b/radicle/src/profile.rs
@@ -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))
}
diff --git a/radicle/src/rad.rs b/radicle/src/rad.rs
index ac4b18959..a54d63b8a 100644
--- a/radicle/src/rad.rs
+++ b/radicle/src/rad.rs
@@ -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();
diff --git a/radicle/src/storage.rs b/radicle/src/storage.rs
index 17d2fcdeb..4b454b1ea 100644
--- a/radicle/src/storage.rs
+++ b/radicle/src/storage.rs
@@ -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
diff --git a/radicle/src/storage/git.rs b/radicle/src/storage/git.rs
index 758874be4..f67a05299 100644
--- a/radicle/src/storage/git.rs
+++ b/radicle/src/storage/git.rs
@@ -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, _) =
diff --git a/radicle/src/storage/git/cob.rs b/radicle/src/storage/git/cob.rs
index ea24fab71..79287b175 100644
--- a/radicle/src/storage/git/cob.rs
+++ b/radicle/src/storage/git/cob.rs
@@ -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.
diff --git a/radicle/src/storage/refs.rs b/radicle/src/storage/refs.rs
index 793d92cdf..3d24e1f6c 100644
--- a/radicle/src/storage/refs.rs
+++ b/radicle/src/storage/refs.rs
@@ -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());
diff --git a/radicle/src/test.rs b/radicle/src/test.rs
index ed374897d..9a9e8eec4 100644
--- a/radicle/src/test.rs
+++ b/radicle/src/test.rs
@@ -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();
diff --git a/radicle/src/test/arbitrary.rs b/radicle/src/test/arbitrary.rs
index a6dd07708..fc26fff40 100644
--- a/radicle/src/test/arbitrary.rs
+++ b/radicle/src/test/arbitrary.rs
@@ -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)
}
diff --git a/radicle/src/test/fixtures.rs b/radicle/src/test/fixtures.rs
index fd768ad41..34d744d59 100644
--- a/radicle/src/test/fixtures.rs
+++ b/radicle/src/test/fixtures.rs
@@ -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);
diff --git a/radicle/src/test/storage.rs b/radicle/src/test/storage.rs
index 6a0668168..4b00f8882 100644
--- a/radicle/src/test/storage.rs
+++ b/radicle/src/test/storage.rs
@@ -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!()
}
Exit code: 0
shell: 'cargo --version rustc --version cargo fmt --check cargo clippy --all-targets --workspace -- --deny warnings cargo build --all-targets --workspace cargo doc --workspace --no-deps cargo test --workspace --no-fail-fast '
Commands:
$ podman run --name e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0 -v /opt/radcis/ci.rad.levitte.org/cci/state/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/s:/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/s:ro -v /opt/radcis/ci.rad.levitte.org/cci/state/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w:/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w -w /e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w -v /opt/radcis/ci.rad.levitte.org/.radicle:/${id}/.radicle:ro -e RAD_HOME=/${id}/.radicle rust:trixie bash /e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/s/script.sh
+ cargo --version
info: syncing channel updates for '1.85-x86_64-unknown-linux-gnu'
info: latest update on 2025-03-18, rust version 1.85.1 (4eb161250 2025-03-15)
info: downloading component 'cargo'
info: downloading component 'clippy'
info: downloading component 'rust-docs'
info: downloading component 'rust-src'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'rustfmt'
info: installing component 'cargo'
info: installing component 'clippy'
info: installing component 'rust-docs'
info: installing component 'rust-src'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'rustfmt'
cargo 1.85.1 (d73d2caf9 2024-12-31)
+ rustc --version
rustc 1.85.1 (4eb161250 2025-03-15)
+ cargo fmt --check
+ cargo clippy --all-targets --workspace -- --deny warnings
Updating crates.io index
Downloading crates ...
Downloaded aead v0.5.2
Downloaded adler2 v2.0.0
Downloaded amplify_num v0.5.2
Downloaded anyhow v1.0.82
Downloaded rfc6979 v0.4.0
Downloaded block-buffer v0.10.4
Downloaded base64ct v1.6.0
Downloaded errno v0.3.9
Downloaded gix-object v0.46.1
Downloaded radicle-git-ext v0.8.0
Downloaded regex v1.11.1
Downloaded multibase v0.9.1
Downloaded gix-utils v0.1.14
Downloaded pbkdf2 v0.12.2
Downloaded gix-config-value v0.14.12
Downloaded phf_shared v0.11.3
Downloaded phf v0.11.3
Downloaded getrandom v0.2.15
Downloaded nonempty v0.5.0
Downloaded noise-framework v0.4.0
Downloaded p256 v0.13.2
Downloaded netservices v0.8.0
Downloaded qcheck-macros v1.0.0
Downloaded gix-quote v0.4.15
Downloaded icu_collections v1.5.0
Downloaded gix-prompt v0.9.1
Downloaded gix-ref v0.49.1
Downloaded zerofrom-derive v0.1.6
Downloaded utf8_iter v1.0.4
Downloaded signature v1.6.4
Downloaded gix-trace v0.1.12
Downloaded popol v3.0.0
Downloaded pkg-config v0.3.30
Downloaded tree-sitter-json v0.24.8
Downloaded poly1305 v0.8.0
Downloaded ppv-lite86 v0.2.17
Downloaded pkcs1 v0.7.5
Downloaded pkcs8 v0.10.2
Downloaded p384 v0.13.0
Downloaded pretty_assertions v1.4.0
Downloaded inquire v0.7.5
Downloaded num-bigint-dig v0.8.4
Downloaded normalize-line-endings v0.3.0
Downloaded bstr v1.9.1
Downloaded group v0.13.0
Downloaded termion v2.0.3
Downloaded thiserror v2.0.12
Downloaded maybe-async v0.2.10
Downloaded typenum v1.17.0
Downloaded signals_receipts v0.2.0
Downloaded yansi v0.5.1
Downloaded walkdir v2.5.0
Downloaded tempfile v3.10.1
Downloaded socket2 v0.5.7
Downloaded tree-sitter-css v0.23.1
Downloaded ryu v1.0.17
Downloaded unicode-ident v1.0.12
Downloaded tinyvec v1.6.0
Downloaded rsa v0.9.6
Downloaded gix-odb v0.66.0
Downloaded similar v2.5.0
Downloaded gix-refspec v0.27.0
Downloaded serde v1.0.219
Downloaded rand v0.8.5
Downloaded url v2.5.4
Downloaded ssh-key v0.6.6
Downloaded unicode-normalization v0.1.23
Downloaded tree-sitter-go v0.23.4
Downloaded serde_json v1.0.116
Downloaded unicode-segmentation v1.11.0
Downloaded tree-sitter-python v0.23.4
Downloaded jiff v0.2.1
Downloaded winnow v0.6.26
Downloaded tree-sitter v0.24.4
Downloaded snapbox v0.4.17
Downloaded libc v0.2.155
Downloaded syn v2.0.89
Downloaded vcpkg v0.2.15
Downloaded tree-sitter-c v0.23.2
Downloaded tree-sitter-md v0.3.2
Downloaded tree-sitter-rust v0.23.2
Downloaded regex-syntax v0.8.5
Downloaded rustix v0.38.34
Downloaded syn v1.0.109
Downloaded smallvec v1.13.2
Downloaded signature v2.2.0
Downloaded tree-sitter-bash v0.23.3
Downloaded regex-automata v0.4.9
Downloaded sec1 v0.7.3
Downloaded yoke v0.7.5
Downloaded tree-sitter-ruby v0.23.1
Downloaded writeable v0.5.5
Downloaded tree-sitter-html v0.23.2
Downloaded timeago v0.4.2
Downloaded thiserror-impl v1.0.69
Downloaded tinystr v0.7.6
Downloaded termion v3.0.0
Downloaded tar v0.4.40
Downloaded shlex v1.3.0
Downloaded serde_derive v1.0.219
Downloaded bloomy v1.2.0
Downloaded idna v1.0.3
Downloaded gix-shallow v0.1.0
Downloaded gix-sec v0.10.12
Downloaded radicle-surf v0.22.0
Downloaded nonempty v0.9.0
Downloaded tree-sitter-typescript v0.23.2
Downloaded unicode-width v0.1.11
Downloaded tree-sitter-toml-ng v0.6.0
Downloaded sha3 v0.10.8
Downloaded tree-sitter-highlight v0.24.4
Downloaded thiserror-impl v2.0.12
Downloaded thiserror v1.0.69
Downloaded synstructure v0.13.1
Downloaded streaming-iterator v0.1.9
Downloaded spki v0.7.3
Downloaded indexmap v2.2.6
Downloaded zerovec v0.10.4
Downloaded xattr v1.3.1
Downloaded utf8parse v0.2.1
Downloaded subtle v2.5.0
Downloaded siphasher v1.0.1
Downloaded num-traits v0.2.18
Downloaded libgit2-sys v0.17.0+1.8.1
Downloaded gix-pack v0.56.0
Downloaded prodash v29.0.2
Downloaded scrypt v0.11.0
Downloaded icu_normalizer_data v1.5.1
Downloaded icu_normalizer v1.5.0
Downloaded icu_locid v1.5.0
Downloaded tinyvec_macros v0.1.1
Downloaded once_cell v1.19.0
Downloaded log v0.4.21
Downloaded lock_api v0.4.11
Downloaded icu_properties_data v1.5.1
Downloaded stable_deref_trait v1.2.0
Downloaded ssh-encoding v0.2.0
Downloaded git2 v0.19.0
Downloaded jobserver v0.1.31
Downloaded gix-validate v0.9.4
Downloaded linux-raw-sys v0.4.13
Downloaded version_check v0.9.4
Downloaded tree-sitter-language v0.1.2
Downloaded opaque-debug v0.3.1
Downloaded litemap v0.7.5
Downloaded lexopt v0.3.0
Downloaded lazy_static v1.5.0
Downloaded itoa v1.0.11
Downloaded io-reactor v0.5.2
Downloaded idna_adapter v1.2.0
Downloaded icu_locid_transform_data v1.5.1
Downloaded gix-tempfile v15.0.0
Downloaded quote v1.0.36
Downloaded proc-macro2 v1.0.92
Downloaded polyval v0.6.2
Downloaded numtoa v0.1.0
Downloaded num-iter v0.1.44
Downloaded num-integer v0.1.46
Downloaded localtime v1.3.1
Downloaded keccak v0.1.5
Downloaded inout v0.1.3
Downloaded icu_locid_transform v1.5.0
Downloaded gix-path v0.10.15
Downloaded zeroize v1.7.0
Downloaded primeorder v0.13.6
Downloaded utf16_iter v1.0.5
Downloaded siphasher v0.3.11
Downloaded gix-packetline v0.18.4
Downloaded radicle-std-ext v0.1.0
Downloaded qcheck v1.0.0
Downloaded proc-macro-error v1.0.4
Downloaded hashbrown v0.14.3
Downloaded p521 v0.13.3
Downloaded yoke-derive v0.7.5
Downloaded spin v0.9.8
Downloaded proc-macro-error-attr v1.0.4
Downloaded socks5-client v0.4.1
Downloaded sha2 v0.10.8
Downloaded sem_safe v0.2.0
Downloaded libm v0.2.8
Downloaded cc v1.2.2
Downloaded iana-time-zone v0.1.60
Downloaded gix-transport v0.44.0
Downloaded miniz_oxide v0.8.8
Downloaded memchr v2.7.2
Downloaded unicode-display-width v0.3.0
Downloaded ssh-cipher v0.2.0
Downloaded der v0.7.9
Downloaded crossbeam-channel v0.5.15
Downloaded hmac v0.12.1
Downloaded gix-traverse v0.43.1
Downloaded icu_properties v1.5.1
Downloaded write16 v1.0.0
Downloaded shell-words v1.1.0
Downloaded flate2 v1.1.1
Downloaded home v0.5.9
Downloaded icu_provider v1.5.0
Downloaded gix-protocol v0.47.0
Downloaded ascii v1.1.0
Downloaded newline-converter v0.3.0
Downloaded gix-url v0.28.2
Downloaded parking_lot v0.12.1
Downloaded memmap2 v0.9.4
Downloaded same-file v1.0.6
Downloaded blowfish v0.9.1
Downloaded anstream v0.6.13
Downloaded pem-rfc7468 v0.7.0
Downloaded parking_lot_core v0.9.9
Downloaded sqlite3-src v0.5.1
Downloaded zerofrom v0.1.6
Downloaded bitflags v2.5.0
Downloaded base64 v0.22.1
Downloaded percent-encoding v2.3.1
Downloaded gix-revision v0.31.1
Downloaded sha1_smol v1.0.0
Downloaded scopeguard v1.2.0
Downloaded gix-features v0.39.1
Downloaded gix-diff v0.49.0
Downloaded elliptic-curve v0.13.8
Downloaded either v1.11.0
Downloaded diff v0.1.13
Downloaded byteorder v1.5.0
Downloaded gix-commitgraph v0.25.1
Downloaded ed25519 v1.5.3
Downloaded data-encoding v2.5.0
Downloaded colored v2.1.0
Downloaded chacha20 v0.9.1
Downloaded amplify_syn v2.0.1
Downloaded universal-hash v0.5.1
Downloaded salsa20 v0.10.2
Downloaded cyphernet v0.5.2
Downloaded crc32fast v1.4.0
Downloaded anstyle-parse v0.2.3
Downloaded gix-revwalk v0.17.0
Downloaded zerovec-derive v0.10.3
Downloaded rand_chacha v0.3.1
Downloaded generic-array v0.14.7
Downloaded fastrand v2.1.0
Downloaded escargot v0.5.10
Downloaded equivalent v1.0.1
Downloaded ec25519 v0.1.0
Downloaded cyphergraphy v0.3.0
Downloaded cypheraddr v0.4.0
Downloaded ct-codecs v1.1.1
Downloaded cpufeatures v0.2.12
Downloaded const-oid v0.9.6
Downloaded colorchoice v1.0.0
Downloaded cipher v0.4.4
Downloaded chacha20poly1305 v0.10.1
Downloaded cfg-if v1.0.0
Downloaded cbc v0.1.2
Downloaded block-padding v0.3.3
Downloaded autocfg v1.2.0
Downloaded anstyle v1.0.6
Downloaded icu_provider_macros v1.5.0
Downloaded sqlite3-sys v0.15.2
Downloaded sqlite v0.32.0
Downloaded snapbox-macros v0.3.8
Downloaded libz-sys v1.1.16
Downloaded gix-negotiate v0.17.0
Downloaded gix-hashtable v0.6.0
Downloaded gix-fs v0.12.1
Downloaded gix-date v0.9.4
Downloaded gix-chunk v0.4.11
Downloaded gix-actor v0.33.2
Downloaded git-ref-format-macro v0.3.0
Downloaded git-ref-format-core v0.3.0
Downloaded fxhash v0.2.1
Downloaded form_urlencoded v1.2.1
Downloaded filetime v0.2.23
Downloaded faster-hex v0.9.0
Downloaded data-encoding-macro-internal v0.1.12
Downloaded data-encoding-macro v0.1.14
Downloaded crypto-bigint v0.5.5
Downloaded base64 v0.21.7
Downloaded gix-lock v15.0.1
Downloaded digest v0.10.7
Downloaded base-x v0.2.11
Downloaded arc-swap v1.7.1
Downloaded gix-command v0.4.1
Downloaded git-ref-format v0.3.0
Downloaded ff v0.13.0
Downloaded crypto-common v0.1.6
Downloaded crossbeam-utils v0.8.19
Downloaded bytesize v2.0.1
Downloaded anstyle-query v1.0.2
Downloaded gix-hash v0.15.1
Downloaded gix-credentials v0.26.0
Downloaded ghash v0.5.1
Downloaded dyn-clone v1.0.17
Downloaded displaydoc v0.2.5
Downloaded ctr v0.9.2
Downloaded bcrypt-pbkdf v0.10.0
Downloaded base64 v0.13.1
Downloaded base32 v0.4.0
Downloaded emojis v0.6.4
Downloaded ecdsa v0.16.9
Downloaded chrono v0.4.38
Downloaded base16ct v0.2.0
Downloaded aho-corasick v1.1.3
Downloaded aes-gcm v0.10.3
Downloaded aes v0.8.4
Downloaded rand_core v0.6.4
Downloaded amplify v4.6.0
Downloaded amplify_derive v4.0.0
Compiling libc v0.2.155
Compiling proc-macro2 v1.0.92
Compiling unicode-ident v1.0.12
Checking cfg-if v1.0.0
Compiling shlex v1.3.0
Compiling version_check v0.9.4
Checking memchr v2.7.2
Compiling typenum v1.17.0
Compiling quote v1.0.36
Compiling jobserver v0.1.31
Checking getrandom v0.2.15
Compiling generic-array v0.14.7
Compiling syn v2.0.89
Compiling cc v1.2.2
Checking aho-corasick v1.1.3
Checking regex-syntax v0.8.5
Compiling serde v1.0.219
Checking rand_core v0.6.4
Checking smallvec v1.13.2
Checking crypto-common v0.1.6
Checking log v0.4.21
Checking fastrand v2.1.0
Compiling autocfg v1.2.0
Checking subtle v2.5.0
Checking regex-automata v0.4.9
Checking bitflags v2.5.0
Compiling syn v1.0.109
Checking once_cell v1.19.0
Checking stable_deref_trait v1.2.0
Checking zeroize v1.7.0
Checking cpufeatures v0.2.12
Checking tinyvec_macros v0.1.1
Checking tinyvec v1.6.0
Compiling synstructure v0.13.1
Compiling crc32fast v1.4.0
Checking bstr v1.9.1
Checking unicode-normalization v0.1.23
Checking block-padding v0.3.3
Compiling icu_locid_transform_data v1.5.1
Checking litemap v0.7.5
Checking writeable v0.5.5
Checking inout v0.1.3
Compiling serde_derive v1.0.219
Compiling zerofrom-derive v0.1.6
Compiling yoke-derive v0.7.5
Compiling zerovec-derive v0.10.3
Compiling displaydoc v0.2.5
Checking zerofrom v0.1.6
Checking yoke v0.7.5
Compiling icu_provider_macros v1.5.0
Checking zerovec v0.10.4
Checking block-buffer v0.10.4
Compiling pkg-config v0.3.30
Compiling icu_properties_data v1.5.1
Checking digest v0.10.7
Checking tinystr v0.7.6
Checking cipher v0.4.4
Checking icu_locid v1.5.0
Compiling icu_normalizer_data v1.5.1
Checking icu_collections v1.5.0
Compiling thiserror v1.0.69
Checking icu_provider v1.5.0
Compiling thiserror-impl v1.0.69
Checking icu_locid_transform v1.5.0
Checking utf16_iter v1.0.5
Checking utf8_iter v1.0.4
Checking write16 v1.0.0
Compiling rustix v0.38.34
Checking icu_properties v1.5.1
Checking sha2 v0.10.8
Checking linux-raw-sys v0.4.13
Checking hashbrown v0.14.3
Checking percent-encoding v2.3.1
Checking itoa v1.0.11
Checking form_urlencoded v1.2.1
Checking universal-hash v0.5.1
Checking opaque-debug v0.3.1
Compiling vcpkg v0.2.15
Compiling amplify_syn v2.0.1
Checking icu_normalizer v1.5.0
Checking idna_adapter v1.2.0
Checking idna v1.0.3
Checking url v2.5.4
Compiling libz-sys v1.1.16
Compiling data-encoding v2.5.0
Checking byteorder v1.5.0
Compiling amplify_derive v4.0.0
Checking amplify_num v0.5.2
Compiling data-encoding-macro-internal v0.1.12
Checking signature v1.6.4
Checking ascii v1.1.0
Checking data-encoding-macro v0.1.14
Checking ed25519 v1.5.3
Checking tempfile v3.10.1
Compiling libgit2-sys v0.17.0+1.8.1
Checking aead v0.5.2
Compiling proc-macro-error-attr v1.0.4
Checking base-x v0.2.11
Checking ct-codecs v1.1.1
Checking ec25519 v0.1.0
Checking multibase v0.9.1
Checking poly1305 v0.8.0
Checking chacha20 v0.9.1
Checking amplify v4.6.0
Compiling proc-macro-error v1.0.4
Checking cyphergraphy v0.3.0
Checking keccak v0.1.5
Compiling git-ref-format-core v0.3.0
Checking sha3 v0.10.8
Checking polyval v0.6.2
Checking hmac v0.12.1
Compiling sqlite3-src v0.5.1
Checking ppv-lite86 v0.2.17
Checking base32 v0.4.0
Checking base64ct v1.6.0
Compiling thiserror v2.0.12
Checking pem-rfc7468 v0.7.0
Checking rand_chacha v0.3.1
Checking cypheraddr v0.4.0
Compiling git-ref-format-macro v0.3.0
Checking ghash v0.5.1
Checking pbkdf2 v0.12.2
Checking chacha20poly1305 v0.10.1
Checking ctr v0.9.2
Checking aes v0.8.4
Compiling thiserror-impl v2.0.12
Compiling crossbeam-utils v0.8.19
Checking aes-gcm v0.10.3
Checking git-ref-format v0.3.0
Checking noise-framework v0.4.0
Checking socks5-client v0.4.1
Checking rand v0.8.5
Checking ssh-encoding v0.2.0
Checking blowfish v0.9.1
Checking cbc v0.1.2
Checking radicle-std-ext v0.1.0
Checking ssh-cipher v0.2.0
Checking bcrypt-pbkdf v0.10.0
Checking cyphernet v0.5.2
Checking signature v2.2.0
Checking ssh-key v0.6.6
Checking crossbeam-channel v0.5.15
Checking qcheck v1.0.0
Checking radicle-ssh v0.9.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-ssh)
Compiling num-traits v0.2.18
Compiling serde_json v1.0.116
Checking equivalent v1.0.1
Checking indexmap v2.2.6
Checking ryu v1.0.17
Checking tree-sitter-language v0.1.2
Checking siphasher v1.0.1
Checking nonempty v0.9.0
Compiling lock_api v0.4.11
Checking radicle-dag v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-dag)
Checking lazy_static v1.5.0
Compiling parking_lot_core v0.9.9
Compiling anyhow v1.0.82
Checking anstyle-query v1.0.2
Checking gix-trace v0.1.12
Checking scopeguard v1.2.0
Checking iana-time-zone v0.1.60
Checking chrono v0.4.38
Checking colored v2.1.0
Checking localtime v1.3.1
Checking bytesize v2.0.1
Checking utf8parse v0.2.1
Checking base64 v0.21.7
Checking anstyle-parse v0.2.3
Checking parking_lot v0.12.1
Checking faster-hex v0.9.0
Checking errno v0.3.9
Checking sem_safe v0.2.0
Checking colorchoice v1.0.0
Checking anstyle v1.0.6
Checking signals_receipts v0.2.0
Checking gix-utils v0.1.14
Checking anstream v0.6.13
Checking adler2 v2.0.0
Checking snapbox-macros v0.3.8
Checking miniz_oxide v0.8.8
Checking gix-hash v0.15.1
Checking similar v2.5.0
Checking normalize-line-endings v0.3.0
Checking same-file v1.0.6
Checking snapbox v0.4.17
Checking walkdir v2.5.0
Checking flate2 v1.1.1
Checking radicle-signals v0.11.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-signals)
Checking prodash v29.0.2
Compiling tree-sitter v0.24.4
Checking sha1_smol v1.0.0
Checking gix-features v0.39.1
Checking yansi v0.5.1
Checking unicode-segmentation v1.11.0
Checking numtoa v0.1.0
Checking diff v0.1.13
Checking pretty_assertions v1.4.0
Compiling xattr v1.3.1
Compiling filetime v0.2.23
Compiling escargot v0.5.10
Checking home v0.5.9
Checking lexopt v0.3.0
Checking gix-path v0.10.15
Compiling tar v0.4.40
Checking termion v2.0.3
Checking newline-converter v0.3.0
Checking fxhash v0.2.1
Checking regex v1.11.1
Checking dyn-clone v1.0.17
Checking streaming-iterator v0.1.9
Checking unicode-width v0.1.11
Checking inquire v0.7.5
Compiling radicle-surf v0.22.0
Checking termion v3.0.0
Checking unicode-display-width v0.3.0
Compiling tree-sitter-ruby v0.23.1
Compiling tree-sitter-c v0.23.2
Compiling tree-sitter-html v0.23.2
Compiling tree-sitter-python v0.23.4
Compiling tree-sitter-rust v0.23.2
Compiling tree-sitter-typescript v0.23.2
Compiling tree-sitter-css v0.23.1
Compiling tree-sitter-md v0.3.2
Compiling tree-sitter-go v0.23.4
Compiling tree-sitter-toml-ng v0.6.0
Compiling tree-sitter-bash v0.23.3
Compiling tree-sitter-json v0.24.8
Checking sqlite3-sys v0.15.2
Checking sqlite v0.32.0
Checking base64 v0.13.1
Checking nonempty v0.5.0
Compiling radicle-cli v0.12.1 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-cli)
Checking tree-sitter-highlight v0.24.4
Checking timeago v0.4.2
Checking jiff v0.2.1
Checking winnow v0.6.26
Checking gix-hashtable v0.6.0
Checking gix-validate v0.9.4
Checking memmap2 v0.9.4
Checking gix-chunk v0.4.11
Checking gix-fs v0.12.1
Checking gix-commitgraph v0.25.1
Checking gix-tempfile v15.0.0
Checking shell-words v1.1.0
Checking gix-lock v15.0.1
Checking gix-command v0.4.1
Checking gix-url v0.28.2
Checking gix-config-value v0.14.12
Checking gix-quote v0.4.15
Checking gix-sec v0.10.12
Checking gix-prompt v0.9.1
Checking gix-packetline v0.18.4
Checking gix-credentials v0.26.0
Checking gix-transport v0.44.0
Checking gix-shallow v0.1.0
Compiling maybe-async v0.2.10
Checking arc-swap v1.7.1
Checking popol v3.0.0
Checking gix-date v0.9.4
Checking either v1.11.0
Checking gix-actor v0.33.2
Checking io-reactor v0.5.2
Checking gix-object v0.46.1
Checking salsa20 v0.10.2
Checking socket2 v0.5.7
Compiling radicle-node v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-node)
Checking siphasher v0.3.11
Checking bloomy v1.2.0
Checking netservices v0.8.0
Checking gix-revwalk v0.17.0
Checking gix-diff v0.49.0
Checking gix-ref v0.49.1
Checking scrypt v0.11.0
Checking gix-revision v0.31.1
Checking gix-traverse v0.43.1
Checking gix-refspec v0.27.0
Checking gix-pack v0.56.0
Checking gix-negotiate v0.17.0
Checking radicle-systemd v0.9.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-systemd)
Checking gix-protocol v0.47.0
Compiling qcheck-macros v1.0.0
Compiling radicle-remote-helper v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-remote-helper)
Checking gix-odb v0.66.0
Checking phf_shared v0.11.3
Checking phf v0.11.3
Checking emojis v0.6.4
Checking git2 v0.19.0
Checking radicle-git-ext v0.8.0
Checking radicle-term v0.12.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-term)
Checking radicle-crypto v0.11.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-crypto)
Checking radicle-cob v0.13.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-cob)
Checking radicle-crdt v0.1.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-crdt)
Checking radicle v0.14.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle)
Checking radicle-cli-test v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-cli-test)
Checking radicle-fetch v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-fetch)
Checking radicle-tools v0.9.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-tools)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 42.70s
+ cargo build --all-targets --workspace
Compiling cfg-if v1.0.0
Compiling libc v0.2.155
Compiling shlex v1.3.0
Compiling memchr v2.7.2
Compiling typenum v1.17.0
Compiling regex-syntax v0.8.5
Compiling jobserver v0.1.31
Compiling getrandom v0.2.15
Compiling aho-corasick v1.1.3
Compiling cc v1.2.2
Compiling rand_core v0.6.4
Compiling generic-array v0.14.7
Compiling smallvec v1.13.2
Compiling regex-automata v0.4.9
Compiling serde v1.0.219
Compiling crypto-common v0.1.6
Compiling fastrand v2.1.0
Compiling log v0.4.21
Compiling bitflags v2.5.0
Compiling subtle v2.5.0
Compiling zerofrom v0.1.6
Compiling once_cell v1.19.0
Compiling stable_deref_trait v1.2.0
Compiling yoke v0.7.5
Compiling zeroize v1.7.0
Compiling cpufeatures v0.2.12
Compiling zerovec v0.10.4
Compiling tinyvec_macros v0.1.1
Compiling tinyvec v1.6.0
Compiling bstr v1.9.1
Compiling unicode-normalization v0.1.23
Compiling tinystr v0.7.6
Compiling block-padding v0.3.3
Compiling writeable v0.5.5
Compiling litemap v0.7.5
Compiling inout v0.1.3
Compiling block-buffer v0.10.4
Compiling icu_locid v1.5.0
Compiling adler2 v2.0.0
Compiling digest v0.10.7
Compiling miniz_oxide v0.8.8
Compiling cipher v0.4.4
Compiling crc32fast v1.4.0
Compiling icu_provider v1.5.0
Compiling icu_locid_transform_data v1.5.1
Compiling icu_properties_data v1.5.1
Compiling icu_locid_transform v1.5.0
Compiling icu_collections v1.5.0
Compiling icu_normalizer_data v1.5.1
Compiling write16 v1.0.0
Compiling utf16_iter v1.0.5
Compiling utf8_iter v1.0.4
Compiling thiserror v1.0.69
Compiling sha2 v0.10.8
Compiling linux-raw-sys v0.4.13
Compiling icu_properties v1.5.1
Compiling hashbrown v0.14.3
Compiling itoa v1.0.11
Compiling percent-encoding v2.3.1
Compiling form_urlencoded v1.2.1
Compiling rustix v0.38.34
Compiling universal-hash v0.5.1
Compiling opaque-debug v0.3.1
Compiling libz-sys v1.1.16
Compiling byteorder v1.5.0
Compiling data-encoding v2.5.0
Compiling amplify_num v0.5.2
Compiling icu_normalizer v1.5.0
Compiling signature v1.6.4
Compiling ascii v1.1.0
Compiling idna_adapter v1.2.0
Compiling idna v1.0.3
Compiling amplify v4.6.0
Compiling url v2.5.4
Compiling ed25519 v1.5.3
Compiling tempfile v3.10.1
Compiling data-encoding-macro v0.1.14
Compiling libgit2-sys v0.17.0+1.8.1
Compiling aead v0.5.2
Compiling base-x v0.2.11
Compiling ct-codecs v1.1.1
Compiling ec25519 v0.1.0
Compiling multibase v0.9.1
Compiling poly1305 v0.8.0
Compiling chacha20 v0.9.1
Compiling cyphergraphy v0.3.0
Compiling keccak v0.1.5
Compiling polyval v0.6.2
Compiling sha3 v0.10.8
Compiling git-ref-format-core v0.3.0
Compiling hmac v0.12.1
Compiling sqlite3-src v0.5.1
Compiling ppv-lite86 v0.2.17
Compiling base64ct v1.6.0
Compiling base32 v0.4.0
Compiling cypheraddr v0.4.0
Compiling pem-rfc7468 v0.7.0
Compiling rand_chacha v0.3.1
Compiling git-ref-format-macro v0.3.0
Compiling pbkdf2 v0.12.2
Compiling ghash v0.5.1
Compiling chacha20poly1305 v0.10.1
Compiling ctr v0.9.2
Compiling aes v0.8.4
Compiling thiserror v2.0.12
Compiling git-ref-format v0.3.0
Compiling noise-framework v0.4.0
Compiling aes-gcm v0.10.3
Compiling rand v0.8.5
Compiling socks5-client v0.4.1
Compiling ssh-encoding v0.2.0
Compiling blowfish v0.9.1
Compiling cbc v0.1.2
Compiling radicle-std-ext v0.1.0
Compiling ssh-cipher v0.2.0
Compiling bcrypt-pbkdf v0.10.0
Compiling cyphernet v0.5.2
Compiling crossbeam-utils v0.8.19
Compiling signature v2.2.0
Compiling ssh-key v0.6.6
Compiling crossbeam-channel v0.5.15
Compiling qcheck v1.0.0
Compiling radicle-ssh v0.9.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-ssh)
Compiling equivalent v1.0.1
Compiling indexmap v2.2.6
Compiling tree-sitter-language v0.1.2
Compiling ryu v1.0.17
Compiling num-traits v0.2.18
Compiling serde_json v1.0.116
Compiling siphasher v1.0.1
Compiling nonempty v0.9.0
Compiling radicle-dag v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-dag)
Compiling lazy_static v1.5.0
Compiling scopeguard v1.2.0
Compiling iana-time-zone v0.1.60
Compiling anstyle-query v1.0.2
Compiling gix-trace v0.1.12
Compiling chrono v0.4.38
Compiling lock_api v0.4.11
Compiling parking_lot_core v0.9.9
Compiling colored v2.1.0
Compiling bytesize v2.0.1
Compiling localtime v1.3.1
Compiling utf8parse v0.2.1
Compiling base64 v0.21.7
Compiling anstyle-parse v0.2.3
Compiling parking_lot v0.12.1
Compiling anyhow v1.0.82
Compiling faster-hex v0.9.0
Compiling errno v0.3.9
Compiling sem_safe v0.2.0
Compiling colorchoice v1.0.0
Compiling anstyle v1.0.6
Compiling signals_receipts v0.2.0
Compiling anstream v0.6.13
Compiling gix-utils v0.1.14
Compiling snapbox-macros v0.3.8
Compiling gix-hash v0.15.1
Compiling similar v2.5.0
Compiling same-file v1.0.6
Compiling normalize-line-endings v0.3.0
Compiling walkdir v2.5.0
Compiling snapbox v0.4.17
Compiling radicle-signals v0.11.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-signals)
Compiling prodash v29.0.2
Compiling flate2 v1.1.1
Compiling tree-sitter v0.24.4
Compiling sha1_smol v1.0.0
Compiling gix-features v0.39.1
Compiling unicode-segmentation v1.11.0
Compiling yansi v0.5.1
Compiling numtoa v0.1.0
Compiling diff v0.1.13
Compiling pretty_assertions v1.4.0
Compiling xattr v1.3.1
Compiling filetime v0.2.23
Compiling home v0.5.9
Compiling lexopt v0.3.0
Compiling gix-path v0.10.15
Compiling tar v0.4.40
Compiling termion v2.0.3
Compiling newline-converter v0.3.0
Compiling fxhash v0.2.1
Compiling regex v1.11.1
Compiling dyn-clone v1.0.17
Compiling streaming-iterator v0.1.9
Compiling unicode-width v0.1.11
Compiling inquire v0.7.5
Compiling radicle-surf v0.22.0
Compiling escargot v0.5.10
Compiling termion v3.0.0
Compiling unicode-display-width v0.3.0
Compiling tree-sitter-html v0.23.2
Compiling tree-sitter-ruby v0.23.1
Compiling tree-sitter-md v0.3.2
Compiling tree-sitter-python v0.23.4
Compiling tree-sitter-rust v0.23.2
Compiling tree-sitter-css v0.23.1
Compiling tree-sitter-go v0.23.4
Compiling tree-sitter-toml-ng v0.6.0
Compiling tree-sitter-json v0.24.8
Compiling tree-sitter-bash v0.23.3
Compiling tree-sitter-c v0.23.2
Compiling tree-sitter-typescript v0.23.2
Compiling sqlite3-sys v0.15.2
Compiling sqlite v0.32.0
Compiling radicle-cli v0.12.1 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-cli)
Compiling nonempty v0.5.0
Compiling base64 v0.13.1
Compiling tree-sitter-highlight v0.24.4
Compiling timeago v0.4.2
Compiling jiff v0.2.1
Compiling winnow v0.6.26
Compiling gix-hashtable v0.6.0
Compiling gix-validate v0.9.4
Compiling memmap2 v0.9.4
Compiling gix-chunk v0.4.11
Compiling gix-commitgraph v0.25.1
Compiling gix-fs v0.12.1
Compiling gix-tempfile v15.0.0
Compiling shell-words v1.1.0
Compiling gix-command v0.4.1
Compiling gix-lock v15.0.1
Compiling gix-url v0.28.2
Compiling gix-config-value v0.14.12
Compiling gix-quote v0.4.15
Compiling gix-date v0.9.4
Compiling gix-actor v0.33.2
Compiling gix-object v0.46.1
Compiling gix-sec v0.10.12
Compiling gix-prompt v0.9.1
Compiling gix-packetline v0.18.4
Compiling gix-revwalk v0.17.0
Compiling gix-traverse v0.43.1
Compiling gix-revision v0.31.1
Compiling gix-diff v0.49.0
Compiling gix-refspec v0.27.0
Compiling gix-pack v0.56.0
Compiling gix-negotiate v0.17.0
Compiling gix-ref v0.49.1
Compiling gix-transport v0.44.0
Compiling gix-credentials v0.26.0
Compiling gix-shallow v0.1.0
Compiling arc-swap v1.7.1
Compiling gix-protocol v0.47.0
Compiling gix-odb v0.66.0
Compiling popol v3.0.0
Compiling either v1.11.0
Compiling io-reactor v0.5.2
Compiling salsa20 v0.10.2
Compiling socket2 v0.5.7
Compiling siphasher v0.3.11
Compiling radicle-node v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-node)
Compiling netservices v0.8.0
Compiling bloomy v1.2.0
Compiling scrypt v0.11.0
Compiling radicle-systemd v0.9.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-systemd)
Compiling radicle-remote-helper v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-remote-helper)
Compiling phf_shared v0.11.3
Compiling phf v0.11.3
Compiling emojis v0.6.4
Compiling git2 v0.19.0
Compiling radicle-git-ext v0.8.0
Compiling radicle-term v0.12.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-term)
Compiling radicle-crypto v0.11.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-crypto)
Compiling radicle-cob v0.13.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-cob)
Compiling radicle-crdt v0.1.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-crdt)
Compiling radicle v0.14.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle)
Compiling radicle-cli-test v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-cli-test)
Compiling radicle-fetch v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-fetch)
Compiling radicle-tools v0.9.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-tools)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 05s
+ cargo doc --workspace --no-deps
Compiling syn v1.0.109
Checking radicle-ssh v0.9.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-ssh)
Checking radicle-dag v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-dag)
Checking radicle-signals v0.11.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-signals)
Compiling radicle-cli v0.12.1 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-cli)
Checking radicle-term v0.12.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-term)
Compiling radicle-node v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-node)
Checking radicle-systemd v0.9.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-systemd)
Documenting radicle-term v0.12.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-term)
Documenting radicle-signals v0.11.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-signals)
Documenting radicle-ssh v0.9.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-ssh)
Documenting radicle-dag v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-dag)
Documenting radicle-systemd v0.9.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-systemd)
Compiling amplify_syn v2.0.1
Compiling proc-macro-error v1.0.4
Compiling data-encoding-macro-internal v0.1.12
Compiling git-ref-format-macro v0.3.0
Compiling amplify_derive v4.0.0
Checking git-ref-format v0.3.0
Checking data-encoding-macro v0.1.14
Checking radicle-git-ext v0.8.0
Checking multibase v0.9.1
Checking radicle-surf v0.22.0
Checking amplify v4.6.0
Checking cyphergraphy v0.3.0
Checking io-reactor v0.5.2
Checking cypheraddr v0.4.0
Checking noise-framework v0.4.0
Checking socks5-client v0.4.1
Checking cyphernet v0.5.2
Checking radicle-crypto v0.11.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-crypto)
Checking netservices v0.8.0
Documenting radicle-crypto v0.11.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-crypto)
Checking radicle-cob v0.13.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-cob)
Documenting radicle-cob v0.13.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-cob)
Documenting radicle-crdt v0.1.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-crdt)
Checking radicle v0.14.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle)
Documenting radicle v0.14.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle)
Checking radicle-cli-test v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-cli-test)
Checking radicle-fetch v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-fetch)
Documenting radicle-cli-test v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-cli-test)
Documenting radicle-fetch v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-fetch)
Documenting radicle-cli v0.12.1 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-cli)
Documenting radicle-node v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-node)
Documenting radicle-remote-helper v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-remote-helper)
Checking radicle-remote-helper v0.10.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-remote-helper)
Documenting radicle-tools v0.9.0 (/e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/radicle-tools)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 10.02s
Generated /e8e7d7f4-045d-4e99-af1f-7f8d44c6b8f0/w/target/doc/radicle/index.html and 22 other files
+ cargo test --workspace --no-fail-fast
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.14s
Running unittests src/lib.rs (target/debug/deps/radicle-abf72186975b3732)
running 171 tests
test canonical::formatter::test::ascii_control_characters ... ok
test canonical::formatter::test::securesystemslib_asserts ... ok
test canonical::formatter::test::ordered_nested_object ... ok
test cob::cache::migrations::_2::tests::test_migration_2 ... ok
test cob::cache::tests::test_migrate_to ... ok
test cob::common::test::test_color ... ok
test cob::common::test::test_emojis ... ok
test cob::cache::migrations::_2::tests::test_patch_json_deserialization ... ok
test cob::cache::tests::test_check_version ... ok
test cob::identity::test::prop_json_eq_str ... ok
test cob::identity::test::test_identity_redact_revision ... ok
test cob::identity::test::test_identity_remove_delegate_concurrent ... ok
test cob::identity::test::test_identity_update_rejected ... ok
test cob::identity::test::test_identity_reject_concurrent ... ok
test cob::identity::test::test_identity_updates ... ok
test cob::issue::cache::tests::test_counts ... ok
test cob::issue::cache::tests::test_get ... ok
test cob::issue::cache::tests::test_is_empty ... ok
test cob::issue::cache::tests::test_list ... ok
test cob::issue::cache::tests::test_list_by_status ... ok
test cob::issue::cache::tests::test_remove ... ok
test cob::identity::test::test_valid_identity ... ok
test cob::identity::test::test_identity_updates_concurrent ... ok
test cob::issue::test::test_embeds ... ok
test cob::issue::test::test_embeds_edit ... ok
test cob::issue::test::test_invalid_actions ... ok
test cob::identity::test::test_identity_updates_concurrent_outdated ... ok
test cob::issue::test::test_invalid_tx ... ok
test cob::issue::test::test_invalid_tx_reference ... ok
test cob::issue::test::test_invalid_cob ... ok
test cob::issue::test::test_concurrency ... ok
test cob::issue::test::test_issue_create_and_assign ... ok
test cob::issue::test::test_issue_all ... ok
test cob::issue::test::test_issue_comment ... ok
test cob::issue::test::test_issue_comment_redact ... ok
test cob::issue::test::test_issue_create_and_change_state ... ok
test cob::issue::test::test_issue_create_and_get ... ok
test cob::issue::test::test_issue_create_and_reassign ... ok
test cob::issue::test::test_issue_create_and_unassign ... ok
test cob::issue::test::test_issue_edit ... ok
test cob::issue::test::test_issue_edit_description ... ok
test cob::issue::test::test_issue_multilines ... ok
test cob::issue::test::test_issue_state_serde ... ok
test cob::issue::test::test_ordering ... ok
test cob::issue::test::test_issue_label ... ok
test cob::issue::test::test_issue_react ... ok
test cob::patch::cache::tests::test_get ... ok
test cob::patch::cache::tests::test_is_empty ... ok
test cob::patch::cache::tests::test_list ... ok
test cob::issue::test::test_issue_reply ... ok
test cob::patch::cache::tests::test_list_by_status ... ok
test cob::patch::test::test_json ... ok
test cob::patch::test::test_json_serialization ... ok
test cob::patch::test::test_patch_create_and_get ... ok
test cob::patch::cache::tests::test_remove ... ok
test cob::patch::test::test_patch_discussion ... ok
test cob::patch::cache::tests::test_counts ... ok
test cob::patch::test::test_patch_merge ... ok
test cob::patch::test::test_patch_redact ... ok
test cob::patch::test::test_patch_review_comment ... ok
test cob::patch::test::test_patch_review ... ok
test cob::patch::test::test_patch_review_duplicate ... ok
test cob::patch::test::test_patch_review_edit ... ok
test cob::patch::test::test_patch_review_remove_summary ... ok
test cob::patch::test::test_patch_review_edit_comment ... ok
test cob::patch::test::test_reactions_json_serialization ... ok
test cob::patch::test::test_revision_edit_redact ... ok
test cob::patch::test::test_revision_reaction ... ok
test cob::patch::test::test_revision_review_merge_redacted ... ok
test cob::patch::test::test_patch_review_revision_redact ... ok
test cob::thread::tests::test_comment_edit_missing ... ok
test cob::thread::tests::test_comment_edit_redacted ... ok
test cob::thread::tests::test_comment_redact_missing ... ok
test cob::thread::tests::test_duplicate_comments ... ok
test cob::thread::tests::test_edit_comment ... ok
test cob::thread::tests::test_redact_comment ... ok
test cob::thread::tests::test_timeline ... ok
test cob::patch::test::test_patch_update ... ok
test git::canonical::tests::test_quorum_merges ... ok
test git::canonical::tests::test_quorum ... ok
test git::test::test_version_from_str ... ok
test git::test::test_version_ord ... ok
test identity::did::test::test_did_encode_decode ... ok
test identity::did::test::test_did_vectors ... ok
test identity::doc::id::test::prop_from_str ... ok
test cob::thread::tests::prop_ordering ... ok
test identity::doc::test::test_canonical_doc ... ok
test git::canonical::tests::test_quorum_properties ... ok
test identity::doc::test::test_duplicate_dids ... ok
test identity::doc::test::test_future_version_error ... ok
test identity::doc::test::test_is_valid_version ... ok
test identity::doc::test::test_canonical_example ... ok
test identity::doc::test::test_not_found ... ok
test identity::doc::test::test_parse_version ... ok
test identity::doc::test::test_visibility_json ... ok
test identity::project::test::test_project_name ... ok
test node::address::store::test::test_alias ... ok
test node::address::store::test::test_disconnected ... ok
test node::address::store::test::test_disconnected_ban ... ok
test node::address::store::test::test_empty ... ok
test node::address::store::test::test_entries ... ok
test node::address::store::test::test_get_none ... ok
test node::address::store::test::test_insert_and_get ... ok
test node::address::store::test::test_insert_and_remove ... ok
test node::address::store::test::test_insert_and_update ... ok
test node::address::store::test::test_insert_duplicate ... ok
test node::address::store::test::test_node_aliases ... ok
test node::address::store::test::test_remove_nothing ... ok
test node::db::test::test_version ... ok
test node::features::test::test_operations ... ok
test node::notifications::store::test::test_branch_notifications ... ok
test node::notifications::store::test::test_clear ... ok
test identity::doc::test::test_max_delegates ... ok
test node::notifications::store::test::test_cob_notifications ... ok
test node::notifications::store::test::test_counts_by_repo ... ok
test node::notifications::store::test::test_duplicate_notifications ... ok
test node::notifications::store::test::test_notification_status ... ok
test node::policy::store::test::test_follow_and_unfollow_node ... ok
test node::policy::store::test::test_node_aliases ... ok
test node::policy::store::test::test_node_policy ... ok
test node::policy::store::test::test_node_policies ... ok
test node::policy::store::test::test_repo_policies ... ok
test node::policy::store::test::test_repo_policy ... ok
test node::policy::store::test::test_update_alias ... ok
test node::policy::store::test::test_seed_and_unseed_repo ... ok
test node::policy::store::test::test_update_scope ... ok
test node::refs::store::test::test_set_and_delete ... ok
test node::refs::store::test::test_count ... ok
test node::refs::store::test::test_set_and_get ... ok
test node::routing::test::test_count ... ok
test node::routing::test::test_entries ... ok
test node::routing::test::test_insert_and_get ... ok
test node::routing::test::test_insert_and_get_resources ... ok
test node::routing::test::test_insert_and_remove ... ok
test node::routing::test::test_insert_duplicate ... ok
test node::routing::test::test_insert_existing_updated_time ... ok
test node::routing::test::test_len ... ok
test node::routing::test::test_remove_many ... ok
test node::routing::test::test_remove_redundant ... ok
test node::routing::test::test_update_existing_multi ... ok
test node::test::test_alias ... ok
test node::test::test_command_result ... ok
test node::test::test_user_agent ... ok
test node::timestamp::tests::test_timestamp_max ... ok
test profile::test::canonicalize_home ... ok
test profile::test::test_config ... ok
test node::routing::test::test_prune ... ok
test rad::tests::test_checkout ... ok
test rad::tests::test_init ... ok
test serde_ext::test::test_localtime ... ok
test serde_ext::test::test_localtime_ext ... ok
test rad::tests::test_fork ... ok
test storage::git::tests::test_references_of ... ok
test storage::git::tests::test_sign_refs ... ok
test storage::git::transport::local::url::test::test_url_parse ... ok
test storage::git::transport::local::url::test::test_url_to_string ... ok
test storage::git::transport::remote::url::test::test_url_parse ... ok
test storage::refs::tests::prop_canonical_roundtrip ... ok
test storage::git::tests::test_remote_refs ... ok
test storage::tests::test_storage ... ok
test test::assert::test::assert_with_message ... ok
test test::assert::test::test_assert_no_move ... ok
test test::assert::test::test_assert_panic_0 - should panic ... ok
test test::assert::test::test_assert_panic_1 - should panic ... ok
test test::assert::test::test_assert_panic_2 - should panic ... ok
test test::assert::test::test_assert_succeed ... ok
test test::assert::test::test_panic_message ... ok
test version::test::test_version ... ok
test storage::refs::tests::test_rid_verification ... ok
test identity::doc::test::prop_encode_decode ... ok
test cob::patch::cache::tests::test_find_by_revision ... ok
test result: ok. 171 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 2.56s
Running unittests src/lib.rs (target/debug/deps/radicle_cli-73b70621e2033db2)
running 14 tests
test commands::rad_patch::review::builder::tests::test_review_comments_before ... ok
test commands::rad_inspect::test::test_tree ... ok
test commands::rad_patch::review::builder::tests::test_review_comments_basic ... ok
test commands::rad_patch::review::builder::tests::test_review_comments_multiline ... ok
test commands::rad_patch::review::builder::tests::test_review_comments_split_hunk ... ok
test git::ddiff::tests::diff_encode_decode_ddiff_hunk ... ok
test git::pretty_diff::test::test_pretty ... ignored
test git::unified_diff::test::test_diff_content_encode_decode_content ... ok
test git::unified_diff::test::test_diff_encode_decode_diff ... ok
test terminal::format::test::test_bytes ... ok
test terminal::format::test::test_strip_comments ... ok
test terminal::patch::test::test_edit_display_message ... ok
test terminal::patch::test::test_create_display_message ... ok
test terminal::patch::test::test_update_display_message ... ok
test result: ok. 13 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.01s
Running unittests src/main.rs (target/debug/deps/rad-53a859daa4b4b5cb)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running tests/commands.rs (target/debug/deps/commands-70a016e590d1668d)
running 96 tests
test framework_home ... ok
test git_push_amend ... ok
test git_push_and_fetch ... ok
test git_push_diverge ... ok
test rad_auth ... ok
test rad_auth_errors ... ok
test rad_block ... ok
test rad_checkout ... ok
test git_tag ... ok
test git_push_rollback ... ok
test git_push_converge ... ok
test rad_clone ... ok
test rad_clean ... ok
test rad_clone_all ... ok
test rad_clone_directory ... ok
test rad_clone_unknown ... ok
test rad_clone_connect ... ok
test rad_cob_multiset ... ok
test rad_cob_log ... ok
test rad_cob_migrate ... ok
test rad_cob_update_identity ... ok
test rad_cob_show ... ok
test rad_clone_partial_fail ... ok
test rad_config ... ok
test rad_cob_update ... ok
test rad_diff ... ok
test rad_id_collaboration ... ignored, slow
test rad_id ... ok
test rad_id_conflict ... ok
test rad_id_private ... ok
test rad_id_threshold ... ok
test rad_id_multi_delegate ... ok
test rad_id_unknown_field ... ok
test rad_id_update_delete_field ... ok
test rad_id_threshold_soft_fork ... ok
test rad_init ... ok
test rad_init_existing ... ok
test rad_init_no_git ... ok
test rad_init_no_seed ... ok
test rad_init_private ... ok
test rad_inbox ... ok
test rad_init_private_clone ... ok
test rad_init_private_no_seed ... ok
test rad_init_private_clone_seed ... ok
test rad_init_private_seed ... ok
test rad_fetch ... ok
test rad_fork ... ok
test rad_init_sync_not_connected ... ok
test rad_init_sync_preferred ... ok
test rad_init_with_existing_remote ... ok
test rad_inspect ... ok
test rad_issue ... ok
test rad_job ... ok
test rad_merge_after_update ... ok
test rad_merge_no_ff ... ok
test rad_merge_via_push ... ok
test rad_node_connect ... ok
test rad_patch ... ok
test rad_node ... ok
test rad_patch_ahead_behind ... ok
test rad_patch_change_base ... ok
test rad_patch_checkout ... ok
test rad_patch_checkout_revision ... ok
test rad_patch_checkout_force ... ok
test rad_patch_detached_head ... ok
test rad_patch_diff ... ok
test rad_init_sync_timeout ... ok
test rad_init_sync_and_clone ... ok
test rad_patch_draft ... ok
test rad_patch_edit ... ok
test rad_patch_fetch_2 ... ok
test rad_patch_merge_draft ... ok
test rad_patch_fetch_1 ... ok
test rad_patch_delete ... ok
test rad_patch_revert_merge ... ok
test rad_patch_open_explore ... ok
test rad_patch_update ... ok
test rad_publish ... ok
test rad_patch_via_push ... ok
test rad_review_by_hunk ... ok
test rad_seed_and_follow ... ok
test rad_remote ... ok
test rad_self ... ok
test rad_seed_many ... ok
test rad_sync_without_node ... ok
test rad_push_and_pull_patches ... ok
test rad_unseed ... ok
test rad_unseed_many ... ok
test rad_watch ... ok
test rad_sync ... ok
test test_clone_without_seeds ... ok
test test_cob_deletion ... ok
test test_cob_replication ... ok
test rad_workflow ... ok
test rad_patch_pull_update ... ok
test test_replication_via_seed ... ok
test result: ok. 95 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 71.72s
Running unittests src/lib.rs (target/debug/deps/radicle_cli_test-84eb7147e836d8b4)
running 3 tests
test tests::test_parse ... ok
test tests::test_run ... ok
test tests::test_example_spaced_brackets ... ok
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_cob-d71a27f25b0bf568)
running 8 tests
test object::tests::test_serde ... ok
test tests::invalid_parse_refstr ... ok
test tests::parse_refstr ... ok
test tests::roundtrip ... ok
test type_name::test::valid_typenames ... ok
test tests::update_cob ... ok
test tests::list_cobs ... ok
test tests::traverse_cobs ... ok
test result: ok. 8 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s
Running unittests src/lib.rs (target/debug/deps/radicle_crdt-80a0089eece3048d)
running 24 tests
test immutable::test::test_merge_equal ... ok
test immutable::test::test_merge_inequal - should panic ... ok
test lwwmap::tests::test_insert ... ok
test lwwmap::tests::test_insert_remove ... ok
test lwwmap::tests::test_is_empty ... ok
test lwwmap::tests::test_remove_insert ... ok
test lwwreg::tests::prop_semilattice ... ok
test lwwreg::tests::test_merge ... ok
test lwwreg::tests::test_set_get ... ok
test gset::tests::prop_semilattice ... ok
test lwwset::tests::test_insert ... ok
test lwwset::tests::test_insert_remove ... ok
test lwwset::tests::test_remove_insert ... ok
test redactable::test::prop_invariants ... ok
test lwwset::tests::prop_semilattice ... ok
test redactable::test::test_both_present ... ok
test redactable::test::test_redacted ... ok
test test::test_generator ... ok
test tests::prop_bool_laws ... ok
test tests::prop_option_laws ... ok
test tests::test_bool ... ok
test tests::test_option ... ok
test lwwmap::tests::prop_semilattice ... ok
test gmap::tests::prop_semilattice ... ok
test result: ok. 24 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s
Running unittests src/lib.rs (target/debug/deps/radicle_crypto-656d6ad780485650)
running 12 tests
test ssh::fmt::test::test_key ... ok
test ssh::fmt::test::test_fingerprint ... ok
test ssh::keystore::tests::test_init_no_passphrase ... ok
test ssh::test::prop_encode_decode_sk ... ok
test ssh::test::test_agent_encoding_remove ... ok
test ssh::test::test_agent_encoding_sign ... ok
test tests::prop_encode_decode ... ok
test tests::test_e25519_dh ... ok
test tests::test_encode_decode ... ok
test tests::prop_key_equality ... ok
test ssh::keystore::tests::test_signer ... ok
test ssh::keystore::tests::test_init_passphrase ... ok
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.83s
Running unittests src/lib.rs (target/debug/deps/radicle_dag-569b0a6854e86d64)
running 20 tests
test tests::test_dependencies ... ok
test tests::test_cycle ... ok
test tests::test_contains ... ok
test tests::test_diamond ... ok
test tests::test_fold_reject ... ok
test tests::test_fold_multiple_roots ... ok
test tests::test_fold_diamond ... ok
test tests::test_complex ... ok
test tests::test_fold_sorting_1 ... ok
test tests::test_fold_sorting_2 ... ok
test tests::test_get ... ok
test tests::test_is_empty ... ok
test tests::test_len ... ok
test tests::test_merge_2 ... ok
test tests::test_merge_1 ... ok
test tests::test_prune_1 ... ok
test tests::test_remove ... ok
test tests::test_prune_2 ... ok
test tests::test_prune_by_sorting ... ok
test tests::test_siblings ... ok
test result: ok. 20 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_fetch-f3e3a6a9676bbcde)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_node-aa693bd6007c5100)
running 113 tests
test control::tests::test_control_socket ... ok
test deserializer::test::prop_decode_next ... ok
test deserializer::test::test_decode_next ... ok
test deserializer::test::test_unparsed ... ok
test service::filter::test::test_parameters ... ok
test service::limiter::test::test_limitter_different_rates ... ok
test service::limiter::test::test_limitter_multi ... ok
test service::limiter::test::test_limitter_refill ... ok
test service::filter::test::test_sizes ... ok
test service::gossip::store::test::test_announced ... ok
test control::tests::test_seed_unseed ... ok
test service::message::tests::test_ref_remote_limit ... ok
test service::message::tests::test_inventory_limit ... ok
test service::message::tests::prop_refs_announcement_signing ... ok
test tests::e2e::missing_default_branch ... ok
test tests::e2e::missing_delegate_default_branch ... ok
test tests::e2e::test_background_foreground_fetch ... ok
test tests::e2e::test_catchup_on_refs_announcements ... ok
test tests::e2e::test_channel_reader_limit ... ok
test tests::e2e::test_clone ... ok
test service::message::tests::test_node_announcement_validate ... ok
test tests::e2e::test_dont_fetch_owned_refs ... ok
test tests::e2e::test_fetch_followed_remotes ... ok
test tests::e2e::test_connection_crossing ... ok
test tests::e2e::test_fetch_preserve_owned_refs ... ok
test tests::e2e::test_fetch_unseeded ... ok
test tests::e2e::test_concurrent_fetches ... ok
test tests::e2e::test_inventory_sync_basic ... ok
test tests::e2e::test_fetch_up_to_date ... ok
test tests::e2e::test_large_fetch ... ok
test tests::e2e::test_migrated_clone ... ok
test tests::e2e::test_missing_remote ... ok
test tests::e2e::test_multiple_offline_inits ... ok
test tests::e2e::test_non_fastforward_sigrefs ... ok
test tests::e2e::test_outdated_delegate_sigrefs ... ok
test tests::e2e::test_outdated_sigrefs ... ok
test tests::e2e::test_replication ... ok
test tests::e2e::test_replication_invalid ... ok
test tests::e2e::test_replication_ref_in_sigrefs ... ok
test tests::e2e::test_inventory_sync_bridge ... ok
test tests::e2e::test_inventory_sync_ring ... ok
test tests::e2e::test_inventory_sync_star ... ok
test tests::test_announcement_rebroadcast ... ok
test tests::test_announcement_rebroadcast_duplicates ... ok
test tests::test_announcement_rebroadcast_timestamp_filtered ... ok
test tests::test_announcement_relay ... ok
test tests::test_connection_kept_alive ... ok
test tests::test_disconnecting_unresponsive_peer ... ok
test tests::test_fetch_missing_inventory_on_gossip ... ok
test tests::test_fetch_missing_inventory_on_schedule ... ok
test tests::test_inbound_connection ... ok
test tests::test_inventory_decode ... ok
test tests::test_init_and_seed ... ok
test tests::test_inventory_relay ... ok
test tests::test_inventory_relay_bad_timestamp ... ok
test tests::test_inventory_sync ... ok
test tests::test_maintain_connections ... ok
test tests::test_maintain_connections_failed_attempt ... ok
test tests::test_maintain_connections_transient ... ok
test tests::test_outbound_connection ... ok
test tests::test_inventory_pruning ... ok
test tests::test_persistent_peer_connect ... ok
test tests::test_persistent_peer_reconnect_success ... ok
test tests::test_persistent_peer_reconnect_attempt ... ok
test tests::test_ping_response ... ok
test tests::test_queued_fetch_from_ann_same_rid ... ok
test tests::test_queued_fetch_from_command_same_rid ... ok
test tests::test_redundant_connect ... ok
test tests::test_queued_fetch_max_capacity ... ok
test tests::test_refs_announcement_followed ... ok
test tests::test_refs_announcement_fetch_trusted_no_inventory ... ok
test tests::test_refs_announcement_no_subscribe ... ok
test tests::test_refs_announcement_offline ... ok
test tests::test_refs_announcement_relay_private ... ok
test tests::test_refs_announcement_relay_public ... ok
test tests::test_refs_synced_event ... ok
test tests::test_seeding ... ok
test wire::frame::test::test_stream_id ... ok
test wire::message::tests::prop_addr ... ok
test tests::test_seed_repo_subscribe ... ok
test wire::message::tests::prop_message_encode_decode ... ok
test tests::prop_inventory_exchange_dense ... ok
test wire::message::tests::test_inv_ann_max_size ... ok
test wire::message::tests::test_node_ann_max_size ... ok
test wire::message::tests::test_pingpong_encode_max_size ... ok
test wire::message::tests::test_pingpong_encode_size_overflow ... ok
test wire::message::tests::prop_zero_bytes_encode_decode ... ok
test wire::protocol::test::test_inventory_ann_with_extension ... ok
test wire::protocol::test::test_pong_message_with_extension ... ok
test wire::tests::prop_filter ... ok
test wire::tests::prop_id ... ok
test wire::tests::prop_oid ... ok
test wire::tests::prop_pubkey ... ok
test wire::tests::prop_refs ... ok
test wire::tests::prop_signature ... ok
test wire::tests::prop_signed_refs ... ok
test wire::tests::prop_string ... ok
test wire::tests::prop_tuple ... ok
test wire::tests::prop_u16 ... ok
test wire::tests::prop_u32 ... ok
test wire::tests::prop_u64 ... ok
test wire::tests::prop_u8 ... ok
test wire::tests::prop_vec ... ok
test wire::tests::test_alias ... ok
test wire::tests::test_bounded_vec_limit ... ok
test wire::tests::test_filter_invalid ... ok
test wire::tests::test_string ... ok
test wire::varint::test::prop_encode_decode ... ok
test wire::varint::test::test_encode_overflow - should panic ... ok
test wire::varint::test::test_encoding ... ok
test wire::message::tests::test_refs_ann_max_size ... ok
test wire::message::tests::prop_message_decoder ... ok
test tests::test_announcement_message_amplification ... ok
test result: ok. 113 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 16.78s
Running unittests src/main.rs (target/debug/deps/radicle_node-504ee4b435228c2a)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_remote_helper-2e4b7b727340b385)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/git-remote-rad.rs (target/debug/deps/git_remote_rad-d0042b3732b04f97)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_signals-85fbd454a3bf0ac6)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_ssh-9b2e5dfee304f504)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_systemd-1b4159ebc8f1d2b1)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_term-96f7925768df62cb)
running 20 tests
test cell::test::test_width ... ok
test ansi::tests::colors_enabled ... ok
test ansi::tests::colors_disabled ... ok
test ansi::tests::wrapping ... ok
test element::test::test_width ... ok
test element::test::test_truncate ... ok
test table::test::test_table_border ... ok
test table::test::test_table ... ok
test table::test::test_table_border_maximized ... ok
test table::test::test_table_border_truncated ... ok
test table::test::test_table_truncate ... ok
test table::test::test_table_unicode ... ok
test table::test::test_truncate ... ok
test table::test::test_table_unicode_truncate ... ok
test textarea::test::test_wrapping ... ok
test textarea::test::test_wrapping_code_block ... ok
test textarea::test::test_wrapping_fenced_block ... ok
test textarea::test::test_wrapping_paragraphs ... ok
test vstack::test::test_vstack ... ok
test vstack::test::test_vstack_maximize ... ok
test result: ok. 20 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/rad-agent.rs (target/debug/deps/rad_agent-51f43656e6abbd73)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/rad-cli-demo.rs (target/debug/deps/rad_cli_demo-e50753c471a8e7ae)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/rad-init.rs (target/debug/deps/rad_init-06e5f7c49fe94697)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/rad-merge.rs (target/debug/deps/rad_merge-d7a1f0d33bca31d0)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/rad-push.rs (target/debug/deps/rad_push-238b5dbc225d7d57)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/rad-self.rs (target/debug/deps/rad_self-9c16f638a097810a)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/rad-set-canonical-refs.rs (target/debug/deps/rad_set_canonical_refs-3c5afa1b9f2bcfe6)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_cli
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_cli_test
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_cob
running 1 test
test radicle-cob/src/backend/git/stable.rs - backend::git::stable::with_advanced_timestamp (line 56) ... ignored
test result: ok. 0 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_crdt
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_crypto
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_dag
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_fetch
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_node
running 6 tests
test radicle-node/src/bounded.rs - bounded::BoundedVec<T,N>::max (line 96) ... ok
test radicle-node/src/bounded.rs - bounded::BoundedVec<T,N>::truncate (line 50) ... ok
test radicle-node/src/bounded.rs - bounded::BoundedVec<T,N>::collect_from (line 30) ... ok
test radicle-node/src/bounded.rs - bounded::BoundedVec<T,N>::unbound (line 149) ... ok
test radicle-node/src/bounded.rs - bounded::BoundedVec<T,N>::with_capacity (line 66) ... ok
test radicle-node/src/bounded.rs - bounded::BoundedVec<T,N>::push (line 122) ... ok
test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.56s
Doc-tests radicle_remote_helper
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_signals
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_ssh
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_systemd
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_term
running 1 test
test radicle-term/src/table.rs - table (line 4) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.40s
Exit code: 0
{
"response": "finished",
"result": "success"
}