- Migrates Quartz to a KMP project

- Converts OpenTimestamps from java to Kotlin
- Moves OTS OkHttp setup to Quartz
This commit is contained in:
Vitor Pamplona
2025-08-27 17:32:39 -04:00
parent d2214889b6
commit 442f75fc41
979 changed files with 6285 additions and 5706 deletions

1
.gitignore vendored
View File

@@ -15,6 +15,7 @@
/.idea/other.xml
/.idea/runConfigurations.xml
/.idea/ChatHistory_schema_v2.xml
/.idea/artifacts/*
.DS_Store
/build
/captures

View File

@@ -21,14 +21,14 @@
package com.vitorpamplona.amethyst
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.vitorpamplona.amethyst.service.ots.OkHttpBitcoinExplorer
import com.vitorpamplona.amethyst.service.ots.OkHttpCalendarBuilder
import com.vitorpamplona.amethyst.service.ots.OtsBlockHeightCache
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
import com.vitorpamplona.quartz.nip01Core.jackson.JsonMapper
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
import com.vitorpamplona.quartz.nip03Timestamp.OtsEvent
import com.vitorpamplona.quartz.nip03Timestamp.OtsResolver
import com.vitorpamplona.quartz.nip03Timestamp.ots.okhttp.OkHttpBitcoinExplorer
import com.vitorpamplona.quartz.nip03Timestamp.ots.okhttp.OkHttpCalendarBuilder
import com.vitorpamplona.quartz.nip03Timestamp.ots.okhttp.OtsBlockHeightCache
import junit.framework.TestCase.assertEquals
import kotlinx.coroutines.runBlocking
import okhttp3.OkHttpClient

View File

@@ -38,7 +38,6 @@ import com.vitorpamplona.amethyst.service.okhttp.DualHttpClientManager
import com.vitorpamplona.amethyst.service.okhttp.EncryptionKeyCache
import com.vitorpamplona.amethyst.service.okhttp.OkHttpWebSocket
import com.vitorpamplona.amethyst.service.okhttp.ProxySettingsAnchor
import com.vitorpamplona.amethyst.service.ots.OtsBlockHeightCache
import com.vitorpamplona.amethyst.service.playback.diskCache.VideoCache
import com.vitorpamplona.amethyst.service.playback.diskCache.VideoCacheFactory
import com.vitorpamplona.amethyst.service.relayClient.CacheClientConnector
@@ -51,6 +50,7 @@ import com.vitorpamplona.amethyst.service.uploads.nip95.Nip95CacheFactory
import com.vitorpamplona.amethyst.ui.tor.TorManager
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip03Timestamp.VerificationStateCache
import com.vitorpamplona.quartz.nip03Timestamp.ots.okhttp.OtsBlockHeightCache
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers

View File

@@ -89,7 +89,6 @@ import com.vitorpamplona.amethyst.model.topNavFeeds.IFeedTopNavFilter
import com.vitorpamplona.amethyst.model.topNavFeeds.OutboxLoaderState
import com.vitorpamplona.amethyst.model.torState.TorRelayState
import com.vitorpamplona.amethyst.service.location.LocationState
import com.vitorpamplona.amethyst.service.ots.OkHttpOtsResolverBuilder
import com.vitorpamplona.amethyst.service.uploads.FileHeader
import com.vitorpamplona.quartz.experimental.bounties.BountyAddValueEvent
import com.vitorpamplona.quartz.experimental.edits.TextNoteModificationEvent
@@ -136,6 +135,7 @@ import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
import com.vitorpamplona.quartz.nip01Core.tags.people.hasAnyTaggedUser
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUserIds
import com.vitorpamplona.quartz.nip01Core.tags.references.references
import com.vitorpamplona.quartz.nip03Timestamp.ots.okhttp.OkHttpOtsResolverBuilder
import com.vitorpamplona.quartz.nip04Dm.PrivateDMCache
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
@@ -323,7 +323,9 @@ class Account(
val otsResolverBuilder: OkHttpOtsResolverBuilder =
OkHttpOtsResolverBuilder(
Amethyst.instance.okHttpClients,
{
Amethyst.instance.okHttpClients.getHttpClient(privacyState.shouldUseTorForMoneyOperations(it))
},
privacyState::shouldUseTorForMoneyOperations,
Amethyst.instance.otsBlockHeightCache,
)

View File

@@ -24,10 +24,10 @@ import android.util.Log
import com.vitorpamplona.amethyst.model.AccountSettings
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.ots.OkHttpOtsResolverBuilder
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip03Timestamp.OtsEvent
import com.vitorpamplona.quartz.nip03Timestamp.ots.okhttp.OkHttpOtsResolverBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.update
import java.util.Base64

View File

@@ -54,10 +54,10 @@ dependencies {
implementation libs.androidx.core.ktx
// Compose's @Immutable and @Stable classes
implementation libs.androidx.runtime.runtime
implementation libs.androidx.compose.runtime
implementation project(path: ':quartz')
testImplementation libs.junit
androidTestImplementation libs.androidx.junit

View File

@@ -7,6 +7,8 @@ plugins {
alias(libs.plugins.diffplugSpotless)
alias(libs.plugins.googleServices) apply false
alias(libs.plugins.jetbrainsComposeCompiler) apply false
alias(libs.plugins.kotlinMultiplatform) apply false
alias(libs.plugins.androidKotlinMultiplatformLibrary) apply false
}
allprojects {
@@ -39,9 +41,18 @@ allprojects {
subprojects {
afterEvaluate {
try {
tasks.named("preBuild") {
dependsOn("spotlessApply")
}
} catch (UnknownTaskException ignored) {
tasks.matching {
it.name.startsWith("pre") && it.name.endsWith("Build")
}.configureEach {
dependsOn("spotlessApply")
}
}
}
}

View File

@@ -51,7 +51,6 @@ composeCompiler {
dependencies {
implementation project(path: ':quartz')
// Import @Immutable and @Stable
implementation platform(libs.androidx.compose.bom)
implementation libs.androidx.ui

View File

@@ -54,6 +54,9 @@ zxing = "3.5.3"
zxingAndroidEmbedded = "4.3.0"
windowCoreAndroid = "1.4.0"
androidxCamera = "1.4.2"
kotlinStdlib = "2.2.10"
kotlinTest = "2.2.10"
core = "1.7.0"
[libraries]
abedElazizShe-image-compressor = { group = "com.github.AbedElazizShe", name = "LightCompressor", version.ref = "lightcompressor" }
@@ -69,11 +72,12 @@ androidx-camera-extensions = { module = "androidx.camera:camera-extensions", ver
androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "androidxCamera" }
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "androidxCamera" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" }
androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragmentKtx" }
androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidxJunit" }
androidx-junit-ktx = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "androidxJunit" }
androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycleRuntimeKtx" }
@@ -89,7 +93,6 @@ androidx-media3-session = { group = "androidx.media3", name = "media3-session",
androidx-media3-ui = { group = "androidx.media3", name = "media3-ui", version.ref = "media3" }
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
androidx-runner = { group = "androidx.test", name = "runner", version.ref = "runner" }
androidx-runtime-runtime = { group = "androidx.compose.runtime", name = "runtime" }
androidx-security-crypto-ktx = { group = "androidx.security", name = "security-crypto-ktx", version.ref = "securityCryptoKtx" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
@@ -121,8 +124,12 @@ kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-cor
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
okhttpCoroutines = { group = "com.squareup.okhttp3", name = "okhttp-coroutines", version.ref = "okhttp" }
rfc3986-normalizer = { group = "org.czeal", name = "rfc3986", version.ref = "rfc3986" }
secp256k1-kmp-common = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp", version.ref = "secp256k1KmpJniAndroid" }
secp256k1-kmp-jni-android = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp-jni-android", version.ref = "secp256k1KmpJniAndroid" }
secp256k1-kmp-jni-jvm = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp-jni-jvm", version.ref = "secp256k1KmpJniAndroid" }
secp256k1-kmp-jni-jvm-linux = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp-jni-jvm-linux", version.ref = "secp256k1KmpJniAndroid" }
secp256k1-kmp-jni-jvm-darwin = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp-jni-jvm-darwin", version.ref = "secp256k1KmpJniAndroid" }
secp256k1-kmp-jni-jvm-mingw = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp-jni-jvm-mingw", version.ref = "secp256k1KmpJniAndroid" }
tor-android = { module = "info.guardianproject:tor-android", version.ref = "torAndroid" }
unifiedpush = { group = "com.github.UnifiedPush", name = "android-connector", version.ref = "unifiedpush" }
url-detector = { group = "io.github.url-detector", name = "url-detector", version.ref = "urlDetector" }
@@ -135,6 +142,9 @@ zoomable = { group = "net.engawapg.lib", name = "zoomable", version.ref = "zooma
zxing = { group = "com.google.zxing", name = "core", version.ref = "zxing" }
zxing-embedded = { group = "com.journeyapps", name = "zxing-android-embedded", version.ref = "zxingAndroidEmbedded" }
androidx-window-core-android = { group = "androidx.window", name = "window-core-android", version.ref = "windowCoreAndroid" }
kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlinStdlib" }
kotlin-test = { group = "org.jetbrains.kotlin", name = "kotlin-test", version.ref = "kotlinTest" }
androidx-core = { group = "androidx.test", name = "core", version.ref = "core" }
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
@@ -147,3 +157,5 @@ jetbrainsKotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
jetbrainsComposeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
serialization = { id = 'org.jetbrains.kotlin.plugin.serialization', version.ref = 'kotlinxSerializationPlugin' }
parcelize = { id = "kotlin-parcelize" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
androidKotlinMultiplatformLibrary = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" }

View File

@@ -1,88 +0,0 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.androidLibrary)
alias(libs.plugins.jetbrainsKotlinAndroid)
alias(libs.plugins.parcelize)
}
android {
namespace = 'com.vitorpamplona.quartz'
compileSdk = libs.versions.android.compileSdk.get().toInteger()
defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInteger()
targetSdk = libs.versions.android.targetSdk.get().toInteger()
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled = true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
create("benchmark") {
initWith(getByName("release"))
signingConfig = signingConfigs.debug
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_21
targetCompatibility JavaVersion.VERSION_21
}
packagingOptions {
resources {
excludes += ['**/libscrypt.dylib']
}
}
}
kotlin {
compilerOptions {
jvmTarget = JvmTarget.JVM_21
freeCompilerArgs.add("-Xstring-concat=inline")
}
}
dependencies {
implementation libs.androidx.core.ktx
implementation platform(libs.androidx.compose.bom)
// @Immutable and @Stable
implementation libs.androidx.runtime.runtime
// Bitcoin secp256k1 bindings to Android
api libs.secp256k1.kmp.jni.android
// LibSodium for ChaCha encryption (NIP-44)
// Wait for @aar support in version catalogs
// implementation 'com.goterl:lazysodium-android:5.2.0@aar'
// implementation 'net.java.dev.jna:jna:5.17.0@aar'
implementation variantOf(libs.lazysodium.android) { artifactType("aar") }
implementation variantOf(libs.jna) { artifactType("aar") }
// Performant Parser of JSONs into Events
api libs.jackson.module.kotlin
// immutable collections to avoid recomposition
api libs.kotlinx.collections.immutable
// Parses URLs from Text:
api libs.url.detector
// Normalizes URLs
api libs.rfc3986.normalizer
// Websockets API
implementation libs.okhttp
testImplementation libs.junit
testImplementation libs.secp256k1.kmp.jni.jvm
androidTestImplementation platform(libs.androidx.compose.bom)
androidTestImplementation libs.androidx.junit
androidTestImplementation libs.androidx.espresso.core
}

157
quartz/build.gradle.kts Normal file
View File

@@ -0,0 +1,157 @@
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidKotlinMultiplatformLibrary)
}
kotlin {
jvm()
// Target declarations - add or remove as needed below. These define
// which platforms this KMP module supports.
// See: https://kotlinlang.org/docs/multiplatform-discover-project.html#targets
androidLibrary {
namespace = "com.vitorpamplona.quartz"
compileSdk = libs.versions.android.compileSdk.get().toInt()
minSdk = libs.versions.android.minSdk.get().toInt()
withHostTestBuilder {
}
withDeviceTestBuilder {
sourceSetTreeName = "test"
}.configure {
instrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
}
compilerOptions {
freeCompilerArgs.add("-Xstring-concat=inline")
}
// For iOS targets, this is also where you should
// configure native binary output. For more information, see:
// https://kotlinlang.org/docs/multiplatform-build-native-binaries.html#build-xcframeworks
// A step-by-step guide on how to include this library in an XCode
// project can be found here:
// https://developer.android.com/kotlin/multiplatform/migrate
val xcfName = "quartz-kmpKit"
iosX64 {
binaries.framework {
baseName = xcfName
}
}
iosArm64 {
binaries.framework {
baseName = xcfName
}
}
iosSimulatorArm64 {
binaries.framework {
baseName = xcfName
}
}
// Source set declarations.
// Declaring a target automatically creates a source set with the same name. By default, the
// Kotlin Gradle Plugin creates additional source sets that depend on each other, since it is
// common to share sources between related targets.
// See: https://kotlinlang.org/docs/multiplatform-hierarchy.html
sourceSets {
commonMain {
dependencies {
implementation(libs.kotlin.stdlib)
implementation(project.dependencies.platform(libs.androidx.compose.bom))
// @Immutable and @Stable
implementation(libs.androidx.compose.runtime)
api(libs.secp256k1.kmp.common)
}
}
jvmMain {
dependencies {
// Bitcoin secp256k1 bindings
api(libs.secp256k1.kmp.jni.jvm)
// Performant Parser of JSONs into Events
api(libs.jackson.module.kotlin)
// immutable collections to avoid recomposition
api(libs.kotlinx.collections.immutable)
// Parses URLs from Text:
api(libs.url.detector)
// Normalizes URLs
api(libs.rfc3986.normalizer)
// Websockets API
implementation(libs.okhttp)
}
}
commonTest {
dependencies {
implementation(libs.kotlin.test)
// Bitcoin secp256k1 bindings
api(libs.secp256k1.kmp.jni.jvm)
}
}
androidMain {
dependencies {
implementation(libs.androidx.core.ktx)
// @Immutable and @Stable
implementation(libs.androidx.compose.runtime)
// Bitcoin secp256k1 bindings to Android
api(libs.secp256k1.kmp.jni.android)
// LibSodium for ChaCha encryption (NIP-44)
implementation ("com.goterl:lazysodium-android:5.2.0@aar")
implementation ("net.java.dev.jna:jna:5.17.0@aar")
// Performant Parser of JSONs into Events
api(libs.jackson.module.kotlin)
// immutable collections to avoid recomposition
api(libs.kotlinx.collections.immutable)
// Parses URLs from Text:
api(libs.url.detector)
// Normalizes URLs
api(libs.rfc3986.normalizer)
// Websockets API
implementation(libs.okhttp)
}
}
getByName("androidDeviceTest") {
dependencies {
implementation(libs.androidx.runner)
implementation(libs.androidx.core)
implementation(libs.androidx.junit)
implementation(libs.androidx.espresso.core)
}
}
iosMain {
dependencies {
// Add iOS-specific dependencies here. This a source set created by Kotlin Gradle
// Plugin (KGP) that each specific iOS target (e.g., iosX64) depends on as
// part of KMPs default source set hierarchy. Note that this source set depends
// on common by default and will correctly pull the iOS artifacts of any
// KMP dependencies declared in commonMain.
}
}
}
}

View File

@@ -1,147 +0,0 @@
{
"cells": [
{
"metadata": {},
"cell_type": "markdown",
"source": "## Creating a new Kind 1 post"
},
{
"metadata": {
"jupyter": {
"is_executing": true
}
},
"cell_type": "code",
"source": [
"USE {\n",
" dependencies(\"fr.acinq.secp256k1:secp256k1-kmp-jni-jvm:0.17.3\")\n",
" dependencies(\"com.goterl:lazysodium-java:5.1.4\")\n",
" dependencies(\"net.java.dev.jna:jna:5.17.0\")\n",
"\n",
" import(\"com.vitorpamplona.quartz.*\")\n",
"}"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"metadata": {
"collapsed": true,
"ExecuteTime": {
"end_time": "2025-06-03T14:42:40.051027Z",
"start_time": "2025-06-03T14:42:40.023052Z"
}
},
"source": [
"import fr.acinq.secp256k1.Secp256k1\n",
"\n",
"import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair\n",
"import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal\n",
"import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync\n",
"import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent\n",
"\n",
"val signer = NostrSignerSync()\n",
"val kind1Template = TextNoteEvent.build(\"New kind 1 Post\")\n",
"val signedEvent = signer.sign(kind1Template)\n",
"\n",
"println(signedEvent)"
],
"outputs": [
{
"ename": "org.jetbrains.kotlinx.jupyter.exceptions.ReplCompilerException",
"evalue": "at Cell In[5], line 3, column 43: Unresolved reference: crypto\nat Cell In[5], line 4, column 43: Unresolved reference: signers\nat Cell In[5], line 5, column 43: Unresolved reference: signers\nat Cell In[5], line 6, column 33: Unresolved reference: nip10Notes\nat Cell In[5], line 8, column 14: Unresolved reference: NostrSignerSync\nat Cell In[5], line 9, column 21: Unresolved reference: TextNoteEvent\nat Cell In[5], line 12, column 1: Overload resolution ambiguity: \npublic inline fun println(message: Any?): Unit defined in kotlin.io\npublic inline fun println(message: Boolean): Unit defined in kotlin.io\npublic inline fun println(message: Byte): Unit defined in kotlin.io\npublic inline fun println(message: Char): Unit defined in kotlin.io\npublic inline fun println(message: CharArray): Unit defined in kotlin.io\npublic inline fun println(message: Double): Unit defined in kotlin.io\npublic inline fun println(message: Float): Unit defined in kotlin.io\npublic inline fun println(message: Int): Unit defined in kotlin.io\npublic inline fun println(message: Long): Unit defined in kotlin.io\npublic inline fun println(message: Short): Unit defined in kotlin.io",
"output_type": "error",
"traceback": [
"org.jetbrains.kotlinx.jupyter.exceptions.ReplCompilerException: at Cell In[5], line 3, column 43: Unresolved reference: crypto",
"at Cell In[5], line 4, column 43: Unresolved reference: signers",
"at Cell In[5], line 5, column 43: Unresolved reference: signers",
"at Cell In[5], line 6, column 33: Unresolved reference: nip10Notes",
"at Cell In[5], line 8, column 14: Unresolved reference: NostrSignerSync",
"at Cell In[5], line 9, column 21: Unresolved reference: TextNoteEvent",
"at Cell In[5], line 12, column 1: Overload resolution ambiguity: ",
"public inline fun println(message: Any?): Unit defined in kotlin.io",
"public inline fun println(message: Boolean): Unit defined in kotlin.io",
"public inline fun println(message: Byte): Unit defined in kotlin.io",
"public inline fun println(message: Char): Unit defined in kotlin.io",
"public inline fun println(message: CharArray): Unit defined in kotlin.io",
"public inline fun println(message: Double): Unit defined in kotlin.io",
"public inline fun println(message: Float): Unit defined in kotlin.io",
"public inline fun println(message: Int): Unit defined in kotlin.io",
"public inline fun println(message: Long): Unit defined in kotlin.io",
"public inline fun println(message: Short): Unit defined in kotlin.io",
"\tat org.jetbrains.kotlinx.jupyter.repl.impl.JupyterCompilerImpl.compileSync(JupyterCompilerImpl.kt:208)",
"\tat org.jetbrains.kotlinx.jupyter.repl.impl.InternalEvaluatorImpl.eval(InternalEvaluatorImpl.kt:126)",
"\tat org.jetbrains.kotlinx.jupyter.repl.impl.CellExecutorImpl$execute$1$result$1.invoke(CellExecutorImpl.kt:80)",
"\tat org.jetbrains.kotlinx.jupyter.repl.impl.CellExecutorImpl$execute$1$result$1.invoke(CellExecutorImpl.kt:78)",
"\tat org.jetbrains.kotlinx.jupyter.repl.impl.ReplForJupyterImpl.withHost(ReplForJupyterImpl.kt:791)",
"\tat org.jetbrains.kotlinx.jupyter.repl.impl.CellExecutorImpl.execute-L4Nmkdk(CellExecutorImpl.kt:78)",
"\tat org.jetbrains.kotlinx.jupyter.repl.execution.CellExecutor$DefaultImpls.execute-L4Nmkdk$default(CellExecutor.kt:13)",
"\tat org.jetbrains.kotlinx.jupyter.repl.impl.ReplForJupyterImpl.evaluateUserCode-wNURfNM(ReplForJupyterImpl.kt:613)",
"\tat org.jetbrains.kotlinx.jupyter.repl.impl.ReplForJupyterImpl.evalExImpl(ReplForJupyterImpl.kt:471)",
"\tat org.jetbrains.kotlinx.jupyter.repl.impl.ReplForJupyterImpl.access$evalExImpl(ReplForJupyterImpl.kt:143)",
"\tat org.jetbrains.kotlinx.jupyter.repl.impl.ReplForJupyterImpl$evalEx$1.invoke(ReplForJupyterImpl.kt:464)",
"\tat org.jetbrains.kotlinx.jupyter.repl.impl.ReplForJupyterImpl$evalEx$1.invoke(ReplForJupyterImpl.kt:463)",
"\tat org.jetbrains.kotlinx.jupyter.repl.impl.ReplForJupyterImpl.withEvalContext(ReplForJupyterImpl.kt:444)",
"\tat org.jetbrains.kotlinx.jupyter.repl.impl.ReplForJupyterImpl.evalEx(ReplForJupyterImpl.kt:463)",
"\tat org.jetbrains.kotlinx.jupyter.messaging.IdeCompatibleMessageRequestProcessor$processExecuteRequest$1$response$1$1.invoke(IdeCompatibleMessageRequestProcessor.kt:159)",
"\tat org.jetbrains.kotlinx.jupyter.messaging.IdeCompatibleMessageRequestProcessor$processExecuteRequest$1$response$1$1.invoke(IdeCompatibleMessageRequestProcessor.kt:158)",
"\tat org.jetbrains.kotlinx.jupyter.streams.BlockingSubstitutionEngine.withDataSubstitution(SubstitutionEngine.kt:70)",
"\tat org.jetbrains.kotlinx.jupyter.streams.StreamSubstitutionManager.withSubstitutedStreams(StreamSubstitutionManager.kt:118)",
"\tat org.jetbrains.kotlinx.jupyter.messaging.IdeCompatibleMessageRequestProcessor.withForkedIn(IdeCompatibleMessageRequestProcessor.kt:335)",
"\tat org.jetbrains.kotlinx.jupyter.messaging.IdeCompatibleMessageRequestProcessor.access$withForkedIn(IdeCompatibleMessageRequestProcessor.kt:54)",
"\tat org.jetbrains.kotlinx.jupyter.messaging.IdeCompatibleMessageRequestProcessor$evalWithIO$1$1.invoke(IdeCompatibleMessageRequestProcessor.kt:349)",
"\tat org.jetbrains.kotlinx.jupyter.streams.BlockingSubstitutionEngine.withDataSubstitution(SubstitutionEngine.kt:70)",
"\tat org.jetbrains.kotlinx.jupyter.streams.StreamSubstitutionManager.withSubstitutedStreams(StreamSubstitutionManager.kt:118)",
"\tat org.jetbrains.kotlinx.jupyter.messaging.IdeCompatibleMessageRequestProcessor.withForkedErr(IdeCompatibleMessageRequestProcessor.kt:324)",
"\tat org.jetbrains.kotlinx.jupyter.messaging.IdeCompatibleMessageRequestProcessor.access$withForkedErr(IdeCompatibleMessageRequestProcessor.kt:54)",
"\tat org.jetbrains.kotlinx.jupyter.messaging.IdeCompatibleMessageRequestProcessor$evalWithIO$1.invoke(IdeCompatibleMessageRequestProcessor.kt:348)",
"\tat org.jetbrains.kotlinx.jupyter.streams.BlockingSubstitutionEngine.withDataSubstitution(SubstitutionEngine.kt:70)",
"\tat org.jetbrains.kotlinx.jupyter.streams.StreamSubstitutionManager.withSubstitutedStreams(StreamSubstitutionManager.kt:118)",
"\tat org.jetbrains.kotlinx.jupyter.messaging.IdeCompatibleMessageRequestProcessor.withForkedOut(IdeCompatibleMessageRequestProcessor.kt:316)",
"\tat org.jetbrains.kotlinx.jupyter.messaging.IdeCompatibleMessageRequestProcessor.evalWithIO(IdeCompatibleMessageRequestProcessor.kt:347)",
"\tat org.jetbrains.kotlinx.jupyter.messaging.IdeCompatibleMessageRequestProcessor$processExecuteRequest$1$response$1.invoke(IdeCompatibleMessageRequestProcessor.kt:158)",
"\tat org.jetbrains.kotlinx.jupyter.messaging.IdeCompatibleMessageRequestProcessor$processExecuteRequest$1$response$1.invoke(IdeCompatibleMessageRequestProcessor.kt:157)",
"\tat org.jetbrains.kotlinx.jupyter.execution.JupyterExecutorImpl$Task.execute(JupyterExecutorImpl.kt:41)",
"\tat org.jetbrains.kotlinx.jupyter.execution.JupyterExecutorImpl$executorThread$1.invoke(JupyterExecutorImpl.kt:83)",
"\tat org.jetbrains.kotlinx.jupyter.execution.JupyterExecutorImpl$executorThread$1.invoke(JupyterExecutorImpl.kt:80)",
"\tat kotlin.concurrent.ThreadsKt$thread$thread$1.run(Thread.kt:30)",
""
]
}
],
"execution_count": 5
},
{
"metadata": {},
"cell_type": "code",
"source": "",
"outputs": [],
"execution_count": null
}
],
"metadata": {
"kernelspec": {
"display_name": "Kotlin",
"language": "kotlin",
"name": "kotlin"
},
"language_info": {
"name": "kotlin",
"version": "1.9.23",
"mimetype": "text/x-kotlin",
"file_extension": ".kt",
"pygments_lexer": "kotlin",
"codemirror_mode": "text/x-kotlin",
"nbconvert_exporter": ""
},
"ktnbPluginMetadata": {
"projectDependencies": [
"Amethyst.amethyst.unitTest"
],
"projectLibraries": false
}
},
"nbformat": 4,
"nbformat_minor": 0
}

View File

@@ -29,10 +29,7 @@ import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNotNull
import junit.framework.TestCase.fail
import kotlinx.coroutines.runBlocking
import org.junit.Assert
import org.junit.Test
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
class OtsTest {
val resolver = OtsResolver()
@@ -75,12 +72,8 @@ class OtsTest {
val signer = NostrSignerInternal(KeyPair())
val countDownLatch = CountDownLatch(1)
val newOts = runBlocking { signer.sign(OtsEvent.build(eventId, upgraded!!)) }
Assert.assertTrue(countDownLatch.await(1, TimeUnit.SECONDS))
println(newOts.toJson())
println(resolver.info(newOts.otsByteArray()))
@@ -91,15 +84,11 @@ class OtsTest {
fun createOTSEventAndVerify() {
val signer = NostrSignerInternal(KeyPair())
val countDownLatch = CountDownLatch(1)
val ots =
runBlocking {
signer.sign(OtsEvent.build(otsEvent2Digest, OtsEvent.stamp(otsEvent2Digest, resolver)))
}
Assert.assertTrue(countDownLatch.await(1, TimeUnit.SECONDS))
println(ots.toJson())
println(resolver.info(ots.otsByteArray()))

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

View File

@@ -0,0 +1,23 @@
/**
* Copyright (c) 2025 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
actual fun platform() = "Android"

Some files were not shown because too many files have changed in this diff Show More