Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[versions]
lightningkmp = "1.11.0"
lightningkmp = "1.10.9-SNAPSHOT"
secp256k1 = "0.21.0" # keep in check with lightning-kmp secp version

kotlin = "2.2.10"
Expand Down
15 changes: 15 additions & 0 deletions phoenix-ios/phoenix-ios/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -8086,6 +8086,9 @@
}
}
}
},
"BLIP 42 (DEBUG build only)" : {

},
"Block height" : {
"localizations" : {
Expand Down Expand Up @@ -11624,6 +11627,9 @@
}
}
}
},
"contact secret" : {

},
"Contact support if needed." : {
"localizations" : {
Expand Down Expand Up @@ -29316,6 +29322,9 @@
}
}
}
},
"payer address" : {

},
"Payer key" : {
"localizations" : {
Expand Down Expand Up @@ -29356,6 +29365,9 @@
}
}
}
},
"payer offer" : {

},
"Payment" : {
"localizations" : {
Expand Down Expand Up @@ -34205,6 +34217,9 @@
}
}
}
},
"Secrets: (DEBUG build only)" : {

},
"Security" : {
"extractionState" : "manual",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,7 @@ extension Lightning_kmpPaymentRequest {
extension Lightning_kmpPeer {

var bootChannelsFlowValue: Dictionary<Bitcoin_kmpByteVector32, Lightning_kmpChannelState> {
if let value = self.bootChannelsFlow.value as? Dictionary<Bitcoin_kmpByteVector32, Lightning_kmpChannelState> {
return value
} else {
return [:]
}
return self.bootChannelsFlow.value ?? [:]
}
}

Expand Down
28 changes: 22 additions & 6 deletions phoenix-ios/phoenix-ios/kotlin/KotlinExtensions+Payments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ extension WalletPaymentInfo {
var msg: String? = nil

if let incomingOfferMetadata = payment.incomingOfferMetadata() {
msg = incomingOfferMetadata.payerNote
msg = incomingOfferMetadata.payerNote_

} else if let outgoingInvoiceRequest = payment.outgoingInvoiceRequest() {
msg = outgoingInvoiceRequest.payerNote
Expand Down Expand Up @@ -165,7 +165,24 @@ extension WalletPaymentInfo {

func addToContactsInfo() -> AddToContactsInfo? {

if payment is Lightning_kmpOutgoingPayment {
if let incoming = payment as? Lightning_kmpIncomingPayment {

if let metadata = payment.incomingOfferMetadata() {
if let rawSecret = metadata.contactSecret_ {
let offer = metadata.payerOffer_
let address = metadata.payerAddress_?.description()
if (offer != nil) || (address != nil) {
let secret = ContactSecret(
id: rawSecret,
incomingPaymentId: incoming.id,
createdAt: Date.now.toMilliseconds()
)
return AddToContactsInfo(offer: offer, address: address, secret: secret)
}
}
}

} else if payment is Lightning_kmpOutgoingPayment {

// First check for a lightning address.
// Remember that an outgoing payment might have both an address & offer (i.e. BIP-353).
Expand All @@ -177,12 +194,11 @@ extension WalletPaymentInfo {
// 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)
return AddToContactsInfo(offer: nil, address: address, secret: nil)
}

let invoiceRequest = payment.outgoingInvoiceRequest()
if let offer = invoiceRequest?.offer {
return AddToContactsInfo(offer: offer, address: nil)
if let offer = payment.outgoingInvoiceRequest()?.offer {
return AddToContactsInfo(offer: offer, address: nil, secret: nil)
}
}

Expand Down
2 changes: 2 additions & 0 deletions phoenix-ios/phoenix-ios/kotlin/KotlinTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ typealias Lightning_kmpChannelManagementFees = Lightning_kmp_coreChannelManageme
typealias Lightning_kmpChannelState = Lightning_kmp_coreChannelState
typealias Lightning_kmpConnection = Lightning_kmp_coreConnection
typealias Lightning_kmpClosing = Lightning_kmp_coreClosing
typealias Lightning_kmpContactAddress = Lightning_kmp_coreContactAddress
typealias Lightning_kmpContactSecrets = Lightning_kmp_coreContactSecrets
typealias Lightning_kmpDatabases = Lightning_kmp_coreDatabases
typealias Lightning_kmpElectrumClient = Lightning_kmp_coreElectrumClient
typealias Lightning_kmpElectrumMiniWallet = Lightning_kmp_coreElectrumMiniWallet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ import PhoenixShared
struct AddToContactsInfo: Hashable {
let offer: Lightning_kmpOfferTypesOffer?
let address: String?
let secret: ContactSecret?
}
100 changes: 96 additions & 4 deletions phoenix-ios/phoenix-ios/views/contacts/ManageContact.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ struct ManageContact: View {
@State private var editAddress_text: String = ""
@State private var editAddress_invalidReason: InvalidReason? = nil

@State private var secrets: [ContactSecret]
@State private var secrets_hasChanges: Bool

enum FooterType: Int {
case expanded_standard = 1
case expanded_squeezed = 2
Expand Down Expand Up @@ -184,7 +187,6 @@ struct ManageContact: View {
hasNewOffer = true
}
}


self._offers = State(initialValue: rows)
self._offers_hasChanges = State(initialValue: (contact != nil && hasNewOffer))
Expand Down Expand Up @@ -214,11 +216,34 @@ struct ManageContact: View {
hasNewAddress = true
}
}


self._addresses = State(initialValue: rows)
self._addresses_hasChanges = State(initialValue: (contact != nil && hasNewAddress))
}
do {
var set = Set<Bitcoin_kmpByteVector32>()
var secrets = Array<ContactSecret>()

if let contact {
for secret in contact.secrets {
if !set.contains(secret.id) {
set.insert(secret.id)
secrets.append(secret)
}
}
}
var hasNewSecret = false
if let newSecret = info?.secret {
if !set.contains(newSecret.id) {
set.insert(newSecret.id)
secrets.append(newSecret)
hasNewSecret = true
}
}

self._secrets = State(initialValue: secrets)
self._secrets_hasChanges = State(initialValue: (contact != nil && hasNewSecret))
}
}

// --------------------------------------------------
Expand Down Expand Up @@ -397,6 +422,9 @@ struct ManageContact: View {
content_trusted()
content_offers()
content_addresses()
#if DEBUG
content_secrets()
#endif
} // </VStack>
.padding()
} // </ScrollView>
Expand Down Expand Up @@ -934,6 +962,67 @@ struct ManageContact: View {
.padding(.top, ROW_VERTICAL_SPACING)
}

@ViewBuilder
func content_secrets() -> some View {

VStack(alignment: HorizontalAlignment.leading, spacing: 0) {

HStack(alignment: VerticalAlignment.center, spacing: 0) {
Text("Secrets: (DEBUG build only)")
Spacer(minLength: 0)
} // </HStack>

VStack(alignment: HorizontalAlignment.leading, spacing: 0) {
ForEach(0 ..< secrets.count, id: \.self) { idx in
content_secret_row(idx)
} // </ForEach>
} // </VStack>

if secrets.isEmpty {
content_secret_emptyRow()
}

} // </VStack>
.padding(.bottom, 30)
}

@ViewBuilder
func content_secret_row(_ index: Int) -> some View {

let row: ContactSecret = secrets[index]

HStack(alignment: VerticalAlignment.firstTextBaseline, spacing: 0) {

bullet()

VStack(alignment: HorizontalAlignment.leading, spacing: 4) {
Text(row.id.toHex())
.foregroundStyle(Color.primary)
Text(verbatim: "incomingPaymentId: \( row.incomingPaymentId?.description() ?? "<nil>" )")
.foregroundStyle(Color.secondary)
}
.lineLimit(1)
.truncationMode(.middle)
.font(.callout)

}
.padding(.top, ROW_VERTICAL_SPACING)
}

@ViewBuilder
func content_secret_emptyRow() -> some View {

HStack(alignment: VerticalAlignment.firstTextBaseline, spacing: 0) {
bullet()
Text("none")
.lineLimit(1)
.foregroundStyle(Color.secondary)
.layoutPriority(-1)
.font(.callout)
}
.padding(.top, ROW_VERTICAL_SPACING)
}

@ViewBuilder
func bullet() -> some View {

Expand Down Expand Up @@ -1290,7 +1379,7 @@ struct ManageContact: View {
if doNotUseDiskImage {
return true
}
if offers_hasChanges || addresses_hasChanges {
if offers_hasChanges || addresses_hasChanges || secrets_hasChanges {
return true
}

Expand Down Expand Up @@ -1487,9 +1576,12 @@ struct ManageContact: View {
photoUri: newPhotoName,
useOfferKey: updatedUseOfferKey,
offers: offers.map { $0.raw },
addresses: addresses.map { $0.raw }
addresses: addresses.map { $0.raw },
secrets: secrets
)

log.debug("updatedContact.secrets.count: \(updatedContact.secrets.count)")

let contactsDb = try await Biz.business.databaseManager.contactsDb()

try await contactsDb.saveContact(contact: updatedContact)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,88 @@ extension DetailsInfoGrid {
}
}

// --------------------------------------------------
// MARK: Section: BLIP 42
// --------------------------------------------------
#if DEBUG

@ViewBuilder
func section_blip42(
_ secret: Bitcoin_kmpByteVector32?,
_ offer: Lightning_kmpOfferTypesOffer?,
_ address: Lightning_kmp_coreUnverifiedContactAddress?
) -> some View {

InlineSection {
header("BLIP 42 (DEBUG build only)")
} content: {
blip42_contactSecret(secret)
blip42_offer(offer)
blip42_address(address)
}
}

@ViewBuilder
func blip42_contactSecret(
_ secret: Bitcoin_kmpByteVector32?
) -> some View {

detailsRow(
identifier: #function,
keyColumnTitle: "contact secret"
) {
if let str = secret?.toHex() {
Text(str)
.lineLimit(2)
.truncationMode(.middle)
} else {
Text(verbatim: "<null>")
.foregroundStyle(Color.secondary)
}
}
}

@ViewBuilder
func blip42_offer(
_ offer: Lightning_kmpOfferTypesOffer?
) -> some View {

detailsRow(
identifier: #function,
keyColumnTitle: "payer offer"
) {
if let str = offer?.encode() {
Text(str)
.lineLimit(2)
.truncationMode(.middle)
} else {
Text(verbatim: "<null>")
.foregroundStyle(Color.secondary)
}
}
}

@ViewBuilder
func blip42_address(
_ address: Lightning_kmp_coreUnverifiedContactAddress?
) -> some View {

detailsRow(
identifier: #function,
keyColumnTitle: "payer address"
) {
if let str = address?.description() {
Text(str)
.lineLimit(2)
.truncationMode(.middle)
} else {
Text(verbatim: "<null>")
.foregroundStyle(Color.secondary)
}
}
}

#endif
// --------------------------------------------------
// MARK: SubSection: Bolt11 Invoice
// --------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ struct Details_Incoming_Bolt12: DetailsInfoGrid {
section_timestamps()
section_incoming()
section_lightningParts(payment)
#if DEBUG
let metadata = payment.metadata
section_blip42(metadata.contactSecret_, metadata.payerOffer_, metadata.payerAddress_)
#endif
}
}
.background(Color.primaryBackground)
Expand Down
Loading