add support for private zaps

This commit is contained in:
greenart7c3
2023-09-11 15:30:00 -03:00
parent a773be12c4
commit ff4d704bde
5 changed files with 162 additions and 52 deletions
@@ -461,6 +461,21 @@ class Account(
AmberUtils.content AmberUtils.content
) )
} }
LnZapEvent.ZapType.PRIVATE -> {
val unsignedEvent = LnZapRequestEvent.createPrivateZap(
event,
userProfile().latestContactList?.relays()?.keys?.ifEmpty { null }
?: localRelays.map { it.url }.toSet(),
keyPair.pubKey.toHexKey(),
pollOption,
message
)
AmberUtils.content = ""
AmberUtils.openAmber(unsignedEvent)
if (AmberUtils.content.isBlank()) return null
return Event.fromJson(AmberUtils.content) as LnZapRequestEvent
}
else -> null else -> null
} }
} else { } else {
@@ -485,10 +500,14 @@ class Account(
fun isNIP47Author(pubkeyHex: String?): Boolean { fun isNIP47Author(pubkeyHex: String?): Boolean {
val privKey = zapPaymentRequest?.secret?.hexToByteArray() ?: keyPair.privKey val privKey = zapPaymentRequest?.secret?.hexToByteArray() ?: keyPair.privKey
if (privKey == null) return false if (privKey == null && !loginWithAmber) return false
val pubKey = CryptoUtils.pubkeyCreate(privKey).toHexKey() if (privKey != null) {
return (pubKey == pubkeyHex) val pubKey = CryptoUtils.pubkeyCreate(privKey).toHexKey()
return (pubKey == pubkeyHex)
}
return (keyPair.pubKey.toHexKey() == pubkeyHex)
} }
fun decryptZapPaymentResponseEvent(zapResponseEvent: LnZapPaymentResponseEvent): Response? { fun decryptZapPaymentResponseEvent(zapResponseEvent: LnZapPaymentResponseEvent): Response? {
@@ -497,9 +516,14 @@ class Account(
val privKey = myNip47.secret?.hexToByteArray() ?: keyPair.privKey val privKey = myNip47.secret?.hexToByteArray() ?: keyPair.privKey
val pubKey = myNip47.pubKeyHex.hexToByteArray() val pubKey = myNip47.pubKeyHex.hexToByteArray()
if (privKey == null) return null if (privKey == null && !loginWithAmber) return null
return zapResponseEvent.response(privKey, pubKey) if (privKey != null) return zapResponseEvent.response(privKey, pubKey)
Log.d("zaps", "decrypt with amber")
AmberUtils.decrypt(zapResponseEvent.content, pubKey.toHexKey(), zapResponseEvent.id)
if (AmberUtils.content.isBlank()) return null
return zapResponseEvent.response(AmberUtils.content)
} }
fun calculateIfNoteWasZappedByAccount(zappedNote: Note?): Boolean { fun calculateIfNoteWasZappedByAccount(zappedNote: Note?): Boolean {
@@ -2546,12 +2570,21 @@ class Account(
if (loginWithAmber && event is LnZapRequestEvent && event.isPrivateZap()) { if (loginWithAmber && event is LnZapRequestEvent && event.isPrivateZap()) {
val decryptedContent = AmberUtils.cachedDecryptedContent[event.id] val decryptedContent = AmberUtils.cachedDecryptedContent[event.id]
if (decryptedContent != null) { if (decryptedContent != null) {
return Event.fromJson(decryptedContent) return try {
Event.fromJson(decryptedContent)
} catch (e: Exception) {
null
}
} }
AmberUtils.decryptZapEvent(event) AmberUtils.decryptZapEvent(event)
if (AmberUtils.content.isBlank()) return null if (AmberUtils.content.isBlank()) return null
if (AmberUtils.content == "Could not decrypt the message") return null
AmberUtils.cachedDecryptedContent[event.id] = AmberUtils.content AmberUtils.cachedDecryptedContent[event.id] = AmberUtils.content
return Event.fromJson(AmberUtils.content) return try {
Event.fromJson(AmberUtils.content)
} catch (e: Exception) {
null
}
} }
return if (event is LnZapRequestEvent && loggedInPrivateKey != null && event.isPrivateZap()) { return if (event is LnZapRequestEvent && loggedInPrivateKey != null && event.isPrivateZap()) {
@@ -105,18 +105,16 @@ object AmberUtils {
} }
fun decryptZapEvent(event: LnZapRequestEvent) { fun decryptZapEvent(event: LnZapRequestEvent) {
if (content.isBlank()) { isActivityRunning = true
isActivityRunning = true openAmber(
openAmber( event.toJson(),
event.toJson(), SignerType.DECRYPT_ZAP_EVENT,
SignerType.DECRYPT_ZAP_EVENT, IntentUtils.activityResultLauncher,
IntentUtils.activityResultLauncher, event.pubKey,
event.pubKey, event.id
event.id )
) while (isActivityRunning) {
while (isActivityRunning) { // do nothing
// do nothing
}
} }
} }
} }
@@ -34,27 +34,57 @@ class EventNotificationConsumer(private val applicationContext: Context) {
// Test with all logged in accounts // Test with all logged in accounts
LocalPreferences.allSavedAccounts().forEach { LocalPreferences.allSavedAccounts().forEach {
val acc = LocalPreferences.loadFromEncryptedStorage(it.npub) val acc = LocalPreferences.loadFromEncryptedStorage(it.npub)
if (acc != null && acc.keyPair.privKey != null) { if (acc != null && (acc.keyPair.privKey != null || acc.loginWithAmber)) {
consumeIfMatchesAccount(event, acc) consumeIfMatchesAccount(event, acc)
} }
} }
} }
private suspend fun consumeIfMatchesAccount(pushWrappedEvent: GiftWrapEvent, account: Account) { private suspend fun consumeIfMatchesAccount(pushWrappedEvent: GiftWrapEvent, account: Account) {
val key = account.keyPair.privKey ?: return val key = account.keyPair.privKey
pushWrappedEvent.unwrap(key)?.let { notificationEvent -> if (account.loginWithAmber) {
if (!LocalCache.justVerify(notificationEvent)) return // invalid event var cached = AmberUtils.cachedDecryptedContent[pushWrappedEvent.id]
if (LocalCache.notes[notificationEvent.id] != null) return // already processed if (cached == null) {
AmberUtils.content = ""
AmberUtils.decrypt(
pushWrappedEvent.content,
pushWrappedEvent.pubKey,
pushWrappedEvent.id,
SignerType.NIP44_DECRYPT
)
cached = AmberUtils.cachedDecryptedContent[pushWrappedEvent.id] ?: ""
}
pushWrappedEvent.unwrap(cached)?.let { notificationEvent ->
if (!LocalCache.justVerify(notificationEvent)) return // invalid event
if (LocalCache.notes[notificationEvent.id] != null) return // already processed
LocalCache.justConsume(notificationEvent, null) LocalCache.justConsume(notificationEvent, null)
unwrapAndConsume(notificationEvent, account)?.let { innerEvent -> unwrapAndConsume(notificationEvent, account)?.let { innerEvent ->
if (innerEvent is PrivateDmEvent) { if (innerEvent is PrivateDmEvent) {
notify(innerEvent, account) notify(innerEvent, account)
} else if (innerEvent is LnZapEvent) { } else if (innerEvent is LnZapEvent) {
notify(innerEvent, account) notify(innerEvent, account)
} else if (innerEvent is ChatMessageEvent) { } else if (innerEvent is ChatMessageEvent) {
notify(innerEvent, account) notify(innerEvent, account)
}
}
}
} else if (key != null) {
pushWrappedEvent.unwrap(key)?.let { notificationEvent ->
if (!LocalCache.justVerify(notificationEvent)) return // invalid event
if (LocalCache.notes[notificationEvent.id] != null) return // already processed
LocalCache.justConsume(notificationEvent, null)
unwrapAndConsume(notificationEvent, account)?.let { innerEvent ->
if (innerEvent is PrivateDmEvent) {
notify(innerEvent, account)
} else if (innerEvent is LnZapEvent) {
notify(innerEvent, account)
} else if (innerEvent is ChatMessageEvent) {
notify(innerEvent, account)
}
} }
} }
} }
@@ -71,16 +101,20 @@ class EventNotificationConsumer(private val applicationContext: Context) {
unwrapAndConsume(it, account) unwrapAndConsume(it, account)
} }
} else if (account.loginWithAmber) { } else if (account.loginWithAmber) {
AmberUtils.content = "" var cached = AmberUtils.cachedDecryptedContent[event.id]
AmberUtils.decrypt( if (cached == null) {
event.content, AmberUtils.content = ""
event.pubKey, AmberUtils.decrypt(
event.id, event.content,
SignerType.NIP44_DECRYPT event.pubKey,
) event.id,
val decryptedContent = AmberUtils.cachedDecryptedContent[event.id] ?: "" SignerType.NIP44_DECRYPT
if (decryptedContent.isNotBlank()) { )
event.cachedGift(account.keyPair.pubKey, decryptedContent)?.let { cached = AmberUtils.cachedDecryptedContent[event.id] ?: ""
}
if (cached.isNotBlank()) {
event.cachedGift(account.keyPair.pubKey, cached)?.let {
LocalCache.justConsume(it, null) LocalCache.justConsume(it, null)
it it
} }
@@ -100,16 +134,19 @@ class EventNotificationConsumer(private val applicationContext: Context) {
it it
} }
} else if (account.loginWithAmber) { } else if (account.loginWithAmber) {
AmberUtils.content = "" var cached = AmberUtils.cachedDecryptedContent[event.id]
AmberUtils.decrypt( if (cached == null) {
event.content, AmberUtils.content = ""
event.pubKey, AmberUtils.decrypt(
event.id, event.content,
SignerType.NIP44_DECRYPT event.pubKey,
) event.id,
val decryptedContent = AmberUtils.cachedDecryptedContent[event.id] ?: "" SignerType.NIP44_DECRYPT
if (decryptedContent.isNotBlank()) { )
event.cachedGossip(account.keyPair.pubKey, decryptedContent)?.let { cached = AmberUtils.cachedDecryptedContent[event.id] ?: ""
}
if (cached.isNotBlank()) {
event.cachedGossip(account.keyPair.pubKey, cached)?.let {
LocalCache.justConsume(it, null) LocalCache.justConsume(it, null)
it it
} }
@@ -61,6 +61,22 @@ class LnZapPaymentResponseEvent(
} }
} }
fun response(decryptedContent: String): Response? {
if (response != null) response
return try {
if (content.isNotEmpty()) {
response = mapper.readValue(decryptedContent, Response::class.java)
response
} else {
null
}
} catch (e: Exception) {
Log.w("LnZapPaymentResponseEvent", "Can't parse content as a payment response: $content", e)
null
}
}
companion object { companion object {
const val kind = 23195 const val kind = 23195
} }
@@ -123,6 +123,32 @@ class LnZapRequestEvent(
return LnZapRequestEvent(id.toHexKey(), pubKey, createdAt, tags, message, "") return LnZapRequestEvent(id.toHexKey(), pubKey, createdAt, tags, message, "")
} }
fun createPrivateZap(
originalNote: EventInterface,
relays: Set<String>,
pubKey: HexKey,
pollOption: Int?,
message: String,
createdAt: Long = TimeUtils.now()
): LnZapRequestEvent {
val content = message
var tags = listOf(
listOf("e", originalNote.id()),
listOf("p", originalNote.pubKey()),
listOf("relays") + relays
)
if (originalNote is AddressableEvent) {
tags = tags + listOf(listOf("a", originalNote.address().toTag()))
}
if (pollOption != null && pollOption >= 0) {
tags = tags + listOf(listOf(POLL_OPTION, pollOption.toString()))
}
tags = tags + listOf(listOf("anon", ""))
return LnZapRequestEvent("", pubKey, createdAt, tags, content, "")
}
fun createAnonymous( fun createAnonymous(
originalNote: EventInterface, originalNote: EventInterface,
relays: Set<String>, relays: Set<String>,