Skip to content
Snippets Groups Projects
Commit 3f0456d7 authored by fred's avatar fred
Browse files

NIP-39 compatibility : need some test

parent daee09e3
No related branches found
No related tags found
1 merge request!49Draft: Nostr
Pipeline #40630 passed
......@@ -115,6 +115,10 @@ pub enum Subcommand {
#[clap(short = 'p', long)]
picture: Option<String>,
/// Profile banner URL
#[clap(short = 'b', long)]
banner: Option<String>,
/// Profile about/description
#[clap(short, long)]
about: Option<String>,
......@@ -127,6 +131,38 @@ pub enum Subcommand {
#[clap(short = 'i', long)]
nip05: Option<String>,
/// GitHub username
#[clap(long)]
github: Option<String>,
/// Twitter username
#[clap(long)]
twitter: Option<String>,
/// Mastodon identity (format: instance/@username)
#[clap(long)]
mastodon: Option<String>,
/// Telegram user ID
#[clap(long)]
telegram: Option<String>,
/// IPFS Gateway URL
#[clap(long)]
ipfs_gw: Option<String>,
/// NOSTR Card IPNS vault key
#[clap(long)]
ipns_vault: Option<String>,
/// ZenCard wallet address
#[clap(long)]
zencard: Option<String>,
/// TW Feed IPNS key
#[clap(long)]
tw_feed: Option<String>,
/// Relay URL to publish profile to
#[clap(short, long)]
relay: Option<String>,
......@@ -139,9 +175,11 @@ pub struct NostrProfile {
pub name: Option<String>,
pub display_name: Option<String>,
pub picture: Option<String>,
pub banner: Option<String>,
pub about: Option<String>,
pub website: Option<String>,
pub nip05: Option<String>,
pub bot: Option<bool>,
#[serde(flatten)]
pub additional_fields: HashMap<String, String>,
}
......@@ -152,9 +190,11 @@ impl Default for NostrProfile {
name: None,
display_name: None,
picture: None,
banner: None,
about: None,
website: None,
nip05: None,
bot: Some(false),
additional_fields: HashMap::new(),
}
}
......@@ -471,9 +511,18 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
name,
display_name,
picture,
banner,
about,
website,
nip05,
github,
twitter,
mastodon,
telegram,
ipfs_gw,
ipns_vault,
zencard,
tw_feed,
relay,
} => {
set_profile(
......@@ -481,9 +530,18 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
name,
display_name,
picture,
banner,
about,
website,
nip05,
github,
twitter,
mastodon,
telegram,
ipfs_gw,
ipns_vault,
zencard,
tw_feed,
relay,
)
.await
......@@ -715,28 +773,46 @@ async fn set_profile(
name: Option<String>,
display_name: Option<String>,
picture: Option<String>,
banner: Option<String>,
about: Option<String>,
website: Option<String>,
nip05: Option<String>,
github: Option<String>,
twitter: Option<String>,
mastodon: Option<String>,
telegram: Option<String>,
ipfs_gw: Option<String>,
ipns_vault: Option<String>,
zencard: Option<String>,
tw_feed: Option<String>,
relay_url: Option<String>,
) -> Result<(), GcliError> {
// Check if no options were provided, and if so, display help
if name.is_none() && display_name.is_none() && picture.is_none() &&
about.is_none() && website.is_none() && nip05.is_none() {
if name.is_none() && display_name.is_none() && picture.is_none() && banner.is_none() &&
about.is_none() && website.is_none() && nip05.is_none() && github.is_none() &&
twitter.is_none() && mastodon.is_none() && telegram.is_none() {
println!("No profile options specified. Available options:");
println!(" -n, --name <NAME> Set profile name");
println!(" -d, --display-name <DISPLAY_NAME> Set profile display name");
println!(" -p, --picture <PICTURE> Set profile picture URL");
println!(" -b, --banner <BANNER> Set profile banner URL");
println!(" -a, --about <ABOUT> Set profile about/description");
println!(" -w, --website <WEBSITE> Set profile website URL");
println!(" -i, --nip05 <NIP05> Set profile NIP-05 identifier");
println!(" --github <USERNAME> Set GitHub username");
println!(" --twitter <USERNAME> Set Twitter username");
println!(" --mastodon <IDENTITY> Set Mastodon identity (instance/@username)");
println!(" --telegram <USER_ID> Set Telegram user ID");
println!(" --ipfs-gw <URL> Set IPFS Gateway URL");
println!(" --ipns-vault <KEY> Set NOSTR Card IPNS vault key");
println!(" --zencard <ADDRESS> Set ZenCard wallet address");
println!(" --tw-feed <KEY> Set TW Feed IPNS key");
println!(" -r, --relay <RELAY> Specify relay URL to publish to");
println!("\nExample: gcli profile set --name \"Alice\" --about \"Nostr user\"");
return Ok(());
}
// Get keypair for signing
// Use the configured address or prompt the user if not set
let keypair = fetch_or_get_keypair(&data, data.cfg.address.clone(), Some(CryptoScheme::Ed25519)).await?;
// Get Nostr pubkey in hex format (used for protocol)
......@@ -748,48 +824,83 @@ async fn set_profile(
// Get Nostr private key in bech32 format (for display)
let nsec = get_nostr_nsec(&keypair)?;
// ---- START: Calculate g1pubv2 and g1pub for tags ----
// Calculate g1pubv2 (gdev SS58) and g1pub (Duniter v1)
let mut g1pub_for_tag: Option<String> = None;
// Calculate g1pubv2 (SS58 address)
let account_id: sp_core::crypto::AccountId32 = match &keypair {
KeyPair::Sr25519(pair) => pair.public().into(),
KeyPair::Ed25519(pair) => pair.public().into(),
};
let gdev_ss58_address = account_id.to_ss58check_with_version(sp_core::crypto::Ss58AddressFormat::custom(42));
let g1pubv2_for_tag: Option<String> = Some(gdev_ss58_address);
let g1pubv2_for_tag: Option<String> = Some(account_id.to_ss58check_with_version(sp_core::crypto::Ss58AddressFormat::custom(42)));
// Calculate g1pub (Duniter v1 pubkey)
match cesium::compute_g1v1_public_key(&keypair) {
Ok(pubkey_g1) => g1pub_for_tag = Some(pubkey_g1),
Err(_e) => {
Err(e) => {
if !matches!(keypair, KeyPair::Ed25519(_)) {
log::info!("Cannot compute g1pub (Duniter v1 pubkey) for tag as the key is not Ed25519.");
} else {
log::warn!("Failed to compute g1pub (Duniter v1 pubkey) for tag.");
log::warn!("Failed to compute g1pub (Duniter v1 pubkey) for tag: {}", e);
}
}
}
// ---- END: Calculate g1pubv2 and g1pub for tags ----
let mut profile_content_obj = NostrProfile::default(); // This is for the 'content' field
// Create profile content
let mut profile_content_obj = NostrProfile::default();
if let Some(val) = name { profile_content_obj.name = Some(val); }
if let Some(val) = display_name { profile_content_obj.display_name = Some(val); }
if let Some(val) = picture { profile_content_obj.picture = Some(val); }
if let Some(val) = banner { profile_content_obj.banner = Some(val); }
if let Some(val) = about { profile_content_obj.about = Some(val); }
if let Some(val) = website { profile_content_obj.website = Some(val); }
if let Some(val) = nip05 { profile_content_obj.nip05 = Some(val); }
profile_content_obj.bot = Some(false);
let profile_content_json = serde_json::to_string(&profile_content_obj)
.map_err(|e| anyhow!("Failed to serialize profile content: {}", e))?;
let mut event = NostrEvent::new(pubkey.clone(), 0, profile_content_json);
event.tags = Vec::new(); // Initialize tags
event.tags = Vec::new();
// Add NIP-39 external identity tags
if let Some(gh) = &github {
event.tags.push(vec!["i".to_string(), format!("github:{}", gh), "".to_string()]);
}
if let Some(tw) = &twitter {
event.tags.push(vec!["i".to_string(), format!("twitter:{}", tw), "".to_string()]);
}
if let Some(mstd) = &mastodon {
event.tags.push(vec!["i".to_string(), format!("mastodon:{}", mstd), "".to_string()]);
}
if let Some(tg) = &telegram {
event.tags.push(vec!["i".to_string(), format!("telegram:{}", tg), "".to_string()]);
}
// Add g1pubv2 and g1pub as tags
// Add g1pubv2 and g1pub tags
if let Some(g1_ss58) = &g1pubv2_for_tag {
event.tags.push(vec!["g1pubv2".to_string(), g1_ss58.clone()]);
// Also add as an external identity for compatibility
event.tags.push(vec!["i".to_string(), format!("g1pubv2:{}", g1_ss58), "".to_string()]);
}
if let Some(g1_key) = &g1pub_for_tag {
event.tags.push(vec!["g1pub".to_string(), g1_key.clone()]);
// Also add as an external identity for compatibility
event.tags.push(vec!["i".to_string(), format!("g1pub:{}", g1_key), "".to_string()]);
}
// Add additional custom tags
if let Some(gw) = &ipfs_gw {
event.tags.push(vec!["i".to_string(), format!("ipfs_gw:{}", gw), "".to_string()]);
}
if let Some(vault) = &ipns_vault {
event.tags.push(vec!["i".to_string(), format!("ipns_vault:{}", vault), "".to_string()]);
}
if let Some(card) = &zencard {
event.tags.push(vec!["i".to_string(), format!("zencard:{}", card), "".to_string()]);
}
if let Some(feed) = &tw_feed {
event.tags.push(vec!["i".to_string(), format!("tw_feed:{}", feed), "".to_string()]);
}
log::debug!("Created event with pubkey: {}", event.pubkey);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment