Skip to content

Commit

Permalink
(android) Decouple offer message from static payer key
Browse files Browse the repository at this point in the history
Added a new setting in Payment options to enable or disable
random payer key. We can now send a payer note with a random
payer key.
  • Loading branch information
dpad85 committed Jun 20, 2024
1 parent ee93aa9 commit a9a69bf
Show file tree
Hide file tree
Showing 16 changed files with 67 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,6 @@ fun ContactCompactView(
Spacer(modifier = Modifier.width(8.dp))
Text(text = contact.name, maxLines = 1, overflow = TextOverflow.Ellipsis)
}
currentOffer?.let {
Spacer(modifier = Modifier.height(4.dp))
Text(text = it.encode(), maxLines = 2, overflow = TextOverflow.Ellipsis, style = MaterialTheme.typography.subtitle2)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ import fr.acinq.phoenix.data.WalletPaymentInfo
import fr.acinq.phoenix.data.lnurl.LnurlPay
import fr.acinq.phoenix.utils.extensions.WalletPaymentState
import fr.acinq.phoenix.utils.extensions.minDepthForFunding
import fr.acinq.phoenix.utils.extensions.offerMetadata
import fr.acinq.phoenix.utils.extensions.incomingOfferMetadata
import fr.acinq.phoenix.utils.extensions.state
import io.ktor.http.Url
import kotlinx.coroutines.delay
Expand Down Expand Up @@ -124,7 +124,7 @@ fun PaymentDetailsSplashView(
LnurlPayInfoView(data.payment as LightningOutgoingPayment, data.metadata.lnurl!!)
}

payment.offerMetadata()?.let { meta ->
payment.incomingOfferMetadata()?.let { meta ->
meta.payerNote?.let {
OfferPayerNote(payerNote = it)
Spacer(modifier = Modifier.height(8.dp))
Expand Down Expand Up @@ -451,7 +451,7 @@ private fun OfferPayerContact(payerPubkey: PublicKey?) {
val contactsManager = business.contactsManager
val contactForOffer = produceState<Either<Unit, ContactInfo>?>(initialValue = null, producer = {
value = payerPubkey?.let {
contactsManager.findContactForPayer(it)?.let {
contactsManager.getContactForPayerPubkey(it)?.let {
Either.Right(it)
} ?: Either.Left(Unit)
} ?: Either.Left(Unit)
Expand All @@ -460,7 +460,11 @@ private fun OfferPayerContact(payerPubkey: PublicKey?) {
SplashLabelRow(label = stringResource(id = R.string.paymentdetails_offer_sender_label)) {
when (val contact = contactForOffer.value){
null -> Text(text = stringResource(id = R.string.utils_loading_data))
is Either.Left -> Text(text = stringResource(id = R.string.paymentdetails_offer_sender_unknown))
is Either.Left -> {
Text(text = stringResource(id = R.string.paymentdetails_offer_sender_unknown))
Spacer(modifier = Modifier.height(4.dp))
Text(text = stringResource(id = R.string.paymentdetails_offer_sender_unknown_details), style = MaterialTheme.typography.subtitle2)
}
is Either.Right -> {
ContactCompactView(contact = contact.value, currentOffer = null, onContactChange = {})
}
Expand Down Expand Up @@ -569,7 +573,7 @@ private fun PaymentDestinationView(data: WalletPaymentInfo) {
val details = payment.details
if (details is LightningOutgoingPayment.Details.Blinded) {
val offer = details.paymentRequest.invoiceRequest.offer
SplashLabelRow(label = stringResource(id = R.string.paymentdetails_offer_label)) {
SplashLabelRow(label = stringResource(id = R.string.paymentdetails_destination_label)) {
ContactOrOfferView(offer = offer)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import fr.acinq.bitcoin.utils.Either
import fr.acinq.lightning.db.IncomingPayment
import fr.acinq.lightning.db.OutgoingPayment
import fr.acinq.lightning.db.SpliceCpfpOutgoingPayment
Expand All @@ -68,7 +67,8 @@ import fr.acinq.phoenix.data.WalletPaymentId
import fr.acinq.phoenix.data.WalletPaymentInfo
import fr.acinq.phoenix.data.walletPaymentId
import fr.acinq.phoenix.utils.extensions.WalletPaymentState
import fr.acinq.phoenix.utils.extensions.offerMetadata
import fr.acinq.phoenix.utils.extensions.incomingOfferMetadata
import fr.acinq.phoenix.utils.extensions.outgoingOfferData
import fr.acinq.phoenix.utils.extensions.state


Expand Down Expand Up @@ -178,22 +178,27 @@ private fun PaymentDescription(paymentInfo: WalletPaymentInfo, modifier: Modifie
true -> stringResource(id = R.string.paymentdetails_desc_legacy_migration)
false -> metadata.userDescription
?: metadata.lnurl?.pay?.metadata?.lnid?.takeIf { it.isNotBlank() }?.let {
stringResource(id = R.string.paymentdetails_desc_identifier, it)
stringResource(id = R.string.paymentdetails_desc_to, it)
}
?: metadata.lnurl?.description
?: payment.offerMetadata()?.let { offerMetadata ->
?: payment.incomingOfferMetadata()?.let { offerMetadata ->
val contactsManager = business.contactsManager
val contactForOffer = produceState<Either<Unit, ContactInfo>?>(initialValue = null, producer = {
value = contactsManager.findContactForPayer(offerMetadata.payerKey)?.let { Either.Right(it) } ?: Either.Left(Unit)
val contactForKey = produceState<ContactInfo?>(initialValue = null, producer = {
value = contactsManager.getContactForPayerPubkey(offerMetadata.payerKey)
})

when (val contact = contactForOffer.value) {
null, is Either.Left -> stringResource(id = R.string.paymentdetails_desc_offer_incoming)
is Either.Right -> {
offerMetadata.payerNote ?: "payment from ${contact.value.name}"
}
contactForKey.value?.let {
offerMetadata.payerNote ?: stringResource(id = R.string.paymentdetails_desc_from, it.name)
}
}
?: payment.outgoingOfferData()?.let {
val contactsManager = business.contactsManager
val contactForOffer = produceState<ContactInfo?>(initialValue = null, producer = {
value = contactsManager.getContactForOffer(it)
})

contactForOffer.value?.let { stringResource(id = R.string.paymentdetails_desc_to, it.name) }
}
?: payment.smartDescription(context)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
import fr.acinq.lightning.Lightning
import fr.acinq.lightning.MilliSatoshi
import fr.acinq.lightning.db.LightningOutgoingPayment
Expand All @@ -30,10 +31,13 @@ import fr.acinq.lightning.io.PaymentNotSent
import fr.acinq.lightning.io.PaymentSent
import fr.acinq.lightning.payment.OutgoingPaymentFailure
import fr.acinq.lightning.wire.OfferTypes
import fr.acinq.phoenix.android.PhoenixApplication
import fr.acinq.phoenix.android.utils.datastore.UserPrefsRepository
import fr.acinq.phoenix.managers.NodeParamsManager
import fr.acinq.phoenix.managers.PeerManager
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import org.slf4j.LoggerFactory
import kotlin.time.Duration.Companion.seconds
Expand All @@ -52,7 +56,7 @@ sealed class OfferState {
}
}

class SendOfferViewModel(val peerManager: PeerManager, val nodeParamsManager: NodeParamsManager) : ViewModel() {
class SendOfferViewModel(val peerManager: PeerManager, val nodeParamsManager: NodeParamsManager, val userPrefs: UserPrefsRepository) : ViewModel() {
private val log = LoggerFactory.getLogger(this::class.java)

var state by mutableStateOf<OfferState>(OfferState.Init)
Expand All @@ -65,11 +69,12 @@ class SendOfferViewModel(val peerManager: PeerManager, val nodeParamsManager: No
}) {
val peer = peerManager.getPeer()
val payerNote = message.takeIf { it.isNotBlank() }
val payerKey = if (userPrefs.getPayOfferWithRandomKey.first()) Lightning.randomKey() else nodeParamsManager.defaultOffer().payerKey
log.info("sending amount=$amount message=$message for offer=$offer")
val paymentResult = peer.payOffer(
amount = amount,
offer = offer,
payerKey = payerNote?.let { nodeParamsManager.defaultOffer().payerKey } ?: Lightning.randomKey(),
payerKey = payerKey,
payerNote = payerNote,
fetchInvoiceTimeout = 30.seconds,
// FIXME: this method should accept a trampolineFees parameter
Expand All @@ -86,9 +91,10 @@ class SendOfferViewModel(val peerManager: PeerManager, val nodeParamsManager: No
private val peerManager: PeerManager,
private val nodeParamsManager: NodeParamsManager,
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
val application = checkNotNull(extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as? PhoenixApplication)
@Suppress("UNCHECKED_CAST")
return SendOfferViewModel(peerManager, nodeParamsManager) as T
return SendOfferViewModel(peerManager, nodeParamsManager, application.userPrefs) as T
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ fun PaymentSettingsView(
}

val isOverpaymentEnabled by userPrefs.getIsOverpaymentEnabled.collectAsState(initial = false)
val isRandomPayerKey by userPrefs.getPayOfferWithRandomKey.collectAsState(initial = false)
CardHeader(text = stringResource(id = R.string.paymentsettings_category_outgoing))
Card {
SettingSwitch(
Expand All @@ -132,6 +133,15 @@ fun PaymentSettingsView(
scope.launch { userPrefs.saveIsOverpaymentEnabled(it) }
}
)
SettingSwitch(
title = stringResource(id = R.string.paymentsettings_random_payer_key_title),
description = stringResource(id = R.string.paymentsettings_random_payer_key_desc),
enabled = true,
isChecked = isRandomPayerKey,
onCheckChangeAttempt = {
scope.launch { userPrefs.savePayOfferWithRandomKey(it) }
}
)
}

val prefLnurlAuthSchemeState = userPrefs.getLnurlAuthScheme.collectAsState(initial = null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class UserPrefsRepository(private val data: DataStore<Preferences>) {
private val SWAP_ADDRESS_FORMAT = intPreferencesKey("SWAP_ADDRESS_FORMAT")
private val LNURL_AUTH_SCHEME = intPreferencesKey("LNURL_AUTH_SCHEME")
private val IS_OVERPAYMENT_ENABLED = booleanPreferencesKey("IS_OVERPAYMENT_ENABLED")
private val PAY_OFFER_WITH_RANDOM_KEY = booleanPreferencesKey("PAY_OFFER_WITH_RANDOM_KEY")
// liquidity policy & channels management
private val LIQUIDITY_POLICY = stringPreferencesKey("LIQUIDITY_POLICY")
private val INCOMING_MAX_SAT_FEE_INTERNAL_TRACKER = longPreferencesKey("INCOMING_MAX_SAT_FEE_INTERNAL_TRACKER")
Expand Down Expand Up @@ -241,6 +242,9 @@ class UserPrefsRepository(private val data: DataStore<Preferences>) {
val getIsOverpaymentEnabled: Flow<Boolean> = safeData.map { it[IS_OVERPAYMENT_ENABLED] ?: false }
suspend fun saveIsOverpaymentEnabled(enabled: Boolean) = data.edit { it[IS_OVERPAYMENT_ENABLED] = enabled }

val getPayOfferWithRandomKey: Flow<Boolean> = safeData.map { it[PAY_OFFER_WITH_RANDOM_KEY] ?: false }
suspend fun savePayOfferWithRandomKey(useRandom: Boolean) = data.edit { it[PAY_OFFER_WITH_RANDOM_KEY] = useRandom }

val getIsTorEnabled: Flow<Boolean> = safeData.map { it[IS_TOR_ENABLED] ?: false }
suspend fun saveIsTorEnabled(isEnabled: Boolean) = data.edit { it[IS_TOR_ENABLED] = isEnabled }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ fun WalletPayment.smartDescription(context: Context): String? = when (this) {
is IncomingPayment -> when (val origin = this.origin) {
is IncomingPayment.Origin.Invoice -> origin.paymentRequest.description
is IncomingPayment.Origin.SwapIn, is IncomingPayment.Origin.OnChain -> context.getString(R.string.paymentdetails_desc_swapin)
is IncomingPayment.Origin.Offer -> context.getString(R.string.paymentdetails_desc_offer_incoming)
is IncomingPayment.Origin.Offer -> null
}
is SpliceOutgoingPayment -> context.getString(R.string.paymentdetails_desc_splice_out)
is ChannelCloseOutgoingPayment -> context.getString(R.string.paymentdetails_desc_closing_channel)
Expand Down
2 changes: 1 addition & 1 deletion phoenix-android/src/main/res/values-b+es+419/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@
<string name="paymentdetails_desc_cpfp">Impulsar transacciones</string>
<string name="paymentdetails_desc_swapout">Intercambiar a %1$s</string>
<string name="paymentdetails_desc_swapin">Depósito en la cadena</string>
<string name="paymentdetails_desc_identifier">Pago a %1$s</string>
<string name="paymentdetails_desc_to">Pago a %1$s</string>

<string name="paymentdetails_edit_dialog_title">Agregar una descripción personalizada para este pago</string>
<string name="paymentdetails_edit_dialog_input_label">Descripción</string>
Expand Down
2 changes: 1 addition & 1 deletion phoenix-android/src/main/res/values-cs/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@
<string name="paymentdetails_desc_cpfp">Postrčit transakci</string>
<string name="paymentdetails_desc_swapout">Swap-out do %1$s</string>
<string name="paymentdetails_desc_swapin">On-Chain vklad</string>
<string name="paymentdetails_desc_identifier">Platba pro %1$s</string>
<string name="paymentdetails_desc_to">Platba pro %1$s</string>
<string name="paymentdetails_desc_offer_incoming">Offer</string>

<string name="paymentdetails_edit_dialog_title">Přidat vlastní popis k této platbě</string>
Expand Down
2 changes: 1 addition & 1 deletion phoenix-android/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@
<string name="paymentdetails_desc_inbound_liquidity">+%1$s eingehende Liquidität</string>
<string name="paymentdetails_desc_swapout">Swap-out an %1$s</string>
<string name="paymentdetails_desc_swapin">On-Chain Einzahlung</string>
<string name="paymentdetails_desc_identifier">Zahlung an %1$s</string>
<string name="paymentdetails_desc_to">Zahlung an %1$s</string>

<string name="paymentdetails_edit_dialog_title">Fügen Sie eine eigene Beschreibung zu dieser Zahlung hinzu</string>
<string name="paymentdetails_edit_dialog_input_label">Beschreibung</string>
Expand Down
2 changes: 1 addition & 1 deletion phoenix-android/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@
<string name="paymentdetails_desc_inbound_liquidity">+%1$s de liquidité entrante</string>
<string name="paymentdetails_desc_swapout">Swap-out vers %1$s</string>
<string name="paymentdetails_desc_swapin">Dépôt on-chain</string>
<string name="paymentdetails_desc_identifier">Paiement vers %1$s</string>
<string name="paymentdetails_desc_to">Paiement vers %1$s</string>

<string name="paymentdetails_edit_dialog_title">Ajouter une description personnalisée à ce paiement</string>
<string name="paymentdetails_edit_dialog_input_label">Description</string>
Expand Down
2 changes: 1 addition & 1 deletion phoenix-android/src/main/res/values-sk/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@
<string name="paymentdetails_desc_inbound_liquidity">+%1$s prichádzajúca likvidita</string>
<string name="paymentdetails_desc_swapout">Swap-out na %1$s</string>
<string name="paymentdetails_desc_swapin">On-chain vklad</string>
<string name="paymentdetails_desc_identifier">Platba pre %1$s</string>
<string name="paymentdetails_desc_to">Platba pre %1$s</string>

<string name="paymentdetails_edit_dialog_title">Pridať vlastný popis k tejto platbe</string>
<string name="paymentdetails_edit_dialog_input_label">Popis</string>
Expand Down
2 changes: 1 addition & 1 deletion phoenix-android/src/main/res/values-vi/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@
<string name="paymentdetails_desc_inbound_liquidity">+%1$s thanh khoản đầu vào</string>
<string name="paymentdetails_desc_swapout">Swap-out thành %1$s</string>
<string name="paymentdetails_desc_swapin">Tiền cọc on-chain</string>
<string name="paymentdetails_desc_identifier">Khoản thanh toán cho %1$s</string>
<string name="paymentdetails_desc_to">Khoản thanh toán cho %1$s</string>

<string name="paymentdetails_edit_dialog_title">Thêm mô tả tùy chọn cho khoản thanh toán này</string>
<string name="paymentdetails_edit_dialog_input_label">Mô tả</string>
Expand Down
7 changes: 6 additions & 1 deletion phoenix-android/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@
<string name="paymentdetails_offer_note_label">Message</string>
<string name="paymentdetails_offer_sender_label">Sent by</string>
<string name="paymentdetails_offer_sender_unknown">Unknown</string>
<string name="paymentdetails_offer_sender_unknown_details">Be careful with messages from unknown sources</string>

<string name="paymentdetails_lnurlpay_service">Serviced by</string>
<string name="paymentdetails_lnurlpay_action_message_label">Message</string>
Expand All @@ -392,7 +393,8 @@
<string name="paymentdetails_desc_inbound_liquidity">+%1$s inbound liquidity</string>
<string name="paymentdetails_desc_swapout">Swap-out to %1$s</string>
<string name="paymentdetails_desc_swapin">On-chain deposit</string>
<string name="paymentdetails_desc_identifier">Payment to %1$s</string>
<string name="paymentdetails_desc_to">Payment to %1$s</string>
<string name="paymentdetails_desc_from">Payment from %1$s</string>
<string name="paymentdetails_desc_offer_incoming">Offer</string>

<string name="paymentdetails_edit_dialog_title">Add a custom description to this payment</string>
Expand Down Expand Up @@ -691,6 +693,9 @@
<string name="paymentsettings_overpayment_enabled">You\'ll be able to overpay Lightning invoices up to 2 times the amount requested. Useful for manual tipping, or as a privacy measure.</string>
<string name="paymentsettings_overpayment_disabled">Disabled (default)</string>

<string name="paymentsettings_random_payer_key_title">Random payer key</string>
<string name="paymentsettings_random_payer_key_desc">Enable if you don\'t want BOLT12 recipients to know that payments come from your wallet.</string>

<!-- settings: currencies -->

<string name="currency_ars_official">Argentine Peso (official rate)</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@
package fr.acinq.phoenix.managers

import fr.acinq.bitcoin.ByteVector32
import fr.acinq.bitcoin.PrivateKey
import fr.acinq.bitcoin.PublicKey
import fr.acinq.lightning.logging.LoggerFactory
import fr.acinq.lightning.payment.OfferPaymentMetadata
import fr.acinq.lightning.utils.UUID
import fr.acinq.lightning.utils.toByteVector
import fr.acinq.lightning.wire.OfferTypes
import fr.acinq.phoenix.PhoenixBusiness
import fr.acinq.phoenix.data.ContactInfo
Expand All @@ -31,7 +28,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch

Expand Down Expand Up @@ -83,7 +79,7 @@ class ContactsManager(
return contact
}

suspend fun findContactForPayer(payerPubkey: PublicKey): ContactInfo? {
suspend fun getContactForPayerPubkey(payerPubkey: PublicKey): ContactInfo? {
return appDb.listContacts().firstOrNull { it.publicKeys.contains(payerPubkey) }
}

Expand Down
Loading

0 comments on commit a9a69bf

Please sign in to comment.