Skip to content
Snippets Groups Projects
Commit 93f7d5d4 authored by poka's avatar poka
Browse files

set g1pubkey

parent fe154a5a
No related branches found
No related tags found
1 merge request!45Nostr
Pipeline #40613 passed
...@@ -11,6 +11,8 @@ use url::Url; ...@@ -11,6 +11,8 @@ use url::Url;
use sha2::{Sha256, Digest}; use sha2::{Sha256, Digest};
use secp256k1::{Secp256k1, SecretKey, PublicKey}; use secp256k1::{Secp256k1, SecretKey, PublicKey};
use bech32::{self, ToBase32, Variant}; use bech32::{self, ToBase32, Variant};
use sp_core::crypto::Ss58Codec;
use crate::commands::cesium;
/// Derive a Nostr key from a Substrate key /// Derive a Nostr key from a Substrate key
fn derive_nostr_keys_from_substrate(keypair: &KeyPair) -> Result<(SecretKey, PublicKey), GcliError> { fn derive_nostr_keys_from_substrate(keypair: &KeyPair) -> Result<(SecretKey, PublicKey), GcliError> {
...@@ -159,7 +161,7 @@ impl Default for NostrProfile { ...@@ -159,7 +161,7 @@ impl Default for NostrProfile {
} }
/// Nostr event structure /// Nostr event structure
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct NostrEvent { pub struct NostrEvent {
pub id: String, pub id: String,
pub pubkey: String, pub pubkey: String,
...@@ -501,7 +503,8 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr ...@@ -501,7 +503,8 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr
// Get Nostr pubkey in bech32 format (for display) // Get Nostr pubkey in bech32 format (for display)
let npub = get_nostr_npub(&keypair)?; let npub = get_nostr_npub(&keypair)?;
println!("Searching for profile with pubkey: {}", pubkey); println!("Searching for profile with Nostr pubkey (hex): {}", pubkey);
println!("Nostr pubkey (bech32): {}", npub);
// Use default relay if none provided // Use default relay if none provided
let relay = relay_url.unwrap_or_else(|| { let relay = relay_url.unwrap_or_else(|| {
...@@ -545,8 +548,10 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr ...@@ -545,8 +548,10 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr
// Wait for response with timeout // Wait for response with timeout
let mut profile_found = false; let mut profile_found = false;
let mut profile = NostrProfile::default(); let mut profile_content = NostrProfile::default(); // To store parsed content
let mut all_messages: Vec<String> = Vec::new(); // Store all received messages for debugging let mut g1pubv2_from_tags: Option<String> = None;
let mut g1pub_from_tags: Option<String> = None;
let mut event_received: Option<NostrEvent> = None; // Store the whole event for JSON output
// Set a timeout for receiving messages // Set a timeout for receiving messages
let timeout = tokio::time::Duration::from_secs(10); // Increased timeout let timeout = tokio::time::Duration::from_secs(10); // Increased timeout
...@@ -564,7 +569,7 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr ...@@ -564,7 +569,7 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr
).await { ).await {
Ok(Some(Ok(msg))) => { Ok(Some(Ok(msg))) => {
if let Message::Text(text) = msg { if let Message::Text(text) = msg {
all_messages.push(text.clone()); event_received = Some(serde_json::from_str::<NostrEvent>(&text).unwrap());
// Parse the message // Parse the message
if let Ok(json) = serde_json::from_str::<Value>(&text) { if let Ok(json) = serde_json::from_str::<Value>(&text) {
...@@ -573,25 +578,34 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr ...@@ -573,25 +578,34 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr
if let Some(event_type) = json.get(0).and_then(|v| v.as_str()) { if let Some(event_type) = json.get(0).and_then(|v| v.as_str()) {
if event_type == "EVENT" && json.get(1).is_some() && json.get(2).is_some() { if event_type == "EVENT" && json.get(1).is_some() && json.get(2).is_some() {
// Attempt to deserialize the whole event first
if let Ok(full_event_obj) = serde_json::from_value::<NostrEvent>(json[2].clone()) {
event_received = Some(full_event_obj.clone()); // Clone for later JSON output
if let Some(content) = json[2]["content"].as_str() { // Then parse the content string into NostrProfile
if let Ok(parsed_profile_content) = serde_json::from_str::<NostrProfile>(&full_event_obj.content) {
// Try to parse the profile profile_content = parsed_profile_content;
match serde_json::from_str::<NostrProfile>(content) {
Ok(parsed_profile) => {
profile = parsed_profile;
profile_found = true; profile_found = true;
// Close the subscription // Extract g1pubv2 and g1pub from tags
for tag_vec in &full_event_obj.tags {
if tag_vec.len() >= 2 {
match tag_vec[0].as_str() {
"g1pubv2" => g1pubv2_from_tags = Some(tag_vec[1].clone()),
"g1pub" => g1pub_from_tags = Some(tag_vec[1].clone()),
_ => {}
}
}
}
let close_msg = json!(["CLOSE", "profile-request"]); let close_msg = json!(["CLOSE", "profile-request"]);
ws_stream.send(Message::Text(close_msg.to_string())).await ws_stream.send(Message::Text(close_msg.to_string())).await
.map_err(|e| anyhow!("Failed to close subscription: {}", e))?; .map_err(|e| anyhow!("Failed to close subscription: {}", e))?;
break; break;
}, } else {
Err(_) => { log::warn!("Failed to parse NostrProfile content from event content string.");
}
} }
} else {
log::warn!("Failed to parse full NostrEvent object from relay message.");
} }
} else if event_type == "EOSE" { } else if event_type == "EOSE" {
// End of stored events // End of stored events
...@@ -630,25 +644,32 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr ...@@ -630,25 +644,32 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr
match data.args.output_format { match data.args.output_format {
OutputFormat::Human => { OutputFormat::Human => {
println!("Profile for npub: {}", npub); println!("Profile for npub: {}", npub);
if let Some(name) = &profile.name { if let Some(name) = &profile_content.name {
println!("Name: {}", name); println!("Name: {}", name);
} }
if let Some(display_name) = &profile.display_name { if let Some(display_name) = &profile_content.display_name {
println!("Display Name: {}", display_name); println!("Display Name: {}", display_name);
} }
if let Some(picture) = &profile.picture { if let Some(picture) = &profile_content.picture {
println!("Picture: {}", picture); println!("Picture: {}", picture);
} }
if let Some(about) = &profile.about { if let Some(about) = &profile_content.about {
println!("About: {}", about); println!("About: {}", about);
} }
if let Some(website) = &profile.website { if let Some(website) = &profile_content.website {
println!("Website: {}", website); println!("Website: {}", website);
} }
if let Some(nip05) = &profile.nip05 { if let Some(nip05) = &profile_content.nip05 {
println!("NIP-05: {}", nip05); println!("NIP-05: {}", nip05);
} }
for (key, value) in &profile.additional_fields { // Display from tags
if let Some(g1pubv2) = &g1pubv2_from_tags {
println!("g1pubv2 (gdev SS58 Tag): {}", g1pubv2);
}
if let Some(g1pub) = &g1pub_from_tags {
println!("g1pub (Duniter v1 Pubkey Tag): {}", g1pub);
}
for (key, value) in &profile_content.additional_fields {
println!("{}: {}", key, value); println!("{}: {}", key, value);
} }
} }
...@@ -656,7 +677,12 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr ...@@ -656,7 +677,12 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr
let output = json!({ let output = json!({
"pubkey_hex": pubkey, "pubkey_hex": pubkey,
"pubkey_bech32": npub, "pubkey_bech32": npub,
"profile": profile "profile_content": profile_content, // The content part
"tags_custom": {
"g1pubv2": g1pubv2_from_tags,
"g1pub": g1pub_from_tags
},
"full_event": event_received // Include the full event if available
}); });
println!("{}", serde_json::to_string_pretty(&output).unwrap()); println!("{}", serde_json::to_string_pretty(&output).unwrap());
} }
...@@ -670,7 +696,12 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr ...@@ -670,7 +696,12 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr
let output = json!({ let output = json!({
"pubkey_hex": pubkey, "pubkey_hex": pubkey,
"pubkey_bech32": npub, "pubkey_bech32": npub,
"profile": NostrProfile::default() "profile_content": NostrProfile::default(), // Default empty profile content
"tags_custom": {
"g1pubv2": Option::<String>::None,
"g1pub": Option::<String>::None
},
"full_event": Option::<NostrEvent>::None
}); });
println!("{}", serde_json::to_string_pretty(&output).unwrap()); println!("{}", serde_json::to_string_pretty(&output).unwrap());
} }
...@@ -719,60 +750,57 @@ async fn set_profile( ...@@ -719,60 +750,57 @@ async fn set_profile(
// Get Nostr private key in bech32 format (for display) // Get Nostr private key in bech32 format (for display)
let nsec = get_nostr_nsec(&keypair)?; let nsec = get_nostr_nsec(&keypair)?;
// Create profile data // ---- START: Calculate g1pubv2 and g1pub for tags ----
let mut profile = NostrProfile::default(); let mut g1pubv2_for_tag: Option<String> = None;
let mut g1pub_for_tag: Option<String> = None;
if let Some(name) = name { let account_id: sp_core::crypto::AccountId32 = match &keypair {
profile.name = Some(name); KeyPair::Sr25519(pair) => pair.public().into(),
} KeyPair::Ed25519(pair) => pair.public().into(),
if let Some(display_name) = display_name { };
profile.display_name = Some(display_name); let gdev_ss58_address = account_id.to_ss58check_with_version(sp_core::crypto::Ss58AddressFormat::custom(42));
} g1pubv2_for_tag = Some(gdev_ss58_address);
if let Some(picture) = picture {
profile.picture = Some(picture); match cesium::compute_g1v1_public_key(&keypair) {
} Ok(pubkey_g1) => g1pub_for_tag = Some(pubkey_g1),
if let Some(about) = about { Err(_e) => {
profile.about = Some(about); 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.");
} }
if let Some(website) = website {
profile.website = Some(website);
} }
if let Some(nip05) = nip05 {
profile.nip05 = Some(nip05);
} }
// ---- END: Calculate g1pubv2 and g1pub for tags ----
// Serialize profile to JSON let mut profile_content_obj = NostrProfile::default(); // This is for the 'content' field
let profile_json = serde_json::to_string(&profile) if let Some(val) = name { profile_content_obj.name = Some(val); }
.map_err(|e| anyhow!("Failed to serialize profile: {}", e))?; 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) = 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); }
// Create and sign Nostr event let profile_content_json = serde_json::to_string(&profile_content_obj)
let mut event = NostrEvent::new(pubkey.clone(), 0, profile_json); .map_err(|e| anyhow!("Failed to serialize profile content: {}", e))?;
// Make sure tags is initialized as an empty array, not null let mut event = NostrEvent::new(pubkey.clone(), 0, profile_content_json);
event.tags = Vec::new(); event.tags = Vec::new(); // Initialize tags
// Add g1pubv2 and g1pub as tags
if let Some(g1_ss58) = &g1pubv2_for_tag {
event.tags.push(vec!["g1pubv2".to_string(), g1_ss58.clone()]);
}
if let Some(g1_key) = &g1pub_for_tag {
event.tags.push(vec!["g1pub".to_string(), g1_key.clone()]);
}
log::debug!("Created event with pubkey: {}", event.pubkey); log::debug!("Created event with pubkey: {}", event.pubkey);
log::debug!("Event content: {}", event.content); log::debug!("Event content: {}", event.content);
log::debug!("Event tags: {:?}", event.tags); // Log the tags
// Calculate ID and sign
event.calculate_id()?; event.calculate_id()?;
log::debug!("Calculated event ID: {}", event.id);
event.sign(&keypair)?; event.sign(&keypair)?;
log::debug!("Signed event with signature: {}", event.sig);
// Log the complete event for debugging
log::debug!("Event to publish: {}", serde_json::to_string_pretty(&event).unwrap());
// Verify the event signature
match verify_nostr_event(&event) {
Ok(true) => log::debug!("Event signature verified successfully"),
Ok(false) => {
log::error!("Event signature verification failed - relay will likely reject this event");
return Err(anyhow!("Event signature verification failed - cannot proceed").into());
},
Err(e) => log::warn!("Error verifying event signature: {}", e),
}
// Use default relay if none provided // Use default relay if none provided
let relay = relay_url.unwrap_or_else(|| { let relay = relay_url.unwrap_or_else(|| {
...@@ -902,25 +930,22 @@ async fn set_profile( ...@@ -902,25 +930,22 @@ async fn set_profile(
match data.args.output_format { match data.args.output_format {
OutputFormat::Human => { OutputFormat::Human => {
println!("Profile data published to npub: {}", npub); println!("Profile data published to npub (bech32): {}", npub);
println!("Nostr pubkey (hex): {}", pubkey);
if let Some(name) = &profile.name { if let Some(val) = &profile_content_obj.name { println!("Name: {}", val); }
println!("Name: {}", name); if let Some(val) = &profile_content_obj.display_name { println!("Display Name: {}", val); }
} if let Some(val) = &profile_content_obj.picture { println!("Picture: {}", val); }
if let Some(display_name) = &profile.display_name { if let Some(val) = &profile_content_obj.about { println!("About: {}", val); }
println!("Display Name: {}", display_name); if let Some(val) = &profile_content_obj.website { println!("Website: {}", val); }
} if let Some(val) = &profile_content_obj.nip05 { println!("NIP-05: {}", val); }
if let Some(picture) = &profile.picture {
println!("Picture: {}", picture); // Print calculated tag values for user feedback
} if let Some(g1_ss58) = &g1pubv2_for_tag {
if let Some(about) = &profile.about { println!("g1pubv2 (Tag to be published): {}", g1_ss58);
println!("About: {}", about);
} }
if let Some(website) = &profile.website { if let Some(g1_key) = &g1pub_for_tag {
println!("Website: {}", website); println!("g1pub (Tag to be published): {}", g1_key);
}
if let Some(nip05) = &profile.nip05 {
println!("NIP-05: {}", nip05);
} }
println!("\nPublished to relay: {}", relay); println!("\nPublished to relay: {}", relay);
...@@ -936,8 +961,12 @@ async fn set_profile( ...@@ -936,8 +961,12 @@ async fn set_profile(
"pubkey_hex": pubkey, "pubkey_hex": pubkey,
"pubkey_bech32": npub, "pubkey_bech32": npub,
"seckey_bech32": nsec, "seckey_bech32": nsec,
"profile": profile, "profile_content_sent": profile_content_obj, // what was in content
"event": event, "calculated_tags": {
"g1pubv2": g1pubv2_for_tag,
"g1pub": g1pub_for_tag
},
"event_sent": event, // The full event, including new tags
"relay": relay, "relay": relay,
"success": success, "success": success,
"response": response_message "response": response_message
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment