Adds a Bloom-based hint indexer with MurMur hash

This commit is contained in:
Vitor Pamplona 2025-02-20 14:05:58 -05:00
parent 94e0f4ed02
commit aaf86bf53e
53 changed files with 5981 additions and 43 deletions

View File

@ -75,11 +75,11 @@ import com.vitorpamplona.quartz.experimental.profileGallery.dimension
import com.vitorpamplona.quartz.experimental.profileGallery.fromEvent
import com.vitorpamplona.quartz.experimental.profileGallery.hash
import com.vitorpamplona.quartz.experimental.profileGallery.mimeType
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate

View File

@ -40,10 +40,10 @@ import com.vitorpamplona.ammolite.relays.filters.EOSETime
import com.vitorpamplona.quartz.experimental.bounties.addedRewardValue
import com.vitorpamplona.quartz.experimental.bounties.hasAdditionalReward
import com.vitorpamplona.quartz.lightning.LnInvoiceUtil
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address

View File

@ -26,7 +26,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.PublicChatChannel
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent

View File

@ -66,10 +66,10 @@ import com.vitorpamplona.quartz.experimental.zapPolls.consensusThreshold
import com.vitorpamplona.quartz.experimental.zapPolls.maxAmount
import com.vitorpamplona.quartz.experimental.zapPolls.minAmount
import com.vitorpamplona.quartz.experimental.zapPolls.tags.PollOptionTag
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.eTags
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohash

View File

@ -165,7 +165,7 @@ import com.vitorpamplona.amethyst.ui.theme.ZeroPadding
import com.vitorpamplona.amethyst.ui.theme.innerPostModifier
import com.vitorpamplona.amethyst.ui.theme.liveStreamTag
import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.isTaggedEvent
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hasHashtags

View File

@ -0,0 +1,135 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.benchmark
import androidx.benchmark.junit4.BenchmarkRule
import androidx.benchmark.junit4.measureRepeated
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
import com.vitorpamplona.quartz.nip01Core.hints.bloom.BloomFilterMurMur3
import com.vitorpamplona.quartz.utils.RandomInstance
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class BloomFilterMurMur3Benchmark {
@get:Rule val benchmarkRule = BenchmarkRule()
val testEncoded = "100:10:AKiEIEQKALgRACEABA==:3"
val key1 = "ca29c211f1c72d5b6622268ff43d2288ea2b2cb5b9aa196ff9f1704fc914b71b".hexToByteArray()
val key2 = "460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c".hexToByteArray()
val key3 = "560c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c".hexToByteArray()
val keys =
mutableListOf<ByteArray>().apply {
for (seed in 0..1000000) {
add(RandomInstance.bytes(32))
}
}
val keys2 =
mutableListOf<ByteArray>().apply {
for (seed in 0..1000000) {
add(RandomInstance.bytes(32))
}
}
@Test
fun addExisting() {
val filter = BloomFilterMurMur3.decode(testEncoded)
benchmarkRule.measureRepeated {
filter.add(key1)
}
}
@Test
fun addNew() {
val filter = BloomFilterMurMur3.decode(testEncoded)
benchmarkRule.measureRepeated {
filter.add(key3)
}
}
@Test
fun mightContainTrue() {
val filter = BloomFilterMurMur3(10_000_000, 5)
filter.add(key1)
keys.forEach(filter::add)
benchmarkRule.measureRepeated {
filter.mightContain(key1)
}
}
@Test
fun mightContainFalse() {
val filter = BloomFilterMurMur3(10_000_000, 5)
keys.forEach(filter::add)
benchmarkRule.measureRepeated {
filter.mightContain(key3)
}
}
@Test
fun decode() {
benchmarkRule.measureRepeated {
BloomFilterMurMur3.decode(testEncoded)
}
}
@Test
fun encode() {
val filter = BloomFilterMurMur3.decode(testEncoded)
benchmarkRule.measureRepeated {
filter.encode()
}
}
@Test
fun largeFilterBuild() {
val bloomFilter = BloomFilterMurMur3(10_000_000, 5)
benchmarkRule.measureRepeated {
keys.forEach(bloomFilter::add)
}
}
@Test
fun largeFilterCheckExisting() {
val bloomFilter = BloomFilterMurMur3(10_000_000, 5)
keys.forEach(bloomFilter::add)
benchmarkRule.measureRepeated {
keys.forEach(bloomFilter::mightContain)
}
}
@Test
fun largeFilterCheckNew() {
val bloomFilter = BloomFilterMurMur3(10_000_000, 5)
keys.forEach(bloomFilter::add)
benchmarkRule.measureRepeated {
keys2.forEach(bloomFilter::mightContain)
}
}
}

View File

@ -0,0 +1,88 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.benchmark
import androidx.benchmark.junit4.BenchmarkRule
import androidx.benchmark.junit4.measureRepeated
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.vitorpamplona.quartz.nip01Core.hints.HintIndexer
import com.vitorpamplona.quartz.utils.RandomInstance
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.nio.charset.Charset
@RunWith(AndroidJUnit4::class)
class HintIndexerBenchmark {
@get:Rule val benchmarkRule = BenchmarkRule()
val keys =
mutableListOf<ByteArray>().apply {
for (seed in 0..1_000_000) {
add(RandomInstance.bytes(32))
}
}
val relays =
getInstrumentation()
.context.assets
.open("relayDB.txt")
.readBytes()
.toString(Charset.forName("utf-8"))
.split("\n")
@Test
fun relayUriHashcode() {
benchmarkRule.measureRepeated {
"wss://relay.bitcoin.social".hashCode()
}
}
@Test
fun getRelayHints() {
val indexer = HintIndexer()
keys.forEach { key ->
(0..5).map {
indexer.index(key, relays.random())
}
}
val key = keys.random()
benchmarkRule.measureRepeated {
indexer.get(key)
}
}
@Test
fun buildIndexer() {
benchmarkRule.measureRepeated {
val indexer = HintIndexer()
keys.forEach { key ->
(0..5).map {
indexer.index(key, relays.random())
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -49,13 +49,12 @@ class BloomFilter(
fun add(value: HexKey) = add(value.hexToByteArray())
fun print(): String {
val builder = StringBuilder()
for (seed in 0 until bits.size()) {
builder.append(if (bits.get(seed)) "1" else "0")
fun print() =
buildString {
for (seed in 0 until bits.size()) {
append(if (bits.get(seed)) "1" else "0")
}
}
return builder.toString()
}
fun add(value: ByteArray) {
lock.write {

View File

@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.experimental.bounties
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtag
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag

View File

@ -22,11 +22,11 @@ package com.vitorpamplona.quartz.experimental.nip95.header
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.core.any
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.eTag

View File

@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core
package com.vitorpamplona.quartz.nip01Core.hints
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Event

View File

@ -0,0 +1,49 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.hints
import com.vitorpamplona.quartz.nip01Core.hints.bloom.BloomFilterMurMur3
/**
* Instead of having one bloom filter per relay, which could create many
* large filters for very few events, this class uses only one mega bloom
* filter and uses the hashcode of the relay uri as differentiator in salt.
*/
class HintIndexer(
size: Int = 10_000_000,
rounds: Int = 5,
) {
private val bloomFilter = BloomFilterMurMur3(size, rounds)
private val relayDB = hashSetOf<String>()
fun index(
id: ByteArray,
relay: String,
) {
relayDB.add(relay)
bloomFilter.add(id, relay.hashCode())
}
fun get(id: ByteArray): List<String> =
relayDB.filter {
bloomFilter.mightContain(id, it.hashCode())
}
}

View File

@ -0,0 +1,33 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.hints
import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint
interface HintProvider {
fun eventHints(): EventIdHint
fun addressIdHints(): AddressHint
fun pubKeyHints(): PubKeyHint
}

View File

@ -0,0 +1,30 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.hints.bloom
import java.util.BitSet
fun BitSet.printBits() =
buildString {
for (seed in 0 until size()) {
append(if (this@printBits.get(seed)) "1" else "0")
}
}

View File

@ -0,0 +1,83 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.hints.bloom
import com.vitorpamplona.quartz.utils.RandomInstance
import java.util.Base64
import java.util.BitSet
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write
class BloomFilterMurMur3(
private val size: Int,
private val rounds: Int,
private val bits: BitSet = BitSet(size),
private val commonSalt: Int = RandomInstance.int(),
) {
private val hasher = MurmurHash3()
private val lock = ReentrantReadWriteLock()
fun add(
value: ByteArray,
salt: Int = commonSalt,
) {
lock.write {
repeat(rounds) {
bits.set(hash(value, salt + it))
}
}
}
fun mightContain(
value: ByteArray,
salt: Int = commonSalt,
): Boolean {
lock.read {
repeat(rounds) {
if (!bits.get(hash(value, salt + it))) return false
}
return true
}
}
private fun hash(
value: ByteArray,
seed: Int,
) = hasher.hash(value, seed).mod(size)
fun encode() = encode(this)
fun printBits() = bits.printBits()
companion object {
fun encode(f: BloomFilterMurMur3): String {
val bitSetB64 = Base64.getEncoder().encodeToString(f.bits.toByteArray())
return "${f.size}:${f.rounds}:$bitSetB64:${f.commonSalt}"
}
fun decode(encodedStr: String): BloomFilterMurMur3 {
val (sizeStr, roundsStr, filterB64, salt) = encodedStr.split(":")
val bitSet = BitSet.valueOf(Base64.getDecoder().decode(filterB64))
return BloomFilterMurMur3(sizeStr.toInt(), roundsStr.toInt(), bitSet, salt.toInt())
}
}
}

View File

@ -0,0 +1,121 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.hints.bloom
class MurmurHash3 {
fun hash(
data: ByteArray,
seed: Int,
) = hash(data, 0, data.size, seed)
/**
* Generates 32 bit hash .
* @param data the byte array to hash
* @param offset the start offset of the data in the array (always 0)
* @param length the length of the data in the array
* @param seed the seed for the hash (int)
* @return 32 bit hash of the given array
*/
fun hash(
data: ByteArray,
offset: Int,
length: Int,
seed: Int,
): Int {
val c1 = -0x3361d2af // 0xcc9e2d51
val c2 = 0x1b873593
var h1 = seed
val roundedEnd = offset + (length and 0xFFFFFFFC.toInt()) // Round down to 4-byte blocks
var i = offset
while (i < roundedEnd) {
var k1 =
(data[i].toInt() and 0xFF) or
((data[i + 1].toInt() and 0xFF) shl 8) or
((data[i + 2].toInt() and 0xFF) shl 16) or
((data[i + 3].toInt() and 0xFF) shl 24)
i += 4
k1 *= c1
k1 = Integer.rotateLeft(k1, 15)
k1 *= c2
h1 = h1 xor k1
h1 = Integer.rotateLeft(h1, 13)
h1 = h1 * 5 + -0x19ab949c // 0xe6546b64
}
// processing tail (remaining bytes)
var k1 = 0
when (length and 3) {
3 -> {
k1 = k1 or ((data[i + 2].toInt() and 0xFF) shl 16)
k1 = k1 or ((data[i + 1].toInt() and 0xFF) shl 8)
k1 = k1 or (data[i].toInt() and 0xFF)
k1 *= c1
k1 = Integer.rotateLeft(k1, 15)
k1 *= c2
h1 = h1 xor k1
}
2 -> {
k1 = k1 or (data[i + 1].toInt() and 0xFF shl 8)
k1 = k1 or (data[i].toInt() and 0xFF)
k1 *= c1
k1 = Integer.rotateLeft(k1, 15)
k1 *= c2
h1 = h1 xor k1
}
1 -> {
k1 = k1 or (data[i].toInt() and 0xFF)
k1 *= c1
k1 = Integer.rotateLeft(k1, 15)
k1 *= c2
h1 = h1 xor k1
}
}
// final mix
h1 = h1 xor length
h1 = fmix32(h1)
return h1
}
private fun fmix32(h: Int): Int {
var f = h
f = f xor (f ushr 16)
f *= -0x7a143595 // 0x85ebca6b
f = f xor (f ushr 13)
f *= -0x3d4d51cb // 0xc2b2ae35
f = f xor (f ushr 16)
return f
}
}

View File

@ -0,0 +1,28 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.hints.types
class AddressHint(
val addressId: String,
var relay: String? = null,
) : Hint {
override fun id() = addressId.toByteArray(Charsets.UTF_8)
}

View File

@ -0,0 +1,31 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.hints.types
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
class EventIdHint(
val eventId: HexKey,
var relay: String? = null,
) : Hint {
override fun id() = eventId.hexToByteArray()
}

View File

@ -0,0 +1,25 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.hints.types
interface Hint {
fun id(): ByteArray
}

View File

@ -0,0 +1,31 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.hints.types
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
class PubKeyHint(
val pubkey: HexKey,
var relay: String? = null,
) : Hint {
override fun id() = pubkey.hexToByteArray()
}

View File

@ -21,11 +21,11 @@
package com.vitorpamplona.quartz.nip03Timestamp
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.eTag

View File

@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.nip04Dm.messages
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.tags.events.EventReference
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag

View File

@ -21,9 +21,9 @@
package com.vitorpamplona.quartz.nip10Notes
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip10Notes.tags.markedETags
import com.vitorpamplona.quartz.nip10Notes.tags.prepareETagsAsReplyTo

View File

@ -20,7 +20,7 @@
*/
package com.vitorpamplona.quartz.nip10Notes.tags
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent

View File

@ -20,9 +20,9 @@
*/
package com.vitorpamplona.quartz.nip17Dm
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent

View File

@ -21,9 +21,9 @@
package com.vitorpamplona.quartz.nip17Dm.files
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag

View File

@ -20,9 +20,9 @@
*/
package com.vitorpamplona.quartz.nip17Dm.files
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTags

View File

@ -21,9 +21,9 @@
package com.vitorpamplona.quartz.nip17Dm.messages
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag

View File

@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.nip17Dm.messages
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTags

View File

@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.nip21UriScheme
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip19Bech32.toNIP19
fun Event.toNostrUri(): String = "nostr:${toNIP19()}"

View File

@ -21,11 +21,11 @@
package com.vitorpamplona.quartz.nip22Comments
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
import com.vitorpamplona.quartz.nip21UriScheme.toNostrUri

View File

@ -21,10 +21,10 @@
package com.vitorpamplona.quartz.nip25Reactions
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.aTag
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag

View File

@ -21,9 +21,9 @@
package com.vitorpamplona.quartz.nip28PublicChat.admin
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.eTags

View File

@ -21,9 +21,9 @@
package com.vitorpamplona.quartz.nip28PublicChat.admin
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip28PublicChat.base.BasePublicChatEvent

View File

@ -21,9 +21,9 @@
package com.vitorpamplona.quartz.nip28PublicChat.admin
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTags

View File

@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.nip28PublicChat.base
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag

View File

@ -21,9 +21,9 @@
package com.vitorpamplona.quartz.nip28PublicChat.message
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent

View File

@ -21,10 +21,10 @@
package com.vitorpamplona.quartz.nip30CustomEmoji.selection
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.signers.eventUpdate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address

View File

@ -21,9 +21,9 @@
package com.vitorpamplona.quartz.nip34Git.issue
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags

View File

@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.nip34Git.issue
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTags

View File

@ -21,9 +21,9 @@
package com.vitorpamplona.quartz.nip34Git.reply
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent

View File

@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.nip34Git.reply
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTags

View File

@ -21,9 +21,9 @@
package com.vitorpamplona.quartz.nip35Torrents
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag

View File

@ -21,9 +21,9 @@
package com.vitorpamplona.quartz.nip53LiveActivities.chat
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent

View File

@ -20,8 +20,8 @@
*/
package com.vitorpamplona.quartz.nip53LiveActivities.chat
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag

View File

@ -22,10 +22,10 @@ package com.vitorpamplona.quartz.nip72ModCommunities.approval
import android.util.Log
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedATags
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedAddresses

View File

@ -20,10 +20,10 @@
*/
package com.vitorpamplona.quartz.nip72ModCommunities.approval
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent
fun TagArrayBuilder<CommunityPostApprovalEvent>.community(event: EventHintBundle<CommunityDefinitionEvent>) = add(event.toATag().toATagArray())

View File

@ -21,12 +21,12 @@
package com.vitorpamplona.quartz.nip75ZapGoals
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
import com.vitorpamplona.quartz.nip01Core.tags.references.reference

View File

@ -25,7 +25,7 @@ import java.security.SecureRandom
object RandomInstance {
private val randomizer = SecureRandom()
fun int(bound: Int) = randomizer.nextInt(bound)
fun int(bound: Int = Int.MAX_VALUE) = randomizer.nextInt(bound)
fun bytes(size: Int) = ByteArray(size).also { randomizer.nextBytes(it) }
}

View File

@ -0,0 +1,114 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.hints
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
import com.vitorpamplona.quartz.nip01Core.hints.bloom.BloomFilterMurMur3
import com.vitorpamplona.quartz.utils.RandomInstance
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertFalse
import junit.framework.TestCase.assertTrue
import org.junit.Test
class BloomFilterMurMur3Test {
val testEncoded = "100:10:AKiEIEQKALgRACEABA==:3"
val testInBinary = "00000000000101010010000100000100001000100101000000000000000111011000100000000000100001000000000000100000000000000000000000000000"
val key1 = "ca29c211f1c72d5b6622268ff43d2288ea2b2cb5b9aa196ff9f1704fc914b71b".hexToByteArray()
val key2 = "460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c".hexToByteArray()
val key3 = "560c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c".hexToByteArray()
val keys =
mutableListOf<ByteArray>().apply {
for (seed in 0..1_000_000) {
add(RandomInstance.bytes(32))
}
}
@Test
fun testCreate() {
val bloomFilter = BloomFilterMurMur3(100, 10, commonSalt = 3)
bloomFilter.add(key1)
bloomFilter.add(key2)
assertEquals(testEncoded, bloomFilter.encode())
assertEquals(testInBinary, bloomFilter.printBits())
assertTrue(bloomFilter.mightContain(key1))
assertTrue(bloomFilter.mightContain(key2))
assertFalse(bloomFilter.mightContain(key3))
}
@Test
fun testDecoding() {
val bloomFilter = BloomFilterMurMur3.decode(testEncoded)
assertEquals(testEncoded, bloomFilter.encode())
assertEquals(testInBinary, bloomFilter.printBits())
assertTrue(bloomFilter.mightContain(key1))
assertTrue(bloomFilter.mightContain(key2))
assertFalse(bloomFilter.mightContain(key3))
}
@Test
fun runProb() {
val bloomFilter = BloomFilterMurMur3.decode(testEncoded)
var failureCounter = 0
repeat(1_000_000) {
if (bloomFilter.mightContain(RandomInstance.bytes(32))) {
failureCounter++
}
}
assertEquals(0, failureCounter)
}
@Test
fun runProb2() {
val bloomFilter = BloomFilterMurMur3(10_000_000, 4)
keys.forEach(bloomFilter::add)
var failureCounter = 0
repeat(1_000_000) {
if (bloomFilter.mightContain(RandomInstance.bytes(32))) {
failureCounter++
}
}
assertTrue("Failures $failureCounter ${failureCounter / 1_000_000.0}", failureCounter / 1_000_000.0f < 0.015)
}
@Test
fun runProb3() {
val bloomFilter = BloomFilterMurMur3(10_000_000, 4)
keys.forEach(bloomFilter::add)
var failureCounter = 0
repeat(1_000_000) {
if (bloomFilter.mightContain(RandomInstance.bytes(32))) {
failureCounter++
}
}
assertTrue("Failures $failureCounter ${failureCounter / 1_000_000.0}", failureCounter / 1_000_000.0f < 0.015)
}
}

View File

@ -0,0 +1,93 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.hints
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint
import com.vitorpamplona.quartz.utils.RandomInstance
import junit.framework.TestCase.assertTrue
import org.junit.Test
class HintIndexerTest {
val key1 = "ca29c211f1c72d5b6622268ff43d2288ea2b2cb5b9aa196ff9f1704fc914b71b".hexToByteArray()
val key2 = "460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c".hexToByteArray()
val key3 = "560c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c".hexToByteArray()
val keys =
mutableListOf<HexKey>().apply {
for (seed in 0..1_000_000) {
add(RandomInstance.bytes(32).toHexKey())
}
}
val relays =
this.javaClass
.getResourceAsStream("relayDB.txt")
?.readAllBytes()
.toString()
.split("\n")
val keyHints =
keys
.map { key ->
(0..5).map { PubKeyHint(key, relays.random()) }
}.flatten()
val key1Relays = (0..5).map { relays.random() }
val key2Relays = (0..4).map { relays.random() }
val key3Relays = (0..3).map { relays.random() }
@Test
fun runExistingKeys() {
val indexer = HintIndexer()
keyHints.forEach {
indexer.index(it.id(), it.relay!!)
}
var failureCounter = 0
repeat(1000) {
val key = keys.random()
if (indexer.get(key.hexToByteArray()).isEmpty()) {
failureCounter++
}
}
assertTrue("Failures $failureCounter ${failureCounter / 1_000.0}", failureCounter / 1_000.0f < 0.015)
}
@Test
fun runProb() {
val indexer = HintIndexer()
keyHints.forEach {
indexer.index(it.id(), it.relay!!)
}
var failureCounter = 0
repeat(1_000) {
if (indexer.get(RandomInstance.bytes(32)).isNotEmpty()) {
failureCounter++
}
}
assertTrue("Failures $failureCounter ${failureCounter / 1_000.0}", failureCounter / 1_000.0f < 0.015)
}
}

File diff suppressed because it is too large Load Diff