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;
use sha2::{Sha256, Digest};
use secp256k1::{Secp256k1, SecretKey, PublicKey};
use bech32::{self, ToBase32, Variant};
use sp_core::crypto::Ss58Codec;
use crate::commands::cesium;
/// Derive a Nostr key from a Substrate key
fn derive_nostr_keys_from_substrate(keypair: &KeyPair) -> Result<(SecretKey, PublicKey), GcliError> {
......@@ -159,7 +161,7 @@ impl Default for NostrProfile {
}
/// Nostr event structure
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct NostrEvent {
pub id: String,
pub pubkey: String,
......@@ -501,7 +503,8 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr
// Get Nostr pubkey in bech32 format (for display)
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
let relay = relay_url.unwrap_or_else(|| {
......@@ -545,8 +548,10 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr
// Wait for response with timeout
let mut profile_found = false;
let mut profile = NostrProfile::default();
let mut all_messages: Vec<String> = Vec::new(); // Store all received messages for debugging
let mut profile_content = NostrProfile::default(); // To store parsed content
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
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
).await {
Ok(Some(Ok(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
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
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() {
// 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() {
// Try to parse the profile
match serde_json::from_str::<NostrProfile>(content) {
Ok(parsed_profile) => {
profile = parsed_profile;
// Then parse the content string into NostrProfile
if let Ok(parsed_profile_content) = serde_json::from_str::<NostrProfile>(&full_event_obj.content) {
profile_content = parsed_profile_content;
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"]);
ws_stream.send(Message::Text(close_msg.to_string())).await
.map_err(|e| anyhow!("Failed to close subscription: {}", e))?;
break;
},
Err(_) => {
}
} else {
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" {
// End of stored events
......@@ -630,25 +644,32 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr
match data.args.output_format {
OutputFormat::Human => {
println!("Profile for npub: {}", npub);
if let Some(name) = &profile.name {
if let Some(name) = &profile_content.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);
}
if let Some(picture) = &profile.picture {
if let Some(picture) = &profile_content.picture {
println!("Picture: {}", picture);
}
if let Some(about) = &profile.about {
if let Some(about) = &profile_content.about {
println!("About: {}", about);
}
if let Some(website) = &profile.website {
if let Some(website) = &profile_content.website {
println!("Website: {}", website);
}
if let Some(nip05) = &profile.nip05 {
if let Some(nip05) = &profile_content.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);
}
}
......@@ -656,7 +677,12 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr
let output = json!({
"pubkey_hex": pubkey,
"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());
}
......@@ -670,7 +696,12 @@ async fn get_profile(data: Data, relay_url: Option<String>) -> Result<(), GcliEr
let output = json!({
"pubkey_hex": pubkey,
"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());
}
......@@ -719,60 +750,57 @@ async fn set_profile(
// Get Nostr private key in bech32 format (for display)
let nsec = get_nostr_nsec(&keypair)?;
// Create profile data
let mut profile = NostrProfile::default();
// ---- START: Calculate g1pubv2 and g1pub for tags ----
let mut g1pubv2_for_tag: Option<String> = None;
let mut g1pub_for_tag: Option<String> = None;
if let Some(name) = name {
profile.name = Some(name);
}
if let Some(display_name) = display_name {
profile.display_name = Some(display_name);
}
if let Some(picture) = picture {
profile.picture = Some(picture);
}
if let Some(about) = about {
profile.about = Some(about);
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));
g1pubv2_for_tag = Some(gdev_ss58_address);
match cesium::compute_g1v1_public_key(&keypair) {
Ok(pubkey_g1) => g1pub_for_tag = Some(pubkey_g1),
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.");
}
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 profile_json = serde_json::to_string(&profile)
.map_err(|e| anyhow!("Failed to serialize profile: {}", e))?;
let mut profile_content_obj = NostrProfile::default(); // This is for the 'content' field
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) = 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 mut event = NostrEvent::new(pubkey.clone(), 0, profile_json);
let profile_content_json = serde_json::to_string(&profile_content_obj)
.map_err(|e| anyhow!("Failed to serialize profile content: {}", e))?;
// Make sure tags is initialized as an empty array, not null
event.tags = Vec::new();
let mut event = NostrEvent::new(pubkey.clone(), 0, profile_content_json);
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!("Event content: {}", event.content);
log::debug!("Event tags: {:?}", event.tags); // Log the tags
// Calculate ID and sign
event.calculate_id()?;
log::debug!("Calculated event ID: {}", event.id);
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
let relay = relay_url.unwrap_or_else(|| {
......@@ -902,25 +930,22 @@ async fn set_profile(
match data.args.output_format {
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 {
println!("Name: {}", name);
}
if let Some(display_name) = &profile.display_name {
println!("Display Name: {}", display_name);
}
if let Some(picture) = &profile.picture {
println!("Picture: {}", picture);
}
if let Some(about) = &profile.about {
println!("About: {}", about);
if let Some(val) = &profile_content_obj.name { println!("Name: {}", val); }
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(val) = &profile_content_obj.about { println!("About: {}", val); }
if let Some(val) = &profile_content_obj.website { println!("Website: {}", val); }
if let Some(val) = &profile_content_obj.nip05 { println!("NIP-05: {}", val); }
// Print calculated tag values for user feedback
if let Some(g1_ss58) = &g1pubv2_for_tag {
println!("g1pubv2 (Tag to be published): {}", g1_ss58);
}
if let Some(website) = &profile.website {
println!("Website: {}", website);
}
if let Some(nip05) = &profile.nip05 {
println!("NIP-05: {}", nip05);
if let Some(g1_key) = &g1pub_for_tag {
println!("g1pub (Tag to be published): {}", g1_key);
}
println!("\nPublished to relay: {}", relay);
......@@ -936,8 +961,12 @@ async fn set_profile(
"pubkey_hex": pubkey,
"pubkey_bech32": npub,
"seckey_bech32": nsec,
"profile": profile,
"event": event,
"profile_content_sent": profile_content_obj, // what was in content
"calculated_tags": {
"g1pubv2": g1pubv2_for_tag,
"g1pub": g1pub_for_tag
},
"event_sent": event, // The full event, including new tags
"relay": relay,
"success": success,
"response": response_message
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment