mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-11-10 07:37:12 +01:00
Caches OTS web calls to avoid pinging the server repeatedly for the same event.
This commit is contained in:
@@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.events
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.hexToByteArray
|
||||
import com.vitorpamplona.quartz.ots.BlockstreamExplorer
|
||||
@@ -30,6 +31,7 @@ import com.vitorpamplona.quartz.ots.DetachedTimestampFile
|
||||
import com.vitorpamplona.quartz.ots.Hash
|
||||
import com.vitorpamplona.quartz.ots.OpenTimestamps
|
||||
import com.vitorpamplona.quartz.ots.VerifyResult
|
||||
import com.vitorpamplona.quartz.ots.exceptions.UrlException
|
||||
import com.vitorpamplona.quartz.ots.op.OpSHA256
|
||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
@@ -37,6 +39,25 @@ import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import java.util.Base64
|
||||
|
||||
@Immutable
|
||||
sealed class VerificationState {
|
||||
@Immutable object NotStarted : VerificationState()
|
||||
|
||||
@Stable
|
||||
class Verified(
|
||||
val verifiedTime: Long,
|
||||
) : VerificationState()
|
||||
|
||||
@Immutable class Error(
|
||||
val errorMessage: String,
|
||||
) : VerificationState()
|
||||
|
||||
@Immutable class NetworkError(
|
||||
val errorMessage: String,
|
||||
val time: Long = TimeUtils.now(),
|
||||
) : VerificationState()
|
||||
}
|
||||
|
||||
@Immutable
|
||||
class OtsEvent(
|
||||
id: HexKey,
|
||||
@@ -47,7 +68,7 @@ class OtsEvent(
|
||||
sig: HexKey,
|
||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
@Transient
|
||||
var verifiedTime: Long? = null
|
||||
var verification: VerificationState = VerificationState.NotStarted
|
||||
|
||||
override fun countMemory(): Long =
|
||||
super.countMemory() +
|
||||
@@ -61,15 +82,24 @@ class OtsEvent(
|
||||
|
||||
fun otsByteArray(): ByteArray = Base64.getDecoder().decode(content)
|
||||
|
||||
fun cacheVerify(): Long? =
|
||||
if (verifiedTime != null) {
|
||||
verifiedTime
|
||||
} else {
|
||||
verifiedTime = verify()
|
||||
verifiedTime
|
||||
fun cacheVerify(): VerificationState =
|
||||
when (val verif = verification) {
|
||||
is VerificationState.Verified -> verif
|
||||
is VerificationState.NotStarted -> verifyState().also { verification = it }
|
||||
is VerificationState.NetworkError -> {
|
||||
// try again in 5 mins
|
||||
if (verif.time < TimeUtils.fiveMinutesAgo()) {
|
||||
verifyState().also { verification = it }
|
||||
} else {
|
||||
verif
|
||||
}
|
||||
}
|
||||
is VerificationState.Error -> verif
|
||||
}
|
||||
|
||||
fun verify(): Long? = digestEvent()?.let { OtsEvent.verify(otsByteArray(), it) }
|
||||
fun verifyState(): VerificationState = digestEvent()?.let { verify(otsByteArray(), it) } ?: VerificationState.Error("Digest Not found")
|
||||
|
||||
fun verify(): Long? = (verifyState() as? VerificationState.Verified)?.verifiedTime
|
||||
|
||||
fun info(): String {
|
||||
val detachedOts = DetachedTimestampFile.deserialize(otsByteArray())
|
||||
@@ -98,7 +128,7 @@ class OtsEvent(
|
||||
|
||||
return if (otsInstance.upgrade(detachedOts)) {
|
||||
// if the change is now verifiable.
|
||||
if (verify(detachedOts, eventId) != null) {
|
||||
if (verify(detachedOts, eventId) is VerificationState.Verified) {
|
||||
Base64.getEncoder().encodeToString(detachedOts.serialize())
|
||||
} else {
|
||||
otsFile
|
||||
@@ -111,28 +141,37 @@ class OtsEvent(
|
||||
fun verify(
|
||||
otsFile: String,
|
||||
eventId: HexKey,
|
||||
): Long? = verify(Base64.getDecoder().decode(otsFile), eventId)
|
||||
): VerificationState = verify(Base64.getDecoder().decode(otsFile), eventId)
|
||||
|
||||
fun verify(
|
||||
otsFile: ByteArray,
|
||||
eventId: HexKey,
|
||||
): Long? = verify(DetachedTimestampFile.deserialize(otsFile), eventId)
|
||||
): VerificationState = verify(DetachedTimestampFile.deserialize(otsFile), eventId)
|
||||
|
||||
fun verify(
|
||||
detachedOts: DetachedTimestampFile,
|
||||
eventId: HexKey,
|
||||
): Long? {
|
||||
): VerificationState {
|
||||
try {
|
||||
val result = otsInstance.verify(detachedOts, eventId.hexToByteArray())
|
||||
if (result == null || result.isEmpty()) {
|
||||
return null
|
||||
return VerificationState.Error("Verification hashmap is empty")
|
||||
} else {
|
||||
return result.get(VerifyResult.Chains.BITCOIN)?.timestamp
|
||||
val time = result.get(VerifyResult.Chains.BITCOIN)?.timestamp
|
||||
return if (time != null) {
|
||||
VerificationState.Verified(time)
|
||||
} else {
|
||||
VerificationState.Error("Does not include a Bitcoin verification")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
Log.e("OpenTimeStamps", "Failed to verify", e)
|
||||
return null
|
||||
return if (e is UrlException) {
|
||||
VerificationState.NetworkError(e.message ?: e.cause?.message ?: "Failed to verify")
|
||||
} else {
|
||||
VerificationState.Error(e.message ?: e.cause?.message ?: "Failed to verify")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user