damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

commit b1fee253b4faa691c8b4eb0c445df74c721bc829
parent e472e559a5653de6487d8c5df98bc77b7811e873
Author: William Casarin <jb55@jb55.com>
Date:   Sun, 14 May 2023 10:48:44 -0700

nwc: fix bug where private nwc zaps weren't getting tracked properly

It was using the wrapper zap request id instead of the inner id. Fix
this type error by creating a ZapRequestId wrapper that makes sure it
uses the proper request id.

Diffstat:
Mdamus/Components/ZapButton.swift | 20++++++++++++--------
Mdamus/Nostr/NostrEvent.swift | 51+++++++++++++++++++++++++++++++++++++++++++++------
Mdamus/Util/WalletConnect.swift | 3++-
Mdamus/Util/Zap.swift | 19+++++++++++++++++--
Mdamus/Util/Zaps.swift | 6+++---
Mdamus/Views/Events/ZapEvent.swift | 2+-
6 files changed, 80 insertions(+), 21 deletions(-)

diff --git a/damus/Components/ZapButton.swift b/damus/Components/ZapButton.swift @@ -174,7 +174,7 @@ struct ZapButton: View { struct ZapButton_Previews: PreviewProvider { static var previews: some View { - let pending_zap = PendingZap(amount_msat: 1000, target: ZapTarget.note(id: "noteid", author: "author"), request: test_zap_request, type: .pub, state: .external(.init(state: .fetching_invoice))) + let pending_zap = PendingZap(amount_msat: 1000, target: ZapTarget.note(id: "noteid", author: "author"), request: .normal(test_zap_request), type: .pub, state: .external(.init(state: .fetching_invoice))) let zaps = ZapsDataModel([.pending(pending_zap)]) ZapButton(damus_state: test_damus_state(), event: test_event, lnurl: "lnurl", zaps: zaps) @@ -203,7 +203,7 @@ func send_zap(damus_state: DamusState, event: NostrEvent, lnurl: String, is_cust let target = ZapTarget.note(id: event.id, author: event.pubkey) let content = comment ?? "" - guard let zapreq = make_zap_request_event(keypair: keypair, content: content, relays: relays, target: target, zap_type: zap_type) else { + guard let mzapreq = make_zap_request_event(keypair: keypair, content: content, relays: relays, target: target, zap_type: zap_type) else { // this should never happen return } @@ -211,7 +211,9 @@ func send_zap(damus_state: DamusState, event: NostrEvent, lnurl: String, is_cust let zap_amount = amount_sats ?? damus_state.settings.default_zap_amount let amount_msat = Int64(zap_amount) * 1000 let pending_zap_state = initial_pending_zap_state(settings: damus_state.settings) - let pending_zap = PendingZap(amount_msat: amount_msat, target: target, request: ZapRequest(ev: zapreq), type: zap_type, state: pending_zap_state) + let pending_zap = PendingZap(amount_msat: amount_msat, target: target, request: mzapreq, type: zap_type, state: pending_zap_state) + let zapreq = mzapreq.potentially_anon_outer_request.ev + let reqid = ZapRequestId(from_makezap: mzapreq) UIImpactFeedbackGenerator(style: .heavy).impactOccurred() damus_state.add_zap(zap: .pending(pending_zap)) @@ -225,7 +227,7 @@ func send_zap(damus_state: DamusState, event: NostrEvent, lnurl: String, is_cust guard let payreq = mpayreq else { // TODO: show error DispatchQueue.main.async { - remove_zap(reqid: zapreq.id, zapcache: damus_state.zaps, evcache: damus_state.events) + remove_zap(reqid: reqid, zapcache: damus_state.zaps, evcache: damus_state.events) let typ = ZappingEventType.failed(.bad_lnurl) let ev = ZappingEvent(is_custom: is_custom, type: typ, event: event) notify(.zapping, ev) @@ -239,7 +241,7 @@ func send_zap(damus_state: DamusState, event: NostrEvent, lnurl: String, is_cust guard let inv = await fetch_zap_invoice(payreq, zapreq: zapreq, sats: zap_amount, zap_type: zap_type, comment: comment) else { DispatchQueue.main.async { - remove_zap(reqid: zapreq.id, zapcache: damus_state.zaps, evcache: damus_state.events) + remove_zap(reqid: reqid, zapcache: damus_state.zaps, evcache: damus_state.events) let typ = ZappingEventType.failed(.fetching_invoice) let ev = ZappingEvent(is_custom: is_custom, type: typ, event: event) notify(.zapping, ev) @@ -253,7 +255,7 @@ func send_zap(damus_state: DamusState, event: NostrEvent, lnurl: String, is_cust case .nwc(let nwc_state): // don't both continuing, user has canceled if case .cancel_fetching_invoice = nwc_state.state { - remove_zap(reqid: zapreq.id, zapcache: damus_state.zaps, evcache: damus_state.events) + remove_zap(reqid: reqid, zapcache: damus_state.zaps, evcache: damus_state.events) return } @@ -308,10 +310,12 @@ func cancel_zap(zap: PendingZap, box: PostBox, zapcache: Zaps, evcache: EventCac if let err = box.cancel_send(evid: nwc_req.id) { return .send_err(err) } - remove_zap(reqid: zap.request.ev.id, zapcache: zapcache, evcache: evcache) + let reqid = ZapRequestId(from_pending: zap) + remove_zap(reqid: reqid, zapcache: zapcache, evcache: evcache) case .failed: - remove_zap(reqid: zap.request.ev.id, zapcache: zapcache, evcache: evcache) + let reqid = ZapRequestId(from_pending: zap) + remove_zap(reqid: reqid, zapcache: zapcache, evcache: evcache) } return nil diff --git a/damus/Nostr/NostrEvent.swift b/damus/Nostr/NostrEvent.swift @@ -512,7 +512,12 @@ func zap_target_to_tags(_ target: ZapTarget) -> [[String]] { } } -func make_private_zap_request_event(identity: FullKeypair, enc_key: FullKeypair, target: ZapTarget, message: String) -> String? { +struct PrivateZapRequest { + let req: ZapRequest + let enc: String +} + +func make_private_zap_request_event(identity: FullKeypair, enc_key: FullKeypair, target: ZapTarget, message: String) -> PrivateZapRequest? { // target tags must be the same as zap request target tags let tags = zap_target_to_tags(target) @@ -520,10 +525,13 @@ func make_private_zap_request_event(identity: FullKeypair, enc_key: FullKeypair, note.id = calculate_event_id(ev: note) note.sig = sign_event(privkey: identity.privkey, ev: note) - guard let note_json = encode_json(note) else { + guard let note_json = encode_json(note), + let enc = encrypt_message(message: note_json, privkey: enc_key.privkey, to_pk: target.pubkey, encoding: .bech32) + else { return nil } - return encrypt_message(message: note_json, privkey: enc_key.privkey, to_pk: target.pubkey, encoding: .bech32) + + return PrivateZapRequest(req: ZapRequest(ev: note), enc: enc) } func decrypt_private_zap(our_privkey: String, zapreq: NostrEvent, target: ZapTarget) -> NostrEvent? { @@ -587,7 +595,30 @@ func generate_private_keypair(our_privkey: String, id: String, created_at: Int64 return FullKeypair(pubkey: pubkey, privkey: privkey) } -func make_zap_request_event(keypair: FullKeypair, content: String, relays: [RelayDescriptor], target: ZapTarget, zap_type: ZapType) -> NostrEvent? { +enum MakeZapRequest { + case priv(ZapRequest, PrivateZapRequest) + case normal(ZapRequest) + + var private_inner_request: ZapRequest { + switch self { + case .priv(let _, let pzr): + return pzr.req + case .normal(let zr): + return zr + } + } + + var potentially_anon_outer_request: ZapRequest { + switch self { + case .priv(let zr, _): + return zr + case .normal(let zr): + return zr + } + } +} + +func make_zap_request_event(keypair: FullKeypair, content: String, relays: [RelayDescriptor], target: ZapTarget, zap_type: ZapType) -> MakeZapRequest? { var tags = zap_target_to_tags(target) var relay_tag = ["relays"] relay_tag.append(contentsOf: relays.map { $0.url.id }) @@ -597,6 +628,8 @@ func make_zap_request_event(keypair: FullKeypair, content: String, relays: [Rela let now = Int64(Date().timeIntervalSince1970) + var privzap_req: PrivateZapRequest? + var message = content switch zap_type { case .pub: @@ -614,14 +647,20 @@ func make_zap_request_event(keypair: FullKeypair, content: String, relays: [Rela guard let privreq = make_private_zap_request_event(identity: keypair, enc_key: kp, target: target, message: message) else { return nil } - tags.append(["anon", privreq]) + tags.append(["anon", privreq.enc]) message = "" + privzap_req = privreq } let ev = NostrEvent(content: message, pubkey: kp.pubkey, kind: 9734, tags: tags, createdAt: now) ev.id = calculate_event_id(ev: ev) ev.sig = sign_event(privkey: kp.privkey, ev: ev) - return ev + let zapreq = ZapRequest(ev: ev) + if let privzap_req { + return .priv(zapreq, privzap_req) + } else { + return .normal(zapreq) + } } func uniq<T: Hashable>(_ xs: [T]) -> [T] { diff --git a/damus/Util/WalletConnect.swift b/damus/Util/WalletConnect.swift @@ -234,7 +234,8 @@ func nwc_error(zapcache: Zaps, evcache: EventCache, resp: FullWalletResponse) { } // remove the pending zap if there was an error - remove_zap(reqid: pzap.request.ev.id, zapcache: zapcache, evcache: evcache) + let reqid = ZapRequestId(from_pending: pzap) + remove_zap(reqid: reqid, zapcache: zapcache, evcache: evcache) return } } diff --git a/damus/Util/Zap.swift b/damus/Util/Zap.swift @@ -105,10 +105,10 @@ class PendingZap { let type: ZapType private(set) var state: PendingZapState - init(amount_msat: Int64, target: ZapTarget, request: ZapRequest, type: ZapType, state: PendingZapState) { + init(amount_msat: Int64, target: ZapTarget, request: MakeZapRequest, type: ZapType, state: PendingZapState) { self.amount_msat = amount_msat self.target = target - self.request = request + self.request = request.private_inner_request self.type = type self.state = state } @@ -125,6 +125,21 @@ class PendingZap { } } +struct ZapRequestId: Equatable { + let reqid: String + + init(from_zap: Zapping) { + self.reqid = from_zap.request.id + } + + init(from_makezap: MakeZapRequest) { + self.reqid = from_makezap.private_inner_request.ev.id + } + + init(from_pending: PendingZap) { + self.reqid = from_pending.request.ev.id + } +} enum Zapping { case zap(Zap) diff --git a/damus/Util/Zaps.swift b/damus/Util/Zaps.swift @@ -91,9 +91,9 @@ class Zaps { } } -func remove_zap(reqid: String, zapcache: Zaps, evcache: EventCache) { - guard let zap = zapcache.remove_zap(reqid: reqid) else { +func remove_zap(reqid: ZapRequestId, zapcache: Zaps, evcache: EventCache) { + guard let zap = zapcache.remove_zap(reqid: reqid.reqid) else { return } - evcache.get_cache_data(zap.target.id).zaps_model.remove(reqid: reqid) + evcache.get_cache_data(zap.target.id).zaps_model.remove(reqid: reqid.reqid) } diff --git a/damus/Views/Events/ZapEvent.swift b/damus/Views/Events/ZapEvent.swift @@ -45,7 +45,7 @@ let test_zap = Zap(event: test_event, invoice: test_zap_invoice, zapper: "zapper let test_private_zap = Zap(event: test_event, invoice: test_zap_invoice, zapper: "zapper", target: .profile("pk"), request: test_zap_request, is_anon: false, private_request: test_event) -let test_pending_zap = PendingZap(amount_msat: 10000, target: .note(id: "id", author: "pk"), request: test_zap_request, type: .pub, state: .external(.init(state: .fetching_invoice))) +let test_pending_zap = PendingZap(amount_msat: 10000, target: .note(id: "id", author: "pk"), request: .normal(test_zap_request), type: .pub, state: .external(.init(state: .fetching_invoice))) struct ZapEvent_Previews: PreviewProvider { static var previews: some View {