All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.plasmalabs.sdk.validation.TransactionAuthorizationInterpreter.scala Maven / Gradle / Ivy

The newest version!
package org.plasmalabs.sdk.validation

import cats.Monad
import cats.implicits._
import org.plasmalabs.sdk.models.{AccumulatorRootId, Datum, LockId}
import org.plasmalabs.sdk.models.box.Attestation
import org.plasmalabs.sdk.models.transaction.IoTransaction
import org.plasmalabs.sdk.validation.algebras.TransactionAuthorizationVerifier
import org.plasmalabs.quivr.models.{Proof, Proposition}
import org.plasmalabs.quivr.api.Verifier
import org.plasmalabs.quivr.runtime.DynamicContext

/**
 * Validates that each Input within a Transaction is properly "authorized".  "Authorized" simply means "does the given
 * Proof satisfy the given Proposition?".
 */
object TransactionAuthorizationInterpreter {

  def make[F[_]: Monad]()(implicit verifier: Verifier[F, Datum]): TransactionAuthorizationVerifier[F] =
    new TransactionAuthorizationVerifier[F] {

      // TODO: Fix cases of `challenges.map(_.getRevealed)`

      /**
       * Verifies each (Proposition, Proof) pair in the given Transaction
       */
      override def validate(context: DynamicContext[F, String, Datum])(
        transaction: IoTransaction
      ): F[Either[TransactionAuthorizationError, IoTransaction]] =
        transaction.inputs
          .foldLeftM(Either.right[TransactionAuthorizationError, IoTransaction](transaction)) {
            case (Left(error), _) => error.asLeft[IoTransaction].pure[F]
            case (_, input) =>
              input.attestation.value match {
                case Attestation.Value.Predicate(p) =>
                  predicateValidate(p.lock.challenges.map(_.getRevealed), p.lock.threshold, p.responses, context).map(
                    r => r.map(_ => transaction)
                  )

                case Attestation.Value.Image(p) =>
                  imageValidate(p.lock.leaves, p.lock.threshold, p.known.map(_.getRevealed), p.responses, context)
                    .map(r => r.map(_ => transaction))

                case Attestation.Value.Commitment(p) =>
                  commitmentValidate(
                    p.lock.root.get,
                    p.lock.threshold,
                    p.known.map(_.getRevealed),
                    p.responses,
                    context
                  ).map(r => r.map(_ => transaction))
                case _ =>
                  (TransactionAuthorizationError.AuthorizationFailed(): TransactionAuthorizationError)
                    .asLeft[IoTransaction]
                    .pure[F]
              }
          }

      private def predicateValidate(
        challenges: Seq[Proposition],
        threshold:  Int,
        responses:  Seq[Proof],
        context:    DynamicContext[F, String, Datum]
      ): F[Either[TransactionAuthorizationError, Boolean]] =
        thresholdVerifier(challenges, responses, threshold, context)

      private def imageValidate(
        leaves:    Seq[LockId],
        threshold: Int,
        known:     Seq[Proposition],
        responses: Seq[Proof],
        context:   DynamicContext[F, String, Datum]
      ): F[Either[TransactionAuthorizationError, Boolean]] =
        // check that the known Propositions match the leaves?
        thresholdVerifier(known, responses, threshold, context)

      // commitments need an additional proof of membership to be provided with the proposition
      private def commitmentValidate(
        root:      AccumulatorRootId,
        threshold: Int,
        known:     Seq[Proposition],
        responses: Seq[Proof],
        context:   DynamicContext[F, String, Datum]
      ): F[Either[TransactionAuthorizationError, Boolean]] =
        thresholdVerifier(known, responses, threshold, context)

      /**
       * *
       * Verifies that at least threshold number of proofs satisfy their associated propositions
       * @param propositions the propositions to be verified
       * @param proofs the proofs to be verified
       * @param threshold the threshold of proofs that must be satisfied
       * @param context the context in which the proofs are to be verified
       * @param verifier the verifier to be used to verify the proofs
       * @return
       */
      private def thresholdVerifier(
        propositions: Seq[Proposition],
        proofs:       Seq[Proof],
        threshold:    Int,
        context:      DynamicContext[F, String, Datum]
      )(implicit verifier: Verifier[F, Datum]): F[Either[TransactionAuthorizationError, Boolean]] =
        if (threshold === 0) true.asRight[TransactionAuthorizationError].pure[F]
        else if (threshold > propositions.size)
          Either
            .left[TransactionAuthorizationError, Boolean](TransactionAuthorizationError.AuthorizationFailed())
            .pure[F]
        else if (proofs.isEmpty)
          Either
            .left[TransactionAuthorizationError, Boolean](TransactionAuthorizationError.AuthorizationFailed())
            .pure[F]
        // We assume a one-to-one pairing of sub-proposition to sub-proof with the assumption that some of the proofs
        // may be Proof.Value.Empty
        else if (proofs.size =!= propositions.size)
          Either
            .left[TransactionAuthorizationError, Boolean](TransactionAuthorizationError.AuthorizationFailed())
            .pure[F]
        else
          propositions
            .zip(proofs)
            .map(p => verifier.evaluate(p._1, p._2, context)) // Evaluate all the (proposition, proof) pairs
            .sequence
            .map(_.partitionMap(identity))
            .map { res =>
              // If at least threshold number of pairs are valid, authorization is successful
              if (res._2.count(identity) >= threshold)
                true.asRight[TransactionAuthorizationError]
              // If authorization fails, return the QuivrRuntimeErrors that were encountered
              else
                TransactionAuthorizationError.AuthorizationFailed(res._1.toList).asLeft[Boolean]
            }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy