amazing recursion with futures on EventSignatures handler.

This commit is contained in:
fiatjaf 2022-03-08 21:06:57 -03:00
parent 3a97f7928b
commit 2f455ab225
4 changed files with 195 additions and 9 deletions

View File

@ -1,5 +1,4 @@
enablePlugins(ScalaJSPlugin)
// enablePlugins(ScalaJSBundlerPlugin)
name := "app"
scalaVersion := "2.13.7"
@ -9,3 +8,7 @@ scalaJSUseMainModuleInitializer := true
libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.1.0"
libraryDependencies += "me.shadaj" %%% "slinky-core" % "0.7.0"
libraryDependencies += "me.shadaj" %%% "slinky-web" % "0.7.0"
libraryDependencies ++= Seq(
"io.circe" %%% "circe-core",
"io.circe" %%% "circe-parser"
).map(_ % "0.14.1")

View File

@ -6,10 +6,10 @@ import slinky.core.FunctionalComponent
import slinky.web.html._
import slinky.core.facade.Hooks._
import app.handlers.{Handler, Nothing, KeyHandling}
import app.handlers.{Handler, Nothing, KeyHandling, EventSignatures}
object Base {
val handlers: List[Handler] = List(KeyHandling, Nothing)
val handlers: List[Handler] = List(KeyHandling, EventSignatures, Nothing)
val component = FunctionalComponent[Unit] { props =>
val (typedValue, setTypedValue) = useState("")

View File

@ -1,21 +1,200 @@
package app.handlers
import scala.annotation.{nowarn, tailrec}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.scalajs.js
import slinky.core.FunctionalComponent
import slinky.web.html._
import slinky.core.facade.Hooks._
import slinky.core.facade.Fragment
import io.circe._
import io.circe.parser._
import io.circe.{Json, HCursor}
import io.circe.parser.{parse}
import app.modules.Nostr
import app.handlers.{Handler}
import app.components.{Item}
object EventSignatures extends Handler {
override def handles(value: String): Boolean = false
val keymatcher = "^[a-f0-9]{64}$".r
override val component = FunctionalComponent[String] { props =>
Fragment(
"nada"
def badProperties(c: HCursor): Seq[String] = Seq(
(
c.get[Double]("kind").getOrElse[Double](-1) >= 0 match {
case true => None;
case false => Some("kind")
}
),
(
keymatcher.matches(
c.get[String]("pubkey").getOrElse("").toLowerCase()
) match {
case true => None;
case false => Some("pubkey")
}
),
(
c.get[String]("content").exists((_) => true) match {
case true => None;
case false => Some("content")
}
),
(
c
.get[List[List[String]]]("tags")
.exists((_) => true) match {
case true => None;
case false => Some("tags")
}
)
)
.filter(res => res.isDefined)
.map(res => res.get)
override def handles(value: String): Boolean = parse(value) match {
case Left(_) => false
case Right(json) => {
badProperties(json.hcursor).length < 4
}
}
type MaybeItem = Future[
Either[slinky.core.TagMod[Nothing], slinky.core.TagMod[Nothing]]
]
@nowarn("cat=other")
def itemWrongProperties(evtj: String): MaybeItem = Future {
val c = parse(evtj).toOption.get.hcursor
val bad = badProperties(c)
if (bad.length > 0) {
Left(
Item.component(
Item.props(
"event missing or wrong properties",
"",
bad.mkString(", ")
)
)
)
} else {
Right(div())
}
}
@nowarn("cat=other")
def itemSerializedEvent(evtj: String): MaybeItem = Future {
val event: js.Dynamic = js.JSON.parse(evtj)
Right(
Item.component(
Item.props(
"serialized event",
"according to nip-01 signature algorithm",
Nostr.serializeEvent(event)
)
)
)
}
@nowarn("cat=other")
def itemEventId(evtj: String): MaybeItem = Future {
val event: js.Dynamic = js.JSON.parse(evtj)
Right(
Item.component(
Item.props(
"event id",
"sha256 hash of serialized event",
Nostr.getEventHash(event)
)
)
)
}
@nowarn("cat=other")
def itemEventIdMatches(evtj: String): MaybeItem = Future {
val c = parse(evtj).toOption.get.hcursor
val event: js.Dynamic = js.JSON.parse(evtj)
def render(result: Boolean) = Item.component(
Item.props(
"does the implied event id match the given event id?",
"if not, that means you're hashing the event uncorrectly",
f"${result match {
case true => "yes"; case false => "no"
}}"
)
)
Nostr
.getEventHash(event) == (c.get[String]("id")).toOption.get match {
case true => Right(render(true))
case false => Left(render(false))
}
}
@nowarn("cat=other")
def itemSignatureValid(evtj: String): MaybeItem = Future {
val event: js.Dynamic = js.JSON.parse(evtj)
def render(result: Boolean) = Item.component(
Item.props(
"is signature valid?",
"",
f"${result match {
case true => "yes"; case false => "no"
}}"
)
)
true match {
case true => Right(render(true))
case false => Left(render(false))
}
}
val protoElements = List[(String) => MaybeItem](
itemWrongProperties,
itemSerializedEvent,
itemEventId,
itemEventIdMatches,
itemSignatureValid
)
@nowarn("cat=other")
override val component = FunctionalComponent[String] { props =>
val (elements, setElements) =
useState(Seq.empty[slinky.core.TagMod[Nothing]])
useEffect(
() => {
def runAndUnwrapUntilFirstLeft(
remaining: List[String => Future[
Either[slinky.core.TagMod[Nothing], slinky.core.TagMod[Nothing]]
]],
acc: List[slinky.core.TagMod[Nothing]]
): Future[List[slinky.core.TagMod[Nothing]]] = remaining match {
case fn :: tail => {
fn(props) flatMap { res =>
res match {
case Left(el) => runAndUnwrapUntilFirstLeft(Nil, el :: acc)
case Right(el) => runAndUnwrapUntilFirstLeft(tail, el :: acc)
}
}
}
case Nil => Future { acc.reverse }
}
runAndUnwrapUntilFirstLeft(protoElements, List()) foreach { elements =>
setElements(elements)
}
() => {}
},
Seq(props)
)
Fragment(elements: _*)
}
}

View File

@ -7,4 +7,8 @@ import scala.scalajs.js
@JSGlobal
object Nostr extends js.Object {
def getPublicKey(text: String): String = js.native
def getEventHash(evt: js.Dynamic): String = js.native
def serializeEvent(evt: js.Dynamic): String = js.native
def verifySignature(evt: js.Dynamic): Boolean = js.native
def signEvent(evt: js.Dynamic, privateKey: String): String = js.native
}