Remove delegate names from identity doc
We are working on other mechanisms to help identify keys, and this is just redundant. Also update some of the CLI examples to create a canonical example for documentation. Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
parent
c33aa87589
commit
207d2133f9
|
|
@ -3,19 +3,19 @@ To create your first radicle project, navigate to a git repository, and run
|
|||
the `init` command:
|
||||
|
||||
```
|
||||
$ rad init --name acme --description "Acme's repository" --no-confirm
|
||||
$ rad init --name heartwood --description "Radicle Heartwood Protocol & Stack" --no-confirm
|
||||
|
||||
Initializing local 🌱 project in .
|
||||
|
||||
ok Project acme created
|
||||
ok Project heartwood created
|
||||
{
|
||||
"name": "acme",
|
||||
"description": "Acme's repository",
|
||||
"name": "heartwood",
|
||||
"description": "Radicle Heartwood Protocol & Stack",
|
||||
"default-branch": "master"
|
||||
}
|
||||
|
||||
|
||||
Your project id is rad:z4EXAgDJk11uFThfVir5FivKhiAeK. You can show it any time by running:
|
||||
Your project id is rad:zb2rNRYmmz7SLZ7xMjM7dddswC3K. You can show it any time by running:
|
||||
rad .
|
||||
|
||||
To publish your project to the network, run:
|
||||
|
|
@ -27,5 +27,5 @@ Projects can be listed with the `ls` command:
|
|||
|
||||
```
|
||||
$ rad ls
|
||||
acme rad:z4EXAgDJk11uFThfVir5FivKhiAeK cdf76ce Acme's repository
|
||||
heartwood rad:zb2rNRYmmz7SLZ7xMjM7dddswC3K cdf76ce Radicle Heartwood Protocol & Stack
|
||||
```
|
||||
|
|
|
|||
|
|
@ -110,8 +110,8 @@ pub fn execute(options: Options, profile: &Profile) -> anyhow::Result<PathBuf> {
|
|||
spinner.finish();
|
||||
|
||||
let remotes = delegates
|
||||
.iter()
|
||||
.map(|d| *d.id)
|
||||
.into_iter()
|
||||
.map(|did| *did)
|
||||
.filter(|id| id != profile.id())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ pub fn clone(id: Id, _interactive: Interactive, ctx: impl term::Context) -> anyh
|
|||
let delegates = doc
|
||||
.delegates
|
||||
.iter()
|
||||
.map(|d| *d.id)
|
||||
.map(|d| **d)
|
||||
.filter(|id| id != profile.id())
|
||||
.collect::<Vec<_>>();
|
||||
let default_branch = doc.payload.default_branch.clone();
|
||||
|
|
|
|||
|
|
@ -8,12 +8,16 @@ pub struct MockSigner {
|
|||
|
||||
impl MockSigner {
|
||||
pub fn new(rng: &mut fastrand::Rng) -> Self {
|
||||
let mut bytes: [u8; 32] = [0; 32];
|
||||
let mut seed: [u8; 32] = [0; 32];
|
||||
|
||||
for byte in &mut bytes {
|
||||
for byte in &mut seed {
|
||||
*byte = rng.u8(..);
|
||||
}
|
||||
let seed = Seed::new(bytes);
|
||||
Self::from_seed(seed)
|
||||
}
|
||||
|
||||
pub fn from_seed(seed: [u8; 32]) -> Self {
|
||||
let seed = Seed::new(seed);
|
||||
let keypair = KeyPair::from_seed(seed);
|
||||
|
||||
Self::from(SecretKey::from(keypair.sk))
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ async fn delegates_projects_handler(
|
|||
let Ok((_, head)) = repo.head() else { return None };
|
||||
let Ok(Doc { payload, delegates, .. }) = repo.project_of(ctx.profile.id()) else { return None };
|
||||
|
||||
if !delegates.iter().any(|d| d.id == delegate) {
|
||||
if !delegates.iter().any(|d| *d == delegate) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use thiserror::Error;
|
|||
use crate::crypto;
|
||||
|
||||
pub use crypto::PublicKey;
|
||||
pub use project::{Delegate, Doc, Id, IdError};
|
||||
pub use project::{Doc, Id, IdError};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum DidError {
|
||||
|
|
|
|||
|
|
@ -68,24 +68,6 @@ impl DocError {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct Delegate {
|
||||
pub name: String,
|
||||
pub id: Did,
|
||||
}
|
||||
|
||||
impl Delegate {
|
||||
fn matches(&self, key: &PublicKey) -> bool {
|
||||
&self.id.0 == key
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Delegate> for PublicKey {
|
||||
fn from(delegate: Delegate) -> Self {
|
||||
delegate.id.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct Payload {
|
||||
|
|
@ -105,7 +87,7 @@ pub struct Doc<V> {
|
|||
pub payload: Payload,
|
||||
#[serde(flatten)]
|
||||
pub extensions: BTreeMap<Namespace, serde_json::Value>,
|
||||
pub delegates: NonEmpty<Delegate>,
|
||||
pub delegates: NonEmpty<Did>,
|
||||
pub threshold: usize,
|
||||
|
||||
#[serde(skip)]
|
||||
|
|
@ -125,13 +107,9 @@ impl Doc<Verified> {
|
|||
}
|
||||
|
||||
/// Attempt to add a new delegate to the document. Returns `true` if it wasn't there before.
|
||||
pub fn delegate(&mut self, name: String, key: crypto::PublicKey) -> bool {
|
||||
let delegate = Delegate {
|
||||
name,
|
||||
id: Did::from(key),
|
||||
};
|
||||
|
||||
if self.delegates.iter().all(|d| d.id != delegate.id) {
|
||||
pub fn delegate(&mut self, key: crypto::PublicKey) -> bool {
|
||||
let delegate = Did::from(key);
|
||||
if self.delegates.iter().all(|id| id != &delegate) {
|
||||
self.delegates.push(delegate);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -233,7 +211,7 @@ impl Doc<Unverified> {
|
|||
name: String,
|
||||
description: String,
|
||||
default_branch: BranchName,
|
||||
delegate: Delegate,
|
||||
delegate: Did,
|
||||
) -> Self {
|
||||
Self {
|
||||
payload: Payload {
|
||||
|
|
@ -252,7 +230,7 @@ impl Doc<Unverified> {
|
|||
name: String,
|
||||
description: String,
|
||||
default_branch: BranchName,
|
||||
delegates: NonEmpty<Delegate>,
|
||||
delegates: NonEmpty<Did>,
|
||||
threshold: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
|
@ -289,15 +267,6 @@ impl Doc<Unverified> {
|
|||
"number of delegates cannot exceed 255",
|
||||
));
|
||||
}
|
||||
if self
|
||||
.delegates
|
||||
.iter()
|
||||
.any(|d| d.name.is_empty() || d.name.len() > MAX_STRING_LENGTH)
|
||||
{
|
||||
return Err(VerificationError::Delegates(
|
||||
"delegate name must not be empty and must not exceed 255 bytes",
|
||||
));
|
||||
}
|
||||
if self.delegates.is_empty() {
|
||||
return Err(VerificationError::Delegates(
|
||||
"delegate list cannot be empty",
|
||||
|
|
@ -471,7 +440,7 @@ impl Identity<Untrusted> {
|
|||
// Check that enough delegates signed this next version.
|
||||
let quorum = signatures
|
||||
.iter()
|
||||
.filter(|(key, _)| trusted.delegates.iter().any(|d| d.matches(key)))
|
||||
.filter(|(key, _)| trusted.delegates.iter().any(|d| &**d == key))
|
||||
.count();
|
||||
if quorum < trusted.threshold {
|
||||
return Err(IdentityError::QuorumNotReached(quorum, trusted.threshold));
|
||||
|
|
@ -498,6 +467,7 @@ mod test {
|
|||
use radicle_crypto::Signer as _;
|
||||
|
||||
use crate::rad;
|
||||
use crate::storage::git::transport;
|
||||
use crate::storage::git::Storage;
|
||||
use crate::storage::{ReadStorage, WriteStorage};
|
||||
use crate::test::arbitrary;
|
||||
|
|
@ -506,6 +476,35 @@ mod test {
|
|||
use super::*;
|
||||
use qcheck_macros::quickcheck;
|
||||
|
||||
#[test]
|
||||
fn test_canonical_example() {
|
||||
let tempdir = tempfile::tempdir().unwrap();
|
||||
let storage = Storage::open(tempdir.path().join("storage")).unwrap();
|
||||
|
||||
transport::local::register(storage.clone());
|
||||
|
||||
let delegate = MockSigner::from_seed([0xff; 32]);
|
||||
let (repo, _) = fixtures::repository(tempdir.path().join("working"));
|
||||
let (id, _, _) = rad::init(
|
||||
&repo,
|
||||
"heartwood",
|
||||
"Radicle Heartwood Protocol & Stack",
|
||||
git::refname!("master"),
|
||||
&delegate,
|
||||
&storage,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
delegate.public_key().to_human(),
|
||||
String::from("z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi")
|
||||
);
|
||||
assert_eq!(
|
||||
id.to_human(),
|
||||
String::from("rad:zb2rNRYmmz7SLZ7xMjM7dddswC3K")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_found() {
|
||||
let tempdir = tempfile::tempdir().unwrap();
|
||||
|
|
@ -559,7 +558,7 @@ mod test {
|
|||
.unwrap();
|
||||
|
||||
// Add Bob as a delegate, and sign it.
|
||||
proj.delegate("bob".to_owned(), *bob.public_key());
|
||||
proj.delegate(*bob.public_key());
|
||||
proj.threshold = 2;
|
||||
proj.sign(&alice)
|
||||
.and_then(|(_, sig)| {
|
||||
|
|
@ -573,7 +572,7 @@ mod test {
|
|||
.unwrap();
|
||||
|
||||
// Add Eve as a delegate, and sign it.
|
||||
proj.delegate("eve".to_owned(), *eve.public_key());
|
||||
proj.delegate(*eve.public_key());
|
||||
proj.sign(&alice)
|
||||
.and_then(|(_, alice_sig)| {
|
||||
proj.sign(&bob).and_then(|(_, bob_sig)| {
|
||||
|
|
|
|||
|
|
@ -59,11 +59,7 @@ pub fn init<G: Signer>(
|
|||
) -> Result<(Id, identity::Doc<Verified>, SignedRefs<Verified>), InitError> {
|
||||
// TODO: Better error when project id already exists in storage, but remote doesn't.
|
||||
let pk = signer.public_key();
|
||||
let delegate = identity::Delegate {
|
||||
// TODO: Use actual user name.
|
||||
name: String::from("anonymous"),
|
||||
id: identity::Did::from(*pk),
|
||||
};
|
||||
let delegate = identity::Did::from(*pk);
|
||||
let doc = identity::Doc::initial(
|
||||
name.to_owned(),
|
||||
description.to_owned(),
|
||||
|
|
@ -348,7 +344,7 @@ mod tests {
|
|||
use radicle_crypto::test::signer::MockSigner;
|
||||
|
||||
use crate::git::{name::component, qualified, Qualified};
|
||||
use crate::identity::{Delegate, Did};
|
||||
use crate::identity::Did;
|
||||
use crate::storage::git::transport;
|
||||
use crate::storage::git::Storage;
|
||||
use crate::storage::{ReadStorage, WriteStorage};
|
||||
|
|
@ -403,13 +399,7 @@ mod tests {
|
|||
assert_eq!(project.name, "acme");
|
||||
assert_eq!(project.description, "Acme's repo");
|
||||
assert_eq!(project.default_branch, git::refname!("master"));
|
||||
assert_eq!(
|
||||
project.delegates.first(),
|
||||
&Delegate {
|
||||
name: String::from("anonymous"),
|
||||
id: Did::from(public_key),
|
||||
}
|
||||
);
|
||||
assert_eq!(project.delegates.first(), &Did::from(public_key));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -500,7 +500,7 @@ impl ReadRepository for Repository {
|
|||
|
||||
let mut heads = Vec::new();
|
||||
for delegate in project.delegates.iter() {
|
||||
let r = self.reference_oid(&delegate.id, &branch_ref)?.into();
|
||||
let r = self.reference_oid(delegate, &branch_ref)?.into();
|
||||
|
||||
heads.push(r);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use qcheck::Arbitrary;
|
|||
|
||||
use crate::collections::HashMap;
|
||||
use crate::git;
|
||||
use crate::identity::{project::Delegate, project::Doc, Did, Id};
|
||||
use crate::identity::{project::Doc, Did, Id};
|
||||
use crate::storage;
|
||||
use crate::storage::refs::{Refs, SignedRefs};
|
||||
use crate::test::storage::MockStorage;
|
||||
|
|
@ -57,21 +57,12 @@ impl Arbitrary for Did {
|
|||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Delegate {
|
||||
fn arbitrary(g: &mut qcheck::Gen) -> Self {
|
||||
Self {
|
||||
name: String::arbitrary(g),
|
||||
id: Did::arbitrary(g),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Doc<Unverified> {
|
||||
fn arbitrary(g: &mut qcheck::Gen) -> Self {
|
||||
let name = String::arbitrary(g);
|
||||
let description = String::arbitrary(g);
|
||||
let default_branch = git::RefString::try_from(String::arbitrary(g)).unwrap();
|
||||
let delegate = Delegate::arbitrary(g);
|
||||
let delegate = Did::arbitrary(g);
|
||||
|
||||
Self::initial(name, description, default_branch, delegate)
|
||||
}
|
||||
|
|
@ -91,16 +82,11 @@ impl Arbitrary for Doc<Verified> {
|
|||
.collect::<String>()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let delegates: NonEmpty<_> = iter::repeat_with(|| Delegate {
|
||||
name: iter::repeat_with(|| rng.alphanumeric())
|
||||
.take(rng.usize(1..16))
|
||||
.collect(),
|
||||
id: Did::arbitrary(g),
|
||||
})
|
||||
.take(rng.usize(1..6))
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let delegates: NonEmpty<_> = iter::repeat_with(|| Did::arbitrary(g))
|
||||
.take(rng.usize(1..6))
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let threshold = delegates.len() / 2 + 1;
|
||||
let doc: Doc<Unverified> =
|
||||
Doc::new(name, description, default_branch, delegates, threshold);
|
||||
|
|
|
|||
Loading…
Reference in New Issue