mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-11 08:47:33 +00:00
fix(clink): keep offer/debit payments on clearnet + short subscription id
Two bugs kept CLINK offer/debit round-trips from completing over the shared account relay client: - The offer relay was treated as a generic "new" relay, so with Tor on it was dialed through the proxy and failed on services that block Tor exits. Register the offer/debit relays as money-operation relays for the duration of the round-trip; the subscribe()-triggered reconnect plus the BasicRelayClient wrong-transport rebuild then move the socket to clearnet. - The subscription id "clink-offer-<event id>" was 76 chars; relays cap REQ subscription ids at 64 (NIP-01) and reject the over-long REQ outright, so the reply never arrived. Use newSubId(); the reply is matched by request id in the listener, not by subscription id. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
f9f7de3ed0
commit
44a6ab6bd4
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.service
|
||||
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.quartz.experimental.clink.client.DebitClient
|
||||
import com.vitorpamplona.quartz.experimental.clink.debits.DebitEvent
|
||||
@@ -28,6 +29,7 @@ import com.vitorpamplona.quartz.experimental.clink.debits.DebitResponse
|
||||
import com.vitorpamplona.quartz.experimental.clink.pointers.NDebit
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.SubscriptionListener
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.single.newSubId
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import kotlinx.coroutines.CancellationException
|
||||
@@ -104,7 +106,9 @@ object ClinkDebitPayer {
|
||||
val relays = client.pointer.relays.toSet()
|
||||
|
||||
val reply = CompletableDeferred<DebitEvent>()
|
||||
val subId = "clink-debit-${request.id}"
|
||||
// A random short id: relays cap subscription ids at 64 chars (NIP-01); the reply is matched
|
||||
// by request id in the listener, not by subId.
|
||||
val subId = newSubId()
|
||||
val filters: Map<NormalizedRelayUrl, List<Filter>> = relays.associateWith { listOf(client.responseFilter(request.id)) }
|
||||
|
||||
val listener =
|
||||
@@ -121,6 +125,12 @@ object ClinkDebitPayer {
|
||||
}
|
||||
}
|
||||
|
||||
// Saved debit wallets are already fed into the money-op relay set by AccountsTorStateConnector,
|
||||
// but register here too so a freshly-added wallet paid before that flow propagates — and any
|
||||
// non-saved debit pointer — still routes under the money-operations Tor preference rather than
|
||||
// the generic `newRelaysViaTor` policy.
|
||||
val torState = Amethyst.instance.torEvaluatorFlow
|
||||
torState.registerMoneyOpRelays(relays)
|
||||
account.client.subscribe(subId, filters, listener)
|
||||
return try {
|
||||
account.client.publish(request, relays)
|
||||
@@ -136,6 +146,7 @@ object ClinkDebitPayer {
|
||||
}
|
||||
} finally {
|
||||
account.client.unsubscribe(subId)
|
||||
torState.unregisterMoneyOpRelays(relays)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.service
|
||||
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.quartz.experimental.clink.client.OfferClient
|
||||
import com.vitorpamplona.quartz.experimental.clink.offers.OfferEvent
|
||||
@@ -28,6 +29,7 @@ import com.vitorpamplona.quartz.experimental.clink.pointers.NOffer
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.SubscriptionListener
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.single.newSubId
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
|
||||
@@ -76,7 +78,9 @@ object ClinkOfferPayer {
|
||||
val request = client.requestInvoice(amountSats = amountSats)
|
||||
|
||||
val reply = CompletableDeferred<OfferEvent>()
|
||||
val subId = "clink-offer-${request.id}"
|
||||
// A random short id: relays cap subscription ids at 64 chars (NIP-01) and reject an
|
||||
// over-long REQ outright. The reply is matched by request id in the listener, not by subId.
|
||||
val subId = newSubId()
|
||||
val filters: Map<NormalizedRelayUrl, List<Filter>> = relays.associateWith { listOf(client.responseFilter(request.id)) }
|
||||
|
||||
val listener =
|
||||
@@ -93,6 +97,14 @@ object ClinkOfferPayer {
|
||||
}
|
||||
}
|
||||
|
||||
// The offer's relays are an ad-hoc payment endpoint, not a saved wallet, so register them
|
||||
// as money-operation relays for the duration of the round-trip. Otherwise account.client
|
||||
// would treat them as generic "new" relays and route them per `newRelaysViaTor`, silently
|
||||
// pushing the payment through Tor (and failing on services that block Tor exits) even when
|
||||
// the user disabled Tor for money operations. The subscribe() below triggers a reconnect, and
|
||||
// BasicRelayClient rebuilds any socket left on the now-wrong (Tor) transport onto clearnet.
|
||||
val torState = Amethyst.instance.torEvaluatorFlow
|
||||
torState.registerMoneyOpRelays(relays)
|
||||
account.client.subscribe(subId, filters, listener)
|
||||
try {
|
||||
account.client.publish(request, relays)
|
||||
@@ -109,6 +121,7 @@ object ClinkOfferPayer {
|
||||
}
|
||||
} finally {
|
||||
account.client.unsubscribe(subId)
|
||||
torState.unregisterMoneyOpRelays(relays)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user