Massive refactoring to unify internal signer and the Amber signer.
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-13
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user