Massive refactoring to unify internal signer and the Amber signer.

This commit is contained in:
Vitor Pamplona
2023-11-19 17:28:17 -05:00
parent 69941002df
commit df9b764c1d
130 changed files with 3890 additions and 5046 deletions
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class AdvertisedRelayListEvent(
@@ -40,9 +41,10 @@ class AdvertisedRelayListEvent(
fun create(
list: List<AdvertisedRelayInfo>,
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): AdvertisedRelayListEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (AdvertisedRelayListEvent) -> Unit
) {
val tags = list.map {
if (it.type == AdvertisedRelayType.BOTH) {
listOf(it.relayUrl)
@@ -51,10 +53,8 @@ class AdvertisedRelayListEvent(
}
}
val msg = ""
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, msg)
val sig = CryptoUtils.sign(id, privateKey)
return AdvertisedRelayListEvent(id.toHexKey(), pubKey, createdAt, tags, msg, sig.toHexKey())
signer.sign(createdAt, kind, tags, msg, onReady)
}
}
@@ -4,6 +4,8 @@ import android.util.Log
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils
import java.io.ByteArrayInputStream
@Immutable
@@ -46,5 +48,14 @@ class AppDefinitionEvent(
companion object {
const val kind = 31990
fun create(
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (AppDefinitionEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
signer.sign(createdAt, kind, tags, "", onReady)
}
}
}
@@ -3,6 +3,8 @@ package com.vitorpamplona.quartz.events
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils
@Immutable
class AppRecommendationEvent(
@@ -19,5 +21,14 @@ class AppRecommendationEvent(
companion object {
const val kind = 31989
fun create(
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (AppRecommendationEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
signer.sign(createdAt, kind, tags, "", onReady)
}
}
}
@@ -3,9 +3,8 @@ package com.vitorpamplona.quartz.events
import androidx.compose.runtime.Immutable
import com.fasterxml.jackson.module.kotlin.readValue
import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class AudioHeaderEvent(
@@ -30,15 +29,16 @@ class AudioHeaderEvent(
private const val STREAM_URL = "stream_url"
private const val WAVEFORM = "waveform"
fun create(
suspend fun create(
description: String,
downloadUrl: String,
streamUrl: String? = null,
wavefront: String? = null,
sensitiveContent: Boolean? = null,
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): AudioHeaderEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (AudioHeaderEvent) -> Unit
) {
val tags = listOfNotNull(
downloadUrl.let { listOf(DOWNLOAD_URL, it) },
streamUrl?.let { listOf(STREAM_URL, it) },
@@ -52,11 +52,7 @@ class AudioHeaderEvent(
}
)
val content = description
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey)
return AudioHeaderEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
signer.sign(createdAt, kind, tags, description, onReady)
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class AudioTrackEvent(
@@ -40,9 +41,10 @@ class AudioTrackEvent(
price: String? = null,
cover: String? = null,
subject: String? = null,
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): AudioTrackEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (AudioTrackEvent) -> Unit
) {
val tags = listOfNotNull(
listOf(MEDIA, media),
listOf(TYPE, type),
@@ -51,10 +53,7 @@ class AudioTrackEvent(
subject?.let { listOf(SUBJECT, it) }
)
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, "")
val sig = CryptoUtils.sign(id, privateKey)
return AudioTrackEvent(id.toHexKey(), pubKey, createdAt, tags, "", sig.toHexKey())
signer.sign(createdAt, kind, tags, "", onReady)
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class BookmarkListEvent(
@@ -16,36 +17,156 @@ class BookmarkListEvent(
content: String,
sig: HexKey
) : GeneralListEvent(id, pubKey, createdAt, kind, tags, content, sig) {
@Transient
var decryptedContent = ""
companion object {
const val kind = 30001
fun addEvent(
earlierVersion: BookmarkListEvent?,
eventId: HexKey,
isPrivate: Boolean,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (BookmarkListEvent) -> Unit
) = addTag(earlierVersion, "e", eventId, isPrivate, signer, createdAt, onReady)
fun addReplaceable(
earlierVersion: BookmarkListEvent?,
aTag: ATag,
isPrivate: Boolean,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (BookmarkListEvent) -> Unit
) = addTag(earlierVersion, "a", aTag.toTag(), isPrivate, signer, createdAt, onReady)
fun addTag(
earlierVersion: BookmarkListEvent?,
tagName: String,
tagValue: HexKey,
isPrivate: Boolean,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (BookmarkListEvent) -> Unit
) {
add(
earlierVersion,
listOf(listOf(tagName, tagValue)),
isPrivate,
signer,
createdAt,
onReady
)
}
fun add(
earlierVersion: BookmarkListEvent?,
listNewTags: List<List<String>>,
isPrivate: Boolean,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (BookmarkListEvent) -> Unit
) {
if (isPrivate) {
if (earlierVersion != null) {
earlierVersion.privateTagsOrEmpty(signer) { privateTags ->
encryptTags(
privateTags = privateTags.plus(listNewTags),
signer = signer
) { encryptedTags ->
create(
content = encryptedTags,
tags = earlierVersion.tags,
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
} else {
encryptTags(
privateTags = listNewTags,
signer = signer
) { encryptedTags ->
create(
content = encryptedTags,
tags = emptyList(),
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
} else {
create(
content = earlierVersion?.content ?: "",
tags = (earlierVersion?.tags ?: emptyList()).plus(listNewTags),
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
fun removeEvent(
earlierVersion: BookmarkListEvent,
eventId: HexKey,
isPrivate: Boolean,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (BookmarkListEvent) -> Unit
) = removeTag(earlierVersion, "e", eventId, isPrivate, signer, createdAt, onReady)
fun removeReplaceable(
earlierVersion: BookmarkListEvent,
aTag: ATag,
isPrivate: Boolean,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (BookmarkListEvent) -> Unit
) = removeTag(earlierVersion, "a", aTag.toTag(), isPrivate, signer, createdAt, onReady)
private fun removeTag(
earlierVersion: BookmarkListEvent,
tagName: String,
tagValue: HexKey,
isPrivate: Boolean,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (BookmarkListEvent) -> Unit
) {
if (isPrivate) {
earlierVersion.privateTagsOrEmpty(signer) { privateTags ->
encryptTags(
privateTags = privateTags.filter { it.size <= 1 || !(it[0] == tagName && it[1] == tagValue) },
signer = signer
) { encryptedTags ->
create(
content = encryptedTags,
tags = earlierVersion.tags.filter { it.size <= 1 || !(it[0] == tagName && it[1] == tagValue) },
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
} else {
create(
content = earlierVersion.content,
tags = earlierVersion.tags.filter { it.size <= 1 || !(it[0] == tagName && it[1] == tagValue) },
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
fun create(
name: String = "",
events: List<String>? = null,
users: List<String>? = null,
addresses: List<ATag>? = null,
content: String,
pubKey: HexKey,
createdAt: Long = TimeUtils.now()
): BookmarkListEvent {
val tags = mutableListOf<List<String>>()
tags.add(listOf("d", name))
events?.forEach {
tags.add(listOf("e", it))
}
users?.forEach {
tags.add(listOf("p", it))
}
addresses?.forEach {
tags.add(listOf("a", it.toTag()))
}
val id = generateId(pubKey, createdAt, kind, tags, content)
return BookmarkListEvent(id.toHexKey(), pubKey, createdAt, tags, content, "")
tags: List<List<String>>,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (BookmarkListEvent) -> Unit
) {
signer.sign(createdAt, kind, tags, content, onReady)
}
fun create(
@@ -59,12 +180,10 @@ class BookmarkListEvent(
privUsers: List<String>? = null,
privAddresses: List<ATag>? = null,
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): BookmarkListEvent {
val pubKey = CryptoUtils.pubkeyCreate(privateKey)
val content = createPrivateTags(privEvents, privUsers, privAddresses, privateKey, pubKey)
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (BookmarkListEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
tags.add(listOf("d", name))
@@ -78,15 +197,9 @@ class BookmarkListEvent(
tags.add(listOf("a", it.toTag()))
}
val id = generateId(pubKey.toHexKey(), createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey)
return BookmarkListEvent(id.toHexKey(), pubKey.toHexKey(), createdAt, tags, content, sig.toHexKey())
}
fun create(
unsignedEvent: BookmarkListEvent, signature: String
): BookmarkListEvent {
return BookmarkListEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
createPrivateTags(privEvents, privUsers, privAddresses, signer) { content ->
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class CalendarDateSlotEvent(
@@ -28,14 +29,12 @@ class CalendarDateSlotEvent(
const val kind = 31922
fun create(
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): CalendarDateSlotEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (CalendarDateSlotEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, "")
val sig = CryptoUtils.sign(id, privateKey)
return CalendarDateSlotEvent(id.toHexKey(), pubKey, createdAt, tags, "", sig.toHexKey())
signer.sign(createdAt, kind, tags, "", onReady)
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class CalendarEvent(
@@ -20,14 +21,12 @@ class CalendarEvent(
const val kind = 31924
fun create(
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): CalendarEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (CalendarEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, "")
val sig = CryptoUtils.sign(id, privateKey)
return CalendarEvent(id.toHexKey(), pubKey, createdAt, tags, "", sig.toHexKey())
signer.sign(createdAt, kind, tags, "", onReady)
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class CalendarRSVPEvent(
@@ -30,14 +31,12 @@ class CalendarRSVPEvent(
const val kind = 31925
fun create(
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): CalendarRSVPEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (CalendarRSVPEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, "")
val sig = CryptoUtils.sign(id, privateKey)
return CalendarRSVPEvent(id.toHexKey(), pubKey, createdAt, tags, "", sig.toHexKey())
signer.sign(createdAt, kind, tags, "", onReady)
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class CalendarTimeSlotEvent(
@@ -32,14 +33,12 @@ class CalendarTimeSlotEvent(
const val kind = 31923
fun create(
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): CalendarTimeSlotEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (CalendarTimeSlotEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, "")
val sig = CryptoUtils.sign(id, privateKey)
return CalendarTimeSlotEvent(id.toHexKey(), pubKey, createdAt, tags, "", sig.toHexKey())
signer.sign(createdAt, kind, tags, "", onReady)
}
}
}
@@ -9,6 +9,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class ChannelCreateEvent(
@@ -29,7 +30,30 @@ class ChannelCreateEvent(
companion object {
const val kind = 40
fun create(channelInfo: ChannelData?, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ChannelCreateEvent {
fun create(
name: String?,
about: String?,
picture: String?,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (ChannelCreateEvent) -> Unit
) {
return create(
ChannelData(
name, about, picture
),
signer,
createdAt,
onReady
)
}
fun create(
channelInfo: ChannelData?,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (ChannelCreateEvent) -> Unit
) {
val content = try {
if (channelInfo != null) {
mapper.writeValueAsString(channelInfo)
@@ -41,15 +65,7 @@ class ChannelCreateEvent(
""
}
val pubKey = keyPair.pubKey.toHexKey()
val tags = emptyList<List<String>>()
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.pubKey)
return ChannelCreateEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
}
fun create(unsignedEvent: ChannelCreateEvent, signature: String): ChannelCreateEvent {
return ChannelCreateEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
signer.sign(createdAt, kind, emptyList(), content, onReady)
}
}
@@ -5,6 +5,7 @@ import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class ChannelHideMessageEvent(
@@ -26,16 +27,19 @@ class ChannelHideMessageEvent(
companion object {
const val kind = 43
fun create(reason: String, messagesToHide: List<String>?, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): ChannelHideMessageEvent {
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
fun create(
reason: String,
messagesToHide: List<String>?,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (ChannelHideMessageEvent) -> Unit
) {
val tags =
messagesToHide?.map {
listOf("e", it)
} ?: emptyList()
val id = generateId(pubKey, createdAt, kind, tags, reason)
val sig = CryptoUtils.sign(id, privateKey)
return ChannelHideMessageEvent(id.toHexKey(), pubKey, createdAt, tags, reason, sig.toHexKey())
signer.sign(createdAt, kind, tags, reason, onReady)
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class ChannelMessageEvent(
@@ -34,12 +35,13 @@ class ChannelMessageEvent(
replyTos: List<String>? = null,
mentions: List<String>? = null,
zapReceiver: List<ZapSplitSetup>? = null,
keyPair: KeyPair,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
markAsSensitive: Boolean,
zapRaiserAmount: Long?,
geohash: String? = null
): ChannelMessageEvent {
geohash: String? = null,
onReady: (ChannelMessageEvent) -> Unit
) {
val content = message
val tags = mutableListOf(
listOf("e", channel, "", "root")
@@ -63,16 +65,7 @@ class ChannelMessageEvent(
tags.add(listOf("g", it))
}
val pubKey = keyPair.pubKey.toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return ChannelMessageEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
}
fun create(
unsignedEvent: ChannelMessageEvent, signature: String
): ChannelMessageEvent {
return ChannelMessageEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -7,6 +7,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class ChannelMetadataEvent(
@@ -30,7 +31,33 @@ class ChannelMetadataEvent(
companion object {
const val kind = 41
fun create(newChannelInfo: ChannelCreateEvent.ChannelData?, originalChannelIdHex: String, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ChannelMetadataEvent {
fun create(
name: String?,
about: String?,
picture: String?,
originalChannelIdHex: String,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (ChannelMetadataEvent) -> Unit
) {
create(
ChannelCreateEvent.ChannelData(
name, about, picture
),
originalChannelIdHex,
signer,
createdAt,
onReady
)
}
fun create(
newChannelInfo: ChannelCreateEvent.ChannelData?,
originalChannelIdHex: String,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (ChannelMetadataEvent) -> Unit
) {
val content =
if (newChannelInfo != null) {
mapper.writeValueAsString(newChannelInfo)
@@ -38,15 +65,8 @@ class ChannelMetadataEvent(
""
}
val pubKey = keyPair.pubKey.toHexKey()
val tags = listOf(listOf("e", originalChannelIdHex, "", "root"))
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return ChannelMetadataEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
}
fun create(unsignedEvent: ChannelMetadataEvent, signature: String): ChannelMetadataEvent {
return ChannelMetadataEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -5,6 +5,7 @@ import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class ChannelMuteUserEvent(
@@ -26,17 +27,19 @@ class ChannelMuteUserEvent(
companion object {
const val kind = 44
fun create(reason: String, usersToMute: List<String>?, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): ChannelMuteUserEvent {
fun create(
reason: String,
usersToMute: List<String>?,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (ChannelMuteUserEvent) -> Unit
) {
val content = reason
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val tags =
usersToMute?.map {
listOf("p", it)
} ?: emptyList()
val tags = usersToMute?.map {
listOf("p", it)
} ?: emptyList()
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey)
return ChannelMuteUserEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -3,10 +3,8 @@ package com.vitorpamplona.quartz.events
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.toImmutableSet
@@ -62,10 +60,10 @@ class ChatMessageEvent(
markAsSensitive: Boolean = false,
zapRaiserAmount: Long? = null,
geohash: String? = null,
keyPair: KeyPair,
createdAt: Long = TimeUtils.now()
): ChatMessageEvent {
val content = msg
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (ChatMessageEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
to?.forEach {
tags.add(listOf("p", it))
@@ -92,17 +90,7 @@ class ChatMessageEvent(
tags.add(listOf("subject", it))
}
val pubKey = keyPair.pubKey.toHexKey()
val id = generateId(pubKey, createdAt, ClassifiedsEvent.kind, tags, content)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return ChatMessageEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
}
fun create(
unsignedEvent: ChatMessageEvent,
signature: String
): ChatMessageEvent {
return ChatMessageEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
signer.sign(createdAt, kind, tags, msg, onReady)
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class ClassifiedsEvent(
@@ -41,9 +42,10 @@ class ClassifiedsEvent(
price: Price?,
location: String?,
publishedAt: Long?,
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): ClassifiedsEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (ClassifiedsEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
tags.add(listOf("d", dTag))
@@ -63,10 +65,7 @@ class ClassifiedsEvent(
publishedAt?.let { tags.add(listOf("publishedAt", it.toString())) }
title?.let { tags.add(listOf("title", it)) }
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, "")
val sig = CryptoUtils.sign(id, privateKey)
return ClassifiedsEvent(id.toHexKey(), pubKey, createdAt, tags, "", sig.toHexKey())
signer.sign(createdAt, kind, tags, "", onReady)
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class CommunityDefinitionEvent(
@@ -26,14 +27,12 @@ class CommunityDefinitionEvent(
const val kind = 34550
fun create(
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): CommunityDefinitionEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (CommunityDefinitionEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, "")
val sig = CryptoUtils.sign(id, privateKey)
return CommunityDefinitionEvent(id.toHexKey(), pubKey, createdAt, tags, "", sig.toHexKey())
signer.sign(createdAt, kind, tags, "", onReady)
}
}
}
@@ -7,6 +7,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class CommunityPostApprovalEvent(
@@ -45,19 +46,23 @@ class CommunityPostApprovalEvent(
companion object {
const val kind = 4550
fun create(approvedPost: Event, community: CommunityDefinitionEvent, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): GenericRepostEvent {
fun create(
approvedPost: Event,
community: CommunityDefinitionEvent,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (CommunityPostApprovalEvent) -> Unit
) {
val content = approvedPost.toJson()
val communities = listOf("a", community.address().toTag())
val replyToPost = listOf("e", approvedPost.id())
val replyToAuthor = listOf("p", approvedPost.pubKey())
val kind = listOf("k", "${approvedPost.kind()}")
val innerKind = listOf("k", "${approvedPost.kind()}")
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val tags: List<List<String>> = listOf(communities, replyToPost, replyToAuthor, kind)
val id = generateId(pubKey, createdAt, GenericRepostEvent.kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey)
return GenericRepostEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
val tags: List<List<String>> = listOf(communities, replyToPost, replyToAuthor, innerKind)
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -11,6 +11,7 @@ import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.decodePublicKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
data class Contact(val pubKeyHex: String, val relayUri: String?)
@@ -99,9 +100,10 @@ class ContactListEvent(
followCommunities: List<ATag>,
followEvents: List<String>,
relayUse: Map<String, ReadWrite>?,
keyPair: KeyPair,
createdAt: Long = TimeUtils.now()
): ContactListEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (ContactListEvent) -> Unit
) {
val content = if (relayUse != null) {
mapper.writeValueAsString(relayUse)
} else {
@@ -131,122 +133,133 @@ class ContactListEvent(
return create(
content = content,
tags = tags,
keyPair = keyPair,
createdAt = createdAt
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
fun followUser(earlierVersion: ContactListEvent, pubKeyHex: String, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ContactListEvent {
if (earlierVersion.isTaggedUser(pubKeyHex)) return earlierVersion
fun followUser(earlierVersion: ContactListEvent, pubKeyHex: String, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (ContactListEvent) -> Unit) {
if (earlierVersion.isTaggedUser(pubKeyHex)) return
return create(
content = earlierVersion.content,
tags = earlierVersion.tags.plus(element = listOf("p", pubKeyHex)),
keyPair = keyPair,
createdAt = createdAt
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
fun unfollowUser(earlierVersion: ContactListEvent, pubKeyHex: String, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ContactListEvent {
if (!earlierVersion.isTaggedUser(pubKeyHex)) return earlierVersion
fun unfollowUser(earlierVersion: ContactListEvent, pubKeyHex: String, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (ContactListEvent) -> Unit) {
if (!earlierVersion.isTaggedUser(pubKeyHex)) return
return create(
content = earlierVersion.content,
tags = earlierVersion.tags.filter { it.size > 1 && it[1] != pubKeyHex },
keyPair = keyPair,
createdAt = createdAt
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
fun followHashtag(earlierVersion: ContactListEvent, hashtag: String, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ContactListEvent {
if (earlierVersion.isTaggedHash(hashtag)) return earlierVersion
fun followHashtag(earlierVersion: ContactListEvent, hashtag: String, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (ContactListEvent) -> Unit) {
if (earlierVersion.isTaggedHash(hashtag)) return
return create(
content = earlierVersion.content,
tags = earlierVersion.tags.plus(element = listOf("t", hashtag)),
keyPair = keyPair,
createdAt = createdAt
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
fun unfollowHashtag(earlierVersion: ContactListEvent, hashtag: String, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ContactListEvent {
if (!earlierVersion.isTaggedHash(hashtag)) return earlierVersion
fun unfollowHashtag(earlierVersion: ContactListEvent, hashtag: String, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (ContactListEvent) -> Unit) {
if (!earlierVersion.isTaggedHash(hashtag)) return
return create(
content = earlierVersion.content,
tags = earlierVersion.tags.filter { it.size > 1 && !it[1].equals(hashtag, true) },
keyPair = keyPair,
createdAt = createdAt
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
fun followGeohash(earlierVersion: ContactListEvent, hashtag: String, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ContactListEvent {
if (earlierVersion.isTaggedGeoHash(hashtag)) return earlierVersion
fun followGeohash(earlierVersion: ContactListEvent, hashtag: String, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (ContactListEvent) -> Unit) {
if (earlierVersion.isTaggedGeoHash(hashtag)) return
return create(
content = earlierVersion.content,
tags = earlierVersion.tags.plus(element = listOf("g", hashtag)),
keyPair = keyPair,
createdAt = createdAt
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
fun unfollowGeohash(earlierVersion: ContactListEvent, hashtag: String, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ContactListEvent {
if (!earlierVersion.isTaggedGeoHash(hashtag)) return earlierVersion
fun unfollowGeohash(earlierVersion: ContactListEvent, hashtag: String, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (ContactListEvent) -> Unit) {
if (!earlierVersion.isTaggedGeoHash(hashtag)) return
return create(
content = earlierVersion.content,
tags = earlierVersion.tags.filter { it.size > 1 && it[1] != hashtag },
keyPair = keyPair,
createdAt = createdAt
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
fun followEvent(earlierVersion: ContactListEvent, idHex: String, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ContactListEvent {
if (earlierVersion.isTaggedEvent(idHex)) return earlierVersion
fun followEvent(earlierVersion: ContactListEvent, idHex: String, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (ContactListEvent) -> Unit) {
if (earlierVersion.isTaggedEvent(idHex)) return
return create(
content = earlierVersion.content,
tags = earlierVersion.tags.plus(element = listOf("e", idHex)),
keyPair = keyPair,
createdAt = createdAt
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
fun unfollowEvent(earlierVersion: ContactListEvent, idHex: String, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ContactListEvent {
if (!earlierVersion.isTaggedEvent(idHex)) return earlierVersion
fun unfollowEvent(earlierVersion: ContactListEvent, idHex: String, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (ContactListEvent) -> Unit) {
if (!earlierVersion.isTaggedEvent(idHex)) return
return create(
content = earlierVersion.content,
tags = earlierVersion.tags.filter { it.size > 1 && it[1] != idHex },
keyPair = keyPair,
createdAt = createdAt
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
fun followAddressableEvent(earlierVersion: ContactListEvent, aTag: ATag, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ContactListEvent {
if (earlierVersion.isTaggedAddressableNote(aTag.toTag())) return earlierVersion
fun followAddressableEvent(earlierVersion: ContactListEvent, aTag: ATag, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (ContactListEvent) -> Unit) {
if (earlierVersion.isTaggedAddressableNote(aTag.toTag())) return
return create(
content = earlierVersion.content,
tags = earlierVersion.tags.plus(element = listOfNotNull("a", aTag.toTag(), aTag.relay)),
keyPair = keyPair,
createdAt = createdAt
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
fun unfollowAddressableEvent(earlierVersion: ContactListEvent, aTag: ATag, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ContactListEvent {
if (!earlierVersion.isTaggedAddressableNote(aTag.toTag())) return earlierVersion
fun unfollowAddressableEvent(earlierVersion: ContactListEvent, aTag: ATag, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (ContactListEvent) -> Unit) {
if (!earlierVersion.isTaggedAddressableNote(aTag.toTag())) return
return create(
content = earlierVersion.content,
tags = earlierVersion.tags.filter { it.size > 1 && it[1] != aTag.toTag() },
keyPair = keyPair,
createdAt = createdAt
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
fun updateRelayList(earlierVersion: ContactListEvent, relayUse: Map<String, ReadWrite>?, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ContactListEvent {
fun updateRelayList(earlierVersion: ContactListEvent, relayUse: Map<String, ReadWrite>?, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (ContactListEvent) -> Unit) {
val content = if (relayUse != null) {
mapper.writeValueAsString(relayUse)
} else {
@@ -256,35 +269,20 @@ class ContactListEvent(
return create(
content = content,
tags = earlierVersion.tags,
keyPair = keyPair,
createdAt = createdAt
)
}
fun create(
unsignedEvent: Event,
signature: String
): ContactListEvent {
return ContactListEvent(
unsignedEvent.id,
unsignedEvent.pubKey,
unsignedEvent.createdAt,
unsignedEvent.tags,
unsignedEvent.content,
signature
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
fun create(
content: String,
tags: List<List<String>>,
keyPair: KeyPair,
createdAt: Long = TimeUtils.now()
): ContactListEvent {
val pubKey = keyPair.pubKey.toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return ContactListEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (ContactListEvent) -> Unit
) {
signer.sign(createdAt, kind, tags, content, onReady)
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class DeletionEvent(
@@ -21,17 +22,10 @@ class DeletionEvent(
companion object {
const val kind = 5
fun create(deleteEvents: List<String>, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): DeletionEvent {
fun create(deleteEvents: List<String>, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (DeletionEvent) -> Unit) {
val content = ""
val pubKey = keyPair.pubKey.toHexKey()
val tags = deleteEvents.map { listOf("e", it) }
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return DeletionEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
}
fun create(unsignedEvent: DeletionEvent, signature: String): DeletionEvent {
return DeletionEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -5,6 +5,7 @@ import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class EmojiPackEvent(
@@ -21,18 +22,16 @@ class EmojiPackEvent(
fun create(
name: String = "",
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): EmojiPackEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (EmojiPackEvent) -> Unit
) {
val content = ""
val pubKey = CryptoUtils.pubkeyCreate(privateKey)
val tags = mutableListOf<List<String>>()
tags.add(listOf("d", name))
val id = generateId(pubKey.toHexKey(), createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey)
return EmojiPackEvent(id.toHexKey(), pubKey.toHexKey(), createdAt, tags, content, sig.toHexKey())
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -7,6 +7,7 @@ import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class EmojiPackSelectionEvent(
@@ -22,26 +23,18 @@ class EmojiPackSelectionEvent(
fun create(
listOfEmojiPacks: List<ATag>?,
keyPair: KeyPair,
createdAt: Long = TimeUtils.now()
): EmojiPackSelectionEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (EmojiPackSelectionEvent) -> Unit
) {
val msg = ""
val pubKey = keyPair.pubKey.toHexKey()
val tags = mutableListOf<List<String>>()
listOfEmojiPacks?.forEach {
tags.add(listOf("a", it.toTag()))
}
val id = generateId(pubKey, createdAt, kind, tags, msg)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return EmojiPackSelectionEvent(id.toHexKey(), pubKey, createdAt, tags, msg, sig?.toHexKey() ?: "")
}
fun create(
unsignedEvent: EmojiPackSelectionEvent, signature: String
): EmojiPackSelectionEvent {
return EmojiPackSelectionEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
signer.sign(createdAt, kind, tags, msg, onReady)
}
}
}
@@ -19,6 +19,7 @@ import com.vitorpamplona.quartz.encoders.Hex
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.Nip19
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils
import java.math.BigDecimal
import java.util.*
@@ -403,11 +404,8 @@ open class Event(
return CryptoUtils.sha256(makeJsonForId(pubKey, createdAt, kind, tags, content).toByteArray())
}
fun create(privateKey: ByteArray, kind: Int, tags: List<List<String>> = emptyList(), content: String = "", createdAt: Long = TimeUtils.now()): Event {
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val id = Companion.generateId(pubKey, createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey).toHexKey()
return Event(id.toHexKey(), pubKey, createdAt, kind, tags, content, sig)
fun create(signer: NostrSigner, kind: Int, tags: List<List<String>> = emptyList(), content: String = "", createdAt: Long = TimeUtils.now(), onReady: (Event) -> Unit) {
return signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -4,6 +4,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
class EventFactory {
companion object {
fun create(
id: String,
pubKey: String,
@@ -55,6 +56,7 @@ class EventFactory {
GenericRepostEvent.kind -> GenericRepostEvent(id, pubKey, createdAt, tags, content, sig)
GiftWrapEvent.kind -> GiftWrapEvent(id, pubKey, createdAt, tags, content, sig)
HighlightEvent.kind -> HighlightEvent(id, pubKey, createdAt, tags, content, sig)
HTTPAuthorizationEvent.kind -> HTTPAuthorizationEvent(id, pubKey, createdAt, tags, content, sig)
LiveActivitiesEvent.kind -> LiveActivitiesEvent(id, pubKey, createdAt, tags, content, sig)
LiveActivitiesChatMessageEvent.kind -> LiveActivitiesChatMessageEvent(id, pubKey, createdAt, tags, content, sig)
LnZapEvent.kind -> LnZapEvent(id, pubKey, createdAt, tags, content, sig)
@@ -71,6 +73,7 @@ class EventFactory {
PrivateDmEvent.kind -> PrivateDmEvent(id, pubKey, createdAt, tags, content, sig)
ReactionEvent.kind -> ReactionEvent(id, pubKey, createdAt, tags, content, sig)
RecommendRelayEvent.kind -> RecommendRelayEvent(id, pubKey, createdAt, tags, content, sig)
RelayAuthEvent.kind -> RelayAuthEvent(id, pubKey, createdAt, tags, content, sig)
RelaySetEvent.kind -> RelaySetEvent(id, pubKey, createdAt, tags, content, sig)
ReportEvent.kind -> ReportEvent(id, pubKey, createdAt, tags, content, sig)
RepostEvent.kind -> RepostEvent(id, pubKey, createdAt, tags, content, sig)
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class FileHeaderEvent(
@@ -56,9 +57,10 @@ class FileHeaderEvent(
torrentInfoHash: String? = null,
encryptionKey: AESGCM? = null,
sensitiveContent: Boolean? = null,
keyPair: KeyPair,
createdAt: Long = TimeUtils.now()
): FileHeaderEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (FileHeaderEvent) -> Unit
) {
val tags = listOfNotNull(
listOf(URL, url),
mimeType?.let { listOf(MIME_TYPE, mimeType) },
@@ -81,10 +83,7 @@ class FileHeaderEvent(
)
val content = alt ?: ""
val pubKey = keyPair.pubKey.toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return FileHeaderEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
import java.util.Base64
@Immutable
@@ -43,33 +44,16 @@ class FileStorageEvent(
fun create(
mimeType: String,
data: ByteArray,
pubKey: HexKey,
createdAt: Long = TimeUtils.now()
): FileStorageEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (FileStorageEvent) -> Unit
) {
val tags = listOfNotNull(
listOf(TYPE, mimeType)
)
val content = encode(data)
val id = generateId(pubKey, createdAt, kind, tags, content)
return FileStorageEvent(id.toHexKey(), pubKey, createdAt, tags, content, "")
}
fun create(
mimeType: String,
data: ByteArray,
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): FileStorageEvent {
val tags = listOfNotNull(
listOf(TYPE, mimeType)
)
val content = encode(data)
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey)
return FileStorageEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -5,6 +5,7 @@ import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class FileStorageHeaderEvent(
@@ -53,49 +54,10 @@ class FileStorageHeaderEvent(
torrentInfoHash: String? = null,
encryptionKey: AESGCM? = null,
sensitiveContent: Boolean? = null,
pubKey: HexKey,
createdAt: Long = TimeUtils.now()
): FileStorageHeaderEvent {
val tags = listOfNotNull(
listOf("e", storageEvent.id),
mimeType?.let { listOf(MIME_TYPE, mimeType) },
alt?.ifBlank { null }?.let { listOf(ALT, it) },
hash?.let { listOf(HASH, it) },
size?.let { listOf(FILE_SIZE, it) },
dimensions?.let { listOf(DIMENSION, it) },
blurhash?.let { listOf(BLUR_HASH, it) },
magnetURI?.let { listOf(MAGNET_URI, it) },
torrentInfoHash?.let { listOf(TORRENT_INFOHASH, it) },
encryptionKey?.let { listOf(ENCRYPTION_KEY, it.key, it.nonce) },
sensitiveContent?.let {
if (it) {
listOf("content-warning", "")
} else {
null
}
}
)
val content = alt ?: ""
val id = generateId(pubKey, createdAt, kind, tags, content)
return FileStorageHeaderEvent(id.toHexKey(), pubKey, createdAt, tags, content, "")
}
fun create(
storageEvent: FileStorageEvent,
mimeType: String? = null,
alt: String? = null,
hash: String? = null,
size: String? = null,
dimensions: String? = null,
blurhash: String? = null,
magnetURI: String? = null,
torrentInfoHash: String? = null,
encryptionKey: AESGCM? = null,
sensitiveContent: Boolean? = null,
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): FileStorageHeaderEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (FileStorageHeaderEvent) -> Unit
) {
val tags = listOfNotNull(
listOf("e", storageEvent.id),
mimeType?.let { listOf(MIME_TYPE, mimeType) },
@@ -117,10 +79,7 @@ class FileStorageHeaderEvent(
)
val content = alt ?: ""
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey)
return FileStorageHeaderEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -7,6 +7,7 @@ import com.vitorpamplona.quartz.encoders.hexToByteArray
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
abstract class GeneralListEvent(
@@ -18,6 +19,9 @@ abstract class GeneralListEvent(
content: String,
sig: HexKey
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
@Transient
private var privateTagsCache: List<List<String>>? = null
fun category() = dTag()
fun bookmarkedPosts() = taggedEvents()
fun bookmarkedPeople() = taggedUsers()
@@ -26,80 +30,73 @@ abstract class GeneralListEvent(
fun title() = tags.firstOrNull { it.size > 1 && it[0] == "title" }?.get(1)
fun nameOrTitle() = name() ?: title()
fun plainContent(privKey: ByteArray): String? {
if (content.isBlank()) return null
return try {
val sharedSecret = CryptoUtils.getSharedSecretNIP04(privKey, pubKey.hexToByteArray())
return CryptoUtils.decryptNIP04(content, sharedSecret)
} catch (e: Exception) {
Log.w("GeneralList", "Error decrypting the message ${e.message} for ${dTag()}")
null
}
}
@Transient
private var privateTagsCache: List<List<String>>? = null
fun privateTags(privKey: ByteArray): List<List<String>>? {
if (privateTagsCache != null) {
return privateTagsCache
}
privateTagsCache = try {
plainContent(privKey)?.let { mapper.readValue<List<List<String>>>(it) }
} catch (e: Throwable) {
Log.w("GeneralList", "Error parsing the JSON ${e.message}")
null
}
fun cachedPrivateTags(): List<List<String>>? {
return privateTagsCache
}
fun privateTags(content: String): List<List<String>>? {
if (privateTagsCache != null) {
return privateTagsCache
fun privateTags(signer: NostrSigner, onReady: (List<List<String>>) -> Unit) {
if (content.isBlank()) return
privateTagsCache?.let {
onReady(it)
return
}
privateTagsCache = try {
content.let { mapper.readValue<List<List<String>>>(it) }
try {
signer.nip04Decrypt(content, pubKey) {
privateTagsCache = mapper.readValue<List<List<String>>>(it)
privateTagsCache?.let {
onReady(it)
}
}
} catch (e: Throwable) {
Log.w("GeneralList", "Error parsing the JSON ${e.message}")
null
}
return privateTagsCache
}
fun privateTagsOrEmpty(privKey: ByteArray?): List<List<String>> {
if (privKey == null) return emptyList()
return privateTags(privKey) ?: emptyList()
fun privateTagsOrEmpty(signer: NostrSigner, onReady: (List<List<String>>) -> Unit) {
privateTags(signer, onReady)
}
fun privateTagsOrEmpty(content: String): List<List<String>> {
return privateTags(content) ?: emptyList()
fun privateTaggedUsers(signer: NostrSigner, onReady: (List<String>) -> Unit) = privateTags(signer) {
onReady(filterUsers(it))
}
fun privateHashtags(signer: NostrSigner, onReady: (List<String>) -> Unit) = privateTags(signer) {
onReady(filterHashtags(it))
}
fun privateGeohashes(signer: NostrSigner, onReady: (List<String>) -> Unit) = privateTags(signer) {
onReady(filterGeohashes(it))
}
fun privateTaggedEvents(signer: NostrSigner, onReady: (List<String>) -> Unit) = privateTags(signer) {
onReady(filterEvents(it))
}
fun privateTaggedAddresses(signer: NostrSigner, onReady: (List<ATag>) -> Unit) = privateTags(signer) {
onReady(filterAddresses(it))
}
fun privateTaggedUsers(privKey: ByteArray) = privateTags(privKey)?.filter { it.size > 1 && it[0] == "p" }?.map { it[1] }
fun privateTaggedUsers(content: String) = privateTags(content)?.filter { it.size > 1 && it[0] == "p" }?.map { it[1] }
fun privateHashtags(privKey: ByteArray) = privateTags(privKey)?.filter { it.size > 1 && it[0] == "t" }?.map { it[1] }
fun privateHashtags(content: String) = privateTags(content)?.filter { it.size > 1 && it[0] == "t" }?.map { it[1] }
fun privateGeohashes(privKey: ByteArray) = privateTags(privKey)?.filter { it.size > 1 && it[0] == "g" }?.map { it[1] }
fun privateGeohashes(content: String) = privateTags(content)?.filter { it.size > 1 && it[0] == "g" }?.map { it[1] }
fun privateTaggedEvents(privKey: ByteArray) = privateTags(privKey)?.filter { it.size > 1 && it[0] == "e" }?.map { it[1] }
fun privateTaggedEvents(content: String) = privateTags(content)?.filter { it.size > 1 && it[0] == "e" }?.map { it[1] }
fun privateTaggedAddresses(privKey: ByteArray) = privateTags(privKey)?.filter { it.firstOrNull() == "a" }?.mapNotNull {
val aTagValue = it.getOrNull(1)
val relay = it.getOrNull(2)
if (aTagValue != null) ATag.parse(aTagValue, relay) else null
fun filterUsers(tags: List<List<String>>): List<String> {
return tags.filter { it.size > 1 && it[0] == "p" }.map { it[1] }
}
fun privateTaggedAddresses(content: String) = privateTags(content)?.filter { it.firstOrNull() == "a" }?.mapNotNull {
val aTagValue = it.getOrNull(1)
val relay = it.getOrNull(2)
fun filterHashtags(tags: List<List<String>>): List<String> {
return tags.filter { it.size > 1 && it[0] == "t" }.map { it[1] }
}
if (aTagValue != null) ATag.parse(aTagValue, relay) else null
fun filterGeohashes(tags: List<List<String>>): List<String> {
return tags.filter { it.size > 1 && it[0] == "g" }.map { it[1] }
}
fun filterEvents(tags: List<List<String>>): List<String> {
return tags.filter { it.size > 1 && it[0] == "e" }.map { it[1] }
}
fun filterAddresses(tags: List<List<String>>): List<ATag> {
return tags.filter { it.firstOrNull() == "a" }.mapNotNull {
val aTagValue = it.getOrNull(1)
val relay = it.getOrNull(2)
if (aTagValue != null) ATag.parse(aTagValue, relay) else null
}
}
companion object {
@@ -108,9 +105,9 @@ abstract class GeneralListEvent(
privUsers: List<String>? = null,
privAddresses: List<ATag>? = null,
privateKey: ByteArray,
pubKey: ByteArray
): String {
signer: NostrSigner,
onReady: (String) -> Unit
) {
val privTags = mutableListOf<List<String>>()
privEvents?.forEach {
privTags.add(listOf("e", it))
@@ -121,23 +118,21 @@ abstract class GeneralListEvent(
privAddresses?.forEach {
privTags.add(listOf("a", it.toTag()))
}
val msg = mapper.writeValueAsString(privTags)
return CryptoUtils.encryptNIP04(
msg,
privateKey,
pubKey
)
return encryptTags(privTags, signer, onReady)
}
fun encryptTags(
privateTags: List<List<String>>? = null,
privateKey: ByteArray
): String {
return CryptoUtils.encryptNIP04(
msg = mapper.writeValueAsString(privateTags),
privateKey = privateKey,
pubKey = CryptoUtils.pubkeyCreate(privateKey)
signer: NostrSigner,
onReady: (String) -> Unit
) {
val msg = mapper.writeValueAsString(privateTags)
signer.nip04Encrypt(
msg,
signer.pubKey,
onReady
)
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class GenericRepostEvent(
@@ -29,13 +30,17 @@ class GenericRepostEvent(
companion object {
const val kind = 16
fun create(boostedPost: EventInterface, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): GenericRepostEvent {
fun create(
boostedPost: EventInterface,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (GenericRepostEvent) -> Unit
) {
val content = boostedPost.toJson()
val replyToPost = listOf("e", boostedPost.id())
val replyToAuthor = listOf("p", boostedPost.pubKey())
val pubKey = keyPair.pubKey.toHexKey()
var tags: List<List<String>> = listOf(replyToPost, replyToAuthor)
if (boostedPost is AddressableEvent) {
@@ -44,13 +49,7 @@ class GenericRepostEvent(
tags = tags + listOf(listOf("k", "${boostedPost.kind()}"))
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return GenericRepostEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
}
fun create(unsignedEvent: GenericRepostEvent, signature: String): GenericRepostEvent {
return GenericRepostEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -6,10 +6,13 @@ import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.hexToByteArray
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.crypto.Nip44Version
import com.vitorpamplona.quartz.crypto.decodeNIP44
import com.vitorpamplona.quartz.crypto.encodeNIP44
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.signers.NostrSignerInternal
@Immutable
class GiftWrapEvent(
@@ -23,66 +26,36 @@ class GiftWrapEvent(
@Transient
private var cachedInnerEvent: Map<HexKey, Event?> = mapOf()
fun cachedGift(privKey: ByteArray): Event? {
val hex = privKey.toHexKey()
if (cachedInnerEvent.contains(hex)) return cachedInnerEvent[hex]
val myInnerEvent = unwrap(privKey = privKey)
if (myInnerEvent is WrappedEvent) {
myInnerEvent.host = this
fun cachedGift(signer: NostrSigner, onReady: (Event) -> Unit) {
cachedInnerEvent[signer.pubKey]?.let {
onReady(it)
return
}
cachedInnerEvent = cachedInnerEvent + Pair(hex, myInnerEvent)
return myInnerEvent
}
unwrap(signer) { gift ->
if (gift is WrappedEvent) {
gift.host = this
}
cachedInnerEvent = cachedInnerEvent + Pair(signer.pubKey, gift)
fun cachedGift(pubKey: ByteArray, decryptedContent: String): Event? {
val hex = pubKey.toHexKey()
if (cachedInnerEvent.contains(hex)) return cachedInnerEvent[hex]
val myInnerEvent = unwrap(decryptedContent)
if (myInnerEvent is WrappedEvent) {
myInnerEvent.host = this
onReady(gift)
}
cachedInnerEvent = cachedInnerEvent + Pair(hex, myInnerEvent)
return myInnerEvent
}
fun unwrap(privKey: ByteArray) = try {
plainContent(privKey)?.let { fromJson(it) }
} catch (e: Exception) {
// Log.e("UnwrapError", "Couldn't Decrypt the content", e)
null
}
fun unwrap(decryptedContent: String) = try {
plainContent(decryptedContent)?.let { fromJson(it) }
} catch (e: Exception) {
// Log.e("UnwrapError", "Couldn't Decrypt the content", e)
null
}
private fun plainContent(privKey: ByteArray): String? {
if (content.isEmpty()) return null
return try {
val toDecrypt = decodeNIP44(content) ?: return null
return when (toDecrypt.v) {
Nip44Version.NIP04.versionCode -> CryptoUtils.decryptNIP04(toDecrypt, privKey, pubKey.hexToByteArray())
Nip44Version.NIP44.versionCode -> CryptoUtils.decryptNIP44(toDecrypt, privKey, pubKey.hexToByteArray())
else -> null
private fun unwrap(signer: NostrSigner, onReady: (Event) -> Unit) {
try {
plainContent(signer) {
onReady(fromJson(it))
}
} catch (e: Exception) {
Log.w("GeneralList", "Error decrypting the message ${e.message}")
null
// Log.e("UnwrapError", "Couldn't Decrypt the content", e)
}
}
private fun plainContent(decryptedContent: String): String? {
if (decryptedContent.isEmpty()) return null
return decryptedContent
private fun plainContent(signer: NostrSigner, onReady: (String) -> Unit) {
if (content.isEmpty()) return
signer.nip44Decrypt(content, pubKey, onReady)
}
fun recipientPubKey() = tags.firstOrNull { it.size > 1 && it[0] == "p" }?.get(1)
@@ -93,22 +66,16 @@ class GiftWrapEvent(
fun create(
event: Event,
recipientPubKey: HexKey,
createdAt: Long = TimeUtils.randomWithinAWeek()
): GiftWrapEvent {
val privateKey = CryptoUtils.privkeyCreate() // GiftWrap is always a random key
val sharedSecret = CryptoUtils.getSharedSecretNIP44(privateKey, recipientPubKey.hexToByteArray())
val content = encodeNIP44(
CryptoUtils.encryptNIP44(
toJson(event),
sharedSecret
)
)
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
createdAt: Long = TimeUtils.randomWithinAWeek(),
onReady: (GiftWrapEvent) -> Unit
) {
val signer = NostrSignerInternal(KeyPair()) // GiftWrap is always a random key
val serializedContent = toJson(event)
val tags = listOf(listOf("p", recipientPubKey))
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey)
return GiftWrapEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
signer.nip44Encrypt(serializedContent, recipientPubKey) {
signer.sign(createdAt, kind, tags, it, onReady)
}
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class HTTPAuthorizationEvent(
@@ -24,9 +25,10 @@ class HTTPAuthorizationEvent(
url: String,
method: String,
body: String? = null,
keyPair: KeyPair,
createdAt: Long = TimeUtils.now()
): HTTPAuthorizationEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (HTTPAuthorizationEvent) -> Unit
) {
var hash = ""
body?.let {
hash = CryptoUtils.sha256(it.toByteArray()).toHexKey()
@@ -38,16 +40,7 @@ class HTTPAuthorizationEvent(
listOf("payload", hash)
)
val pubKey = keyPair.pubKey.toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, "")
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return HTTPAuthorizationEvent(id.toHexKey(), pubKey, createdAt, tags, "", sig?.toHexKey() ?: "")
}
fun create(
unsignedEvent: HTTPAuthorizationEvent, signature: String
): HTTPAuthorizationEvent {
return HTTPAuthorizationEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
signer.sign(createdAt, kind, tags, "", onReady)
}
}
}
@@ -5,6 +5,7 @@ import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class HighlightEvent(
@@ -27,14 +28,11 @@ class HighlightEvent(
fun create(
msg: String,
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): HighlightEvent {
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val tags = mutableListOf<List<String>>()
val id = generateId(pubKey, createdAt, kind, tags, msg)
val sig = CryptoUtils.sign(id, privateKey)
return HighlightEvent(id.toHexKey(), pubKey, createdAt, tags, msg, sig.toHexKey())
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (HighlightEvent) -> Unit
) {
signer.sign(createdAt, kind, emptyList(), msg, onReady)
}
}
}
@@ -7,6 +7,7 @@ import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class LiveActivitiesChatMessageEvent(
@@ -50,12 +51,13 @@ class LiveActivitiesChatMessageEvent(
replyTos: List<String>? = null,
mentions: List<String>? = null,
zapReceiver: List<ZapSplitSetup>? = null,
keyPair: KeyPair,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
markAsSensitive: Boolean,
zapRaiserAmount: Long?,
geohash: String? = null
): LiveActivitiesChatMessageEvent {
geohash: String? = null,
onReady: (LiveActivitiesChatMessageEvent) -> Unit
) {
val content = message
val tags = mutableListOf(
listOf("a", activity.toTag(), "", "root")
@@ -79,16 +81,7 @@ class LiveActivitiesChatMessageEvent(
tags.add(listOf("g", it))
}
val pubKey = keyPair.pubKey.toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return LiveActivitiesChatMessageEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
}
fun create(
unsignedEvent: LiveActivitiesChatMessageEvent, signature: String
): LiveActivitiesChatMessageEvent {
return LiveActivitiesChatMessageEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class LiveActivitiesEvent(
@@ -50,14 +51,12 @@ class LiveActivitiesEvent(
const val STATUS_ENDED = "ended"
fun create(
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): LiveActivitiesEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (LiveActivitiesEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, "")
val sig = CryptoUtils.sign(id, privateKey)
return LiveActivitiesEvent(id.toHexKey(), pubKey, createdAt, tags, "", sig.toHexKey())
signer.sign(createdAt, kind, tags, "", onReady)
}
}
}
@@ -11,6 +11,7 @@ import com.vitorpamplona.quartz.encoders.hexToByteArray
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class LnZapPaymentRequestEvent(
@@ -28,24 +29,28 @@ class LnZapPaymentRequestEvent(
fun walletServicePubKey() = tags.firstOrNull() { it.size > 1 && it[0] == "p" }?.get(1)
fun lnInvoice(privKey: ByteArray, pubkey: ByteArray): String? {
if (lnInvoice != null) {
return lnInvoice
fun talkingWith(oneSideHex: String): HexKey {
return if (pubKey == oneSideHex) walletServicePubKey() ?: pubKey else pubKey
}
fun lnInvoice(signer: NostrSigner, onReady: (String) -> Unit) {
lnInvoice?.let {
onReady(it)
return
}
return try {
val sharedSecret = CryptoUtils.getSharedSecretNIP04(privKey, pubkey)
try {
signer.nip04Decrypt(content, talkingWith(signer.pubKey)) { jsonText ->
val payInvoiceMethod = mapper.readValue(jsonText, Request::class.java)
val jsonText = CryptoUtils.decryptNIP04(content, sharedSecret)
lnInvoice = (payInvoiceMethod as? PayInvoiceMethod)?.params?.invoice
val payInvoiceMethod = mapper.readValue(jsonText, Request::class.java)
lnInvoice = (payInvoiceMethod as? PayInvoiceMethod)?.params?.invoice
return lnInvoice
lnInvoice?.let {
onReady(it)
}
}
} catch (e: Exception) {
Log.w("BookmarkList", "Error decrypting the message ${e.message}")
null
}
}
@@ -55,24 +60,21 @@ class LnZapPaymentRequestEvent(
fun create(
lnInvoice: String,
walletServicePubkey: String,
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): LnZapPaymentRequestEvent {
val pubKey = CryptoUtils.pubkeyCreate(privateKey)
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (LnZapPaymentRequestEvent) -> Unit
) {
val serializedRequest = mapper.writeValueAsString(PayInvoiceMethod.create(lnInvoice))
val content = CryptoUtils.encryptNIP04(
serializedRequest,
privateKey,
walletServicePubkey.hexToByteArray()
)
val tags = mutableListOf<List<String>>()
tags.add(listOf("p", walletServicePubkey))
val id = generateId(pubKey.toHexKey(), createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey)
return LnZapPaymentRequestEvent(id.toHexKey(), pubKey.toHexKey(), createdAt, tags, content, sig.toHexKey())
signer.nip04Encrypt(
serializedRequest,
walletServicePubkey
) { content ->
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
}
@@ -9,6 +9,7 @@ import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class LnZapPaymentResponseEvent(
@@ -19,7 +20,6 @@ class LnZapPaymentResponseEvent(
content: String,
sig: HexKey
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
// Once one of an app user decrypts the payment, all users else can see it.
@Transient
private var response: Response? = null
@@ -27,53 +27,37 @@ class LnZapPaymentResponseEvent(
fun requestAuthor() = tags.firstOrNull() { it.size > 1 && it[0] == "p" }?.get(1)
fun requestId() = tags.firstOrNull() { it.size > 1 && it[0] == "e" }?.get(1)
private fun decrypt(privKey: ByteArray, pubKey: ByteArray): String? {
return try {
val sharedSecret = CryptoUtils.getSharedSecretNIP04(privKey, pubKey)
fun talkingWith(oneSideHex: String): HexKey {
return if (pubKey == oneSideHex) requestAuthor() ?: pubKey else pubKey
}
val retVal = CryptoUtils.decryptNIP04(content, sharedSecret)
if (retVal.startsWith(PrivateDmEvent.nip18Advertisement)) {
retVal.substring(16)
} else {
retVal
private fun plainContent(signer: NostrSigner, onReady: (String) -> Unit) {
try {
signer.nip04Decrypt(content, talkingWith(signer.pubKey)) { content ->
onReady(content)
}
} catch (e: Exception) {
Log.w("PrivateDM", "Error decrypting the message ${e.message}")
null
}
}
fun response(privKey: ByteArray, pubKey: ByteArray): Response? {
if (response != null) response
return try {
if (content.isNotEmpty()) {
val decrypted = decrypt(privKey, pubKey)
response = mapper.readValue(decrypted, Response::class.java)
response
} else {
null
}
} catch (e: Exception) {
Log.w("LnZapPaymentResponseEvent", "Can't parse content as a payment response: $content", e)
null
fun response(signer: NostrSigner, onReady: (Response) -> Unit) {
response?.let {
onReady(it)
return
}
}
fun response(decryptedContent: String): Response? {
if (response != null) response
return try {
try {
if (content.isNotEmpty()) {
response = mapper.readValue(decryptedContent, Response::class.java)
response
} else {
null
plainContent(signer) {
mapper.readValue(it, Response::class.java)?.let {
response = it
onReady(it)
}
}
}
} catch (e: Exception) {
Log.w("LnZapPaymentResponseEvent", "Can't parse content as a payment response: $content", e)
null
}
}
@@ -7,6 +7,7 @@ import com.vitorpamplona.quartz.encoders.Bech32
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.LnInvoiceUtil
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils
import java.nio.charset.Charset
import java.security.SecureRandom
@@ -29,15 +30,13 @@ class LnZapPrivateEvent(
const val kind = 9733
fun create(
privateKey: ByteArray,
signer: NostrSigner,
tags: List<List<String>> = emptyList(),
content: String = "",
createdAt: Long = TimeUtils.now()
): Event {
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey).toHexKey()
return Event(id.toHexKey(), pubKey, createdAt, kind, tags, content, sig)
createdAt: Long = TimeUtils.now(),
onReady: (LnZapPrivateEvent) -> Unit
) {
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -4,9 +4,12 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.utils.*
import com.vitorpamplona.quartz.encoders.Bech32
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.hexToByteArray
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.signers.NostrSignerInternal
import java.nio.charset.Charset
import java.security.SecureRandom
import javax.crypto.BadPaddingException
@@ -34,8 +37,6 @@ class LnZapRequestEvent(
fun isPrivateZap() = tags.any { t -> t.size >= 2 && t[0] == "anon" && t[1].isNotBlank() }
fun getPrivateZapEvent(loggedInUserPrivKey: ByteArray, pubKey: HexKey): Event? {
if (privateZapEvent != null) return privateZapEvent
val anonTag = tags.firstOrNull { t -> t.size >= 2 && t[0] == "anon" }
if (anonTag != null) {
val encnote = anonTag[1]
@@ -44,8 +45,7 @@ class LnZapRequestEvent(
val note = decryptPrivateZapMessage(encnote, loggedInUserPrivKey, pubKey.hexToByteArray())
val decryptedEvent = fromJson(note)
if (decryptedEvent.kind == 9733) {
privateZapEvent = decryptedEvent
return privateZapEvent
return decryptedEvent
}
} catch (e: Exception) {
e.printStackTrace()
@@ -55,22 +55,39 @@ class LnZapRequestEvent(
return null
}
fun cachedPrivateZap(): Event? {
return privateZapEvent
}
fun decryptPrivateZap(signer: NostrSigner, onReady: (Event) -> Unit) {
privateZapEvent?.let {
onReady(it)
return
}
signer.decryptZapEvent(this) {
// caches it
privateZapEvent = it
onReady(it)
}
}
companion object {
const val kind = 9734
fun create(
originalNote: EventInterface,
relays: Set<String>,
privateKey: ByteArray,
signer: NostrSigner,
pollOption: Int?,
message: String,
zapType: LnZapEvent.ZapType,
toUserPubHex: String?, // Overrides in case of Zap Splits
createdAt: Long = TimeUtils.now()
): LnZapRequestEvent {
var content = message
var privkey = privateKey
var pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
createdAt: Long = TimeUtils.now(),
onReady: (LnZapRequestEvent) -> Unit
) {
if (zapType == LnZapEvent.ZapType.NONZAP) return
var tags = listOf(
listOf("e", originalNote.id()),
listOf("p", toUserPubHex ?: originalNote.pubKey()),
@@ -82,196 +99,43 @@ class LnZapRequestEvent(
if (pollOption != null && pollOption >= 0) {
tags = tags + listOf(listOf(POLL_OPTION, pollOption.toString()))
}
if (zapType == LnZapEvent.ZapType.ANONYMOUS) {
tags = tags + listOf(listOf("anon", ""))
privkey = CryptoUtils.privkeyCreate()
pubKey = CryptoUtils.pubkeyCreate(privkey).toHexKey()
tags = tags + listOf(listOf("anon"))
NostrSignerInternal(KeyPair()).sign(createdAt, kind, tags, message, onReady)
} else if (zapType == LnZapEvent.ZapType.PRIVATE) {
val encryptionPrivateKey = createEncryptionPrivateKey(privateKey.toHexKey(), originalNote.id(), createdAt)
val noteJson = (LnZapPrivateEvent.create(privkey, listOf(tags[0], tags[1]), message)).toJson()
val encryptedContent = encryptPrivateZapMessage(noteJson, encryptionPrivateKey, originalNote.pubKey().hexToByteArray())
tags = tags + listOf(listOf("anon", encryptedContent))
content = "" // make sure public content is empty, as the content is encrypted
privkey = encryptionPrivateKey // sign event with generated privkey
pubKey = CryptoUtils.pubkeyCreate(encryptionPrivateKey).toHexKey() // updated event with according pubkey
tags = tags + listOf(listOf("anon", ""))
signer.sign(createdAt, kind, tags, message, onReady)
} else {
signer.sign(createdAt, kind, tags, message, onReady)
}
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privkey)
return LnZapRequestEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
}
fun create(
unsignedEvent: LnZapRequestEvent,
signature: String
): LnZapRequestEvent {
return LnZapRequestEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
}
fun createPublic(
userHex: String,
relays: Set<String>,
pubKey: HexKey,
message: String,
createdAt: Long = TimeUtils.now()
): LnZapRequestEvent {
val tags = listOf(
listOf("p", userHex),
listOf("relays") + relays
)
val id = generateId(pubKey, createdAt, kind, tags, message)
return LnZapRequestEvent(id.toHexKey(), pubKey, createdAt, tags, message, "")
}
fun createPublic(
originalNote: EventInterface,
relays: Set<String>,
pubKey: HexKey,
pollOption: Int?,
message: String,
toUserPubHex: String?, // Overrides in case of Zap Splits
createdAt: Long = TimeUtils.now()
): LnZapRequestEvent {
var tags = listOf(
listOf("e", originalNote.id()),
listOf("p", toUserPubHex ?: 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()))
}
val id = generateId(pubKey, createdAt, kind, tags, message)
return LnZapRequestEvent(id.toHexKey(), pubKey, createdAt, tags, message, "")
}
fun createPrivateZap(
originalNote: EventInterface,
relays: Set<String>,
pubKey: HexKey,
pollOption: Int?,
message: String,
toUserPubHex: String?,
createdAt: Long = TimeUtils.now()
): LnZapRequestEvent {
val content = message
var tags = listOf(
listOf("e", originalNote.id()),
listOf("p", toUserPubHex ?: 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("zap", pubKey, createdAt, tags, content, "")
}
fun createPrivateZap(
userHex: String,
relays: Set<String>,
pubKey: HexKey,
message: String,
createdAt: Long = TimeUtils.now()
): LnZapRequestEvent {
val content = message
var tags = listOf(
listOf("p", userHex),
listOf("relays") + relays
)
tags = tags + listOf(listOf("anon", ""))
return LnZapRequestEvent("zap", pubKey, createdAt, tags, content, "")
}
fun createAnonymous(
originalNote: EventInterface,
relays: Set<String>,
pollOption: Int?,
message: String,
toUserPubHex: String?, // Overrides in case of Zap Splits
createdAt: Long = TimeUtils.now()
): LnZapRequestEvent {
var tags = listOf(
listOf("e", originalNote.id()),
listOf("p", toUserPubHex ?: 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", ""))
val privkey = CryptoUtils.privkeyCreate()
val pubKey = CryptoUtils.pubkeyCreate(privkey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, message)
val sig = CryptoUtils.sign(id, privkey)
return LnZapRequestEvent(id.toHexKey(), pubKey, createdAt, tags, message, sig.toHexKey())
}
fun createAnonymous(
userHex: String,
relays: Set<String>,
message: String,
createdAt: Long = TimeUtils.now()
): LnZapRequestEvent {
var tags = listOf(
listOf("p", userHex),
listOf("relays") + relays
)
tags = tags + listOf(listOf("anon", ""))
val privkey = CryptoUtils.privkeyCreate()
val pubKey = CryptoUtils.pubkeyCreate(privkey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, message)
val sig = CryptoUtils.sign(id, privkey)
return LnZapRequestEvent(id.toHexKey(), pubKey, createdAt, tags, message, sig.toHexKey())
}
fun create(
userHex: String,
relays: Set<String>,
privateKey: ByteArray,
signer: NostrSigner,
message: String,
zapType: LnZapEvent.ZapType,
createdAt: Long = TimeUtils.now()
): LnZapRequestEvent {
var content = message
var privkey = privateKey
var pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
createdAt: Long = TimeUtils.now(),
onReady: (LnZapRequestEvent) -> Unit
) {
if (zapType == LnZapEvent.ZapType.NONZAP) return
var tags = listOf(
listOf("p", userHex),
listOf("relays") + relays
)
if (zapType == LnZapEvent.ZapType.ANONYMOUS) {
privkey = CryptoUtils.privkeyCreate()
pubKey = CryptoUtils.pubkeyCreate(privkey).toHexKey()
tags = tags + listOf(listOf("anon", ""))
NostrSignerInternal(KeyPair()).sign(createdAt, kind, tags, message, onReady)
} else if (zapType == LnZapEvent.ZapType.PRIVATE) {
val encryptionPrivateKey = createEncryptionPrivateKey(privateKey.toHexKey(), userHex, createdAt)
val noteJson = LnZapPrivateEvent.create(privkey, listOf(tags[0], tags[1]), message).toJson()
val encryptedContent = encryptPrivateZapMessage(noteJson, encryptionPrivateKey, userHex.hexToByteArray())
tags = tags + listOf(listOf("anon", encryptedContent))
content = ""
privkey = encryptionPrivateKey
pubKey = CryptoUtils.pubkeyCreate(encryptionPrivateKey).toHexKey()
tags = tags + listOf(listOf("anon", ""))
signer.sign(createdAt, kind, tags, message, onReady)
} else {
signer.sign(createdAt, kind, tags, message, onReady)
}
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privkey)
return LnZapRequestEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
}
@@ -281,7 +145,7 @@ class LnZapRequestEvent(
return CryptoUtils.sha256(strbyte)
}
private fun encryptPrivateZapMessage(msg: String, privkey: ByteArray, pubkey: ByteArray): String {
fun encryptPrivateZapMessage(msg: String, privkey: ByteArray, pubkey: ByteArray): String {
val sharedSecret = CryptoUtils.getSharedSecretNIP04(privkey, pubkey)
val iv = ByteArray(16)
SecureRandom().nextBytes(iv)
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class LongTextNoteEvent(
@@ -33,8 +34,14 @@ class LongTextNoteEvent(
companion object {
const val kind = 30023
fun create(msg: String, replyTos: List<String>?, mentions: List<String>?, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): LongTextNoteEvent {
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
fun create(
msg: String,
replyTos: List<String>?,
mentions: List<String>?,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (LongTextNoteEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
replyTos?.forEach {
tags.add(listOf("e", it))
@@ -42,9 +49,7 @@ class LongTextNoteEvent(
mentions?.forEach {
tags.add(listOf("p", it))
}
val id = generateId(pubKey, createdAt, kind, tags, msg)
val sig = CryptoUtils.sign(id, privateKey)
return LongTextNoteEvent(id.toHexKey(), pubKey, createdAt, tags, msg, sig.toHexKey())
signer.sign(createdAt, kind, tags, msg, onReady)
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
import java.io.ByteArrayInputStream
@Stable
@@ -149,20 +150,20 @@ class MetadataEvent(
companion object {
const val kind = 0
fun create(contactMetaData: String, identities: List<IdentityClaim>, pubKey: HexKey, privateKey: ByteArray?, createdAt: Long = TimeUtils.now()): MetadataEvent {
fun create(
contactMetaData: String,
identities: List<IdentityClaim>,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (MetadataEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
identities.forEach {
tags.add(listOf("i", it.platformIdentity(), it.proof))
}
val id = generateId(pubKey, createdAt, kind, tags, contactMetaData)
val sig = if (privateKey == null) null else CryptoUtils.sign(id, privateKey)
return MetadataEvent(id.toHexKey(), pubKey, createdAt, tags, contactMetaData, sig?.toHexKey() ?: "")
}
fun create(unsignedEvent: MetadataEvent, signature: String): MetadataEvent {
return MetadataEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
signer.sign(createdAt, kind, tags, contactMetaData, onReady)
}
}
}
@@ -9,6 +9,8 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
import java.util.UUID
@Immutable
class MuteListEvent(
@@ -19,41 +21,51 @@ class MuteListEvent(
content: String,
sig: HexKey
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
fun plainContent(privKey: ByteArray): String? {
return try {
val sharedSecret = CryptoUtils.getSharedSecretNIP04(privKey, pubKey.hexToByteArray())
return CryptoUtils.decryptNIP04(content, sharedSecret)
} catch (e: Exception) {
Log.w("BookmarkList", "Error decrypting the message ${e.message}")
null
}
}
@Transient
private var privateTagsCache: List<List<String>>? = null
fun privateTags(privKey: ByteArray): List<List<String>>? {
if (privateTagsCache != null) {
return privateTagsCache
private fun privateTags(signer: NostrSigner, onReady: (List<List<String>>) -> Unit) {
if (content.isBlank()) return
privateTagsCache?.let {
onReady(it)
return
}
privateTagsCache = try {
plainContent(privKey)?.let { mapper.readValue<List<List<String>>>(it) }
try {
signer.nip04Decrypt(content, pubKey) {
privateTagsCache = mapper.readValue<List<List<String>>>(it)
privateTagsCache?.let {
onReady(it)
}
}
} catch (e: Throwable) {
Log.w("BookmarkList", "Error parsing the JSON ${e.message}")
null
Log.w("MuteList", "Error parsing the JSON ${e.message}")
}
return privateTagsCache
}
fun privateTaggedUsers(privKey: ByteArray) = privateTags(privKey)?.filter { it.firstOrNull() == "p" }?.mapNotNull { it.getOrNull(1) }
fun privateTaggedEvents(privKey: ByteArray) = privateTags(privKey)?.filter { it.firstOrNull() == "e" }?.mapNotNull { it.getOrNull(1) }
fun privateTaggedAddresses(privKey: ByteArray) = privateTags(privKey)?.filter { it.firstOrNull() == "a" }?.mapNotNull {
val aTagValue = it.getOrNull(1)
val relay = it.getOrNull(2)
fun privateTaggedUsers(signer: NostrSigner, onReady: (List<String>) -> Unit) = privateTags(signer) {
onReady(it.filter { it.size > 1 && it[0] == "p" }.map { it[1] } )
}
fun privateHashtags(signer: NostrSigner, onReady: (List<String>) -> Unit) = privateTags(signer) {
onReady(it.filter { it.size > 1 && it[0] == "t" }.map { it[1] } )
}
fun privateGeohashes(signer: NostrSigner, onReady: (List<String>) -> Unit) = privateTags(signer) {
onReady(it.filter { it.size > 1 && it[0] == "g" }.map { it[1] } )
}
fun privateTaggedEvents(signer: NostrSigner, onReady: (List<String>) -> Unit) = privateTags(signer) {
onReady(it.filter { it.size > 1 && it[0] == "e" }.map { it[1] } )
}
if (aTagValue != null) ATag.parse(aTagValue, relay) else null
fun privateTaggedAddresses(signer: NostrSigner, onReady: (List<ATag>) -> Unit) = privateTags(signer) {
onReady(
it.filter { it.firstOrNull() == "a" }.mapNotNull {
val aTagValue = it.getOrNull(1)
val relay = it.getOrNull(2)
if (aTagValue != null) ATag.parse(aTagValue, relay) else null
}
)
}
companion object {
@@ -68,11 +80,10 @@ class MuteListEvent(
privUsers: List<String>? = null,
privAddresses: List<ATag>? = null,
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): MuteListEvent {
val pubKey = CryptoUtils.pubkeyCreate(privateKey)
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (MuteListEvent) -> Unit
) {
val privTags = mutableListOf<List<String>>()
privEvents?.forEach {
privTags.add(listOf("e", it))
@@ -85,12 +96,6 @@ class MuteListEvent(
}
val msg = mapper.writeValueAsString(privTags)
val content = CryptoUtils.encryptNIP04(
msg,
privateKey,
pubKey
)
val tags = mutableListOf<List<String>>()
events?.forEach {
tags.add(listOf("e", it))
@@ -102,9 +107,9 @@ class MuteListEvent(
tags.add(listOf("a", it.toTag()))
}
val id = generateId(pubKey.toHexKey(), createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey)
return MuteListEvent(id.toHexKey(), pubKey.toHexKey(), createdAt, tags, content, sig.toHexKey())
signer.nip04Encrypt(msg, signer.pubKey) { content ->
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
}
@@ -5,28 +5,65 @@ import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
import kotlin.math.sign
class NIP24Factory {
data class Result(val msg: Event, val wraps: List<GiftWrapEvent>)
private fun recursiveGiftWrapCreation(
event: Event,
remainingTos: List<HexKey>,
signer: NostrSigner,
output: MutableList<GiftWrapEvent>,
onReady: (List<GiftWrapEvent>) -> Unit
) {
if (remainingTos.isEmpty()) {
onReady(output)
return
}
val next = remainingTos.first()
SealedGossipEvent.create(
event = event,
encryptTo = next,
signer = signer
) { seal ->
GiftWrapEvent.create(
event = seal,
recipientPubKey = next
) { giftWrap ->
output.add(giftWrap)
recursiveGiftWrapCreation(event, remainingTos.minus(next), signer, output, onReady)
}
}
}
private fun createWraps(event: Event, to: Set<HexKey>, signer: NostrSigner, onReady: (List<GiftWrapEvent>) -> Unit) {
val wraps = mutableListOf<GiftWrapEvent>()
recursiveGiftWrapCreation(event, to.toList(), signer, wraps, onReady)
}
fun createMsgNIP24(
msg: String,
to: List<HexKey>,
keyPair: KeyPair,
signer: NostrSigner,
subject: String? = null,
replyTos: List<String>? = null,
mentions: List<String>? = null,
zapReceiver: List<ZapSplitSetup>? = null,
markAsSensitive: Boolean = false,
zapRaiserAmount: Long? = null,
geohash: String? = null
): Result {
val senderPublicKey = keyPair.pubKey.toHexKey()
geohash: String? = null,
onReady: (Result) -> Unit
) {
val senderPublicKey = signer.pubKey
val senderMessage = ChatMessageEvent.create(
ChatMessageEvent.create(
msg = msg,
to = to,
keyPair = keyPair,
signer = signer,
subject = subject,
replyTos = replyTos,
mentions = mentions,
@@ -34,75 +71,60 @@ class NIP24Factory {
markAsSensitive = markAsSensitive,
zapRaiserAmount = zapRaiserAmount,
geohash = geohash
)
return Result(
msg = senderMessage,
wraps = to.plus(senderPublicKey).map {
GiftWrapEvent.create(
event = SealedGossipEvent.create(
event = senderMessage,
encryptTo = it,
privateKey = keyPair.privKey!!
),
recipientPubKey = it
) { senderMessage ->
createWraps(senderMessage, to.plus(senderPublicKey).toSet(), signer) { wraps ->
onReady(
Result(
msg = senderMessage,
wraps = wraps
)
)
}
)
}
}
fun createReactionWithinGroup(content: String, originalNote: EventInterface, to: List<HexKey>, from: KeyPair): Result {
val senderPublicKey = from.pubKey.toHexKey()
fun createReactionWithinGroup(content: String, originalNote: EventInterface, to: List<HexKey>, signer: NostrSigner, onReady: (Result) -> Unit) {
val senderPublicKey = signer.pubKey
val senderReaction = ReactionEvent.create(
ReactionEvent.create(
content,
originalNote,
from
)
return Result(
msg = senderReaction,
wraps = to.plus(senderPublicKey).map {
GiftWrapEvent.create(
event = SealedGossipEvent.create(
event = senderReaction,
encryptTo = it,
privateKey = from.privKey!!
),
recipientPubKey = it
signer
) { senderReaction ->
createWraps(senderReaction, to.plus(senderPublicKey).toSet(), signer) { wraps->
onReady(
Result(
msg = senderReaction,
wraps = wraps
)
)
}
)
}
}
fun createReactionWithinGroup(emojiUrl: EmojiUrl, originalNote: EventInterface, to: List<HexKey>, from: KeyPair): Result {
val senderPublicKey = from.pubKey.toHexKey()
fun createReactionWithinGroup(emojiUrl: EmojiUrl, originalNote: EventInterface, to: List<HexKey>, signer: NostrSigner, onReady: (Result) -> Unit) {
val senderPublicKey = signer.pubKey
val senderReaction = ReactionEvent.create(
ReactionEvent.create(
emojiUrl,
originalNote,
from
)
return Result(
msg = senderReaction,
wraps = to.plus(senderPublicKey).map {
GiftWrapEvent.create(
event = SealedGossipEvent.create(
event = senderReaction,
encryptTo = it,
privateKey = from.privKey!!
),
recipientPubKey = it
signer
) { senderReaction ->
createWraps(senderReaction, to.plus(senderPublicKey).toSet(), signer) { wraps ->
onReady(
Result(
msg = senderReaction,
wraps = wraps
)
)
}
)
}
}
fun createTextNoteNIP24(
msg: String,
to: List<HexKey>,
keyPair: KeyPair,
signer: NostrSigner,
replyTos: List<String>? = null,
mentions: List<String>? = null,
addresses: List<ATag>?,
@@ -113,13 +135,14 @@ class NIP24Factory {
root: String?,
directMentions: Set<HexKey>,
zapRaiserAmount: Long? = null,
geohash: String? = null
): Result {
val senderPublicKey = keyPair.pubKey.toHexKey()
geohash: String? = null,
onReady: (Result) -> Unit
) {
val senderPublicKey = signer.pubKey
val senderMessage = TextNoteEvent.create(
TextNoteEvent.create(
msg = msg,
keyPair = keyPair,
signer = signer,
replyTos = replyTos,
mentions = mentions,
zapReceiver = zapReceiver,
@@ -131,20 +154,15 @@ class NIP24Factory {
markAsSensitive = markAsSensitive,
zapRaiserAmount = zapRaiserAmount,
geohash = geohash
)
return Result(
msg = senderMessage,
wraps = to.plus(senderPublicKey).map {
GiftWrapEvent.create(
event = SealedGossipEvent.create(
event = senderMessage,
encryptTo = it,
privateKey = keyPair.privKey!!
),
recipientPubKey = it
) { senderMessage ->
createWraps(senderMessage, to.plus(senderPublicKey).toSet(), signer) { wraps ->
onReady(
Result(
msg = senderMessage,
wraps = wraps
)
)
}
)
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class NNSEvent(
@@ -24,14 +25,12 @@ class NNSEvent(
const val kind = 30053
fun create(
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): NNSEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (NNSEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, "")
val sig = CryptoUtils.sign(id, privateKey)
return NNSEvent(id.toHexKey(), pubKey, createdAt, tags, "", sig.toHexKey())
signer.sign(createdAt, kind, tags, "", onReady)
}
}
}
@@ -5,6 +5,7 @@ import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.persistentSetOf
import kotlinx.collections.immutable.toImmutableSet
@@ -18,8 +19,6 @@ class PeopleListEvent(
content: String,
sig: HexKey
) : GeneralListEvent(id, pubKey, createdAt, kind, tags, content, sig) {
@Transient
var decryptedContent: String? = null
@Transient
var publicAndPrivateUserCache: ImmutableSet<HexKey>? = null
@Transient
@@ -34,69 +33,74 @@ class PeopleListEvent(
return (privateUserList + publicUserList).toImmutableSet()
}
fun publicAndPrivateWords(privateKey: ByteArray?): ImmutableSet<String> {
fun publicAndPrivateWords(signer: NostrSigner, onReady: (ImmutableSet<String>) -> Unit) {
publicAndPrivateWordCache?.let {
return it
onReady(it)
return
}
publicAndPrivateWordCache = filterTagList("word", privateTagsOrEmpty(privKey = privateKey))
return publicAndPrivateWordCache ?: persistentSetOf()
privateTagsOrEmpty(signer) {
publicAndPrivateWordCache = filterTagList("word", it)
publicAndPrivateWordCache?.let {
onReady(it)
}
}
}
fun publicAndPrivateUsers(privateKey: ByteArray?): ImmutableSet<HexKey> {
fun publicAndPrivateUsers(signer: NostrSigner, onReady: (ImmutableSet<String>) -> Unit) {
publicAndPrivateUserCache?.let {
return it
onReady(it)
return
}
publicAndPrivateUserCache = filterTagList("p", privateTagsOrEmpty(privKey = privateKey))
return publicAndPrivateUserCache ?: persistentSetOf()
privateTagsOrEmpty(signer) {
publicAndPrivateUserCache = filterTagList("p", it)
publicAndPrivateUserCache?.let {
onReady(it)
}
}
}
fun publicAndPrivateWords(decryptedContent: String): ImmutableSet<HexKey> {
publicAndPrivateWordCache?.let {
return it
@Immutable
data class UsersAndWords(val users: ImmutableSet<String>, val words: ImmutableSet<String>)
fun publicAndPrivateUsersAndWords(signer: NostrSigner, onReady: (UsersAndWords) -> Unit) {
publicAndPrivateUserCache?.let { userList ->
publicAndPrivateWordCache?.let { wordList ->
onReady(UsersAndWords(userList, wordList))
return
}
}
publicAndPrivateWordCache = filterTagList("word", privateTagsOrEmpty(decryptedContent))
privateTagsOrEmpty(signer) {
publicAndPrivateUserCache = filterTagList("p", it)
publicAndPrivateWordCache = filterTagList("word", it)
return publicAndPrivateWordCache ?: persistentSetOf()
}
fun publicAndPrivateUsers(decryptedContent: String): ImmutableSet<HexKey> {
publicAndPrivateUserCache?.let {
return it
publicAndPrivateUserCache?.let { userList ->
publicAndPrivateWordCache?.let { wordList ->
onReady(
UsersAndWords(userList, wordList)
)
}
}
}
publicAndPrivateUserCache = filterTagList("p", privateTagsOrEmpty(decryptedContent))
return publicAndPrivateUserCache ?: persistentSetOf()
}
fun isTagged(key: String, tag: String, isPrivate: Boolean, privateKey: ByteArray): Boolean {
fun isTagged(key: String, tag: String, isPrivate: Boolean, signer: NostrSigner, onReady: (Boolean) -> Unit) {
return if (isPrivate) {
privateTagsOrEmpty(privKey = privateKey).any { it.size > 1 && it[0] == key && it[1] == tag }
privateTagsOrEmpty(signer = signer) {
onReady(
it.any { it.size > 1 && it[0] == key && it[1] == tag }
)
}
} else {
isTagged(key, tag)
onReady(isTagged(key, tag))
}
}
fun isTagged(key: String, tag: String, isPrivate: Boolean, decryptedContent: String): Boolean {
return if (isPrivate) {
privateTagsOrEmpty(decryptedContent).any { it.size > 1 && it[0] == key && it[1] == tag }
} else {
isTagged(key, tag)
}
}
fun isTaggedWord(word: String, isPrivate: Boolean, signer: NostrSigner, onReady: (Boolean) -> Unit) = isTagged( "word", word, isPrivate, signer, onReady)
fun isTaggedWord(word: String, isPrivate: Boolean, privateKey: ByteArray) = isTagged( "word", word, isPrivate, privateKey)
fun isTaggedUser(idHex: String, isPrivate: Boolean, privateKey: ByteArray) = isTagged( "p", idHex, isPrivate, privateKey)
fun isTaggedUser(idHex: String, isPrivate: Boolean, decryptedContent: String) = isTagged( "p", idHex, isPrivate, decryptedContent)
fun isTaggedWord(idHex: String, isPrivate: Boolean, decryptedContent: String) = isTagged( "word", idHex, isPrivate, decryptedContent)
fun isTaggedUser(idHex: String, isPrivate: Boolean, signer: NostrSigner, onReady: (Boolean) -> Unit) = isTagged( "p", idHex, isPrivate, signer, onReady)
companion object {
const val kind = 30000
@@ -106,83 +110,56 @@ class PeopleListEvent(
return "30000:$pubKeyHex:$blockList"
}
fun createListWithTag(name: String, key: String, tag: String, isPrivate: Boolean, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): PeopleListEvent {
return if (isPrivate) {
create(
content = encryptTags(listOf(listOf(key, tag)), privateKey),
tags = listOf(listOf("d", name)),
privateKey = privateKey,
createdAt = createdAt
)
fun createListWithTag(name: String, key: String, tag: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (PeopleListEvent) -> Unit) {
if (isPrivate) {
encryptTags(listOf(listOf(key, tag)), signer) { encryptedTags ->
create(
content = encryptedTags,
tags = listOf(listOf("d", name)),
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
} else {
create(
content = "",
tags = listOf(listOf("d", name), listOf(key, tag)),
privateKey = privateKey,
createdAt = createdAt
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
fun createListWithUser(name: String, pubKeyHex: String, isPrivate: Boolean, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): PeopleListEvent {
return createListWithTag(name, "p", pubKeyHex, isPrivate, privateKey, createdAt)
fun createListWithUser(name: String, pubKeyHex: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (PeopleListEvent) -> Unit) {
return createListWithTag(name, "p", pubKeyHex, isPrivate, signer, createdAt, onReady)
}
fun createListWithWord(name: String, word: String, isPrivate: Boolean, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): PeopleListEvent {
return createListWithTag(name, "word", word, isPrivate, privateKey, createdAt)
fun createListWithWord(name: String, word: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (PeopleListEvent) -> Unit) {
return createListWithTag(name, "word", word, isPrivate, signer, createdAt, onReady)
}
fun createListWithUser(name: String, pubKeyHex: String, isPrivate: Boolean, pubKey: HexKey, encryptedContent: String, createdAt: Long = TimeUtils.now()): PeopleListEvent {
return if (isPrivate) {
create(
content = encryptedContent,
tags = listOf(listOf("d", name)),
pubKey = pubKey,
createdAt = createdAt
)
} else {
create(
content = "",
tags = listOf(listOf("d", name), listOf("p", pubKeyHex)),
pubKey = pubKey,
createdAt = createdAt
)
}
}
fun createListWithWord(name: String, word: String, isPrivate: Boolean, pubKey: HexKey, encryptedContent: String, createdAt: Long = TimeUtils.now()): PeopleListEvent {
return if (isPrivate) {
create(
content = encryptedContent,
tags = listOf(listOf("d", name)),
pubKey = pubKey,
createdAt = createdAt
)
} else {
create(
content = "",
tags = listOf(listOf("d", name), listOf("word", word)),
pubKey = pubKey,
createdAt = createdAt
)
}
}
fun addUsers(earlierVersion: PeopleListEvent, listPubKeyHex: List<String>, isPrivate: Boolean, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): PeopleListEvent {
return if (isPrivate) {
create(
content = encryptTags(
privateTags = earlierVersion.privateTagsOrEmpty(privKey = privateKey).plus(
fun addUsers(earlierVersion: PeopleListEvent, listPubKeyHex: List<String>, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (PeopleListEvent) -> Unit) {
if (isPrivate) {
earlierVersion.privateTagsOrEmpty(signer) { privateTags ->
encryptTags(
privateTags = privateTags.plus(
listPubKeyHex.map {
listOf("p", it)
}
),
privateKey = privateKey
),
tags = earlierVersion.tags,
privateKey = privateKey,
createdAt = createdAt
)
signer = signer
) { encryptedTags ->
create(
content = encryptedTags,
tags = earlierVersion.tags,
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
} else {
create(
content = earlierVersion.content,
@@ -191,143 +168,99 @@ class PeopleListEvent(
listOf("p", it)
}
),
privateKey = privateKey,
createdAt = createdAt
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
fun addWord(earlierVersion: PeopleListEvent, word: String, isPrivate: Boolean, pubKey: HexKey, encryptedContent: String, createdAt: Long = TimeUtils.now()): PeopleListEvent {
return if (isPrivate) {
create(
content = encryptedContent,
tags = earlierVersion.tags,
pubKey = pubKey,
createdAt = createdAt
)
} else {
create(
content = earlierVersion.content,
tags = earlierVersion.tags.plus(element = listOf("word", word)),
pubKey = pubKey,
createdAt = createdAt
)
fun addWord(earlierVersion: PeopleListEvent, word: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (PeopleListEvent) -> Unit) {
return addTag(earlierVersion, "word", word, isPrivate, signer, createdAt, onReady)
}
fun addUser(earlierVersion: PeopleListEvent, pubKeyHex: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (PeopleListEvent) -> Unit) {
return addTag(earlierVersion, "p", pubKeyHex, isPrivate, signer, createdAt, onReady)
}
fun addTag(earlierVersion: PeopleListEvent, key: String, tag: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (PeopleListEvent) -> Unit) {
earlierVersion.isTagged(key, tag, isPrivate, signer) { isTagged ->
if (!isTagged) {
if (isPrivate) {
earlierVersion.privateTagsOrEmpty(signer) { privateTags ->
encryptTags(
privateTags = privateTags.plus(element = listOf(key, tag)),
signer = signer
) { encryptedTags ->
create(
content = encryptedTags,
tags = earlierVersion.tags,
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
} else {
create(
content = earlierVersion.content,
tags = earlierVersion.tags.plus(element = listOf(key, tag)),
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
}
}
fun addUser(earlierVersion: PeopleListEvent, pubKeyHex: String, isPrivate: Boolean, pubKey: HexKey, encryptedContent: String, createdAt: Long = TimeUtils.now()): PeopleListEvent {
return if (isPrivate) {
create(
content = encryptedContent,
tags = earlierVersion.tags,
pubKey = pubKey,
createdAt = createdAt
)
} else {
create(
content = earlierVersion.content,
tags = earlierVersion.tags.plus(element = listOf("p", pubKeyHex)),
pubKey = pubKey,
createdAt = createdAt
)
fun removeWord(earlierVersion: PeopleListEvent, word: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (PeopleListEvent) -> Unit) {
return removeTag(earlierVersion, "word", word, isPrivate, signer, createdAt, onReady)
}
fun removeUser(earlierVersion: PeopleListEvent, pubKeyHex: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (PeopleListEvent) -> Unit) {
return removeTag(earlierVersion, "p", pubKeyHex, isPrivate, signer, createdAt, onReady)
}
fun removeTag(earlierVersion: PeopleListEvent, key: String, tag: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (PeopleListEvent) -> Unit) {
earlierVersion.isTagged(key, tag, isPrivate, signer) { isTagged ->
if (isTagged) {
if (isPrivate) {
earlierVersion.privateTagsOrEmpty(signer) { privateTags ->
encryptTags(
privateTags = privateTags.filter { it.size > 1 && !(it[0] == key && it[1] == tag) },
signer = signer
) { encryptedTags ->
create(
content = encryptedTags,
tags = earlierVersion.tags.filter { it.size > 1 && !(it[0] == key && it[1] == tag) },
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
} else {
create(
content = earlierVersion.content,
tags = earlierVersion.tags.filter { it.size > 1 && !(it[0] == key && it[1] == tag) },
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
}
}
fun removeTag(earlierVersion: PeopleListEvent, tag: String, isPrivate: Boolean, pubKey: HexKey, encryptedContent: String, createdAt: Long = TimeUtils.now()): PeopleListEvent {
return if (isPrivate) {
create(
content = encryptedContent,
tags = earlierVersion.tags,
pubKey = pubKey,
createdAt = createdAt
)
} else {
create(
content = earlierVersion.content,
tags = earlierVersion.tags.filter { it.size > 1 && it[1] != tag },
pubKey = pubKey,
createdAt = createdAt
)
}
}
fun addWord(earlierVersion: PeopleListEvent, word: String, isPrivate: Boolean, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): PeopleListEvent {
return addTag(earlierVersion, "word", word, isPrivate, privateKey, createdAt)
}
fun addUser(earlierVersion: PeopleListEvent, pubKeyHex: String, isPrivate: Boolean, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): PeopleListEvent {
return addTag(earlierVersion, "p", pubKeyHex, isPrivate, privateKey, createdAt)
}
fun addTag(earlierVersion: PeopleListEvent, key: String, tag: String, isPrivate: Boolean, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): PeopleListEvent {
if (earlierVersion.isTagged(key, tag, isPrivate, privateKey)) return earlierVersion
return if (isPrivate) {
create(
content = encryptTags(
privateTags = earlierVersion.privateTagsOrEmpty(privKey = privateKey).plus(element = listOf(key, tag)),
privateKey = privateKey
),
tags = earlierVersion.tags,
privateKey = privateKey,
createdAt = createdAt
)
} else {
create(
content = earlierVersion.content,
tags = earlierVersion.tags.plus(element = listOf(key, tag)),
privateKey = privateKey,
createdAt = createdAt
)
}
}
fun removeWord(earlierVersion: PeopleListEvent, word: String, isPrivate: Boolean, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): PeopleListEvent {
return removeTag(earlierVersion, "word", word, isPrivate, privateKey, createdAt)
}
fun removeUser(earlierVersion: PeopleListEvent, pubKeyHex: String, isPrivate: Boolean, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): PeopleListEvent {
return removeTag(earlierVersion, "p", pubKeyHex, isPrivate, privateKey, createdAt)
}
fun removeTag(earlierVersion: PeopleListEvent, key: String, tag: String, isPrivate: Boolean, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): PeopleListEvent {
if (!earlierVersion.isTagged(key, tag, isPrivate, privateKey)) return earlierVersion
return if (isPrivate) {
create(
content = encryptTags(
privateTags = earlierVersion.privateTagsOrEmpty(privKey = privateKey).filter { it.size > 1 && !(it[0] == key && it[1] == tag) },
privateKey = privateKey
),
tags = earlierVersion.tags.filter { it.size > 1 && !(it[0] == key && it[1] == tag) },
privateKey = privateKey,
createdAt = createdAt
)
} else {
create(
content = earlierVersion.content,
tags = earlierVersion.tags.filter { it.size > 1 && !(it[0] == key && it[1] == tag) },
privateKey = privateKey,
createdAt = createdAt
)
}
}
fun create(content: String, tags: List<List<String>>, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): PeopleListEvent {
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey)
return PeopleListEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
}
fun create(content: String, tags: List<List<String>>, pubKey: HexKey, createdAt: Long = TimeUtils.now()): PeopleListEvent {
val id = generateId(pubKey, createdAt, kind, tags, content)
return PeopleListEvent(id.toHexKey(), pubKey, createdAt, tags, content, "")
}
fun create(unsignedEvent: PeopleListEvent, signature: String): PeopleListEvent {
return PeopleListEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
fun create(
content: String,
tags: List<List<String>>,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (PeopleListEvent) -> Unit
) {
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class PinListEvent(
@@ -24,18 +25,16 @@ class PinListEvent(
fun create(
pins: List<String>,
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): PinListEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (PinListEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
pins.forEach {
tags.add(listOf("pin", it))
}
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, "")
val sig = CryptoUtils.sign(id, privateKey)
return PinListEvent(id.toHexKey(), pubKey, createdAt, tags, "", sig.toHexKey())
signer.sign(createdAt, kind, tags, "", onReady)
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
const val POLL_OPTION = "poll_option"
const val VALUE_MAXIMUM = "value_maximum"
@@ -48,8 +49,7 @@ class PollNoteEvent(
replyTos: List<String>?,
mentions: List<String>?,
addresses: List<ATag>?,
pubKey: HexKey,
privateKey: ByteArray?,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
pollOptions: Map<Int, String>,
valueMaximum: Int?,
@@ -59,8 +59,9 @@ class PollNoteEvent(
zapReceiver: List<ZapSplitSetup>? = null,
markAsSensitive: Boolean,
zapRaiserAmount: Long?,
geohash: String? = null
): PollNoteEvent {
geohash: String? = null,
onReady: (PollNoteEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
replyTos?.forEach {
tags.add(listOf("e", it))
@@ -99,15 +100,7 @@ class PollNoteEvent(
tags.add(listOf("g", it))
}
val id = generateId(pubKey, createdAt, kind, tags, msg)
val sig = if (privateKey == null) null else CryptoUtils.sign(id, privateKey)
return PollNoteEvent(id.toHexKey(), pubKey, createdAt, tags, msg, sig?.toHexKey() ?: "")
}
fun create(
unsignedEvent: PollNoteEvent, signature: String
): PollNoteEvent {
return PollNoteEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
signer.sign(createdAt, kind, tags, msg, onReady)
}
}
}
@@ -9,7 +9,10 @@ import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.HexValidator
import com.vitorpamplona.quartz.encoders.Hex
import com.vitorpamplona.quartz.encoders.hexToByteArray
import com.vitorpamplona.quartz.signers.NostrSigner
import kotlinx.collections.immutable.persistentSetOf
import java.util.UUID
@Immutable
class PrivateDmEvent(
@@ -20,6 +23,9 @@ class PrivateDmEvent(
content: String,
sig: HexKey
) : Event(id, pubKey, createdAt, kind, tags, content, sig), ChatroomKeyable {
@Transient
private var decryptedContent: Map<HexKey, String> = mapOf()
/**
* This may or may not be the actual recipient's pub key. The event is intended to look like a
* nip-04 EncryptedDmEvent but may omit the recipient, too. This value can be queried and used
@@ -59,20 +65,26 @@ class PrivateDmEvent(
tags.any { it.size > 1 && it[0] == "p" && it[1] == pubkeyHex }
}
fun plainContent(privKey: ByteArray, pubKey: ByteArray): String? {
return try {
val sharedSecret = CryptoUtils.getSharedSecretNIP04(privKey, pubKey)
fun cachedContentFor(signer: NostrSigner): String? {
return decryptedContent[signer.pubKey]
}
val retVal = CryptoUtils.decryptNIP04(content, sharedSecret)
fun plainContent(signer: NostrSigner, onReady: (String) -> Unit) {
decryptedContent[signer.pubKey]?.let {
onReady(it)
return
}
if (retVal.startsWith(nip18Advertisement)) {
signer.nip04Decrypt(content, talkingWith(signer.pubKey)) { retVal ->
val content = if (retVal.startsWith(nip18Advertisement)) {
retVal.substring(16)
} else {
retVal
}
} catch (e: Exception) {
Log.w("PrivateDM", "Error decrypting the message ${e.message}")
null
decryptedContent = decryptedContent + Pair(signer.pubKey, content)
onReady(content)
}
}
@@ -82,28 +94,24 @@ class PrivateDmEvent(
const val nip18Advertisement = "[//]: # (nip18)\n"
fun create(
recipientPubKey: ByteArray,
recipientPubKey: HexKey,
msg: String,
replyTos: List<String>? = null,
mentions: List<String>? = null,
zapReceiver: List<ZapSplitSetup>? = null,
keyPair: KeyPair,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
publishedRecipientPubKey: ByteArray? = null,
publishedRecipientPubKey: HexKey? = null,
advertiseNip18: Boolean = true,
markAsSensitive: Boolean,
zapRaiserAmount: Long?,
geohash: String? = null
): PrivateDmEvent {
geohash: String? = null,
onReady: (PrivateDmEvent) -> Unit
) {
val message = if (advertiseNip18) { nip18Advertisement } else { "" } + msg
val content = if (keyPair.privKey == null) message else CryptoUtils.encryptNIP04(
message,
keyPair.privKey,
recipientPubKey
)
val tags = mutableListOf<List<String>>()
publishedRecipientPubKey?.let {
tags.add(listOf("p", publishedRecipientPubKey.toHexKey()))
tags.add(listOf("p", publishedRecipientPubKey))
}
replyTos?.forEach {
tags.add(listOf("e", it))
@@ -124,60 +132,9 @@ class PrivateDmEvent(
tags.add(listOf("g", it))
}
val pubKey = keyPair.pubKey.toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return PrivateDmEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
}
fun createWithoutSignature(
msg: String,
replyTos: List<String>? = null,
mentions: List<String>? = null,
zapReceiver: List<ZapSplitSetup>? = null,
keyPair: KeyPair,
createdAt: Long = TimeUtils.now(),
publishedRecipientPubKey: ByteArray? = null,
advertiseNip18: Boolean = true,
markAsSensitive: Boolean,
zapRaiserAmount: Long?,
geohash: String? = null
): PrivateDmEvent {
val message = if (advertiseNip18) { nip18Advertisement } else { "" } + msg
val content = message
val tags = mutableListOf<List<String>>()
publishedRecipientPubKey?.let {
tags.add(listOf("p", publishedRecipientPubKey.toHexKey()))
signer.nip04Encrypt(message, recipientPubKey) { content ->
signer.sign(createdAt, kind, tags, content, onReady)
}
replyTos?.forEach {
tags.add(listOf("e", it))
}
mentions?.forEach {
tags.add(listOf("p", it))
}
zapReceiver?.forEach {
tags.add(listOf("zap", it.lnAddressOrPubKeyHex, it.relay ?: "", it.weight.toString()))
}
if (markAsSensitive) {
tags.add(listOf("content-warning", ""))
}
zapRaiserAmount?.let {
tags.add(listOf("zapraiser", "$it"))
}
geohash?.let {
tags.add(listOf("g", it))
}
val pubKey = keyPair.pubKey.toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, content)
return PrivateDmEvent(id.toHexKey(), pubKey, createdAt, tags, content, "")
}
fun create(
unsignedEvent: PrivateDmEvent,
signature: String,
): PrivateDmEvent {
return PrivateDmEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class ReactionEvent(
@@ -23,29 +24,25 @@ class ReactionEvent(
companion object {
const val kind = 7
fun createWarning(originalNote: EventInterface, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ReactionEvent {
return create("\u26A0\uFE0F", originalNote, keyPair, createdAt)
fun createWarning(originalNote: EventInterface, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (ReactionEvent) -> Unit) {
return create("\u26A0\uFE0F", originalNote, signer, createdAt, onReady)
}
fun createLike(originalNote: EventInterface, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ReactionEvent {
return create("+", originalNote, keyPair, createdAt)
fun createLike(originalNote: EventInterface, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (ReactionEvent) -> Unit) {
return create("+", originalNote, signer, createdAt, onReady)
}
fun create(content: String, originalNote: EventInterface, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ReactionEvent {
fun create(content: String, originalNote: EventInterface, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (ReactionEvent) -> Unit) {
var tags = listOf(listOf("e", originalNote.id()), listOf("p", originalNote.pubKey()))
if (originalNote is AddressableEvent) {
tags = tags + listOf(listOf("a", originalNote.address().toTag()))
}
val pubKey = keyPair.pubKey.toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return ReactionEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
return signer.sign(createdAt, kind, tags, content, onReady)
}
fun create(emojiUrl: EmojiUrl, originalNote: EventInterface, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ReactionEvent {
fun create(emojiUrl: EmojiUrl, originalNote: EventInterface, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (ReactionEvent) -> Unit) {
val content = ":${emojiUrl.code}:"
val pubKey = keyPair.pubKey.toHexKey()
var tags = listOf(
listOf("e", originalNote.id()),
@@ -57,13 +54,7 @@ class ReactionEvent(
tags = tags + listOf(listOf("a", originalNote.address().toTag()))
}
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return ReactionEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
}
fun create(unsignedEvent: ReactionEvent, signature: String): ReactionEvent {
return ReactionEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -5,6 +5,7 @@ import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
import java.net.URI
@Immutable
@@ -22,13 +23,15 @@ class RecommendRelayEvent(
companion object {
const val kind = 2
fun create(relay: URI, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): RecommendRelayEvent {
fun create(
relay: URI,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (RecommendRelayEvent) -> Unit
) {
val content = relay.toString()
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val tags = listOf<List<String>>()
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey)
return RecommendRelayEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -5,6 +5,7 @@ import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class RelayAuthEvent(
@@ -21,20 +22,19 @@ class RelayAuthEvent(
companion object {
const val kind = 22242
fun create(relay: String, challenge: String, pubKey: HexKey, privateKey: ByteArray?, createdAt: Long = TimeUtils.now()): RelayAuthEvent {
fun create(
relay: String,
challenge: String,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (RelayAuthEvent) -> Unit
) {
val content = ""
val tags = listOf(
listOf("relay", relay),
listOf("challenge", challenge)
)
val localPubKey = if (pubKey.isBlank() && privateKey != null) CryptoUtils.pubkeyCreate(privateKey).toHexKey() else pubKey
val id = generateId(localPubKey, createdAt, kind, tags, content)
val sig = if (privateKey == null) null else CryptoUtils.sign(id, privateKey)
return RelayAuthEvent(id.toHexKey(), localPubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
}
fun create(unsignedEvent: RelayAuthEvent, signature: String): RelayAuthEvent {
return RelayAuthEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class RelaySetEvent(
@@ -24,18 +25,16 @@ class RelaySetEvent(
fun create(
relays: List<String>,
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): RelaySetEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (RelaySetEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
relays.forEach {
tags.add(listOf("r", it))
}
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, "")
val sig = CryptoUtils.sign(id, privateKey)
return RelaySetEvent(id.toHexKey(), pubKey, createdAt, tags, "", sig.toHexKey())
signer.sign(createdAt, kind, tags, "", onReady)
}
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
data class ReportedKey(val key: String, val reportType: ReportEvent.ReportType)
@@ -57,35 +58,36 @@ class ReportEvent(
fun create(
reportedPost: EventInterface,
type: ReportType,
keyPair: KeyPair,
signer: NostrSigner,
content: String = "",
createdAt: Long = TimeUtils.now()
): ReportEvent {
createdAt: Long = TimeUtils.now(),
onReady: (ReportEvent) -> Unit
) {
val reportPostTag = listOf("e", reportedPost.id(), type.name.lowercase())
val reportAuthorTag = listOf("p", reportedPost.pubKey(), type.name.lowercase())
val pubKey = keyPair.pubKey.toHexKey()
var tags: List<List<String>> = listOf(reportPostTag, reportAuthorTag)
if (reportedPost is AddressableEvent) {
tags = tags + listOf(listOf("a", reportedPost.address().toTag()))
}
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return ReportEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
signer.sign(createdAt, kind, tags, content, onReady)
}
fun create(reportedUser: String, type: ReportType, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): ReportEvent {
fun create(
reportedUser: String,
type: ReportType,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (ReportEvent) -> Unit
) {
val content = ""
val reportAuthorTag = listOf("p", reportedUser, type.name.lowercase())
val pubKey = keyPair.pubKey.toHexKey()
val tags: List<List<String>> = listOf(reportAuthorTag)
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return ReportEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
signer.sign(createdAt, kind, tags, content, onReady)
}
}
@@ -6,6 +6,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class RepostEvent(
@@ -29,26 +30,24 @@ class RepostEvent(
companion object {
const val kind = 6
fun create(boostedPost: EventInterface, keyPair: KeyPair, createdAt: Long = TimeUtils.now()): RepostEvent {
fun create(
boostedPost: EventInterface,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (RepostEvent) -> Unit
) {
val content = boostedPost.toJson()
val replyToPost = listOf("e", boostedPost.id())
val replyToAuthor = listOf("p", boostedPost.pubKey())
val pubKey = keyPair.pubKey.toHexKey()
var tags: List<List<String>> = listOf(replyToPost, replyToAuthor)
if (boostedPost is AddressableEvent) {
tags = tags + listOf(listOf("a", boostedPost.address().toTag()))
}
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return RepostEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig?.toHexKey() ?: "")
}
fun create(unsignedEvent: RepostEvent, signature: String): RepostEvent {
return RepostEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
@@ -11,6 +11,8 @@ import com.vitorpamplona.quartz.crypto.Nip44Version
import com.vitorpamplona.quartz.crypto.decodeNIP44
import com.vitorpamplona.quartz.crypto.encodeNIP44
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
import java.util.UUID
@Immutable
class SealedGossipEvent(
@@ -24,119 +26,66 @@ class SealedGossipEvent(
@Transient
private var cachedInnerEvent: Map<HexKey, Event?> = mapOf()
fun cachedGossip(privKey: ByteArray): Event? {
val hex = privKey.toHexKey()
if (cachedInnerEvent.contains(hex)) return cachedInnerEvent[hex]
val gossip = unseal(privKey = privKey)
val event = gossip?.mergeWith(this)
if (event is WrappedEvent) {
event.host = host ?: this
fun cachedGossip(signer: NostrSigner, onReady: (Event) -> Unit) {
cachedInnerEvent[signer.pubKey]?.let {
onReady(it)
return
}
cachedInnerEvent = cachedInnerEvent + Pair(hex, event)
return event
}
unseal(signer) { gossip ->
val event = gossip.mergeWith(this)
if (event is WrappedEvent) {
event.host = host ?: this
}
fun cachedGossip(pubKey: ByteArray, decryptedContent: String): Event? {
val hex = pubKey.toHexKey()
if (cachedInnerEvent.contains(hex)) return cachedInnerEvent[hex]
val gossip = unseal(decryptedContent)
val event = gossip?.mergeWith(this)
if (event is WrappedEvent) {
event.host = host ?: this
cachedInnerEvent = cachedInnerEvent + Pair(signer.pubKey, event)
onReady(event)
}
cachedInnerEvent = cachedInnerEvent + Pair(hex, event)
return event
}
fun unseal(privKey: ByteArray): Gossip? = try {
plainContent(privKey)?.let { Gossip.fromJson(it) }
} catch (e: Exception) {
Log.w("GossipEvent", "Fail to decrypt or parse Gossip", e)
null
}
fun unseal(decryptedContent: String): Gossip? = try {
plainContent(decryptedContent)?.let { Gossip.fromJson(it) }
} catch (e: Exception) {
Log.w("GossipEvent", "Fail to decrypt or parse Gossip", e)
null
}
private fun plainContent(decryptedContent: String): String? {
if (decryptedContent.isEmpty()) return null
return decryptedContent
}
private fun plainContent(privKey: ByteArray): String? {
if (content.isEmpty()) return null
return try {
val toDecrypt = decodeNIP44(content) ?: return null
return when (toDecrypt.v) {
Nip44Version.NIP04.versionCode -> CryptoUtils.decryptNIP04(toDecrypt, privKey, pubKey.hexToByteArray())
Nip44Version.NIP44.versionCode -> CryptoUtils.decryptNIP44(toDecrypt, privKey, pubKey.hexToByteArray())
else -> null
private fun unseal(signer: NostrSigner, onReady: (Gossip) -> Unit) {
try {
plainContent(signer) {
onReady(Gossip.fromJson(it))
}
} catch (e: Exception) {
Log.w("GossipEvent", "Error decrypting the message ${e.message}")
null
Log.w("GossipEvent", "Fail to decrypt or parse Gossip", e)
}
}
private fun plainContent(signer: NostrSigner, onReady: (String) -> Unit) {
if (content.isEmpty()) return
signer.nip44Decrypt(content, pubKey, onReady)
}
companion object {
const val kind = 13
fun create(
event: Event,
encryptTo: HexKey,
privateKey: ByteArray,
createdAt: Long = TimeUtils.now()
): SealedGossipEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (SealedGossipEvent) -> Unit
) {
val gossip = Gossip.create(event)
return create(gossip, encryptTo, privateKey, createdAt)
create(gossip, encryptTo, signer, createdAt, onReady)
}
fun create(
gossip: Gossip,
encryptTo: HexKey,
privateKey: ByteArray,
createdAt: Long = TimeUtils.randomWithinAWeek()
): SealedGossipEvent {
val sharedSecret = CryptoUtils.getSharedSecretNIP44(privateKey, encryptTo.hexToByteArray())
val content = encodeNIP44(
CryptoUtils.encryptNIP44(
Gossip.toJson(gossip),
sharedSecret
)
)
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
signer: NostrSigner,
createdAt: Long = TimeUtils.randomWithinAWeek(),
onReady: (SealedGossipEvent) -> Unit
) {
val msg = Gossip.toJson(gossip)
val tags = listOf<List<String>>()
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, privateKey)
return SealedGossipEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
}
fun create(
encryptedContent: String,
pubKey: HexKey,
createdAt: Long = TimeUtils.randomWithinAWeek()
): SealedGossipEvent {
val tags = listOf<List<String>>()
val id = generateId(pubKey, createdAt, kind, tags, encryptedContent)
return SealedGossipEvent(id.toHexKey(), pubKey, createdAt, tags, encryptedContent, "")
}
fun create(
unsignedEvent: SealedGossipEvent,
signature: String
): SealedGossipEvent {
return SealedGossipEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
signer.nip44Encrypt(msg, encryptTo) { content ->
signer.sign(createdAt, kind, tags, content, onReady)
}
}
}
}
@@ -7,6 +7,7 @@ import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class StatusEvent(
@@ -25,51 +26,38 @@ class StatusEvent(
msg: String,
type: String,
expiration: Long?,
keyPair: KeyPair,
createdAt: Long = TimeUtils.now()
): StatusEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (StatusEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
tags.add(listOf("d", type))
expiration?.let { tags.add(listOf("expiration", it.toString())) }
val pubKey = keyPair.pubKey.toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, msg)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return StatusEvent(id.toHexKey(), pubKey, createdAt, tags, msg, sig?.toHexKey() ?: "")
signer.sign(createdAt, kind, tags, msg, onReady)
}
fun update(
event: StatusEvent,
newStatus: String,
keyPair: KeyPair,
createdAt: Long = TimeUtils.now()
): StatusEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (StatusEvent) -> Unit
) {
val tags = event.tags
val pubKey = event.pubKey()
val id = generateId(pubKey, createdAt, kind, tags, newStatus)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return StatusEvent(id.toHexKey(), pubKey, createdAt, tags, newStatus, sig?.toHexKey() ?: "")
signer.sign(createdAt, kind, tags, newStatus, onReady)
}
fun clear(
event: StatusEvent,
keyPair: KeyPair,
createdAt: Long = TimeUtils.now()
): StatusEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (StatusEvent) -> Unit
) {
val msg = ""
val tags = event.tags.filter { it.size > 1 && it[0] == "d" }
val pubKey = event.pubKey()
val id = generateId(pubKey, createdAt, kind, tags, msg)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return StatusEvent(id.toHexKey(), pubKey, createdAt, tags, msg, sig?.toHexKey() ?: "")
}
fun create(
unsignedEvent: StatusEvent,
signature: String
): StatusEvent {
return StatusEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
signer.sign(createdAt, kind, tags, msg, onReady)
}
}
}
@@ -9,6 +9,7 @@ import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner
@Immutable
class TextNoteEvent(
@@ -38,9 +39,10 @@ class TextNoteEvent(
root: String?,
directMentions: Set<HexKey>,
geohash: String? = null,
keyPair: KeyPair,
createdAt: Long = TimeUtils.now()
): TextNoteEvent {
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
onReady: (TextNoteEvent) -> Unit
) {
val tags = mutableListOf<List<String>>()
replyTos?.forEach {
if (it == root) {
@@ -95,17 +97,7 @@ class TextNoteEvent(
tags.add(listOf("g", it))
}
val pubKey = keyPair.pubKey.toHexKey()
val id = generateId(pubKey, createdAt, kind, tags, msg)
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
return TextNoteEvent(id.toHexKey(), pubKey, createdAt, tags, msg, sig?.toHexKey() ?: "")
}
fun create(
unsignedEvent: TextNoteEvent, signature: String
): TextNoteEvent {
return TextNoteEvent(unsignedEvent.id, unsignedEvent.pubKey, unsignedEvent.createdAt, unsignedEvent.tags, unsignedEvent.content, signature)
signer.sign(createdAt, kind, tags, msg, onReady)
}
}
}
@@ -0,0 +1,200 @@
package com.vitorpamplona.quartz.signers
import android.content.ContentResolver
import android.content.Intent
import android.net.Uri
import android.util.Log
import android.util.LruCache
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.events.EventInterface
import com.vitorpamplona.quartz.events.LnZapRequestEvent
enum class SignerType {
SIGN_EVENT,
NIP04_ENCRYPT,
NIP04_DECRYPT,
NIP44_ENCRYPT,
NIP44_DECRYPT,
GET_PUBLIC_KEY,
DECRYPT_ZAP_EVENT
}
class ExternalSignerLauncher(
private val npub: String,
private val signerPackageName: String = "com.greenart7c3.nostrsigner"
) {
private val contentCache = LruCache<String, (String) -> Unit>(20)
private var signerAppLauncher: ((Intent) -> Unit)? = null
private var contentResolver: (() -> ContentResolver)? = null
/**
* Call this function when the launcher becomes available on activity, fragment or compose
*/
fun registerLauncher(
launcher: ((Intent) -> Unit),
contentResolver: (() -> ContentResolver),
) {
this.signerAppLauncher = launcher
this.contentResolver = contentResolver
}
/**
* Call this function when the activity is destroyed or is about to be replaced.
*/
fun clearLauncher() {
this.signerAppLauncher = null
this.contentResolver = null
}
fun newResult(data: Intent) {
val signature = data.getStringExtra("signature") ?: ""
val id = data.getStringExtra("id") ?: ""
if (id.isNotBlank()) {
contentCache.get(id)?.invoke(signature)
}
}
fun openSignerApp(
data: String,
type: SignerType,
pubKey: HexKey,
id: String,
onReady: (String)-> Unit
) {
signerAppLauncher?.let {
openSignerApp(
data, type, it, pubKey, id, onReady
)
}
}
private fun openSignerApp(
data: String,
type: SignerType,
intentLauncher: (Intent) -> Unit,
pubKey: HexKey,
id: String,
onReady: (String)-> Unit
) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:$data"))
val signerType = when (type) {
SignerType.SIGN_EVENT -> "sign_event"
SignerType.NIP04_ENCRYPT -> "nip04_encrypt"
SignerType.NIP04_DECRYPT -> "nip04_decrypt"
SignerType.NIP44_ENCRYPT -> "nip44_encrypt"
SignerType.NIP44_DECRYPT -> "nip44_decrypt"
SignerType.GET_PUBLIC_KEY -> "get_public_key"
SignerType.DECRYPT_ZAP_EVENT -> "decrypt_zap_event"
}
intent.putExtra("type", signerType)
intent.putExtra("pubKey", pubKey)
intent.putExtra("id", id)
if (type !== SignerType.GET_PUBLIC_KEY) {
intent.putExtra("current_user", npub)
}
intent.`package` = signerPackageName
contentCache.put(id, onReady)
intentLauncher(intent)
}
fun openSigner(event: EventInterface, columnName: String = "signature", onReady: (String)-> Unit) {
val result = getDataFromResolver(SignerType.SIGN_EVENT, arrayOf(event.toJson(), event.pubKey()), columnName)
if (result == null) {
openSignerApp(
event.toJson(),
SignerType.SIGN_EVENT,
"",
event.id(),
onReady
)
} else {
onReady(result)
}
}
fun getDataFromResolver(signerType: SignerType, data: Array<out String>, columnName: String = "signature"): String? {
return contentResolver?.let { it() }?.let {
getDataFromResolver(signerType, data, columnName, it)
}
}
fun getDataFromResolver(signerType: SignerType, data: Array<out String>, columnName: String = "signature", contentResolver: ContentResolver): String? {
val localData = if (signerType !== SignerType.GET_PUBLIC_KEY) {
data.toList().plus(npub).toTypedArray()
} else {
data
}
contentResolver.query(
Uri.parse("content://${signerPackageName}.$signerType"),
localData,
null,
null,
null
).use {
if (it == null) {
return null
}
if (it.moveToFirst()) {
val index = it.getColumnIndex(columnName)
if (index < 0) {
Log.d("getDataFromResolver", "column '$columnName' not found")
return null
}
return it.getString(index)
}
}
return null
}
fun decrypt(encryptedContent: String, pubKey: HexKey, signerType: SignerType = SignerType.NIP04_DECRYPT, onReady: (String)-> Unit) {
val id = (encryptedContent + pubKey).hashCode().toString()
val result = getDataFromResolver(signerType, arrayOf(encryptedContent, pubKey))
if (result == null) {
openSignerApp(
encryptedContent,
signerType,
pubKey,
id,
onReady
)
} else {
onReady(result)
}
}
fun encrypt(decryptedContent: String, pubKey: HexKey, signerType: SignerType = SignerType.NIP04_ENCRYPT, onReady: (String)-> Unit) {
val id = (decryptedContent + pubKey).hashCode().toString()
val result = getDataFromResolver(signerType, arrayOf(decryptedContent, pubKey))
if (result == null) {
openSignerApp(
decryptedContent,
signerType,
pubKey,
id,
onReady
)
} else {
onReady(result)
}
}
fun decryptZapEvent(event: LnZapRequestEvent, onReady: (String)-> Unit) {
val result = getDataFromResolver(SignerType.DECRYPT_ZAP_EVENT, arrayOf(event.toJson(), event.pubKey))
if (result == null) {
openSignerApp(
event.toJson(),
SignerType.DECRYPT_ZAP_EVENT,
event.pubKey,
event.id,
onReady
)
} else {
onReady(result)
}
}
}
@@ -0,0 +1,23 @@
package com.vitorpamplona.quartz.signers
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.EventFactory
import com.vitorpamplona.quartz.events.LnZapRequestEvent
import com.vitorpamplona.quartz.events.PeopleListEvent
import kotlinx.collections.immutable.ImmutableSet
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
abstract class NostrSigner(val pubKey: HexKey) {
abstract fun <T: Event> sign(createdAt: Long, kind: Int, tags: List<List<String>>, content: String, onReady: (T) -> Unit)
abstract fun nip04Encrypt(decryptedContent: String, toPublicKey: HexKey, onReady: (String)-> Unit)
abstract fun nip04Decrypt(encryptedContent: String, fromPublicKey: HexKey, onReady: (String)-> Unit)
abstract fun nip44Encrypt(decryptedContent: String, toPublicKey: HexKey, onReady: (String)-> Unit)
abstract fun nip44Decrypt(encryptedContent: String, fromPublicKey: HexKey, onReady: (String)-> Unit)
abstract fun decryptZapEvent(event: LnZapRequestEvent, onReady: (Event)-> Unit)
}
@@ -0,0 +1,110 @@
package com.vitorpamplona.quartz.signers
import android.util.Log
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.hexToByteArray
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.encoders.toNpub
import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.EventFactory
import com.vitorpamplona.quartz.events.LnZapRequestEvent
class NostrSignerExternal(
pubKey: HexKey,
val launcher: ExternalSignerLauncher = ExternalSignerLauncher(pubKey.hexToByteArray().toNpub()),
): NostrSigner(pubKey) {
override fun <T: Event> sign(
createdAt: Long,
kind: Int,
tags: List<List<String>>,
content: String,
onReady: (T) -> Unit
) {
val id = Event.generateId(pubKey, createdAt, kind, tags, content).toHexKey()
val event = Event(
id = id,
pubKey = pubKey,
createdAt = createdAt,
kind = kind,
tags = tags,
content = content,
sig = ""
)
launcher.openSigner(event) { signature ->
(EventFactory.create(
event.id,
event.pubKey,
event.createdAt,
event.kind,
event.tags,
event.content,
signature
) as? T?)?.let {
onReady(it)
}
}
}
override fun nip04Encrypt(decryptedContent: String, toPublicKey: HexKey, onReady: (String)-> Unit) {
Log.d("NostrExternalSigner", "Encrypt NIP04 Event: ${decryptedContent}")
return launcher.encrypt(
decryptedContent,
toPublicKey,
SignerType.NIP04_ENCRYPT,
onReady
)
}
override fun nip04Decrypt(encryptedContent: String, fromPublicKey: HexKey, onReady: (String)-> Unit) {
Log.d("NostrExternalSigner", "Decrypt NIP04 Event: ${encryptedContent}")
return launcher.decrypt(
encryptedContent,
fromPublicKey,
SignerType.NIP04_DECRYPT,
onReady
)
}
override fun nip44Encrypt(decryptedContent: String, toPublicKey: HexKey, onReady: (String)-> Unit) {
Log.d("NostrExternalSigner", "Encrypt NIP44 Event: ${decryptedContent}")
return launcher.encrypt(
decryptedContent,
pubKey,
SignerType.NIP44_ENCRYPT,
onReady
)
}
override fun nip44Decrypt(encryptedContent: String, fromPublicKey: HexKey, onReady: (String)-> Unit) {
Log.d("NostrExternalSigner", "Decrypt NIP44 Event: ${encryptedContent}")
return launcher.decrypt(
encryptedContent,
pubKey,
SignerType.NIP44_DECRYPT,
onReady
)
}
override fun decryptZapEvent(event: LnZapRequestEvent, onReady: (Event)-> Unit) {
return launcher.decryptZapEvent(event) {
val event = try {
Event.fromJson(it)
} catch( e: Exception) {
Log.e("NostrExternalSigner", "Unable to parse returned decrypted Zap: ${it}")
null
}
event?.let {
onReady(event)
}
}
}
}
@@ -0,0 +1,218 @@
package com.vitorpamplona.quartz.signers
import android.util.Log
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.crypto.KeyPair
import com.vitorpamplona.quartz.crypto.Nip44Version
import com.vitorpamplona.quartz.crypto.decodeNIP44
import com.vitorpamplona.quartz.crypto.encodeNIP44
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.hexToByteArray
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.EventFactory
import com.vitorpamplona.quartz.events.LnZapPrivateEvent
import com.vitorpamplona.quartz.events.LnZapRequestEvent
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class NostrSignerInternal(val keyPair: KeyPair): NostrSigner(keyPair.pubKey.toHexKey()) {
override fun <T: Event> sign(
createdAt: Long,
kind: Int,
tags: List<List<String>>,
content: String,
onReady: (T)-> Unit
) {
if (keyPair.privKey == null) return
if (isUnsignedPrivateEvent(kind, tags)) {
// this is a private zap
signPrivateZap(createdAt, kind, tags, content, onReady)
} else {
signNormal(createdAt, kind, tags, content, onReady)
}
}
fun isUnsignedPrivateEvent(
kind: Int,
tags: List<List<String>>,
): Boolean {
return kind == LnZapRequestEvent.kind && tags.any { t -> t.size > 1 && t[0] == "anon" && t[1].isBlank() }
}
fun <T: Event> signNormal(
createdAt: Long,
kind: Int,
tags: List<List<String>>,
content: String,
onReady: (T)-> Unit
) {
if (keyPair.privKey == null) return
val id = Event.generateId(pubKey, createdAt, kind, tags, content)
val sig = CryptoUtils.sign(id, keyPair.privKey).toHexKey()
onReady(
EventFactory.create(
id.toHexKey(),
pubKey,
createdAt,
kind,
tags,
content,
sig
) as T // Must never crash
)
}
override fun nip04Encrypt(decryptedContent: String, toPublicKey: HexKey, onReady: (String)-> Unit) {
if (keyPair.privKey == null) return
onReady(
CryptoUtils.encryptNIP04(
decryptedContent,
keyPair.privKey,
toPublicKey.hexToByteArray()
)
)
}
override fun nip04Decrypt(encryptedContent: String, fromPublicKey: HexKey, onReady: (String)-> Unit) {
if (keyPair.privKey == null) return
try {
val sharedSecret = CryptoUtils.getSharedSecretNIP04(keyPair.privKey, fromPublicKey.hexToByteArray())
onReady(CryptoUtils.decryptNIP04(encryptedContent, sharedSecret))
} catch (e: Exception) {
Log.w("NIP04Decrypt", "Error decrypting the message ${e.message} on ${encryptedContent}")
}
}
override fun nip44Encrypt(decryptedContent: String, toPublicKey: HexKey, onReady: (String)-> Unit) {
if (keyPair.privKey == null) return
val sharedSecret = CryptoUtils.getSharedSecretNIP44(keyPair.privKey, toPublicKey.hexToByteArray())
onReady(
encodeNIP44(
CryptoUtils.encryptNIP44(
decryptedContent,
sharedSecret
)
)
)
}
override fun nip44Decrypt(encryptedContent: String, fromPublicKey: HexKey, onReady: (String)-> Unit) {
if (keyPair.privKey == null) return
val toDecrypt = decodeNIP44(encryptedContent) ?: return
when (toDecrypt.v) {
Nip44Version.NIP04.versionCode -> CryptoUtils.decryptNIP04(toDecrypt, keyPair.privKey, fromPublicKey.hexToByteArray())
Nip44Version.NIP44.versionCode -> CryptoUtils.decryptNIP44(toDecrypt, keyPair.privKey, fromPublicKey.hexToByteArray())
else -> null
}?.let {
onReady(it)
}
}
private fun <T> signPrivateZap(
createdAt: Long,
kind: Int,
tags: List<List<String>>,
content: String,
onReady: (T)-> Unit
) {
if (keyPair.privKey == null) return
val zappedEvent = tags.firstOrNull { it.size > 1 && it[0] == "e" }?.let { it[1] }
val userHex = tags.firstOrNull { it.size > 1 && it[0] == "p" }?.let { it[1] } ?: return
// if it is a Zap for an Event, use event.id if not, use the user's pubkey
val idToGeneratePrivateKey = zappedEvent ?: userHex
val encryptionPrivateKey =
LnZapRequestEvent.createEncryptionPrivateKey(keyPair.privKey.toHexKey(), idToGeneratePrivateKey, createdAt)
val fullTagsNoAnon = tags.filter { t -> t.getOrNull(0) != "anon" }
LnZapPrivateEvent.create(this, fullTagsNoAnon, content) {
val noteJson = it.toJson()
val encryptedContent = LnZapRequestEvent.encryptPrivateZapMessage(
noteJson,
encryptionPrivateKey,
userHex.hexToByteArray()
)
val newTags = tags.filter { t -> t.getOrNull(0) != "anon" } + listOf(listOf("anon", encryptedContent))
val newContent = ""
NostrSignerInternal(KeyPair(encryptionPrivateKey)).signNormal(createdAt, kind, newTags, newContent, onReady)
}
}
override fun decryptZapEvent(event: LnZapRequestEvent, onReady: (Event)-> Unit) {
if (keyPair.privKey == null) return
val recipientPK = event.zappedAuthor().firstOrNull()
val recipientPost = event.zappedPost().firstOrNull()
val privateEvent = if (recipientPK == pubKey) {
// if the receiver is logged in, these are the params.
val privateKeyToUse = keyPair.privKey
val pubkeyToUse = event.pubKey
event.getPrivateZapEvent(privateKeyToUse, pubkeyToUse)
} else {
// if the sender is logged in, these are the params
val altPubkeyToUse = recipientPK
val altPrivateKeyToUse = if (recipientPost != null) {
LnZapRequestEvent.createEncryptionPrivateKey(
keyPair.privKey.toHexKey(),
recipientPost,
event.createdAt
)
} else if (recipientPK != null) {
LnZapRequestEvent.createEncryptionPrivateKey(
keyPair.privKey.toHexKey(),
recipientPK,
event.createdAt
)
} else {
null
}
try {
if (altPrivateKeyToUse != null && altPubkeyToUse != null) {
val altPubKeyFromPrivate = CryptoUtils.pubkeyCreate(altPrivateKeyToUse).toHexKey()
if (altPubKeyFromPrivate == event.pubKey) {
val result = event.getPrivateZapEvent(altPrivateKeyToUse, altPubkeyToUse)
if (result == null) {
Log.w(
"Private ZAP Decrypt",
"Fail to decrypt Zap from ${event.id}"
)
}
result
} else {
null
}
} else {
null
}
} catch (e: Exception) {
Log.e("Account", "Failed to create pubkey for ZapRequest ${event.id}", e)
null
}
}
privateEvent?.let {
onReady(it)
}
}
}