From b63bfc9a34657c5767c507deb7c059e24dd22779 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Tue, 25 Jun 2024 09:06:47 +0100 Subject: refactor: replace keys with signer so that nip46 bunker signing can be added --- src/client.rs | 15 +-- src/git.rs | 176 +++++++++++++++++--------------- src/login.rs | 12 ++- src/repo_ref.rs | 257 ++++++++++++++++++++++++----------------------- src/sub_commands/init.rs | 17 ++-- src/sub_commands/push.rs | 9 +- src/sub_commands/send.rs | 32 +++--- test_utils/src/lib.rs | 9 +- 8 files changed, 283 insertions(+), 244 deletions(-) diff --git a/src/client.rs b/src/client.rs index 56f0e16..9dba528 100644 --- a/src/client.rs +++ b/src/client.rs @@ -34,7 +34,7 @@ pub struct Client { pub trait Connect { fn default() -> Self; fn new(opts: Params) -> Self; - async fn set_keys(&mut self, keys: &nostr::Keys); + async fn set_signer(&mut self, signer: NostrSigner); async fn disconnect(&self) -> Result<()>; fn get_fallback_relays(&self) -> &Vec; fn get_more_fallback_relays(&self) -> &Vec; @@ -91,17 +91,20 @@ impl Connect for Client { } fn new(opts: Params) -> Self { Client { - client: nostr_sdk::Client::new(&opts.keys.unwrap_or(nostr::Keys::generate())), + client: nostr_sdk::ClientBuilder::new() + .signer(&opts.keys.unwrap_or(nostr::Keys::generate())) + // .database( + // SQLiteDatabase::open(get_dirs()?.config_dir().join("cache.sqlite")).await?, + // ) + .build(), fallback_relays: opts.fallback_relays, more_fallback_relays: opts.more_fallback_relays, blaster_relays: opts.blaster_relays, } } - async fn set_keys(&mut self, keys: &nostr::Keys) { - self.client - .set_signer(Some(NostrSigner::Keys(keys.clone()))) - .await; + async fn set_signer(&mut self, signer: NostrSigner) { + self.client.set_signer(Some(signer)).await; } async fn disconnect(&self) -> Result<()> { diff --git a/src/git.rs b/src/git.rs index 7f44861..46687ae 100644 --- a/src/git.rs +++ b/src/git.rs @@ -828,7 +828,7 @@ fn extract_sig_from_patch_tags<'a>( mod tests { use std::fs; - use test_utils::{generate_repo_ref_event, git::GitTestRepo, TEST_KEY_1_KEYS}; + use test_utils::{generate_repo_ref_event, git::GitTestRepo}; use super::*; @@ -1591,10 +1591,12 @@ mod tests { mod apply_patch { + use test_utils::TEST_KEY_1_SIGNER; + use super::*; use crate::{repo_ref::RepoRef, sub_commands::send::generate_patch_event}; - fn generate_patch_from_head_commit(test_repo: &GitTestRepo) -> Result { + async fn generate_patch_from_head_commit(test_repo: &GitTestRepo) -> Result { let original_oid = test_repo.git_repo.head()?.peel_to_commit()?.id(); let git_repo = Repo::from_path(&test_repo.dir)?; generate_patch_event( @@ -1602,7 +1604,7 @@ mod tests { &git_repo.get_root_commit()?, &oid_to_sha1(&original_oid), Some(nostr::EventId::all_zeros()), - &TEST_KEY_1_KEYS, + &TEST_KEY_1_SIGNER, &RepoRef::try_from(generate_repo_ref_event()).unwrap(), None, None, @@ -1610,6 +1612,7 @@ mod tests { &None, &[], ) + .await } fn test_patch_applies_to_repository(patch_event: nostr::Event) -> Result<()> { let test_repo = GitTestRepo::default(); @@ -1649,19 +1652,21 @@ mod tests { use super::*; - #[test] - fn simple_signature_author_committer_same_as_git_user_0_unixtime_no_pgp_signature() + #[tokio::test] + async fn simple_signature_author_committer_same_as_git_user_0_unixtime_no_pgp_signature() -> Result<()> { let source_repo = GitTestRepo::default(); source_repo.populate()?; fs::write(source_repo.dir.join("x1.md"), "some content")?; source_repo.stage_and_commit("add x1.md")?; - test_patch_applies_to_repository(generate_patch_from_head_commit(&source_repo)?) + test_patch_applies_to_repository( + generate_patch_from_head_commit(&source_repo).await?, + ) } - #[test] - fn signature_with_specific_author_time() -> Result<()> { + #[tokio::test] + async fn signature_with_specific_author_time() -> Result<()> { let source_repo = GitTestRepo::default(); source_repo.populate()?; fs::write(source_repo.dir.join("x1.md"), "some content")?; @@ -1675,11 +1680,13 @@ mod tests { None, )?; - test_patch_applies_to_repository(generate_patch_from_head_commit(&source_repo)?) + test_patch_applies_to_repository( + generate_patch_from_head_commit(&source_repo).await?, + ) } - #[test] - fn author_name_and_email_not_current_git_user() -> Result<()> { + #[tokio::test] + async fn author_name_and_email_not_current_git_user() -> Result<()> { let source_repo = GitTestRepo::default(); source_repo.populate()?; fs::write(source_repo.dir.join("x1.md"), "some content")?; @@ -1693,11 +1700,13 @@ mod tests { None, )?; - test_patch_applies_to_repository(generate_patch_from_head_commit(&source_repo)?) + test_patch_applies_to_repository( + generate_patch_from_head_commit(&source_repo).await?, + ) } - #[test] - fn comiiter_name_and_email_not_current_git_user_or_author() -> Result<()> { + #[tokio::test] + async fn comiiter_name_and_email_not_current_git_user_or_author() -> Result<()> { let source_repo = GitTestRepo::default(); source_repo.populate()?; fs::write(source_repo.dir.join("x1.md"), "some content")?; @@ -1715,13 +1724,15 @@ mod tests { )?), )?; - test_patch_applies_to_repository(generate_patch_from_head_commit(&source_repo)?) + test_patch_applies_to_repository( + generate_patch_from_head_commit(&source_repo).await?, + ) } // TODO: pgp signature - #[test] - fn unique_author_and_commiter_details() -> Result<()> { + #[tokio::test] + async fn unique_author_and_commiter_details() -> Result<()> { let source_repo = GitTestRepo::default(); source_repo.populate()?; fs::write(source_repo.dir.join("x1.md"), "some content")?; @@ -1739,13 +1750,15 @@ mod tests { )?), )?; - test_patch_applies_to_repository(generate_patch_from_head_commit(&source_repo)?) + test_patch_applies_to_repository( + generate_patch_from_head_commit(&source_repo).await?, + ) } } } mod apply_patch_chain { - use test_utils::TEST_KEY_1_KEYS; + use test_utils::TEST_KEY_1_SIGNER; use super::*; use crate::{ @@ -1754,8 +1767,8 @@ mod tests { static BRANCH_NAME: &str = "add-example-feature"; // returns original_repo, cover_letter_event, patch_events - fn generate_test_repo_and_events() -> Result<(GitTestRepo, nostr::Event, Vec)> - { + async fn generate_test_repo_and_events() + -> Result<(GitTestRepo, nostr::Event, Vec)> { let original_repo = GitTestRepo::default(); let oid3 = original_repo.populate_with_test_branch()?; let oid2 = original_repo.git_repo.find_commit(oid3)?.parent_id(0)?; @@ -1767,11 +1780,12 @@ mod tests { Some(("test".to_string(), "test".to_string())), &git_repo, &[oid_to_sha1(&oid1), oid_to_sha1(&oid2), oid_to_sha1(&oid3)], - &TEST_KEY_1_KEYS, + &TEST_KEY_1_SIGNER, &RepoRef::try_from(generate_repo_ref_event()).unwrap(), &None, &[], - )?; + ) + .await?; events.reverse(); @@ -1784,9 +1798,9 @@ mod tests { mod when_branch_root_is_tip_of_main { use super::*; - #[test] - fn branch_gets_created_with_name_specified_in_proposal() -> Result<()> { - let (_, _, patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn branch_gets_created_with_name_specified_in_proposal() -> Result<()> { + let (_, _, patch_events) = generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; let git_repo = Repo::from_path(&test_repo.dir)?; @@ -1799,9 +1813,9 @@ mod tests { Ok(()) } - #[test] - fn branch_checked_out() -> Result<()> { - let (_, _, patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn branch_checked_out() -> Result<()> { + let (_, _, patch_events) = generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; let git_repo = Repo::from_path(&test_repo.dir)?; @@ -1813,9 +1827,9 @@ mod tests { Ok(()) } - #[test] - fn patches_get_created_as_commits() -> Result<()> { - let (original_repo, _, patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn patches_get_created_as_commits() -> Result<()> { + let (original_repo, _, patch_events) = generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; let git_repo = Repo::from_path(&test_repo.dir)?; @@ -1827,9 +1841,9 @@ mod tests { Ok(()) } - #[test] - fn branch_tip_is_most_recent_patch() -> Result<()> { - let (original_repo, _, patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn branch_tip_is_most_recent_patch() -> Result<()> { + let (original_repo, _, patch_events) = generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; let git_repo = Repo::from_path(&test_repo.dir)?; @@ -1841,9 +1855,9 @@ mod tests { Ok(()) } - #[test] - fn previously_checked_out_branch_tip_does_not_change() -> Result<()> { - let (_, _, patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn previously_checked_out_branch_tip_does_not_change() -> Result<()> { + let (_, _, patch_events) = generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; let existing_branch = test_repo.get_checked_out_branch_name()?; @@ -1858,9 +1872,9 @@ mod tests { Ok(()) } - #[test] - fn returns_all_patches_applied() -> Result<()> { - let (_, _, patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn returns_all_patches_applied() -> Result<()> { + let (_, _, patch_events) = generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; let git_repo = Repo::from_path(&test_repo.dir)?; @@ -1873,9 +1887,9 @@ mod tests { mod when_branch_root_is_tip_behind_main { use super::*; - #[test] - fn branch_gets_created_with_name_specified_in_proposal() -> Result<()> { - let (_, _, patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn branch_gets_created_with_name_specified_in_proposal() -> Result<()> { + let (_, _, patch_events) = generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; std::fs::write(test_repo.dir.join("m3.md"), "some content")?; @@ -1890,9 +1904,9 @@ mod tests { Ok(()) } - #[test] - fn branch_checked_out() -> Result<()> { - let (_, _, patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn branch_checked_out() -> Result<()> { + let (_, _, patch_events) = generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; std::fs::write(test_repo.dir.join("m3.md"), "some content")?; @@ -1906,9 +1920,9 @@ mod tests { Ok(()) } - #[test] - fn branch_tip_is_most_recent_patch() -> Result<()> { - let (original_repo, _, patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn branch_tip_is_most_recent_patch() -> Result<()> { + let (original_repo, _, patch_events) = generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; std::fs::write(test_repo.dir.join("m3.md"), "some content")?; @@ -1922,9 +1936,9 @@ mod tests { Ok(()) } - #[test] - fn previously_checked_out_branch_tip_does_not_change() -> Result<()> { - let (_, _, patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn previously_checked_out_branch_tip_does_not_change() -> Result<()> { + let (_, _, patch_events) = generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; std::fs::write(test_repo.dir.join("m3.md"), "some content")?; @@ -1941,9 +1955,9 @@ mod tests { Ok(()) } - #[test] - fn returns_all_patches_applied() -> Result<()> { - let (_, _, patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn returns_all_patches_applied() -> Result<()> { + let (_, _, patch_events) = generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; let git_repo = Repo::from_path(&test_repo.dir)?; @@ -1962,9 +1976,10 @@ mod tests { mod when_branch_already_checked_out { use super::*; - #[test] - fn branch_tip_is_most_recent_patch() -> Result<()> { - let (original_repo, _, mut patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn branch_tip_is_most_recent_patch() -> Result<()> { + let (original_repo, _, mut patch_events) = + generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; let git_repo = Repo::from_path(&test_repo.dir)?; @@ -1978,9 +1993,9 @@ mod tests { Ok(()) } - #[test] - fn returns_all_patches_applied() -> Result<()> { - let (_, _, mut patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn returns_all_patches_applied() -> Result<()> { + let (_, _, mut patch_events) = generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; let git_repo = Repo::from_path(&test_repo.dir)?; @@ -1993,9 +2008,10 @@ mod tests { mod when_branch_not_checked_out { use super::*; - #[test] - fn branch_tip_is_most_recent_patch() -> Result<()> { - let (original_repo, _, mut patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn branch_tip_is_most_recent_patch() -> Result<()> { + let (original_repo, _, mut patch_events) = + generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; let git_repo = Repo::from_path(&test_repo.dir)?; @@ -2010,9 +2026,9 @@ mod tests { Ok(()) } - #[test] - fn branch_checked_out() -> Result<()> { - let (_, _, mut patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn branch_checked_out() -> Result<()> { + let (_, _, mut patch_events) = generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; let git_repo = Repo::from_path(&test_repo.dir)?; @@ -2027,9 +2043,9 @@ mod tests { Ok(()) } - #[test] - fn returns_all_patches_applied() -> Result<()> { - let (_, _, mut patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn returns_all_patches_applied() -> Result<()> { + let (_, _, mut patch_events) = generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; let git_repo = Repo::from_path(&test_repo.dir)?; @@ -2048,9 +2064,9 @@ mod tests { mod when_branch_already_checked_out { use super::*; - #[test] - fn returns_all_patches_applied_0() -> Result<()> { - let (_, _, patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn returns_all_patches_applied_0() -> Result<()> { + let (_, _, patch_events) = generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; let git_repo = Repo::from_path(&test_repo.dir)?; @@ -2063,9 +2079,9 @@ mod tests { mod when_branch_not_checked_out { use super::*; - #[test] - fn branch_checked_out() -> Result<()> { - let (_, _, patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn branch_checked_out() -> Result<()> { + let (_, _, patch_events) = generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; let git_repo = Repo::from_path(&test_repo.dir)?; @@ -2080,9 +2096,9 @@ mod tests { Ok(()) } - #[test] - fn returns_all_patches_applied_0() -> Result<()> { - let (_, _, patch_events) = generate_test_repo_and_events()?; + #[tokio::test] + async fn returns_all_patches_applied_0() -> Result<()> { + let (_, _, patch_events) = generate_test_repo_and_events().await?; let test_repo = GitTestRepo::default(); test_repo.populate()?; let git_repo = Repo::from_path(&test_repo.dir)?; diff --git a/src/login.rs b/src/login.rs index ad9902d..e1669c1 100644 --- a/src/login.rs +++ b/src/login.rs @@ -3,7 +3,9 @@ use std::str::FromStr; use anyhow::{bail, Context, Result}; use nostr::PublicKey; use nostr_database::Order; -use nostr_sdk::{Alphabet, FromBech32, JsonUtil, Kind, NostrDatabase, SingleLetterTag, ToBech32}; +use nostr_sdk::{ + Alphabet, FromBech32, JsonUtil, Kind, NostrDatabase, NostrSigner, SingleLetterTag, ToBech32, +}; use nostr_sqlite::SQLiteDatabase; #[cfg(not(test))] @@ -28,7 +30,7 @@ pub async fn launch( #[cfg(test)] client: Option<&MockConnect>, #[cfg(not(test))] client: Option<&Client>, change_user: bool, -) -> Result<(nostr::Keys, UserRef)> { +) -> Result<(NostrSigner, UserRef)> { if let Ok(keys) = match get_keys_without_prompts(git_repo, nsec, password, change_user) { Ok(keys) => Ok(keys), Err(error) => { @@ -73,7 +75,7 @@ pub async fn launch( // get user ref let user_ref = get_user_details(&keys.public_key(), client, git_repo).await?; print_logged_in_as(&user_ref, client.is_none())?; - Ok((keys, user_ref)) + Ok((NostrSigner::Keys(keys), user_ref)) } else { fresh_login(git_repo, client, change_user).await } @@ -172,7 +174,7 @@ async fn fresh_login( #[cfg(test)] client: Option<&MockConnect>, #[cfg(not(test))] client: Option<&Client>, always_save: bool, -) -> Result<(nostr::Keys, UserRef)> { +) -> Result<(NostrSigner, UserRef)> { // prompt for nsec let mut prompt = "login with nsec"; let keys = loop { @@ -196,7 +198,7 @@ async fn fresh_login( } let user_ref = get_user_details(&keys.public_key(), client, git_repo).await?; print_logged_in_as(&user_ref, client.is_none())?; - Ok((keys, user_ref)) + Ok((NostrSigner::Keys(keys), user_ref)) } fn save_keys(git_repo: &Repo, keys: &nostr::Keys, always_save: bool) -> Result<()> { diff --git a/src/repo_ref.rs b/src/repo_ref.rs index 7016257..2b0d024 100644 --- a/src/repo_ref.rs +++ b/src/repo_ref.rs @@ -2,6 +2,7 @@ use std::{fs::File, io::BufReader, str::FromStr}; use anyhow::{bail, Context, Result}; use nostr::{nips::nip19::Nip19, FromBech32, PublicKey, Tag, TagStandard, ToBech32}; +use nostr_sdk::NostrSigner; use serde::{Deserialize, Serialize}; #[cfg(not(test))] @@ -93,66 +94,67 @@ impl TryFrom for RepoRef { pub static REPO_REF_KIND: u16 = 30_617; impl RepoRef { - pub fn to_event(&self, keys: &nostr::Keys) -> Result { - nostr_sdk::EventBuilder::new( - nostr::event::Kind::Custom(REPO_REF_KIND), - "", - [ - vec![ - Tag::identifier(if self.identifier.to_string().is_empty() { - // fiatjaf thought a random string. its not in the draft nip. - // thread_rng() - // .sample_iter(&Alphanumeric) - // .take(15) - // .map(char::from) - // .collect() - - // an identifier based on first commit is better so that users dont - // accidentally create two seperate identifiers for the same repo - // there is a hesitancy to use the commit id - // in another conversaion with fiatjaf he suggested the first 6 character of - // the commit id - // here we are using 7 which is the standard for shorthand commit id - self.root_commit.to_string()[..7].to_string() - } else { - self.identifier.to_string() - }), - Tag::custom( - nostr::TagKind::Custom(std::borrow::Cow::Borrowed("r")), - vec![self.root_commit.to_string(), "euc".to_string()], - ), - Tag::from_standardized(TagStandard::Name(self.name.clone())), - Tag::from_standardized(TagStandard::Description(self.description.clone())), - Tag::custom( - nostr::TagKind::Custom(std::borrow::Cow::Borrowed("clone")), - self.git_server.clone(), - ), - Tag::custom( - nostr::TagKind::Custom(std::borrow::Cow::Borrowed("web")), - self.web.clone(), - ), - Tag::custom( - nostr::TagKind::Custom(std::borrow::Cow::Borrowed("relays")), - self.relays.clone(), - ), - Tag::custom( - nostr::TagKind::Custom(std::borrow::Cow::Borrowed("maintainers")), - self.maintainers - .iter() - .map(std::string::ToString::to_string) - .collect::>(), - ), - Tag::custom( - nostr::TagKind::Custom(std::borrow::Cow::Borrowed("alt")), - vec![format!("git repository: {}", self.name.clone())], - ), - ], - // code languages and hashtags - ] - .concat(), - ) - .to_event(keys) - .context("failed to create repository reference event") + pub async fn to_event(&self, signer: &NostrSigner) -> Result { + signer + .sign_event_builder(nostr_sdk::EventBuilder::new( + nostr::event::Kind::Custom(REPO_REF_KIND), + "", + [ + vec![ + Tag::identifier(if self.identifier.to_string().is_empty() { + // fiatjaf thought a random string. its not in the draft nip. + // thread_rng() + // .sample_iter(&Alphanumeric) + // .take(15) + // .map(char::from) + // .collect() + + // an identifier based on first commit is better so that users dont + // accidentally create two seperate identifiers for the same repo + // there is a hesitancy to use the commit id + // in another conversaion with fiatjaf he suggested the first 6 + // character of the commit id + // here we are using 7 which is the standard for shorthand commit id + self.root_commit.to_string()[..7].to_string() + } else { + self.identifier.to_string() + }), + Tag::custom( + nostr::TagKind::Custom(std::borrow::Cow::Borrowed("r")), + vec![self.root_commit.to_string(), "euc".to_string()], + ), + Tag::from_standardized(TagStandard::Name(self.name.clone())), + Tag::from_standardized(TagStandard::Description(self.description.clone())), + Tag::custom( + nostr::TagKind::Custom(std::borrow::Cow::Borrowed("clone")), + self.git_server.clone(), + ), + Tag::custom( + nostr::TagKind::Custom(std::borrow::Cow::Borrowed("web")), + self.web.clone(), + ), + Tag::custom( + nostr::TagKind::Custom(std::borrow::Cow::Borrowed("relays")), + self.relays.clone(), + ), + Tag::custom( + nostr::TagKind::Custom(std::borrow::Cow::Borrowed("maintainers")), + self.maintainers + .iter() + .map(std::string::ToString::to_string) + .collect::>(), + ), + Tag::custom( + nostr::TagKind::Custom(std::borrow::Cow::Borrowed("alt")), + vec![format!("git repository: {}", self.name.clone())], + ), + ], + // code languages and hashtags + ] + .concat(), + )) + .await + .context("failed to create repository reference event") } } @@ -308,7 +310,7 @@ mod tests { use super::*; - fn create() -> nostr::Event { + async fn create() -> nostr::Event { RepoRef { identifier: "123412341".to_string(), name: "test name".to_string(), @@ -322,34 +324,38 @@ mod tests { relays: vec!["ws://relay1.io".to_string(), "ws://relay2.io".to_string()], maintainers: vec![TEST_KEY_1_KEYS.public_key(), TEST_KEY_2_KEYS.public_key()], } - .to_event(&TEST_KEY_1_KEYS) + .to_event(&TEST_KEY_1_SIGNER) + .await .unwrap() } mod try_from { use super::*; - #[test] - fn identifier() { - assert_eq!(RepoRef::try_from(create()).unwrap().identifier, "123412341",) + #[tokio::test] + async fn identifier() { + assert_eq!( + RepoRef::try_from(create().await).unwrap().identifier, + "123412341", + ) } - #[test] - fn name() { - assert_eq!(RepoRef::try_from(create()).unwrap().name, "test name",) + #[tokio::test] + async fn name() { + assert_eq!(RepoRef::try_from(create().await).unwrap().name, "test name",) } - #[test] - fn description() { + #[tokio::test] + async fn description() { assert_eq!( - RepoRef::try_from(create()).unwrap().description, + RepoRef::try_from(create().await).unwrap().description, "test description", ) } - #[test] - fn root_commit_is_r_tag() { + #[tokio::test] + async fn root_commit_is_r_tag() { assert_eq!( - RepoRef::try_from(create()).unwrap().root_commit, + RepoRef::try_from(create().await).unwrap().root_commit, "5e664e5a7845cd1373c79f580ca4fe29ab5b34d2", ) } @@ -358,42 +364,43 @@ mod tests { use nostr::JsonUtil; use super::*; - fn create_with_incorrect_first_commit_ref(s: &str) -> nostr::Event { + async fn create_with_incorrect_first_commit_ref(s: &str) -> nostr::Event { nostr::Event::from_json( create() + .await .as_json() .replace("5e664e5a7845cd1373c79f580ca4fe29ab5b34d2", s), ) .unwrap() } - #[test] - fn less_than_40_characters() { + #[tokio::test] + async fn less_than_40_characters() { let s = "5e664e5a7845cd1373"; assert_eq!( - RepoRef::try_from(create_with_incorrect_first_commit_ref(s)) + RepoRef::try_from(create_with_incorrect_first_commit_ref(s).await) .unwrap() .root_commit, "", ) } - #[test] - fn more_than_40_characters() { + #[tokio::test] + async fn more_than_40_characters() { let s = "5e664e5a7845cd1373c79f580ca4fe29ab5b34d2111111111"; assert_eq!( - RepoRef::try_from(create_with_incorrect_first_commit_ref(s)) + RepoRef::try_from(create_with_incorrect_first_commit_ref(s).await) .unwrap() .root_commit, "", ) } - #[test] - fn not_hex_characters() { + #[tokio::test] + async fn not_hex_characters() { let s = "xxx64e5a7845cd1373c79f580ca4fe29ab5b34d2"; assert_eq!( - RepoRef::try_from(create_with_incorrect_first_commit_ref(s)) + RepoRef::try_from(create_with_incorrect_first_commit_ref(s).await) .unwrap() .root_commit, "", @@ -401,18 +408,18 @@ mod tests { } } - #[test] - fn git_server() { + #[tokio::test] + async fn git_server() { assert_eq!( - RepoRef::try_from(create()).unwrap().git_server, + RepoRef::try_from(create().await).unwrap().git_server, vec!["https://localhost:1000"], ) } - #[test] - fn web() { + #[tokio::test] + async fn web() { assert_eq!( - RepoRef::try_from(create()).unwrap().web, + RepoRef::try_from(create().await).unwrap().web, vec![ "https://exampleproject.xyz".to_string(), "https://gitworkshop.dev/123".to_string() @@ -420,18 +427,18 @@ mod tests { ) } - #[test] - fn relays() { + #[tokio::test] + async fn relays() { assert_eq!( - RepoRef::try_from(create()).unwrap().relays, + RepoRef::try_from(create().await).unwrap().relays, vec!["ws://relay1.io".to_string(), "ws://relay2.io".to_string()], ) } - #[test] - fn maintainers() { + #[tokio::test] + async fn maintainers() { assert_eq!( - RepoRef::try_from(create()).unwrap().maintainers, + RepoRef::try_from(create().await).unwrap().maintainers, vec![TEST_KEY_1_KEYS.public_key(), TEST_KEY_2_KEYS.public_key()], ) } @@ -442,57 +449,59 @@ mod tests { mod tags { use super::*; - #[test] - fn identifier() { + #[tokio::test] + async fn identifier() { assert!( create() + .await .tags .iter() .any(|t| t.as_vec()[0].eq("d") && t.as_vec()[1].eq("123412341")) ) } - #[test] - fn name() { + #[tokio::test] + async fn name() { assert!( create() + .await .tags .iter() .any(|t| t.as_vec()[0].eq("name") && t.as_vec()[1].eq("test name")) ) } - #[test] - fn alt() { + #[tokio::test] + async fn alt() { assert!( - create().tags.iter().any(|t| t.as_vec()[0].eq("alt") + create().await.tags.iter().any(|t| t.as_vec()[0].eq("alt") && t.as_vec()[1].eq("git repository: test name")) ) } - #[test] - fn description() { - assert!(create().tags.iter().any( + #[tokio::test] + async fn description() { + assert!(create().await.tags.iter().any( |t| t.as_vec()[0].eq("description") && t.as_vec()[1].eq("test description") )) } - #[test] - fn root_commit_as_reference() { - assert!(create().tags.iter().any(|t| t.as_vec()[0].eq("r") + #[tokio::test] + async fn root_commit_as_reference() { + assert!(create().await.tags.iter().any(|t| t.as_vec()[0].eq("r") && t.as_vec()[1].eq("5e664e5a7845cd1373c79f580ca4fe29ab5b34d2"))) } - #[test] - fn git_server() { - assert!(create().tags.iter().any( + #[tokio::test] + async fn git_server() { + assert!(create().await.tags.iter().any( |t| t.as_vec()[0].eq("clone") && t.as_vec()[1].eq("https://localhost:1000") )) } - #[test] - fn relays() { - let event = create(); + #[tokio::test] + async fn relays() { + let event = create().await; let relays_tag: &nostr::Tag = event .tags .iter() @@ -503,9 +512,9 @@ mod tests { assert_eq!(relays_tag.as_vec()[2], "ws://relay2.io"); } - #[test] - fn web() { - let event = create(); + #[tokio::test] + async fn web() { + let event = create().await; let web_tag: &nostr::Tag = event.tags.iter().find(|t| t.as_vec()[0].eq("web")).unwrap(); assert_eq!(web_tag.as_vec().len(), 3); @@ -513,9 +522,9 @@ mod tests { assert_eq!(web_tag.as_vec()[2], "https://gitworkshop.dev/123"); } - #[test] - fn maintainers() { - let event = create(); + #[tokio::test] + async fn maintainers() { + let event = create().await; let maintainers_tag: &nostr::Tag = event .tags .iter() @@ -532,9 +541,9 @@ mod tests { ); } - #[test] - fn no_other_tags() { - assert_eq!(create().tags.len(), 9) + #[tokio::test] + async fn no_other_tags() { + assert_eq!(create().await.tags.len(), 9) } } } diff --git a/src/sub_commands/init.rs b/src/sub_commands/init.rs index 4d1bdfb..4afe83c 100644 --- a/src/sub_commands/init.rs +++ b/src/sub_commands/init.rs @@ -59,7 +59,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { #[cfg(test)] let mut client = ::default(); - let (keys, user_ref) = login::launch( + let (signer, user_ref) = login::launch( &git_repo, &cli_args.nsec, &cli_args.password, @@ -68,8 +68,6 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { ) .await?; - client.set_keys(&keys).await; - let repo_ref = if let Ok(rep_ref) = repo_ref::fetch( &git_repo, root_commit.to_string(), @@ -180,7 +178,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { let mut maintainers_string = if !args.other_maintainers.is_empty() { [args.other_maintainers.clone()].concat().join(" ") } else if repo_ref.is_none() && repo_config_result.is_err() { - keys.public_key().to_bech32()? + signer.public_key().await?.to_bech32()? } else { let maintainers = if let Ok(config) = &repo_config_result { config.maintainers.clone() @@ -193,7 +191,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { .collect() } else { //unreachable - vec![keys.public_key().to_bech32()?] + vec![signer.public_key().await?.to_bech32()?] }; // add current user if not present if maintainers.iter().any(|m| { @@ -205,7 +203,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { }) { maintainers.join(" ") } else { - [maintainers, vec![keys.public_key().to_bech32()?]] + [maintainers, vec![signer.public_key().await?.to_bech32()?]] .concat() .join(" ") } @@ -231,7 +229,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { } // add current user incase removed if !maintainers.iter().any(|m| user_ref.public_key.eq(m)) { - maintainers.push(keys.public_key()); + maintainers.push(signer.public_key().await?); } break maintainers; } @@ -300,7 +298,10 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { relays: relays.clone(), maintainers: maintainers.clone(), } - .to_event(&keys)?; + .to_event(&signer) + .await?; + + client.set_signer(signer).await; send_events( &client, diff --git a/src/sub_commands/push.rs b/src/sub_commands/push.rs index ade2ff8..92c1c18 100644 --- a/src/sub_commands/push.rs +++ b/src/sub_commands/push.rs @@ -148,7 +148,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { ahead.len() ); - let (keys, user_ref) = login::launch( + let (signer, user_ref) = login::launch( &git_repo, &cli_args.nsec, &cli_args.password, @@ -157,8 +157,6 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { ) .await?; - client.set_keys(&keys).await; - let mut patch_events: Vec = vec![]; for commit in &ahead { patch_events.push( @@ -167,7 +165,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { &root_commit, commit, Some(proposal_root_event.id), - &keys, + &signer, &repo_ref, patch_events.last().map(nostr::Event::id), None, @@ -175,11 +173,14 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { &None, &[], ) + .await .context("cannot make patch event from commit")?, ); } println!("pushing {} commits", ahead.len()); + client.set_signer(signer).await; + send_events( &client, patch_events, diff --git a/src/sub_commands/send.rs b/src/sub_commands/send.rs index 8971d8b..1d20e90 100644 --- a/src/sub_commands/send.rs +++ b/src/sub_commands/send.rs @@ -8,7 +8,7 @@ use nostr::{ nips::{nip01::Coordinate, nip10::Marker, nip19::Nip19}, EventBuilder, FromBech32, Tag, TagKind, ToBech32, UncheckedUrl, }; -use nostr_sdk::{hashes::sha1::Hash as Sha1Hash, TagStandard}; +use nostr_sdk::{hashes::sha1::Hash as Sha1Hash, NostrSigner, TagStandard}; use super::list::tag_value; #[cfg(not(test))] @@ -178,7 +178,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { } else { None }; - let (keys, user_ref) = login::launch( + let (signer, user_ref) = login::launch( &git_repo, &cli_args.nsec, &cli_args.password, @@ -187,7 +187,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { ) .await?; - client.set_keys(&keys).await; + client.set_signer(signer.clone()).await; let repo_ref = repo_ref::fetch( &git_repo, @@ -208,11 +208,12 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { cover_letter_title_description.clone(), &git_repo, &commits, - &keys, + &signer, &repo_ref, &root_proposal_id, &mention_tags, - )?; + ) + .await?; println!( "posting {} patch{} {} a covering letter...", @@ -576,11 +577,11 @@ async fn get_root_proposal_id_and_mentions_from_in_reply_to( pub static PATCH_KIND: u16 = 1617; #[allow(clippy::too_many_lines)] -pub fn generate_cover_letter_and_patch_events( +pub async fn generate_cover_letter_and_patch_events( cover_letter_title_description: Option<(String, String)>, git_repo: &Repo, commits: &[Sha1Hash], - keys: &nostr::Keys, + signer: &NostrSigner, repo_ref: &RepoRef, root_proposal_id: &Option, mentions: &[nostr::Tag], @@ -592,7 +593,7 @@ pub fn generate_cover_letter_and_patch_events( let mut events = vec![]; if let Some((title, description)) = cover_letter_title_description { - events.push(EventBuilder::new( + events.push(signer.sign_event_builder(EventBuilder::new( nostr::event::Kind::Custom(PATCH_KIND), format!( "From {} Mon Sep 17 00:00:00 2001\nSubject: [PATCH 0/{}] {title}\n\n{description}", @@ -655,8 +656,7 @@ pub fn generate_cover_letter_and_patch_events( .map(|pk| Tag::public_key(*pk)) .collect(), ].concat(), - ) - .to_event(keys) + )).await .context("failed to create cover-letter event")?); } @@ -667,7 +667,7 @@ pub fn generate_cover_letter_and_patch_events( &root_commit, commit, events.first().map(|event| event.id), - keys, + signer, repo_ref, events.last().map(nostr::Event::id), if events.is_empty() { @@ -695,6 +695,7 @@ pub fn generate_cover_letter_and_patch_events( root_proposal_id, if events.is_empty() { mentions } else { &[] }, ) + .await .context("failed to generate patch event")?, ); } @@ -864,12 +865,12 @@ pub fn patch_supports_commit_ids(event: &nostr::Event) -> bool { #[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_lines)] -pub fn generate_patch_event( +pub async fn generate_patch_event( git_repo: &Repo, root_commit: &Sha1Hash, commit: &Sha1Hash, thread_event_id: Option, - keys: &nostr::Keys, + signer: &nostr_sdk::NostrSigner, repo_ref: &RepoRef, parent_patch_event_id: Option, series_count: Option<(u64, u64)>, @@ -882,7 +883,7 @@ pub fn generate_patch_event( .context("failed to get parent commit")?; let relay_hint = repo_ref.relays.first().map(nostr::UncheckedUrl::from); - EventBuilder::new( + signer.sign_event_builder(EventBuilder::new( nostr::event::Kind::Custom(PATCH_KIND), git_repo .make_patch_from_commit(commit,&series_count) @@ -999,8 +1000,7 @@ pub fn generate_patch_event( ], ] .concat(), - ) - .to_event(keys) + )).await .context("failed to sign event") } // TODO diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs index bf6c37b..ade60fc 100644 --- a/test_utils/src/lib.rs +++ b/test_utils/src/lib.rs @@ -4,7 +4,7 @@ use anyhow::{bail, ensure, Context, Result}; use dialoguer::theme::{ColorfulTheme, Theme}; use directories::ProjectDirs; use nostr::{self, nips::nip65::RelayMetadata, Kind, Tag}; -use nostr_sdk::{serde_json, TagStandard}; +use nostr_sdk::{serde_json, NostrSigner, TagStandard}; use once_cell::sync::Lazy; use rexpect::session::{Options, PtySession}; use strip_ansi_escapes::strip_str; @@ -29,6 +29,13 @@ pub static TEST_KEY_1_ENCRYPTED_WEAK: &str = "ncryptsec1qg835almhlrmyxqtqeva44d5 pub static TEST_KEY_1_KEYS: Lazy = Lazy::new(|| nostr::Keys::from_str(TEST_KEY_1_NSEC).unwrap()); +pub static TEST_KEY_1_SIGNER: Lazy = + Lazy::new(|| NostrSigner::Keys(nostr::Keys::from_str(TEST_KEY_1_NSEC).unwrap())); + +pub fn generate_test_key_1_signer() -> NostrSigner { + NostrSigner::Keys(nostr::Keys::from_str(TEST_KEY_1_NSEC).unwrap()) +} + pub fn generate_test_key_1_metadata_event(name: &str) -> nostr::Event { nostr::event::EventBuilder::metadata(&nostr::Metadata::new().name(name)) .to_event(&TEST_KEY_1_KEYS) -- cgit v1.2.3