Skip to content

Commit

Permalink
If payment is sent to a lightningAddress, this information is now pro…
Browse files Browse the repository at this point in the history
…perly stored in the payment_metadata table
  • Loading branch information
robbiehanson committed Nov 14, 2024
1 parent ff8c07d commit 25c530d
Show file tree
Hide file tree
Showing 15 changed files with 176 additions and 131 deletions.
15 changes: 14 additions & 1 deletion phoenix-ios/phoenix-ios/kotlin/KotlinExtensions+Payments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,19 @@ extension WalletPaymentInfo {
func addToContactsInfo() -> AddToContactsInfo? {

if payment is Lightning_kmpOutgoingPayment {
// Todo: check for lightning address (requires db change in metadata table)

// First check for a lightning address.
// Remember that an outgoing payment might have both an address & offer (i.e. BIP-353).
// But from the user's perspective, they sent a payment to the address.
// The fact that it used an offer under-the-hood is just a technicality.
// What they expect to save is the lightning address.
//
// Note: in the future we may support something like "offer pinning" for an LN address.
// But that's a different feature. The user's perspective remains the same.
//
if let address = self.metadata.lightningAddress {
return AddToContactsInfo(offer: nil, address: address)
}

let invoiceRequest = payment.outgoingInvoiceRequest()
if let offer = invoiceRequest?.offer {
Expand All @@ -230,6 +242,7 @@ extension WalletPaymentMetadata {
originalFiat: nil,
userDescription: nil,
userNotes: nil,
lightningAddress: nil,
modifiedAt: nil
)
}
Expand Down
6 changes: 6 additions & 0 deletions phoenix-ios/phoenix-ios/officers/BusinessManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ class BusinessManager {
}.store(in: &appCancellables)

WatchTower.shared.prepare()

#if DEBUG
if let path = PlatformIosKt.getDatabaseFilesDirectoryPath(ctx: PlatformContext.default) {
log.debug("DB path: \(path)")
}
#endif
}

// --------------------------------------------------
Expand Down
26 changes: 12 additions & 14 deletions phoenix-ios/phoenix-ios/views/send/ValidateView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1809,10 +1809,6 @@ struct ValidateView: View {
log.warning("ignore: payment already in progress")
return
}
guard let peer = Biz.business.peerManager.peerStateValue() else {
log.warning("ignore: peer == nil")
return
}

paymentInProgress = true
payOfferProblem = nil
Expand All @@ -1832,14 +1828,16 @@ struct ValidateView: View {
payerKey = Lightning_randomKey()
}

let response: Lightning_kmpOfferNotPaid? = try await peer.betterPayOffer(
paymentId: paymentId,
amount: Lightning_kmpMilliSatoshi(msat: msat),
offer: model.offer,
payerKey: payerKey,
payerNote: payerNote,
fetchInvoiceTimeoutInSeconds: 30
)
let response: Lightning_kmpOfferNotPaid? =
try await Biz.business.sendManager._payBolt12Offer(
paymentId: paymentId,
amount: Lightning_kmpMilliSatoshi(msat: msat),
offer: model.offer,
lightningAddress: model.lightningAddress,
payerKey: payerKey,
payerNote: payerNote,
fetchInvoiceTimeoutInSeconds: 30
)

paymentInProgress = false

Expand Down Expand Up @@ -1992,7 +1990,7 @@ struct ValidateView: View {
do {
let result1: Bitcoin_kmpEither<SendManager.LnurlPayError, LnurlPay.Invoice> =
try await Biz.business.sendManager.lnurlPay_requestInvoice(
paymentIntent: model.paymentIntent,
pay: model,
amount: updatedMsat,
comment: commentSnapshot
)
Expand All @@ -2014,7 +2012,7 @@ struct ValidateView: View {
let invoice: LnurlPay.Invoice = result1.right!

try await Biz.business.sendManager.lnurlPay_payInvoice(
paymentIntent: model.paymentIntent,
pay: model,
amount: updatedMsat,
comment: commentSnapshot,
invoice: invoice,
Expand Down
7 changes: 2 additions & 5 deletions phoenix-ios/phoenix-ios/views/transactions/PaymentCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,8 @@ fileprivate var log = LoggerFactory.shared.logger(filename, .warning)

struct PaymentCell : View {

static let fetchOptions = WalletPaymentFetchOptions.companion.Descriptions.plus(
other: WalletPaymentFetchOptions.companion.OriginalFiat
).plus(
other: WalletPaymentFetchOptions.companion.Contact
)
static let fetchOptions = WalletPaymentFetchOptions.companion.Common
// ^ Descriptions + OriginalFiat + Contacts

private let paymentsManager = Biz.business.paymentsManager

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ data class WalletPaymentMetadata(
val originalFiat: ExchangeRate.BitcoinPriceRate? = null,
val userDescription: String? = null,
val userNotes: String? = null,
val lightningAddress: String? = null,
val modifiedAt: Long? = null
)

Expand Down Expand Up @@ -211,6 +212,7 @@ data class WalletPaymentFetchOptions(val flags: Int) { // <- bitmask
val OriginalFiat = WalletPaymentFetchOptions(1 shl 3)
val Contact = WalletPaymentFetchOptions(1 shl 4)

val Common = Descriptions + OriginalFiat + Contact
val All = Descriptions + Lnurl + UserNotes + OriginalFiat + Contact
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ class MetadataQueries(val database: PaymentsDatabase) {
user_notes = data.user_notes,
modified_at = data.modified_at,
original_fiat_type = data.original_fiat?.first,
original_fiat_rate = data.original_fiat?.second
original_fiat_rate = data.original_fiat?.second,
lightning_address = data.lightning_address
)
}

Expand All @@ -48,33 +49,20 @@ class MetadataQueries(val database: PaymentsDatabase) {
WalletPaymentFetchOptions.None -> {
null
}
WalletPaymentFetchOptions.Descriptions + WalletPaymentFetchOptions.OriginalFiat -> {
getMetadataDescriptionsAndOriginalFiat(id)
}
WalletPaymentFetchOptions.Descriptions -> {
getMetadataDescriptions(id)
WalletPaymentFetchOptions.Common -> {
getMetadataCommon(id)
}
else -> {
getMetadataAll(id)
}
}
}

private fun getMetadataDescriptions(id: WalletPaymentId): WalletPaymentMetadata? {
return queries.fetchDescriptions(
type = id.dbType.value,
id = id.dbId,
mapper = ::mapDescriptions
).executeAsOneOrNull()
}

private fun getMetadataDescriptionsAndOriginalFiat(
id: WalletPaymentId
): WalletPaymentMetadata? {
return queries.fetchDescriptionsAndOriginalFiat(
private fun getMetadataCommon(id: WalletPaymentId): WalletPaymentMetadata? {
return queries.fetchCommon(
type = id.dbType.value,
id = id.dbId,
mapper = ::mapDescriptionsAndOriginalFiat
mapper = ::mapCommon
).executeAsOneOrNull()
}

Expand Down Expand Up @@ -120,35 +108,22 @@ class MetadataQueries(val database: PaymentsDatabase) {
user_notes = userNotes,
modified_at = modifiedAt,
original_fiat_type = null,
original_fiat_rate = null
original_fiat_rate = null,
lightning_address = null
)
}
didUpdateWalletPaymentMetadata(id, database)
}
}

companion object {
fun mapDescriptions(
lnurl_description: String?,
user_description: String?,
modified_at: Long?
): WalletPaymentMetadata {
val lnurl = if (lnurl_description != null) {
LnurlPayMetadata.placeholder(lnurl_description)
} else null
return WalletPaymentMetadata(
userDescription = user_description,
lnurl = lnurl,
modifiedAt = modified_at
)
}

fun mapDescriptionsAndOriginalFiat(
fun mapCommon(
lnurl_description: String?,
user_description: String?,
modified_at: Long?,
original_fiat_type: String?,
original_fiat_rate: Double?
original_fiat_rate: Double?,
lightning_address: String?
): WalletPaymentMetadata {
val lnurl = if (lnurl_description != null) {
LnurlPayMetadata.placeholder(lnurl_description)
Expand All @@ -170,6 +145,7 @@ class MetadataQueries(val database: PaymentsDatabase) {
lnurl = lnurl,
originalFiat = originalFiat,
userDescription = user_description,
lightningAddress = lightning_address,
modifiedAt = modified_at
)
}
Expand All @@ -189,7 +165,8 @@ class MetadataQueries(val database: PaymentsDatabase) {
user_notes: String?,
modified_at: Long?,
original_fiat_type: String?,
original_fiat_rate: Double?
original_fiat_rate: Double?,
lightning_address: String?
): WalletPaymentMetadata {
val lnurlBase =
if (lnurl_base_type != null && lnurl_base_blob != null) {
Expand Down Expand Up @@ -219,6 +196,7 @@ class MetadataQueries(val database: PaymentsDatabase) {
original_fiat = originalFiat,
user_description = user_description,
user_notes = user_notes,
lightning_address = lightning_address,
modified_at = modified_at
).deserialize()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ data class WalletPaymentMetadataRow(
val original_fiat: Pair<String, Double>? = null,
val user_description: String? = null,
val user_notes: String? = null,
val lightning_address: String? = null,
val modified_at: Long? = null
) {

Expand Down Expand Up @@ -271,6 +272,7 @@ data class WalletPaymentMetadataRow(
originalFiat = originalFiat,
userDescription = user_description,
userNotes = user_notes,
lightningAddress = lightning_address,
modifiedAt = modified_at
)
}
Expand All @@ -286,6 +288,7 @@ data class WalletPaymentMetadataRow(
&& original_fiat == null
&& user_description == null
&& user_notes == null
&& lightning_address == null
}

companion object {
Expand Down Expand Up @@ -319,6 +322,7 @@ data class WalletPaymentMetadataRow(
original_fiat = originalFiat,
user_description = metadata.userDescription,
user_notes = metadata.userNotes,
lightning_address = metadata.lightningAddress,
modified_at = metadata.modifiedAt
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import fr.acinq.lightning.wire.OfferTypes
import fr.acinq.phoenix.PhoenixBusiness
import fr.acinq.phoenix.data.ContactAddress
import fr.acinq.phoenix.data.ContactInfo
import fr.acinq.phoenix.data.WalletPaymentInfo
import fr.acinq.phoenix.db.SqliteAppDb
import fr.acinq.phoenix.utils.extensions.incomingOfferMetadata
import fr.acinq.phoenix.utils.extensions.outgoingInvoiceRequest
Expand Down Expand Up @@ -122,24 +123,6 @@ class ContactsManager(
return contactsMap.value[contactId]
}

fun contactIdForPayment(payment: WalletPayment): UUID? {
return if (payment is IncomingPayment) {
payment.incomingOfferMetadata()?.let { offerMetadata ->
publicKeyMap.value[offerMetadata.payerKey]
}
} else {
payment.outgoingInvoiceRequest()?.let {invoiceRequest ->
offerMap.value[invoiceRequest.offer.offerId]
}
}
}

fun contactForPayment(payment: WalletPayment): ContactInfo? {
return contactIdForPayment(payment)?.let { contactId ->
contactForId(contactId)
}
}

fun contactIdForOfferId(offerId: ByteVector32): UUID? {
return offerMap.value[offerId]
}
Expand Down Expand Up @@ -177,4 +160,24 @@ class ContactsManager(
contactForId(contactId)
}
}

fun contactIdForPaymentInfo(paymentInfo: WalletPaymentInfo): UUID? {
return if (paymentInfo.payment is IncomingPayment) {
paymentInfo.payment.incomingOfferMetadata()?.let { offerMetadata ->
contactIdForPayerPubKey(offerMetadata.payerKey)
}
} else {
paymentInfo.metadata.lightningAddress?.let { address ->
contactIdForLightningAddress(address)
} ?: paymentInfo.payment.outgoingInvoiceRequest()?.let { invoiceRequest ->
contactIdForOfferId(invoiceRequest.offer.offerId)
}
}
}

fun contactForPaymentInfo(paymentInfo: WalletPaymentInfo): ContactInfo? {
return contactIdForPaymentInfo(paymentInfo)?.let { contactId ->
contactForId(contactId)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class PaymentsFetcher(
// So we should refresh them everytime.
// Note that fetching this information doesn't require a trip to the database.
// Everything is already in memory, so the lookup is very fast.
val updatedContact = contactsManager.contactForPayment(paymentInfo.payment)
val updatedContact = contactsManager.contactForPaymentInfo(paymentInfo)
paymentInfo.copy(contact = updatedContact)
} else {
paymentInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,17 +200,20 @@ class PaymentsManager(
is WalletPaymentId.ChannelCloseOutgoingPaymentId -> paymentsDb().getChannelCloseOutgoingPayment(id.id, options)
is WalletPaymentId.SpliceCpfpOutgoingPaymentId -> paymentsDb().getSpliceCpfpOutgoingPayment(id.id, options)
is WalletPaymentId.InboundLiquidityOutgoingPaymentId -> paymentsDb().getInboundLiquidityOutgoingPayment(id.id, options)
}?.let {
val payment = it.first
val contact = if (options.contains(WalletPaymentFetchOptions.Contact)) {
contactsManager.contactForPayment(payment)
} else { null }
WalletPaymentInfo(
payment = payment,
metadata = it.second ?: WalletPaymentMetadata(),
contact = contact,
}?.let { pair ->
val paymentInfo = WalletPaymentInfo(
payment = pair.first,
metadata = pair.second ?: WalletPaymentMetadata(),
contact = null,
fetchOptions = options
)
if (options.contains(WalletPaymentFetchOptions.Contact)) {
contactsManager.contactForPaymentInfo(paymentInfo)?.let { contact ->
paymentInfo.copy(contact = contact)
} ?: paymentInfo
} else {
paymentInfo
}
}
}
}
Loading

0 comments on commit 25c530d

Please sign in to comment.