Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | ref(deps): Cache Nostr keys to avoid async calls |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
2be15f5044cd004efe4598da2ad988fb |
User & Date: | carlos 2023-10-27 14:56:56 |
Context
2023-11-12
| ||
11:23 | chore: Bump dependencies check-in: 7668189ce1 user: carlos tags: trunk | |
2023-10-27
| ||
14:56 | ref(deps): Cache Nostr keys to avoid async calls check-in: 2be15f5044 user: carlos tags: trunk | |
2023-10-22
| ||
09:46 | ref(deps): Bump ln-sdk and powpub dependencies check-in: 8321f1314c user: carlos tags: trunk | |
Changes
Changes to Cargo.lock.
︙ | ︙ | |||
2300 2301 2302 2303 2304 2305 2306 | version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" [[package]] name = "ln-sdk" version = "0.2.0-SNAPSHOT" | | | 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 | version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" [[package]] name = "ln-sdk" version = "0.2.0-SNAPSHOT" source = "git+https://oak-node.net/cgit/ln-sdk.git?rev=8366a4dcaf4df53b4ecafde1af30b2fb865e8200#8366a4dcaf4df53b4ecafde1af30b2fb865e8200" dependencies = [ "anyhow", "async-trait", "base64 0.20.0", "bech32", "bitcoin_hashes 0.12.0", "cfg_aliases", |
︙ | ︙ | |||
2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 | "openssl-sys", "schannel", "security-framework", "security-framework-sys", "tempfile", ] [[package]] name = "nom" version = "4.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" dependencies = [ "memchr", | > > > > > > | 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 | "openssl-sys", "schannel", "security-framework", "security-framework-sys", "tempfile", ] [[package]] name = "negentropy" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e664971378a3987224f7a0e10059782035e89899ae403718ee07de85bec42afe" [[package]] name = "nom" version = "4.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" dependencies = [ "memchr", |
︙ | ︙ | |||
2603 2604 2605 2606 2607 2608 2609 | dependencies = [ "windows-sys 0.48.0", ] [[package]] name = "nostr" version = "0.24.0" | | > | | | 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 | dependencies = [ "windows-sys 0.48.0", ] [[package]] name = "nostr" version = "0.24.0" source = "git+https://github.com/rust-nostr/nostr?rev=2799d037ecef0f14e993ef140920ed9c90d8374b#2799d037ecef0f14e993ef140920ed9c90d8374b" dependencies = [ "aes", "base64 0.21.4", "bip39", "bitcoin 0.30.1", "cbc", "chacha20", "getrandom 0.2.10", "instant", "negentropy", "once_cell", "reqwest", "serde", "serde_json", "tracing", "url-fork", ] [[package]] name = "nostr-sdk" version = "0.24.0" source = "git+https://github.com/rust-nostr/nostr?rev=2799d037ecef0f14e993ef140920ed9c90d8374b#2799d037ecef0f14e993ef140920ed9c90d8374b" dependencies = [ "async-utility", "nostr", "nostr-sdk-net", "once_cell", "thiserror", "tokio", "tracing", ] [[package]] name = "nostr-sdk-net" version = "0.24.0" source = "git+https://github.com/rust-nostr/nostr?rev=2799d037ecef0f14e993ef140920ed9c90d8374b#2799d037ecef0f14e993ef140920ed9c90d8374b" dependencies = [ "futures-util", "thiserror", "tokio", "tokio-rustls 0.24.1", "tokio-socks", "tokio-tungstenite", |
︙ | ︙ | |||
3208 3209 3210 3211 3212 3213 3214 | "flate2", "miniz_oxide", ] [[package]] name = "powpub" version = "0.1.4-SNAPSHOT" | | | 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 | "flate2", "miniz_oxide", ] [[package]] name = "powpub" version = "0.1.4-SNAPSHOT" source = "git+https://oak-node.net/cgit/powpub.git?rev=d8e050a51cf12622562efef34d2d6de8b58f363e#d8e050a51cf12622562efef34d2d6de8b58f363e" dependencies = [ "anyhow", "async-trait", "base64 0.20.0", "bech32", "cfg_aliases", "env_logger", |
︙ | ︙ |
Changes to Cargo.toml.
︙ | ︙ | |||
10 11 12 13 14 15 16 | chrono = { version = "*", default-features = false } deltachat = { git = "https://github.com/deltachat/deltachat-core-rust", tag="1.108.0"} futures-lite = "*" futures-util = "*" hex = "*" itertools = "*" lightning-invoice = "*" | | | | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | chrono = { version = "*", default-features = false } deltachat = { git = "https://github.com/deltachat/deltachat-core-rust", tag="1.108.0"} futures-lite = "*" futures-util = "*" hex = "*" itertools = "*" lightning-invoice = "*" ln-sdk = { git = "https://oak-node.net/cgit/ln-sdk.git", rev = "8366a4dcaf4df53b4ecafde1af30b2fb865e8200", features = [ "client-lnd", "utoipa" ] } log = "*" log4rs = "*" qrcode-generator = "*" nostr = { git = "https://github.com/rust-nostr/nostr", default-features = false, features = ["nip04", "nip05", "nip47"], rev = "2799d037ecef0f14e993ef140920ed9c90d8374b" } nostr-sdk = { git = "https://github.com/rust-nostr/nostr", default-features = false, rev = "2799d037ecef0f14e993ef140920ed9c90d8374b" } powpub = { git = "https://oak-node.net/cgit/powpub.git", rev = "d8e050a51cf12622562efef34d2d6de8b58f363e", features = [ "client-lnd", "utoipa" ] } reqwest = { version = "*", default-features = false, features = ["json", "native-tls", "socks"] } rocket = { version = "0.5.0-rc.3", features = ["json"] } rocket_db_pools = { version = "0.1.0-rc.3", features = ["sqlx_sqlite"] } rocket_dyn_templates = { version = "0.1.0-rc.3", features = ["tera"] } serde = "*" serde_json = "*" serde_with = "*" |
︙ | ︙ |
Changes to src/bot/nostr.rs.
︙ | ︙ | |||
39 40 41 42 43 44 45 46 47 48 49 50 51 52 | }) } #[derive(Clone)] pub struct NostrContext { /// The nostr client pub(crate) client: Arc<Client>, pub(in crate::bot) nostr_bot_owner_nprofile: Profile, } impl NostrContext { /// Calculates a map with the current status of each relay used by the [Client] pub(crate) async fn get_relay_status(&self) -> HashMap<String, OakRelayStatus> { let mut result = HashMap::new(); | > > | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | }) } #[derive(Clone)] pub struct NostrContext { /// The nostr client pub(crate) client: Arc<Client>, /// The cached nostr keys. We don't expect to change them at runtime, so a cache helps bypass unnecessary locking on the client. pub(crate) client_keys_cached: Keys, pub(in crate::bot) nostr_bot_owner_nprofile: Profile, } impl NostrContext { /// Calculates a map with the current status of each relay used by the [Client] pub(crate) async fn get_relay_status(&self) -> HashMap<String, OakRelayStatus> { let mut result = HashMap::new(); |
︙ | ︙ | |||
126 127 128 129 130 131 132 | Ok(()) } pub(in crate::bot) fn decrypt(&self, event: &Event) -> String { debug!("Encrypted: {}", event.content); let decrypted_msg = decrypt( | > > | > | | | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | Ok(()) } pub(in crate::bot) fn decrypt(&self, event: &Event) -> String { debug!("Encrypted: {}", event.content); let decrypted_msg = decrypt( &self .client_keys_cached .secret_key() .expect("Client keys don't contain a sk"), &self.nostr_bot_owner_nprofile.public_key, &event.content, ) .unwrap(); debug!("Decrypted: {}", decrypted_msg); decrypted_msg } } #[async_trait] impl BotSender for NostrContext { async fn send(&mut self, msg: String) { let nostr_bot_encrypted_msg = EventBuilder::new_encrypted_direct_msg( &self.client_keys_cached, self.nostr_bot_owner_nprofile.public_key, &msg, None, // TODO Add owner msg ID as reply-to ) .map_err(|e| error!("Failed to create encrypted DM for owner: {e:?}")) .unwrap() .to_event(&self.client_keys_cached) .unwrap(); self.client .send_event(nostr_bot_encrypted_msg) .await .map_err(|e| error!("Failed to send Nostr message: {e:?}")) .unwrap(); |
︙ | ︙ | |||
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | client.add_relay(relay.clone(), None).await?; } client.connect().await; info!("Nostr bot successful configured"); nostr_bot_status.to_state_running()?; Ok(Some(NostrContext { client: Arc::new(client), nostr_bot_owner_nprofile: owner_profile, })) } } } pub async fn start( | > > | 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 | client.add_relay(relay.clone(), None).await?; } client.connect().await; info!("Nostr bot successful configured"); nostr_bot_status.to_state_running()?; let client_keys_cached = client.keys().await; Ok(Some(NostrContext { client: Arc::new(client), client_keys_cached, nostr_bot_owner_nprofile: owner_profile, })) } } } pub async fn start( |
︙ | ︙ |
Changes to src/bot/ons/nip47.rs.
1 2 3 4 5 6 7 8 9 10 11 | use std::collections::HashSet; use std::string::ToString; use anyhow::{anyhow, Result}; use hex::ToHex; use lightning_invoice::Bolt11Invoice; use ln_sdk::client_v2::lnd::{LndClient, LndConfig}; use ln_sdk::client_v2::CanPayInvoices; use ln_sdk::http::HttpConfig; use nostr::nips::nip47::*; use nostr::prelude::{decrypt, encrypt, FromSkStr}; | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | use std::collections::HashSet; use std::string::ToString; use anyhow::{anyhow, Result}; use hex::ToHex; use lightning_invoice::Bolt11Invoice; use ln_sdk::client_v2::lnd::{LndClient, LndConfig}; use ln_sdk::client_v2::CanPayInvoices; use ln_sdk::http::HttpConfig; use nostr::nips::nip47::*; use nostr::prelude::{decrypt, encrypt, FromSkStr}; use nostr::{Event, EventBuilder, Filter, JsonUtil, Keys, Kind, Tag, Timestamp}; use rocket_db_pools::Connection; use serde::{Deserialize, Serialize}; use serde_json::{from_str, to_string}; use sqlx::pool::PoolConnection; use sqlx::Sqlite; use utoipa::ToSchema; |
︙ | ︙ | |||
102 103 104 105 106 107 108 | async fn on_startup( &self, ctx: &mut NostrContext, _spo: &StartupParamsOptional, _db: &mut PoolConnection<Sqlite>, ) -> Result<()> { let info_ev = EventBuilder::new(Kind::WalletConnectInfo, "pay_invoice", &[]) | | | 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | async fn on_startup( &self, ctx: &mut NostrContext, _spo: &StartupParamsOptional, _db: &mut PoolConnection<Sqlite>, ) -> Result<()> { let info_ev = EventBuilder::new(Kind::WalletConnectInfo, "pay_invoice", &[]) .to_event(&ctx.client_keys_cached)?; let relays = ctx.client.relays().await; for (relay_url, _) in relays { let supports_nip47 = ctx .test_nip47_info_event(info_ev.clone(), relay_url.clone()) .await .unwrap_or(false); |
︙ | ︙ | |||
165 166 167 168 169 170 171 | // Derived from secret part of NWC info // The pubkey part of the NWC info is this client's pubkey let matching_keys = nwc_secrets.iter().find(|&k| k.public_key() == event.pubkey); match matching_keys { Some(nwc_keys) => { let sk = nwc_keys.secret_key()?; | | > | 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | // Derived from secret part of NWC info // The pubkey part of the NWC info is this client's pubkey let matching_keys = nwc_secrets.iter().find(|&k| k.public_key() == event.pubkey); match matching_keys { Some(nwc_keys) => { let sk = nwc_keys.secret_key()?; let decrypted = decrypt(&sk, &ctx.client_keys_cached.public_key(), &event.content)?; let req = Request::from_json(&decrypted)?; match (req.method, req.params) { ( Method::PayInvoice, RequestParams::PayInvoice(PayInvoiceRequestParams { invoice }), ) => { |
︙ | ︙ | |||
204 205 206 207 208 209 210 | result: None, } } }; let resp_json_decrypted = resp.as_json(); let resp_json_encrypted = encrypt( | | | | 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | result: None, } } }; let resp_json_decrypted = resp.as_json(); let resp_json_encrypted = encrypt( &ctx.client_keys_cached.secret_key()?, &nwc_keys.public_key(), resp_json_decrypted, )?; let resp_ev = EventBuilder::new( Kind::WalletConnectResponse, resp_json_encrypted, &[ Tag::Event(event.id, None, None), Tag::PubKey(nwc_keys.public_key(), None), ], ) .to_event(&ctx.client_keys_cached)?; ctx.client.send_event(resp_ev).await?; info!("Sent NWC response event"); } _ => { // TODO Handle new methods // TODO Show errors for invalid method / request combos |
︙ | ︙ |
Changes to src/bot/ons/ons1.rs.
︙ | ︙ | |||
71 72 73 74 75 76 77 | .kind(Kind::EncryptedDirectMessage) .since(Timestamp::now()) .authors(vec![ctx .nostr_bot_owner_nprofile .public_key .serialize() .encode_hex::<String>()]) | | | < < | < > | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | .kind(Kind::EncryptedDirectMessage) .since(Timestamp::now()) .authors(vec![ctx .nostr_bot_owner_nprofile .public_key .serialize() .encode_hex::<String>()]) .pubkey(ctx.client_keys_cached.public_key())) } async fn handle( &self, ctx: &mut NostrContext, lnd_client: &LndClient, event: &Event, _composite_params: &StartupParamsOptional, lnd_config: &LndConfig, http_config: &HttpConfig, ) -> Result<Option<()>> { if event.kind == Kind::EncryptedDirectMessage { // Process DMs specifically sent by the owner, ignore the rest if event.tags.iter().any( |t| matches!(t, Tag::PubKey(pk, _) if pk == &ctx.client_keys_cached.public_key()), ) { info!("Received DM from bot owner"); let msg_from_owner = ctx.decrypt(event); info!("Processing command: {msg_from_owner}"); handle_owner_msg(ctx, lnd_client, msg_from_owner, lnd_config, http_config).await; } |
︙ | ︙ |
Changes to src/bot/ons/ons4.rs.
︙ | ︙ | |||
73 74 75 76 77 78 79 | Ok(Filter::new() .kind(Kind::Custom(API_CALL_MESSAGE_KIND)) .since(Timestamp::now()) // ephemeral ones don't need since .authors(vec![deck_profile .public_key .serialize() .encode_hex::<String>()]) | | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | Ok(Filter::new() .kind(Kind::Custom(API_CALL_MESSAGE_KIND)) .since(Timestamp::now()) // ephemeral ones don't need since .authors(vec![deck_profile .public_key .serialize() .encode_hex::<String>()]) .pubkey(ctx.client_keys_cached.public_key())) } async fn handle( &self, ctx: &mut NostrContext, lnd_client: &LndClient, event: &Event, |
︙ | ︙ | |||
159 160 161 162 163 164 165 | amount_sat: r.amt_paid_sat.unwrap_or_default(), ts: r.settle_date, }; let content_json = serde_json::to_string(&content)?; let event_resp_item = EventBuilder::new( Kind::Custom(API_CALL_RESP_MESSAGE_KIND), nip04::encrypt( | | | | | 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | amount_sat: r.amt_paid_sat.unwrap_or_default(), ts: r.settle_date, }; let content_json = serde_json::to_string(&content)?; let event_resp_item = EventBuilder::new( Kind::Custom(API_CALL_RESP_MESSAGE_KIND), nip04::encrypt( &ctx.client_keys_cached.secret_key()?, &deck_nprofile.public_key, content_json, )?, &[Tag::PubKey(deck_nprofile.public_key, None)], ) .to_event(&ctx.client_keys_cached)?; ctx.client.send_event(event_resp_item).await?; } let event_resp_eot = EventBuilder::new( Kind::Custom(API_CALL_RESP_EOT_MESSAGE_KIND), "", &[Tag::PubKey(deck_nprofile.public_key, None)], ) .to_event(&ctx.client_keys_cached)?; ctx.client.send_event(event_resp_eot).await?; info!("Response sent"); } Ok(None) } fn is_enabled(&self, composite_params_opt: &StartupParamsOptional) -> bool { composite_params_opt.ons4.enabled } } |