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,21 +500,30 @@ 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
if (privKey != null) {
val pubKey = CryptoUtils.pubkeyCreate(privKey).toHexKey() val pubKey = CryptoUtils.pubkeyCreate(privKey).toHexKey()
return (pubKey == pubkeyHex) return (pubKey == pubkeyHex)
} }
return (keyPair.pubKey.toHexKey() == pubkeyHex)
}
fun decryptZapPaymentResponseEvent(zapResponseEvent: LnZapPaymentResponseEvent): Response? { fun decryptZapPaymentResponseEvent(zapResponseEvent: LnZapPaymentResponseEvent): Response? {
val myNip47 = zapPaymentRequest ?: return null val myNip47 = zapPaymentRequest ?: return null
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,7 +105,6 @@ object AmberUtils {
} }
fun decryptZapEvent(event: LnZapRequestEvent) { fun decryptZapEvent(event: LnZapRequestEvent) {
if (content.isBlank()) {
isActivityRunning = true isActivityRunning = true
openAmber( openAmber(
event.toJson(), event.toJson(),
@@ -119,4 +118,3 @@ object AmberUtils {
} }
} }
} }
}
@@ -34,14 +34,43 @@ 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
if (account.loginWithAmber) {
var cached = AmberUtils.cachedDecryptedContent[pushWrappedEvent.id]
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)
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)
}
}
}
} else if (key != null) {
pushWrappedEvent.unwrap(key)?.let { notificationEvent -> pushWrappedEvent.unwrap(key)?.let { notificationEvent ->
if (!LocalCache.justVerify(notificationEvent)) return // invalid event if (!LocalCache.justVerify(notificationEvent)) return // invalid event
if (LocalCache.notes[notificationEvent.id] != null) return // already processed if (LocalCache.notes[notificationEvent.id] != null) return // already processed
@@ -59,6 +88,7 @@ class EventNotificationConsumer(private val applicationContext: Context) {
} }
} }
} }
}
private fun unwrapAndConsume(event: Event, account: Account): Event? { private fun unwrapAndConsume(event: Event, account: Account): Event? {
if (!LocalCache.justVerify(event)) return null if (!LocalCache.justVerify(event)) return null
@@ -71,6 +101,8 @@ class EventNotificationConsumer(private val applicationContext: Context) {
unwrapAndConsume(it, account) unwrapAndConsume(it, account)
} }
} else if (account.loginWithAmber) { } else if (account.loginWithAmber) {
var cached = AmberUtils.cachedDecryptedContent[event.id]
if (cached == null) {
AmberUtils.content = "" AmberUtils.content = ""
AmberUtils.decrypt( AmberUtils.decrypt(
event.content, event.content,
@@ -78,9 +110,11 @@ class EventNotificationConsumer(private val applicationContext: Context) {
event.id, event.id,
SignerType.NIP44_DECRYPT SignerType.NIP44_DECRYPT
) )
val decryptedContent = AmberUtils.cachedDecryptedContent[event.id] ?: "" cached = AmberUtils.cachedDecryptedContent[event.id] ?: ""
if (decryptedContent.isNotBlank()) { }
event.cachedGift(account.keyPair.pubKey, decryptedContent)?.let {
if (cached.isNotBlank()) {
event.cachedGift(account.keyPair.pubKey, cached)?.let {
LocalCache.justConsume(it, null) LocalCache.justConsume(it, null)
it it
} }
@@ -100,6 +134,8 @@ class EventNotificationConsumer(private val applicationContext: Context) {
it it
} }
} else if (account.loginWithAmber) { } else if (account.loginWithAmber) {
var cached = AmberUtils.cachedDecryptedContent[event.id]
if (cached == null) {
AmberUtils.content = "" AmberUtils.content = ""
AmberUtils.decrypt( AmberUtils.decrypt(
event.content, event.content,
@@ -107,9 +143,10 @@ class EventNotificationConsumer(private val applicationContext: Context) {
event.id, event.id,
SignerType.NIP44_DECRYPT SignerType.NIP44_DECRYPT
) )
val decryptedContent = AmberUtils.cachedDecryptedContent[event.id] ?: "" cached = AmberUtils.cachedDecryptedContent[event.id] ?: ""
if (decryptedContent.isNotBlank()) { }
event.cachedGossip(account.keyPair.pubKey, decryptedContent)?.let { 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>,