Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nostr: add sign_psbt method to NIP46 #123

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/nostr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ nip06 = ["dep:bip39", "dep:bitcoin"]
nip11 = ["dep:reqwest"]
nip19 = ["dep:bech32"]
nip21 = ["nip19"]
nip46 = ["nip04"]
nip46 = ["dep:bitcoin", "bitcoin?/base64", "bitcoin?/serde", "nip04"]
nip47 = ["nip04"]

[dependencies]
Expand Down
64 changes: 61 additions & 3 deletions crates/nostr/src/nips/nip46.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ use core::fmt;
use core::str::FromStr;
use std::borrow::Cow;

use bitcoin::psbt::PartiallySignedTransaction;
use bitcoin::Network;
use bitcoin_hashes::sha256::Hash as Sha256Hash;
use bitcoin_hashes::Hash;
use secp256k1::schnorr::Signature;
use secp256k1::{rand, Message as Secp256k1Message, XOnlyPublicKey};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::{json, Value};
use url::form_urlencoded::byte_serialize;
use url::Url;
Expand Down Expand Up @@ -117,7 +119,7 @@ impl From<unsigned::Error> for Error {
}

/// Request
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Request {
/// Describe
Describe,
Expand Down Expand Up @@ -152,6 +154,19 @@ pub enum Request {
},
/// Sign Schnorr
SignSchnorr(String),
/// Sign PSBT
SignPSBT {
/// Bitcoin Network
network: Network,
/// PSBT
#[serde(
serialize_with = "serialize_psbt",
deserialize_with = "deserialize_psbt"
)]
psbt: PartiallySignedTransaction,
/// Descriptor
descriptor: Option<String>,
},
}

impl Request {
Expand All @@ -167,6 +182,7 @@ impl Request {
Self::Nip04Encrypt { .. } => "nip04_encrypt".to_string(),
Self::Nip04Decrypt { .. } => "nip04_decrypt".to_string(),
Self::SignSchnorr(_) => "sign_schnorr".to_string(),
Self::SignPSBT { .. } => "sign_psbt".to_string(),
}
}

Expand All @@ -185,6 +201,17 @@ impl Request {
Self::Nip04Encrypt { public_key, text } => vec![json!(public_key), json!(text)],
Self::Nip04Decrypt { public_key, text } => vec![json!(public_key), json!(text)],
Self::SignSchnorr(value) => vec![json!(value)],
Self::SignPSBT {
network,
psbt,
descriptor,
} => {
let mut params = vec![json!(network.to_string()), json!(psbt.to_string())];
if let Some(descriptor) = descriptor {
params.push(json!(descriptor));
}
params
}
}
}

Expand Down Expand Up @@ -237,6 +264,7 @@ impl Request {
let sig: Signature = keys.sign_schnorr(&message)?;
Some(Response::SignSchnorr(sig))
}
Self::SignPSBT { .. } => None,
};
Ok(res)
}
Expand All @@ -256,7 +284,7 @@ pub struct DelegationResult {
}

/// Response
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Response {
/// Describe
Describe(Vec<String>),
Expand All @@ -272,6 +300,17 @@ pub enum Response {
Nip04Decrypt(String),
/// Sign Schnorr
SignSchnorr(Signature),
/// Sign PSBT
SignPSBT {
/// PSBT
#[serde(
serialize_with = "serialize_psbt",
deserialize_with = "deserialize_psbt"
)]
psbt: PartiallySignedTransaction,
/// Finalized
finalized: bool,
},
}

/// Message
Expand Down Expand Up @@ -320,6 +359,10 @@ impl Message {
Response::Nip04Encrypt(encrypted_content) => json!(encrypted_content),
Response::Nip04Decrypt(decrypted_content) => json!(decrypted_content),
Response::SignSchnorr(sig) => json!(sig),
Response::SignPSBT { psbt, finalized } => json!({
"psbt": psbt.to_string(),
"finalized": finalized,
}),
}),
error: None,
}
Expand Down Expand Up @@ -624,6 +667,21 @@ impl fmt::Display for NostrConnectURI {
}
}

fn serialize_psbt<S>(psbt: &PartiallySignedTransaction, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&psbt.to_string())
}

fn deserialize_psbt<'de, D>(deserializer: D) -> Result<PartiallySignedTransaction, D::Error>
where
D: Deserializer<'de>,
{
let psbt = String::deserialize(deserializer)?;
PartiallySignedTransaction::from_str(&psbt).map_err(serde::de::Error::custom)
}

#[cfg(test)]
mod test {
use std::str::FromStr;
Expand Down