org.plasmalabs.sdk.servicekit.WalletStateApi.scala Maven / Gradle / Ivy
The newest version!
package org.plasmalabs.sdk.servicekit
import cats.data.{OptionT, Validated, ValidatedNel}
import cats.effect.kernel.{Resource, Sync}
import cats.implicits._
import com.google.protobuf.ByteString
import io.circe.parser._
import io.circe.syntax.EncoderOps
import org.plasmalabs.quivr.models.{KeyPair, Preimage, Proposition, VerificationKey}
import org.plasmalabs.sdk.builders.TransactionBuilderApi
import org.plasmalabs.sdk.builders.locks.{LockTemplate, PropositionTemplate}
import org.plasmalabs.sdk.codecs.AddressCodecs
import org.plasmalabs.sdk.codecs.LockTemplateCodecs.{decodeLockTemplate, encodeLockTemplate}
import org.plasmalabs.sdk.common.ContainsEvidence.Ops
import org.plasmalabs.sdk.common.ContainsImmutable.instances._
import org.plasmalabs.sdk.dataApi.WalletStateAlgebra
import org.plasmalabs.sdk.models.box.Lock
import org.plasmalabs.sdk.models.{Indices, LockAddress, LockId}
import org.plasmalabs.sdk.utils.Encoding
import org.plasmalabs.sdk.wallet.WalletApi
import java.sql.Statement
/**
* An implementation of the WalletInteractionAlgebra that uses a database to store state information.
*/
object WalletStateApi {
/**
* Creates an instance of the WalletStateAlgebra that uses a database to store state information.
*
* @param connection the JDBC connection.
* @param walletApi the wallet api.
* @return an instance of the WalletStateAlgebra that uses a database to store state information.
*/
def make[F[_]: Sync](
connection: Resource[F, java.sql.Connection],
walletApi: WalletApi[F]
): WalletStateAlgebra[F] =
new WalletStateAlgebra[F] {
private def getFellowshipIdx(fellowship: String, stmnt: Statement) = OptionT(for {
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT x_fellowship, fellowship FROM fellowships WHERE fellowship = '${fellowship}'"
)
)
x <- Sync[F].delay(rs.getInt("x_fellowship"))
} yield if (rs.next()) Some(x) else None)
private def getTemplateIdx(template: String, stmnt: Statement) = OptionT(for {
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT y_template, template FROM templates WHERE template = '${template}'"
)
)
y <- Sync[F].delay(rs.getInt("y_template"))
} yield if (rs.next()) Some(y) else None)
private def getList(x: Int, y: Int, stmnt: Statement) = OptionT.liftF(for {
rs <- Sync[F].blocking(
stmnt.executeQuery(
"SELECT x_fellowship, y_template, z_interaction, address FROM cartesian " +
s"WHERE x_fellowship = ${x} AND y_template = ${y}"
)
)
hasNext <- Sync[F].delay(rs.next())
pair <- Sync[F].iterateWhileM((hasNext, List[(Indices, String)]())) { x =>
val (_, list) = x
for {
x <- Sync[F].delay(rs.getInt("x_fellowship"))
y <- Sync[F].delay(rs.getInt("y_template"))
z <- Sync[F].delay(rs.getInt("z_interaction"))
address <- Sync[F].delay(rs.getString("address"))
hasNext <- Sync[F].delay(rs.next())
} yield (hasNext, (Indices(x, y, z), address) :: list)
}(_._1)
} yield pair._2)
override def getInteractionList(fellowship: String, template: String): F[Option[List[(Indices, String)]]] =
connection.use { conn =>
(for {
stmnt <- OptionT.liftF(Sync[F].blocking(conn.createStatement()))
x <- getFellowshipIdx(fellowship, stmnt)
y <- getTemplateIdx(template, stmnt)
list <- getList(x, y, stmnt)
} yield list).value
}
override def getIndicesBySignature(signatureProposition: Proposition.DigitalSignature): F[Option[Indices]] =
connection.use { conn =>
for {
stmnt <- Sync[F].blocking(conn.createStatement())
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT x_fellowship, y_template, z_interaction, routine, vk FROM " +
s"cartesian WHERE routine = '${signatureProposition.routine}' AND " +
s"vk = '${Encoding.encodeToBase58(signatureProposition.verificationKey.toByteArray)}'"
)
)
hasNext <- Sync[F].delay(rs.next())
x <- Sync[F].delay(rs.getInt("x_fellowship"))
y <- Sync[F].delay(rs.getInt("y_template"))
z <- Sync[F].delay(rs.getInt("z_interaction"))
} yield if (hasNext) Some(Indices(x, y, z)) else None
}
def getLockByIndex(indices: Indices): F[Option[Lock.Predicate]] = connection.use { conn =>
for {
stmnt <- Sync[F].blocking(conn.createStatement())
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT x_fellowship, y_template, z_interaction, lock_predicate FROM " +
s"cartesian WHERE x_fellowship = ${indices.x} AND " +
s"y_template = ${indices.y} AND " +
s"z_interaction = ${indices.z}"
)
)
_ <- Sync[F].delay(rs.next())
someLockPredicate <- Sync[F].delay(rs.getString("lock_predicate"))
} yield Option(someLockPredicate).map(lock_predicate =>
Lock.Predicate.parseFrom(
Encoding.decodeFromBase58Check(lock_predicate).toOption.get
)
)
}
def setCurrentIndices(fellowship: String, template: String, interaction: Int): F[Option[Indices]] =
connection.use { conn =>
for {
stmnt <- Sync[F].blocking(conn.createStatement())
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT x_fellowship, fellowship FROM fellowships WHERE fellowship = '${fellowship}'"
)
)
x <- Sync[F].delay(rs.getInt("x_fellowship"))
query =
s"SELECT y_template, template FROM templates WHERE template = '${template}'"
rs <- Sync[F].blocking(
stmnt.executeQuery(
query
)
)
y <- Sync[F].delay(rs.getInt("y_template"))
res <- Sync[F].blocking(
stmnt.executeUpdate(
s"DELETE FROM " +
s"cartesian WHERE x_fellowship = ${x} AND " +
s"y_template = ${y} AND " +
s"z_interaction > ${interaction}"
)
)
} yield if (res > 0) Some(Indices(x, y, interaction)) else None
}
def getLockByAddress(lockAddress: String): F[Option[Lock.Predicate]] = connection.use { conn =>
for {
stmnt <- Sync[F].blocking(conn.createStatement())
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT address, lock_predicate FROM " +
s"cartesian WHERE address = '$lockAddress'"
)
)
hasNext <- Sync[F].delay(rs.next())
lock_predicate <- Sync[F].delay(rs.getString("lock_predicate"))
} yield
if (hasNext)
Some(
Lock.Predicate.parseFrom(
Encoding.decodeFromBase58Check(lock_predicate).toOption.get
)
)
else None
}
override def updateWalletState(
lockPredicate: String,
lockAddress: String,
routine: Option[String],
vk: Option[String],
indices: Indices
): F[Unit] = connection.use { conn =>
for {
stmnt <- Sync[F].blocking(conn.createStatement())
statement =
s"INSERT INTO cartesian (x_fellowship, y_template, z_interaction, lock_predicate, address, routine, vk) VALUES (${indices.x}, ${indices.y}, ${indices.z}, '${lockPredicate}', '" +
lockAddress + "', " + routine
.map(x => s"'$x'")
.getOrElse("NULL") + ", " + vk
.map(x => s"'$x'")
.getOrElse("NULL") + ")"
_ <- Sync[F].blocking(
stmnt.executeUpdate(statement)
)
} yield ()
}
override def getNextIndicesForFunds(fellowship: String, template: String): F[Option[Indices]] = connection.use {
conn =>
for {
stmnt <- Sync[F].blocking(conn.createStatement())
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT x_fellowship, fellowship FROM fellowships WHERE fellowship = '${fellowship}'"
)
)
x <- Sync[F].delay(rs.getInt("x_fellowship"))
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT y_template, template FROM templates WHERE template = '${template}'"
)
)
y <- Sync[F].delay(rs.getInt("y_template"))
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT x_fellowship, y_template, MAX(z_interaction) as z_index FROM cartesian WHERE x_fellowship = ${x} AND y_template = ${y}"
)
)
z <- Sync[F].delay(rs.getInt("z_index"))
} yield if (rs.next()) Some(Indices(x, y, z + 1)) else None
}
private def validateFellowship(
fellowship: String
): F[ValidatedNel[String, String]] =
connection.use { conn =>
for {
stmnt <- Sync[F].blocking(conn.createStatement())
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT x_fellowship, fellowship FROM fellowships WHERE fellowship = '${fellowship}'"
)
)
} yield
if (rs.next()) Validated.validNel(fellowship)
else Validated.invalidNel("Fellowship not found")
}
private def validateTemplate(
template: String
): F[ValidatedNel[String, String]] =
connection.use { conn =>
for {
stmnt <- Sync[F].blocking(conn.createStatement())
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT y_template, template FROM templates WHERE template = '${template}'"
)
)
} yield
if (rs.next()) Validated.validNel(template)
else Validated.invalidNel("Template not found")
}
def validateCurrentIndicesForFunds(
fellowship: String,
template: String,
someInteraction: Option[Int]
): F[ValidatedNel[String, Indices]] = for {
validatedFellowship <- validateFellowship(fellowship)
validatedTemplate <- validateTemplate(template)
indices <- getCurrentIndicesForFunds(fellowship, template, someInteraction)
} yield (
validatedFellowship,
validatedTemplate,
indices.toValidNel("Indices not found")
).mapN((_, _, index) => index)
override def getAddress(
fellowship: String,
template: String,
someInteraction: Option[Int]
): F[Option[String]] = connection.use { conn =>
for {
stmnt <- Sync[F].blocking(conn.createStatement())
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT x_fellowship, fellowship FROM fellowships WHERE fellowship = '${fellowship}'"
)
)
x <- Sync[F].delay(rs.getInt("x_fellowship"))
query =
s"SELECT y_template, template FROM templates WHERE template = '${template}'"
rs <- Sync[F].blocking(
stmnt.executeQuery(
query
)
)
y <- Sync[F].delay(rs.getInt("y_template"))
query = s"SELECT address, x_fellowship, y_template, " + someInteraction
.map(_ => "z_interaction as z_index")
.getOrElse(
"MAX(z_interaction) as z_index"
) + s" FROM cartesian WHERE x_fellowship = ${x} AND y_template = ${y}" + someInteraction
.map(x => s" AND z_interaction = ${x}")
.getOrElse("")
rs <- Sync[F].blocking(
stmnt.executeQuery(
query
)
)
address <- Sync[F].delay(Option(rs.getString("address")))
} yield address
}
override def getCurrentIndicesForFunds(
fellowship: String,
template: String,
someInteraction: Option[Int]
): F[Option[Indices]] = connection.use { conn =>
for {
stmnt <- Sync[F].blocking(conn.createStatement())
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT x_fellowship, fellowship FROM fellowships WHERE fellowship = '${fellowship}'"
)
)
x <- Sync[F].delay(rs.getInt("x_fellowship"))
query =
s"SELECT y_template, template FROM templates WHERE template = '${template}'"
rs <- Sync[F].blocking(
stmnt.executeQuery(
query
)
)
y <- Sync[F].delay(rs.getInt("y_template"))
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT x_fellowship, y_template, " + someInteraction
.map(_ => "z_interaction as z_index")
.getOrElse(
"MAX(z_interaction) as z_index"
) + s" FROM cartesian WHERE x_fellowship = ${x} AND y_template = ${y}" + someInteraction
.map(x => s" AND z_interaction = ${x}")
.getOrElse("")
)
)
z <- someInteraction
.map(x => Sync[F].point(x))
.getOrElse(Sync[F].delay(rs.getInt("z_index")))
} yield if (rs.next()) Some(Indices(x, y, z)) else None
}
override def getCurrentAddress: F[String] = connection.use { conn =>
for {
stmnt <- Sync[F].blocking(conn.createStatement())
rs <- Sync[F].blocking(
stmnt.executeQuery(
"SELECT address, MAX(z_interaction) FROM cartesian WHERE x_fellowship = 1 AND y_template = 1 group by x_fellowship, y_template"
)
)
lockAddress <- Sync[F].delay(rs.getString("address"))
} yield lockAddress
}
override def initWalletState(
networkId: Int,
ledgerId: Int,
mainKey: KeyPair
): F[Unit] = {
import TransactionBuilderApi.implicits._
import cats.implicits._
import org.plasmalabs.sdk.common.ContainsEvidence.Ops
import org.plasmalabs.sdk.common.ContainsImmutable.instances._
connection.use { conn =>
for {
stmnt <- Sync[F].delay(conn.createStatement())
_ <- Sync[F].delay(
stmnt.execute(
"CREATE TABLE IF NOT EXISTS cartesian (id INTEGER PRIMARY KEY," +
" x_fellowship INTEGER NOT NULL, y_template INTEGER NOT NULL, z_interaction INTEGER NOT NULL, " +
"lock_predicate TEXT NOT NULL, address TEXT NOT NULL, routine TEXT, vk TEXT)"
)
)
_ <- Sync[F].delay(
stmnt.execute(
"CREATE TABLE IF NOT EXISTS fellowships (fellowship TEXT," +
" x_fellowship INTEGER PRIMARY KEY ASC)"
)
)
_ <- Sync[F].delay(
stmnt.execute(
"CREATE TABLE IF NOT EXISTS templates (template TEXT NOT NULL," +
" y_template INTEGER PRIMARY KEY ASC, lock TEXT NOT NULL)"
)
)
_ <- Sync[F].delay(
stmnt.execute(
"CREATE TABLE IF NOT EXISTS verification_keys (x_fellowship INTEGER NOT NULL," +
" y_template INTEGER NOT NULL, vks TEXT NOT NULL, PRIMARY KEY (x_fellowship, y_template))"
)
)
_ <- Sync[F].delay(
stmnt.execute(
"CREATE TABLE IF NOT EXISTS digests (id INTEGER PRIMARY KEY, digest_evidence TEXT NOT NULL," +
" preimage_input TEXT NOT NULL, preimage_salt TEXT NOT NULL)"
)
)
_ <- Sync[F].delay(
stmnt.execute(
"CREATE UNIQUE INDEX IF NOT EXISTS digest_idx ON digests (digest_evidence)"
)
)
_ <- Sync[F].delay(
stmnt.execute(
"CREATE UNIQUE INDEX IF NOT EXISTS template_names_idx ON templates (template)"
)
)
_ <- Sync[F].delay(
stmnt.execute(
"CREATE INDEX IF NOT EXISTS fellowship_names_idx ON fellowships (fellowship)"
)
)
_ <- Sync[F].delay(
stmnt.execute(
"CREATE UNIQUE INDEX IF NOT EXISTS cartesian_coordinates ON cartesian (x_fellowship, y_template, z_interaction)"
)
)
_ <- Sync[F].delay(
stmnt.execute(
"CREATE INDEX IF NOT EXISTS signature_idx ON cartesian (routine, vk)"
)
)
defaultTemplate <- Sync[F].delay(
LockTemplate.PredicateTemplate[F](
List(
PropositionTemplate.SignatureTemplate[F]("ExtendedEd25519", 0)
),
1
)
)
genesisTemplate <- Sync[F].delay(
LockTemplate.PredicateTemplate[F](
List(
PropositionTemplate
.HeightTemplate[F]("header", 1, Long.MaxValue)
),
1
)
)
_ <- Sync[F].delay(
stmnt.executeUpdate(
"INSERT INTO fellowships (fellowship, x_fellowship) VALUES ('nofellowship', 0)"
)
)
_ <- Sync[F].delay(
stmnt.executeUpdate(
"INSERT INTO fellowships (fellowship, x_fellowship) VALUES ('self', 1)"
)
)
_ <- Sync[F].delay(
stmnt.executeUpdate(
s"INSERT INTO templates (template, y_template, lock) VALUES ('default', 1, '${encodeLockTemplate(defaultTemplate).toString}')"
)
)
_ <- Sync[F].delay(
stmnt.executeUpdate(
s"INSERT INTO templates (template, y_template, lock) VALUES ('genesis', 2, '${encodeLockTemplate(genesisTemplate).toString}')"
)
)
selfDefaultVk <- walletApi.deriveChildKeysPartial(mainKey, 1, 1).map(_.vk)
_ <- Sync[F].delay(
stmnt.executeUpdate(
s"INSERT INTO verification_keys (x_fellowship, y_template, vks) VALUES (1, 1, '${List(Encoding.encodeToBase58(selfDefaultVk.toByteArray)).asJson.toString}')"
)
)
_ <- Sync[F].delay(
stmnt.executeUpdate(
s"INSERT INTO verification_keys (x_fellowship, y_template, vks) VALUES (0, 2, '${List[String]().asJson.toString}')"
)
)
defaultSignatureLock <- getLock("self", "default", 1).map(_.get)
signatureLockAddress = LockAddress(
networkId,
ledgerId,
LockId(Lock().withPredicate(defaultSignatureLock.getPredicate).sizedEvidence.digest.value)
)
childVk <- walletApi.deriveChildVerificationKey(selfDefaultVk, 1)
genesisHeightLock <- getLock("nofellowship", "genesis", 1).map(_.get)
heightLockAddress = LockAddress(
networkId,
ledgerId,
LockId(Lock().withPredicate(genesisHeightLock.getPredicate).sizedEvidence.digest.value)
)
_ <- Sync[F].delay(
stmnt.executeUpdate(
"INSERT INTO cartesian (x_fellowship, y_template, z_interaction, lock_predicate, address, routine, vk) VALUES (1, 1, 1, '" +
Encoding
.encodeToBase58Check(
defaultSignatureLock.getPredicate.toByteArray
) +
"', '" +
signatureLockAddress.toBase58() + "', " + "'ExtendedEd25519', " + "'" +
Encoding.encodeToBase58(childVk.toByteArray)
+ "'" + ")"
)
)
_ <- Sync[F].delay(
stmnt.executeUpdate(
"INSERT INTO cartesian (x_fellowship, y_template, z_interaction, lock_predicate, address) VALUES (0, 2, 1, '" +
Encoding
.encodeToBase58Check(
genesisHeightLock.getPredicate.toByteArray
) +
"', '" +
heightLockAddress.toBase58() + "')"
)
)
_ <- Sync[F].delay(stmnt.close())
} yield ()
}
}
def validateWalletInitialization(
networkId: Int,
ledgerId: Int,
mainKey: KeyPair
): F[Either[Seq[String], Unit]] = {
import scala.util.control.Exception._
connection.use { conn =>
for {
stmnt <- Sync[F].delay(conn.createStatement())
rsOpt <- Sync[F].blocking(
allCatch opt stmnt.executeQuery(
s"SELECT address, vk FROM cartesian WHERE x_fellowship = 1 AND y_template = 1 AND z_interaction = 1"
)
)
address <- Sync[F].delay(
rsOpt.flatMap(rs => allCatch opt AddressCodecs.decodeAddress(rs.getString("address")).toOption) flatten
)
vk <- Sync[F].delay(
rsOpt
.flatMap(rs =>
allCatch opt Encoding.decodeFromBase58(rs.getString("vk")).map(VerificationKey.parseFrom).toOption
) flatten
)
expectedChildKey <- walletApi.deriveChildKeys(mainKey, Indices(1, 1, 1))
_ <- Sync[F].delay(stmnt.close())
} yield {
val errs = if (address.isDefined && vk.isDefined) {
val idErrors =
if (address.exists(a => a.network == networkId && a.ledger == ledgerId)) Seq()
else Seq("networkId or ledgerId mismatch")
val vkErrors = if (vk.contains(expectedChildKey.vk)) Seq() else Seq("mainKey mismatch")
idErrors ++ vkErrors
} else Seq("State not initialized")
if (errs.nonEmpty) Left(errs) else Right(())
}
}
}
override def getPreimage(
digestProposition: Proposition.Digest
): F[Option[Preimage]] = connection.use { conn =>
for {
stmnt <- Sync[F].blocking(conn.createStatement())
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT preimage_input, preimage_salt FROM digests WHERE " +
s"digest_evidence = '${Encoding.encodeToBase58Check(digestProposition.sizedEvidence.digest.value.toByteArray)}'"
)
)
hasNext <- Sync[F].delay(rs.next())
preimageInput <- Sync[F].delay(rs.getString("preimage_input"))
preimageSalt <- Sync[F].delay(rs.getString("preimage_salt"))
} yield
if (hasNext) {
val input = Encoding.decodeFromBase58Check(preimageInput).toOption.get
val salt = Encoding.decodeFromBase58Check(preimageSalt).toOption.get
Preimage(ByteString.copyFrom(input), ByteString.copyFrom(salt)).some
} else None
}
override def addPreimage(preimage: Preimage, digestProposition: Proposition.Digest): F[Unit] =
connection.use { conn =>
for {
stmnt <- Sync[F].blocking(conn.createStatement())
statement = {
val digestEvidence =
Encoding.encodeToBase58Check(digestProposition.sizedEvidence.digest.value.toByteArray)
val input = Encoding.encodeToBase58Check(preimage.input.toByteArray)
val salt = Encoding.encodeToBase58Check(preimage.salt.toByteArray)
s"INSERT INTO digests (digest_evidence, preimage_input, preimage_salt) VALUES " +
s"('${digestEvidence}', '${input}', '${salt}')"
}
_ <- Sync[F].blocking(
stmnt.executeUpdate(statement)
)
} yield ()
}
override def addEntityVks(
fellowship: String,
template: String,
fellows: List[String]
): F[Unit] = connection.use { conn =>
for {
stmnt <- Sync[F].blocking(conn.createStatement())
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT x_fellowship FROM fellowships WHERE fellowship = '${fellowship}'"
)
)
x <- Sync[F].delay(rs.getInt("x_fellowship"))
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT y_template FROM templates WHERE template = '${template}'"
)
)
y <- Sync[F].delay(rs.getInt("y_template"))
statement =
s"INSERT INTO verification_keys (x_fellowship, y_template, vks) VALUES (${x}, ${y}, " +
s"'${fellows.asJson.toString}')"
_ <- Sync[F].blocking(
stmnt.executeUpdate(statement)
)
} yield ()
}
override def getEntityVks(
fellowship: String,
template: String
): F[Option[List[String]]] = connection.use { conn =>
for {
stmnt <- Sync[F].blocking(conn.createStatement())
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT x_fellowship FROM fellowships WHERE fellowship = '${fellowship}'"
)
)
x <- Sync[F].delay(rs.getInt("x_fellowship"))
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT y_template FROM templates WHERE template = '${template}'"
)
)
y <- Sync[F].delay(rs.getInt("y_template"))
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT vks FROM verification_keys WHERE x_fellowship = ${x} AND y_template = ${y}"
)
)
vks <- Sync[F].delay(rs.getString("vks"))
} yield
if (!rs.next()) None
else parse(vks).toOption.flatMap(_.as[List[String]].toOption)
}
override def addNewLockTemplate(
template: String,
lockTemplate: LockTemplate[F]
): F[Unit] = connection.use { conn =>
for {
stmnt <- Sync[F].blocking(conn.createStatement())
// FIXME: do not use max, use autoincrement
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT MAX(y_template) as y_index FROM templates"
)
)
y <- Sync[F].delay(rs.getInt("y_index"))
statement =
s"INSERT INTO templates (template, y_template, lock) VALUES ('${template}', ${y + 1}, '${encodeLockTemplate(lockTemplate).toString}')"
_ <- Sync[F].blocking(
stmnt.executeUpdate(statement)
)
} yield ()
}
override def getLockTemplate(
template: String
): F[Option[LockTemplate[F]]] = connection.use { conn =>
for {
stmnt <- Sync[F].blocking(conn.createStatement())
rs <- Sync[F].blocking(
stmnt.executeQuery(
s"SELECT lock FROM templates WHERE template = '${template}'"
)
)
lockStr <- Sync[F].delay(rs.getString("lock"))
} yield
if (!rs.next()) None
else
parse(lockStr).toOption.flatMap(decodeLockTemplate[F](_).toOption)
}
override def getLock(
fellowship: String,
template: String,
nextInteraction: Int
): F[Option[Lock]] = for {
changeTemplate <- getLockTemplate(template)
entityVks <- getEntityVks(fellowship, template)
.map(
_.map(
_.map(vk =>
VerificationKey.parseFrom(
Encoding.decodeFromBase58(vk).toOption.get
)
)
)
)
childVks <- entityVks
.map(vks => vks.map(walletApi.deriveChildVerificationKey(_, nextInteraction)).sequence)
.sequence
changeLock <- changeTemplate
.flatMap(template => childVks.map(vks => template.build(vks).map(_.toOption)))
.sequence
.map(_.flatten)
} yield changeLock
}
}