Skip to content
Open
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ tokio-util = { version = "0.7.15", features = ["compat"], optional = true }
[features]
default = ["tokio"]
tokio = ["dep:tokio", "tokio-util"]
frigate = []

[dev-dependencies]
async-std = "1.13.0"
Expand Down
37 changes: 37 additions & 0 deletions src/notification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//!
//! - [`Notification::Header`] for `"blockchain.headers.subscribe"`
//! - [`Notification::ScriptHash`] for `"blockchain.scripthash.subscribe"`
//! - [`Notification::SpSubscribe`] for `"blockchain.silentpayments.subscribe"`
//! - [`Notification::Unknown`] for unrecognized or unsupported methods
//!
//! Each variant wraps a struct that contains the deserialized payload for that notification type.
Expand Down Expand Up @@ -32,6 +33,11 @@ pub enum Notification {
/// status.
ScriptHash(ScriptHashNotification),

/// A notification from `"blockchain.silentpayments.subscribe"` indicating a new history
/// of transactions
#[cfg(feature = "frigate")]
SpSubscribe(SpNotification),

/// A catch-all for notifications with unrecognized methods.
///
/// The original [`RawNotification`] is preserved for downstream inspection.
Expand All @@ -52,6 +58,10 @@ impl Notification {
"blockchain.scripthash.subscribe" => {
ScriptHashNotification::deserialize(params).map(Notification::ScriptHash)
}
#[cfg(feature = "frigate")]
"blockchain.silentpayments.subscribe" => {
SpNotification::deserialize(params).map(Notification::SpSubscribe)
}
_ => Ok(Notification::Unknown(raw.clone())),
}
}
Expand Down Expand Up @@ -102,3 +112,30 @@ impl ScriptHashNotification {
self.param_1
}
}

#[cfg(feature = "frigate")]
#[derive(Debug, Clone, serde::Deserialize)]
pub struct SpSubscription {
pub address: String,
pub labels: Vec<u32>,
pub start_height: u32,
}

#[cfg(feature = "frigate")]
#[derive(Debug, Clone, serde::Deserialize)]
pub struct TxTweak {
pub height: u32,
pub tx_hash: bitcoin::Txid,
pub tweak_key: bitcoin::secp256k1::PublicKey,
}

/// A notification indicating new confirmed transactions
///
/// Corresponds to `"blockchain.silentpayments.subscribe"` Frigate Electrum notification method
#[cfg(feature = "frigate")]
#[derive(Debug, Clone, serde::Deserialize)]
pub struct SpNotification {
pub subscription: SpSubscription,
pub progress: f32,
pub history: Vec<TxTweak>,
}
28 changes: 28 additions & 0 deletions src/pending_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ macro_rules! gen_pending_request_types {
};
}

#[cfg(not(feature = "frigate"))]
gen_pending_request_types! {
Header,
HeaderWithProof,
Expand All @@ -106,6 +107,33 @@ gen_pending_request_types! {
Custom
}

#[cfg(feature = "frigate")]
gen_pending_request_types! {
Header,
HeaderWithProof,
Headers,
HeadersWithCheckpoint,
EstimateFee,
HeadersSubscribe,
RelayFee,
GetBalance,
GetHistory,
GetMempool,
ListUnspent,
ScriptHashSubscribe,
ScriptHashUnsubscribe,
BroadcastTx,
GetTx,
GetTxMerkle,
GetTxidFromPos,
GetFeeHistogram,
Banner,
Ping,
Version,
SpSubscribe,
SpUnSubscribe
}

type Handler =
Box<dyn FnOnce(Result<Value, Value>) -> Result<Option<Event>, serde_json::Error> + Send + Sync>;

Expand Down
90 changes: 90 additions & 0 deletions src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,3 +656,93 @@ impl Request for Ping {
("server.ping".into(), vec![])
}
}

/// A request to establish connection with Frigate Electrum client
///
/// This corresponds to the `"server.version"` Frigate Electrum RPC method
///
/// See: https://github.com/sparrowwallet/frigate
#[cfg(feature = "frigate")]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Version {
pub client_name: CowStr,
pub version: CowStr,
}

#[cfg(feature = "frigate")]
impl Request for Version {
type Response = Vec<String>;

fn to_method_and_params(&self) -> MethodAndParams {
(
"server.version".into(),
vec![self.client_name.clone().into(), self.version.clone().into()],
)
}
}

/// A request to subscribe to payment outputs belonging to the provided keys
///
/// This corresponds to the `"blockchain.silentpayments.subscribe"` Frigate Electrum RPC method.
/// It returns The silent payment address that has been subscribed.
///
/// See: https://github.com/sparrowwallet/frigate#blockchainsilentpaymentssubscribe
#[cfg(feature = "frigate")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SpSubscribe {
pub scan_priv_key: bitcoin::secp256k1::SecretKey,
pub scan_pub_key: bitcoin::secp256k1::PublicKey,
pub start_height: Option<u32>,
pub labels: Option<Vec<u32>>,
}

#[cfg(feature = "frigate")]
impl Request for SpSubscribe {
type Response = String;

fn to_method_and_params(&self) -> MethodAndParams {
let mut params = vec![
serde_json::json!(self.scan_priv_key),
serde_json::json!(self.scan_pub_key),
];

if let Some(start_height) = self.start_height {
params.push(start_height.into());
}

if let Some(labels) = &self.labels {
params.push(labels.clone().into());
}

("blockchain.silentpayments.subscribe".into(), params)
}
}

/// A request to unsubscribe to payment outputs belonging to the provided keys
///
/// This corresponds to the `"blockchain.silentpayments.unsubscribe"` Frigate Electrum RPC method.
/// It returns The silent payment address that has been subscribed.This should cancel any scans that
/// may be currently running for this address.
///
/// See: https://github.com/sparrowwallet/frigate#blockchainsilentpaymentsunsubscribe
#[cfg(feature = "frigate")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SpUnSubscribe {
pub scan_priv_key: bitcoin::secp256k1::SecretKey,
pub scan_pub_key: bitcoin::secp256k1::PublicKey,
}

#[cfg(feature = "frigate")]
impl Request for SpUnSubscribe {
type Response = String;

fn to_method_and_params(&self) -> MethodAndParams {
(
"blockchain.silentpayments.unsubscribe".into(),
vec![
serde_json::json!(self.scan_priv_key),
serde_json::json!(self.scan_pub_key),
],
)
}
}