diff --git a/build.sbt b/build.sbt index cba74cbe6d..d0156af6e4 100644 --- a/build.sbt +++ b/build.sbt @@ -300,7 +300,7 @@ lazy val rootSettings = Seq( ) def runErgoTask(task: String, sigmastateVersion: String, log: Logger): Unit = { - val ergoBranch = "master" + val ergoBranch = "test-coverage" val sbtEnvVars = Seq("BUILD_ENV" -> "test", "SIGMASTATE_VERSION" -> sigmastateVersion) log.info(s"Testing current build in Ergo (branch $ergoBranch):") @@ -354,7 +354,7 @@ commands += Command.command("ergoItTest") { state => } def runSpamTestTask(task: String, sigmastateVersion: String, log: Logger): Unit = { - val spamBranch = "revert-23-revert-22-serialize-opt" + val spamBranch = "master" val envVars = Seq("SIGMASTATE_VERSION" -> sigmastateVersion, "SPECIAL_VERSION" -> specialVersion, // SSH_SPAM_REPO_KEY should be set (see Jenkins Credentials Binding Plugin) diff --git a/common/src/main/scala/scalan/TypeDesc.scala b/common/src/main/scala/scalan/TypeDesc.scala index 36d56e8f0e..0d15988399 100644 --- a/common/src/main/scala/scalan/TypeDesc.scala +++ b/common/src/main/scala/scalan/TypeDesc.scala @@ -3,7 +3,12 @@ package scalan import scala.reflect.ClassTag import scala.annotation.implicitNotFound -/** Base type for all runtime type descriptors. */ +/** Base type for all runtime type descriptors. Sigma uses type descriptors to + * represent structure of the data values. Data values of registers and context + * variables come equipped with type descriptors in order to check the actual type + * is the same as the type expected by the script. + * @see [[getReg]], [[getVar]] + */ @implicitNotFound(msg = "No RType available for ${A}.") abstract class RType[A] { /** Class tag suitable for construct instances of Array[A]. */ @@ -15,6 +20,9 @@ abstract class RType[A] { /** Returns true is data size of `x: A` is the same for all `x`. * This is useful optimizations of calculating sizes of collections. */ def isConstantSize: Boolean + + /** Creates empty immutable array of this type. */ + def emptyArray: Array[A] = Array.empty[A](classTag) } object RType { @@ -56,7 +64,8 @@ object RType { } /** Descriptor used to represent primitive types. */ - case class PrimitiveType[A](classTag: ClassTag[A]) extends RType[A] { + case class PrimitiveType[A](classTag: ClassTag[A], + override val emptyArray: Array[A]) extends RType[A] { override def name: String = classTag.toString() /** We assume all primitive types have inhabitants of the same size. */ override def isConstantSize: Boolean = true @@ -66,15 +75,15 @@ object RType { val AnyRefType : RType[AnyRef] = GeneralType[AnyRef] (ClassTag.AnyRef) val NothingType : RType[Nothing] = GeneralType[Nothing] (ClassTag.Nothing) - implicit val BooleanType : RType[Boolean] = PrimitiveType[Boolean] (ClassTag.Boolean) - implicit val ByteType : RType[Byte] = PrimitiveType[Byte] (ClassTag.Byte) - implicit val ShortType : RType[Short] = PrimitiveType[Short] (ClassTag.Short) - implicit val IntType : RType[Int] = PrimitiveType[Int] (ClassTag.Int) - implicit val LongType : RType[Long] = PrimitiveType[Long] (ClassTag.Long) - implicit val CharType : RType[Char] = PrimitiveType[Char] (ClassTag.Char) - implicit val FloatType : RType[Float] = PrimitiveType[Float] (ClassTag.Float) - implicit val DoubleType : RType[Double] = PrimitiveType[Double] (ClassTag.Double) - implicit val UnitType : RType[Unit] = PrimitiveType[Unit] (ClassTag.Unit) + implicit val BooleanType : RType[Boolean] = PrimitiveType[Boolean] (ClassTag.Boolean, Array.emptyBooleanArray) + implicit val ByteType : RType[Byte] = PrimitiveType[Byte] (ClassTag.Byte, Array.emptyByteArray) + implicit val ShortType : RType[Short] = PrimitiveType[Short] (ClassTag.Short, Array.emptyShortArray) + implicit val IntType : RType[Int] = PrimitiveType[Int] (ClassTag.Int, Array.emptyIntArray) + implicit val LongType : RType[Long] = PrimitiveType[Long] (ClassTag.Long, Array.emptyLongArray) + implicit val CharType : RType[Char] = PrimitiveType[Char] (ClassTag.Char, Array.emptyCharArray) + implicit val FloatType : RType[Float] = PrimitiveType[Float] (ClassTag.Float, Array.emptyFloatArray) + implicit val DoubleType : RType[Double] = PrimitiveType[Double] (ClassTag.Double, Array.emptyDoubleArray) + implicit val UnitType : RType[Unit] = PrimitiveType[Unit] (ClassTag.Unit, Array[Unit]()(ClassTag.Unit)) /** Descriptor of the type A narrowed to the single inhabitant `value`. */ case class SingletonType[A](value: A, classTag: ClassTag[A])() extends RType[A] { diff --git a/core/src/main/scala/scalan/Entities.scala b/core/src/main/scala/scalan/Entities.scala index cb8a333150..376bd5a529 100644 --- a/core/src/main/scala/scalan/Entities.scala +++ b/core/src/main/scala/scalan/Entities.scala @@ -1,14 +1,14 @@ package scalan -import java.util.Objects - -import scala.annotation.tailrec import scala.language.higherKinds import scalan.util.ReflectionUtil.ClassOps +/** A slice in the Scalan cake with base classes for various descriptors. */ trait Entities extends TypeDescs { self: Scalan => - /** Base class for all descriptors of staged traits. */ + /** Base class for all descriptors of staged traits. + * See derived classes in `impl` packages. + */ abstract class EntityElem[A] extends Elem[A] with scala.Equals { /** Optional parent type in inheritance hierarchy */ def parent: Option[Elem[_]] = None @@ -28,7 +28,7 @@ trait Entities extends TypeDescs { self: Scalan => case _ => false }) - override def hashCode = Objects.hash(getClass, typeArgsDescs) + override def hashCode = getClass.hashCode() * 31 + typeArgsDescs.hashCode() } /** Base class for all descriptors of staged traits with one type parameter. */ diff --git a/core/src/main/scala/scalan/staged/ProgramGraphs.scala b/core/src/main/scala/scalan/staged/ProgramGraphs.scala index bcffabc964..4ff0378d3e 100644 --- a/core/src/main/scala/scalan/staged/ProgramGraphs.scala +++ b/core/src/main/scala/scalan/staged/ProgramGraphs.scala @@ -41,7 +41,7 @@ trait ProgramGraphs extends AstGraphs { self: Scalan => val res = DBuffer.ofSize[Int](len) cfor(0)(_ < len, _ + 1) { i => val sym = deps(i) - if (pred(sym) && !sym.isVar) // TODO remove isVar condition here and below + if (pred(sym) && !sym.isVar) // TODO optimize: remove isVar condition here and below res += sym.node.nodeId } res diff --git a/docs/sigmastate_protocols/sigmastate_protocols.bib b/docs/sigmastate_protocols/sigmastate_protocols.bib index 80f267a749..160cad7d1f 100644 --- a/docs/sigmastate_protocols/sigmastate_protocols.bib +++ b/docs/sigmastate_protocols/sigmastate_protocols.bib @@ -88,13 +88,7 @@ @misc{zcash1 howpublished = {\url{https://z.cash}} } -@misc{advtutorial, - year = {2019}, - month = {03}, - title = {Advanced ErgoScript Tutorial}, - howpublished = {\url{https://docs.ergoplatform.com/sigmastate_protocols.pdf}} -} -@misc{tutorial, +@misc{whitepaper, year = {2019}, month = {03}, title = {ErgoScript, a Cryptocurrency Scripting Language Supporting Noninteractive Zero-Knowledge Proofs}, @@ -118,7 +112,7 @@ @misc{coinjoin @misc{langrepo, author = {Scorex Foundation}, - title = {Sigmastate Interpretter (ErgoScript)}, + title = {Sigmastate Interpretter}, year = {2017}, publisher = {GitHub}, journal = {GitHub repository}, @@ -1365,7 +1359,7 @@ @article{kokoris2016enhancing @inproceedings{aumasson2013blake2, title={{BLAKE2}: simpler, smaller, fast as {MD5}}, - author={Aumasson, Jean-Philippe and Neves, Samuel and Wilcox-O’Hearn, Zooko and Winnerlein, Christian}, + author={Aumasson, Jean-Philippe and Neves, Samuel and Wilcox-O’Hearn, Zooko and Winnerlein, Christian}, booktitle={International Conference on Applied Cryptography and Network Security}, pages={119--135}, year={2013}, @@ -1408,8 +1402,8 @@ @misc{fivehrs } @misc{ethattacks, - title={Ethereum Network Attacker’s IP Address Is Traceable}, + title={Ethereum Network Attacker’s IP Address Is Traceable}, author={Bok Khoo}, year=2016, note={\url{https://www.bokconsulting.com.au/blog/ethereum-network-attackers-ip-address-is-traceable/}} -} \ No newline at end of file +} diff --git a/docs/sigmastate_protocols/sigmastate_protocols.pdf b/docs/sigmastate_protocols/sigmastate_protocols.pdf index d4361b87bb..3fd2f9cb3b 100644 Binary files a/docs/sigmastate_protocols/sigmastate_protocols.pdf and b/docs/sigmastate_protocols/sigmastate_protocols.pdf differ diff --git a/docs/sigmastate_protocols/sigmastate_protocols.tex b/docs/sigmastate_protocols/sigmastate_protocols.tex index 1efd325559..cbdd2ce608 100644 --- a/docs/sigmastate_protocols/sigmastate_protocols.tex +++ b/docs/sigmastate_protocols/sigmastate_protocols.tex @@ -163,7 +163,7 @@ \subsection{Other Features of \langname} %The set of predicates is richer than in Bitcoin, but still lean in order to allow for efficient processing even by light clients. Like in Bitcoin, we allow the use of current height of the blockchain; unlike Bitcoin, we also allow the use of information contained in the spending transaction, such as inputs it is trying to spend and outputs it is trying to create. This feature enables self-replication and sophisticated (even Turing-complete) long-term script behaviour, as described in examples below. -The following sections present smart contracts using \langname. More details of \langname are available in the tutorial~\cite{tutorial} and the code for the below examples is available on GitHub~\cite{langrepo}. +The following sections present smart contracts using \langname. More details of \langname are available in the white paper~\cite{whitepaper} and the code for the below examples is available on GitHub~\cite{langrepo}. \section{Basic Examples: Enhanced Spending Contracts} The examples below use P2SH address and highlight some limitations of Bitcoin. @@ -187,40 +187,6 @@ \subsection{Short-lived Unconfirmed Transactions: Paying for Coffee} Observe that if the transaction is not mined before height $i$ then the transaction becomes invalid. When paying at a coffee shop, for example, Alice can set $i$ close to the height $h$ at the time of broadcast, for instance, $i = h + 10$. Alice can still send non-timed payments by making $i$ very large. Since the context variables are part of the message in constructing the zero-knowledge proof, a miner cannot change it (to make this transaction valid). -\subsection{Revenue Sharing Contracts} - -Assume that Alice, Bob and Carol agree to share revenue in the ratios of 50\%, 30\% and 20\% respectively. Let \texttt{alice}, \texttt{bob} and \texttt{carol} be their public keys. The following describes how to create an address that automatically enforces this for any coins sent to it. First define a collection: -\begin{verbatim} -val spenders = Coll((alice, 50), (bob, 30), (carol, 20)) -\end{verbatim} - -Let \texttt{feePropBytesHash} to be the hash of the script for a box that pays transaction fees and let \texttt{fee} be the specified fee. -% There may be advantages of storing the public keys in a register rather than hardwiring them to address -The revenue sharing address is the P2SH address of the following script: - -\begin{verbatim} -val feeBox = OUTPUTS(0) // assume that first output pays fee -val validFeeBox = blake2b256(feeBox.propositionBytes) == feePropBytesHash -val amt = SELF.value - fee // balance remaining after deducting fee -val ratios = spenders.map({(e:(Coll[Byte], Int)) => e._2}) -val total = ratios.fold(0, {(l:Int, r:Int) => l + r}) -val validOuts = spenders.map({(e:(Coll[Byte], Int)) => - val pubKeyHash = e._1 // hash of a public key - val ratio = e._2 // ratio of funds to be paid to public key - val share = amt / total * total // actual amount based on ratio - OUTPUTS.exists({(b:Box) => - b.value == share && blake2b256(b.propositionBytes) == pubKeyHash - }) -}) -validOuts.fold(true, {(l:Boolean, r:Boolean) => l && r}) && validFeeBox -\end{verbatim} - -Any funds sent to this address can only be spent in a transaction that creats boxes paying to Alice, Bob and Carol in the ratios defined above (after deducting transaction fee). - -%\subsection{Automated Salary Contracts} -%further improvements. Can we reduce offchain data size, do multiple mixes offchain? - - \subsection{Reversible Addresses} We create a useful primitives called {\em reversible addresses}, designed for storing funds in a hot-wallet. %These addresses have anti-theft features in the following sense: @@ -317,6 +283,50 @@ \subsection{Cold-Wallet Contracts} % \item For two keys the amount is 10\% or 1000 Ergs (whichever is higher). % \item If all three two keys are spending then there are no restrictions. %\end{enumerate} +\ignore{ +\subsection{Revenue Sharing Contracts} + +Assume that Alice, Bob and Carol agree to share revenue with a 50\%, 30\% and 20\% ratio respectively. The following describes a contract that automatically enforces this for any coins sent to it. Let \texttt{outputScriptHash} be the hash of the following script: + +%First create a script and compute its hash: +% There may be advantages of storing the public keys in a register rather than hardwiring them to address +%First add the public keys \texttt{alice}, \texttt{bob} and \texttt{carol} of type \texttt{proveDlog} into the environment \texttt{env}. First create a script. + +%First create an outputScript = + +\begin{verbatim} +val spenders = SELF.R4[Coll[(SigmaProp, Int)]].get +val index = getVar[Int](1).get // index of current spender +val pubKey:SigmaProp = spenders(index)._1 // pub key of current spender +val ratio:Int = spenders(index)._2 // ratio of current spender +val total = spenders.fold(0, {(accum:Int, s:(SigmaProp, Int)) => accum + s._2}) +val balance = SELF.value - SELF.value / total * ratio +val remainingSpenders = spenders.filter({(s:(SigmaProp, Int)) => s._1 != pubKey}) +val outSpenders = OUTPUTS(0).R4[Coll[(SigmaProp, Int)]].get +val validOut = OUTPUTS(0).propositionBytes == SELF.propositionBytes && + OUTPUTS(0).value >= balance && remainingSpenders == outSpenders +pubKey && (outSpenders.size == 0 || validOut) +\end{verbatim} + +The revenue sharing contract is encoded in the P2SH address of the following script: +\begin{verbatim} +val spenders = Coll((alice, 50), (bob, 30), (carol, 20)) +val index = getVar[Int](1).get // index of current spender +val pubKey:SigmaProp = spenders(index)._1 // pub key of current spender +val ratio:Int = spenders(index)._2 // ratio of current spender +val balance = SELF.value - SELF.value / 100 * ratio // Assume total is 100 +val remainingSpenders = spenders.filter({(s:(SigmaProp, Int)) => s._1 != pubKey}) +val outSpenders = OUTPUTS(0).R4[Coll[(SigmaProp, Int)]].get +val validOut = OUTPUTS(0).propositionBytes == outputScriptHash && + OUTPUTS(0).value >= balance && remainingSpenders == outSpenders +pubKey && validOut +\end{verbatim} + +For any funds sent to this address, each party can spend only its share and must create another box with the balance amount that enforces the same condition for the remaining spenders. + +%\subsection{Automated Salary Contracts} +%further improvements. Can we reduce offchain data size, do multiple mixes offchain? +} \section{Two-party Protocols} @@ -432,74 +442,66 @@ \subsection{\mixname: Non-Interactive CoinJoin} \end{enumerate} We use the non-interactive variant, where $c = H(t_0 \Vert t_1\Vert m)$. We call this \texttt{proveDHTuple}$(g, h, u, v)$. Observe that this protocol requires 4 exponentiations for verification, while \texttt{proveDlog} requires 2. -We also need a variant of \texttt{proveDlog} denoted as \texttt{proveDlogH}$(h, v)$ (not yet present in \langname), that takes in 2 parameters and proves knowledge of $x$ such that $v = h^x$ for arbitrary generator $h$. Currently, this can be done as \texttt{proveDlogH}$(h, v) = $ \texttt{proveDHTuple}$(h, h, v, v)$. A client can verify such tuples in 2 exponentiations. - \subsubsection{The Basic Protocol} Without loss of generality, Alice will pool and Bob will mix. -% In practice, each coin must go through multiple stages of mix, with the choice of going via pool randomly decided after each mix. -Let $g$ be the generator of \texttt{proveDlog}. +Let $g$ be the generator of \texttt{proveDlog}. Each box also has two registers $\alpha, \beta$ for storing group elements. + \begin{enumerate} \item \textbf{Pool:} To add a coin to the H-pool, Alice picks random $x\in \mathbb{Z}_q$ and creates an output box $A$ containing $u = g^x$ protected by the script given below. She waits for Bob to join, who will do so by spending $A$ in a transaction satisfying following conditions: \begin{enumerate} - % \item It has two outputs $O_0, O_1$ containing tuples $(h, w_0)$, $(h, w_1)$ respectively for $w_0, w_1\in G$. - \item It has two outputs $O_0, O_1$ containing pairs $(c, d_0)$, $(c, d_1)$ respectively for $d_0, d_1\in G$. - \item One of $(g, u, c, d_0), (g, u, c, d_1)$ is of the form $(g, g^x, g^y, g^{xy})$, a valid Diffie-Hellman tuple. This is encoded as $\texttt{proveDHTuple}(g, u, c, d_0)\lor \texttt{proveDHTuple}(g, u, c, d_1)$. + \item It has two outputs $O_0, O_1$ whose registers $(\alpha, \beta)$ contain pairs $(c, d)$, $(d, c)$ respectively for some $c, d\in G$. + \item One of $(g, u, c, d), (g, u, d, c)$ is of the form $(g, g^x, g^y, g^{xy})$, a valid Diffie-Hellman tuple. This is encoded as $\texttt{proveDHTuple}(g, u, c, d)\lor \texttt{proveDHTuple}(g, u, d, c)$. \item Apart from the above, the boxes $O_0, O_1$ are otherwise identical. That is, they have the same value as that of $A$ and are protected the script $\tau_\textsf{A} \lor \tau_\textsf{B}$ given in the Mix step below. \end{enumerate} - \item \textbf{Mix:} Bob picks secrets $(b, y, z) \in \mathbb{Z}_2\times \mathbb{Z}_q\times\mathbb{Z}_q$ and spends $A$ with one of his own box to create two output boxes $O_0, O_1$ of equal value such that $O_b$ is spendable by Alice alone and $O_{1-b}$ by Bob alone. - The boxes have registers $c, d$ containing elements from $G$ as follows: + \item \textbf{Mix:} Bob picks secrets $(b, y) \in \mathbb{Z}_2\times \mathbb{Z}_q$ and spends $A$ with one or more of his own boxes to create two output boxes $O_0, O_1$ of equal value such that $O_b$ is spendable by Alice alone and $O_{1-b}$ by Bob alone. This is done as follows. \begin{enumerate} - \item %Let $(h, v) = ({g}^{y}, {u}^{y})$. - Registers $(c, d)$ of $O_b$ and $O_{1-b}$ are set to $(g^y, u^y)$ and $(g^y, g^z)$ respectively. - % If the DDH problem in $G$ is hard, then given $(g, g^x)$ the distributions $({g}^{y}, {g}^{xy})$ and - % $({g}^{xy}, {g}^{y})$ are computationally indistinguishable. In other words, without knowledge of $x$ or $y$, one cannot guess $b$ with probability better than $1/2$. + \item + Registers $(\alpha, \beta)$ of $O_b$ and $O_{1-b}$ are set to $(g^y, u^y)$ and $(u^y, g^y)$ respectively. \item Each box is protected by the proposition $\tau_\textsf{A} \lor \tau_\textsf{B}$, where $\tau_\textsf{A}$ and $\tau_\textsf{B}$ are as follows: - $\tau_\textsf{A} = $ ``Prove knowledge of $x$ such that ${d} = {c}^{x}$ via $\texttt{proveDlogH}(c, d)$.'' + $\tau_\textsf{A} = $ ``Prove knowledge of $x$ such that ${u} = {g}^{x}$ and $\beta = \alpha^x$ via $\texttt{proveDHTuple}(g, \alpha, u, \beta)$.'' - $\tau_{\textsf{B}} = $ ``Prove knowledge of $y$ such that $d = {g}^{y}$ via $\texttt{proveDlog}(d)$.'' + $\tau_{\textsf{B}} = $ ``Prove knowledge of $y$ such that $\beta = {g}^{y}$ via $\texttt{proveDlog}(\beta)$.'' \end{enumerate} - % \item \textbf{Spend:} Alice and Bob spent their respective boxes using their secrets, possibly sending funds back to the pool or mix stages. Bob already knows which coin belongs to him. For each output, Alice will parse the data as $(g, h, u, v)$ and select the one with $v = h^x$. \end{enumerate} After the mix, Alice and Bob can spent their respective boxes using their secrets. -Alice can identify her box as the one with $d = c^x$. +Alice can identify her box as the one with $\beta = \alpha^x$. %\subsubsection{Implementing \mixname In \langname} \paragraph{\langname Code:} First compute \texttt{fullMixScriptHash}, the hash of the following script: \begin{verbatim} -val c = SELF.R4[GroupElement].get -val d = SELF.R5[GroupElement].get -def proveDlogH(h:GroupElement, v:GroupElement) = proveDHTuple(h, h, v, v) -proveDlog(d) || proveDlogH(c, d) +val alpha = SELF.R4[GroupElement].get +val beta = SELF.R5[GroupElement].get +proveDlog(beta) || proveDHTuple(g, alpha, u, beta) \end{verbatim} Next create a script, \texttt{halfMixScript}, having the following code: \begin{verbatim} -val u = SELF.R4[GroupElement].get -val c = OUTPUT(0).R4[GroupElement].get -val d0 = OUTPUT(0).R5[GroupElement].get -val d1 = OUTPUT(1).R5[GroupElement].get -val bob = c == OUTPUT(1).R4[GroupElement].get && - (proveDHTuple(g, u, c, d0) || proveDHTuple(g, u, c, d1)) -val alice = proveDlog(u) // so Alice can spend if no one joins for a long time -val fullMixBox = {(b:Box) => blake2b256(b.propositionBytes) == fullMixScriptHash} -val fullMixTx = OUTPUT(0).value == SELF.value && OUTPUT(1).value == SELF.value && -fullMixBox(OUTPUT(0)) && fullMixBox(OUTPUT(1)) - -fullMixTx && (bob || alice) +val alpha = OUTPUT(0).R4[GroupElement].get +val beta = OUTPUT(0).R5[GroupElement].get +val alpha1 = OUTPUT(1).R4[GroupElement].get +val beta1 = OUTPUT(1).R5[GroupElement].get + +def fullMixBox(b:Box) = blake2b256(b.propositionBytes) == fullMixScriptHash && + b.value == SELF.value + +fullMixBox(OUTPUT(0)) && fullMixBox(OUTPUT(1)) && +alpha == beta1 && beta == alpha1 && +(proveDHTuple(g, u, alpha, beta) || proveDHTuple(g, u, beta, alpha)) \end{verbatim} -Alice's Half-Mix box is protected by \texttt{halfMixScript} given above, which Bob can spend using the condition \texttt{bob}. In case no one spends her box for a long time, she can do so herself using the condition \texttt{alice}, as long as she spends it in a mix transaction. +Alice's Half-Mix box is protected by \texttt{halfMixScript} given above, which Bob can spend using the condition \texttt{bob}. In case no one spends her box for a long time, she can do so herself by playing the role of Bob. \subsubsection{Analysis Of The Protocol} -\paragraph{Security:} Observe that registers $(c, d)$ of $O_b$ and $O_{1-b}$ contain $(g^y, g^{xy})$ and $(g^y, g^z)$ respectively, implying that $O_b$'s spending condition reduces to $\texttt{proveDlog}(g^{xy}) \lor \texttt{proveDlogH}(g^y, g^{xy})$ and $O_{1-b}$'s reduces to $\texttt{proveDlog}(g^z) \lor \texttt{proveDlogH}(g^{y}, g^z)$. Thus, while Alice can spend $O_b$ using \texttt{proveDlogH} with her secret $x$, she cannot satisfy the spending condition of $O_{1-b}$. Similarly, Bob can only spend $O_{1-b}$ using \texttt{proveDlog} with his secret $y$. Bob must generate $O_0, O_1$ this way because he must prove that one of the outputs contains a valid DH tuple. +\paragraph{Security:} Observe that registers $(\alpha, \beta)$ of $O_b$ and $O_{1-b}$ contain $(g^y, g^{xy})$ and $(g^{xy}, g^y)$ respectively, implying that $O_b$'s spending condition reduces to +$\texttt{proveDlog}(g^{xy}) \lor \texttt{proveDHTuple}(g, g^y, g^x, g^{xy})$ and $O_{1-b}$'s reduces to $\texttt{proveDlog}(g^y) \lor \texttt{proveDHTuple}(g, g^{xy}, g^x, g^y)$. Thus, while Alice can spend $O_b$ using \texttt{proveDHTuple} with her secret $x$, she cannot satisfy the spending condition of $O_{1-b}$. Similarly, Bob can only spend $O_{1-b}$ using \texttt{proveDlog} with his secret $y$. Bob must generate $O_0, O_1$ this way because he must prove that one of the outputs contains a valid DH tuple. -For privacy, observe that any two identical boxes protected by \texttt{halfMixScript} have {\em spender indistinguisbility} because each one is spent using a $\Sigma$-OR-proof that is zero-knowledge~\cite{Dam10}. Also, any algorithm that can distinghish the two boxes can be directly used to solve the {\em Decision Diffie Hellman} (DDH) problem. Thus, the boxes are indistingushable if the DDH problem is hard. +For privacy, observe that any two identical boxes protected by \texttt{halfMixScript} have {\em spender indistinguishability} because each one is spent using a $\Sigma$-OR-proof that is zero-knowledge~\cite{Dam10}. It can be shown that an algorithm that can distinguish the two boxes can be used to solve the {\em Decision Diffie Hellman} (DDH) problem. Thus, the boxes are indistinguishable if the DDH problem is hard. \paragraph{Comparing with CoinJoin:} CoinJoin~\cite{coinjoin} is a privacy enhancing protocol, where multiple parties provide inputs and create outputs in a single transaction computed interactively such that the original inputs and outputs are unlinked. The optimal use of CoinJoin is when two inputs of equal value are joined to generate two outputs of equal value, and the process is repeated, as depicted in Figure~\ref{fig:coinjoin}. This requires two parties to interactively sign a transaction offchain and this interactive nature is the primary drawback of CoinJoin, which \mixname aims to overcome. @@ -596,17 +598,10 @@ \subsubsection{Handling Fee In \mixname} The mix step will create an additional box with two tokens spendable by the second spender. \end{enumerate} -%Instead of paying fee in tokens, a miner ca accumulate the fee and pay directly in Ergs at the end. This - -% \textbf{Fee Accumulation:} Starting with a fixed number of tokens restricts the number of mixes. To allow arbitrary number of mixes, we suggest a {\em fee accumulation} strategy, where the fee is accumulated rather than paid upfront. -%For instance, fee for the mix transaction can be paid when spending the mixed outputs. -% The fee keeps accumulating as long as the coin is circulated within the system (i.e., sent back to the H-pool or used in another mix operation) and is paid when the coin exits the system. - -%\section{Finite-State Machine} \section{Conclusion} -This article described smart contracts written in \langname. The examples build upon concepts from the \langname tutorial~\cite{tutorial}. More advanced contracts will be discussed in another tutorial. +This article described smart contracts written in \langname. The examples build upon concepts from the \langname white-paper~\cite{whitepaper}. More advanced contracts will be discussed in another tutorial. \bibliographystyle{unsrt} \bibliography{sigmastate_protocols} -\end{document} \ No newline at end of file +\end{document} diff --git a/docs/wpaper/sigma.tex b/docs/wpaper/sigma.tex index cae84028b2..6c0896c013 100644 --- a/docs/wpaper/sigma.tex +++ b/docs/wpaper/sigma.tex @@ -548,7 +548,7 @@ \subsection{Background} The meaning of the proof corresponding to $\tnode(k)$ is ``the prover knows witnesses for at least $k$ children of this node''. Semantically, $\andnode$ and $\ornode$ are simply special cases of $\tnode$: the meaning of the proof corresponding $\andnode$ (respectively, $\ornode)$ is ``the prover knows witnesses for all children (respectively, at least one child) of this node''. However, $\andnode$ and $\ornode$ are implemented differently from $\tnode$ for efficiency. -For the purposes of this description, it does not matter what specific atomic $\Sigma$-protocols are used at the leaves. They can be, for example, \texttt{proveDlog(x)} for proving knowledge of the discrete logarithm $w$ of $x=g^w$, or \texttt{proveDHtuple(g1, g2, u1, u2)} for proving that $(g_1, g_2, u_1, u_2)$ form a Diffie-Hellman tuple, i.e., $\exists w \mbox{\ such\ that\ } u_1 = g_1^w \, \wedge \, u_2 = h_2^w$. In general, we will assume the prover has some secret $w$ and wants to prove some property of it. +For the purposes of this description, it does not matter what specific atomic $\Sigma$-protocols are used at the leaves. They can be, for example, \texttt{proveDlog(x)} for proving knowledge of the discrete logarithm $w$ of $x=g^w$, or \texttt{proveDHtuple(g1, g2, u1, u2)} for proving that $(g_1, g_2, u_1, u_2)$ form a Diffie-Hellman tuple, i.e., $\exists w \mbox{\ such\ that\ } u_1 = g_1^w \, \wedge \, u_2 = g_2^w$. In general, we will assume the prover has some secret $w$ and wants to prove some property of it. A $\Sigma$-protocol consist of three messages: @@ -561,13 +561,13 @@ \subsection{Background} In order to make an atomic $\Sigma$-protocol non-interactive using the so-called Fiat-Shamir heuristic, the Prover would compute $e$ by hashing $a$, and the Verifier would check that $e$ is indeed a hash of $a$. However, once atomic $\Sigma$-protocols are composed using $\andnode$, $\ornode$, and $\tnode$, the challenge computation becomes more involved, as we describe below. -$\Sigma$-protocols have the property of \emph{special honest-verifier zero-knowledge}. For the purposes of this description, it means that given a random $e$, it is possible to compute $a$ and $z$ without knowing the secret witness $w$ that is normally needed by the prover. This computation is called ``simulation''. Moreover, the triple $(a, e, z)$ computed via simulation is distributed identically to the triple $(a, e, z)$ that results form a $\Sigma$ protocol that is run by the honest prover (who knows the secret witness $w$) and verifier (who generates a uniform $e$). The trick that makes simulation possible is that the response $z$ is chosen by the simulator before commitment $a$, in contrast to the prover, who is forced to choose $a$ before $z$. +$\Sigma$-protocols have the property of \emph{special honest-verifier zero-knowledge}. For the purposes of this description, it means that given a random $e$, it is possible to compute $a$ and $z$ without knowing the secret witness $w$ that is normally needed by the prover. This computation is called ``simulation''. Moreover, the triple $(a, e, z)$ computed via simulation is distributed identically to the triple $(a, e, z)$ that results form a $\Sigma$-protocol that is run by the honest prover (who knows the secret witness $w$) and verifier (who generates a uniform $e$). The trick that makes simulation possible is that the response $z$ is chosen by the simulator before commitment $a$, in contrast to the prover, who is forced to choose $a$ before $z$. $\Sigma$-protocols used in this work must also satisfy a property of \emph{special soundness}, which means that given two triples $(a_1, e_1, z_1)$ and $(a_2, e_2, z_2)$ that are both accepted by the verifier, and $a_1=a_2$ while $e_1\neq e_2$, it is possible to compute the Prover's secret witness $w$ in polynomial time and thus directly verify that the statement claimed by the Prover is true. Note that this computation is never performed, because the Prover will never actually answer two different challenges $e_1\neq e_2$ for the same commitment $a_1=a_2$. In order for composition of $\Sigma$-protocols using $\andnode$, $\ornode$, and $\tnode$ to work, the challenge $e$ in all protocols must be a binary string of the same length, which we will call $t$. (This implies that if the Verifier performs arithmetic modulo $q$ on $e$, then $2^t - new CollOverArray[T](Array[T]()) + new CollOverArray[T](cT.emptyArray) } @NeverInline diff --git a/sigma-api/src/main/resources/special/sigma/SigmaDsl.scalan b/sigma-api/src/main/resources/special/sigma/SigmaDsl.scalan index b846712fc2..38136b80fd 100644 --- a/sigma-api/src/main/resources/special/sigma/SigmaDsl.scalan +++ b/sigma-api/src/main/resources/special/sigma/SigmaDsl.scalan @@ -166,7 +166,6 @@ package special.sigma { def anyOf(conditions: Ref[Coll[Boolean]]): Ref[Boolean] = this.builder.anyOf(conditions); def anyZK(conditions: Ref[Coll[SigmaProp]]): Ref[SigmaProp] = this.builder.anyZK(conditions); def xorOf(conditions: Ref[Coll[Boolean]]): Ref[Boolean] = this.builder.xorOf(conditions); - def PubKey(base64String: Ref[String]): Ref[SigmaProp] = this.builder.PubKey(base64String); def sigmaProp(b: Ref[Boolean]): Ref[SigmaProp] = this.builder.sigmaProp(b); def blake2b256(bytes: Ref[Coll[Byte]]): Ref[Coll[Byte]] = this.builder.blake2b256(bytes); def sha256(bytes: Ref[Coll[Byte]]): Ref[Coll[Byte]] = this.builder.sha256(bytes); @@ -191,7 +190,6 @@ package special.sigma { def anyOf(conditions: Ref[Coll[Boolean]]): Ref[Boolean]; def anyZK(conditions: Ref[Coll[SigmaProp]]): Ref[SigmaProp]; def xorOf(conditions: Ref[Coll[Boolean]]): Ref[Boolean]; - def PubKey(base64String: Ref[String]): Ref[SigmaProp]; def sigmaProp(b: Ref[Boolean]): Ref[SigmaProp]; def blake2b256(bytes: Ref[Coll[Byte]]): Ref[Coll[Byte]]; def sha256(bytes: Ref[Coll[Byte]]): Ref[Coll[Byte]]; diff --git a/sigma-api/src/main/scala/special/sigma/CostTable.scala b/sigma-api/src/main/scala/special/sigma/CostTable.scala deleted file mode 100644 index a1e7daafc9..0000000000 --- a/sigma-api/src/main/scala/special/sigma/CostTable.scala +++ /dev/null @@ -1,52 +0,0 @@ -package special.sigma - -case class CostTable(operCosts: Map[String, Double]) extends (String => Int) { - override def apply(operId: String): Int = { - operCosts.get(operId) match { - case Some(cost) => (cost * 1000000).toInt - case None => sys.error(s"Cannot find cost in CostTable for $operId") - } - } -} - -object CostTable { - type ExpressionCost = Int - val DefaultCosts = CostTable.fromSeq(Seq( - ("Const: () => Unit", 0.000001), - ("Const: () => Boolean", 0.000001), - ("Const: () => Byte", 0.000001), - ("Const: () => Short", 0.000001), - ("Const: () => Int", 0.000001), - ("Const: () => Long", 0.000001), - ("Const: () => BigInt", 0.000001), - ("Const: () => String", 0.000001), - ("Const: () => GroupElement", 0.000001), - ("Const: () => SigmaProp", 0.000001), - ("Const: () => Array[IV]", 0.000001), - ("Self$: Context => Box", 0.000001), - ("SelectField", 0.000001), - ("AccessKiloByteOfData", 0.000001), - ("AccessBox: Context => Box", 0.000001), - - ("GetVar: (Context, Byte) => Option[T]", 0.000001), - ("DeserializeVar: (Context, Byte) => Option[T]", 0.000001), - - ("GetRegister: (Box, Byte) => Option[T]", 0.000001), - ("DeserializeRegister: (Box, Byte) => Option[T]", 0.000001), - - ("ExtractRegisterAs: (Box,Byte) => Array[BigInt]", 0.000001), - ("SigmaPropIsValid: SigmaProp => Boolean", 0.000001), - ("SigmaPropBytes: SigmaProp => Array[Byte]", 0.000001), - ("BinAnd: (Boolean, Boolean) => Boolean", 0.000001), - ("BinOr: (Boolean, Boolean) => Boolean", 0.000001), - ("BinXor: (Boolean, Boolean) => Boolean", 0.000001), - ("+: (BigInt, BigInt) => BigInt", 0.0001), - ("+_per_item: (BigInt, BigInt) => BigInt", 0.000001) - )) - - def fromSeq(items: Seq[(String, Double)]): CostTable = { - CostTable(items.toMap) - } -} - - diff --git a/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala b/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala index c4c92b7d4b..78488d11cf 100644 --- a/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala +++ b/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala @@ -9,18 +9,34 @@ import scalan._ @scalan.Liftable trait CostModel { + /** Cost of accessing SELF box and/or each item in INPUTS, OUTPUTS, dataInputs. */ def AccessBox: Int // costOf("AccessBox: Context => Box") + + // TODO refactor: remove not used def AccessAvlTree: Int // costOf("AccessAvlTree: Context => AvlTree") + /** Cost of accessing context variable (`getVar` operation in ErgoScript). */ def GetVar: Int // costOf("ContextVar: (Context, Byte) => Option[T]") + + // TODO refactor: remove not used def DeserializeVar: Int // costOf("DeserializeVar: (Context, Byte) => Option[T]") + /** Cost of accessing register in a box (e.g. `R4[Int]` operation in ErgoScript). */ def GetRegister: Int // costOf("AccessRegister: (Box,Byte) => Option[T]") + + // TODO refactor: remove not used def DeserializeRegister: Int // costOf("DeserializeRegister: (Box,Byte) => Option[T]") + /** Cost of accessing a property of an object like Header or AvlTree. */ def SelectField: Int // costOf("SelectField") + + // TODO refactor: remove not used def CollectionConst: Int // costOf("Const: () => Array[IV]") + + // TODO refactor: remove not used def AccessKiloByteOfData: Int // costOf("AccessKiloByteOfData") + + // TODO refactor: remove not used /** Size of public key in bytes */ def PubKeySize: Long } @@ -674,8 +690,6 @@ trait SigmaContract { def xorOf(conditions: Coll[Boolean]): Boolean = this.builder.xorOf(conditions) - def PubKey(base64String: String): SigmaProp = this.builder.PubKey(base64String) - def sigmaProp(b: Boolean): SigmaProp = this.builder.sigmaProp(b) def blake2b256(bytes: Coll[Byte]): Coll[Byte] = this.builder.blake2b256(bytes) @@ -713,46 +727,113 @@ trait SigmaContract { @scalan.Liftable @WithMethodCallRecognizers trait SigmaDslBuilder { + + /** Access to collection operations. */ def Colls: CollBuilder + + /** Access to Monoid operations */ def Monoids: MonoidBuilder + + /** Access to operations used in cost computation. */ def Costing: CostedBuilder + + /** Access to cost model (aka CostTable) parameters. */ def CostModel: CostModel - def verifyZK(cond: => SigmaProp): Boolean + def verifyZK(cond: => SigmaProp): Boolean // TODO refactor: can be removed after TestSigmaDslBuilder is removed + /** + * Logical threshold operation. + * AtLeast has two inputs: integer `bound`` and a collection of `props` same as in anyZK/allZK. + * @param bound number of props which should be proven in order to satisfy verifier + * @param props a collection of sigma propositions of which at least the `bound` number should be proved. + * @return THRESHOLD sigma protocol proposition wrapped in SigmaProp value. + */ def atLeast(bound: Int, props: Coll[SigmaProp]): SigmaProp + /** @return true if all the elements in collection are true. */ def allOf(conditions: Coll[Boolean]): Boolean + + /** Returns a sigma proposition which is proven when ALL the propositions in the `conditions` are proven. + * @param conditions a collection of propositions + * @return AND sigma protocol proposition + */ def allZK(conditions: Coll[SigmaProp]): SigmaProp + /** Returns true if at least one element in the `conditions` is true, otherwise false. */ def anyOf(conditions: Coll[Boolean]): Boolean + + /** Returns a sigma proposition which is proven when at least one of the propositions in the `conditions` is proven. + * @param conditions a collection of propositions + * @return OR sigma protocol proposition + */ def anyZK(conditions: Coll[SigmaProp]): SigmaProp + /** Similar to `allOf`, but performing logical XOR operation between all conditions. */ def xorOf(conditions: Coll[Boolean]): Boolean - def PubKey(base64String: String): SigmaProp - + /** Creates trivial sigma proposition with the given underlying Boolean value. + * @param b boolean value to be wrapped into SigmaProp + * @return sigma proposition with can be combined with other SigmaProp values + */ def sigmaProp(b: Boolean): SigmaProp + /** Calculate Blake2b256 hash from the input `bytes`. */ def blake2b256(bytes: Coll[Byte]): Coll[Byte] + + /** Calculate Sha256 hash from the input `bytes`.*/ def sha256(bytes: Coll[Byte]): Coll[Byte] + /** Convert big-endian `bytes` representation (Coll[Byte]) to the corresponding BigInt value. + * @param bytes collection of bytes in big-endian format + */ def byteArrayToBigInt(bytes: Coll[Byte]): BigInt + + /** Converts Long value `l` to the big-endian bytes representation. */ def longToByteArray(l: Long): Coll[Byte] + + /** Convert big-endian `bytes` representation (Coll[Byte]) to the corresponding Long value. */ def byteArrayToLong(bytes: Coll[Byte]): Long + /** Creates a new SigmaProp value representing public key of the discrete logarithm + * signature protocol. + * @param g an element of the elliptic curve group which serves as the public key + */ def proveDlog(g: GroupElement): SigmaProp + + /** Creates a new SigmaProp value representing sigma proposition of the Diffie Hellman + * signature protocol. Common input: (g,h,u,v) + */ def proveDHTuple(g: GroupElement, h: GroupElement, u: GroupElement, v: GroupElement): SigmaProp - /** - * The generator g of the group is an element of the group such that, when written multiplicatively, every element - * of the group is a power of g. + /** The generator g of the group is an element of the group such that, when written + * multiplicative form, every element of the group is a power of g. * @return the generator of this Dlog group */ def groupGenerator: GroupElement + /** + * Transforms serialized bytes of ErgoTree with segregated constants by replacing constants + * at given positions with new values. This operation allow to use serialized scripts as + * pre-defined templates. + * The typical usage is "check that output box have proposition equal to given script bytes, + * where minerPk (constants(0)) is replaced with currentMinerPk". + * Each constant in original scriptBytes have SType serialized before actual data (see ConstantSerializer). + * During substitution each value from newValues is checked to be an instance of the corresponding type. + * This means, the constants during substitution cannot change their types. + * + * @param scriptBytes serialized ErgoTree with ConstantSegregationFlag set to 1. + * @param positions zero based indexes in ErgoTree.constants array which should be replaced with new values + * @param newValues new values to be injected into the corresponding positions in ErgoTree.constants array + * @return original scriptBytes array where only specified constants are replaced and all other bytes remain exactly the same + */ @Reified("T") def substConstants[T](scriptBytes: Coll[Byte], positions: Coll[Int], newValues: Coll[T])(implicit cT: RType[T]): Coll[Byte] + + /** Decodes the given bytes to the corresponding GroupElement using default serialization. + * @param encoded serialized bytes of some GroupElement value + * @see GroupElementSerializer + */ def decodePoint(encoded: Coll[Byte]): GroupElement /** Create DSL big integer from existing `java.math.BigInteger`*/ @@ -764,6 +845,7 @@ trait SigmaDslBuilder { /** Construct a new authenticated dictionary with given parameters and tree root digest. */ def avlTree(operationFlags: Byte, digest: Coll[Byte], keyLength: Int, valueLengthOpt: Option[Int]): AvlTree + /** Returns a byte-wise XOR of the two collections of bytes. */ def xor(l: Coll[Byte], r: Coll[Byte]): Coll[Byte] } diff --git a/sigma-api/src/main/scala/special/sigma/package.scala b/sigma-api/src/main/scala/special/sigma/package.scala index 92b4069b2c..07bac16e47 100644 --- a/sigma-api/src/main/scala/special/sigma/package.scala +++ b/sigma-api/src/main/scala/special/sigma/package.scala @@ -6,16 +6,11 @@ import org.bouncycastle.math.ec.ECPoint import scalan.RType import scalan.RType.GeneralType -import scala.reflect.{ClassTag, classTag} - -package sigma { - - case class ArgType(override val name: String) extends RType[Any] { - override def classTag: ClassTag[Any] = ClassTag.Any - override def isConstantSize: Boolean = false // pessimistic but safe default - } -} +import scala.reflect.classTag +/** The following implicit values are used as type descriptors of all the predefined Sigma types. + * @see [[RType]] class + */ package object sigma { implicit val BigIntRType: RType[BigInt] = new GeneralType(classTag[BigInt]) { @@ -34,8 +29,6 @@ package object sigma { implicit val BoxRType: RType[Box] = GeneralType(classTag[Box]) implicit val ContextRType: RType[Context] = GeneralType(classTag[Context]) - // these are not wrapper types since they are used directly in ErgoTree values (e.g. Constants) - // and no conversion is necessary implicit val HeaderRType: RType[Header] = new GeneralType(classTag[Header]) { override def isConstantSize: Boolean = true } @@ -64,5 +57,4 @@ package object sigma { implicit val SizeContextRType: RType[SizeContext] = RType.fromClassTag(classTag[SizeContext]) implicit val SizeBuilderRType: RType[SizeBuilder] = RType.fromClassTag(classTag[SizeBuilder]) - def argRType(name: String): RType[Any] = ArgType(name) } \ No newline at end of file diff --git a/sigma-impl/src/main/scala/special/sigma/SigmaDslOverArrays.scala b/sigma-impl/src/main/scala/special/sigma/SigmaDslOverArrays.scala index d092fb40a1..b1bf83ca6d 100644 --- a/sigma-impl/src/main/scala/special/sigma/SigmaDslOverArrays.scala +++ b/sigma-impl/src/main/scala/special/sigma/SigmaDslOverArrays.scala @@ -13,13 +13,14 @@ import special.SpecialPredef import special.collection._ import scalan.util.Extensions.BigIntegerOps +// TODO refactor: this class is not necessary and can be removed class TestSigmaDslBuilder extends SigmaDslBuilder { // manual fix def Colls: CollBuilder = new CollOverArrayBuilder def Monoids: MonoidBuilder = new MonoidBuilderInst def Costing: CostedBuilder = new CCostedBuilder @NeverInline - def CostModel: CostModel = new TestCostModel + def CostModel: CostModel = ??? @NeverInline def verifyZK(proof: => SigmaProp): Boolean = proof.isValid @@ -52,9 +53,6 @@ class TestSigmaDslBuilder extends SigmaDslBuilder { @NeverInline def sha256(bytes: Coll[Byte]): Coll[Byte] = Colls.fromArray(Sha256.hash(bytes.toArray)) - @NeverInline - def PubKey(base64String: String): SigmaProp = ??? - @NeverInline def byteArrayToBigInt(bytes: Coll[Byte]): BigInt = { val bi = new BigInteger(bytes.toArray).to256BitValueExact diff --git a/sigma-impl/src/main/scala/special/sigma/TestCostModel.scala b/sigma-impl/src/main/scala/special/sigma/TestCostModel.scala deleted file mode 100644 index 2a7dd319e5..0000000000 --- a/sigma-impl/src/main/scala/special/sigma/TestCostModel.scala +++ /dev/null @@ -1,21 +0,0 @@ -package special.sigma - -import scala.reflect.ClassTag -import scalan.Internal - -@Internal -class TestCostModel extends CostModel { - def AccessBox: Int = CostTable.DefaultCosts("AccessBox: Context => Box") - def AccessAvlTree: Int = CostTable.DefaultCosts("AccessAvlTree: Context => AvlTree") - - def GetVar: Int = CostTable.DefaultCosts("GetVar: (Context, Byte) => Option[T]") - def DeserializeVar: Int = CostTable.DefaultCosts("DeserializeVar: (Context, Byte) => Option[T]") - - def GetRegister: Int = CostTable.DefaultCosts("GetRegister: (Box, Byte) => Option[T]") - def DeserializeRegister: Int = CostTable.DefaultCosts("DeserializeRegister: (Box, Byte) => Option[T]") - - def SelectField: Int = CostTable.DefaultCosts("SelectField") - def CollectionConst: Int = CostTable.DefaultCosts("Const: () => Array[IV]") - def AccessKiloByteOfData: Int = CostTable.DefaultCosts("AccessKiloByteOfData") - override def PubKeySize: Long = 32 -} diff --git a/sigma-library/src/main/scala/special/sigma/SigmaDslUnit.scala b/sigma-library/src/main/scala/special/sigma/SigmaDslUnit.scala index a31b4e4fde..9d47cf1cc2 100644 --- a/sigma-library/src/main/scala/special/sigma/SigmaDslUnit.scala +++ b/sigma-library/src/main/scala/special/sigma/SigmaDslUnit.scala @@ -169,7 +169,6 @@ package special.sigma { def anyOf(conditions: Ref[Coll[Boolean]]): Ref[Boolean] = this.builder.anyOf(conditions); def anyZK(conditions: Ref[Coll[SigmaProp]]): Ref[SigmaProp] = this.builder.anyZK(conditions); def xorOf(conditions: Ref[Coll[Boolean]]): Ref[Boolean] = this.builder.xorOf(conditions); - def PubKey(base64String: Ref[String]): Ref[SigmaProp] = this.builder.PubKey(base64String); def sigmaProp(b: Ref[Boolean]): Ref[SigmaProp] = this.builder.sigmaProp(b); def blake2b256(bytes: Ref[Coll[Byte]]): Ref[Coll[Byte]] = this.builder.blake2b256(bytes); def sha256(bytes: Ref[Coll[Byte]]): Ref[Coll[Byte]] = this.builder.sha256(bytes); @@ -194,7 +193,6 @@ package special.sigma { def anyOf(conditions: Ref[Coll[Boolean]]): Ref[Boolean]; def anyZK(conditions: Ref[Coll[SigmaProp]]): Ref[SigmaProp]; def xorOf(conditions: Ref[Coll[Boolean]]): Ref[Boolean]; - def PubKey(base64String: Ref[String]): Ref[SigmaProp]; def sigmaProp(b: Ref[Boolean]): Ref[SigmaProp]; def blake2b256(bytes: Ref[Coll[Byte]]): Ref[Coll[Byte]]; def sha256(bytes: Ref[Coll[Byte]]): Ref[Coll[Byte]]; diff --git a/sigma-library/src/main/scala/special/sigma/impl/SigmaDslImpl.scala b/sigma-library/src/main/scala/special/sigma/impl/SigmaDslImpl.scala index 0682271b7e..b964d94b0b 100644 --- a/sigma-library/src/main/scala/special/sigma/impl/SigmaDslImpl.scala +++ b/sigma-library/src/main/scala/special/sigma/impl/SigmaDslImpl.scala @@ -3151,7 +3151,7 @@ object SigmaContract extends EntityObject("SigmaContract") { override protected def collectMethods: Map[java.lang.reflect.Method, MethodDesc] = { super.collectMethods ++ Elem.declaredMethods(classOf[SigmaContract], classOf[SSigmaContract], Set( - "builder", "Collection", "verifyZK", "atLeast", "allOf", "allZK", "anyOf", "anyZK", "xorOf", "PubKey", "sigmaProp", "blake2b256", "sha256", "byteArrayToBigInt", "longToByteArray", "byteArrayToLong", "proveDlog", "proveDHTuple", "groupGenerator", "decodePoint", "substConstants" + "builder", "Collection", "verifyZK", "atLeast", "allOf", "allZK", "anyOf", "anyZK", "xorOf", "sigmaProp", "blake2b256", "sha256", "byteArrayToBigInt", "longToByteArray", "byteArrayToLong", "proveDlog", "proveDHTuple", "groupGenerator", "decodePoint", "substConstants" )) } } @@ -3268,13 +3268,6 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { true, false, element[Boolean])) } - override def PubKey(base64String: Ref[String]): Ref[SigmaProp] = { - asRep[SigmaProp](mkMethodCall(self, - SigmaDslBuilderClass.getMethod("PubKey", classOf[Sym]), - Array[AnyRef](base64String), - true, false, element[SigmaProp])) - } - override def sigmaProp(b: Ref[Boolean]): Ref[SigmaProp] = { asRep[SigmaProp](mkMethodCall(self, SigmaDslBuilderClass.getMethod("sigmaProp", classOf[Sym]), @@ -3468,13 +3461,6 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { true, true, element[Boolean])) } - def PubKey(base64String: Ref[String]): Ref[SigmaProp] = { - asRep[SigmaProp](mkMethodCall(source, - SigmaDslBuilderClass.getMethod("PubKey", classOf[Sym]), - Array[AnyRef](base64String), - true, true, element[SigmaProp])) - } - def sigmaProp(b: Ref[Boolean]): Ref[SigmaProp] = { asRep[SigmaProp](mkMethodCall(source, SigmaDslBuilderClass.getMethod("sigmaProp", classOf[Sym]), @@ -3583,7 +3569,7 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { override protected def collectMethods: Map[java.lang.reflect.Method, MethodDesc] = { super.collectMethods ++ Elem.declaredMethods(classOf[SigmaDslBuilder], classOf[SSigmaDslBuilder], Set( - "Colls", "Monoids", "Costing", "CostModel", "verifyZK", "atLeast", "allOf", "allZK", "anyOf", "anyZK", "xorOf", "PubKey", "sigmaProp", "blake2b256", "sha256", "byteArrayToBigInt", "longToByteArray", "byteArrayToLong", "proveDlog", "proveDHTuple", "groupGenerator", "substConstants", "decodePoint", "avlTree", "xor" + "Colls", "Monoids", "Costing", "CostModel", "verifyZK", "atLeast", "allOf", "allZK", "anyOf", "anyZK", "xorOf", "sigmaProp", "blake2b256", "sha256", "byteArrayToBigInt", "longToByteArray", "byteArrayToLong", "proveDlog", "proveDHTuple", "groupGenerator", "substConstants", "decodePoint", "avlTree", "xor" )) } } @@ -3715,16 +3701,6 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { def unapply(exp: Sym): Nullable[(Ref[SigmaDslBuilder], Ref[Coll[Boolean]])] = unapply(exp.node) } - object PubKey { - def unapply(d: Def[_]): Nullable[(Ref[SigmaDslBuilder], Ref[String])] = d match { - case MethodCall(receiver, method, args, _) if method.getName == "PubKey" && receiver.elem.isInstanceOf[SigmaDslBuilderElem[_]] => - val res = (receiver, args(0)) - Nullable(res).asInstanceOf[Nullable[(Ref[SigmaDslBuilder], Ref[String])]] - case _ => Nullable.None - } - def unapply(exp: Sym): Nullable[(Ref[SigmaDslBuilder], Ref[String])] = unapply(exp.node) - } - object sigmaProp { def unapply(d: Def[_]): Nullable[(Ref[SigmaDslBuilder], Ref[Boolean])] = d match { case MethodCall(receiver, method, args, _) if method.getName == "sigmaProp" && receiver.elem.isInstanceOf[SigmaDslBuilderElem[_]] => diff --git a/sigmastate/src/main/scala/org/ergoplatform/ErgoAddress.scala b/sigmastate/src/main/scala/org/ergoplatform/ErgoAddress.scala index 6af5c4fca6..6adc685cc6 100644 --- a/sigmastate/src/main/scala/org/ergoplatform/ErgoAddress.scala +++ b/sigmastate/src/main/scala/org/ergoplatform/ErgoAddress.scala @@ -4,11 +4,11 @@ import java.util import com.google.common.primitives.Ints import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix -import scorex.crypto.hash.{Blake2b256, Digest32} +import scorex.crypto.hash.{Digest32, Blake2b256} import scorex.util.encode.Base58 import sigmastate.Values._ import sigmastate._ -import sigmastate.basics.DLogProtocol.{ProveDlog, ProveDlogProp} +import sigmastate.basics.DLogProtocol.{ProveDlogProp, ProveDlog} import sigmastate.lang.exceptions.SigmaException import sigmastate.serialization._ import sigmastate.utxo.{DeserializeContext, Slice} @@ -64,15 +64,33 @@ import scala.util.Try * address = prefix byte ++ content bytes ++ checksum * */ - sealed trait ErgoAddress { + /** Address type code used to differentiate between pay-to-public-key, pay-to-script, + * pay-to-script-hash addresses. + * + * NOTE: Network type code is defined by [[ErgoAddressEncoder]] attached to each ErgoAddress + * instance and it is not included in this value. + * + * @see [[P2PKAddress]], [[Pay2SAddress]], [[Pay2SHAddress]] + */ val addressTypePrefix: Byte + /** Serialized bytes of the address content (depending on the address type). + * Doesn't include network type and address type prefix byte. + * @see [[P2PKAddress]], [[Pay2SAddress]], [[Pay2SHAddress]] + */ val contentBytes: Array[Byte] + /** ErgoTree which corresponds to the address (depending on the address type). + * @see [[P2PKAddress]], [[Pay2SAddress]], [[Pay2SHAddress]] + */ val script: ErgoTree + + /** Network type code to be used in address encoding. */ + def networkPrefix: NetworkPrefix } +/** Implementation of pay-to-public-key [[ErgoAddress]]. */ class P2PKAddress(val pubkey: ProveDlog, val pubkeyBytes: Array[Byte]) (implicit val encoder: ErgoAddressEncoder) extends ErgoAddress { @@ -83,10 +101,12 @@ class P2PKAddress(val pubkey: ProveDlog, override val script: ErgoTree = { // NOTE: we don't segregate constants because the whole tree is single constant - // and we want different addreses of this type to have different `script` values + // and we want different addresses of this type to have different `script` values ErgoTree(ErgoTree.DefaultHeader, ErgoTree.EmptyConstants, SigmaPropConstant(pubkey)) } + override def networkPrefix: NetworkPrefix = encoder.networkPrefix + override def equals(obj: Any): Boolean = obj match { case p2pk: P2PKAddress => util.Arrays.equals(pubkeyBytes, p2pk.pubkeyBytes) case _ => false @@ -98,27 +118,45 @@ class P2PKAddress(val pubkey: ProveDlog, } object P2PKAddress { + /** Value added to the prefix byte in the serialized bytes of an encoded P2PK address. + * @see [[ErgoAddressEncoder.toString]] + */ val addressTypePrefix: Byte = 1: Byte + /** Constructs [[P2PKAddress]] instance using the public key of the given [[ProveDlog]]. */ def apply(pubkey: ProveDlog)(implicit encoder: ErgoAddressEncoder): P2PKAddress = { val bs = GroupElementSerializer.toBytes(pubkey.h) new P2PKAddress(pubkey, bs) } } +/** Implementation of pay-to-script-hash [[ErgoAddress]]. */ class Pay2SHAddress(val scriptHash: Array[Byte])(implicit val encoder: ErgoAddressEncoder) extends ErgoAddress { override val addressTypePrefix: Byte = Pay2SHAddress.addressTypePrefix override val contentBytes: Array[Byte] = scriptHash + override def networkPrefix: NetworkPrefix = encoder.networkPrefix + import Pay2SHAddress._ - /** The proposition which checks that `contextVar(1)` has original script, - * which evaluates to true and also whose hash equals to this `scriptHash`. - * Assumes the context variable accessed as getVar[Coll[Byte]](1)` to contain serialized original script bytes. - * @see ErgoLikeInterpreterSpecification."P2SH - 160 bits" test - * similar script checked in "P2SH - 160 bits" test in sigma repository, but here we use 192 bits - **/ + /** The proposition which checks that `contextVar(scriptId)` has original script + * (whose hash equals to this [[scriptHash]]) which evaluates to true. + * + * Assumes the context variable is accessed as `getVar[Coll[Byte]](1).get` + * and contains serialized original script bytes. + * + * NOTE: This script is not stored in [[contentBytes]] of the address. + * So the address doesn't depend on this script which means this specific script can be + * changed without breaking the addresses. + * + * NOTE: The ErgoTree is created without segregation of the constants. + * + * NOTE: that P2SH address is using 192-bits hash, unlike the "P2SH - 160 bits" example + * (and well-known P2SH addresses in Bitcoin) + * + * @see ErgoLikeInterpreterSpecification the "P2SH - 160 bits" test + */ override val script = { val hashEquals = EQ( Slice(CalcBlake2b256(GetVarByteArray(scriptId).get), IntConstant(0), IntConstant(24)), @@ -139,22 +177,37 @@ class Pay2SHAddress(val scriptHash: Array[Byte])(implicit val encoder: ErgoAddre } object Pay2SHAddress { + /** An id of the context variable used in pay-to-script-hash address script. + * @see [[Pay2SHAddress.script]] + */ val scriptId = 1: Byte + + /** Value added to the prefix byte in the serialized bytes of an encoded P2SH address. + * @see [[ErgoAddressEncoder.toString]] + */ val addressTypePrefix: Byte = 2: Byte /** Create Pay-to-script-hash address with the given underlying script (ErgoTree). + * + * The tree is first transformed to proposition, substituting the constants if necessary + * and then the other constructor is called. * * @param script ErgoTree representation of guarding script - * @param encoder address encoder which is used to encode address bytes as String */ + * @param encoder address encoder which is used to encode address bytes as String + */ def apply(script: ErgoTree)(implicit encoder: ErgoAddressEncoder): Pay2SHAddress = { val prop = script.toProposition(replaceConstants = script.isConstantSegregation) apply(prop) } /** Create Pay-to-script-hash address with the given underlying proposition (SigmaPropValue). + * + * The proposition is serialized to bytes, then hashed (Blake2b256) and then 24 + * bytes of the hash are taken as contentBytes of the address. * * @param prop Value representation of guarding script (aka proposition) - * @param encoder address encoder which is used to encode address bytes as String */ + * @param encoder address encoder which is used to encode address bytes as String + */ def apply(prop: SigmaPropValue)(implicit encoder: ErgoAddressEncoder): Pay2SHAddress = { val sb = ValueSerializer.serialize(prop) val sbh = ErgoAddressEncoder.hash192(sb) @@ -162,6 +215,7 @@ object Pay2SHAddress { } } +/** Implementation of pay-to-script [[ErgoAddress]]. */ class Pay2SAddress(override val script: ErgoTree, val scriptBytes: Array[Byte]) (implicit val encoder: ErgoAddressEncoder) extends ErgoAddress { @@ -169,6 +223,8 @@ class Pay2SAddress(override val script: ErgoTree, override val contentBytes: Array[Byte] = scriptBytes + override def networkPrefix: NetworkPrefix = encoder.networkPrefix + override def equals(obj: Any): Boolean = obj match { case p2s: Pay2SAddress => util.Arrays.equals(scriptBytes, p2s.scriptBytes) case _ => false @@ -180,23 +236,35 @@ class Pay2SAddress(override val script: ErgoTree, } object Pay2SAddress { + /** Value added to the prefix byte in the serialized bytes of an encoded P2S address. + * @see [[ErgoAddressEncoder.toString()]] + */ val addressTypePrefix: Byte = 3: Byte + /** Create Pay-to-script address with the given underlying script (ErgoTree). + * + * The tree is serialized to contentBytes using default ErgoTree serializer. + * + * @param script ErgoTree representation of guarding script + * @param encoder address encoder which is used to encode address bytes as String + */ def apply(script: ErgoTree)(implicit encoder: ErgoAddressEncoder): Pay2SAddress = { val sb = ErgoTreeSerializer.DefaultSerializer.serializeErgoTree(script) new Pay2SAddress(script, sb) } } - +/** Network-aware encoder for ErgoAddress <-> Base58String conversions. + * @param networkPrefix network prefix value to be used in address encoding. + */ case class ErgoAddressEncoder(networkPrefix: NetworkPrefix) { import ErgoAddressEncoder._ + /** This value is be used implicitly in the methods below. */ implicit private val ergoAddressEncoder: ErgoAddressEncoder = this - val ChecksumLength = 4 - + /** Converts the given [[ErgoAddress]] to Base58 string. */ def toString(address: ErgoAddress): String = { val withNetworkByte = (networkPrefix + address.addressTypePrefix).toByte +: address.contentBytes @@ -204,11 +272,14 @@ case class ErgoAddressEncoder(networkPrefix: NetworkPrefix) { Base58.encode(withNetworkByte ++ checksum) } + /** Returns true if the given `addrHeadByte` is a header byte of a testnet address, false otherwise. */ def isTestnetAddress(addrHeadByte: Byte): Boolean = addrHeadByte > TestnetNetworkPrefix + /** Returns true if the given `addrHeadByte` is a header byte of a mainnet address, false otherwise. */ def isMainnetAddress(addrHeadByte: Byte): Boolean = addrHeadByte < TestnetNetworkPrefix - def fromString(addrStr: String): Try[ErgoAddress] = Base58.decode(addrStr).flatMap { bytes => + /** Converts the given Base58 string to [[ErgoAddress]] or an error packed in Try. */ + def fromString(addrBase58Str: String): Try[ErgoAddress] = Base58.decode(addrBase58Str).flatMap { bytes => Try { val headByte = bytes.head networkPrefix match { @@ -219,7 +290,7 @@ case class ErgoAddressEncoder(networkPrefix: NetworkPrefix) { val (withoutChecksum, checksum) = bytes.splitAt(bytes.length - ChecksumLength) if (!util.Arrays.equals(hash256(withoutChecksum).take(ChecksumLength), checksum)) { - throw new Exception(s"Checksum check fails for $addrStr") + throw new Exception(s"Checksum check fails for $addrBase58Str") } val contentBytes = withoutChecksum.tail @@ -231,7 +302,7 @@ case class ErgoAddressEncoder(networkPrefix: NetworkPrefix) { new P2PKAddress(ProveDlog(p), contentBytes) case Pay2SHAddress.addressTypePrefix => if (contentBytes.length != 24) { //192-bits hash used - throw new Exception(s"Improper content in P2SH script: $addrStr") + throw new Exception(s"Improper content in P2SH script: $addrBase58Str") } new Pay2SHAddress(contentBytes) case Pay2SAddress.addressTypePrefix => @@ -243,6 +314,9 @@ case class ErgoAddressEncoder(networkPrefix: NetworkPrefix) { } } + /** Pattern recognizer of [[Pay2SHAddress.script]] propositions. + * If matched extracts the corresponding hash bytes as `Coll[Byte]` value. + */ object IsPay2SHAddress { def unapply(exp: SigmaPropValue): Option[Coll[Byte]] = exp match { case SigmaAnd(Seq( @@ -255,6 +329,11 @@ case class ErgoAddressEncoder(networkPrefix: NetworkPrefix) { } } + /** Converts the given [[ErgoTree]] to the corresponding [[ErgoAddress]]. + * It is inverse of [[ErgoAddress.script]] such that `fromProposition(addr.script) == addr` + * + * @return Failure(ex) if the `proposition` cannot be converted to any type of address. + */ def fromProposition(proposition: ErgoTree): Try[ErgoAddress] = Try { proposition.root match { case Right(SigmaPropConstant(ProveDlogProp(d))) => P2PKAddress(d) @@ -269,11 +348,21 @@ case class ErgoAddressEncoder(networkPrefix: NetworkPrefix) { } object ErgoAddressEncoder { + /** Type of the network prefix value. */ type NetworkPrefix = Byte + + /** Value of the prefix byte used to encode Mainnet ErgoAddress. */ val MainnetNetworkPrefix: NetworkPrefix = 0.toByte + + /** Value of the prefix byte used to encode Testnet ErgoAddress. */ val TestnetNetworkPrefix: NetworkPrefix = 16.toByte + /** Length of the checksum section of encoded ergo address bytes. */ + val ChecksumLength = 4 + + /** Helper method to hash the given array using Blake2b256. */ def hash256(input: Array[Byte]): Digest32 = Blake2b256(input) + /** Helper method to hash the given array using Blake2b256 and take first 192 bit (24 bytes). */ def hash192(input: Array[Byte]): Array[Byte] = hash256(input).take(24) } \ No newline at end of file diff --git a/sigmastate/src/main/scala/org/ergoplatform/ErgoBox.scala b/sigmastate/src/main/scala/org/ergoplatform/ErgoBox.scala index 0ab065f61c..90fb04a8fd 100644 --- a/sigmastate/src/main/scala/org/ergoplatform/ErgoBox.scala +++ b/sigmastate/src/main/scala/org/ergoplatform/ErgoBox.scala @@ -1,10 +1,10 @@ package org.ergoplatform import com.google.common.primitives.Shorts -import org.ergoplatform.ErgoBox.{NonMandatoryRegisterId, TokenId} +import org.ergoplatform.ErgoBox.{TokenId, NonMandatoryRegisterId} import org.ergoplatform.settings.ErgoAlgos import scorex.crypto.authds.ADKey -import scorex.crypto.hash.{Blake2b256, Digest32} +import scorex.crypto.hash.{Digest32, Blake2b256} import scorex.util._ import sigmastate.SCollection.SByteArray import sigmastate.SType.AnyOps @@ -13,7 +13,7 @@ import sigmastate._ import sigmastate.eval.Extensions._ import sigmastate.eval._ import sigmastate.serialization.SigmaSerializer -import sigmastate.utils.{Helpers, SigmaByteReader, SigmaByteWriter} +import sigmastate.utils.{SigmaByteReader, SigmaByteWriter, Helpers} import sigmastate.utxo.ExtractCreationInfo import special.collection._ @@ -42,7 +42,7 @@ import scala.runtime.ScalaRunTime * @param additionalTokens - secondary tokens the box contains * @param additionalRegisters - additional registers the box can carry over * @param transactionId - id of transaction which created the box - * @param index - number of box (from 0 to total number of boxes the transaction with transactionId created - 1) + * @param index - index of the box (from 0 to total number of boxes the transaction with transactionId created - 1) * @param creationHeight - height when a transaction containing the box was created. * This height is declared by user and should not exceed height of the block, * containing the transaction with this box. @@ -60,6 +60,7 @@ class ErgoBox( import ErgoBox._ + /** Blake2b256 hash of the serialized `bytes`. */ lazy val id: BoxId = ADKey @@ Blake2b256.hash(bytes) override def get(identifier: RegisterId): Option[Value[SType]] = { @@ -71,6 +72,9 @@ class ErgoBox( } } + /** Serialized content of this box. + * @see [[ErgoBox.sigmaSerializer]] + */ lazy val bytes: Array[Byte] = ErgoBox.sigmaSerializer.toBytes(this) override def equals(arg: Any): Boolean = arg match { @@ -78,9 +82,13 @@ class ErgoBox( case _ => false } + // TODO refactor: fix hashCode, it should be consistent with [[equals]] and should be based on [[id]] override def hashCode(): Int = ScalaRunTime._hashCode((value, ergoTree, additionalTokens, additionalRegisters, index, creationHeight)) + /** Convert this box to [[ErgoBoxCandidate]] by forgetting transaction reference data + * (transactionId, index). + */ def toCandidate: ErgoBoxCandidate = new ErgoBoxCandidate(value, ergoTree, creationHeight, additionalTokens, additionalRegisters) @@ -92,6 +100,7 @@ class ErgoBox( object ErgoBox { type BoxId = ADKey object BoxId { + /** Size in bytes of the box identifier. */ val size: Short = 32 } @@ -118,6 +127,8 @@ object ErgoBox { abstract class MandatoryRegisterId(override val number: Byte, val purpose: String) extends RegisterId abstract class NonMandatoryRegisterId(override val number: Byte) extends RegisterId + type AdditionalRegisters = Map[NonMandatoryRegisterId, _ <: EvaluatedValue[_ <: SType]] + object R0 extends MandatoryRegisterId(0, "Monetary value, in Ergo tokens") object R1 extends MandatoryRegisterId(1, "Guarding script") object R2 extends MandatoryRegisterId(2, "Secondary tokens") @@ -166,18 +177,6 @@ object ErgoBox { val allZerosModifierId: ModifierId = Array.fill[Byte](32)(0.toByte).toModifierId - def apply(value: Long, - ergoTree: ErgoTree, - creationHeight: Int, - additionalTokens: Seq[(TokenId, Long)] = Nil, - additionalRegisters: Map[NonMandatoryRegisterId, _ <: EvaluatedValue[_ <: SType]] = Map.empty, - transactionId: ModifierId = allZerosModifierId, - boxIndex: Short = 0): ErgoBox = - new ErgoBox(value, ergoTree, - Colls.fromArray(additionalTokens.toArray[(TokenId, Long)]), - additionalRegisters, - transactionId, boxIndex, creationHeight) - object sigmaSerializer extends SigmaSerializer[ErgoBox, ErgoBox] { override def serialize(obj: ErgoBox, w: SigmaByteWriter): Unit = { diff --git a/sigmastate/src/main/scala/org/ergoplatform/ErgoBoxCandidate.scala b/sigmastate/src/main/scala/org/ergoplatform/ErgoBoxCandidate.scala index a937fb4824..d5ada2cce3 100644 --- a/sigmastate/src/main/scala/org/ergoplatform/ErgoBoxCandidate.scala +++ b/sigmastate/src/main/scala/org/ergoplatform/ErgoBoxCandidate.scala @@ -42,15 +42,31 @@ class ErgoBoxCandidate(val value: Long, val additionalRegisters: Map[NonMandatoryRegisterId, _ <: EvaluatedValue[_ <: SType]] = Map()) extends ErgoBoxAssets { - def proposition: BoolValue = ergoTree.toProposition(ergoTree.isConstantSegregation).asBoolValue + /** Transforms this tree to a proposition, substituting the constants if the constant + * segregation flag is set. + * @see [[SigmaPropValue]] + */ + def proposition: SigmaPropValue = ergoTree.toProposition(ergoTree.isConstantSegregation) + /** Returns the serialized bytes of the guarding [[ErgoTree]]. */ lazy val propositionBytes: Array[Byte] = ergoTree.bytes + /** Serialized bytes of this Box without transaction reference data (transactionId and boxIndex). */ lazy val bytesWithNoRef: Array[Byte] = ErgoBoxCandidate.serializer.toBytes(this) + /** Creates a new [[ErgoBox]] based on this candidate using the given transaction reference data. + * + * @param txId id of transaction which created the box + * @param boxIndex index of the box in the transaction's OUTPUTS + */ def toBox(txId: ModifierId, boxIndex: Short) = new ErgoBox(value, ergoTree, additionalTokens, additionalRegisters, txId, boxIndex, creationHeight) + /** Extracts register by id. + * + * @param identifier id of the register to return. + * @return Some(value) if the register is present, None otherwise + */ def get(identifier: RegisterId): Option[Value[SType]] = { identifier match { case ValueRegId => Some(LongConstant(value)) @@ -72,14 +88,19 @@ class ErgoBoxCandidate(val value: Long, } } - override def hashCode(): Int = + // TODO refactor: fix hashCode, it should be consistent with [[equals]] and use [[bytesWithNoRef]] + // Since all five properties are also serialized as part of bytesWithNoRef, then this + // hashCode is safe, but less efficient and simple than it can be. + override def hashCode(): Int = { ScalaRunTime._hashCode((value, ergoTree, additionalTokens, additionalRegisters, creationHeight)) + } override def toString: Idn = s"ErgoBoxCandidate($value, $ergoTree," + s"tokens: (${additionalTokens.map(t => ErgoAlgos.encode(t._1) + ":" + t._2).toArray.mkString(", ")}), " + s"$additionalRegisters, creationHeight: $creationHeight)" - lazy val tokens: Map[ModifierId, Long] = + /** Additional tokens stored in the box. */ + lazy val tokens: Map[ModifierId, Long] = additionalTokens .toArray .map(t => bytesToId(t._1) -> t._2) @@ -87,20 +108,36 @@ class ErgoBoxCandidate(val value: Long, } object ErgoBoxCandidate { + /** Default value of encoded (txId, boxIndex) pair encoded as Coll[Byte] + * and returned as part of Reference Register (R3) of ErgoBoxCandidate. + * + * In contrast [[ErgoBox]] extends [[ErgoBoxCandidate]] by adding `transactionId` and + * `index` properties which are returned as part of R3. + */ val UndefinedBoxRef: Coll[Byte] = Array.fill(34)(0: Byte).toColl /** @hotspot don't beautify the code */ object serializer extends SigmaSerializer[ErgoBoxCandidate, ErgoBoxCandidate] { - def serializeBodyWithIndexedDigests(obj: ErgoBoxCandidate, + /** Helper method for [[ErgoBoxCandidate]] serialization. + * If `tokensInTx` is defined, then token ids are not serialized, instead they are + * looked up in this collection and the corresponding index is serialized. + * This saves at least (32 - 4) bytes for each token. The ids themselves are + * serialized elsewhere, e.g. as part of transaction. + * + * @param box candidate box to be serialized + * @param tokensInTx optional collection of token ids that should be replaced by indexes. + * @param w writer of serialized bytes + */ + def serializeBodyWithIndexedDigests(box: ErgoBoxCandidate, tokensInTx: Option[Coll[TokenId]], w: SigmaByteWriter): Unit = { - w.putULong(obj.value) - w.putBytes(DefaultSerializer.serializeErgoTree(obj.ergoTree)) - w.putUInt(obj.creationHeight) - w.putUByte(obj.additionalTokens.size) + w.putULong(box.value) + w.putBytes(DefaultSerializer.serializeErgoTree(box.ergoTree)) + w.putUInt(box.creationHeight) + w.putUByte(box.additionalTokens.size) - val unzipped = Colls.unzip(obj.additionalTokens) + val unzipped = Colls.unzip(box.additionalTokens) val ids = unzipped._1 val amounts = unzipped._2 val limit = ids.length @@ -116,8 +153,8 @@ object ErgoBoxCandidate { } w.putULong(amount) } - - val nRegs = obj.additionalRegisters.keys.size + + val nRegs = box.additionalRegisters.keys.size if (nRegs + ErgoBox.startingNonMandatoryIndex > 255) sys.error(s"The number of non-mandatory indexes $nRegs exceeds ${255 - ErgoBox.startingNonMandatoryIndex} limit.") w.putUByte(nRegs) @@ -127,7 +164,7 @@ object ErgoBoxCandidate { val endReg = ErgoBox.startingNonMandatoryIndex + nRegs - 1 cfor(startReg: Int)(_ <= endReg, _ + 1) { regId => val reg = ErgoBox.findRegisterByIndex(regId.toByte).get - obj.get(reg) match { + box.get(reg) match { case Some(v) => w.putValue(v) case None => @@ -141,6 +178,9 @@ object ErgoBoxCandidate { serializeBodyWithIndexedDigests(obj, None, w) } + /** Helper method to parse [[ErgoBoxCandidate]] previously serialized by + * [[serializeBodyWithIndexedDigests()]]. + */ def parseBodyWithIndexedDigests(digestsInTx: Option[Coll[TokenId]], r: SigmaByteReader): ErgoBoxCandidate = { val previousPositionLimit = r.positionLimit r.positionLimit = r.position + ErgoBox.MaxBoxSize diff --git a/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeContext.scala b/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeContext.scala index bacbfa7312..438978fd34 100644 --- a/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeContext.scala +++ b/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeContext.scala @@ -194,7 +194,7 @@ object ErgoLikeContext { /** When interpreted evaluates to a ByteArrayConstant built from Context.minerPubkey */ case object MinerPubkey extends NotReadyValueByteArray with ValueCompanion { override def opCode: OpCode = OpCodes.MinerPubkeyCode - def opType = SFunc(SContext, SCollection.SByteArray) + override val opType = SFunc(SContext, SCollection.SByteArray) override def companion = this } @@ -202,30 +202,30 @@ case object MinerPubkey extends NotReadyValueByteArray with ValueCompanion { case object Height extends NotReadyValueInt with ValueCompanion { override def companion = this override def opCode: OpCode = OpCodes.HeightCode - def opType = SFunc(SContext, SInt) + override val opType = SFunc(SContext, SInt) } /** When interpreted evaluates to a collection of BoxConstant built from Context.boxesToSpend */ case object Inputs extends LazyCollection[SBox.type] with ValueCompanion { override def companion = this override def opCode: OpCode = OpCodes.InputsCode - val tpe = SCollection(SBox) - def opType = SFunc(SContext, tpe) + override def tpe = SCollection.SBoxArray + override val opType = SFunc(SContext, tpe) } /** When interpreted evaluates to a collection of BoxConstant built from Context.spendingTransaction.outputs */ case object Outputs extends LazyCollection[SBox.type] with ValueCompanion { override def companion = this override def opCode: OpCode = OpCodes.OutputsCode - val tpe = SCollection(SBox) - def opType = SFunc(SContext, tpe) + override def tpe = SCollection.SBoxArray + override val opType = SFunc(SContext, tpe) } /** When interpreted evaluates to a AvlTreeConstant built from Context.lastBlockUtxoRoot */ case object LastBlockUtxoRootHash extends NotReadyValueAvlTree with ValueCompanion { override def companion = this override def opCode: OpCode = OpCodes.LastBlockUtxoRootHashCode - def opType = SFunc(SContext, tpe) + override val opType = SFunc(SContext, tpe) } @@ -233,19 +233,25 @@ case object LastBlockUtxoRootHash extends NotReadyValueAvlTree with ValueCompani case object Self extends NotReadyValueBox with ValueCompanion { override def companion = this override def opCode: OpCode = OpCodes.SelfCode - def opType = SFunc(SContext, SBox) + override val opType = SFunc(SContext, SBox) } +/** When interpreted evaluates to the singleton instance of [[special.sigma.Context]]. + * Corresponds to `CONTEXT` variable in ErgoScript which can be used like `CONTEXT.headers`. + */ case object Context extends NotReadyValue[SContext.type] with ValueCompanion { override def companion = this override def opCode: OpCode = OpCodes.ContextCode override def tpe: SContext.type = SContext - override def opType: SFunc = SFunc(SUnit, SContext) + override val opType: SFunc = SFunc(SUnit, SContext) } +/** When interpreted evaluates to the singleton instance of [[special.sigma.SigmaDslBuilder]]. + * Corresponds to `Global` variable in ErgoScript which can be used like `Global.groupGenerator`. + */ case object Global extends NotReadyValue[SGlobal.type] with ValueCompanion { override def companion = this override def opCode: OpCode = OpCodes.GlobalCode override def tpe: SGlobal.type = SGlobal - override def opType: SFunc = SFunc(SUnit, SGlobal) + override val opType: SFunc = SFunc(SUnit, SGlobal) } diff --git a/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeInterpreter.scala b/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeInterpreter.scala index 7deda754c0..aa060e892e 100644 --- a/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeInterpreter.scala +++ b/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeInterpreter.scala @@ -23,7 +23,13 @@ class ErgoLikeInterpreter(implicit val IR: IRContext) extends Interpreter { sys.error(s"Failed deserialization of $d: expected deserialized value to have type ${d.tpe}; got ${outVal.tpe}") else Some(outVal) - case _ => None + case _ => + // TODO HF: this case is not possible because `ErgoBox.get` + // returns lookups values from `additionalRegisters` Map with values + // of type EvaluatedValue, which are always Constant nodes in practice. + // Also, this branch is never executed so can be safely removed + // (better as part of as part of HF) + None } }.orElse(d.default) case _ => super.substDeserialize(context, updateContext, node) diff --git a/sigmastate/src/main/scala/org/ergoplatform/dsl/AvlTreeHelpers.scala b/sigmastate/src/main/scala/org/ergoplatform/dsl/AvlTreeHelpers.scala index a06b32db03..9e877a104e 100644 --- a/sigmastate/src/main/scala/org/ergoplatform/dsl/AvlTreeHelpers.scala +++ b/sigmastate/src/main/scala/org/ergoplatform/dsl/AvlTreeHelpers.scala @@ -1,13 +1,12 @@ package org.ergoplatform.dsl import special.collection.Coll -import sigmastate.serialization.OperationSerializer import sigmastate.eval.{CAvlTree, CostingSigmaDslBuilder} import scorex.crypto.authds.{ADKey, ADValue} import scorex.crypto.hash.{Digest32, Blake2b256} import sigmastate.{AvlTreeData, AvlTreeFlags} import special.sigma.AvlTree -import scorex.crypto.authds.avltree.batch.{BatchAVLProver, Operation, Insert} +import scorex.crypto.authds.avltree.batch.{BatchAVLProver, Insert} import CostingSigmaDslBuilder.Colls object AvlTreeHelpers { @@ -25,12 +24,6 @@ object AvlTreeHelpers { (CAvlTree(treeData), avlProver) } - def serializeOperations(avlProver: BatchAVLProver[Digest32, Blake2b256.type], operations: Seq[Operation]): Coll[Byte] = { - val serializer = new OperationSerializer(avlProver.keyLength, avlProver.valueLengthOpt) - val opsBytes: Array[Byte] = serializer.serializeSeq(operations) - Colls.fromArray(opsBytes) - } - implicit class ADKeyArrayOps(arr: Array[ADKey]) { def toColl: Coll[Coll[Byte]] = Colls.fromArray(arr.map(x => Colls.fromArray(x))) } diff --git a/sigmastate/src/main/scala/org/ergoplatform/validation/ValidationRules.scala b/sigmastate/src/main/scala/org/ergoplatform/validation/ValidationRules.scala index 8d07c2e0ec..33f17b3add 100644 --- a/sigmastate/src/main/scala/org/ergoplatform/validation/ValidationRules.scala +++ b/sigmastate/src/main/scala/org/ergoplatform/validation/ValidationRules.scala @@ -15,7 +15,7 @@ import sigmastate.serialization.OpCodes.{OpCode, OpCodeExtra} import sigmastate.serialization.TypeSerializer.embeddableIdToType import sigmastate.serialization.{OpCodes, ValueSerializer} import sigmastate.utxo.DeserializeContext -import sigmastate.utils.Helpers._ +import sigmastate.utils.Helpers._ // required for Scala 2.11 import scala.collection.mutable /** Base class for different validation rules registered in ValidationRules.currentSettings. @@ -37,8 +37,9 @@ case class ValidationRule( */ @inline protected final def checkRule(): Unit = { if (!_checked) { - if (ValidationRules.currentSettings.getStatus(this.id).isEmpty) + if (ValidationRules.currentSettings.getStatus(this.id).isEmpty) { throw new SigmaException(s"ValidationRule $this not found in validation settings") + } _checked = true // prevent this check on every call (only first call is checked) } // upon successful return we know the rule is registered with EnabledRule status @@ -121,10 +122,12 @@ object ValidationRules { "Check the index expression for accessing collection element is supported.") { final def apply[Ctx <: IRContext, T](ctx: Ctx)(coll: Value[SCollection[_]], i: IntValue, iSym: ctx.Ref[Int]): Unit = { checkRule() - if (!ctx.isSupportedIndexExpression(iSym)) + if (!ctx.isSupportedIndexExpression(iSym)) { + // TODO consensus: cover with tests throwValidationException( new SigmaException(s"Unsupported index expression $i when accessing collection $coll", i.sourceContext.toOption), Array(coll, i)) + } } } diff --git a/sigmastate/src/main/scala/sigmastate/EcPointFunctions.scala b/sigmastate/src/main/scala/sigmastate/EcPointFunctions.scala deleted file mode 100644 index 8701291a5d..0000000000 --- a/sigmastate/src/main/scala/sigmastate/EcPointFunctions.scala +++ /dev/null @@ -1,41 +0,0 @@ -package sigmastate - -import java.math.BigInteger - -import scala.util.Try - - -object EcPointFunctions { - def decodeBigIntPair(bytes: Array[Byte]): Try[(BigInteger, BigInteger)] = Try { - val xSize = bytes.head - val ySize = bytes.tail.head - - assert(bytes.length == 2 + xSize + ySize) - - val xBytes = bytes.slice(2, xSize + 2) - val yBytes = bytes.slice(xSize + 2, xSize + ySize + 2) - - val x = new BigInteger(1, xBytes) - val y = new BigInteger(1, yBytes) - - x -> y - } - - def decodeBigIntTriple(bytes: Array[Byte]): Try[(BigInteger, BigInteger, BigInteger)] = Try { - val xSize = bytes.head - val ySize = bytes.tail.head - val zSize = bytes.tail.tail.head - - assert(bytes.length == 3 + xSize + ySize + zSize) - - val xBytes = bytes.slice(3, xSize + 3) - val yBytes = bytes.slice(xSize + 3, xSize + ySize + 3) - val zBytes = bytes.slice(xSize + ySize + 3, xSize + ySize + zSize + 3) - - val x = new BigInteger(1, xBytes) - val y = new BigInteger(1, yBytes) - val z = new BigInteger(1, zBytes) - - (x, y, z) - } -} diff --git a/sigmastate/src/main/scala/sigmastate/SigSerializer.scala b/sigmastate/src/main/scala/sigmastate/SigSerializer.scala index 4e3b4cd39d..c17178e9c3 100644 --- a/sigmastate/src/main/scala/sigmastate/SigSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/SigSerializer.scala @@ -100,7 +100,7 @@ object SigSerializer { case and: CAND => // Verifier Step 2: If the node is AND, then all of its children get e_0 as the challenge - val (seq, finalPos) = and.sigmaBooleans.foldLeft(Seq[UncheckedSigmaTree]() -> (pos + chalLen)) { case ((s, p), child) => + val (seq, finalPos) = and.children.foldLeft(Seq[UncheckedSigmaTree]() -> (pos + chalLen)) { case ((s, p), child) => val (rewrittenChild, consumed) = traverseNode(child, bytes, p, Some(challenge)) (s :+ rewrittenChild, p + consumed) } @@ -112,14 +112,14 @@ object SigSerializer { // The rightmost child gets a challenge computed as an XOR of the challenges of all the other children and e_0. // Read all the children but the last and compute the XOR of all the challenges including e_0 - val (seq, lastPos, lastChallenge) = or.sigmaBooleans.init.foldLeft((Seq[UncheckedSigmaTree](), pos + chalLen, challenge)) { + val (seq, lastPos, lastChallenge) = or.children.init.foldLeft((Seq[UncheckedSigmaTree](), pos + chalLen, challenge)) { case ((s, p, challengeXOR), child) => val (rewrittenChild, consumed) = traverseNode(child, bytes, p, challengeOpt = None) val ch = Challenge @@ xor(challengeXOR, rewrittenChild.challenge) (s :+ rewrittenChild, p + consumed, ch) } // use the computed XOR for last child's challenge - val (lastChild, numRightChildBytes) = traverseNode(or.sigmaBooleans.last, bytes, lastPos, Some(lastChallenge)) + val (lastChild, numRightChildBytes) = traverseNode(or.children.last, bytes, lastPos, Some(lastChallenge)) COrUncheckedNode(challenge, seq :+ lastChild) -> (lastPos + numRightChildBytes - pos) case t: CTHRESHOLD => @@ -127,11 +127,11 @@ object SigSerializer { // evaluate the polynomial Q(x) at points 1, 2, ..., n to get challenges for child 1, 2, ..., n, respectively. // Read the polynomial -- it has n-k coefficients - val endPolyPos = pos + chalLen + hashSize * (t.sigmaBooleans.length - t.k) + val endPolyPos = pos + chalLen + hashSize * (t.children.length - t.k) val polynomial = GF2_192_Poly.fromByteArray(challenge, bytes.slice(pos + chalLen, endPolyPos)) - val (seq, finalPos, _) = t.sigmaBooleans.foldLeft((Seq[UncheckedSigmaTree](), endPolyPos, 1)) { + val (seq, finalPos, _) = t.children.foldLeft((Seq[UncheckedSigmaTree](), endPolyPos, 1)) { case ((s, p, childIndex), child) => val (rewrittenChild, consumed) = traverseNode(child, bytes, p, Some(Challenge @@ polynomial.evaluate(childIndex.toByte).toByteArray)) (s :+ rewrittenChild, p + consumed, childIndex + 1) diff --git a/sigmastate/src/main/scala/sigmastate/UncheckedTree.scala b/sigmastate/src/main/scala/sigmastate/UncheckedTree.scala index 59a970f30f..be1df762fd 100644 --- a/sigmastate/src/main/scala/sigmastate/UncheckedTree.scala +++ b/sigmastate/src/main/scala/sigmastate/UncheckedTree.scala @@ -82,4 +82,14 @@ case class CThresholdUncheckedNode(override val challenge: Challenge, require(k >= 0 && k <= children.length) override val conjectureType = ConjectureType.ThresholdConjecture + + override def canEqual(other: Any) = other.isInstanceOf[CThresholdUncheckedNode] + override def equals(other: Any) = (this eq other.asInstanceOf[AnyRef]) || (other match { + case other: CThresholdUncheckedNode => + util.Arrays.equals(challenge, other.challenge) && + children == other.children && + k == other.k && + polynomialOpt == other.polynomialOpt + case _ => false + }) } diff --git a/sigmastate/src/main/scala/sigmastate/UnprovenTree.scala b/sigmastate/src/main/scala/sigmastate/UnprovenTree.scala index c3e74e7191..d5651f66f1 100644 --- a/sigmastate/src/main/scala/sigmastate/UnprovenTree.scala +++ b/sigmastate/src/main/scala/sigmastate/UnprovenTree.scala @@ -6,9 +6,6 @@ import com.google.common.primitives.Shorts import gf2t.GF2_192_Poly import sigmastate.basics.DLogProtocol.{FirstDLogProverMessage, ProveDlog} import sigmastate.basics.VerifierMessage.Challenge -import sigmastate.Values.{SigmaBoolean, SigmaPropConstant} -import sigmastate.basics.{FirstDiffieHellmanTupleProverMessage, FirstProverMessage, ProveDHTuple, SigmaProtocol} -import sigmastate.serialization.ErgoTreeSerializer import sigmastate.Values.{ErgoTree, SigmaBoolean, SigmaPropConstant} import sigmastate.basics.{FirstDiffieHellmanTupleProverMessage, FirstProverMessage, ProveDHTuple} import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer @@ -35,56 +32,128 @@ trait ProofTreeConjecture extends ProofTree { val children: Seq[ProofTree] } +/** + * Data type which encodes position of a node in a tree. + * + * Position is encoded like following (the example provided is for CTHRESHOLD(2, Seq(pk1, pk2, pk3 && pk4)) : + * + * 0 + * / | \ + * / | \ + * 0-0 0-1 0-2 + * /| + * / | + * / | + * / | + * 0-2-0 0-2-1 + * + * So a hint associated with pk1 has a position "0-0", pk4 - "0-2-1" . + * + * Please note that "0" prefix is for a crypto tree. There are several kinds of trees during evaluation. + * Initial mixed tree (ergoTree) would have another prefix. + * + * @param positions - positions from root (inclusive) in top-down order + */ +case class NodePosition(positions: Seq[Int]) { + def child(childIdx: Int): NodePosition = NodePosition(positions :+ childIdx) + + override def toString: String = positions.mkString("-") +} + +object NodePosition { + /** + * Prefix to encode node positions in a crypto tree. + */ + val CryptoTreePrefix = NodePosition(Seq(0)) + + /** + * Prefix to encode node positions in an ErgoTree instance. + */ + val ErgoTreePrefix = NodePosition(Seq(1)) +} + +/** + * A node of a sigma-tree used by the prover. See ProverInterpreter comments and the + * ErgoScript white-paper https://ergoplatform.org/docs/ErgoScript.pdf , Appendix A, for details + */ sealed trait UnprovenTree extends ProofTree { + + /** + * Position of the node in the tree, see comments for `position` field in + * `sigmastate.interpreter.Hint` + */ + val position: NodePosition + + /** + * Node's sigma-protocol statement to be proven. + */ val proposition: SigmaBoolean + /** + * Whether the node represents simulated sigma-protocol + */ val simulated: Boolean def real: Boolean = !simulated + /** + * Challenge used by the prover. + */ val challengeOpt: Option[Array[Byte]] def withChallenge(challenge: Challenge): UnprovenTree def withSimulated(newSimulated: Boolean): UnprovenTree + + def withPosition(updatedPosition: NodePosition): UnprovenTree } sealed trait UnprovenLeaf extends UnprovenTree with ProofTreeLeaf -sealed trait UnprovenConjecture extends UnprovenTree with ProofTreeConjecture { -} +sealed trait UnprovenConjecture extends UnprovenTree with ProofTreeConjecture case class CAndUnproven(override val proposition: CAND, override val challengeOpt: Option[Challenge] = None, override val simulated: Boolean, - children: Seq[ProofTree]) extends UnprovenConjecture { + children: Seq[ProofTree], + override val position: NodePosition = NodePosition.CryptoTreePrefix) extends UnprovenConjecture { override val conjectureType = ConjectureType.AndConjecture override def withChallenge(challenge: Challenge) = this.copy(challengeOpt = Some(challenge)) override def withSimulated(newSimulated: Boolean) = this.copy(simulated = newSimulated) + + override def withPosition(updatedPosition: NodePosition): UnprovenTree = this.copy(position = updatedPosition) } case class COrUnproven(override val proposition: COR, override val challengeOpt: Option[Challenge] = None, override val simulated: Boolean, - children: Seq[ProofTree]) extends UnprovenConjecture { + children: Seq[ProofTree], + override val position: NodePosition = NodePosition.CryptoTreePrefix) extends UnprovenConjecture { override val conjectureType = ConjectureType.OrConjecture override def withChallenge(challenge: Challenge) = this.copy(challengeOpt = Some(challenge)) override def withSimulated(newSimulated: Boolean) = this.copy(simulated = newSimulated) + + override def withPosition(updatedPosition: NodePosition): UnprovenTree = this.copy(position = updatedPosition) } +/** + * Unproven threshold k-out-n conjecture. k secrets will be proven, (n-k) simulated. + * For details on challenge and polynomial used in this case, see [CramerDamgardSchoenmakers94]. + */ case class CThresholdUnproven(override val proposition: CTHRESHOLD, override val challengeOpt: Option[Challenge] = None, override val simulated: Boolean, k: Integer, children: Seq[ProofTree], - polynomialOpt: Option[GF2_192_Poly]) extends UnprovenConjecture { + polynomialOpt: Option[GF2_192_Poly], + override val position: NodePosition = NodePosition.CryptoTreePrefix) extends UnprovenConjecture { require(k >= 0 && k <= children.length, "Wrong k value") require(children.size <= 255) // Our polynomial arithmetic can take only byte inputs @@ -95,6 +164,8 @@ case class CThresholdUnproven(override val proposition: CTHRESHOLD, override def withSimulated(newSimulated: Boolean) = this.copy(simulated = newSimulated) + override def withPosition(updatedPosition: NodePosition) = this.copy(position = updatedPosition) + def withPolynomial(newPolynomial: GF2_192_Poly) = this.copy(polynomialOpt = Some(newPolynomial)) } @@ -103,22 +174,27 @@ case class UnprovenSchnorr(override val proposition: ProveDlog, override val commitmentOpt: Option[FirstDLogProverMessage], randomnessOpt: Option[BigInteger], override val challengeOpt: Option[Challenge] = None, - override val simulated: Boolean) extends UnprovenLeaf { + override val simulated: Boolean, + override val position: NodePosition = NodePosition.CryptoTreePrefix) extends UnprovenLeaf { override def withChallenge(challenge: Challenge) = this.copy(challengeOpt = Some(challenge)) override def withSimulated(newSimulated: Boolean) = this.copy(simulated = newSimulated) + + override def withPosition(updatedPosition: NodePosition) = this.copy(position = updatedPosition) } case class UnprovenDiffieHellmanTuple(override val proposition: ProveDHTuple, override val commitmentOpt: Option[FirstDiffieHellmanTupleProverMessage], randomnessOpt: Option[BigInteger], override val challengeOpt: Option[Challenge] = None, - override val simulated: Boolean - ) extends UnprovenLeaf { + override val simulated: Boolean, + override val position: NodePosition = NodePosition.CryptoTreePrefix) extends UnprovenLeaf { override def withChallenge(challenge: Challenge) = this.copy(challengeOpt = Some(challenge)) override def withSimulated(newSimulated: Boolean) = this.copy(simulated = newSimulated) + + override def withPosition(updatedPosition: NodePosition) = this.copy(position = updatedPosition) } diff --git a/sigmastate/src/main/scala/sigmastate/Values.scala b/sigmastate/src/main/scala/sigmastate/Values.scala index 5b1cea0cce..cb029de642 100644 --- a/sigmastate/src/main/scala/sigmastate/Values.scala +++ b/sigmastate/src/main/scala/sigmastate/Values.scala @@ -6,12 +6,8 @@ import java.util.Objects import org.bitbucket.inkytonik.kiama.relation.Tree import org.bitbucket.inkytonik.kiama.rewriting.Rewriter.{strategy, everywherebu} -import org.ergoplatform.ErgoLikeContext import org.ergoplatform.validation.ValidationException import scalan.{Nullable, RType} -import scorex.crypto.authds.{ADDigest, SerializedAdProof} -import scorex.crypto.authds.avltree.batch.BatchAVLVerifier -import scorex.crypto.hash.{Digest32, Blake2b256} import scalan.util.CollectionUtil._ import sigmastate.SCollection.{SIntArray, SByteArray} import sigmastate.interpreter.CryptoConstants.EcPointType @@ -36,7 +32,7 @@ import sigmastate.lang.DefaultSigmaBuilder._ import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer import sigmastate.serialization.transformers.ProveDHTupleSerializer import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import special.sigma.{AnyValue, AvlTree, PreHeader, Header, _} +import special.sigma.{AvlTree, PreHeader, Header, _} import sigmastate.lang.SourceContext import special.collection.Coll @@ -153,7 +149,7 @@ object Values { ft.getGenericType case _ => tpe } - SFunc(Vector(), resType) + SFunc(mutable.WrappedArray.empty, resType) } } @@ -332,6 +328,7 @@ object Values { } object GroupElementConstant { + def apply(value: EcPointType): Constant[SGroupElement.type] = apply(SigmaDsl.GroupElement(value)) def apply(value: GroupElement): Constant[SGroupElement.type] = Constant[SGroupElement.type](value, SGroupElement) def unapply(v: SValue): Option[GroupElement] = v match { case Constant(value: GroupElement, SGroupElement) => Some(value) @@ -484,7 +481,7 @@ object Values { object BoolArrayConstant { def apply(value: Coll[Boolean]): CollectionConstant[SBoolean.type] = CollectionConstant[SBoolean.type](value, SBoolean) - def apply(value: Array[Boolean]): CollectionConstant[SBoolean.type] = CollectionConstant[SBoolean.type](value.toColl, SBoolean) + def apply(value: Array[Boolean]): CollectionConstant[SBoolean.type] = apply(value.toColl) def unapply(node: SValue): Option[Coll[Boolean]] = node match { case coll: CollectionConstant[SBoolean.type] @unchecked => coll match { case CollectionConstant(arr, SBoolean) => Some(arr) @@ -548,9 +545,6 @@ object Values { } object SigmaBoolean { - val PropBytes = "propBytes" - val IsValid = "isValid" - /** @hotspot don't beautify this code */ object serializer extends SigmaSerializer[SigmaBoolean, SigmaBoolean] { val dhtSerializer = ProveDHTupleSerializer(ProveDHTuple.apply) @@ -563,27 +557,27 @@ object Values { case dht: ProveDHTuple => dhtSerializer.serialize(dht, w) case _: TrivialProp => // besides opCode no additional bytes case and: CAND => - val nChildren = and.sigmaBooleans.length + val nChildren = and.children.length w.putUShort(nChildren) cfor(0)(_ < nChildren, _ + 1) { i => - val c = and.sigmaBooleans(i) + val c = and.children(i) serializer.serialize(c, w) } case or: COR => - val nChildren = or.sigmaBooleans.length + val nChildren = or.children.length w.putUShort(nChildren) cfor(0)(_ < nChildren, _ + 1) { i => - val c = or.sigmaBooleans(i) + val c = or.children(i) serializer.serialize(c, w) } case th: CTHRESHOLD => w.putUShort(th.k) - val nChildren = th.sigmaBooleans.length + val nChildren = th.children.length w.putUShort(nChildren) cfor(0)(_ < nChildren, _ + 1) { i => - val c = th.sigmaBooleans(i) + val c = th.children(i) serializer.serialize(c, w) } } @@ -649,6 +643,8 @@ object Values { trait OptionValue[T <: SType] extends Value[SOption[T]] { } + // TODO HF: SomeValue and NoneValue are not used in ErgoTree and can be + // either removed or implemented in v4.x case class SomeValue[T <: SType](x: Value[T]) extends OptionValue[T] { override def companion = SomeValue val tpe = SOption(x.tpe) @@ -685,6 +681,7 @@ object Values { val tpe = SCollection[V](elementType) + // TODO refactor: this method is not used and can be removed lazy val value = { val xs = items.cast[EvaluatedValue[V]].map(_.value) val tElement = Evaluation.stypeToRType(elementType) @@ -732,8 +729,6 @@ object Values { implicit class SigmaBooleanOps(val sb: SigmaBoolean) extends AnyVal { def toSigmaProp: SigmaPropValue = SigmaPropConstant(sb) def isProven: Value[SBoolean.type] = SigmaPropIsProven(SigmaPropConstant(sb)) - def propBytes: Value[SByteArray] = SigmaPropBytes(SigmaPropConstant(sb)) - def toAnyValue: AnyValue = eval.Extensions.toAnyValue(sb)(SType.SigmaBooleanRType) def showToString: String = sb match { case ProveDlog(v) => s"ProveDlog(${showECPoint(v)})" @@ -815,9 +810,9 @@ object Values { */ case class FuncValue(args: IndexedSeq[(Int,SType)], body: Value[SType]) extends NotReadyValue[SFunc] { override def companion = FuncValue - lazy val tpe: SFunc = SFunc(args.map(_._2), body.tpe) + lazy val tpe: SFunc = SFunc(args.toArray.map(_._2), body.tpe) /** This is not used as operation, but rather to form a program structure */ - override def opType: SFunc = SFunc(Vector(), tpe) + override def opType: SFunc = SFunc(mutable.WrappedArray.empty, tpe) } object FuncValue extends ValueCompanion { override def opCode: OpCode = FuncValueCode @@ -932,7 +927,7 @@ object Values { @inline final def isConstantSegregation: Boolean = ErgoTree.isConstantSegregation(header) @inline final def hasSize: Boolean = ErgoTree.hasSize(header) - private var _bytes: Array[Byte] = propositionBytes + private[sigmastate] var _bytes: Array[Byte] = propositionBytes /** Serialized bytes of this tree. */ final def bytes: Array[Byte] = { @@ -981,7 +976,7 @@ object Values { throw error } - /** Override equality to exclude `complexity`. */ + /** The default equality of case class is overridden to exclude `complexity`. */ override def canEqual(that: Any): Boolean = that.isInstanceOf[ErgoTree] override def hashCode(): Int = header * 31 + Objects.hash(constants, root) @@ -1019,7 +1014,7 @@ object Values { def substConstants(root: SValue, constants: IndexedSeq[Constant[SType]]): SValue = { val store = new ConstantStore(constants) - val substRule = strategy[Value[_ <: SType]] { + val substRule = strategy[Any] { case ph: ConstantPlaceholder[_] => Some(store.get(ph.id)) case _ => None diff --git a/sigmastate/src/main/scala/sigmastate/basics/BcDlogGroup.scala b/sigmastate/src/main/scala/sigmastate/basics/BcDlogGroup.scala index a75447bc68..f11edf5a01 100644 --- a/sigmastate/src/main/scala/sigmastate/basics/BcDlogGroup.scala +++ b/sigmastate/src/main/scala/sigmastate/basics/BcDlogGroup.scala @@ -4,8 +4,7 @@ import java.math.BigInteger import org.bouncycastle.asn1.x9.X9ECParameters import org.bouncycastle.crypto.ec.CustomNamedCurves -import org.bouncycastle.math.ec.custom.djb.Curve25519Point -import org.bouncycastle.math.ec.custom.sec.{SecP256K1Point, SecP384R1Point, SecP521R1Point} +import org.bouncycastle.math.ec.custom.sec.SecP256K1Point import org.bouncycastle.math.ec.ECPoint import org.bouncycastle.util.BigIntegers import spire.syntax.all.cfor @@ -133,26 +132,6 @@ abstract class BcDlogGroup[ElemType <: ECPoint](val x9params: X9ECParameters) ex k } - /** - * This function returns the k least significant bytes of the number x - * - * @param x - * @param k - * @return k least significant bits of x - */ - def getKLeastSignBytes(x: BigInteger, k: Int): Array[Byte] = { //To retrieve the k least significant bits of a number x we do: - //lsb = x mod (2^8k) - val modulo = BigInteger.valueOf(2).pow(8 * k) - x.mod(modulo).toByteArray - } - - def checkMembershipAndCreate(x: BigInteger, y: BigInteger): Try[ElemType] = Try { - val valid = checkCurveMembership(x, y) - // checks validity - if (!valid) throw new IllegalArgumentException("x, y values are not a point on this curve") - curve.validatePoint(x, y).asInstanceOf[ElemType] - } - /** * * @return the order of this Dlog group @@ -165,16 +144,6 @@ abstract class BcDlogGroup[ElemType <: ECPoint](val x9params: X9ECParameters) ex */ override lazy val identity: ElemType = curve.getInfinity.asInstanceOf[ElemType] - - /** - * Create point from its affine coordinates - * @param x - X coordinate - * @param y - Y coordinate - * @return - */ - def createPoint(x: BigInteger, y: BigInteger): ElemType = curve.createPoint(x, y).asInstanceOf[ElemType] - - /** * Calculates the inverse of the given GroupElement. * @@ -413,10 +382,4 @@ abstract class BcDlogGroup[ElemType <: ECPoint](val x9params: X9ECParameters) ex } } -object SecP384R1 extends BcDlogGroup[SecP384R1Point](CustomNamedCurves.getByName("secp384r1")) - -object SecP521R1 extends BcDlogGroup[SecP521R1Point](CustomNamedCurves.getByName("secp521r1")) - -object Curve25519 extends BcDlogGroup[Curve25519Point](CustomNamedCurves.getByName("curve25519")) - object SecP256K1 extends BcDlogGroup[SecP256K1Point](CustomNamedCurves.getByName("secp256k1")) \ No newline at end of file diff --git a/sigmastate/src/main/scala/sigmastate/basics/DLogProtocol.scala b/sigmastate/src/main/scala/sigmastate/basics/DLogProtocol.scala index e20f2d2360..364dc41e57 100644 --- a/sigmastate/src/main/scala/sigmastate/basics/DLogProtocol.scala +++ b/sigmastate/src/main/scala/sigmastate/basics/DLogProtocol.scala @@ -79,38 +79,6 @@ object DLogProtocol { override type SP = DLogSigmaProtocol } - class DLogInteractiveProver(override val publicInput: ProveDlog, override val privateInputOpt: Option[DLogProverInput]) - extends InteractiveProver[DLogSigmaProtocol, ProveDlog, DLogProverInput] { - - var rOpt: Option[BigInteger] = None - - override def firstMessage: FirstDLogProverMessage = { - assert(privateInputOpt.isDefined, "Secret is not known") - assert(rOpt.isEmpty, "Already generated r") - - val (r, fm) = DLogInteractiveProver.firstMessage() - rOpt = Some(r) - fm - } - - override def secondMessage(challenge: Challenge): SecondDLogProverMessage = { - assert(privateInputOpt.isDefined, "Secret is not known") - assert(rOpt.isDefined) - - val rnd = rOpt.get - - val privateInput = privateInputOpt.get - - val sm = DLogInteractiveProver.secondMessage(privateInput, rnd, challenge) - rOpt = None - sm - } - - override def simulate(challenge: Challenge): (FirstDLogProverMessage, SecondDLogProverMessage) = { - assert(privateInputOpt.isEmpty, "Secret is known, simulation is probably wrong action") - DLogInteractiveProver.simulate(publicInput, challenge) - } - } object DLogInteractiveProver { import CryptoConstants.secureRandom diff --git a/sigmastate/src/main/scala/sigmastate/basics/DiffieHellmanTupleProtocol.scala b/sigmastate/src/main/scala/sigmastate/basics/DiffieHellmanTupleProtocol.scala index db27256ddc..bda3f4ec56 100644 --- a/sigmastate/src/main/scala/sigmastate/basics/DiffieHellmanTupleProtocol.scala +++ b/sigmastate/src/main/scala/sigmastate/basics/DiffieHellmanTupleProtocol.scala @@ -84,41 +84,6 @@ object ProveDHTupleProp { } } -class DiffieHellmanTupleInteractiveProver(override val publicInput: ProveDHTuple, - override val privateInputOpt: Option[DiffieHellmanTupleProverInput]) - extends InteractiveProver[DiffieHellmanTupleProtocol, ProveDHTuple, DiffieHellmanTupleProverInput] { - - var rOpt: Option[BigInteger] = None - - override def firstMessage: FirstDiffieHellmanTupleProverMessage = { - assert(privateInputOpt.isDefined, "Secret is not known") - assert(rOpt.isEmpty, "Already generated r") - - val (r, fm) = DiffieHellmanTupleInteractiveProver.firstMessage(publicInput) - rOpt = Some(r) - fm - } - - override def secondMessage(challenge: Challenge): SecondDiffieHellmanTupleProverMessage = { - assert(privateInputOpt.isDefined, "Secret is not known") - assert(rOpt.isDefined) - - val rnd = rOpt.get - - val privateInput = privateInputOpt.get - - val sm = DiffieHellmanTupleInteractiveProver.secondMessage(privateInput, rnd, challenge) - rOpt = None - sm - } - - override def simulate(challenge: Challenge): - (FirstDiffieHellmanTupleProverMessage, SecondDiffieHellmanTupleProverMessage) = { - assert(privateInputOpt.isEmpty, "Secret is known, simulation is probably wrong action") - DiffieHellmanTupleInteractiveProver.simulate(publicInput, challenge) - } -} - object DiffieHellmanTupleInteractiveProver { import sigmastate.interpreter.CryptoConstants.dlogGroup diff --git a/sigmastate/src/main/scala/sigmastate/basics/SigmaProtocolFunctions.scala b/sigmastate/src/main/scala/sigmastate/basics/SigmaProtocolFunctions.scala index d1acb7a9cd..199cdd1bf8 100644 --- a/sigmastate/src/main/scala/sigmastate/basics/SigmaProtocolFunctions.scala +++ b/sigmastate/src/main/scala/sigmastate/basics/SigmaProtocolFunctions.scala @@ -149,17 +149,3 @@ trait SigmaProtocolTranscript[SP <: SigmaProtocol[SP], CI <: SigmaProtocolCommon def accepted: Boolean } -trait SigmaProtocolMsg - -object SigmaProtocolFunctions { - - case class FirstMessage(s: SigmaProtocolMsg) - - case class RandomChallenge(challenge: Challenge) - - case class SecondMessage(s: SigmaProtocolMsg) - - case object StartInteraction - - case class Transcript(a: SigmaProtocolMsg, e: Challenge, z: SigmaProtocolMsg, accepted: Boolean) -} diff --git a/sigmastate/src/main/scala/sigmastate/eval/BigIntegerOps.scala b/sigmastate/src/main/scala/sigmastate/eval/BigIntegerOps.scala index 9c5561a483..547092bad6 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/BigIntegerOps.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/BigIntegerOps.scala @@ -26,7 +26,6 @@ object OrderingOps { object NumericOps { trait BigIntegerIsIntegral extends Integral[BigInteger] { -// private val BI = implicitly[Integral[BigInt]] def quot(x: BigInteger, y: BigInteger): BigInteger = x.divide(y) def rem(x: BigInteger, y: BigInteger): BigInteger = x.remainder(y) def plus(x: BigInteger, y: BigInteger): BigInteger = x.add(y) diff --git a/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala b/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala index 5d979ca418..9163367e22 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala @@ -4,14 +4,13 @@ import java.math.BigInteger import java.util import org.bouncycastle.math.ec.ECPoint -import org.ergoplatform.settings.ErgoAlgos import org.ergoplatform.{ErgoBox, SigmaConstants} import org.ergoplatform.validation.ValidationRules import scorex.crypto.authds.avltree.batch._ import scorex.crypto.authds.{ADDigest, ADKey, SerializedAdProof, ADValue} import sigmastate.SCollection.SByteArray import sigmastate.{TrivialProp, _} -import sigmastate.Values.{Constant, EvaluatedValue, SValue, ConstantNode, Value, ErgoTree, SigmaBoolean} +import sigmastate.Values.{Constant, EvaluatedValue, SValue, ConstantNode, ErgoTree, SigmaBoolean} import sigmastate.interpreter.CryptoConstants.EcPointType import sigmastate.interpreter.{CryptoConstants, Interpreter} import special.collection.{Size, CSizeOption, SizeColl, CCostedBuilder, CollType, SizeOption, CostedBuilder, Coll} @@ -20,15 +19,17 @@ import sigmastate.eval.Extensions._ import spire.syntax.all.cfor import scala.util.{Success, Failure} -import scalan.RType +import scalan.{Nullable, RType} import scorex.crypto.hash.{Digest32, Sha256, Blake2b256} import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.basics.ProveDHTuple import sigmastate.lang.Terms.OperationId +import sigmastate.lang.TransformingSigmaBuilder import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer import sigmastate.serialization.{SigmaSerializer, GroupElementSerializer} import special.Types.TupleType +import scala.collection.mutable import scala.reflect.ClassTag /** Interface implmented by wrappers to provide access to the underlying wrapped value. */ @@ -272,12 +273,12 @@ case class CostingBox(isCost: Boolean, val ebox: ErgoBox) extends Box with Wrapp lazy val bytes: Coll[Byte] = Colls.fromArray(ebox.bytes) lazy val bytesWithoutRef: Coll[Byte] = Colls.fromArray(ebox.bytesWithNoRef) lazy val propositionBytes: Coll[Byte] = Colls.fromArray(ebox.propositionBytes) - lazy val registers: Coll[AnyValue] = regs(ebox, isCost) + lazy val registers: Coll[AnyValue] = regs(ebox) override def wrappedValue: ErgoBox = ebox override def getReg[T](i: Int)(implicit tT: RType[T]): Option[T] = - if (isCost) { + if (isCost) { // TODO refactor: remove isCost branch (it was added before Sizes and now is never executed) val optV = if (i < 0 || i >= registers.length) None else { @@ -340,9 +341,7 @@ object CostingBox { import Evaluation._ - def colBytes(b: Array[Byte])(implicit IR: Evaluation): Coll[Byte] = IR.sigmaDslBuilderValue.Colls.fromArray(b) - - def regs(ebox: ErgoBox, isCost: Boolean): Coll[AnyValue] = { + def regs(ebox: ErgoBox): Coll[AnyValue] = { val res = new Array[AnyValue](ErgoBox.maxRegisters) def checkNotYetDefined(id: Int, newValue: SValue) = @@ -463,6 +462,29 @@ object CHeader { * @see [[CostModel]] for detailed descriptions */ class CCostModel extends CostModel { + import CCostModel._ + + override def AccessBox: Int = AccessBoxCost + + override def AccessAvlTree: Int = AccessAvlTreeCost + + override def GetVar: Int = GetVarCost + + def DeserializeVar: Int = DeserializeVarCost + + def GetRegister: Int = GetRegisterCost + + def DeserializeRegister: Int = DeserializeRegisterCost + + def SelectField: Int = SelectFieldCost + + def CollectionConst: Int = CollectionConstCost + + def AccessKiloByteOfData: Int = AccessKiloByteOfDataCost + + def PubKeySize: Long = CryptoConstants.EncodedGroupElementLength +} +object CCostModel { private def costOf(opName: String, opType: SFunc): Int = { val operId = OperationId(opName, opType) costOf(operId) @@ -473,28 +495,36 @@ class CCostModel extends CostModel { cost } - def AccessBox: Int = costOf("AccessBox", SFunc(SContext, SBox)) + // NOTE: lazy vals are necessary to avoid initialization exception - def AccessAvlTree: Int = costOf("AccessAvlTree", SFunc(SContext, SAvlTree)) + private val AccessBoxOpType: SFunc = SFunc(SContext, SBox) + private lazy val AccessBoxCost: Int = costOf("AccessBox", AccessBoxOpType) - def GetVar: Int = costOf("GetVar", SFunc(IndexedSeq(SContext, SByte), SOption(SOption.tT))) + private val AccessAvlTreeOpType: SFunc = SFunc(SContext, SAvlTree) + private lazy val AccessAvlTreeCost: Int = costOf("AccessAvlTree", AccessAvlTreeOpType) - def DeserializeVar: Int = costOf("DeserializeVar", SFunc(IndexedSeq(SContext, SByte), SOption(SOption.tT))) + private val GetVarOpType: SFunc = SFunc(Array(SContext, SByte), SOption.ThisType) + private lazy val GetVarCost: Int = costOf("GetVar", GetVarOpType) - def GetRegister: Int = costOf("GetRegister", SFunc(IndexedSeq(SBox, SByte), SOption(SOption.tT))) + private val DeserializeVarOpType: SFunc = SFunc(Array(SContext, SByte), SOption.ThisType) + private lazy val DeserializeVarCost: Int = costOf("DeserializeVar", DeserializeVarOpType) - def DeserializeRegister: Int = costOf("DeserializeRegister", SFunc(IndexedSeq(SBox, SByte), SOption(SOption.tT))) + private val GetRegisterOpType: SFunc = SFunc(Array(SBox, SByte), SOption.ThisType) + private lazy val GetRegisterCost: Int = costOf("GetRegister", GetRegisterOpType) - def SelectField: Int = costOf("SelectField", SFunc(IndexedSeq(), SUnit)) + private val DeserializeRegisterOpType: SFunc = SFunc(Array(SBox, SByte), SOption.ThisType) + private lazy val DeserializeRegisterCost: Int = costOf("DeserializeRegister", DeserializeRegisterOpType) - def CollectionConst: Int = costOf("Const", SFunc(IndexedSeq(), SCollection(STypeVar("IV")))) + private val SelectFieldOpType: SFunc = SFunc(mutable.WrappedArray.empty, SUnit) + private lazy val SelectFieldCost: Int = costOf("SelectField", SelectFieldOpType) - def AccessKiloByteOfData: Int = costOf("AccessKiloByteOfData", SFunc(IndexedSeq(), SUnit)) + private val CollectionConstOpType: SFunc = SFunc(mutable.WrappedArray.empty, SCollection.ThisType) + private lazy val CollectionConstCost: Int = costOf("Const", CollectionConstOpType) - def PubKeySize: Long = CryptoConstants.EncodedGroupElementLength + private val AccessKiloByteOfDataOpType: SFunc = SFunc(mutable.WrappedArray.empty, SUnit) + private lazy val AccessKiloByteOfDataCost: Int = costOf("AccessKiloByteOfData", AccessKiloByteOfDataOpType) } - /** A default implementation of [[SigmaDslBuilder]] interface. * @see [[SigmaDslBuilder]] for detailed descriptions */ @@ -540,6 +570,7 @@ class CostingSigmaDslBuilder extends TestSigmaDslBuilder { dsl => case m => sys.error(s"Point of type ${m.getClass} is not supported") } + /** Wraps the given sigma proposition into SigmaDsl value of type SigmaProp. */ def SigmaProp(sigmaTree: SigmaBoolean): SigmaProp = new CSigmaProp(sigmaTree) /** Extract `sigmastate.Values.SigmaBoolean` from DSL's `SigmaProp` type. */ @@ -553,11 +584,18 @@ class CostingSigmaDslBuilder extends TestSigmaDslBuilder { dsl => CAvlTree(treeData) } + /** Wraps the given tree data into SigmaDsl value of type [[AvlTree]]. */ def avlTree(treeData: AvlTreeData): AvlTree = { CAvlTree(treeData) } + /** Wraps the given [[ErgoBox]] into SigmaDsl value of type [[Box]]. + * @param ebox the value to be wrapped + * @see [[sigmastate.SBox]], [[special.sigma.Box]] + */ def Box(ebox: ErgoBox): Box = CostingBox(false, ebox) + + /** Extracts [[ErgoBox]] from the given [[Box]] instance. This is inverse to the Box method. */ def toErgoBox(b: Box): ErgoBox = b.asInstanceOf[CostingBox].ebox /** @hotspot don't beautify this code */ @@ -633,8 +671,12 @@ class CostingSigmaDslBuilder extends TestSigmaDslBuilder { dsl => positions: Coll[Int], newValues: Coll[T]) (implicit cT: RType[T]): Coll[Byte] = { - val typedNewVals = newValues.toArray.map(_.asInstanceOf[Value[SType]]) - val res = SubstConstants.eval(scriptBytes.toArray, positions.toArray, typedNewVals) + val typedNewVals = newValues.toArray.map(v => TransformingSigmaBuilder.liftAny(v) match { + case Nullable(v) => v + case _ => sys.error(s"Cannot evaluate substConstants($scriptBytes, $positions, $newValues): cannot lift value $v") + }) + + val res = SubstConstants.eval(scriptBytes.toArray, positions.toArray, typedNewVals)(validationSettings) Colls.fromArray(res) } @@ -665,24 +707,24 @@ case class CostingDataContext( vars: Coll[AnyValue], var isCost: Boolean) extends Context { - @inline def builder: SigmaDslBuilder = CostingSigmaDslBuilder + @inline override def builder: SigmaDslBuilder = CostingSigmaDslBuilder - @inline def HEIGHT: Int = height + @inline override def HEIGHT: Int = height - @inline def SELF: Box = selfBox + @inline override def SELF: Box = selfBox - @inline def dataInputs: Coll[Box] = _dataInputs + @inline override def dataInputs: Coll[Box] = _dataInputs - @inline def INPUTS = inputs + @inline override def INPUTS = inputs - @inline def OUTPUTS = outputs + @inline override def OUTPUTS = outputs - @inline def LastBlockUtxoRootHash = lastBlockUtxoRootHash + @inline override def LastBlockUtxoRootHash = lastBlockUtxoRootHash - @inline def minerPubKey = _minerPubKey + @inline override def minerPubKey = _minerPubKey - def findSelfBoxIndex: Int = { + private def findSelfBoxIndex: Int = { var i = 0 while (i < inputs.length) { if (inputs(i) eq selfBox) return i @@ -728,4 +770,33 @@ case class CostingDataContext( } else None } } + + /** Return a new context instance with variables collection updated. + * @param bindings a new binding of the context variables with new values. + * @return a new instance (if `bindings` non-empty) with the specified bindings. + * other existing bindings are copied to the new instance + */ + def withUpdatedVars(bindings: (Int, AnyValue)*): CostingDataContext = { + if (bindings.isEmpty) return this + + val ids = bindings.map(_._1).toArray + val values = bindings.map(_._2).toArray + val maxVarId = ids.max // INV: ids is not empty + val requiredNewLength = maxVarId + 1 + + val newVars = if (vars.length < requiredNewLength) { + // grow vars collection + val currVars = vars.toArray + val buf = new Array[AnyValue](requiredNewLength) + Array.copy(currVars, 0, buf, 0, currVars.length) + cfor(0)(_ < ids.length, _ + 1) { i => + buf(ids(i)) = values(i) + } + buf.toColl + } else { + vars.updateMany(ids.toColl, values.toColl) + } + + this.copy(vars = newVars) + } } diff --git a/sigmastate/src/main/scala/sigmastate/eval/CostingRules.scala b/sigmastate/src/main/scala/sigmastate/eval/CostingRules.scala index 9d011e5a15..1b6b016627 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/CostingRules.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/CostingRules.scala @@ -235,11 +235,6 @@ trait CostingRules extends SigmaLibrary { IR: IRContext => costedBuilder.mkSizeColl(sizes) } - def mkSizeColl[T](len: Ref[Int], sItem: RSize[T]): Ref[Size[Coll[T]]] = { - val sizes = colBuilder.replicate(len, sItem) - costedBuilder.mkSizeColl(sizes) - } - def mkSizeOption[T](size: RSize[T]): Ref[Size[WOption[T]]] = costedBuilder.mkSizeOption(SOME(size)) def mkSizePair[A, B](l: RSize[A], r: RSize[B]): Ref[Size[(A,B)]] = costedBuilder.mkSizePair(l, r) @@ -444,12 +439,6 @@ trait CostingRules extends SigmaLibrary { IR: IRContext => def minerPubKey: RCostedColl[Byte] = knownLengthCollPropertyAccess(_.minerPubKey, EncodedGroupElementInfo) - def getVar[T](id: RCosted[Byte])(implicit tT: Ref[WRType[T]]): RCostedOption[T] = { ??? -// defaultOptionPropertyAccess(_.getVar(id.value)(tT.eA), asSizeContext(_).reg) -// val opt = ctx.getVar(id)(cT) -// dsl.costOption(opt, dsl.CostModel.GetVar) - } - def selfBoxIndex: RCosted[Int] = constantSizePropertyAccess(_.selfBoxIndex) } diff --git a/sigmastate/src/main/scala/sigmastate/eval/Evaluation.scala b/sigmastate/src/main/scala/sigmastate/eval/Evaluation.scala index a063fe9394..c1a6fe3d54 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/Evaluation.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/Evaluation.scala @@ -1,6 +1,5 @@ package sigmastate.eval -import java.lang.Math import java.math.BigInteger import org.bouncycastle.math.ec.ECPoint @@ -8,18 +7,15 @@ import org.ergoplatform._ import org.ergoplatform.validation.ValidationRules.{CheckLoopLevelInCostFunction, CheckCostFuncOperation} import sigmastate._ import sigmastate.Values.{Value, GroupElementConstant, SigmaBoolean, Constant} -import sigmastate.lang.Terms.OperationId -import sigmastate.utxo.CostTableStat import scala.reflect.ClassTag import scala.util.Try import sigmastate.SType._ -import sigmastate.interpreter.CryptoConstants.EcPointType import scalan.{Nullable, RType} import scalan.RType._ import sigma.types.PrimViewType import sigmastate.basics.DLogProtocol.ProveDlog -import sigmastate.basics.{ProveDHTuple, DLogProtocol} +import sigmastate.basics.{ProveDHTuple} import special.sigma.Extensions._ import sigmastate.lang.exceptions.CostLimitException import sigmastate.serialization.OpCodes @@ -402,13 +398,6 @@ trait Evaluation extends RuntimeCosting { IR: IRContext => } } - object IsTupleFN { - def unapply(fn: String): Nullable[Byte] = { - if (fn.startsWith("_")) Nullable[Byte](fn.substring(1).toByte) - else Nullable.None.asInstanceOf[Nullable[Byte]] - } - } - import sigmastate._ import special.sigma.{Context => SigmaContext} @@ -433,8 +422,6 @@ trait Evaluation extends RuntimeCosting { IR: IRContext => env } - case class EvaluatedEntry(env: DataEnv, sym: Sym, value: AnyRef) - protected def printEnvEntry(sym: Sym, value: AnyRef) = { def trim[A](arr: Array[A]) = arr.take(arr.length min 100) def show(x: Any) = x match { @@ -475,7 +462,6 @@ trait Evaluation extends RuntimeCosting { IR: IRContext => this._currentCost = java.lang.Math.addExact(this._currentCost, n) } @inline def currentCost: Int = _currentCost - @inline def resetCost() = { _currentCost = initialCost } } /** Implements finite state machine with stack of graph blocks (scopes), @@ -571,6 +557,7 @@ trait Evaluation extends RuntimeCosting { IR: IRContext => if (accumulatedCost > limit) { // if (cost < limit) // println(s"FAIL FAST in loop: $accumulatedCost > $limit") + // TODO cover with tests throw new CostLimitException(accumulatedCost, msgCostLimitError(accumulatedCost, limit), None) } } @@ -661,6 +648,7 @@ trait Evaluation extends RuntimeCosting { IR: IRContext => In(input: special.collection.Coll[Byte]@unchecked), In(positions: special.collection.Coll[Int]@unchecked), In(newVals: special.collection.Coll[Any]@unchecked), _) => + // TODO refactor: call sigmaDslBuilderValue.substConstants val typedNewVals = newVals.toArray.map(v => builder.liftAny(v) match { case Nullable(v) => v case _ => sys.error(s"Cannot evaluate substConstants($input, $positions, $newVals): cannot lift value $v") @@ -782,28 +770,6 @@ trait Evaluation extends RuntimeCosting { IR: IRContext => case ThunkForce(In(t: ThunkData[Any])) => out(t()) - case SDBM.sigmaProp(_, In(isValid: Boolean)) => - val res = CSigmaProp(sigmastate.TrivialProp(isValid)) - out(res) - case SDBM.proveDlog(_, In(g: EcPointType)) => - val res = CSigmaProp(DLogProtocol.ProveDlog(g)) - out(res) - case SDBM.proveDHTuple(_, In(g: EcPointType), In(h: EcPointType), In(u: EcPointType), In(v: EcPointType)) => - val res = CSigmaProp(ProveDHTuple(g, h, u, v)) - out(res) - case SDBM.avlTree(_, In(flags: Byte), - In(digest: SColl[Byte]@unchecked), In(keyLength: Int), - In(valueLengthOpt: Option[Int]@unchecked)) => - val res = sigmaDslBuilderValue.avlTree(flags, digest, keyLength, valueLengthOpt) - out(res) - -// case CReplCollCtor(valueSym @ In(value), In(len: Int)) => -// val res = sigmaDslBuilderValue.Colls.replicate(len, value)(asType[Any](valueSym.elem.sourceType)) -// out(res) -// -// case PairOfColsCtor(In(ls: SColl[a]@unchecked), In(rs: SColl[b]@unchecked)) => -// val res = sigmaDslBuilderValue.Colls.pairColl(ls, rs) -// out(res) case CSizePrimCtor(In(dataSize: Long), tVal) => val res = new special.collection.CSizePrim(dataSize, tVal.eA.sourceType) @@ -817,15 +783,6 @@ trait Evaluation extends RuntimeCosting { IR: IRContext => case CSizeOptionCtor(In(optSize: Option[SSize[_]] @unchecked)) => val res = new special.collection.CSizeOption(optSize) out(res) -// case CSizeAnyValueCtor(tVal, In(valueSize: SSize[Any] @unchecked)) => -// val res = new special.sigma.CSizeAnyValue(tVal.eA.sourceType.asInstanceOf[RType[Any]], valueSize) -// out(res) -// case CSizeBoxCtor( -// In(propBytes: SSize[SColl[Byte]]@unchecked), In(bytes: SSize[SColl[Byte]]@unchecked), -// In(bytesWithoutRef: SSize[SColl[Byte]]@unchecked), In(regs: SSize[SColl[Option[SAnyValue]]]@unchecked), -// In(tokens: SSize[SColl[(SColl[Byte], Long)]]@unchecked)) => -// val res = new EvalSizeBox(propBytes, bytes, bytesWithoutRef, regs, tokens) -// out(res) case costOp: CostOf => out(costOp.eval) @@ -837,8 +794,6 @@ trait Evaluation extends RuntimeCosting { IR: IRContext => case SizeOf(sym @ In(data)) => val tpe = elemToSType(sym.elem) val size = tpe match { -// case SAvlTree => -// data.asInstanceOf[special.sigma.AvlTree].dataSize case _ => data match { case w: WrapperOf[_] => tpe.dataSize(w.wrappedValue.asWrappedType) @@ -958,7 +913,7 @@ object Evaluation { case PreHeaderRType => SPreHeader case SigmaPropRType => SSigmaProp case SigmaBooleanRType => SSigmaProp - case tup: TupleType => STuple(tup.items.map(t => rtypeToSType(t)).toIndexedSeq) + case tup: TupleType => STuple(tup.items.map(t => rtypeToSType(t))) case at: ArrayType[_] => SCollection(rtypeToSType(at.tA)) case ct: CollType[_] => SCollection(rtypeToSType(ct.tItem)) case ft: FuncType[_,_] => SFunc(rtypeToSType(ft.tDom), rtypeToSType(ft.tRange)) diff --git a/sigmastate/src/main/scala/sigmastate/eval/Extensions.scala b/sigmastate/src/main/scala/sigmastate/eval/Extensions.scala index 451fc9297c..69b8f11f7a 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/Extensions.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/Extensions.scala @@ -3,8 +3,8 @@ package sigmastate.eval import java.math.BigInteger import scalan.RType -import sigmastate.SType -import sigmastate.Values.Constant +import sigmastate.{SCollection, SType, SCollectionType} +import sigmastate.Values.{Constant, ConstantNode} import sigmastate.lang.DefaultSigmaBuilder import special.collection.Coll import special.sigma._ @@ -44,6 +44,15 @@ object Extensions { f(coll(i)) } } + + /** Helper type synonym. */ + type ElemTpe = SType { type WrappedType = T} + + /** Wraps the collection into ConstantNode using collection's element type. */ + def toConstant: Constant[SCollection[ElemTpe]] = { + val elemTpe = Evaluation.rtypeToSType(coll.tItem).asInstanceOf[ElemTpe] + ConstantNode[SCollection[ElemTpe]](coll, SCollectionType(elemTpe)) + } } // NOTE: it cannot extend AnyVal because of compiler error: type parameter of value class may not be specialized diff --git a/sigmastate/src/main/scala/sigmastate/eval/IRContext.scala b/sigmastate/src/main/scala/sigmastate/eval/IRContext.scala index 61a2d11f6c..3f5b27b762 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/IRContext.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/IRContext.scala @@ -92,16 +92,7 @@ trait IRContext extends Evaluation with TreeBuilding { val costFun = compile[SSize[SContext], Int, Size[Context], Int](getDataEnv, costF, Some(maxCost)) val (_, estimatedCost) = costFun(Sized.sizeOf(ctx)) if (estimatedCost > maxCost) { - throw new CostLimitException(estimatedCost, s"Estimated execution cost $estimatedCost exceeds the limit $maxCost in $exp") - } - estimatedCost - } - - def checkCostEx(ctx: SContext, exp: Value[SType], - costF: Ref[((Int, Size[Context])) => Int], maxCost: Long): Int = { - val costFun = compile[(Int, SSize[SContext]), Int, (Int, Size[Context]), Int](getDataEnv, costF, Some(maxCost)) - val (_, estimatedCost) = costFun((0, Sized.sizeOf(ctx))) - if (estimatedCost > maxCost) { + // TODO cover with tests throw new CostLimitException(estimatedCost, s"Estimated execution cost $estimatedCost exceeds the limit $maxCost in $exp") } estimatedCost @@ -125,7 +116,7 @@ trait IRContext extends Evaluation with TreeBuilding { * And taking into account the cleaning of the garbage and cutting the blockchain history, * the old scripts at some point will die out of the blockchain. */ - def checkCostWithContext(ctx: SContext, exp: Value[SType], + def checkCostWithContext(ctx: SContext, costF: Ref[((Context, (Int, Size[Context]))) => Int], maxCost: Long, initCost: Long): Try[Int] = Try { val costFun = compile[(SContext, (Int, SSize[SContext])), Int, (Context, (Int, Size[Context])), Int]( getDataEnv, costF, Some(maxCost)) @@ -139,6 +130,7 @@ trait IRContext extends Evaluation with TreeBuilding { val scaledCost = JMath.multiplyExact(estimatedCost.toLong, CostTable.costFactorIncrease.toLong) / CostTable.costFactorDecrease val totalCost = JMath.addExact(initCost, scaledCost) if (totalCost > maxCost) { + // TODO cover with tests throw new CostLimitException(totalCost, msgCostLimitError(totalCost, maxCost), None) } totalCost.toInt diff --git a/sigmastate/src/main/scala/sigmastate/eval/RuntimeCosting.scala b/sigmastate/src/main/scala/sigmastate/eval/RuntimeCosting.scala index 8403e82abb..9cf0178843 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/RuntimeCosting.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/RuntimeCosting.scala @@ -7,29 +7,22 @@ import scalan.util.CollectionUtil.TraversableOps import org.ergoplatform._ import sigmastate._ import sigmastate.Values._ -import sigmastate.interpreter.CryptoConstants import sigmastate.lang.Terms._ import sigmastate.lang.exceptions.CosterException import sigmastate.serialization.OpCodes -import sigmastate.utxo.CostTable.Cost import sigmastate.utxo._ import scalan.compilation.GraphVizConfig import SType._ -import org.ergoplatform.SigmaConstants._ import scalan.RType._ -import scorex.crypto.hash.{Sha256, Blake2b256} import sigmastate.interpreter.Interpreter.ScriptEnv import sigmastate.lang.{Terms, SourceContext} -import sigma.types.PrimViewType import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.basics.ProveDHTuple import sigmastate.interpreter.CryptoConstants.EcPointType import special.collection.CollType -import special.Types._ -import special.sigma.{GroupElementRType, AvlTreeRType, BigIntegerRType, BoxRType, ECPointRType, BigIntRType, SigmaPropRType} +import special.sigma.{GroupElementRType, AvlTreeRType, BoxRType, BigIntRType, SigmaPropRType} import special.sigma.Extensions._ import org.ergoplatform.validation.ValidationRules._ -import scalan.util.ReflectionUtil import scalan.ExactIntegral._ import scalan.ExactNumeric._ import scalan.ExactOrdering.{ShortIsExactOrdering, ByteIsExactOrdering, IntIsExactOrdering, LongIsExactOrdering} @@ -396,12 +389,6 @@ trait RuntimeCosting extends CostingRules { IR: IRContext => implicit def extendCostedCollElem[A](elem: Elem[CostedColl[A]]): CostedCollElem[A, CostedColl[A]] = elem.asInstanceOf[CostedCollElem[A, CostedColl[A]]] - def splitCostedFunc2[A,B](f: RFuncCosted[A,B]): (Ref[A=>B], Ref[((Int, Size[A])) => Int]) = { - implicit val eA = f.elem.eDom.eVal - val calcF = f.sliceCalc - val costF = f.sliceCost - (calcF, costF) - } def splitCostedFunc2[A, B](f: RFuncCosted[A,B], okRemoveIsValid: Boolean): (Ref[A=>Any], Ref[((Int, Size[A])) => Int]) = { implicit val eA = f.elem.eDom.eVal val calcF = f.sliceCalc(okRemoveIsValid) @@ -779,9 +766,6 @@ trait RuntimeCosting extends CostingRules { IR: IRContext => private val _colBuilder: LazyRep[CollBuilder] = MutableLazy(variable[CollBuilder]) @inline def colBuilder: Ref[CollBuilder] = _colBuilder.value -// private val _sizeBuilder: LazyRep[SizeBuilder] = MutableLazy(RCSizeBuilder()) -// @inline def sizeBuilder: Ref[SizeBuilder] = _sizeBuilder.value - private val _costedBuilder: LazyRep[CostedBuilder] = MutableLazy(RCCostedBuilder()) @inline def costedBuilder: Ref[CostedBuilder] = _costedBuilder.value @@ -814,8 +798,6 @@ trait RuntimeCosting extends CostingRules { IR: IRContext => _contextDependantNodes = debox.Set.ofSize[Int](InitDependantNodes) } - import Cost._ - def removeIsProven[T,R](f: Ref[T] => Ref[Any]): Ref[T] => Ref[Any] = { x: Ref[T] => val y = f(x); val res = y match { @@ -846,7 +828,6 @@ trait RuntimeCosting extends CostingRules { IR: IRContext => case SAvlTree => avlTreeElement case SSigmaProp => sigmaPropElement case STuple(Seq(a, b)) => pairElement(stypeToElem(a), stypeToElem(b)) -// case STuple(items) => tupleStructElement(items.map(stypeToElem(_)):_*) case c: SCollectionType[a] => collElement(stypeToElem(c.elemType)) case o: SOption[a] => wOptionElement(stypeToElem(o.elemType)) case SFunc(Seq(tpeArg), tpeRange, Nil) => funcElement(stypeToElem(tpeArg), stypeToElem(tpeRange)) @@ -871,9 +852,6 @@ trait RuntimeCosting extends CostingRules { IR: IRContext => case _: HeaderElem[_] => SHeader case _: PreHeaderElem[_] => SPreHeader case _: SigmaPropElem[_] => SSigmaProp -// case se: StructElem[_] => -// assert(se.fieldNames.zipWithIndex.forall { case (n,i) => n == s"_${i+1}" }) -// STuple(se.fieldElems.map(elemToSType(_)).toIndexedSeq) case ce: CollElem[_, _] => SCollection(elemToSType(ce.eItem)) case fe: FuncElem[_, _] => SFunc(elemToSType(fe.eDom), elemToSType(fe.eRange)) case pe: PairElem[_, _] => STuple(elemToSType(pe.eFst), elemToSType(pe.eSnd)) @@ -981,11 +959,6 @@ trait RuntimeCosting extends CostingRules { IR: IRContext => import sigmastate._ - protected def isOperationNode(v: SValue): Boolean = v match { - case _: Block | _: BlockValue | _: TaggedVariableNode[_] | _: ValNode | _: ValDef | _: ValUse[_] | _: FuncValue => false - case _ => true - } - protected def onTreeNodeCosted[T <: SType]( ctx: RCosted[Context], env: CostingEnv, node: Value[T], costed: RCosted[T#WrappedType]): Unit = { @@ -1017,11 +990,11 @@ trait RuntimeCosting extends CostingRules { IR: IRContext => implicit val tA = ct.tItem implicit val sizedA = Sized.typeToSized(tA) liftConst(Sized.sizeOf(x.asInstanceOf[special.collection.Coll[a]])) - case ct: OptionType[a] => + case ct: OptionType[a] => // TODO cover with tests implicit val tA = ct.tA implicit val sizedA = Sized.typeToSized(tA) liftConst(Sized.sizeOf(x.asInstanceOf[Option[a]])) - case ct: PairType[a, b] => + case ct: PairType[a, b] => // TODO cover with tests implicit val tA = ct.tFst implicit val tB = ct.tSnd implicit val sizedA = Sized.typeToSized(tA) @@ -1146,6 +1119,17 @@ trait RuntimeCosting extends CostingRules { IR: IRContext => var ruleStack: List[CostingRuleStat] = Nil + /** Recursively translates each ErgoTree `node` into the corresponding cost formula. + * The cost formula is represented using graph-based IR defined by this IRContext cake. + * Each `node: Value[T]` which evaluates to the value of type `T` is transformed + * to a value of type `RCosted[A]` which is a synonym of `Ref[Costed[A]]` type. + * The translation is performed recursively on a structure of the ErgoTree expression. + * + * @param ctx reference to the graph node, which represents costed `CONTEXT` expression. + * @param env environment of costed ValDef nodes (see BlockValue case). + * @param node expression to be costed + * @return a reference to the graph node of type Costed[T#WrappedType]` + */ protected def evalNode[T <: SType](ctx: RCosted[Context], env: CostingEnv, node: Value[T]): RCosted[T#WrappedType] = { import WOption._ def eval[T <: SType](node: Value[T]): RCosted[T#WrappedType] = evalNode(ctx, env, node) @@ -1177,9 +1161,6 @@ trait RuntimeCosting extends CostingRules { IR: IRContext => } val res: Ref[Any] = node match { - case TaggedVariableNode(id, _) => - env.getOrElse(id, !!!(s"TaggedVariable $id not found in environment $env")) - case c @ Constant(v, tpe) => v match { case p: SSigmaProp => assert(tpe == SSigmaProp) @@ -1717,7 +1698,7 @@ trait RuntimeCosting extends CostingRules { IR: IRContext => def tC = evalNode(ctx, env, t) def eC = evalNode(ctx, env, e) val resV = IF (cC.value) THEN tC.value ELSE eC.value - val resCost = opCost(resV, Array(cC.cost, tC.cost, eC.cost), costOf("If", SFunc(Vector(SBoolean, If.tT, If.tT), If.tT))) + val resCost = opCost(resV, Array(cC.cost, tC.cost, eC.cost), costOf("If", If.GenericOpType)) RCCostedPrim(resV, resCost, tC.size) // TODO costing: implement tC.size max eC.size case rel: Relation[t, _] => diff --git a/sigmastate/src/main/scala/sigmastate/eval/SigmaCoster.scala b/sigmastate/src/main/scala/sigmastate/eval/SigmaCoster.scala deleted file mode 100644 index 60e031a9ba..0000000000 --- a/sigmastate/src/main/scala/sigmastate/eval/SigmaCoster.scala +++ /dev/null @@ -1,11 +0,0 @@ -package sigmastate.eval - - -class SigmaCoster[Ctx <: RuntimeCosting](val ctx: Ctx) { - - -} - -object SigmaCoster { - -} diff --git a/sigmastate/src/main/scala/sigmastate/eval/Sized.scala b/sigmastate/src/main/scala/sigmastate/eval/Sized.scala index 3f8c7d4756..a6362ffbb1 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/Sized.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/Sized.scala @@ -129,10 +129,6 @@ object Sized extends SizedLowPriority { val SizeBoxBytesWithoutRefsMax = new CSizeColl( Colls.replicate(MaxBoxSize.value - SizeOfInputRefBytes, SizeByte)) - private def sizeOfTokens(b: Box): Size[Coll[(Coll[Byte], Long)]] = { - new CSizeColl(Colls.replicate(b.tokens.length, SizeToken)) - } - implicit val boxIsSized: Sized[Box] = Sized.instance((b: Box) => { new EvalSizeBox( SizePropositionBytesMax, diff --git a/sigmastate/src/main/scala/sigmastate/eval/TreeBuilding.scala b/sigmastate/src/main/scala/sigmastate/eval/TreeBuilding.scala index 52b43b0088..92e4018a96 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/TreeBuilding.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/TreeBuilding.scala @@ -1,7 +1,7 @@ package sigmastate.eval -import sigmastate.Values.{BlockValue, BoolValue, Constant, ConstantNode, EvaluatedCollection, SValue, SigmaPropConstant, ValDef, ValUse, Value} +import sigmastate.Values.{BlockValue, BoolValue, Constant, ConstantNode, SValue, SigmaPropConstant, ValDef, ValUse, Value} import org.ergoplatform._ import org.ergoplatform.{Height, Inputs, Outputs, Self} @@ -24,10 +24,8 @@ trait TreeBuilding extends RuntimeCosting { IR: IRContext => import Box._ import CollBuilder._ import SigmaDslBuilder._ - import CCostedBuilder._ import BigInt._ import WOption._ - import AvlTree._ import GroupElement._ private val ContextM = ContextMethods @@ -116,7 +114,11 @@ trait TreeBuilding extends RuntimeCosting { IR: IRContext => } } - def buildValue(ctx: Ref[Context], + /** Transforms the given graph node into the corresponding ErgoTree node. + * It is mutually recursive with processAstGraph, so it's part of the recursive + * algorithms required by buildTree method. + */ + private def buildValue(ctx: Ref[Context], mainG: PGraph, env: DefEnv, s: Sym, @@ -134,7 +136,7 @@ trait TreeBuilding extends RuntimeCosting { IR: IRContext => val varId = defId + 1 // arguments are treated as ValDefs and occupy id space val env1 = env + (x -> (varId, elemToSType(x.elem))) val block = processAstGraph(ctx, mainG, env1, lam, varId + 1, constantsProcessing) - val rhs = mkFuncValue(Vector((varId, elemToSType(x.elem))), block) + val rhs = mkFuncValue(Array((varId, elemToSType(x.elem))), block) rhs case Def(Apply(fSym, xSym, _)) => val Seq(f, x) = Seq(fSym, xSym).map(recurse) @@ -205,17 +207,7 @@ trait TreeBuilding extends RuntimeCosting { IR: IRContext => mkPlusModQ(l.asBigInt, r.asBigInt) case BIM.minusModQ(In(l), In(r)) => mkMinusModQ(l.asBigInt, r.asBigInt) - case Def(ApplyBinOp(IsArithOp(opCode), xSym, ySym)) => - val Seq(x, y) = Seq(xSym, ySym).map(recurse) - mkArith(x.asNumValue, y.asNumValue, opCode) - case Def(ApplyBinOp(IsRelationOp(mkNode), xSym, ySym)) => - val Seq(x, y) = Seq(xSym, ySym).map(recurse) - mkNode(x, y) - case Def(ApplyBinOpLazy(IsLogicalBinOp(mkNode), xSym, ySym)) => - val Seq(x, y) = Seq(xSym, ySym).map(recurse) - mkNode(x, y) - case Def(ApplyUnOp(IsLogicalUnOp(mkNode), xSym)) => - mkNode(recurse(xSym)) + case Def(ApplyUnOp(IsNumericUnOp(mkNode), xSym)) => mkNode(recurse(xSym)) @@ -293,9 +285,9 @@ trait TreeBuilding extends RuntimeCosting { IR: IRContext => case OM.isDefined(In(optionSym)) => mkOptionIsDefined(optionSym.asValue[SOption[SType]]) - case SigmaM.and_bool_&&(In(prop), In(cond)) => + case SigmaM.and_bool_&&(In(prop), In(cond)) => // TODO refactor: remove or cover by tests: it is never executed SigmaAnd(Seq(prop.asSigmaProp, mkBoolToSigmaProp(cond.asBoolValue))) - case SigmaM.or_bool_||(In(prop), In(cond)) => + case SigmaM.or_bool_||(In(prop), In(cond)) => // TODO refactor: remove or cover by tests: it is never executed SigmaOr(Seq(prop.asSigmaProp, mkBoolToSigmaProp(cond.asBoolValue))) case SigmaM.and_sigma_&&(In(p1), In(p2)) => SigmaAnd(Seq(p1.asSigmaProp, p2.asSigmaProp)) @@ -344,7 +336,7 @@ trait TreeBuilding extends RuntimeCosting { IR: IRContext => case _ => mkCreateProveDHTuple(g.asGroupElement, h.asGroupElement, u.asGroupElement, v.asGroupElement) } - case SDBM.sigmaProp(_, In(cond)) => + case SDBM.sigmaProp(_, In(cond)) => // TODO refactor: remove or cover by tests: it is never executed mkBoolToSigmaProp(cond.asBoolValue) case SDBM.byteArrayToBigInt(_, colSym) => mkByteArrayToBigInt(recurse(colSym)) @@ -397,6 +389,10 @@ trait TreeBuilding extends RuntimeCosting { IR: IRContext => } } + /** Transforms the given AstGraph node (Lambda of Thunk) into the corresponding ErgoTree node. + * It is mutually recursive with buildValue, so it's part of the recursive + * algorithms required by buildTree method. + */ private def processAstGraph(ctx: Ref[Context], mainG: PGraph, env: DefEnv, @@ -430,6 +426,13 @@ trait TreeBuilding extends RuntimeCosting { IR: IRContext => res } + /** Transforms the given function `f` from graph-based IR to ErgoTree expression. + * + * @param f reference to the graph node representing function from Context. + * @param constantsProcessing if Some(store) is specified, then each constant is + * segregated and a placeholder is inserted in the resulting expression. + * @return expression of ErgoTree which corresponds to the function `f` + */ def buildTree[T <: SType](f: Ref[Context => Any], constantsProcessing: Option[ConstantStore] = None): Value[T] = { val Def(Lambda(lam,_,_,_)) = f diff --git a/sigmastate/src/main/scala/sigmastate/eval/Zero.scala b/sigmastate/src/main/scala/sigmastate/eval/Zero.scala index 04c744988b..a2ca688d23 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/Zero.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/Zero.scala @@ -54,8 +54,8 @@ object Zero extends ZeroLowPriority { case AvlTreeRType => Zero[AvlTree] case SigmaPropRType => sigmaPropIsZero case ct: CollType[a] => collIsZero(typeToZero(ct.tItem), ct.tItem) - case ct: OptionType[a] => optionIsZero(typeToZero(ct.tA)) - case ct: PairType[a, b] => pairIsZero(typeToZero(ct.tFst), typeToZero(ct.tSnd)) + case ct: OptionType[a] => optionIsZero(typeToZero(ct.tA)) // TODO cover with tests + case ct: PairType[a, b] => pairIsZero(typeToZero(ct.tFst), typeToZero(ct.tSnd)) // TODO cover with tests case _ => sys.error(s"Don't know how to compute Zero for type $t") }).asInstanceOf[Zero[T]] diff --git a/sigmastate/src/main/scala/sigmastate/interpreter/Hint.scala b/sigmastate/src/main/scala/sigmastate/interpreter/Hint.scala index 1998bfd15d..13570e846c 100644 --- a/sigmastate/src/main/scala/sigmastate/interpreter/Hint.scala +++ b/sigmastate/src/main/scala/sigmastate/interpreter/Hint.scala @@ -2,7 +2,7 @@ package sigmastate.interpreter import java.math.BigInteger -import sigmastate.UncheckedTree +import sigmastate.{NodePosition, UncheckedTree} import sigmastate.Values.SigmaBoolean import sigmastate.basics.FirstProverMessage import sigmastate.basics.VerifierMessage.Challenge @@ -12,7 +12,14 @@ import sigmastate.basics.VerifierMessage.Challenge * and the prover knows only a secret for the public key pk1, the prover fails on proving without a hint. But if the * prover knows that pk2 is known to another party, the prover may prove the statement (with an empty proof for "pk2"). */ -trait Hint +trait Hint { + + /** + * A hint is related to a subtree (or a leaf) of a tree. This field encodes a position in the tree. + * See `NodePosition` ScalaDoc for details. + */ + val position: NodePosition +} /** * A hint which is indicating that a secret associated with its public image "image" is already proven. @@ -41,7 +48,8 @@ abstract class SecretProven extends Hint { */ case class RealSecretProof(image: SigmaBoolean, challenge: Challenge, - uncheckedTree: UncheckedTree) extends SecretProven + uncheckedTree: UncheckedTree, + override val position: NodePosition) extends SecretProven /** * A hint which contains a proof-of-knowledge for a secret associated with its public image "image", @@ -49,7 +57,8 @@ case class RealSecretProof(image: SigmaBoolean, */ case class SimulatedSecretProof(image: SigmaBoolean, challenge: Challenge, - uncheckedTree: UncheckedTree) extends SecretProven + uncheckedTree: UncheckedTree, + override val position: NodePosition) extends SecretProven /** @@ -71,7 +80,8 @@ abstract class CommitmentHint extends Hint { */ case class OwnCommitment(override val image: SigmaBoolean, secretRandomness: BigInteger, - commitment: FirstProverMessage) extends CommitmentHint + commitment: FirstProverMessage, + override val position: NodePosition) extends CommitmentHint /** * A hint which contains a commitment to randomness associated with a public image of a secret. @@ -79,7 +89,9 @@ case class OwnCommitment(override val image: SigmaBoolean, * @param image - image of a secret * @param commitment - commitment to randomness used while proving knowledge of the secret */ -case class RealCommitment(override val image: SigmaBoolean, commitment: FirstProverMessage) extends CommitmentHint +case class RealCommitment(override val image: SigmaBoolean, + commitment: FirstProverMessage, + override val position: NodePosition) extends CommitmentHint /** * A hint which contains a commitment to randomness associated with a public image of a secret. @@ -87,7 +99,9 @@ case class RealCommitment(override val image: SigmaBoolean, commitment: FirstPro * @param image - image of a secret * @param commitment - commitment to randomness used while proving knowledge of the secret */ -case class SimulatedCommitment(override val image: SigmaBoolean, commitment: FirstProverMessage) extends CommitmentHint +case class SimulatedCommitment(override val image: SigmaBoolean, + commitment: FirstProverMessage, + override val position: NodePosition) extends CommitmentHint /** diff --git a/sigmastate/src/main/scala/sigmastate/interpreter/Interpreter.scala b/sigmastate/src/main/scala/sigmastate/interpreter/Interpreter.scala index 1ddbce9ad5..1c78a7f82f 100644 --- a/sigmastate/src/main/scala/sigmastate/interpreter/Interpreter.scala +++ b/sigmastate/src/main/scala/sigmastate/interpreter/Interpreter.scala @@ -53,7 +53,7 @@ trait Interpreter extends ScorexLogging { val currCost = JMath.addExact(context.initCost, scriptComplexity) val remainingLimit = context.costLimit - currCost if (remainingLimit <= 0) - throw new CostLimitException(currCost, msgCostLimitError(currCost, context.costLimit), None) + throw new CostLimitException(currCost, msgCostLimitError(currCost, context.costLimit), None) // TODO cover with tests val ctx1 = context.withInitCost(currCost).asInstanceOf[CTX] (ctx1, script) @@ -71,17 +71,18 @@ trait Interpreter extends ScorexLogging { CheckDeserializedScriptType(d, script) Some(script) - case _ => None + case _ => None // TODO cover with tests } else - None + None // TODO cover with tests case _ => None } def toValidScriptType(exp: SValue): BoolValue = exp match { case v: Value[SBoolean.type]@unchecked if v.tpe == SBoolean => v case p: SValue if p.tpe == SSigmaProp => p.asSigmaProp.isProven - case x => throw new Error(s"Context-dependent pre-processing should produce tree of type Boolean or SigmaProp but was $x") + case x => // TODO cover with tests + throw new Error(s"Context-dependent pre-processing should produce tree of type Boolean or SigmaProp but was $x") } class MutableCell[T](var value: T) @@ -95,7 +96,7 @@ trait Interpreter extends ScorexLogging { ergoTree.toProposition(ergoTree.isConstantSegregation) case Left(UnparsedErgoTree(_, error)) if validationSettings.isSoftFork(error) => TrueSigmaProp - case Left(UnparsedErgoTree(_, error)) => + case Left(UnparsedErgoTree(_, error)) => // TODO cover with tests throw new InterpreterException( "Script has not been recognized due to ValidationException, and it cannot be accepted as soft-fork.", None, Some(error)) } @@ -106,7 +107,7 @@ trait Interpreter extends ScorexLogging { * We can estimate cost of the tree evaluation only after this step.*/ def applyDeserializeContext(context: CTX, exp: Value[SType]): (BoolValue, CTX) = { val currContext = new MutableCell(context) - val substRule = strategy[Value[_ <: SType]] { case x => + val substRule = strategy[Any] { case x: SValue => substDeserialize(currContext.value, { ctx: CTX => currContext.value = ctx }, x) } val Some(substTree: SValue) = everywherebu(substRule)(exp) @@ -114,19 +115,6 @@ trait Interpreter extends ScorexLogging { (res, currContext.value) } - def checkCost(context: CTX, exp: Value[SType], costF: Ref[((Int, IR.Size[IR.Context])) => Int]): Int = { - import IR.Size._ - import IR.Context._ - val costingCtx = context.toSigmaContext(IR, isCost = true) - val maxCost = context.costLimit - val costFun = IR.compile[(Int, SSize[SContext]), Int, (Int, Size[Context]), Int](IR.getDataEnv, costF, Some(maxCost)) - val (_, estimatedCost) = costFun((0, Sized.sizeOf(costingCtx))) - if (estimatedCost > maxCost) { - throw new CostLimitException(estimatedCost, s"Estimated execution cost $estimatedCost exceeds the limit $maxCost in $exp") - } - estimatedCost - } - def calcResult(context: special.sigma.Context, calcF: Ref[IR.Context => Any]): special.sigma.SigmaProp = { import IR._ import Context._ @@ -169,7 +157,7 @@ trait Interpreter extends ScorexLogging { CheckCostFunc(IR)(asRep[Any => Int](costF)) val costingCtx = context.toSigmaContext(IR, isCost = true) - val estimatedCost = IR.checkCostWithContext(costingCtx, exp, costF, maxCost, initCost).getOrThrow + val estimatedCost = IR.checkCostWithContext(costingCtx, costF, maxCost, initCost).getOrThrow IR.onEstimatedCost(env, exp, costingRes, costingCtx, estimatedCost) @@ -244,7 +232,7 @@ trait Interpreter extends ScorexLogging { val initCost = JMath.addExact(ergoTree.complexity.toLong, context.initCost) val remainingLimit = context.costLimit - initCost if (remainingLimit <= 0) - throw new CostLimitException(initCost, msgCostLimitError(initCost, context.costLimit), None) + throw new CostLimitException(initCost, msgCostLimitError(initCost, context.costLimit), None) // TODO cover with tests val contextWithCost = context.withInitCost(initCost).asInstanceOf[CTX] @@ -296,7 +284,7 @@ trait Interpreter extends ScorexLogging { * per the verifier algorithm of the leaf's Sigma-protocol. * If the verifier algorithm of the Sigma-protocol for any of the leaves rejects, then reject the entire proof. */ - val computeCommitments: Strategy = everywherebu(rule[UncheckedSigmaTree] { + val computeCommitments: Strategy = everywherebu(rule[Any] { case c: UncheckedConjecture => c // Do nothing for internal nodes case sn: UncheckedSchnorr => @@ -307,7 +295,7 @@ trait Interpreter extends ScorexLogging { val (a, b) = DiffieHellmanTupleInteractiveProver.computeCommitment(dh.proposition, dh.challenge, dh.secondMessage) dh.copy(commitmentOpt = Some(FirstDiffieHellmanTupleProverMessage(a, b))) - case _ => ??? + case _: UncheckedSigmaTree => ??? }) def verify(ergoTree: ErgoTree, diff --git a/sigmastate/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala b/sigmastate/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala index 95e620076e..1c7cc0082c 100644 --- a/sigmastate/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala +++ b/sigmastate/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala @@ -11,7 +11,7 @@ import sigmastate.Values._ import sigmastate._ import sigmastate.basics.DLogProtocol._ import sigmastate.basics.VerifierMessage.Challenge -import sigmastate.basics.{DiffieHellmanTupleInteractiveProver, DiffieHellmanTupleProverInput, FirstDiffieHellmanTupleProverMessage, ProveDHTuple, SecondDiffieHellmanTupleProverMessage, SigmaProtocolPrivateInput} +import sigmastate.basics._ import sigmastate.lang.exceptions.CostLimitException import sigmastate.utils.Helpers @@ -28,7 +28,29 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor override type ProofT = UncheckedTree - val secrets: Seq[SigmaProtocolPrivateInput[_, _]] + def secrets: Seq[SigmaProtocolPrivateInput[_, _]] + + /** + * Public keys of prover's secrets. This operation can be costly if there are many + * secrets the prover knows, consider re-implementation of this field then. + */ + def publicKeys: Seq[SigmaBoolean] = secrets.map(_.publicImage.asInstanceOf[SigmaBoolean]) + + /** + * Generate commitments for given ergo tree for prover's secrets. + * The prover is reducing the given tree to crypto-tree by using the given context, + * and then generates commitments. + */ + def generateCommitments(ergoTree: ErgoTree, ctx: CTX): HintsBag = { + generateCommitmentsFor(ergoTree, ctx, publicKeys) + } + + /** + * Generate commitments for given crypto-tree (sigma-tree) for prover's secrets. + */ + def generateCommitments(sigmaTree: SigmaBoolean): HintsBag = { + generateCommitmentsFor(sigmaTree, publicKeys) + } /** * The comments in this section are taken from the algorithm for the @@ -127,7 +149,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor * In a bottom-up traversal of the tree, do the following for each node: * */ - def markReal(hintsBag: HintsBag): Strategy = everywherebu(rule[UnprovenTree] { + def markReal(hintsBag: HintsBag): Strategy = everywherebu(rule[Any] { case and: CAndUnproven => // If the node is AND, mark it "real" if all of its children are marked real; else mark it "simulated" val simulated = and.children.exists(_.asInstanceOf[UnprovenTree].simulated) @@ -149,26 +171,44 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor // else mark it "simulated" val isReal = hintsBag.realImages.contains(ul.proposition) || secrets.exists { case in: SigmaProtocolPrivateInput[_, _] => in.publicImage == ul.proposition - case _ => false } ul.withSimulated(!isReal) - case t => + case t: UnprovenTree => error(s"Don't know how to markReal($t)") }) + /** + * Set positions for children of a unproven inner node (conjecture, so AND/OR/THRESHOLD) + */ + protected def setPositions(uc: UnprovenConjecture): UnprovenConjecture = { + val updChildren = uc.children.zipWithIndex.map { case (pt, idx) => + pt.asInstanceOf[UnprovenTree].withPosition(uc.position.child(idx)) + } + uc match { + case and: CAndUnproven => and.copy(children = updChildren) + case or: COrUnproven => or.copy(children = updChildren) + case threshold: CThresholdUnproven => threshold.copy(children = updChildren) + } + } + /** * Prover Step 3: This step will change some "real" nodes to "simulated" to make sure each node has - * the right number of simulated children. + * the right number of simulated children. Also, children will get proper position set during this step. * In a top-down traversal of the tree, do the following for each node: */ - val polishSimulated: Strategy = everywheretd(rule[UnprovenTree] { + val polishSimulated: Strategy = everywheretd(rule[Any] { case and: CAndUnproven => // If the node is marked "simulated", mark all of its children "simulated" - if (and.simulated) and.copy(children = and.children.map(_.asInstanceOf[UnprovenTree].withSimulated(true))) - else and + val a = if (and.simulated) { + and.copy(children = and.children.map(_.asInstanceOf[UnprovenTree].withSimulated(true))) + } else { + and + } + setPositions(a) + case or: COrUnproven => // If the node is marked "simulated", mark all of its children "simulated" - if (or.simulated) { + val o = if (or.simulated) { or.copy(children = or.children.map(_.asInstanceOf[UnprovenTree].withSimulated(true))) } else { // If the node is OR marked "real", mark all but one of its children "simulated" @@ -186,10 +226,12 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor }._1 or.copy(children = newChildren) } + setPositions(o) case t: CThresholdUnproven => // If the node is marked "simulated", mark all of its children "simulated" - if (t.simulated) t.copy(children = t.children.map(_.asInstanceOf[UnprovenTree].withSimulated(true))) - else { + val th = if (t.simulated) { + t.copy(children = t.children.map(_.asInstanceOf[UnprovenTree].withSimulated(true))) + } else { // If the node is THRESHOLD(k) marked "real", mark all but k of its children "simulated" // (the node is guaranteed, by the previous step, to have at least k "real" children). // Which particular ones are left "real" is not important for security; @@ -207,9 +249,10 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor }._1 t.copy(children = newChildren) } + setPositions(th) case su: UnprovenSchnorr => su case dhu: UnprovenDiffieHellmanTuple => dhu - case _ => ??? + case _: UnprovenTree => ??? }) /** @@ -219,7 +262,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor * Prover Step 6: For every leaf marked "real", use the first prover step of the Sigma-protocol for that leaf to * compute the commitment a. */ - def simulateAndCommit(hintsBag: HintsBag): Strategy = everywheretd(rule[ProofTree] { + def simulateAndCommit(hintsBag: HintsBag): Strategy = everywheretd(rule[Any] { // Step 4 part 1: If the node is marked "real", then each of its simulated children gets a fresh uniformly // random challenge in {0,1}^t. case and: CAndUnproven if and.real => and // A real AND node has no simulated children @@ -232,7 +275,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor } else { // take challenge from previously done proof stored in the hints bag, // or generate random challenge for simulated child - val newChallenge = hintsBag.proofs.find(_.image == c.proposition).map(_.challenge).getOrElse( + val newChallenge = hintsBag.proofs.find(_.position == c.position).map(_.challenge).getOrElse( Challenge @@ secureRandomBytes(CryptoFunctions.soundnessBytes) ) c.withChallenge(newChallenge) @@ -320,7 +363,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor // otherwise, compute the commitment (if the node is real) or simulate it (if the node is simulated) // Step 6 (real leaf -- compute the commitment a or take it from the hints bag) - hintsBag.commitments.find(_.image == su.proposition).map { cmtHint => + hintsBag.commitments.find(_.position == su.position).map { cmtHint => su.copy(commitmentOpt = Some(cmtHint.commitment.asInstanceOf[FirstDLogProverMessage])) }.getOrElse { if (su.simulated) { @@ -340,7 +383,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor // or simulate it (if the node is simulated) // Step 6 (real leaf -- compute the commitment a or take it from the hints bag) - hintsBag.commitments.find(_.image == dhu.proposition).map { cmtHint => + hintsBag.commitments.find(_.position == dhu.position).map { cmtHint => dhu.copy(commitmentOpt = Some(cmtHint.commitment.asInstanceOf[FirstDiffieHellmanTupleProverMessage])) }.getOrElse { if (dhu.simulated) { @@ -355,7 +398,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor } } - case a: Any => error(s"Don't know how to challengeSimulated($a)") + case t: ProofTree => error(s"Don't know how to challengeSimulated($t)") }) private def extractChallenge(pt: ProofTree): Option[Array[Byte]] = pt match { @@ -370,7 +413,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor * the challenge e for every node marked "real" below the root and, additionally, the response z for every leaf * marked "real" */ - def proving(hintsBag: HintsBag): Strategy = everywheretd(rule[ProofTree] { + def proving(hintsBag: HintsBag): Strategy = everywheretd(rule[Any] { // If the node is a non-leaf marked real whose challenge is e_0, proceed as follows: case and: CAndUnproven if and.real => assert(and.challengeOpt.isDefined) @@ -433,7 +476,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor val z = privKeyOpt match { case Some(privKey: DLogProverInput) => - hintsBag.ownCommitments.find(_.image == su.proposition).map { oc => + hintsBag.ownCommitments.find(_.position == su.position).map { oc => DLogInteractiveProver.secondMessage( privKey, oc.secretRandomness, @@ -445,8 +488,8 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor su.challengeOpt.get) } - case None => - hintsBag.realProofs.find(_.image == su.proposition).map { proof => + case _ => + hintsBag.realProofs.find(_.position == su.position).map { proof => val provenSchnorr = proof.uncheckedTree.asInstanceOf[UncheckedSchnorr] provenSchnorr.secondMessage }.getOrElse { @@ -466,7 +509,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor val z = privKeyOpt match { case Some(privKey) => - hintsBag.ownCommitments.find(_.image == dhu.proposition).map { oc => + hintsBag.ownCommitments.find(_.position == dhu.position).map { oc => DiffieHellmanTupleInteractiveProver.secondMessage( privKey.asInstanceOf[DiffieHellmanTupleProverInput], oc.secretRandomness, @@ -479,7 +522,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor } case None => - hintsBag.realProofs.find(_.image == dhu.proposition).map { proof => + hintsBag.realProofs.find(_.position == dhu.position).map { proof => val provenSchnorr = proof.uncheckedTree.asInstanceOf[UncheckedDiffieHellmanTuple] provenSchnorr.secondMessage }.getOrElse { @@ -501,7 +544,9 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor case ut: UnprovenTree => ut - case a: Any => log.warn("Wrong input in prove(): ", a); ??? + case t: ProofTree => + log.warn("Wrong input in prove(): ", t); + ??? }) diff --git a/sigmastate/src/main/scala/sigmastate/interpreter/ProverResult.scala b/sigmastate/src/main/scala/sigmastate/interpreter/ProverResult.scala index 99b79d442b..9c14d295c9 100644 --- a/sigmastate/src/main/scala/sigmastate/interpreter/ProverResult.scala +++ b/sigmastate/src/main/scala/sigmastate/interpreter/ProverResult.scala @@ -26,7 +26,7 @@ class ProverResult(val proof: Array[Byte], val extension: ContextExtension) { } object ProverResult { - val empty: ProverResult = ProverResult(Array[Byte](), ContextExtension.empty) + val empty: ProverResult = ProverResult(Array.emptyByteArray, ContextExtension.empty) def apply(proof: Array[Byte], extension: ContextExtension): ProverResult = new ProverResult(proof, extension) @@ -41,7 +41,10 @@ object ProverResult { override def parse(r: SigmaByteReader): ProverResult = { val sigBytesCount = r.getUShort() - val proofBytes = r.getBytes(sigBytesCount) + val proofBytes = if (sigBytesCount == 0) + Array.emptyByteArray // this allows to avoid hundreds of thousands of allocations + else + r.getBytes(sigBytesCount) val ce = ContextExtension.serializer.parse(r) ProverResult(proofBytes, ce) } diff --git a/sigmastate/src/main/scala/sigmastate/interpreter/ProverUtils.scala b/sigmastate/src/main/scala/sigmastate/interpreter/ProverUtils.scala index 40d057bf4d..ff19d2cc0c 100644 --- a/sigmastate/src/main/scala/sigmastate/interpreter/ProverUtils.scala +++ b/sigmastate/src/main/scala/sigmastate/interpreter/ProverUtils.scala @@ -1,12 +1,66 @@ package sigmastate.interpreter -import sigmastate.{ProofTree, SigSerializer, UncheckedConjecture, UncheckedLeaf, UncheckedSigmaTree} +import sigmastate.{NodePosition, ProofTree, SigSerializer, SigmaConjecture, SigmaProofOfKnowledgeLeaf, UncheckedConjecture, UncheckedLeaf, UncheckedSigmaTree} import sigmastate.Values.{ErgoTree, SigmaBoolean} +import sigmastate.basics.DLogProtocol.{DLogInteractiveProver, ProveDlog} +import sigmastate.basics.{DiffieHellmanTupleInteractiveProver, ProveDHTuple} import sigmastate.basics.VerifierMessage.Challenge trait ProverUtils extends Interpreter { + /** + * Generate commitments for a given ergoTree (mixed-tree) and public keys. + * + * First, the given tree is to be reduced to crypto-tree (sigma-tree) by using context provided. + */ + def generateCommitmentsFor(ergoTree: ErgoTree, + context: CTX, + generateFor: Seq[SigmaBoolean]): HintsBag = { + val reducedTree = fullReduction(ergoTree, context, Interpreter.emptyEnv)._1 + generateCommitmentsFor(reducedTree, generateFor) + } + + /** + * A method which is is generating commitments for all the public keys provided. + * + * Currently only keys in form of ProveDlog and ProveDiffieHellman are supported, not more complex subtrees. + * + * @param sigmaTree - crypto-tree + * @param generateFor - public keys for which commitments should be generated + * @return generated commitments (private, containing secret randomness, and public, containing only commitments) + */ + def generateCommitmentsFor(sigmaTree: SigmaBoolean, + generateFor: Seq[SigmaBoolean]): HintsBag = { + + def traverseNode(sb: SigmaBoolean, + bag: HintsBag, + position: NodePosition): HintsBag = { + sb match { + case sc: SigmaConjecture => + sc.children.zipWithIndex.foldLeft(bag) { case (b, (child, idx)) => + traverseNode(child, b, position.child(idx)) + } + case leaf: SigmaProofOfKnowledgeLeaf[_, _] => + if (generateFor.contains(leaf)) { + val (r, a) = leaf match { + case _: ProveDlog => + DLogInteractiveProver.firstMessage() + case pdh: ProveDHTuple => + DiffieHellmanTupleInteractiveProver.firstMessage(pdh) + case _ => ??? + } + val hints = Seq(OwnCommitment(leaf, r, a, position), RealCommitment(leaf, a, position)) + bag.addHints(hints: _*) + } else { + bag + } + } + } + + traverseNode(sigmaTree, HintsBag.empty, position = NodePosition.CryptoTreePrefix) + } + /** * A method which is extracting partial proofs of secret knowledge for particular secrets with their * respective public images given. Useful for distributed signature applications. @@ -14,19 +68,19 @@ trait ProverUtils extends Interpreter { * See DistributedSigSpecification for examples of usage. * * @param context - context used to reduce the proposition - * @param exp - proposition to reduce + * @param ergoTree - proposition to reduce * @param proof - proof for reduced proposition * @param realSecretsToExtract - public keys of secrets with real proofs * @param simulatedSecretsToExtract - public keys of secrets with simulated proofs * @return - bag of OtherSecretProven and OtherCommitment hints */ def bagForMultisig(context: CTX, - exp: ErgoTree, + ergoTree: ErgoTree, proof: Array[Byte], realSecretsToExtract: Seq[SigmaBoolean], simulatedSecretsToExtract: Seq[SigmaBoolean] = Seq.empty): HintsBag = { - val reducedTree = fullReduction(exp, context, Interpreter.emptyEnv)._1 + val reducedTree = fullReduction(ergoTree, context, Interpreter.emptyEnv)._1 val ut = SigSerializer.parseAndComputeChallenges(reducedTree, proof) val proofTree = computeCommitments(ut).get.asInstanceOf[UncheckedSigmaTree] @@ -34,11 +88,12 @@ trait ProverUtils extends Interpreter { def traverseNode(tree: ProofTree, realPropositions: Seq[SigmaBoolean], simulatedPropositions: Seq[SigmaBoolean], - hintsBag: HintsBag): HintsBag = { + hintsBag: HintsBag, + position: NodePosition): HintsBag = { tree match { case inner: UncheckedConjecture => - inner.children.foldLeft(hintsBag) { case (hb, c) => - traverseNode(c, realPropositions, simulatedPropositions, hb) + inner.children.zipWithIndex.foldLeft(hintsBag) { case (hb, (c, idx)) => + traverseNode(c, realPropositions, simulatedPropositions, hb, position.child(idx)) } case leaf: UncheckedLeaf[_] => val realFound = realPropositions.contains(leaf.proposition) @@ -46,13 +101,13 @@ trait ProverUtils extends Interpreter { if (realFound || simulatedFound) { val hints = if (realFound) { Seq( - RealCommitment(leaf.proposition, leaf.commitmentOpt.get), - RealSecretProof(leaf.proposition, Challenge @@ leaf.challenge, leaf) + RealCommitment(leaf.proposition, leaf.commitmentOpt.get, position), + RealSecretProof(leaf.proposition, Challenge @@ leaf.challenge, leaf, position) ) } else { Seq( - SimulatedCommitment(leaf.proposition, leaf.commitmentOpt.get), - SimulatedSecretProof(leaf.proposition, Challenge @@ leaf.challenge, leaf) + SimulatedCommitment(leaf.proposition, leaf.commitmentOpt.get, position), + SimulatedSecretProof(leaf.proposition, Challenge @@ leaf.challenge, leaf, position) ) } hintsBag.addHints(hints: _*) @@ -60,7 +115,7 @@ trait ProverUtils extends Interpreter { } } - traverseNode(proofTree, realSecretsToExtract, simulatedSecretsToExtract, HintsBag.empty) + traverseNode(proofTree, realSecretsToExtract, simulatedSecretsToExtract, HintsBag.empty, NodePosition.CryptoTreePrefix) } } diff --git a/sigmastate/src/main/scala/sigmastate/lang/SigmaBinder.scala b/sigmastate/src/main/scala/sigmastate/lang/SigmaBinder.scala index 7b2b9aeb62..214a4abb01 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/SigmaBinder.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/SigmaBinder.scala @@ -37,7 +37,7 @@ class SigmaBinder(env: ScriptEnv, builder: SigmaBuilder, /** Rewriting of AST with respect to environment to resolve all references to global names * and infer their types. */ - private def eval(e: SValue, env: ScriptEnv): SValue = rewrite(reduce(strategy[SValue]({ + private def eval(e: SValue, env: ScriptEnv): SValue = rewrite(reduce(strategy[Any]({ case i @ Ident(n, NoType) => env.get(n) match { case Some(v) => Option(liftAny(v).get.withPropagatedSrcCtx(i.sourceContext)) case None => n match { diff --git a/sigmastate/src/main/scala/sigmastate/lang/SigmaParser.scala b/sigmastate/src/main/scala/sigmastate/lang/SigmaParser.scala index 07f6988e20..83810260eb 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/SigmaParser.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/SigmaParser.scala @@ -69,10 +69,23 @@ object SigmaParser extends Exprs with Types with Core { mkConstant[SLong.type](-value, SLong) case _ => error(s"cannot prefix $arg with op $opName", arg.sourceContext) } + case "!" => mkLogicalNot(arg.asBoolValue) - case "-" => mkNegation(arg.asNumValue) - case "~" => mkBitInversion(arg.asNumValue) - case _ => error(s"Unknown prefix operation $opName for $arg", arg.sourceContext) + + case "-" => + if (arg.tpe.isNumTypeOrNoType) + mkNegation(arg.asNumValue) + else + error(s"Numeric argument expected for '$opName' operation: $arg", arg.sourceContext) + + case "~" => + if (arg.tpe.isNumTypeOrNoType) + mkBitInversion(arg.asNumValue) + else + error(s"Numeric argument expected for '$opName' operation: $arg", arg.sourceContext) + + case _ => + error(s"Unknown prefix operation $opName for $arg", arg.sourceContext) } } @@ -87,12 +100,23 @@ object SigmaParser extends Exprs with Types with Core { case ">" => mkGT(l, r) case "<=" => mkLE(l, r) case "<" => mkLT(l, r) - case "-" => mkMinus(l.asValue[SLong.type], r.asValue[SLong.type]) - case "|" => mkBitOr(l.asNumValue, r.asNumValue) - case "&" => mkBitAnd(l.asNumValue, r.asNumValue) + case "-" => mkMinus(l.asNumValue, r.asNumValue) + + case "|" => + if (l.tpe.isNumTypeOrNoType && r.tpe.isNumTypeOrNoType) + mkBitOr(l.asNumValue, r.asNumValue) + else + error(s"Numeric arguments expected for '$opName' operation: ($l, $r)", l.sourceContext) + + case "&" => + if (l.tpe.isNumTypeOrNoType && r.tpe.isNumTypeOrNoType) + mkBitAnd(l.asNumValue, r.asNumValue) + else + error(s"Numeric arguments expected for '$opName' operation: ($l, $r)", l.sourceContext) + case _ if parseAsMethods.contains(opName) => mkMethodCallLike(l, opName, IndexedSeq(r)) - case "/" => mkDivide(l.asValue[SLong.type], r.asValue[SLong.type]) - case "%" => mkModulo(l.asValue[SLong.type], r.asValue[SLong.type]) + case "/" => mkDivide(l.asNumValue, r.asNumValue) + case "%" => mkModulo(l.asNumValue, r.asNumValue) case _ => error(s"Unknown binary operation $opName", l.sourceContext) } } diff --git a/sigmastate/src/main/scala/sigmastate/lang/SigmaPredef.scala b/sigmastate/src/main/scala/sigmastate/lang/SigmaPredef.scala index 22719d741d..ca7d94e147 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/SigmaPredef.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/SigmaPredef.scala @@ -44,52 +44,48 @@ object SigmaPredef { import builder._ /** Type variable used in the signatures of global functions below. */ - private val tT = STypeVar("T") - private val tK = STypeVar("K") - private val tL = STypeVar("L") - private val tR = STypeVar("R") - private val tO = STypeVar("O") + import SType.{tT, tR, tK, tL, tO, paramT, paramR} private val undefined: IrBuilderFunc = PartialFunction.empty[(SValue, Seq[SValue]), SValue] val AllOfFunc = PredefinedFunc("allOf", - Lambda(IndexedSeq("conditions" -> SCollection(SBoolean)), SBoolean, None), + Lambda(Array("conditions" -> SCollection(SBoolean)), SBoolean, None), PredefFuncInfo({ case (_, Seq(col: Value[SCollection[SBoolean.type]]@unchecked)) => mkAND(col) }), OperationInfo(AND, "Returns true if \\emph{all} the elements in collection are \\lst{true}.", Seq(ArgInfo("conditions", "a collection of conditions"))) ) val AnyOfFunc = PredefinedFunc("anyOf", - Lambda(Vector("conditions" -> SCollection(SBoolean)), SBoolean, None), + Lambda(Array("conditions" -> SCollection(SBoolean)), SBoolean, None), PredefFuncInfo( { case (_, Seq(col: Value[SCollection[SBoolean.type]]@unchecked)) => mkOR(col) }), OperationInfo(OR, "Returns true if \\emph{any} the elements in collection are \\lst{true}.", Seq(ArgInfo("conditions", "a collection of conditions"))) ) val XorOfFunc = PredefinedFunc("xorOf", - Lambda(Vector("conditions" -> SCollection(SBoolean)), SBoolean, None), + Lambda(Array("conditions" -> SCollection(SBoolean)), SBoolean, None), PredefFuncInfo({ case (_, Seq(col: Value[SCollection[SBoolean.type]]@unchecked)) => mkXorOf(col) }), OperationInfo(XorOf, "Similar to \\lst{allOf}, but performing logical XOR operation between all conditions instead of \\lst{&&}", Seq(ArgInfo("conditions", "a collection of conditions"))) ) val AllZKFunc = PredefinedFunc("allZK", - Lambda(IndexedSeq("propositions" -> SCollection(SSigmaProp)), SSigmaProp, None), + Lambda(Array("propositions" -> SCollection(SSigmaProp)), SSigmaProp, None), PredefFuncInfo(undefined), OperationInfo(SigmaAnd, "Returns sigma proposition which is proven when \\emph{all} the elements in collection are proven.", Seq(ArgInfo("propositions", "a collection of propositions"))) ) val AnyZKFunc = PredefinedFunc("anyZK", - Lambda(IndexedSeq("propositions" -> SCollection(SSigmaProp)), SSigmaProp, None), + Lambda(Array("propositions" -> SCollection(SSigmaProp)), SSigmaProp, None), PredefFuncInfo(undefined), OperationInfo(SigmaOr, "Returns sigma proposition which is proven when \\emph{any} of the elements in collection is proven.", Seq(ArgInfo("propositions", "a collection of propositions"))) ) val AtLeastFunc = PredefinedFunc("atLeast", - Lambda(Vector("k" -> SInt, "conditions" -> SCollection(SSigmaProp)), SSigmaProp, None), + Lambda(Array("k" -> SInt, "conditions" -> SCollection(SSigmaProp)), SSigmaProp, None), PredefFuncInfo( { case (_, Seq(bound: IntValue@unchecked, arr: Value[SCollection[SSigmaProp.type]]@unchecked)) => mkAtLeast(bound, arr) @@ -106,13 +102,13 @@ object SigmaPredef { val OuterJoinFunc = PredefinedFunc( "outerJoin", Lambda( - Seq(STypeParam(tK), STypeParam(tL), STypeParam(tR), STypeParam(tO)), - Vector( + Array(STypeParam(tK), STypeParam(tL), STypeParam(tR), STypeParam(tO)), + Array( "left" -> SCollection(STuple(tK, tL)), "right" -> SCollection(STuple(tK, tR)), - "l" -> SFunc(IndexedSeq(tK, tL), tO), - "r" -> SFunc(IndexedSeq(tK, tR), tO), - "inner" -> SFunc(IndexedSeq(tK, tL, tR), tO) + "l" -> SFunc(Array(tK, tL), tO), + "r" -> SFunc(Array(tK, tR), tO), + "inner" -> SFunc(Array(tK, tL, tR), tO) ), SCollection(STuple(tK, tO)), None), PredefFuncInfo(undefined), @@ -121,14 +117,14 @@ object SigmaPredef { ) val ZKProofFunc = PredefinedFunc("ZKProof", - Lambda(Vector("block" -> SSigmaProp), SBoolean, None), + Lambda(Array("block" -> SSigmaProp), SBoolean, None), PredefFuncInfo({ case (_, Seq(block: SigmaPropValue@unchecked)) => mkZKProofBlock(block) }), OperationInfo(ZKProofBlock, "", Seq(ArgInfo("", ""))) ) val SigmaPropFunc = PredefinedFunc("sigmaProp", - Lambda(Vector("condition" -> SBoolean), SSigmaProp, None), + Lambda(Array("condition" -> SBoolean), SSigmaProp, None), PredefFuncInfo({ case (_, Seq(b: BoolValue@unchecked)) => mkBoolToSigmaProp(b) }), OperationInfo(BoolToSigmaProp, """Embedding of \lst{Boolean} values to \lst{SigmaProp} values. @@ -140,7 +136,7 @@ object SigmaPredef { ) val GetVarFunc = PredefinedFunc("getVar", - Lambda(Seq(STypeParam(tT)), Vector("varId" -> SByte), SOption(tT), None), + Lambda(Array(paramT), Array("varId" -> SByte), SOption(tT), None), PredefFuncInfo( { case (Ident(_, SFunc(_, SOption(rtpe), _)), Seq(id: Constant[SNumericType]@unchecked)) => mkGetVar(SByte.downcast(id.value.asInstanceOf[AnyVal]), rtpe) @@ -151,7 +147,7 @@ object SigmaPredef { ) def PKFunc(networkPrefix: NetworkPrefix) = PredefinedFunc("PK", - Lambda(Vector("input" -> SString), SSigmaProp, None), + Lambda(Array("input" -> SString), SSigmaProp, None), PredefFuncInfo( { case (_, Seq(arg: EvaluatedValue[SString.type]@unchecked)) => ErgoAddressEncoder(networkPrefix).fromString(arg.value).get match { @@ -164,7 +160,7 @@ object SigmaPredef { ) val DeserializeFunc = PredefinedFunc("deserialize", - Lambda(Seq(STypeParam(tT)), Vector("str" -> SString), tT, None), + Lambda(Array(paramT), Array("str" -> SString), tT, None), PredefFuncInfo( { case (Ident(_, SFunc(_, tpe, _)), args) => if (args.length != 1) @@ -185,7 +181,7 @@ object SigmaPredef { ) val FromBase58Func = PredefinedFunc("fromBase58", - Lambda(Vector("input" -> SString), SByteArray, None), + Lambda(Array("input" -> SString), SByteArray, None), PredefFuncInfo( { case (_, Seq(arg: EvaluatedValue[SString.type]@unchecked)) => ByteArrayConstant(Base58.decode(arg.value).get) @@ -195,7 +191,7 @@ object SigmaPredef { ) val FromBase64Func = PredefinedFunc("fromBase64", - Lambda(Vector("input" -> SString), SByteArray, None), + Lambda(Array("input" -> SString), SByteArray, None), PredefFuncInfo( { case (_, Seq(arg: EvaluatedValue[SString.type]@unchecked)) => ByteArrayConstant(Base64.decode(arg.value).get) @@ -205,7 +201,7 @@ object SigmaPredef { ) val Blake2b256Func = PredefinedFunc("blake2b256", - Lambda(Vector("input" -> SByteArray), SByteArray, None), + Lambda(Array("input" -> SByteArray), SByteArray, None), PredefFuncInfo( { case (_, Seq(arg: Value[SByteArray]@unchecked)) => mkCalcBlake2b256(arg) @@ -215,7 +211,7 @@ object SigmaPredef { ) val Sha256Func = PredefinedFunc("sha256", - Lambda(Vector("input" -> SByteArray), SByteArray, None), + Lambda(Array("input" -> SByteArray), SByteArray, None), PredefFuncInfo( { case (_, Seq(arg: Value[SByteArray]@unchecked)) => mkCalcSha256(arg) @@ -225,7 +221,7 @@ object SigmaPredef { ) val ByteArrayToBigIntFunc = PredefinedFunc("byteArrayToBigInt", - Lambda(Vector("input" -> SByteArray), SBigInt, None), + Lambda(Array("input" -> SByteArray), SBigInt, None), PredefFuncInfo( { case (_, Seq(arg: Value[SByteArray]@unchecked)) => mkByteArrayToBigInt(arg) @@ -236,7 +232,7 @@ object SigmaPredef { ) val ByteArrayToLongFunc = PredefinedFunc("byteArrayToLong", - Lambda(Vector("input" -> SByteArray), SLong, None), + Lambda(Array("input" -> SByteArray), SLong, None), PredefFuncInfo( { case (_, Seq(arg: Value[SByteArray]@unchecked)) => mkByteArrayToLong(arg) @@ -246,7 +242,7 @@ object SigmaPredef { ) val DecodePointFunc = PredefinedFunc("decodePoint", - Lambda(Vector("input" -> SByteArray), SGroupElement, None), + Lambda(Array("input" -> SByteArray), SGroupElement, None), PredefFuncInfo( { case (_, Seq(arg: Value[SByteArray]@unchecked)) => mkDecodePoint(arg) @@ -257,7 +253,7 @@ object SigmaPredef { ) val LongToByteArrayFunc = PredefinedFunc("longToByteArray", - Lambda(Vector("input" -> SLong), SByteArray, None), + Lambda(Array("input" -> SLong), SByteArray, None), PredefFuncInfo( { case (_, Seq(arg: Value[SLong.type]@unchecked)) => mkLongToByteArray(arg) @@ -268,7 +264,7 @@ object SigmaPredef { ) val ProveDHTupleFunc = PredefinedFunc("proveDHTuple", - Lambda(Vector("g" -> SGroupElement, "h" -> SGroupElement, "u" -> SGroupElement, "v" -> SGroupElement), SSigmaProp, None), + Lambda(Array("g" -> SGroupElement, "h" -> SGroupElement, "u" -> SGroupElement, "v" -> SGroupElement), SSigmaProp, None), PredefFuncInfo( { case (_, Seq(g, h, u, v)) => mkCreateProveDHTuple(g.asGroupElement, h.asGroupElement, u.asGroupElement, v.asGroupElement) @@ -282,7 +278,7 @@ object SigmaPredef { ) val ProveDlogFunc = PredefinedFunc("proveDlog", - Lambda(Vector("value" -> SGroupElement), SSigmaProp, None), + Lambda(Array("value" -> SGroupElement), SSigmaProp, None), PredefFuncInfo( { case (_, Seq(arg: Value[SGroupElement.type]@unchecked)) => mkCreateProveDlog(arg) @@ -295,7 +291,7 @@ object SigmaPredef { ) val AvlTreeFunc = PredefinedFunc("avlTree", - Lambda(Vector("operationFlags" -> SByte, "digest" -> SByteArray, "keyLength" -> SInt, "valueLengthOpt" -> SIntOption), SAvlTree, None), + Lambda(Array("operationFlags" -> SByte, "digest" -> SByteArray, "keyLength" -> SInt, "valueLengthOpt" -> SIntOption), SAvlTree, None), PredefFuncInfo( { case (_, Seq(flags, digest, keyLength, valueLength)) => mkCreateAvlTree(flags.asByteValue, digest.asByteArray, keyLength.asIntValue, valueLength.asOption[SInt.type]) @@ -311,8 +307,8 @@ object SigmaPredef { val SubstConstantsFunc = PredefinedFunc("substConstants", Lambda( - Seq(STypeParam(tT)), - Vector("scriptBytes" -> SByteArray, "positions" -> SIntArray, "newValues" -> SCollection(tT)), + Seq(paramT), + Array("scriptBytes" -> SByteArray, "positions" -> SIntArray, "newValues" -> SCollection(tT)), SByteArray, None ), PredefFuncInfo( @@ -338,8 +334,8 @@ object SigmaPredef { val ExecuteFromVarFunc = PredefinedFunc("executeFromVar", Lambda( - Seq(STypeParam(tT)), - Vector("id" -> SByte), + Seq(paramT), + Array("id" -> SByte), tT, None ), PredefFuncInfo(undefined), @@ -356,8 +352,8 @@ object SigmaPredef { val ExecuteFromSelfRegFunc = PredefinedFunc("executeFromSelfReg", Lambda( - Seq(STypeParam(tT)), - Vector("id" -> SByte, "default" -> SOption(tT)), + Seq(paramT), + Array("id" -> SByte, "default" -> SOption(tT)), tT, None ), PredefFuncInfo(undefined), @@ -403,21 +399,21 @@ object SigmaPredef { def comparisonOp(symbolName: String, opDesc: ValueCompanion, desc: String, args: Seq[ArgInfo]) = { PredefinedFunc(symbolName, - Lambda(Seq(STypeParam(tT)), Vector("left" -> tT, "right" -> tT), SBoolean, None), + Lambda(Seq(paramT), Array("left" -> tT, "right" -> tT), SBoolean, None), PredefFuncInfo(undefined), OperationInfo(opDesc, desc, args) ) } def binaryOp(symbolName: String, opDesc: ValueCompanion, desc: String, args: Seq[ArgInfo]) = { PredefinedFunc(symbolName, - Lambda(Seq(STypeParam(tT)), Vector("left" -> tT, "right" -> tT), tT, None), + Lambda(Seq(paramT), Array("left" -> tT, "right" -> tT), tT, None), PredefFuncInfo(undefined), OperationInfo(opDesc, desc, args) ) } def logicalOp(symbolName: String, opDesc: ValueCompanion, desc: String, args: Seq[ArgInfo]) = { PredefinedFunc(symbolName, - Lambda(Vector("left" -> SBoolean, "right" -> SBoolean), SBoolean, None), + Lambda(Array("left" -> SBoolean, "right" -> SBoolean), SBoolean, None), PredefFuncInfo(undefined), OperationInfo(opDesc, desc, args) ) @@ -472,37 +468,37 @@ object SigmaPredef { Seq(ArgInfo("left", "left operand"), ArgInfo("right", "right operand"))), PredefinedFunc("binary_|", - Lambda(Vector("left" -> SByteArray, "right" -> SByteArray), SByteArray, None), + Lambda(Array("left" -> SByteArray, "right" -> SByteArray), SByteArray, None), PredefFuncInfo(undefined), OperationInfo(Xor, "Byte-wise XOR of two collections of bytes", - Seq(ArgInfo("left", "left operand"), ArgInfo("right", "right operand"))) + Array(ArgInfo("left", "left operand"), ArgInfo("right", "right operand"))) ), logicalOp("||", BinOr, "Logical OR of two operands", - Seq(ArgInfo("left", "left operand"), ArgInfo("right", "right operand"))), + Array(ArgInfo("left", "left operand"), ArgInfo("right", "right operand"))), logicalOp("&&", BinAnd, "Logical AND of two operands", - Seq(ArgInfo("left", "left operand"), ArgInfo("right", "right operand"))), + Array(ArgInfo("left", "left operand"), ArgInfo("right", "right operand"))), logicalOp("^", BinXor, "Logical XOR of two operands", - Seq(ArgInfo("left", "left operand"), ArgInfo("right", "right operand"))) + Array(ArgInfo("left", "left operand"), ArgInfo("right", "right operand"))) ).map(f => f.name -> f).toMap val unaryFuncs: Map[String, PredefinedFunc] = Seq( PredefinedFunc("unary_!", - Lambda(Vector("input" -> SBoolean), SBoolean, None), + Lambda(Array("input" -> SBoolean), SBoolean, None), PredefFuncInfo(undefined), OperationInfo(LogicalNot, "Logical NOT operation. Returns \\lst{true} if input is \\lst{false} and \\lst{false} if input is \\lst{true}.", Seq(ArgInfo("input", "input \\lst{Boolean} value"))) ), PredefinedFunc("unary_-", - Lambda(Seq(STypeParam(tT)), Vector("input" -> tT), tT, None), + Lambda(Array(paramT), Array("input" -> tT), tT, None), PredefFuncInfo(undefined), OperationInfo(Negation, "Negates numeric value \\lst{x} by returning \\lst{-x}.", Seq(ArgInfo("input", "value of numeric type"))) ), PredefinedFunc("unary_~", - Lambda(Seq(STypeParam(tT)), Vector("input" -> tT), tT, None), + Lambda(Seq(paramT), Array("input" -> tT), tT, None), PredefFuncInfo(undefined), OperationInfo(BitInversion, "Invert every bit of the numeric value.", @@ -517,14 +513,14 @@ object SigmaPredef { */ val specialFuncs: Map[String, PredefinedFunc] = Seq( PredefinedFunc("selectField", - Lambda(Seq(STypeParam(tT), STypeParam(tR)), Vector("input" -> tT, "fieldIndex" -> SByte), tR, None), + Lambda(Array(paramT, paramR), Array("input" -> tT, "fieldIndex" -> SByte), tR, None), PredefFuncInfo(undefined), OperationInfo(SelectField, "Select tuple field by its 1-based index. E.g. \\lst{input._1} is transformed to \\lst{SelectField(input, 1)}", Seq(ArgInfo("input", "tuple of items"), ArgInfo("fieldIndex", "index of an item to select"))) ), PredefinedFunc("treeLookup", - Lambda(Vector("tree" -> SAvlTree, "key" -> SByteArray, "proof" -> SByteArray), SOption(SByteArray), None), + Lambda(Array("tree" -> SAvlTree, "key" -> SByteArray, "proof" -> SByteArray), SOption(SByteArray), None), PredefFuncInfo(undefined), OperationInfo(TreeLookup, "", @@ -533,7 +529,7 @@ object SigmaPredef { ArgInfo("proof", "proof to perform verification of the operation"))) ), PredefinedFunc("if", - Lambda(Seq(STypeParam(tT)), Vector("condition" -> SBoolean, "trueBranch" -> tT, "falseBranch" -> tT), tT, None), + Lambda(Array(paramT), Array("condition" -> SBoolean, "trueBranch" -> tT, "falseBranch" -> tT), tT, None), PredefFuncInfo(undefined), OperationInfo(If, "Compute condition, if true then compute trueBranch else compute falseBranch", @@ -542,21 +538,21 @@ object SigmaPredef { ArgInfo("falseBranch", "expression to execute when \\lst{condition == false}"))) ), PredefinedFunc("upcast", - Lambda(Seq(STypeParam(tT), STypeParam(tR)), Vector("input" -> tT), tR, None), + Lambda(Array(paramT, paramR), Array("input" -> tT), tR, None), PredefFuncInfo(undefined), OperationInfo(Upcast, "Cast this numeric value to a bigger type (e.g. Int to Long)", Seq(ArgInfo("input", "value to cast"))) ), PredefinedFunc("downcast", - Lambda(Seq(STypeParam(tT), STypeParam(tR)), Vector("input" -> tT), tR, None), + Lambda(Array(paramT, paramR), Array("input" -> tT), tR, None), PredefFuncInfo(undefined), OperationInfo(Downcast, "Cast this numeric value to a smaller type (e.g. Long to Int). Throws exception if overflow.", Seq(ArgInfo("input", "value to cast"))) ), PredefinedFunc("apply", - Lambda(Seq(STypeParam(tT), STypeParam(tR)), Vector("func" -> SFunc(tT, tR), "args" -> tT), tR, None), + Lambda(Array(paramT, paramR), Array("func" -> SFunc(tT, tR), "args" -> tT), tR, None), PredefFuncInfo(undefined), OperationInfo(Apply, "Apply the function to the arguments. ", @@ -564,7 +560,7 @@ object SigmaPredef { ArgInfo("args", "list of arguments"))) ), PredefinedFunc("placeholder", - Lambda(Seq(STypeParam(tT)), Vector("id" -> SInt), tT, None), + Lambda(Array(paramT), Array("id" -> SInt), tT, None), PredefFuncInfo(undefined), OperationInfo(ConstantPlaceholder, "Create special ErgoTree node which can be replaced by constant with given id.", diff --git a/sigmastate/src/main/scala/sigmastate/lang/SigmaSpecializer.scala b/sigmastate/src/main/scala/sigmastate/lang/SigmaSpecializer.scala index ccc05721e5..e2e6869904 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/SigmaSpecializer.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/SigmaSpecializer.scala @@ -29,7 +29,7 @@ class SigmaSpecializer(val builder: SigmaBuilder) { /** Rewriting of AST with respect to environment to resolve all references * to let bound and lambda bound names. */ - private def eval(env: Map[String, SValue], e: SValue): SValue = rewrite(reduce(strategy[SValue]({ + private def eval(env: Map[String, SValue], e: SValue): SValue = rewrite(reduce(strategy[Any]({ case Ident(n, _) => env.get(n) case _ @ Block(binds, res) => diff --git a/sigmastate/src/main/scala/sigmastate/lang/SigmaTyper.scala b/sigmastate/src/main/scala/sigmastate/lang/SigmaTyper.scala index 93adb756c5..e1b6c1df03 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/SigmaTyper.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/SigmaTyper.scala @@ -25,7 +25,7 @@ class SigmaTyper(val builder: SigmaBuilder, predefFuncRegistry: PredefinedFuncRe private implicit val implicitPredefFuncRegistry: PredefinedFuncRegistry = predefFuncRegistry - private val tT = STypeVar("T") // to be used in typing rules + import SType.tT private val predefinedEnv: Map[String, SType] = predefFuncRegistry.funcs.mapValues(f => f.declaration.tpe) @@ -598,8 +598,8 @@ class SigmaTyper(val builder: SigmaBuilder, predefFuncRegistry: PredefinedFuncRe // traverse the tree bottom-up checking that all the nodes have a type var untyped: SValue = null - rewrite(everywherebu(rule[SValue]{ - case v => + rewrite(everywherebu(rule[Any]{ + case v: SValue => if (v.tpe == NoType) untyped = v v }))(assigned) @@ -671,7 +671,7 @@ object SigmaTyper { val remainingVars = tparams.filterNot { p => subst.contains(p.ident) } SFunc(args.map(applySubst(_, subst)), applySubst(res, subst), remainingVars) case _ => - val substRule = rule[SType] { + val substRule = rule[Any] { case id: STypeVar if subst.contains(id) => subst(id) } rewrite(everywherebu(substRule))(tpe) diff --git a/sigmastate/src/main/scala/sigmastate/lang/SourceContext.scala b/sigmastate/src/main/scala/sigmastate/lang/SourceContext.scala index 29d0178c2a..d9f4f3415e 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/SourceContext.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/SourceContext.scala @@ -23,7 +23,14 @@ object SourceContext { .map { case (((start, _), line), lineIndex) => SourceContext(lineIndex + 1, index - start + 1, line) - }.get + }.getOrElse { + // at least one line in the input + // point to the last character of the last line + val lastLine = lines.last + val iLine = lines.length - 1 + val iCol = lastLine.length - 1 + SourceContext(iLine, iCol, lastLine) + } } def fromParserFailure(e: Failure[_, String]): SourceContext = diff --git a/sigmastate/src/main/scala/sigmastate/lang/Terms.scala b/sigmastate/src/main/scala/sigmastate/lang/Terms.scala index 5dc00703cd..1e4d86af83 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/Terms.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/Terms.scala @@ -45,10 +45,11 @@ object Terms { case class ZKProofBlock(body: SigmaPropValue) extends BoolValue { override def companion = ZKProofBlock override def tpe = SBoolean - override def opType: SFunc = SFunc(SSigmaProp, SBoolean) + override def opType: SFunc = ZKProofBlock.OpType } object ZKProofBlock extends ValueCompanion { override def opCode: OpCode = OpCodes.Undefined + val OpType = SFunc(SSigmaProp, SBoolean) } trait Val extends Value[SType] { @@ -272,8 +273,8 @@ object Terms { * @return AST where all nodes with missing source context are set to the given srcCtx */ def withPropagatedSrcCtx[T <: SType](srcCtx: Nullable[SourceContext]): Value[T] = { - rewrite(everywherebu(rule[SValue] { - case node if node != null && node.sourceContext.isEmpty => + rewrite(everywherebu(rule[Any] { + case node: SValue if node != null && node.sourceContext.isEmpty => node.withSrcCtx(srcCtx) }))(v).asValue[T] } diff --git a/sigmastate/src/main/scala/sigmastate/lang/Types.scala b/sigmastate/src/main/scala/sigmastate/lang/Types.scala index ebd66a2627..486944bd89 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/Types.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/Types.scala @@ -39,7 +39,7 @@ trait Types extends Core { case STuple(items) => SFunc(items, r) case _ => - SFunc(IndexedSeq(d), r) + SFunc(Array(d), r) } } val Type: P[SType] = P( `=>`.? ~~ PostfixType ~ TypeBounds ~ `*`.? ) @@ -101,7 +101,7 @@ trait Types extends Core { val SimpleType = { // Can't `cut` after the opening paren, because we might be trying to parse `()` // or `() => T`! only cut after parsing one type - val TupleType = P( "(" ~/ Type.repTC() ~ ")" ).map(items => STuple(items.toIndexedSeq)) + val TupleType = P( "(" ~/ Type.repTC() ~ ")" ).map(items => STuple(items.toArray)) val BasicType = P( TupleType | TypeId ) P( Index ~ BasicType ~ TypeArgs.rep ).map { case (_, t: STuple, Seq()) => t diff --git a/sigmastate/src/main/scala/sigmastate/lang/exceptions/SigmaBuilderExceptions.scala b/sigmastate/src/main/scala/sigmastate/lang/exceptions/ConstraintFailed.scala similarity index 61% rename from sigmastate/src/main/scala/sigmastate/lang/exceptions/SigmaBuilderExceptions.scala rename to sigmastate/src/main/scala/sigmastate/lang/exceptions/ConstraintFailed.scala index f9af8e43a3..8073b624e2 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/exceptions/SigmaBuilderExceptions.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/exceptions/ConstraintFailed.scala @@ -5,6 +5,3 @@ import sigmastate.lang.SourceContext final class ConstraintFailed(message: String, source: Option[SourceContext] = None) extends BuilderException(message, source) -final class ArithException(message: String, source: Option[SourceContext] = None) - extends BuilderException(message, source) - diff --git a/sigmastate/src/main/scala/sigmastate/lang/exceptions/SigmaBinderExceptions.scala b/sigmastate/src/main/scala/sigmastate/lang/exceptions/InvalidArguments.scala similarity index 60% rename from sigmastate/src/main/scala/sigmastate/lang/exceptions/SigmaBinderExceptions.scala rename to sigmastate/src/main/scala/sigmastate/lang/exceptions/InvalidArguments.scala index 65d59a4c91..c6bf0324ef 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/exceptions/SigmaBinderExceptions.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/exceptions/InvalidArguments.scala @@ -5,5 +5,3 @@ import sigmastate.lang.SourceContext final class InvalidArguments(message: String, source: Option[SourceContext] = None) extends BinderException(message, source) -final class InvalidTypeArguments(message: String, source: Option[SourceContext] = None) - extends BinderException(message, source) diff --git a/sigmastate/src/main/scala/sigmastate/lang/exceptions/SigmaInterpreterExceptions.scala b/sigmastate/src/main/scala/sigmastate/lang/exceptions/SigmaInterpreterExceptions.scala deleted file mode 100644 index f8edf935c8..0000000000 --- a/sigmastate/src/main/scala/sigmastate/lang/exceptions/SigmaInterpreterExceptions.scala +++ /dev/null @@ -1,8 +0,0 @@ -package sigmastate.lang.exceptions - -import sigmastate.lang.SourceContext - -final class OptionUnwrapNone(message: String, source: Option[SourceContext] = None) - extends InterpreterException(message, source) - - diff --git a/sigmastate/src/main/scala/sigmastate/serialization/ConcreteCollectionBooleanConstantSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/ConcreteCollectionBooleanConstantSerializer.scala index 1d4b402c94..8dd4b22be3 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/ConcreteCollectionBooleanConstantSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/ConcreteCollectionBooleanConstantSerializer.scala @@ -20,7 +20,7 @@ case class ConcreteCollectionBooleanConstantSerializer(cons: (IndexedSeq[Value[S cfor(0)(_ < len, _ + 1) { i => bits(i) = items(i) match { case v: BooleanConstant => v.value - case v => error(s"Expected collection of BooleanConstant values, got: $v") + case v => error(s"Expected collection of BooleanConstant values, got: $v") // TODO cover with tests } } w.putBits(bits, bitsInfo) diff --git a/sigmastate/src/main/scala/sigmastate/serialization/CreateAvlTreeSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/CreateAvlTreeSerializer.scala index efd84f38b6..ecbb37040e 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/CreateAvlTreeSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/CreateAvlTreeSerializer.scala @@ -8,6 +8,7 @@ import sigmastate.Values._ import sigmastate.lang.Terms.ValueOps import sigmastate.utils.SigmaByteWriter.DataInfo +// TODO refactor: remove not used case class CreateAvlTreeSerializer( cons: (ByteValue, Value[SByteArray], IntValue, Value[SIntOption]) => AvlTreeValue ) diff --git a/sigmastate/src/main/scala/sigmastate/serialization/DataJsonEncoder.scala b/sigmastate/src/main/scala/sigmastate/serialization/DataJsonEncoder.scala index 7736779612..9a44051927 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/DataJsonEncoder.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/DataJsonEncoder.scala @@ -62,6 +62,7 @@ object DataJsonEncoder { case tup: STuple => val tArr = tup.items.toArray if (tArr.length != 2) { + // TODO cover with tests throw new SerializerException("Tuples with length not equal to 2 are not supported") } val rtypeArr = tArr.map(x => Evaluation.stypeToRType(x)) @@ -173,6 +174,7 @@ object DataJsonEncoder { case t: STuple => val tArr = t.items.toArray if (tArr.length != 2) { + // TODO cover with tests throw new SerializerException("Tuples with length not equal to 2 are not supported") } val collSource = mutable.ArrayBuilder.make[Any]() @@ -223,7 +225,7 @@ object DataJsonEncoder { implicit val tItem = (tpe match { case tTup: STuple if tTup.items.length == 2 => Evaluation.stypeToRType(tpe) - case _: STuple => + case _: STuple => // TODO cover with tests throw new SerializerException("Tuples with length not equal to 2 are not supported") case _ => Evaluation.stypeToRType(tpe) @@ -258,12 +260,13 @@ object DataJsonEncoder { (data, tpe) } - def decode(json: Json): (SType#WrappedType) = { + def decode(json: Json): SType#WrappedType = { val (data, _) = decodeWithTpe(json) data } - def decodeAnyValue(json: Json): (AnyValue) = { + // TODO cover with tests + def decodeAnyValue(json: Json): AnyValue = { val tpe = SigmaParser.parseType(json.hcursor.downField("type").focus.get.asString.get) val value = json.hcursor.downField("value").focus.get val data = decodeData(value, tpe) diff --git a/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala index cd6ac43bc3..de5d47f4c7 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala @@ -69,7 +69,7 @@ object DataSerializer { val len = arr.length assert(arr.length == t.items.length, s"Type $t doesn't correspond to value $arr") if (len > 0xFFFF) - sys.error(s"Length of tuple $arr exceeds ${0xFFFF} limit.") + sys.error(s"Length of tuple $arr exceeds ${0xFFFF} limit.") // TODO cover with tests var i = 0 while (i < arr.length) { serialize[SType](arr(i), t.items(i), w) @@ -81,7 +81,7 @@ object DataSerializer { /** Reads a data value from Reader. The data value bytes is expected to confirm * to the type descriptor `tpe`. */ - def deserialize[T <: SType](tpe: T, r: SigmaByteReader): (T#WrappedType) = { + def deserialize[T <: SType](tpe: T, r: SigmaByteReader): T#WrappedType = { val depth = r.level r.level = depth + 1 val res = (tpe match { @@ -97,8 +97,11 @@ object DataSerializer { new String(bytes, StandardCharsets.UTF_8) case SBigInt => val size: Short = r.getUShort().toShort - if (size > SBigInt.MaxSizeInBytes) + // TODO HF: replace with validation rule to enable soft-forkability + if (size > SBigInt.MaxSizeInBytes) { + // TODO cover consensus with tests throw new SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes: $size") + } val valueBytes = r.getBytes(size) SigmaDsl.BigInt(new BigInteger(valueBytes)) case SGroupElement => @@ -134,7 +137,7 @@ object DataSerializer { case SBoolean => Colls.fromArray(r.getBits(len)).asInstanceOf[Coll[T#WrappedType]] case SByte => - // TODO make covered + // TODO cover with tests Colls.fromArray(r.getBytes(len)).asInstanceOf[Coll[T#WrappedType]] case _ => implicit val tItem = (tpeElem match { diff --git a/sigmastate/src/main/scala/sigmastate/serialization/ErgoTreeSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/ErgoTreeSerializer.scala index e809854b78..719cb576cc 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/ErgoTreeSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/ErgoTreeSerializer.scala @@ -241,7 +241,7 @@ class ErgoTreeSerializer { val (header, _, constants, treeBytes) = deserializeHeaderWithTreeBytes(r) val w = SigmaSerializer.startWriter() w.put(header) - w.putUInt(constants.length) + w.putUInt(constants.length) // TODO HF: this should not be serialized when segregation is off val constantSerializer = ConstantSerializer(DeserializationSigmaBuilder) constants.zipWithIndex.foreach { @@ -254,6 +254,7 @@ class ErgoTreeSerializer { val newConsts = constantStore.getAll assert(newConsts.length == 1) val newConst = newConsts.head + // TODO HF: replace assert with require assert(c.tpe == newConst.tpe, s"expected new constant to have the same ${c.tpe} tpe, got ${newConst.tpe}") constantSerializer.serialize(newConst, w) case (c, _) => diff --git a/sigmastate/src/main/scala/sigmastate/serialization/ModQArithOpSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/ModQArithOpSerializer.scala index 9ca556effb..3e0bb9119d 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/ModQArithOpSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/ModQArithOpSerializer.scala @@ -6,6 +6,7 @@ import sigmastate.utils.SigmaByteWriter.DataInfo import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} import sigmastate.{ModQArithOpCompanion, SType, ModQArithOp} +// TODO HF: make sure it is covered with tests case class ModQArithOpSerializer(override val opDesc: ModQArithOpCompanion, cons: (BigIntValue, BigIntValue) => BigIntValue) extends ValueSerializer[ModQArithOp] { val leftInfo: DataInfo[SValue] = opDesc.argInfos(0) diff --git a/sigmastate/src/main/scala/sigmastate/serialization/ModQSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/ModQSerializer.scala index 9017ea23f5..26be575e41 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/ModQSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/ModQSerializer.scala @@ -1,17 +1,15 @@ package sigmastate.serialization -// import sigmastate.Operations.ModQInfo import sigmastate.Values.Value import sigmastate.lang.Terms._ import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} import sigmastate.{ModQ, SType} +// TODO HF: make sure it is covered with tests object ModQSerializer extends ValueSerializer[ModQ] { override def opDesc = ModQ def serialize(obj: ModQ, w: SigmaByteWriter): Unit = { - // TODO soft-fork: - // w.putValue(obj.input, ModQInfo.thisArg) w.putValue(obj.input, "this") } diff --git a/sigmastate/src/main/scala/sigmastate/serialization/OperationSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/OperationSerializer.scala index 85b0eeb695..630c78c728 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/OperationSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/OperationSerializer.scala @@ -64,7 +64,7 @@ class OperationSerializer(keyLength: Int, valueLengthOpt: Option[Int]) extends S case Insert(key, value) => serializeKeyValue(4: Byte, key, value) case Update(key, value) => serializeKeyValue(5: Byte, key, value) case InsertOrUpdate(key, value) => serializeKeyValue(6: Byte, key, value) - case _ => w.put(0: Byte) + case _ => w.put(0: Byte) // TODO cover with tests } } diff --git a/sigmastate/src/main/scala/sigmastate/serialization/SigmaSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/SigmaSerializer.scala index e9b306be84..cdb61cd85b 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/SigmaSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/SigmaSerializer.scala @@ -65,10 +65,16 @@ object SigmaSerializer { trait SigmaSerializer[TFamily, T <: TFamily] extends Serializer[TFamily, T, SigmaByteReader, SigmaByteWriter] { + /** Wraps the given writer in SigmaByteWriter and delegates to [[serialize]]. + * NOTE: it is used in spam tests. + */ def serializeWithGenericWriter(obj: T, w: Writer): Unit = { serialize(obj, new SigmaByteWriter(w, None)) } + /** Wraps the given reader in SigmaByteReader and delegates to [[parse]]. + * NOTE: it is used in spam tests. + */ def parseWithGenericReader(r: Reader)(implicit vs: SigmaValidationSettings): TFamily = { val sigmaByteReader = new SigmaByteReader(r, new ConstantStore(), diff --git a/sigmastate/src/main/scala/sigmastate/serialization/TypeSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/TypeSerializer.scala index cf0e5dcd4b..3c6659d056 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/TypeSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/TypeSerializer.scala @@ -6,6 +6,7 @@ import org.ergoplatform.validation.ValidationRules.{CheckPrimitiveTypeCode, Chec import sigmastate._ import sigmastate.lang.exceptions.InvalidTypePrefix import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import spire.syntax.all.cfor /** Serialization of types according to specification in TypeSerialization.md. */ object TypeSerializer extends ByteBufferSerializer[SType] { @@ -89,7 +90,7 @@ object TypeSerializer extends ByteBufferSerializer[SType] { serialize(t2, w) } case STuple(items) if items.length < 2 => - sys.error(s"Invalid Tuple type with less than 2 items $items") + sys.error(s"Invalid Tuple type with less than 2 items $items") // TODO cover with tests case tup: STuple => tup.items.length match { case 3 => // Triple of types @@ -173,7 +174,10 @@ object TypeSerializer extends ByteBufferSerializer[SType] { c match { case STuple.TupleTypeCode => { val len = r.getUByte() - val items = (0 until len).map(_ => deserialize(r, depth + 1)) + val items = new Array[SType](len) + cfor(0)(_ < len, _ + 1) { i => + items(i) = deserialize(r, depth + 1) + } STuple(items) } case SAny.typeCode => SAny diff --git a/sigmastate/src/main/scala/sigmastate/trees.scala b/sigmastate/src/main/scala/sigmastate/trees.scala index 8b88870cc9..eb56661cd2 100644 --- a/sigmastate/src/main/scala/sigmastate/trees.scala +++ b/sigmastate/src/main/scala/sigmastate/trees.scala @@ -16,13 +16,29 @@ import sigmastate.utxo.{Transformer, SimpleTransformerCompanion} import scala.collection.mutable import scala.collection.mutable.ArrayBuffer + +/** + * Basic trait for inner nodes of crypto-trees, so AND/OR/THRESHOLD sigma-protocol connectives + */ +trait SigmaConjecture extends SigmaBoolean { + def children: Seq[SigmaBoolean] +} + +/** + * Basic trait for leafs of crypto-trees, such as ProveDlog and ProveDiffieHellman instances + */ +trait SigmaProofOfKnowledgeLeaf[SP <: SigmaProtocol[SP], S <: SigmaProtocolPrivateInput[SP, _]] + extends SigmaBoolean with SigmaProtocolCommonInput[SP] + + /** * AND conjunction for sigma propositions */ -case class CAND(sigmaBooleans: Seq[SigmaBoolean]) extends SigmaBoolean { +case class CAND(override val children: Seq[SigmaBoolean]) extends SigmaConjecture { /** The same code is used for AND operation, but they belong to different type hierarchies. */ override val opCode: OpCode = OpCodes.AndCode } + object CAND { import TrivialProp._ def normalized(items: Seq[SigmaBoolean]): SigmaBoolean = { @@ -44,10 +60,11 @@ object CAND { /** * OR disjunction for sigma propositions */ -case class COR(sigmaBooleans: Seq[SigmaBoolean]) extends SigmaBoolean { +case class COR(children: Seq[SigmaBoolean]) extends SigmaConjecture { /** The same code is also used for OR operation, but they belong to different type hierarchies. */ override val opCode: OpCode = OpCodes.OrCode } + object COR { import TrivialProp._ def normalized(items: Seq[SigmaBoolean]): SigmaBoolean = { @@ -69,15 +86,13 @@ object COR { /** * THRESHOLD connector for sigma propositions */ -case class CTHRESHOLD(k: Int, sigmaBooleans: Seq[SigmaBoolean]) extends SigmaBoolean { +case class CTHRESHOLD(k: Int, children: Seq[SigmaBoolean]) extends SigmaConjecture { // Our polynomial arithmetic can take only byte inputs - require(k >= 0 && k <= sigmaBooleans.length && sigmaBooleans.length <= 255) + require(k >= 0 && k <= children.length && children.length <= 255) override val opCode: OpCode = OpCodes.AtLeastCode } -trait SigmaProofOfKnowledgeLeaf[SP <: SigmaProtocol[SP], S <: SigmaProtocolPrivateInput[SP, _]] - extends SigmaBoolean with SigmaProtocolCommonInput[SP] /** Represents boolean values (true/false) in SigmaBoolean tree. * Participates in evaluation of CAND, COR, THRESHOLD connectives over SigmaBoolean values. @@ -110,11 +125,12 @@ object TrivialProp { */ case class BoolToSigmaProp(value: BoolValue) extends SigmaPropValue { override def companion = BoolToSigmaProp - def tpe = SSigmaProp - val opType = SFunc(SBoolean, SSigmaProp) + override def tpe = SSigmaProp + override def opType = BoolToSigmaProp.OpType } object BoolToSigmaProp extends ValueCompanion { override def opCode: OpCode = OpCodes.BoolToSigmaPropCode + val OpType = SFunc(SBoolean, SSigmaProp) } /** ErgoTree operation to create a new SigmaProp value representing public key @@ -122,12 +138,14 @@ object BoolToSigmaProp extends ValueCompanion { case class CreateProveDlog(value: Value[SGroupElement.type]) extends SigmaPropValue { override def companion = CreateProveDlog override def tpe = SSigmaProp - override def opType = SFunc(SGroupElement, SSigmaProp) + override def opType = CreateProveDlog.OpType } object CreateProveDlog extends ValueCompanion { override def opCode: OpCode = OpCodes.ProveDlogCode + val OpType = SFunc(SGroupElement, SSigmaProp) } +// TODO refactor: remove not used class /** Construct a new authenticated dictionary with given parameters and tree root digest.*/ case class CreateAvlTree(operationFlags: ByteValue, digest: Value[SByteArray], @@ -168,10 +186,11 @@ trait SigmaTransformerCompanion extends ValueCompanion { */ case class SigmaAnd(items: Seq[SigmaPropValue]) extends SigmaTransformer[SigmaPropValue, SigmaPropValue] { override def companion = SigmaAnd - def tpe = SSigmaProp - val opType = SFunc(SCollection.SSigmaPropArray, SSigmaProp) + override def tpe = SSigmaProp + override def opType = SigmaAnd.OpType } object SigmaAnd extends SigmaTransformerCompanion { + val OpType = SFunc(SCollection.SSigmaPropArray, SSigmaProp) override def opCode: OpCode = OpCodes.SigmaAndCode override def argInfos: Seq[ArgInfo] = SigmaAndInfo.argInfos def apply(first: SigmaPropValue, second: SigmaPropValue, tail: SigmaPropValue*): SigmaAnd = SigmaAnd(Array(first, second) ++ tail) @@ -182,16 +201,22 @@ object SigmaAnd extends SigmaTransformerCompanion { */ case class SigmaOr(items: Seq[SigmaPropValue]) extends SigmaTransformer[SigmaPropValue, SigmaPropValue] { override def companion = SigmaOr - def tpe = SSigmaProp - val opType = SFunc(SCollection.SSigmaPropArray, SSigmaProp) + override def tpe = SSigmaProp + override def opType = SigmaOr.OpType } object SigmaOr extends SigmaTransformerCompanion { + val OpType = SFunc(SCollection.SSigmaPropArray, SSigmaProp) override def opCode: OpCode = OpCodes.SigmaOrCode override def argInfos: Seq[ArgInfo] = SigmaOrInfo.argInfos def apply(head: SigmaPropValue, tail: SigmaPropValue*): SigmaOr = SigmaOr(head +: tail) } +/** Base trait for companions of OR, AND and XorOf nodes. */ +trait LogicalTransformerCompanion extends ValueCompanion { + def argInfos: Seq[ArgInfo] + val OpType: SFunc = SFunc(SCollection.SBooleanArray, SBoolean) +} /** * OR logical conjunction @@ -199,7 +224,7 @@ object SigmaOr extends SigmaTransformerCompanion { case class OR(input: Value[SCollection[SBoolean.type]]) extends Transformer[SCollection[SBoolean.type], SBoolean.type] with NotReadyValueBoolean { override def companion = OR - override val opType = SFunc(SCollection.SBooleanArray, SBoolean) + override def opType = OR.OpType } object OR extends LogicalTransformerCompanion { @@ -217,7 +242,7 @@ object OR extends LogicalTransformerCompanion { case class XorOf(input: Value[SCollection[SBoolean.type]]) extends Transformer[SCollection[SBoolean.type], SBoolean.type] with NotReadyValueBoolean { override def companion = XorOf - override val opType = SFunc(SCollection.SBooleanArray, SBoolean) + override def opType = XorOf.OpType } object XorOf extends LogicalTransformerCompanion { @@ -226,8 +251,6 @@ object XorOf extends LogicalTransformerCompanion { def apply(children: Seq[Value[SBoolean.type]]): XorOf = XorOf(ConcreteCollection.fromSeq(children)) - - def apply(head: Value[SBoolean.type], tail: Value[SBoolean.type]*): XorOf = apply(head +: tail) } /** @@ -237,11 +260,7 @@ case class AND(input: Value[SCollection[SBoolean.type]]) extends Transformer[SCollection[SBoolean.type], SBoolean.type] with NotReadyValueBoolean { override def companion = AND - override val opType = SFunc(SCollection.SBooleanArray, SBoolean) -} - -trait LogicalTransformerCompanion extends ValueCompanion { - def argInfos: Seq[ArgInfo] + override def opType = AND.OpType } object AND extends LogicalTransformerCompanion { @@ -265,18 +284,20 @@ case class AtLeast(bound: Value[SInt.type], input: Value[SCollection[SSigmaProp. with NotReadyValue[SSigmaProp.type] { override def companion = AtLeast override def tpe: SSigmaProp.type = SSigmaProp - override def opType: SFunc = SFunc(IndexedSeq(SInt, SCollection.SBooleanArray), SBoolean) + override def opType: SFunc = AtLeast.OpType } object AtLeast extends ValueCompanion { override def opCode: OpCode = AtLeastCode + val OpType: SFunc = SFunc(Array(SInt, SCollection.SBooleanArray), SBoolean) val MaxChildrenCount: Int = SigmaConstants.MaxChildrenCountForAtLeastOp.value def apply(bound: Value[SInt.type], children: Seq[SigmaPropValue]): AtLeast = AtLeast(bound, ConcreteCollection.fromSeq(children)) - def apply(bound: Value[SInt.type], head: SigmaPropValue, tail: SigmaPropValue*): AtLeast = apply(bound, head +: tail) + def apply(bound: Value[SInt.type], head: SigmaPropValue, tail: SigmaPropValue*): AtLeast = + apply(bound, head +: tail) /** @hotspot don't beautify this code */ def reduce(bound: Int, children: Seq[SigmaBoolean]): SigmaBoolean = { @@ -337,19 +358,19 @@ object AtLeast extends ValueCompanion { */ case class Upcast[T <: SNumericType, R <: SNumericType](input: Value[T], tpe: R) extends Transformer[T, R] { - import Upcast._ require(input.tpe.isInstanceOf[SNumericType], s"Cannot create Upcast node for non-numeric type ${input.tpe}") override def companion = Upcast - override val opType = SFunc(Vector(tT), tR) + override def opType = Upcast.OpType } trait NumericCastCompanion extends ValueCompanion { def argInfos: Seq[ArgInfo] + val OpType = SFunc(Array(SType.tT), SType.tR) } object Upcast extends NumericCastCompanion { override def opCode: OpCode = OpCodes.UpcastCode override def argInfos: Seq[ArgInfo] = UpcastInfo.argInfos - val tT = STypeVar("T") - val tR = STypeVar("R") + def tT = SType.tT + def tR = SType.tR } /** @@ -357,17 +378,16 @@ object Upcast extends NumericCastCompanion { */ case class Downcast[T <: SNumericType, R <: SNumericType](input: Value[T], tpe: R) extends Transformer[T, R] { - import Downcast._ require(input.tpe.isInstanceOf[SNumericType], s"Cannot create Downcast node for non-numeric type ${input.tpe}") override def companion = Downcast - override val opType = SFunc(Vector(tT), tR) + override def opType = Downcast.OpType } object Downcast extends NumericCastCompanion { override def opCode: OpCode = OpCodes.DowncastCode override def argInfos: Seq[ArgInfo] = DowncastInfo.argInfos - val tT = STypeVar("T") - val tR = STypeVar("R") + def tT = SType.tT + def tR = SType.tR } /** @@ -376,9 +396,10 @@ object Downcast extends NumericCastCompanion { case class LongToByteArray(input: Value[SLong.type]) extends Transformer[SLong.type, SByteArray] with NotReadyValueByteArray { override def companion = LongToByteArray - override val opType = SFunc(SLong, SByteArray) + override def opType = LongToByteArray.OpType } object LongToByteArray extends SimpleTransformerCompanion { + val OpType = SFunc(SLong, SByteArray) override def opCode: OpCode = OpCodes.LongToByteArrayCode override def argInfos: Seq[ArgInfo] = LongToByteArrayInfo.argInfos } @@ -389,9 +410,10 @@ object LongToByteArray extends SimpleTransformerCompanion { case class ByteArrayToLong(input: Value[SByteArray]) extends Transformer[SByteArray, SLong.type] with NotReadyValueLong { override def companion = ByteArrayToLong - override val opType = SFunc(SByteArray, SLong) + override def opType = ByteArrayToLong.OpType } object ByteArrayToLong extends SimpleTransformerCompanion { + val OpType = SFunc(SByteArray, SLong) override def opCode: OpCode = OpCodes.ByteArrayToLongCode override def argInfos: Seq[ArgInfo] = ByteArrayToLongInfo.argInfos } @@ -402,9 +424,10 @@ object ByteArrayToLong extends SimpleTransformerCompanion { case class ByteArrayToBigInt(input: Value[SByteArray]) extends Transformer[SByteArray, SBigInt.type] with NotReadyValueBigInt { override def companion = ByteArrayToBigInt - override val opType = SFunc(SByteArray, SBigInt) + override val opType = ByteArrayToBigInt.OpType } object ByteArrayToBigInt extends SimpleTransformerCompanion { + val OpType = SFunc(SByteArray, SBigInt) override def opCode: OpCode = OpCodes.ByteArrayToBigIntCode override def argInfos: Seq[ArgInfo] = ByteArrayToBigIntInfo.argInfos } @@ -415,17 +438,20 @@ object ByteArrayToBigInt extends SimpleTransformerCompanion { case class DecodePoint(input: Value[SByteArray]) extends Transformer[SByteArray, SGroupElement.type] with NotReadyValueGroupElement { override def companion = DecodePoint - override val opType = SFunc(SByteArray, SGroupElement) + override def opType = DecodePoint.OpType } object DecodePoint extends SimpleTransformerCompanion { + val OpType = SFunc(SByteArray, SGroupElement) override def opCode: OpCode = OpCodes.DecodePointCode override def argInfos: Seq[ArgInfo] = DecodePointInfo.argInfos } -trait CalcHash extends Transformer[SByteArray, SByteArray] with NotReadyValueByteArray { - val input: Value[SByteArray] - val hashFn: CryptographicHash32 - override val opType = SFunc(SByteArray, SByteArray) +sealed abstract class CalcHash extends Transformer[SByteArray, SByteArray] with NotReadyValueByteArray { + def hashFn: CryptographicHash32 + override def opType = CalcHash.OpType +} +object CalcHash { + val OpType = SFunc(SByteArray, SByteArray) } /** @@ -469,14 +495,12 @@ object CalcSha256 extends SimpleTransformerCompanion { */ case class SubstConstants[T <: SType](scriptBytes: Value[SByteArray], positions: Value[SIntArray], newValues: Value[SCollection[T]]) extends NotReadyValueByteArray { - import SubstConstants._ override def companion = SubstConstants - override val opType = SFunc(Vector(SByteArray, SIntArray, SCollection(tT)), SByteArray) + override val opType = SFunc(Array(SByteArray, SIntArray, SCollection(SType.tT)), SByteArray) } object SubstConstants extends ValueCompanion { override def opCode: OpCode = OpCodes.SubstConstantsCode - val tT = STypeVar("T") def eval(scriptBytes: Array[Byte], positions: Array[Int], @@ -490,7 +514,6 @@ object SubstConstants extends ValueCompanion { sealed trait Triple[LIV <: SType, RIV <: SType, OV <: SType] extends NotReadyValue[OV] { val left: Value[LIV] val right: Value[RIV] - override def opType = SFunc(Vector(left.tpe, right.tpe), tpe) } sealed trait OneArgumentOperation[IV <: SType, OV <: SType] extends NotReadyValue[OV] { @@ -513,6 +536,7 @@ case class ArithOp[T <: SType](left: Value[T], right: Value[T], override val opC extends TwoArgumentsOperation[T, T, T] with NotReadyValue[T] { override def companion: ValueCompanion = ArithOp.operations(opCode) override def tpe: T = left.tpe + override val opType = SFunc(Array[SType](left.tpe, right.tpe), tpe) override def opName: String = ArithOp.opcodeToArithOpName(opCode) // TODO refactor: avoid such enumaration, use ArithOp.operations map instead @@ -550,7 +574,8 @@ object ArithOp { } /** Negation operation on numeric type T. */ -case class Negation[T <: SNumericType](input: Value[T]) extends OneArgumentOperation[T, T] { +case class Negation[T <: SType](input: Value[T]) extends OneArgumentOperation[T, T] { + require(input.tpe.isNumTypeOrNoType, s"invalid type ${input.tpe}") override def companion = Negation override def tpe: T = input.tpe } @@ -559,7 +584,8 @@ object Negation extends OneArgumentOperationCompanion { override def argInfos: Seq[ArgInfo] = NegationInfo.argInfos } -case class BitInversion[T <: SNumericType](input: Value[T]) extends OneArgumentOperation[T, T] { +case class BitInversion[T <: SType](input: Value[T]) extends OneArgumentOperation[T, T] { + require(input.tpe.isNumTypeOrNoType, s"invalid type ${input.tpe}") override def companion = BitInversion override def tpe: T = input.tpe } @@ -568,10 +594,12 @@ object BitInversion extends OneArgumentOperationCompanion { override def argInfos: Seq[ArgInfo] = BitInversionInfo.argInfos } -case class BitOp[T <: SNumericType](left: Value[T], right: Value[T], override val opCode: OpCode) +case class BitOp[T <: SType](left: Value[T], right: Value[T], override val opCode: OpCode) extends TwoArgumentsOperation[T, T, T] with NotReadyValue[T] { + require(left.tpe.isNumTypeOrNoType && right.tpe.isNumTypeOrNoType, s"invalid types left:${left.tpe}, right:${right.tpe}") override def companion = BitOp.operations(opCode) override def tpe: T = left.tpe + override val opType = SFunc(Array[SType](left.tpe, right.tpe), tpe) } /** NOTE: by-name argument is required for correct initialization order. */ class BitOpCompanion(val opCode: OpCode, val name: String, _argInfos: => Seq[ArgInfo]) extends TwoArgumentOperationCompanion { @@ -596,6 +624,7 @@ object BitOp { } } +// TODO HF: implement modular operations case class ModQ(input: Value[SBigInt.type]) extends NotReadyValue[SBigInt.type] { override def companion = ModQ @@ -610,7 +639,7 @@ case class ModQArithOp(left: Value[SBigInt.type], right: Value[SBigInt.type], ov extends NotReadyValue[SBigInt.type] { override def companion = ModQArithOp.operations(opCode) override def tpe: SBigInt.type = SBigInt - override def opType: SFunc = SFunc(Vector(left.tpe, right.tpe), tpe) + override def opType: SFunc = SFunc(Array(left.tpe, right.tpe), tpe) } abstract class ModQArithOpCompanion(val opCode: OpCode, val name: String) extends ValueCompanion { def argInfos: Seq[ArgInfo] @@ -648,8 +677,10 @@ case class Xor(override val left: Value[SByteArray], extends TwoArgumentsOperation[SByteArray, SByteArray, SByteArray] with NotReadyValueByteArray { override def companion = Xor + override def opType = Xor.OpType } object Xor extends TwoArgumentOperationCompanion { + val OpType = SFunc(Array(SByteArray, SByteArray), SByteArray) override def opCode: OpCode = XorCode override def argInfos: Seq[ArgInfo] = XorInfo.argInfos } @@ -659,8 +690,10 @@ case class Exponentiate(override val left: Value[SGroupElement.type], extends TwoArgumentsOperation[SGroupElement.type, SBigInt.type, SGroupElement.type] with NotReadyValueGroupElement { override def companion = Exponentiate + override def opType = Exponentiate.OpType } object Exponentiate extends TwoArgumentOperationCompanion { + val OpType = SFunc(Array(SGroupElement, SBigInt), SGroupElement) override def opCode: OpCode = ExponentiateCode override def argInfos: Seq[ArgInfo] = ExponentiateInfo.argInfos } @@ -670,8 +703,10 @@ case class MultiplyGroup(override val left: Value[SGroupElement.type], extends TwoArgumentsOperation[SGroupElement.type, SGroupElement.type, SGroupElement.type] with NotReadyValueGroupElement { override def companion = MultiplyGroup + override def opType = MultiplyGroup.OpType } object MultiplyGroup extends TwoArgumentOperationCompanion { + val OpType = SFunc(Array(SGroupElement, SGroupElement), SGroupElement) override def opCode: OpCode = MultiplyGroupCode override def argInfos: Seq[ArgInfo] = MultiplyGroupInfo.argInfos } @@ -681,8 +716,10 @@ sealed trait Relation[LIV <: SType, RIV <: SType] extends Triple[LIV, RIV, SBool with NotReadyValueBoolean trait SimpleRelation[T <: SType] extends Relation[T, T] { - val tT = STypeVar("T") - override val opType = SFunc(Vector(tT, tT), SBoolean) + override def opType = SimpleRelation.GenericOpType +} +object SimpleRelation { + val GenericOpType = SFunc(SType.IndexedSeqOfT2, SBoolean) } trait RelationCompanion extends ValueCompanion { @@ -762,8 +799,10 @@ object NEQ extends RelationCompanion { case class BinOr(override val left: BoolValue, override val right: BoolValue) extends Relation[SBoolean.type, SBoolean.type] { override def companion = BinOr + override def opType = BinOr.OpType } object BinOr extends RelationCompanion { + val OpType = SFunc(Array(SBoolean, SBoolean), SBoolean) override def opCode: OpCode = BinOrCode override def argInfos: Seq[ArgInfo] = BinOrInfo.argInfos } @@ -775,8 +814,10 @@ object BinOr extends RelationCompanion { case class BinAnd(override val left: BoolValue, override val right: BoolValue) extends Relation[SBoolean.type, SBoolean.type] { override def companion = BinAnd + override def opType = BinAnd.OpType } object BinAnd extends RelationCompanion { + val OpType = SFunc(Array(SBoolean, SBoolean), SBoolean) override def opCode: OpCode = BinAndCode override def argInfos: Seq[ArgInfo] = BinAndInfo.argInfos } @@ -784,8 +825,10 @@ object BinAnd extends RelationCompanion { case class BinXor(override val left: BoolValue, override val right: BoolValue) extends Relation[SBoolean.type, SBoolean.type] { override def companion = BinXor + override def opType = BinXor.OpType } object BinXor extends RelationCompanion { + val OpType = SFunc(Array(SBoolean, SBoolean), SBoolean) override def opCode: OpCode = BinXorCode override def argInfos: Seq[ArgInfo] = BinXorInfo.argInfos } @@ -799,7 +842,7 @@ sealed trait Quadruple[IV1 <: SType, IV2 <: SType, IV3 <: SType, OV <: SType] ex val second: Value[IV2] val third: Value[IV3] - val opType = SFunc(Vector(first.tpe, second.tpe, third.tpe), tpe) + val opType = SFunc(Array(first.tpe, second.tpe, third.tpe), tpe) } /** @@ -844,14 +887,15 @@ case class If[T <: SType](condition: Value[SBoolean.type], trueBranch: Value[T], object If extends QuadrupleCompanion { override def opCode: OpCode = OpCodes.IfCode override def argInfos: Seq[ArgInfo] = IfInfo.argInfos - val tT = STypeVar("T") + val GenericOpType = SFunc(Array(SBoolean, SType.tT, SType.tT), SType.tT) } case class LogicalNot(input: Value[SBoolean.type]) extends NotReadyValueBoolean { override def companion = LogicalNot - override val opType = SFunc(Vector(SBoolean), SBoolean) + override def opType = LogicalNot.OpType } object LogicalNot extends ValueCompanion { + val OpType = SFunc(Array(SBoolean), SBoolean) override def opCode: OpCode = OpCodes.LogicalNotCode } diff --git a/sigmastate/src/main/scala/sigmastate/types.scala b/sigmastate/src/main/scala/sigmastate/types.scala index c7f466d171..2cec907904 100644 --- a/sigmastate/src/main/scala/sigmastate/types.scala +++ b/sigmastate/src/main/scala/sigmastate/types.scala @@ -62,8 +62,13 @@ sealed trait SType extends SigmaNode { val typeCode: SType.TypeCode /** Approximate size of the given value in bytes. It is actual size only for primitive types.*/ - def dataSize(v: SType#WrappedType): Long = sys.error(s"Don't know how to compute dataCost($v) with T = $this") + def dataSize(v: SType#WrappedType): Long + /** Returns true if this type embeddable, i.e. a type that can be combined with type + * constructor for optimized encoding. For each embeddable type `T`, and type + * constructor `C`, the type `C[T]` can be represented by a single byte. + * @see [[sigmastate.serialization.TypeSerializer]] + */ def isEmbeddable: Boolean = false /** Returns true if dataSize doesn't depend on data value. @@ -73,10 +78,6 @@ sealed trait SType extends SigmaNode { /** Elvis operator for types. See https://en.wikipedia.org/wiki/Elvis_operator*/ def ?:(whenNoType: => SType): SType = if (this == NoType) whenNoType else this - /** Construct tree node Constant for a given data object. */ - def mkConstant(v: WrappedType): Value[this.type] = - sys.error(s"Don't know how mkConstant for data value $v with T = $this") - def withSubstTypes(subst: Map[STypeVar, SType]): SType = if (subst.isEmpty) this else @@ -95,9 +96,6 @@ sealed trait SType extends SigmaNode { object SType { /** Representation of type codes used in serialization. */ type TypeCode = Byte - object Codes { - - } val DummyValue = 0.asWrappedType @@ -122,6 +120,38 @@ object SType { } implicit val ErgoLikeContextRType: RType[ErgoLikeContext] = RType.fromClassTag(classTag[ErgoLikeContext]) + /** Named type variables and parameters used in generic types and method signatures. + * Generic type terms like `(Coll[IV],(IV) => Boolean) => Boolean` are used to represent + * method types of `Coll`` and `Option`` types. Each such type is an instance of [[SFunc]]. + * To represent variables (such as `IV` in the example above) [[STypeVar]] instances + * are used. + * + * Generic types are not supported by ErgoTree serialization format and STypeVars are + * used internally and never serialized (there is no serializer for STypeVar). + * Thus the usage of type variables is limited. + * + * All necessary type variables can be declared in advance and reused across all code + * base. This allows to avoid allocation of many duplicates and also improve + * performance of SType values. + */ + val tT = STypeVar("T") + val tR = STypeVar("R") + val tK = STypeVar("K") + val tL = STypeVar("L") + val tO = STypeVar("O") + val tD = STypeVar("D") + val tV = STypeVar("V") + val tIV = STypeVar("IV") + val tOV = STypeVar("OV") + + val paramT = STypeParam(tT) + val paramR = STypeParam(tR) + val paramIV = STypeParam(tIV) + val paramOV = STypeParam(tOV) + + val IndexedSeqOfT1: IndexedSeq[SType] = Array(SType.tT) + val IndexedSeqOfT2: IndexedSeq[SType] = Array(SType.tT, SType.tT) + /** All pre-defined types should be listed here. Note, NoType is not listed. * Should be in sync with sigmastate.lang.Types.predefTypes. */ val allPredefTypes = Seq(SBoolean, SByte, SShort, SInt, SLong, SBigInt, SContext, SGlobal, SHeader, SPreHeader, SAvlTree, SGroupElement, SSigmaProp, SString, SBox, SUnit, SAny) @@ -154,7 +184,17 @@ object SType { val okRange = f1.tRange.canBeTypedAs(f2.tRange) okDom && okRange } + + /** Returns true if this type is numeric (Byte, Short, etc.) + * @see [[sigmastate.SNumericType]] + */ def isNumType: Boolean = tpe.isInstanceOf[SNumericType] + + /** Returns true if this type is either numeric (Byte, Short, etc.) or is NoType. + * @see [[sigmastate.SNumericType]] + */ + def isNumTypeOrNoType: Boolean = isNumType || tpe == NoType + def asNumType: SNumericType = tpe.asInstanceOf[SNumericType] def asFunc: SFunc = tpe.asInstanceOf[SFunc] def asProduct: SProduct = tpe.asInstanceOf[SProduct] @@ -188,30 +228,6 @@ object SType { implicit class AnyOps(val x: Any) extends AnyVal { def asWrappedType: SType#WrappedType = x.asInstanceOf[SType#WrappedType] } - - def typeOfData(x: Any): Option[SType] = Option(x match { - case _: Boolean => SBoolean - case _: Byte => SByte - case _: Short => SShort - case _: Int => SInt - case _: Long => SLong - case _: BigInteger => SBigInt - case _: CryptoConstants.EcPointType => SGroupElement - case _: ErgoBox => SBox - case _: AvlTreeData => SAvlTree - case _: Unit => SUnit - case _: Array[Boolean] => SBooleanArray - case _: Array[Byte] => SByteArray - case _: Array[Short] => SShortArray - case _: Array[Int] => SIntArray - case _: Array[Long] => SLongArray - case _: Array[BigInteger] => SBigIntArray - case _: Array[EcPointType] => SGroupElementArray - case _: Array[ErgoBox] => SBoxArray - case _: Array[AvlTreeData] => SAvlTreeArray - case v: SValue => v.tpe - case _ => null - }) } /** Basic interface for all type companions. @@ -241,9 +257,6 @@ trait STypeCompanion { .groupBy(_.objType.typeId) .map { case (typeId, ms) => (typeId -> ms.map(m => m.methodId -> m).toMap) } - def hasMethodWithId(methodId: Byte): Boolean = - getMethodById(methodId).isDefined - /** Lookup method by its id in this type. */ @inline def getMethodById(methodId: Byte): Option[SMethod] = _methodsMap.get(typeId) @@ -288,33 +301,14 @@ trait SProduct extends SType { ms } - /** Checks if `this` product has exactly the same methods as `that`. */ - def sameMethods(that: SProduct): Boolean = { - if (methods.lengthCompare(that.methods.length) != 0) return false - // imperative to avoid allocation as it is supposed to be used frequently - for (i <- methods.indices) { - if (methods(i).name != that.methods(i).name) return false - } - true - } - def method(methodName: String): Option[SMethod] = methods.find(_.name == methodName) } /** Base trait implemented by all generic types (those which has type parameters, * e.g. Coll[T], Option[T], etc.)*/ trait SGenericType { + /** Type parameters of this generic type. */ def typeParams: Seq[STypeParam] - def tparamSubst: Map[STypeVar, SType] - - lazy val substitutedTypeParams: Seq[STypeParam] = - typeParams.map { tp => - tparamSubst.getOrElse(tp.ident, tp.ident) match { - case v: STypeVar => STypeParam(v) - case _ => tp - } - } - } /** Special interface to access CostingHandler. @@ -429,6 +423,8 @@ case object NoType extends SType { type WrappedType = Nothing override val typeCode = 0: Byte override def isConstantSize = true + override def dataSize(v: SType#WrappedType): Long = + sys.error(s"$this.dataSize($v) is not defined") } /** Base trait for all pre-defined types which are not necessary primitive (e.g. Box, AvlTree). @@ -453,7 +449,6 @@ trait SPrimType extends SType with SPredefType { /** Primitive type recognizer to pattern match on TypeCode */ object SPrimType { - def unapply(tc: TypeCode): Option[SType] = SType.typeCodeToType.get(tc) def unapply(t: SType): Option[SType] = SType.allPredefTypes.find(_ == t) /** Type code of the last valid prim type so that (1 to LastPrimTypeCode) is a range of valid codes. */ @@ -515,7 +510,7 @@ object SNumericType extends STypeCompanion { | Each boolean corresponds to one bit. """.stripMargin) - override val methods: Seq[SMethod] = Vector( + override val methods: Seq[SMethod] = Array( ToByteMethod, // see Downcast ToShortMethod, // see Downcast ToIntMethod, // see Downcast @@ -559,7 +554,6 @@ case object SBoolean extends SPrimType with SEmbeddable with SLogical with SProd .withInfo(PropertyCall, "Convert true to 1 and false to 0"), ) */ - override def mkConstant(v: Boolean): Value[SBoolean.type] = BooleanConstant(v) override def dataSize(v: SType#WrappedType): Long = 1 override def isConstantSize = true } @@ -568,7 +562,6 @@ case object SByte extends SPrimType with SEmbeddable with SNumericType with SMon override type WrappedType = Byte override val typeCode: TypeCode = 2: Byte override def typeId = typeCode - override def mkConstant(v: Byte): Value[SByte.type] = ByteConstant(v) override def dataSize(v: SType#WrappedType): Long = 1 override def isConstantSize = true override def upcast(v: AnyVal): Byte = v match { @@ -588,7 +581,6 @@ case object SShort extends SPrimType with SEmbeddable with SNumericType with SMo override type WrappedType = Short override val typeCode: TypeCode = 3: Byte override def typeId = typeCode - override def mkConstant(v: Short): Value[SShort.type] = ShortConstant(v) override def dataSize(v: SType#WrappedType): Long = 2 override def isConstantSize = true override def upcast(v: AnyVal): Short = v match { @@ -608,7 +600,6 @@ case object SInt extends SPrimType with SEmbeddable with SNumericType with SMono override type WrappedType = Int override val typeCode: TypeCode = 4: Byte override def typeId = typeCode - override def mkConstant(v: Int): Value[SInt.type] = IntConstant(v) override def dataSize(v: SType#WrappedType): Long = 4 override def isConstantSize = true override def upcast(v: AnyVal): Int = v match { @@ -630,7 +621,6 @@ case object SLong extends SPrimType with SEmbeddable with SNumericType with SMon override type WrappedType = Long override val typeCode: TypeCode = 5: Byte override def typeId = typeCode - override def mkConstant(v: Long): Value[SLong.type] = LongConstant(v) override def dataSize(v: SType#WrappedType): Long = 8 override def isConstantSize = true override def upcast(v: AnyVal): Long = v match { @@ -654,10 +644,9 @@ case object SBigInt extends SPrimType with SEmbeddable with SNumericType with SM override type WrappedType = BigInt override val typeCode: TypeCode = 6: Byte override def typeId = typeCode - override def mkConstant(v: BigInt): Value[SBigInt.type] = BigIntConstant(v) /** Type of Relation binary op like GE, LE, etc. */ - val RelationOpType = SFunc(Vector(SBigInt, SBigInt), SBoolean) + val RelationOpType = SFunc(Array(SBigInt, SBigInt), SBoolean) /** The maximum size of BigInteger value in byte array representation. */ val MaxSizeInBytes: Long = SigmaConstants.MaxBigIntSizeInBytes.value @@ -714,7 +703,6 @@ case object SString extends SProduct with SMonoType { override type WrappedType = String override val typeCode: TypeCode = 102: Byte override def typeId = typeCode - override def mkConstant(v: String): Value[SString.type] = StringConstant(v) override def dataSize(v: SType#WrappedType): Long = v.asInstanceOf[String].length override def isConstantSize = false } @@ -726,22 +714,22 @@ case object SGroupElement extends SProduct with SPrimType with SEmbeddable with override def typeId = typeCode override def coster: Option[CosterFactory] = Some(Coster(_.GroupElementCoster)) - val GetEncodedMethod: SMethod = SMethod(this, "getEncoded", SFunc(IndexedSeq(this), SByteArray), 2) + lazy val GetEncodedMethod: SMethod = SMethod(this, "getEncoded", SFunc(IndexedSeq(this), SByteArray), 2) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, "Get an encoding of the point value.") - val ExponentiateMethod: SMethod = SMethod(this, "exp", SFunc(IndexedSeq(this, SBigInt), this), 3) + lazy val ExponentiateMethod: SMethod = SMethod(this, "exp", SFunc(IndexedSeq(this, SBigInt), this), 3) .withIRInfo({ case (builder, obj, _, Seq(arg), _) => builder.mkExponentiate(obj.asGroupElement, arg.asBigInt) }) .withInfo(Exponentiate, "Exponentiate this \\lst{GroupElement} to the given number. Returns this to the power of k", ArgInfo("k", "The power")) - val MultiplyMethod: SMethod = SMethod(this, "multiply", SFunc(IndexedSeq(this, SGroupElement), this), 4) + lazy val MultiplyMethod: SMethod = SMethod(this, "multiply", SFunc(IndexedSeq(this, SGroupElement), this), 4) .withIRInfo({ case (builder, obj, _, Seq(arg), _) => builder.mkMultiplyGroup(obj.asGroupElement, arg.asGroupElement) }) .withInfo(MultiplyGroup, "Group operation.", ArgInfo("other", "other element of the group")) - val NegateMethod: SMethod = SMethod(this, "negate", SFunc(this, this), 5) + lazy val NegateMethod: SMethod = SMethod(this, "negate", SFunc(this, this), 5) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, "Inverse element of the group.") @@ -755,7 +743,6 @@ case object SGroupElement extends SProduct with SPrimType with SEmbeddable with MultiplyMethod, NegateMethod ) - override def mkConstant(v: GroupElement): Value[SGroupElement.type] = GroupElementConstant(v) override def dataSize(v: SType#WrappedType): Long = CryptoConstants.EncodedGroupElementLength.toLong override def isConstantSize = true } @@ -765,7 +752,6 @@ case object SSigmaProp extends SProduct with SPrimType with SEmbeddable with SLo override type WrappedType = SigmaProp override val typeCode: TypeCode = 8: Byte override def typeId = typeCode - override def mkConstant(v: SigmaProp): Value[SSigmaProp.type] = SigmaPropConstant(v) /** The maximum size of SigmaProp value in serialized byte array representation. */ val MaxSizeInBytes: Long = SigmaConstants.MaxSigmaPropSizeInBytes.value @@ -775,9 +761,9 @@ case object SSigmaProp extends SProduct with SPrimType with SEmbeddable with SLo override def isConstantSize = true val PropBytes = "propBytes" val IsProven = "isProven" - val PropBytesMethod = SMethod(this, PropBytes, SFunc(this, SByteArray), 1) + lazy val PropBytesMethod = SMethod(this, PropBytes, SFunc(this, SByteArray), 1) .withInfo(SigmaPropBytes, "Serialized bytes of this sigma proposition taken as ErgoTree.") - val IsProvenMethod = SMethod(this, IsProven, SFunc(this, SBoolean), 2) + lazy val IsProvenMethod = SMethod(this, IsProven, SFunc(this, SBoolean), 2) .withInfo(// available only at frontend of ErgoScript "Verify that sigma proposition is proven.") protected override def getMethods() = super.getMethods() ++ Seq( @@ -790,6 +776,8 @@ case object SAny extends SPrimType { override type WrappedType = Any override val typeCode: TypeCode = 97: Byte override def isConstantSize = false + override def dataSize(v: SType#WrappedType): Long = + sys.error(s"$this.dataSize($v) is not defined") } /** The type with single inhabitant value `()` */ @@ -821,8 +809,7 @@ case class SOption[ElemType <: SType](elemType: ElemType) extends SProduct with override def toString = s"Option[$elemType]" override def toTermString: String = s"Option[${elemType.toTermString}]" - val typeParams: Seq[STypeParam] = Seq(STypeParam(tT)) - def tparamSubst: Map[STypeVar, SType] = Map(tT -> elemType) + lazy val typeParams: Seq[STypeParam] = Array(SType.paramT) } object SOption extends STypeCompanion { @@ -832,7 +819,7 @@ object SOption extends STypeCompanion { val OptionCollectionTypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * OptionCollectionTypeConstrId).toByte override def typeId = OptionTypeCode - override def coster: Option[CosterFactory] = Some(Coster(_.OptionCoster)) + override val coster: Option[CosterFactory] = Some(Coster(_.OptionCoster)) type SBooleanOption = SOption[SBoolean.type] type SByteOption = SOption[SByte.type] @@ -856,15 +843,12 @@ object SOption extends STypeCompanion { implicit val SSigmaPropOption = SOption(SSigmaProp) implicit val SBoxOption = SOption(SBox) - implicit def optionTypeCollection[V <: SType](implicit tV: V): SOption[SCollection[V]] = SOption(SCollection[V]) - val IsDefined = "isDefined" val Get = "get" val GetOrElse = "getOrElse" val Fold = "fold" - val tT = STypeVar("T") - val tR = STypeVar("R") + import SType.{tT, tR, paramT, paramR} val ThisType = SOption(tT) val IsDefinedMethod = SMethod(this, IsDefined, SFunc(ThisType, SBoolean), 2) @@ -881,7 +865,7 @@ object SOption extends STypeCompanion { |return the result of evaluating \lst{default}. """.stripMargin, ArgInfo("default", "the default value")) - val FoldMethod = SMethod(this, Fold, SFunc(IndexedSeq(ThisType, tR, SFunc(tT, tR)), tR, Seq(tT, tR)), 5) + val FoldMethod = SMethod(this, Fold, SFunc(Array(ThisType, tR, SFunc(tT, tR)), tR, Seq(tT, tR)), 5) .withInfo(MethodCall, """Returns the result of applying \lst{f} to this option's | value if the option is nonempty. Otherwise, evaluates @@ -892,7 +876,7 @@ object SOption extends STypeCompanion { ArgInfo("f", "the function to apply if nonempty")) val MapMethod = SMethod(this, "map", - SFunc(IndexedSeq(ThisType, SFunc(tT, tR)), SOption(tR), Seq(STypeParam(tT), STypeParam(tR))), 7) + SFunc(Array(ThisType, SFunc(tT, tR)), SOption(tR), Array(paramT, paramR)), 7) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, """Returns a \lst{Some} containing the result of applying \lst{f} to this option's @@ -901,7 +885,7 @@ object SOption extends STypeCompanion { """.stripMargin, ArgInfo("f", "the function to apply")) val FilterMethod = SMethod(this, "filter", - SFunc(IndexedSeq(ThisType, SFunc(tT, SBoolean)), ThisType, Seq(STypeParam(tT))), 8) + SFunc(Array(ThisType, SFunc(tT, SBoolean)), ThisType, Array(paramT)), 8) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, """Returns this option if it is nonempty and applying the predicate \lst{p} to @@ -931,16 +915,12 @@ trait SCollection[T <: SType] extends SProduct with SGenericType { case class SCollectionType[T <: SType](elemType: T) extends SCollection[T] { override val typeCode: TypeCode = SCollectionType.CollectionTypeCode - override def mkConstant(v: Coll[T#WrappedType]): Value[this.type] = - CollectionConstant(v, elemType).asValue[this.type] - override def dataSize(v: SType#WrappedType): Long = { val coll = v.asInstanceOf[Coll[T#WrappedType]] implicit val sT = Sized.typeToSized(Evaluation.stypeToRType(elemType)) Sized.sizeOf(coll).dataSize } def typeParams: Seq[STypeParam] = SCollectionType.typeParams - def tparamSubst: Map[STypeVar, SType] = Map(tIV -> elemType) protected override def getMethods() = super.getMethods() ++ SCollection.methods override def toString = s"Coll[$elemType]" override def toTermString = s"Coll[${elemType.toTermString}]" @@ -951,19 +931,18 @@ object SCollectionType { val CollectionTypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * CollectionTypeConstrId).toByte val NestedCollectionTypeConstrId = 2 val NestedCollectionTypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * NestedCollectionTypeConstrId).toByte - val typeParams = Seq(STypeParam(tIV.name)) + val typeParams: Seq[STypeParam] = Array(SType.paramIV) } object SCollection extends STypeCompanion with MethodByNameUnapply { override def typeId = SCollectionType.CollectionTypeCode override def coster: Option[CosterFactory] = Some(Coster(_.CollCoster)) - val tIV = STypeVar("IV") - val paramIV = STypeParam(tIV) - val tOV = STypeVar("OV") - val paramOV = STypeParam(tOV) - val tK = STypeVar("K") - val tV = STypeVar("V") + import SType.{tK, tV, paramIV, paramOV} + + def tIV = SType.tIV + def tOV = SType.tOV + val ThisType = SCollection(tIV) val tOVColl = SCollection(tOV) val tPredicate = SFunc(tIV, SBoolean) @@ -1110,6 +1089,7 @@ object SCollection extends STypeCompanion with MethodByNameUnapply { SFunc(IndexedSeq(ThisType, tIV, SInt), SInt, Seq(paramIV)), 27) .withIRInfo(MethodCallIrBuilder).withInfo(MethodCall, "") + // TODO HF: related to https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 lazy val FindMethod = SMethod(this, "find", SFunc(IndexedSeq(ThisType, tPredicate), SOption(tIV), Seq(paramIV)), 28) .withIRInfo(MethodCallIrBuilder).withInfo(MethodCall, "") @@ -1179,7 +1159,6 @@ object SCollection extends STypeCompanion with MethodByNameUnapply { ) def apply[T <: SType](elemType: T): SCollection[T] = SCollectionType(elemType) def apply[T <: SType](implicit elemType: T, ov: Overload1): SCollection[T] = SCollectionType(elemType) - def unapply[T <: SType](tColl: SCollection[T]): Option[T] = Some(tColl.elemType) type SBooleanArray = SCollection[SBoolean.type] type SByteArray = SCollection[SByte.type] @@ -1244,12 +1223,7 @@ case class STuple(items: IndexedSeq[SType]) extends SCollection[SAny.type] { colMethods ++ tupleMethods } - /** Construct tree node Constant for a given data object. */ - override def mkConstant(v: Coll[Any]): Value[this.type] = - Constant[STuple](v, this).asValue[this.type] - val typeParams = Nil - val tparamSubst: Map[STypeVar, SType] = Map.empty override def toTermString = s"(${items.map(_.toTermString).mkString(",")})" override def toString = s"(${items.mkString(",")})" @@ -1272,7 +1246,7 @@ object STuple extends STypeCompanion { def typeId = TupleTypeCode lazy val colMethods = { - val subst = Map(SCollection.tIV -> SAny) + val subst = Map(SType.tIV -> SAny) // TODO: implement other val activeMethods = Set(1.toByte, 10.toByte) SCollection.methods.filter(m => activeMethods.contains(m.methodId)).map { m => @@ -1282,7 +1256,7 @@ object STuple extends STypeCompanion { def methods: Seq[SMethod] = sys.error(s"Shouldn't be called.") - def apply(items: SType*): STuple = STuple(items.toIndexedSeq) + def apply(items: SType*): STuple = STuple(items.toArray) val MaxTupleLength: Int = SigmaConstants.MaxTupleLength.value private val componentNames = Array.tabulate(MaxTupleLength){ i => s"_${i + 1}" } def componentNameByIndex(i: Int): String = @@ -1296,7 +1270,7 @@ object STuple extends STypeCompanion { /** Helper constuctor/extractor for tuples of two types. */ object SPair { - def apply(l: SType, r: SType) = STuple(Vector(l, r)) + def apply(l: SType, r: SType) = STuple(Array(l, r)) def unapply(t: STuple): Nullable[(SType, SType)] = t match { case STuple(IndexedSeq(l, r)) => Nullable((l, r)) case _ => Nullable.None @@ -1320,11 +1294,10 @@ case class SFunc(tDom: IndexedSeq[SType], tRange: SType, tpeParams: Seq[STypePa override def dataSize(v: SType#WrappedType) = 8L import SFunc._ val typeParams: Seq[STypeParam] = tpeParams - val tparamSubst: Map[STypeVar, SType] = Map.empty // defined in MethodCall.typeSubst def getGenericType: SFunc = { val typeParams: Seq[STypeParam] = tDom.zipWithIndex - .map { case (_, i) => STypeParam(tD.name + (i + 1)) } :+ STypeParam(tR.name) + .map { case (_, i) => STypeParam(SType.tD.name + (i + 1)) } :+ STypeParam(SType.tR.name) val ts = typeParams.map(_.ident) SFunc(ts.init.toIndexedSeq, ts.last, Nil) } @@ -1332,8 +1305,6 @@ case class SFunc(tDom: IndexedSeq[SType], tRange: SType, tpeParams: Seq[STypePa } object SFunc { - val tD = STypeVar("D") - val tR = STypeVar("R") final val FuncTypeCode: TypeCode = OpCodes.FirstFuncType def apply(tDom: SType, tRange: SType): SFunc = SFunc(Array(tDom), tRange) // @hotspot val identity = { x: Any => x } @@ -1344,6 +1315,8 @@ case class STypeApply(name: String, args: IndexedSeq[SType] = IndexedSeq()) exte override type WrappedType = Any override val typeCode = STypeApply.TypeCode override def isConstantSize = false + override def dataSize(v: SType#WrappedType): Long = + sys.error(s"$this.dataSize($v) is not defined") } object STypeApply { val TypeCode = 94: Byte @@ -1357,6 +1330,8 @@ case class STypeVar(name: String) extends SType { override def isConstantSize = false override def toString = name override def toTermString: String = name + override def dataSize(v: SType#WrappedType): Long = + sys.error(s"$this.dataSize($v) is not defined") } object STypeVar { val TypeCode: TypeCode = 103: Byte @@ -1368,22 +1343,24 @@ case object SBox extends SProduct with SPredefType with SMonoType { override type WrappedType = Box override val typeCode: TypeCode = 99: Byte override def typeId = typeCode - override def mkConstant(v: Box): Value[SBox.type] = BoxConstant(v) override def dataSize(v: SType#WrappedType): Long = { val box = v.asInstanceOf[this.WrappedType] Sized.sizeOf(box).dataSize } override def isConstantSize = false - val tT = STypeVar("T") + import SType.{tT, paramT} + + lazy val GetRegFuncType = SFunc(Array(SBox), SOption(tT), Array(paramT)) + def registers(idOfs: Int): Seq[SMethod] = { allRegisters.map { i => i match { case r: MandatoryRegisterId => - SMethod(this, s"R${i.asIndex}", SFunc(IndexedSeq(SBox), SOption(tT), Seq(STypeParam(tT))), (idOfs + i.asIndex + 1).toByte) + SMethod(this, s"R${i.asIndex}", GetRegFuncType, (idOfs + i.asIndex + 1).toByte) .withInfo(ExtractRegisterAs, r.purpose) case _ => - SMethod(this, s"R${i.asIndex}", SFunc(IndexedSeq(SBox), SOption(tT), Seq(STypeParam(tT))), (idOfs + i.asIndex + 1).toByte) + SMethod(this, s"R${i.asIndex}", GetRegFuncType, (idOfs + i.asIndex + 1).toByte) .withInfo(ExtractRegisterAs, "Non-mandatory register") } } @@ -1424,7 +1401,7 @@ case object SBox extends SProduct with SPredefType with SMonoType { | identifier followed by box index in the transaction outputs. """.stripMargin ) // see ExtractCreationInfo - lazy val getRegMethod = SMethod(this, "getReg", SFunc(IndexedSeq(SBox, SInt), SOption(tT), Seq(STypeParam(tT))), 7) + lazy val getRegMethod = SMethod(this, "getReg", SFunc(Array(SBox, SInt), SOption(tT), Array(paramT)), 7) .withInfo(ExtractRegisterAs, """ Extracts register by id and type. | Type param \lst{T} expected type of the register. @@ -1438,7 +1415,7 @@ case object SBox extends SProduct with SPredefType with SMonoType { // should be lazy to solve recursive initialization - protected override def getMethods() = super.getMethods() ++ Vector( + protected override def getMethods() = super.getMethods() ++ Array( ValueMethod, // see ExtractAmount PropositionBytesMethod, // see ExtractScriptBytes BytesMethod, // see ExtractBytes @@ -1456,22 +1433,21 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { override type WrappedType = AvlTree override val typeCode: TypeCode = 100: Byte override def typeId = typeCode - override def mkConstant(v: AvlTree): Value[SAvlTree.type] = AvlTreeConstant(v) override def dataSize(v: SType#WrappedType): Long = AvlTreeData.TreeDataSize override def isConstantSize = true import SOption._ - val TCollOptionCollByte = SCollection(SByteArrayOption) - val CollKeyValue = SCollection(STuple(SByteArray, SByteArray)) + lazy val TCollOptionCollByte = SCollection(SByteArrayOption) + lazy val CollKeyValue = SCollection(STuple(SByteArray, SByteArray)) - val digestMethod = SMethod(this, "digest", SFunc(this, SByteArray), 1) + lazy val digestMethod = SMethod(this, "digest", SFunc(this, SByteArray), 1) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, """Returns digest of the state represented by this tree. | Authenticated tree \lst{digest} = \lst{root hash bytes} ++ \lst{tree height} """.stripMargin) - val enabledOperationsMethod = SMethod(this, "enabledOperations", SFunc(this, SByte), 2) + lazy val enabledOperationsMethod = SMethod(this, "enabledOperations", SFunc(this, SByte), 2) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, """ Flags of enabled operations packed in single byte. @@ -1479,38 +1455,38 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { | \lst{isUpdateAllowed == (enabledOperations & 0x02) != 0}\newline | \lst{isRemoveAllowed == (enabledOperations & 0x04) != 0} """.stripMargin) - val keyLengthMethod = SMethod(this, "keyLength", SFunc(this, SInt), 3) + lazy val keyLengthMethod = SMethod(this, "keyLength", SFunc(this, SInt), 3) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, """ | """.stripMargin) - val valueLengthOptMethod = SMethod(this, "valueLengthOpt", SFunc(this, SIntOption), 4) + lazy val valueLengthOptMethod = SMethod(this, "valueLengthOpt", SFunc(this, SIntOption), 4) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, """ | """.stripMargin) - val isInsertAllowedMethod = SMethod(this, "isInsertAllowed", SFunc(this, SBoolean), 5) + lazy val isInsertAllowedMethod = SMethod(this, "isInsertAllowed", SFunc(this, SBoolean), 5) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, """ | """.stripMargin) - val isUpdateAllowedMethod = SMethod(this, "isUpdateAllowed", SFunc(this, SBoolean), 6) + lazy val isUpdateAllowedMethod = SMethod(this, "isUpdateAllowed", SFunc(this, SBoolean), 6) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, """ | """.stripMargin) - val isRemoveAllowedMethod = SMethod(this, "isRemoveAllowed", SFunc(this, SBoolean), 7) + lazy val isRemoveAllowedMethod = SMethod(this, "isRemoveAllowed", SFunc(this, SBoolean), 7) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, """ | """.stripMargin) - val updateOperationsMethod = SMethod(this, "updateOperations", + lazy val updateOperationsMethod = SMethod(this, "updateOperations", SFunc(IndexedSeq(SAvlTree, SByte), SAvlTree), 8) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, @@ -1518,7 +1494,7 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { | """.stripMargin) - val containsMethod = SMethod(this, "contains", + lazy val containsMethod = SMethod(this, "contains", SFunc(IndexedSeq(SAvlTree, SByteArray, SByteArray), SBoolean), 9) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, @@ -1535,7 +1511,7 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { | """.stripMargin) - val getMethod = SMethod(this, "get", + lazy val getMethod = SMethod(this, "get", SFunc(IndexedSeq(SAvlTree, SByteArray, SByteArray), SByteArrayOption), 10) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, @@ -1552,7 +1528,7 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { | """.stripMargin) - val getManyMethod = SMethod(this, "getMany", + lazy val getManyMethod = SMethod(this, "getMany", SFunc(IndexedSeq(SAvlTree, SByteArray2, SByteArray), TCollOptionCollByte), 11) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, @@ -1567,7 +1543,7 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { | """.stripMargin) - val insertMethod = SMethod(this, "insert", + lazy val insertMethod = SMethod(this, "insert", SFunc(IndexedSeq(SAvlTree, CollKeyValue, SByteArray), SAvlTreeOption), 12) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, @@ -1584,7 +1560,7 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { | """.stripMargin) - val updateMethod = SMethod(this, "update", + lazy val updateMethod = SMethod(this, "update", SFunc(IndexedSeq(SAvlTree, CollKeyValue, SByteArray), SAvlTreeOption), 13) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, @@ -1601,7 +1577,7 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { | """.stripMargin) - val removeMethod = SMethod(this, "remove", + lazy val removeMethod = SMethod(this, "remove", SFunc(IndexedSeq(SAvlTree, SByteArray2, SByteArray), SAvlTreeOption), 14) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, @@ -1618,7 +1594,7 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { | """.stripMargin) - val updateDigestMethod = SMethod(this, "updateDigest", + lazy val updateDigestMethod = SMethod(this, "updateDigest", SFunc(IndexedSeq(SAvlTree, SByteArray), SAvlTree), 15) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, @@ -1650,27 +1626,26 @@ case object SContext extends SProduct with SPredefType with SMonoType { override type WrappedType = ErgoLikeContext override val typeCode: TypeCode = 101: Byte override def typeId = typeCode - override def mkConstant(v: ErgoLikeContext): Value[SContext.type] = - sys.error(s"Cannot create constant of type Context") /** Approximate data size of the given context without ContextExtension. */ override def dataSize(v: SType#WrappedType): Long = { - sys.error(s"Should not be used, use SizeContext and Sized typeclass instead") + sys.error(s"$this.dataSize($v) is not defined") } override def isConstantSize = false - val tT = STypeVar("T") - val dataInputsMethod = property("dataInputs", SBoxArray, 1) - val headersMethod = property("headers", SHeaderArray, 2) - val preHeaderMethod = property("preHeader", SPreHeader, 3) - val inputsMethod = property("INPUTS", SBoxArray, 4, Inputs) - val outputsMethod = property("OUTPUTS", SBoxArray, 5, Outputs) - val heightMethod = property("HEIGHT", SInt, 6, Height) - val selfMethod = property("SELF", SBox, 7, Self) - val selfBoxIndexMethod = property("selfBoxIndex", SInt, 8) - val lastBlockUtxoRootHashMethod = property("LastBlockUtxoRootHash", SAvlTree, 9, LastBlockUtxoRootHash) - val minerPubKeyMethod = property("minerPubKey", SByteArray, 10, MinerPubkey) - val getVarMethod = SMethod(this, "getVar", SFunc(IndexedSeq(SContext, SByte), SOption(tT), Seq(STypeParam(tT))), 11) + import SType.{tT, paramT} + + lazy val dataInputsMethod = property("dataInputs", SBoxArray, 1) + lazy val headersMethod = property("headers", SHeaderArray, 2) + lazy val preHeaderMethod = property("preHeader", SPreHeader, 3) + lazy val inputsMethod = property("INPUTS", SBoxArray, 4, Inputs) + lazy val outputsMethod = property("OUTPUTS", SBoxArray, 5, Outputs) + lazy val heightMethod = property("HEIGHT", SInt, 6, Height) + lazy val selfMethod = property("SELF", SBox, 7, Self) + lazy val selfBoxIndexMethod = property("selfBoxIndex", SInt, 8) + lazy val lastBlockUtxoRootHashMethod = property("LastBlockUtxoRootHash", SAvlTree, 9, LastBlockUtxoRootHash) + lazy val minerPubKeyMethod = property("minerPubKey", SByteArray, 10, MinerPubkey) + lazy val getVarMethod = SMethod(this, "getVar", SFunc(Array(SContext, SByte), SOption(tT), Array(paramT)), 11) .withInfo(GetVar, "Get context variable with given \\lst{varId} and type.", ArgInfo("varId", "\\lst{Byte} identifier of context variable")) @@ -1685,7 +1660,6 @@ case object SHeader extends SProduct with SPredefType with SMonoType { override type WrappedType = Header override val typeCode: TypeCode = 104: Byte override def typeId = typeCode - override def mkConstant(v: Header): Value[SHeader.type] = HeaderConstant(v) /** Approximate data size of the given context without ContextExtension. */ override def dataSize(v: SType#WrappedType): Long = { @@ -1707,21 +1681,21 @@ case object SHeader extends SProduct with SPredefType with SMonoType { } override def isConstantSize = true - val idMethod = property("id", SByteArray, 1) - val versionMethod = property("version", SByte, 2) - val parentIdMethod = property("parentId", SByteArray, 3) - val ADProofsRootMethod = property("ADProofsRoot", SByteArray, 4) - val stateRootMethod = property("stateRoot", SAvlTree, 5) - val transactionsRootMethod = property("transactionsRoot", SByteArray, 6) - val timestampMethod = property("timestamp", SLong, 7) - val nBitsMethod = property("nBits", SLong, 8) - val heightMethod = property("height", SInt, 9) - val extensionRootMethod = property("extensionRoot", SByteArray, 10) - val minerPkMethod = property("minerPk", SGroupElement, 11) - val powOnetimePkMethod = property("powOnetimePk", SGroupElement, 12) - val powNonceMethod = property("powNonce", SByteArray, 13) - val powDistanceMethod = property("powDistance", SBigInt, 14) - val votesMethod = property("votes", SByteArray, 15) + lazy val idMethod = property("id", SByteArray, 1) + lazy val versionMethod = property("version", SByte, 2) + lazy val parentIdMethod = property("parentId", SByteArray, 3) + lazy val ADProofsRootMethod = property("ADProofsRoot", SByteArray, 4) + lazy val stateRootMethod = property("stateRoot", SAvlTree, 5) + lazy val transactionsRootMethod = property("transactionsRoot", SByteArray, 6) + lazy val timestampMethod = property("timestamp", SLong, 7) + lazy val nBitsMethod = property("nBits", SLong, 8) + lazy val heightMethod = property("height", SInt, 9) + lazy val extensionRootMethod = property("extensionRoot", SByteArray, 10) + lazy val minerPkMethod = property("minerPk", SGroupElement, 11) + lazy val powOnetimePkMethod = property("powOnetimePk", SGroupElement, 12) + lazy val powNonceMethod = property("powNonce", SByteArray, 13) + lazy val powDistanceMethod = property("powDistance", SBigInt, 14) + lazy val votesMethod = property("votes", SByteArray, 15) protected override def getMethods() = super.getMethods() ++ Seq( idMethod, versionMethod, parentIdMethod, ADProofsRootMethod, stateRootMethod, transactionsRootMethod, @@ -1735,7 +1709,6 @@ case object SPreHeader extends SProduct with SPredefType with SMonoType { override type WrappedType = PreHeader override val typeCode: TypeCode = 105: Byte override def typeId = typeCode - override def mkConstant(v: PreHeader): Value[SPreHeader.type] = PreHeaderConstant(v) /** Approximate data size of the given context without ContextExtension. */ override def dataSize(v: SType#WrappedType): Long = { @@ -1749,13 +1722,13 @@ case object SPreHeader extends SProduct with SPredefType with SMonoType { } override def isConstantSize = true - val versionMethod = property("version", SByte, 1) - val parentIdMethod = property("parentId", SByteArray, 2) - val timestampMethod = property("timestamp", SLong, 3) - val nBitsMethod = property("nBits", SLong, 4) - val heightMethod = property("height", SInt, 5) - val minerPkMethod = property("minerPk", SGroupElement, 6) - val votesMethod = property("votes", SByteArray, 7) + lazy val versionMethod = property("version", SByte, 1) + lazy val parentIdMethod = property("parentId", SByteArray, 2) + lazy val timestampMethod = property("timestamp", SLong, 3) + lazy val nBitsMethod = property("nBits", SLong, 4) + lazy val heightMethod = property("height", SInt, 5) + lazy val minerPkMethod = property("minerPk", SGroupElement, 6) + lazy val votesMethod = property("votes", SByteArray, 7) protected override def getMethods() = super.getMethods() ++ Seq( versionMethod, parentIdMethod, timestampMethod, nBitsMethod, heightMethod, minerPkMethod, votesMethod @@ -1781,20 +1754,18 @@ case object SGlobal extends SProduct with SPredefType with SMonoType { override type WrappedType = SigmaDslBuilder override val typeCode: TypeCode = 106: Byte override def typeId = typeCode - override def mkConstant(v: SigmaDslBuilder): Value[SGlobal.type] = { - sys.error(s"Constants of SGlobal type cannot be created.") - } /** Approximate data size of the given context without ContextExtension. */ override def dataSize(v: SType#WrappedType): Long = { - sys.error(s"Should not be used, use SizeContext and Sized typeclass instead") + sys.error(s"$this.dataSize($v) is not defined") } override def isConstantSize = true // only fixed amount of global information is allowed - val tT = STypeVar("T") - val groupGeneratorMethod = SMethod(this, "groupGenerator", SFunc(this, SGroupElement), 1) + import SType.tT + + lazy val groupGeneratorMethod = SMethod(this, "groupGenerator", SFunc(this, SGroupElement), 1) .withIRInfo({ case (builder, obj, method, args, tparamSubst) => GroupGenerator }) .withInfo(GroupGenerator, "") - val xorMethod = SMethod(this, "xor", SFunc(IndexedSeq(this, SByteArray, SByteArray), SByteArray), 2) + lazy val xorMethod = SMethod(this, "xor", SFunc(IndexedSeq(this, SByteArray, SByteArray), SByteArray), 2) .withIRInfo({ case (_, _, _, Seq(l, r), _) => Xor(l.asByteArray, r.asByteArray) }) diff --git a/sigmastate/src/main/scala/sigmastate/utils/SigmaByteReader.scala b/sigmastate/src/main/scala/sigmastate/utils/SigmaByteReader.scala index d133495663..1d679c3b53 100644 --- a/sigmastate/src/main/scala/sigmastate/utils/SigmaByteReader.scala +++ b/sigmastate/src/main/scala/sigmastate/utils/SigmaByteReader.scala @@ -17,9 +17,12 @@ class SigmaByteReader(val r: Reader, if (position > positionLimit) throw new InputSizeLimitExceeded(s"SigmaByteReader position limit $positionLimit is reached at position $position") - - val valDefTypeStore: ValDefTypeStore = new ValDefTypeStore() - + /** The reader should be lightweight to create. In most cases ErgoTrees don't have + * ValDef nodes hence the store is not necessary and it's initialization dominates the + * reader instantiation time. Hence it's lazy. + * @hotspot + */ + lazy val valDefTypeStore: ValDefTypeStore = new ValDefTypeStore() override type CH = r.CH diff --git a/sigmastate/src/main/scala/sigmastate/utils/SigmaByteWriter.scala b/sigmastate/src/main/scala/sigmastate/utils/SigmaByteWriter.scala index c5b34010af..a212b1be31 100644 --- a/sigmastate/src/main/scala/sigmastate/utils/SigmaByteWriter.scala +++ b/sigmastate/src/main/scala/sigmastate/utils/SigmaByteWriter.scala @@ -254,7 +254,7 @@ object SigmaByteWriter { implicit def argInfoToDataInfo[T](arg: ArgInfo)(implicit fmt: FormatDescriptor[T]): DataInfo[T] = DataInfo(arg, fmt) - // TODO remove this conversion and make it explicit + // TODO refactor: remove this conversion and make it explicit /**Helper conversion */ implicit def nameToDataInfo[T](name: String)(implicit fmt: FormatDescriptor[T]): DataInfo[T] = ArgInfo(name, "") } diff --git a/sigmastate/src/main/scala/sigmastate/utxo/CostTable.scala b/sigmastate/src/main/scala/sigmastate/utxo/CostTable.scala index cdb9029073..428f202058 100644 --- a/sigmastate/src/main/scala/sigmastate/utxo/CostTable.scala +++ b/sigmastate/src/main/scala/sigmastate/utxo/CostTable.scala @@ -311,93 +311,6 @@ object CostTable { //Maximum number of expressions in initial(non-reduced script) val MaxExpressions = 300 - object Cost { - val ConstantNode = 1 - - val HeightAccess = 1 - val InputsAccess = 1 - val OutputsAccess = 1 - val SelfAccess = 1 - val VariableAccess = 1 - - val ExtractAmount = 10 - val ExtractScriptBytes = 10 - val ExtractRegister = 10 - - /** The cost for CustomByteArray declaration. Additional cost to be calculated when data is known - (and CustomByteArray being converted to ByteArrayLeaf) */ - val ByteArrayDeclaration = 1 - - val ByteArrayPerKilobyte = 200 - - val BoxPerKilobyte = 50 - - val TripleDeclaration = 3 - - val QuadrupleDeclaration = 4 - - val BooleanConstantDeclaration = 1 - val ByteConstantDeclaration = 1 - val ShortConstantDeclaration = 1 - val IntConstantDeclaration = 1 - val LongConstantDeclaration = 1 - val BigIntConstantDeclaration = 1 - val StringConstantDeclaration = 1 - val GroupElementConstantDeclaration = 10 - val SigmaPropConstantDeclaration = 10 - val BoxConstantDeclaration = 10 - val AvlTreeConstantDeclaration = 50 - - val AndDeclaration = 1 - val AndPerChild = 1 - - val OrDeclaration = 1 - val OrPerChild = 1 - - val BinOrDeclaration = 1 - val BinAndDeclaration = 1 - val BinXorDeclaration = 1 - val IfDeclaration = 1 - - /**PropLeaf declaration cost, wrapped script cost to be added as well.*/ - val AtLeastDeclaration = 1 - val AtLeastPerChild = 1 - - //PropLeaf declaration cost, wrapped script cost to be added as well. - val PropLeafDeclaration = 500 - - /** Cost of Blake256 declaration */ - val Blake256bDeclaration = 20 - - val DlogDeclaration = 10000 - - val TxHasOutputDeclaration = 100 - val TxOutputDeclaration = 100 - - val SizeOfDeclaration = 30 - val ByIndexDeclaration = 50 - val SelectFieldDeclaration = 50 - val SigmaPropIsProvenDeclaration = 50 - val SigmaPropBytesDeclaration = 50 - - val MapDeclaration = 100 - val FilterDeclaration = 200 - val ExistsDeclaration = 200 - val ForAllDeclaration = 200 - val FoldDeclaration = 200 - - val ConcreteCollectionDeclaration = 20 - val TupleDeclaration = 20 - val LambdaDeclaration = 1 - - - val Exponentiate = 5000 - val MultiplyGroup = 50 - - val OptionGet = 1 - val OptionGetOrElse = 1 - val OptionIsDefined = 1 - } } diff --git a/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala b/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala index 39b4b28138..bed8d10335 100644 --- a/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala +++ b/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala @@ -6,11 +6,9 @@ import sigmastate.lang.Terms._ import sigmastate._ import sigmastate.serialization.OpCodes.OpCode import sigmastate.serialization.OpCodes -import sigmastate.utxo.CostTable.Cost -import org.ergoplatform.ErgoBox.{R3, RegisterId} +import org.ergoplatform.ErgoBox.RegisterId import sigmastate.Operations._ -import sigmastate.lang.exceptions.{OptionUnwrapNone, InterpreterException} -import special.sigma.InvalidType +import sigmastate.lang.exceptions.InterpreterException trait Transformer[IV <: SType, OV <: SType] extends NotReadyValue[OV] { @@ -22,7 +20,6 @@ case class MapCollection[IV <: SType, OV <: SType]( mapper: Value[SFunc]) extends Transformer[SCollection[IV], SCollection[OV]] { override def companion = MapCollection - implicit def tOV = mapper.asValue[OV].tpe override val tpe = SCollection[OV](mapper.tpe.tRange.asInstanceOf[OV]) override val opType = SCollection.MapMethod.stype.asFunc } @@ -46,7 +43,7 @@ case class Slice[IV <: SType](input: Value[SCollection[IV]], from: Value[SInt.ty override val tpe = input.tpe override def opType = { val tpeColl = SCollection(input.tpe.typeParams.head.ident) - SFunc(Vector(tpeColl, SInt, SInt), tpeColl) + SFunc(Array(tpeColl, SInt, SInt), tpeColl) } } object Slice extends ValueCompanion { @@ -109,19 +106,11 @@ object Fold extends ValueCompanion { def sum[T <: SNumericType](input: Value[SCollection[T]], varId: Int)(implicit tT: T) = Fold(input, Constant(tT.upcast(0.toByte), tT), - FuncValue(Vector((varId, STuple(tT, tT))), + FuncValue(Array((varId, STuple(tT, tT))), Plus( SelectField(ValUse(varId, STuple(tT, tT)), 1).asNumValue, SelectField(ValUse(varId, STuple(tT, tT)), 2).asNumValue)) ) - - def concat[T <: SType](input: Value[SCollection[SCollection[T]]])(implicit tT: T): Fold[SCollection[T], T] = { - val tColl = SCollection(tT) - Fold[SCollection[T], T](input, - ConcreteCollection(Array[Value[T]](), tT).asValue[T], - FuncValue(Array((1, tColl), (2, tColl)), Append(ValUse(1, tColl), ValUse(2, tColl))) - ) - } } case class ByIndex[V <: SType](input: Value[SCollection[V]], @@ -154,7 +143,7 @@ object SelectField extends ValueCompanion { case class SigmaPropIsProven(input: Value[SSigmaProp.type]) extends Transformer[SSigmaProp.type, SBoolean.type] with NotReadyValueBoolean { override def companion = SigmaPropIsProven - override def opType = SFunc(input.tpe, SBoolean) + override val opType = SFunc(input.tpe, SBoolean) } object SigmaPropIsProven extends ValueCompanion { override def opCode: OpCode = OpCodes.SigmaPropIsProvenCode @@ -176,9 +165,10 @@ trait SimpleTransformerCompanion extends ValueCompanion { case class SizeOf[V <: SType](input: Value[SCollection[V]]) extends Transformer[SCollection[V], SInt.type] with NotReadyValueInt { override def companion = SizeOf - override val opType = SFunc(SCollection(SCollection.tIV), SInt) + override def opType = SizeOf.OpType } object SizeOf extends SimpleTransformerCompanion { + val OpType = SFunc(SCollection(SType.tIV), SInt) override def opCode: OpCode = OpCodes.SizeOfCode override def argInfos: Seq[ArgInfo] = SizeOfInfo.argInfos } @@ -188,45 +178,50 @@ sealed trait Extract[V <: SType] extends Transformer[SBox.type, V] { case class ExtractAmount(input: Value[SBox.type]) extends Extract[SLong.type] with NotReadyValueLong { override def companion = ExtractAmount - override val opType = SFunc(SBox, SLong) + override def opType = ExtractAmount.OpType } object ExtractAmount extends SimpleTransformerCompanion { + val OpType = SFunc(SBox, SLong) override def opCode: OpCode = OpCodes.ExtractAmountCode override def argInfos: Seq[ArgInfo] = ExtractAmountInfo.argInfos } case class ExtractScriptBytes(input: Value[SBox.type]) extends Extract[SByteArray] with NotReadyValueByteArray { override def companion = ExtractScriptBytes - override val opType = SFunc(SBox, SByteArray) + override def opType = ExtractScriptBytes.OpType } object ExtractScriptBytes extends SimpleTransformerCompanion { + val OpType = SFunc(SBox, SByteArray) override def opCode: OpCode = OpCodes.ExtractScriptBytesCode override def argInfos: Seq[ArgInfo] = ExtractScriptBytesInfo.argInfos } case class ExtractBytes(input: Value[SBox.type]) extends Extract[SByteArray] with NotReadyValueByteArray { override def companion = ExtractBytes - override val opType = SFunc(SBox, SByteArray) + override def opType = ExtractBytes.OpType } object ExtractBytes extends SimpleTransformerCompanion { + val OpType = SFunc(SBox, SByteArray) override def opCode: OpCode = OpCodes.ExtractBytesCode override def argInfos: Seq[ArgInfo] = ExtractBytesInfo.argInfos } case class ExtractBytesWithNoRef(input: Value[SBox.type]) extends Extract[SByteArray] with NotReadyValueByteArray { override def companion = ExtractBytesWithNoRef - override val opType = SFunc(SBox, SByteArray) + override def opType = ExtractBytesWithNoRef.OpType } object ExtractBytesWithNoRef extends SimpleTransformerCompanion { + val OpType = SFunc(SBox, SByteArray) override def opCode: OpCode = OpCodes.ExtractBytesWithNoRefCode override def argInfos: Seq[ArgInfo] = ExtractBytesWithNoRefInfo.argInfos } case class ExtractId(input: Value[SBox.type]) extends Extract[SByteArray] with NotReadyValueByteArray { override def companion = ExtractId - override val opType = SFunc(SBox, SByteArray) + override def opType = ExtractId.OpType } object ExtractId extends SimpleTransformerCompanion { + val OpType = SFunc(SBox, SByteArray) override def opCode: OpCode = OpCodes.ExtractIdCode override def argInfos: Seq[ArgInfo] = ExtractIdInfo.argInfos } @@ -236,7 +231,7 @@ case class ExtractRegisterAs[V <: SType]( input: Value[SBox.type], override val tpe: SOption[V]) extends Extract[SOption[V]] with NotReadyValue[SOption[V]] { override def companion = ExtractRegisterAs - override def opType = SFunc(Vector(SBox, SByte), tpe) + override val opType = SFunc(Array(SBox, SByte), tpe) } object ExtractRegisterAs extends ValueCompanion { override def opCode: OpCode = OpCodes.ExtractRegisterAs @@ -274,7 +269,7 @@ trait Deserialize[V <: SType] extends NotReadyValue[V] */ case class DeserializeContext[V <: SType](id: Byte, tpe: V) extends Deserialize[V] { override def companion = DeserializeContext - override val opType = SFunc(Vector(SContext, SByte), tpe) + override val opType = SFunc(Array(SContext, SByte), tpe) } object DeserializeContext extends ValueCompanion { override def opCode: OpCode = OpCodes.DeserializeContextCode @@ -285,7 +280,7 @@ object DeserializeContext extends ValueCompanion { */ case class DeserializeRegister[V <: SType](reg: RegisterId, tpe: V, default: Option[Value[V]] = None) extends Deserialize[V] { override def companion = DeserializeRegister - override val opType = SFunc(Vector(SBox, SByte, SOption(tpe)), tpe) + override val opType = SFunc(Array(SBox, SByte, SOption(tpe)), tpe) } object DeserializeRegister extends ValueCompanion { override def opCode: OpCode = OpCodes.DeserializeRegisterCode @@ -293,7 +288,7 @@ object DeserializeRegister extends ValueCompanion { case class GetVar[V <: SType](varId: Byte, override val tpe: SOption[V]) extends NotReadyValue[SOption[V]] { override def companion = GetVar - override val opType = SFunc(Vector(SContext, SByte), tpe) + override val opType = SFunc(Array(SContext, SByte), tpe) } object GetVar extends ValueCompanion { override def opCode: OpCode = OpCodes.GetVarCode diff --git a/sigmastate/src/test/scala/org/ergoplatform/ErgoAddressSpecification.scala b/sigmastate/src/test/scala/org/ergoplatform/ErgoAddressSpecification.scala index b7ae2b050a..4358b35f20 100644 --- a/sigmastate/src/test/scala/org/ergoplatform/ErgoAddressSpecification.scala +++ b/sigmastate/src/test/scala/org/ergoplatform/ErgoAddressSpecification.scala @@ -2,36 +2,35 @@ package org.ergoplatform import java.math.BigInteger -import org.ergoplatform.ErgoAddressEncoder.{MainnetNetworkPrefix, TestnetNetworkPrefix} -import org.scalatest.prop.PropertyChecks -import org.scalatest.{PropSpec, Assertion, Matchers, TryValues} +import org.ergoplatform.ErgoAddressEncoder.{hash256, MainnetNetworkPrefix, TestnetNetworkPrefix} +import org.ergoplatform.validation.{ValidationException, ValidationRules} +import org.scalatest.{Assertion, TryValues} import sigmastate.basics.DLogProtocol import sigmastate.basics.DLogProtocol.DLogProverInput import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer import sigmastate.serialization.ValueSerializer -import sigmastate.serialization.generators.ObjectGenerators -import org.ergoplatform.ErgoScriptPredef._ -import org.ergoplatform.validation.ValidationSpecification -import sigmastate.{AvlTreeData, SType} -import sigmastate.Values.{EvaluatedValue, SigmaPropConstant, ByteArrayConstant, IntConstant, ErgoTree} +import scorex.util.encode.Base58 +import sigmastate.{SigmaAnd, SType} +import sigmastate.Values.{UnparsedErgoTree, Constant, ByteArrayConstant, IntConstant, ErgoTree} import sigmastate.eval.IRContext import sigmastate.helpers._ -import sigmastate.interpreter.{ContextExtension, Interpreter} +import sigmastate.helpers.TestingHelpers._ +import sigmastate.interpreter.ContextExtension import sigmastate.interpreter.Interpreter.{ScriptNameProp, ScriptEnv} import sigmastate.lang.Terms.ValueOps import sigmastate.utils.Helpers._ +import special.sigma.SigmaDslTesting + +class ErgoAddressSpecification extends SigmaDslTesting with TryValues { -class ErgoAddressSpecification extends PropSpec - with ObjectGenerators - with PropertyChecks - with Matchers - with TryValues - with SigmaTestingCommons { private implicit val ergoAddressEncoder: ErgoAddressEncoder = new ErgoAddressEncoder(TestnetNetworkPrefix) def addressRoundtrip(addr: ErgoAddress): Assertion = { - ergoAddressEncoder.fromString(ergoAddressEncoder.toString(addr)).get shouldBe addr + val addrStr = ergoAddressEncoder.toString(addr) + val parsedAddr = ergoAddressEncoder.fromString(addrStr).get + parsedAddr shouldBe addr + ergoAddressEncoder.toString(parsedAddr) shouldBe addrStr } property("P2PK roundtrip") { @@ -72,14 +71,102 @@ class ErgoAddressSpecification extends PropSpec property("fromProposition() should properly distinct all types of addresses from script AST") { val pk: DLogProtocol.ProveDlog = DLogProverInput(BigInteger.ONE).publicImage + val pk10: DLogProtocol.ProveDlog = DLogProverInput(BigInteger.TEN).publicImage - val p2s: Pay2SAddress = Pay2SAddress(TrueProp) + val p2s: Pay2SAddress = Pay2SAddress(ErgoTree.fromProposition(SigmaAnd(pk, pk10))) val p2sh: Pay2SHAddress = Pay2SHAddress(pk) val p2pk: P2PKAddress = P2PKAddress(pk) - ergoAddressEncoder.fromProposition(p2s.script).success.value shouldBe p2s - ergoAddressEncoder.fromProposition(p2sh.script).success.value shouldBe p2sh - ergoAddressEncoder.fromProposition(p2pk.script).success.value.isInstanceOf[P2PKAddress] shouldBe true + p2s.toString shouldBe "JryiCXrZf5VDetH1PM7rKDX3q4sLF34AdErWJFqG87Hf5ikTDf636b35Nr7goWMdRUKA3ZPxdeqFNbQzBjhnDR9SUMYwDX1tdV8ZuGgXwQPaRVcB9" + p2sh.toString shouldBe "qETVgcEctaXurNbFRgGUcZEGg4EKa8R4a5UNHY7" + p2pk.toString shouldBe "3WwXpssaZwcNzaGMv3AgxBdTPJQBt5gCmqBsg3DykQ39bYdhJBsN" + + assertResult(true)(p2s != p2sh && p2sh != p2s) + assertResult(true)(p2sh != p2pk && p2pk != p2sh) + assertResult(true)(p2pk != p2s && p2s != p2pk) + + val parsed_p2s = ergoAddressEncoder.fromProposition(p2s.script).success.value + assertResult(false)(parsed_p2s eq p2s) + assertResult(true)(parsed_p2s == p2s && p2s == parsed_p2s) + parsed_p2s.hashCode() shouldBe p2s.hashCode() + + val parsed_p2sh = ergoAddressEncoder.fromProposition(p2sh.script).success.value + assertResult(false)(parsed_p2sh eq p2sh) + assertResult(true)(parsed_p2sh == p2sh && p2sh == parsed_p2sh) + parsed_p2sh.hashCode() shouldBe p2sh.hashCode() + + val parsed_p2pk = ergoAddressEncoder.fromProposition(p2pk.script).success.value + assertResult(false)(parsed_p2pk eq p2pk) + assertResult(true)(parsed_p2pk == p2pk && p2pk == parsed_p2pk) + parsed_p2pk.hashCode() shouldBe p2pk.hashCode() + + val tree = ErgoTree.fromProposition(pk) + val p2s_2: Pay2SAddress = Pay2SAddress(tree) // address created via P2S constructor method + assertResult(true)(p2s_2 != p2pk && p2pk != p2s_2) + + // not equal to created via encoder (how "good" is that!) + val from_tree = ergoAddressEncoder.fromProposition(tree).success.value + assertResult(true)(from_tree != p2s_2) + assertResult(true)(from_tree == p2pk) + } + + property("decode with wrong address") { + assertExceptionThrown( + ergoAddressEncoder.fromString( + "JryiCXrZf5VDetH1PM7rKDX3q4sLF34AdErWJFqG87Hf5ikTDf636b35Nr7goWMdRUKA3ZPxdeqFNbQzBjhnDR9SUMYwDX1tdV8ZuGgXwQPaRVcB8") + .getOrThrow, + t => t.getMessage.contains("Checksum check fails") + ) + + { + val invalid_p2sh = new Pay2SHAddress(Array[Byte](1, 2, 3)) + val addrStr = ergoAddressEncoder.toString(invalid_p2sh) + + assertExceptionThrown( + ergoAddressEncoder.fromString(addrStr).getOrThrow, + t => t.getMessage.contains("Improper content in P2SH script: 41fKjb7zWNw") + ) + } + + { + val pk: DLogProtocol.ProveDlog = DLogProverInput(BigInteger.ONE).publicImage + val p2pk = P2PKAddress(pk)(ergoAddressEncoder) + + val invalidAddrType = 4.toByte + val withNetworkByte = (ergoAddressEncoder.networkPrefix + invalidAddrType).toByte +: p2pk.contentBytes + + val checksum = hash256(withNetworkByte).take(ErgoAddressEncoder.ChecksumLength) + val invalidAddrStr = Base58.encode(withNetworkByte ++ checksum) + + assertExceptionThrown( + ergoAddressEncoder.fromString(invalidAddrStr).getOrThrow, + t => t.getMessage.contains("Unsupported address type: 4") + ) + } + + { + val unparsedTree = new ErgoTree( + ErgoTree.ConstantSegregationHeader, + Array[Constant[SType]](), + Left(UnparsedErgoTree(Array[Byte](), ValidationException("", ValidationRules.CheckTypeCode, Seq()))) + ) + assertExceptionThrown( + ergoAddressEncoder.fromProposition(unparsedTree).getOrThrow, + t => t.getMessage.contains("Cannot create ErgoAddress form unparsed ergo tree") + ) + } + + { + val invalidTree = new ErgoTree( + ErgoTree.ConstantSegregationHeader, + Array[Constant[SType]](), + Right(IntConstant(10).asSigmaProp) + ) + assertExceptionThrown( + ergoAddressEncoder.fromProposition(invalidTree).getOrThrow, + t => t.getMessage.contains("Cannot create ErgoAddress form proposition") + ) + } } property("decode with wrong network prefix") { @@ -96,7 +183,7 @@ class ErgoAddressSpecification extends PropSpec def testPay2SHAddress(address: Pay2SHAddress, scriptBytes: Array[Byte])(implicit IR: IRContext) = { val scriptId = 1.toByte - val boxToSpend = ErgoBox(10, address.script, creationHeight = 5) + val boxToSpend = testBox(10, address.script, creationHeight = 5) val ctx = ErgoLikeContextTesting.dummy(boxToSpend) .withExtension(ContextExtension(Seq( scriptId -> ByteArrayConstant(scriptBytes) // provide script bytes in context variable diff --git a/sigmastate/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala b/sigmastate/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala index 79036122d0..5b4f0e7f26 100644 --- a/sigmastate/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala +++ b/sigmastate/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala @@ -1,23 +1,122 @@ package org.ergoplatform import org.ergoplatform.ErgoBox.TokenId -import org.scalatest.prop.GeneratorDrivenPropertyChecks -import org.scalatest.{Matchers, PropSpec} -import scorex.util.Random -import sigmastate.Values.ByteArrayConstant -import sigmastate.helpers.SigmaTestingCommons -import sigmastate.interpreter.{ContextExtension, ProverResult} +import org.ergoplatform.settings.ErgoAlgos +import scorex.crypto.hash.Digest32 +import scorex.util.{Random, ModifierId} +import sigmastate.SCollection.SByteArray +import sigmastate.{SSigmaProp, SPair, SInt, TrivialProp, SType} +import sigmastate.Values.{LongConstant, FalseLeaf, ConstantNode, SigmaPropConstant, ConstantPlaceholder, TrueSigmaProp, ByteArrayConstant, ErgoTree} +import sigmastate.interpreter.{ProverResult, ContextExtension} import sigmastate.serialization.SigmaSerializer -import sigmastate.serialization.generators.ObjectGenerators import sigmastate.eval._ import sigmastate.eval.Extensions._ import sigmastate.SType._ +import sigmastate.utils.Helpers +import special.sigma.SigmaDslTesting -class ErgoLikeTransactionSpec extends PropSpec - with GeneratorDrivenPropertyChecks - with Matchers - with ObjectGenerators - with SigmaTestingCommons { +class ErgoLikeTransactionSpec extends SigmaDslTesting { + + property("ErgoBox test vectors") { + val token1 = "6e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f0001" + val token2 = "a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600" + val b1 = new ErgoBoxCandidate( + 10L, + ErgoTree(ErgoTree.DefaultHeader, Vector(), TrueSigmaProp), + 100, + Coll( + Digest32 @@ (ErgoAlgos.decodeUnsafe(token1)) -> 10000000L, + Digest32 @@ (ErgoAlgos.decodeUnsafe(token2)) -> 500L + ) + ) + val b1_clone = new ErgoBoxCandidate( + 10L, + ErgoTree(ErgoTree.DefaultHeader, Vector(), TrueSigmaProp), + 100, + Coll( + Digest32 @@ (ErgoAlgos.decodeUnsafe(token1)) -> 10000000L, + Digest32 @@ (ErgoAlgos.decodeUnsafe(token2)) -> 500L + ) + ) + val b2 = new ErgoBoxCandidate( + 10L, + ErgoTree(ErgoTree.ConstantSegregationHeader, Vector(TrueSigmaProp), ConstantPlaceholder(0, SSigmaProp)), + 100, + Coll( + Digest32 @@ (ErgoAlgos.decodeUnsafe(token1)) -> 10000000L, + Digest32 @@ (ErgoAlgos.decodeUnsafe(token2)) -> 500L + ) + ) + val b3 = new ErgoBox( + 10L, + ErgoTree(ErgoTree.DefaultHeader, Vector(), TrueSigmaProp), + Coll( + Digest32 @@ (ErgoAlgos.decodeUnsafe(token1)) -> 10000000L, + Digest32 @@ (ErgoAlgos.decodeUnsafe(token2)) -> 500L + ), + Map( + ErgoBox.R5 -> ByteArrayConstant(Helpers.decodeBytes("7fc87f7f01ff")), + ErgoBox.R4 -> FalseLeaf + ), + ModifierId @@ ("218301ae8000018008637f0021fb9e00018055486f0b514121016a00ff718080"), + 0.toShort, + 100 + ) + val expectedTokens = Map[ModifierId, Long](ModifierId @@ token1 -> 10000000L, ModifierId @@ token2 -> 500L) + val assetHolder = ErgoBoxAssetsHolder(10L, expectedTokens) + + b1.tokens shouldBe expectedTokens + b1_clone.tokens shouldBe expectedTokens + b3.tokens shouldBe expectedTokens + + assertResult(true)(b1.hashCode() == b1.hashCode()) + assertResult(true)(b1 == b1) + assertResult(true)(b1 != assetHolder) + + assertResult(true)(b1.hashCode() == b1_clone.hashCode()) + assertResult(true)(b1 == b1_clone) + + assertResult(true)(b1.hashCode() != b3.hashCode() || b1 != b3) + assertResult(true)(b1 != b3) + assertResult(true)(b3 != b1) + + val b4 = b3.toCandidate + assertResult(true)(b3 != b4) + assertResult(true)(b4 == b3) // asymmetic !!! + assertResult(true)(b3.hashCode() != b4.hashCode()) + + b1.get(ErgoBox.R0).get shouldBe LongConstant(10) + b1.get(ErgoBox.R1).get shouldBe ByteArrayConstant(Helpers.decodeBytes("0008d3")) + + { // test case for R2 + val res = b1.get(ErgoBox.R2).get + val exp = Coll( + ErgoAlgos.decodeUnsafe(token1).toColl -> 10000000L, + ErgoAlgos.decodeUnsafe(token2).toColl -> 500L + ).map(identity).toConstant + // TODO HF: fix collections equality and remove map(identity) + // (PairOfColl should be equal CollOverArray but now it is not) + res shouldBe exp + } + + { // test case for R3 + val res = b1.get(ErgoBox.R3).get + res shouldBe ConstantNode[SType]( + (100, Helpers.decodeBytes("00000000000000000000000000000000000000000000000000000000000000000000")).asWrappedType, + SPair(SInt, SByteArray) + ) + } + + { // test case for R5 + val res = b3.get(ErgoBox.R5).get + res shouldBe ByteArrayConstant(Helpers.decodeBytes("7fc87f7f01ff")) + } + + { // for proposition + b1.proposition shouldBe SigmaPropConstant(CSigmaProp(TrivialProp.TrueProp)) + b2.proposition shouldBe SigmaPropConstant(CSigmaProp(TrivialProp.TrueProp)) + } + } property("ErgoLikeTransaction: Serializer round trip") { forAll(MinSuccessful(50)) { t: ErgoLikeTransaction => roundTripTest(t)(ErgoLikeTransaction.serializer) } @@ -104,6 +203,16 @@ class ErgoLikeTransactionSpec extends PropSpec val itx8 = new ErgoLikeTransaction(headInput8 +: tailInputs, di, txIn.outputCandidates) (itx8.messageToSign sameElements initialMessage) shouldBe true + // ProverResult equality checks + val copiedResult = new ProverResult(newProof7.proof, newProof7.extension) + assertResult(true)(copiedResult == newProof7) + assertResult(true, "hash codes")(copiedResult.hashCode() == newProof7.hashCode()) + assertResult(true, "different types")(newProof7 != headInput7) + assertResult(true, "different extensions")(newProof8 != newProof7) + val tmpResult = new ProverResult(Array[Byte](), newProof7.extension) + assertResult(true, "different proofs")(tmpResult != newProof7) + assertResult(true)(newProof8.hashCode() != newProof7.hashCode() || newProof8 != newProof7) + /** * Check data inputs modifications */ diff --git a/sigmastate/src/test/scala/org/ergoplatform/ErgoScriptPredefSpec.scala b/sigmastate/src/test/scala/org/ergoplatform/ErgoScriptPredefSpec.scala index c58455ba76..f0f9b17495 100644 --- a/sigmastate/src/test/scala/org/ergoplatform/ErgoScriptPredefSpec.scala +++ b/sigmastate/src/test/scala/org/ergoplatform/ErgoScriptPredefSpec.scala @@ -5,17 +5,18 @@ import org.ergoplatform.ErgoBox.R4 import org.ergoplatform.mining.emission.EmissionRules import org.ergoplatform.settings.MonetarySettings import org.scalacheck.Gen -import scorex.crypto.hash.{Blake2b256, Digest32} +import scorex.crypto.hash.{Digest32, Blake2b256} import scorex.util.Random -import sigmastate.Values.{ByteArrayConstant, CollectionConstant, ErgoTree, IntConstant, SigmaPropConstant} +import sigmastate.Values.{SigmaPropConstant, CollectionConstant, ByteArrayConstant, IntConstant, ErgoTree} import sigmastate._ -import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog} -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.basics.DLogProtocol.{ProveDlog, DLogProverInput} +import sigmastate.helpers.{ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons, ContextEnrichingTestProvingInterpreter} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.{ScriptNameProp, emptyEnv} -import sigmastate.interpreter.{ContextExtension, ProverResult} +import sigmastate.interpreter.{ProverResult, ContextExtension} import sigmastate.lang.Terms.ValueOps import sigmastate.serialization.ValueSerializer -import sigmastate.utxo.{ByIndex, CostTable, ExtractCreationInfo, SelectField} +import sigmastate.utxo.{CostTable, ExtractCreationInfo, ByIndex, SelectField} import scalan.util.BenchmarkUtil._ import ErgoScriptPredef._ import sigmastate.utils.Helpers._ @@ -42,7 +43,7 @@ class ErgoScriptPredefSpec extends SigmaTestingCommons { val prop = EQ(Height, ErgoScriptPredef.boxCreationHeight(ByIndex(Outputs, IntConstant(0)))).toSigmaProp val propInlined = EQ(Height, SelectField(ExtractCreationInfo(ByIndex(Outputs, IntConstant(0))), 1).asIntValue).toSigmaProp prop shouldBe propInlined - val inputBox = ErgoBox(1, prop, nextHeight, Seq(), Map()) + val inputBox = testBox(1, prop, nextHeight, Seq(), Map()) val inputBoxes = IndexedSeq(inputBox) val inputs = inputBoxes.map(b => Input(b.id, emptyProverResult)) val minerBox = new ErgoBoxCandidate(1, SigmaPropConstant(minerProp), nextHeight) @@ -102,10 +103,10 @@ class ErgoScriptPredefSpec extends SigmaTestingCommons { newProp: ErgoTree, inputR4Val: CollectionConstant[SByte.type]): Try[Unit] = Try { val outputR4Val: CollectionConstant[SByte.type] = ByteArrayConstant(Random.randomBytes()) - val inputBoxes = IndexedSeq(ErgoBox(emission.foundersCoinsTotal, prop, 0, Seq(), Map(R4 -> inputR4Val))) + val inputBoxes = IndexedSeq(testBox(emission.foundersCoinsTotal, prop, 0, Seq(), Map(R4 -> inputR4Val))) val inputs = inputBoxes.map(b => Input(b.id, emptyProverResult)) - val newFoundersBox = ErgoBox(remainingAmount, newProp, 0, Seq(), Map(R4 -> outputR4Val)) - val collectedBox = ErgoBox(inputBoxes.head.value - remainingAmount, TrueProp, 0) + val newFoundersBox = testBox(remainingAmount, newProp, 0, Seq(), Map(R4 -> outputR4Val)) + val collectedBox = testBox(inputBoxes.head.value - remainingAmount, TrueProp, 0) val spendingTransaction = ErgoLikeTransaction(inputs, IndexedSeq(newFoundersBox, collectedBox)) val ctx = ErgoLikeContextTesting( currentHeight = height, @@ -124,9 +125,9 @@ class ErgoScriptPredefSpec extends SigmaTestingCommons { val minerPk = prover.dlogSecrets.head.publicImage val prop = ErgoScriptPredef.rewardOutputScript(settings.minerRewardDelay, minerPk) val verifier = new ErgoLikeTestInterpreter - val inputBoxes = IndexedSeq(ErgoBox(20, prop, 0, Seq(), Map())) + val inputBoxes = IndexedSeq(testBox(20, prop, 0, Seq(), Map())) val inputs = inputBoxes.map(b => Input(b.id, emptyProverResult)) - val spendingTransaction = ErgoLikeTransaction(inputs, IndexedSeq(ErgoBox(inputBoxes.head.value, TrueProp, 0))) + val spendingTransaction = ErgoLikeTransaction(inputs, IndexedSeq(testBox(inputBoxes.head.value, TrueProp, 0))) val ctx = ErgoLikeContextTesting( currentHeight = inputBoxes.head.creationHeight + settings.minerRewardDelay, @@ -157,7 +158,7 @@ class ErgoScriptPredefSpec extends SigmaTestingCommons { val prover = new ContextEnrichingTestProvingInterpreter val minerPk = prover.dlogSecrets.head.publicImage val prop = ErgoScriptPredef.emissionBoxProp(settings) - val emissionBox = ErgoBox(emission.coinsTotal, prop, 0, Seq(), Map()) + val emissionBox = testBox(emission.coinsTotal, prop, 0, Seq(), Map()) val minerProp = ErgoScriptPredef.rewardOutputScript(settings.minerRewardDelay, minerPk) // collect coins during the fixed rate period @@ -215,7 +216,7 @@ class ErgoScriptPredefSpec extends SigmaTestingCommons { def check(inputBoxes: IndexedSeq[ErgoBox]): Try[Unit] = Try { val inputs = inputBoxes.map(b => Input(b.id, emptyProverResult)) val amount = inputBoxes.map(_.value).sum - val spendingTransaction = ErgoLikeTransaction(inputs, IndexedSeq(ErgoBox(amount, pubkey.toSigmaProp, 0))) + val spendingTransaction = ErgoLikeTransaction(inputs, IndexedSeq(testBox(amount, pubkey.toSigmaProp, 0))) val ctx = ErgoLikeContextTesting( currentHeight = 50, @@ -233,37 +234,37 @@ class ErgoScriptPredefSpec extends SigmaTestingCommons { measure(10) { i => // transaction with the only input with enough token should pass val inputs0 = IndexedSeq( - ErgoBox(20, prop, 0, Seq((wrongId, tokenAmount), (tokenId, tokenAmount), (wrongId2, tokenAmount)), Map()) + testBox(20, prop, 0, Seq((wrongId, tokenAmount), (tokenId, tokenAmount), (wrongId2, tokenAmount)), Map()) ) check(inputs0).get shouldBe (()) // transaction with the only input with insufficient token should fail val inputs1 = IndexedSeq( - ErgoBox(20, prop, 0, Seq((wrongId, tokenAmount), (tokenId, tokenAmount - 1)), Map()) + testBox(20, prop, 0, Seq((wrongId, tokenAmount), (tokenId, tokenAmount - 1)), Map()) ) check(inputs1) shouldBe 'failure // transaction with multiple inputs with insufficient token should fail val inputs2 = IndexedSeq( - ErgoBox(20, prop, 0, Seq((wrongId, tokenAmount), (tokenId, tokenAmount - 2)), Map()), - ErgoBox(20, prop, 0, Seq((wrongId, tokenAmount)), Map()), - ErgoBox(20, prop, 0, Seq((tokenId, 1), (wrongId2, tokenAmount)), Map()) + testBox(20, prop, 0, Seq((wrongId, tokenAmount), (tokenId, tokenAmount - 2)), Map()), + testBox(20, prop, 0, Seq((wrongId, tokenAmount)), Map()), + testBox(20, prop, 0, Seq((tokenId, 1), (wrongId2, tokenAmount)), Map()) ) check(inputs2) shouldBe 'failure // transaction with multiple inputs with enough token should pass val inputs3 = IndexedSeq( - ErgoBox(20, prop, 0, Seq((wrongId, 1), (tokenId, tokenAmount / 2)), Map()), - ErgoBox(20, prop, 0, Seq((wrongId, 1)), Map()), - ErgoBox(20, prop, 0, Seq((tokenId, tokenAmount / 2 + 1), (wrongId2, 1)), Map()) + testBox(20, prop, 0, Seq((wrongId, 1), (tokenId, tokenAmount / 2)), Map()), + testBox(20, prop, 0, Seq((wrongId, 1)), Map()), + testBox(20, prop, 0, Seq((tokenId, tokenAmount / 2 + 1), (wrongId2, 1)), Map()) ) check(inputs3).getOrThrow // A transaction which contains input with no tokens val inputs4 = IndexedSeq( - ErgoBox(20, prop, 0, Seq((wrongId, 1), (tokenId, tokenAmount / 2)), Map()), - ErgoBox(20, prop, 0, Seq(), Map()), - ErgoBox(20, prop, 0, Seq((tokenId, tokenAmount / 2 + 1), (wrongId2, 1)), Map()) + testBox(20, prop, 0, Seq((wrongId, 1), (tokenId, tokenAmount / 2)), Map()), + testBox(20, prop, 0, Seq(), Map()), + testBox(20, prop, 0, Seq((tokenId, tokenAmount / 2 + 1), (wrongId2, 1)), Map()) ) check(inputs4) shouldBe 'success } diff --git a/sigmastate/src/test/scala/org/ergoplatform/dsl/TestContractSpec.scala b/sigmastate/src/test/scala/org/ergoplatform/dsl/TestContractSpec.scala index 14aec73804..1467acf92c 100644 --- a/sigmastate/src/test/scala/org/ergoplatform/dsl/TestContractSpec.scala +++ b/sigmastate/src/test/scala/org/ergoplatform/dsl/TestContractSpec.scala @@ -3,24 +3,23 @@ package org.ergoplatform.dsl import sigmastate.interpreter.Interpreter.ScriptNameProp import scala.collection.mutable -import sigmastate.interpreter.{ContextExtension, CostedProverResult, ProverResult} +import sigmastate.interpreter.{ProverResult, CostedProverResult} import scala.collection.mutable.ArrayBuffer import org.ergoplatform.ErgoBox.NonMandatoryRegisterId -import org.ergoplatform.SigmaConstants.ScriptCostLimit import scalan.Nullable import scorex.crypto.hash.Digest32 import scala.util.Try -import org.ergoplatform.{ErgoBox, ErgoLikeContext} -import org.ergoplatform.dsl.ContractSyntax.{ErgoScript, Proposition, Token, TokenId} -import org.ergoplatform.validation.ValidationRules +import org.ergoplatform.{ErgoLikeContext, ErgoBox} +import org.ergoplatform.dsl.ContractSyntax.{Token, TokenId, ErgoScript, Proposition} import sigmastate.{AvlTreeData, SType} import sigmastate.Values.{ErgoTree, EvaluatedValue} -import sigmastate.eval.{CSigmaProp, Evaluation, IRContext} -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.eval.{IRContext, CSigmaProp, Evaluation} +import sigmastate.helpers.{ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons, ContextEnrichingTestProvingInterpreter} +import sigmastate.helpers.TestingHelpers._ import sigmastate.lang.Terms.ValueOps -import special.sigma.{AnyValue, SigmaProp, TestValue} +import special.sigma.{AnyValue, TestValue, SigmaProp} case class TestContractSpec(testSuite: SigmaTestingCommons)(implicit val IR: IRContext) extends ContractSpec { @@ -84,7 +83,7 @@ case class TestContractSpec(testSuite: SigmaTestingCommons)(implicit val IR: IRC minerPubkey = ErgoLikeContextTesting.dummyPubkey, dataBoxes = dataBoxes, boxesToSpend = boxesToSpend, - spendingTransaction = testSuite.createTransaction(dataBoxes, tx.outputs.map(_.ergoBox).toIndexedSeq), + spendingTransaction = createTransaction(dataBoxes, tx.outputs.map(_.ergoBox).toIndexedSeq), selfIndex = boxesToSpend.indexOf(utxoBox.ergoBox) ) ctx } @@ -119,7 +118,7 @@ case class TestContractSpec(testSuite: SigmaTestingCommons)(implicit val IR: IRC private[dsl] lazy val ergoBox: ErgoBox = { val tokens = _tokens.map { t => (Digest32 @@ t.id.toArray, t.value) } - ErgoBox(value, propSpec.ergoTree, tx.block.height, tokens, _regs) + testBox(value, propSpec.ergoTree, tx.block.height, tokens, _regs) } def id = ergoBox.id } diff --git a/sigmastate/src/test/scala/sigmastate/CostingSpecification.scala b/sigmastate/src/test/scala/sigmastate/CostingSpecification.scala index 6715e3cd1c..aa944390cc 100644 --- a/sigmastate/src/test/scala/sigmastate/CostingSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/CostingSpecification.scala @@ -3,20 +3,21 @@ package sigmastate import org.ergoplatform.SigmaConstants.ScriptCostLimit import org.ergoplatform.ErgoScriptPredef.TrueProp import org.ergoplatform.validation.ValidationRules -import org.ergoplatform.{ErgoBox, ErgoLikeContext, ErgoLikeTransaction} +import org.ergoplatform.{ErgoLikeContext, ErgoBox} import scorex.crypto.authds.avltree.batch.Lookup import scorex.crypto.authds.{ADDigest, ADKey} import scorex.crypto.hash.Blake2b256 -import sigmastate.Values.{AvlTreeConstant, BigIntConstant, BooleanConstant, ByteArrayConstant, ConstantPlaceholder, ErgoTree, IntConstant, TrueLeaf} +import sigmastate.Values.{TrueLeaf, BigIntConstant, AvlTreeConstant, ConstantPlaceholder, ByteArrayConstant, IntConstant, ErgoTree, BooleanConstant} import sigmastate.eval.Extensions._ import sigmastate.eval.Sized._ import sigmastate.eval._ +import sigmastate.helpers.TestingHelpers._ import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeTestInterpreter} import sigmastate.interpreter.ContextExtension -import sigmastate.interpreter.Interpreter.{ScriptEnv, ScriptNameProp, emptyEnv} +import sigmastate.interpreter.Interpreter.{ScriptNameProp, ScriptEnv, emptyEnv} import sigmastate.utxo.CostTable import sigmastate.utxo.CostTable._ -import special.sigma.{AvlTree, SigmaTestingData} +import special.sigma.{SigmaTestingData, AvlTree} class CostingSpecification extends SigmaTestingData { implicit lazy val IR = new TestingIRContext { @@ -58,8 +59,8 @@ class CostingSpecification extends SigmaTestingData { Map(ErgoBox.R4 -> ByteArrayConstant(Array[Byte](1, 2, 3)), ErgoBox.R5 -> IntConstant(3), ErgoBox.R6 -> AvlTreeConstant(avlTree))) - lazy val outBoxA = ErgoBox(10, pkA, 0) - lazy val outBoxB = ErgoBox(20, pkB, 0) + lazy val outBoxA = testBox(10, pkA, 0) + lazy val outBoxB = testBox(20, pkB, 0) lazy val tx = createTransaction(IndexedSeq(dataBox), IndexedSeq(outBoxA, outBoxB)) lazy val context = new ErgoLikeContext( diff --git a/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala index ec69ff8d39..50158c74b2 100644 --- a/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -1,13 +1,109 @@ package sigmastate -import org.ergoplatform.validation.ValidationException +import org.ergoplatform.settings.ErgoAlgos +import org.ergoplatform.validation.{ValidationException, ValidationRules} +import scalan.Nullable +import sigmastate.Values._ +import sigmastate.lang.SourceContext import special.sigma.SigmaTestingData +import sigmastate.lang.Terms._ +import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer /** Regression tests with ErgoTree related test vectors. * This test vectors verify various constants which are consensus critical and should not change. */ class ErgoTreeSpecification extends SigmaTestingData { + property("Value.sourceContext") { + val srcCtx = SourceContext.fromParserIndex(0, "") + val value = IntConstant(10) + value.sourceContext shouldBe Nullable.None + + value.withSrcCtx(Nullable(srcCtx)) + value.sourceContext shouldBe Nullable(srcCtx) + + assertExceptionThrown( + value.withSrcCtx(Nullable(srcCtx)), + t => t.isInstanceOf[RuntimeException] && t.getMessage.contains("can be set only once")) + } + + property("Value.opType") { + Seq( + Block(Seq(), null), + ValNode("x", SInt, null), + ApplyTypes(null, Seq()), + TaggedVariableNode(1, SByte), + ValDef(1, null), + ValUse(1, SInt), + BlockValue(IndexedSeq(), null) + ).foreach { node => + assertExceptionThrown(node.opType, t => t.getMessage.contains("is not supported for node")) + } + FuncValue(Vector((1, SInt)), ValUse(1, SInt)).opType shouldBe + SFunc(Vector(), SFunc(SInt, SInt)) + } + + property("ErgoTree.toProposition") { + val t1 = new ErgoTree( + 16.toByte, + Array(IntConstant(1)), + Right(BoolToSigmaProp(EQ(ConstantPlaceholder(0, SInt), IntConstant(1)))) + ) + + val t = new ErgoTree( + 16.toByte, + Array(IntConstant(1)), + Left(UnparsedErgoTree(t1.bytes, ValidationException("", ValidationRules.CheckTypeCode, Seq()))) + ) + assertExceptionThrown( + t.toProposition(true), + t => t.isInstanceOf[ValidationException] + ) + } + + property("ErgoTree.template") { + val t = new ErgoTree( + 16.toByte, + Array(IntConstant(1)), + Right(BoolToSigmaProp(EQ(ConstantPlaceholder(0, SInt), IntConstant(1)))) + ) + t.template shouldBe ErgoAlgos.decodeUnsafe("d19373000402") + } + + property("ErgoTree.bytes") { + val t = new ErgoTree( + 16.toByte, + Array(IntConstant(1)), + Right(BoolToSigmaProp(EQ(ConstantPlaceholder(0, SInt), IntConstant(1)))) + ) + val expectedBytes = DefaultSerializer.serializeErgoTree(t) + t._bytes shouldBe expectedBytes + } + + property("ErgoTree equality") { + val t1 = new ErgoTree( + 16.toByte, + Array(IntConstant(1)), + Right(BoolToSigmaProp(EQ(ConstantPlaceholder(0, SInt), IntConstant(1)))) + ) + val t2 = new ErgoTree(16.toByte, Array(IntConstant(1)), Right(TrueSigmaProp)) + val t3 = new ErgoTree(16.toByte, Array(IntConstant(1)), Right(TrueSigmaProp)) + val t4 = new ErgoTree(16.toByte, Vector(), Right(TrueSigmaProp)) + val t5 = new ErgoTree(ErgoTree.DefaultHeader, Vector(), Right(TrueSigmaProp)) + assert(t1 != t2) + assert(t2 == t3) + assert(t3 != t4) + assert(t4 != t5) + assert(t5 != t1) + } + + property("ConstantNode equality") { + assert(IntConstant(10) == IntConstant(10)) + assert(ShortConstant(10) == ShortConstant(10)) + assert(IntConstant(10) != IntConstant(11)) + assert(IntConstant(10) != ShortConstant(10)) + } + val typeCodes = Table( ("constant", "expectedValue"), (SPrimType.LastPrimTypeCode, 8), @@ -51,6 +147,17 @@ class ErgoTreeSpecification extends SigmaTestingData { t.typeCode should be <= SPrimType.LastPrimTypeCode } } + forAll(Table(("type", "isConstantSize"), + (NoType, true), + (SString, false), + (SAny, false), + (SUnit, true), + (SFunc(SInt, SAny), false), + (STypeApply("T"), false), + (SType.tT, false) + )) { (t, isConst) => + t.isConstantSize shouldBe isConst + } } /** Expected parameters of resolved method (see `methods` table below). diff --git a/sigmastate/src/test/scala/sigmastate/FailingToProveSpec.scala b/sigmastate/src/test/scala/sigmastate/FailingToProveSpec.scala index ac846b2b6c..ed166795f3 100644 --- a/sigmastate/src/test/scala/sigmastate/FailingToProveSpec.scala +++ b/sigmastate/src/test/scala/sigmastate/FailingToProveSpec.scala @@ -1,9 +1,7 @@ package sigmastate -import org.ergoplatform.{ErgoBox, ErgoLikeContext} -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeTestInterpreter, SigmaTestingCommons} -import org.ergoplatform.{ErgoBox, ErgoLikeContext, ErgoLikeInterpreter, ErgoLikeTransaction} import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.lang.Terms._ import org.scalatest.TryValues._ import sigmastate.interpreter.Interpreter.{ScriptNameProp, emptyEnv} @@ -34,9 +32,9 @@ class FailingToProveSpec extends SigmaTestingCommons { | } """.stripMargin).asBoolValue.toSigmaProp - val selfBox = ErgoBox(200L, compiledScript, 0) - val o1 = ErgoBox(101L, TrueProp, 5001) - val o2 = ErgoBox(99L, TrueProp, 5001) + val selfBox = testBox(200L, compiledScript, 0) + val o1 = testBox(101L, TrueProp, 5001) + val o2 = testBox(99L, TrueProp, 5001) val tx = createTransaction(IndexedSeq(o1, o2)) val ctx = ErgoLikeContextTesting( currentHeight = 5001, @@ -67,10 +65,10 @@ class FailingToProveSpec extends SigmaTestingCommons { | } """.stripMargin).asBoolValue.toSigmaProp - val selfBox = ErgoBox(200L, compiledScript, 0) - val o1 = ErgoBox(102L, TrueProp, 5001) - val o2 = ErgoBox(98L, TrueProp, 5001) - val o3 = ErgoBox(100L, TrueProp, 5001) + val selfBox = testBox(200L, compiledScript, 0) + val o1 = testBox(102L, TrueProp, 5001) + val o2 = testBox(98L, TrueProp, 5001) + val o3 = testBox(100L, TrueProp, 5001) val tx = createTransaction(IndexedSeq(o1, o2, o3)) val ctx = ErgoLikeContextTesting( currentHeight = 5001, diff --git a/sigmastate/src/test/scala/sigmastate/SigmaProtocolSpecification.scala b/sigmastate/src/test/scala/sigmastate/SigmaProtocolSpecification.scala new file mode 100644 index 0000000000..b5381c2780 --- /dev/null +++ b/sigmastate/src/test/scala/sigmastate/SigmaProtocolSpecification.scala @@ -0,0 +1,27 @@ +package sigmastate + +import gf2t.{GF2_192_Poly, GF2_192} +import sigmastate.basics.VerifierMessage.Challenge +import special.sigma.SigmaTestingData + +class SigmaProtocolSpecification extends SigmaTestingData { + + property("CThresholdUncheckedNode equality") { + val c1 = Challenge @@ Array[Byte](1) + val c2 = Challenge @@ Array[Byte](2) + val n0 = CThresholdUncheckedNode(c1, Seq(), 0, None) + val n1 = CThresholdUncheckedNode(c1, Seq(), 0, None) + val n2 = CThresholdUncheckedNode(c2, Seq(), 0, None) + val n3 = CThresholdUncheckedNode(c2, Seq(n1), 0, None) + val n4 = CThresholdUncheckedNode(c2, Seq(n1), 1, None) + val p = GF2_192_Poly.interpolate(new Array[Byte](0), new Array[GF2_192](0), new GF2_192(0)) + val n5 = CThresholdUncheckedNode(c2, Seq(n1), 1, Some(p)) + + assertResult(true)(n0 == n1) + assertResult(true)(n1 != n2) + assertResult(true)(n2 != n3) + assertResult(true)(n3 != n4) + assertResult(true)(n4 != n5) + } + +} diff --git a/sigmastate/src/test/scala/sigmastate/SoftForkabilitySpecification.scala b/sigmastate/src/test/scala/sigmastate/SoftForkabilitySpecification.scala index 95c7fedf70..2cfeaa45fa 100644 --- a/sigmastate/src/test/scala/sigmastate/SoftForkabilitySpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/SoftForkabilitySpecification.scala @@ -8,6 +8,7 @@ import sigmastate.Values.ErgoTree.EmptyConstants import sigmastate.Values.{UnparsedErgoTree, NotReadyValueInt, ByteArrayConstant, Tuple, IntConstant, ErgoTree, ValueCompanion} import sigmastate.eval.Colls import sigmastate.helpers.{ErgoLikeContextTesting, ErgoLikeTestInterpreter, ErgoLikeTestProvingInterpreter} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.{ScriptNameProp, emptyEnv} import sigmastate.interpreter.{ProverResult, ContextExtension} import sigmastate.lang.Terms._ diff --git a/sigmastate/src/test/scala/sigmastate/TestingInterpreterSpecification.scala b/sigmastate/src/test/scala/sigmastate/TestingInterpreterSpecification.scala index 9a2091a0cb..0a14afc166 100644 --- a/sigmastate/src/test/scala/sigmastate/TestingInterpreterSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/TestingInterpreterSpecification.scala @@ -10,6 +10,7 @@ import sigmastate.lang.Terms._ import org.ergoplatform._ import scorex.util.encode.Base58 import sigmastate.helpers.{ErgoLikeContextTesting, ErgoLikeTestInterpreter, ErgoLikeTestProvingInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.serialization.ValueSerializer import TrivialProp._ import sigmastate.utils.Helpers._ @@ -115,7 +116,7 @@ class TestingInterpreterSpecification extends SigmaTestingCommons { "dk2" -> dk2, "bytes1" -> Array[Byte](1, 2, 3), "bytes2" -> Array[Byte](4, 5, 6), - "box1" -> ErgoBox(10, ErgoScriptPredef.TrueProp, 0, Seq(), Map( + "box1" -> testBox(10, ErgoScriptPredef.TrueProp, 0, Seq(), Map( reg1 -> IntArrayConstant(Array[Int](1, 2, 3)), reg2 -> BoolArrayConstant(Array[Boolean](true, false, true))))) val prop = compile(env, code).asBoolValue.toSigmaProp diff --git a/sigmastate/src/test/scala/sigmastate/TypesSpecification.scala b/sigmastate/src/test/scala/sigmastate/TypesSpecification.scala new file mode 100644 index 0000000000..af72d49781 --- /dev/null +++ b/sigmastate/src/test/scala/sigmastate/TypesSpecification.scala @@ -0,0 +1,85 @@ +package sigmastate + +import java.math.BigInteger + +import org.ergoplatform.ErgoBox +import org.ergoplatform.ErgoScriptPredef.TrueProp +import org.ergoplatform.settings.ErgoAlgos +import scalan.RType +import scorex.crypto.hash.Digest32 +import sigmastate.basics.DLogProtocol +import sigmastate.interpreter.CryptoConstants +import special.sigma.SigmaTestingData +import sigmastate.SType.AnyOps +import sigmastate.Values.{ShortConstant, LongConstant, BigIntConstant, AvlTreeConstant, IntConstant, ByteConstant} +import sigmastate.eval.SigmaDsl +import special.collection.Coll +import special.sigma.BigInt +import sigmastate.helpers.TestingHelpers._ + +class TypesSpecification extends SigmaTestingData { + + def Coll[T](items: T*)(implicit cT: RType[T]) = SigmaDsl.Colls.fromItems(items:_*) + + def check(tpe: SType, v: Any, exp: Long) = + tpe.dataSize(v.asWrappedType) shouldBe exp + + def checkColl[T](elemTpe: SType, arr: Coll[T], exp: Long) = + check(sigmastate.SCollection(elemTpe), arr, exp) + + property("SType.dataSize") { + check(SUnit, (), 1) + check(SBoolean, true, 1) + check(SByte, 1.toByte, 1) + check(SShort, 1.toShort, 2) + check(SInt, 1, 4) + check(SLong, 1, 8) + check(SString, "abc", 3) + check(SBigInt, BigInteger.ZERO, 32L) + check(SBigInt, BigInteger.ONE, 32L) + check(SBigInt, BigInteger.valueOf(Long.MaxValue), 32L) + check(SBigInt, { val i = BigInteger.valueOf(Long.MaxValue); i.multiply(i) }, 32L) + forAll { n: BigInt => + check(SBigInt, n, 32L) + + } + val g = CryptoConstants.dlogGroup.generator + check(SGroupElement, g, 33L) + check(SSigmaProp, DLogProtocol.ProveDlog(g), 1024L) + check(SAvlTree, null, 44L) + check(sigmastate.SOption(SInt), Some(10), 1L + 4) + checkColl(SInt, Coll[Int](10,20), 2L * 4) + checkColl(SInt, Coll[Int](), 0L) + checkColl(SBigInt, Coll[BigInt](SigmaDsl.BigInt(BigInteger.ZERO), SigmaDsl.BigInt(BigInteger.valueOf(Long.MaxValue))), 2 * 32L) + check(STuple(SInt, STuple(SInt, SInt)), Array(10, Array[Any](20, 30)), 2 + 4 + (2 + 4 + 4)) + + val (tree, _) = createAvlTreeAndProver() + + val box = SigmaDsl.Box(testBox(20, TrueProp, 0, + Seq( + (Digest32 @@ (ErgoAlgos.decodeUnsafe("6e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f0001")), + 10000000L) + ), + Map( + ErgoBox.R4 -> ByteConstant(1.toByte), + ErgoBox.R5 -> ShortConstant(1024.toShort), + ErgoBox.R6 -> IntConstant(1024 * 1024), + ErgoBox.R7 -> LongConstant(1024.toLong), + ErgoBox.R8 -> BigIntConstant(222L), + ErgoBox.R9 -> AvlTreeConstant(tree) + ))) + + check(SBox, box, 4096L) + } + + property("SType.dataSize (not defined)") { + val assertError = (t: Throwable) => t.getMessage.contains("is not defined") + assertExceptionThrown(check(NoType, null, 0), assertError) + assertExceptionThrown(check(SAny, null, 0), assertError) + assertExceptionThrown(check(STypeApply("T"), null, 0), assertError) + assertExceptionThrown(check(SType.tT, null, 0), assertError) + assertExceptionThrown(check(SGlobal, null, 0), assertError) + assertExceptionThrown(check(SContext, null, 0), assertError) + } + +} diff --git a/sigmastate/src/test/scala/sigmastate/eval/CostingTest.scala b/sigmastate/src/test/scala/sigmastate/eval/CostingTest.scala index 91904896a2..5146a612bb 100644 --- a/sigmastate/src/test/scala/sigmastate/eval/CostingTest.scala +++ b/sigmastate/src/test/scala/sigmastate/eval/CostingTest.scala @@ -1,24 +1,13 @@ package sigmastate.eval -import java.math.BigInteger - -import com.google.common.base.Strings import org.bouncycastle.math.ec.ECPoint import sigmastate._ -import sigmastate.Values.{ConstantPlaceholder, _} +import sigmastate.Values._ import sigmastate.helpers.ContextEnrichingTestProvingInterpreter -import sigmastate.interpreter.CryptoConstants -import sigmastate.lang.{LangTests, TransformingSigmaBuilder, SigmaCompiler} -import sigmastate.utxo.CostTable.Cost -import sigmastate.utxo.{SigmaPropBytes, ExtractCreationInfo, SizeOf, SelectField} -import SType._ -import org.ergoplatform.{Height, Self, MinerPubkey} +import sigmastate.lang.LangTests import scalan.util.BenchmarkUtil._ import scalan.BaseCtxTests -import sigmastate.SCollection.SByteArray import sigmastate.basics.DLogProtocol -import sigmastate.basics.DLogProtocol.ProveDlog -import sigmastate.lang.Terms.ValueOps import sigmastate.serialization.OpCodes class CostingTest extends BaseCtxTests with LangTests with ExampleContracts with ErgoScriptTestkit { cake => @@ -28,36 +17,11 @@ class CostingTest extends BaseCtxTests with LangTests with ExampleContracts with import IR._ import GroupElement._ import BigInt._ - import Context._; import SigmaContract._ - import Cost._; import CollBuilder._; import Coll._; import Box._; import SigmaProp._; - import SigmaDslBuilder._; import WOption._ + import Context._ + import CollBuilder._; import Coll._; import Box._; import SigmaProp._; + import SigmaDslBuilder._ import Liftables._ - ignore("SType.dataSize") { - def check(tpe: SType, v: Any, exp: Long) = - tpe.dataSize(v.asWrappedType) shouldBe exp - - check(SBoolean, true, 1) - check(SByte, 1.toByte, 1) - check(SShort, 1.toShort, 2) - check(SInt, 1, 4) - check(SLong, 1, 8) - check(SString, "abc", 3) - check(SBigInt, BigInteger.ZERO, SBigInt.MaxSizeInBytes) - check(SBigInt, BigInteger.ONE, SBigInt.MaxSizeInBytes) - check(SBigInt, BigInteger.valueOf(Long.MaxValue), SBigInt.MaxSizeInBytes) - check(SBigInt, { val i = BigInteger.valueOf(Long.MaxValue); i.multiply(i) }, SBigInt.MaxSizeInBytes) - val g = CryptoConstants.dlogGroup.generator - check(SGroupElement, g, CryptoConstants.groupSize) - check(SSigmaProp, DLogProtocol.ProveDlog(g), CryptoConstants.groupSize + 1) - check(sigmastate.SOption(SInt), Some(10), 1 + 4) - def checkColl(elemTpe: SType, arr: Array[Any], exp: Long) = - check(sigmastate.SCollection(SInt), arr, exp) - checkColl(SInt, Array(10,20), 2 + 2L * 4) - checkColl(SInt, Array(), 2) - checkColl(SBigInt, Array(BigInteger.ZERO, BigInteger.valueOf(Long.MaxValue)), 2 + 0 + 8) - check(STuple(SInt, STuple(SInt, SInt)), Array(10, Array[Any](20, 30)), 2 + 4 + (2 + 4 + 4)) - } ignore("constants") { // check("int", "1", _ => 1, _ => constCost[Int], _ => sizeOf(1)) diff --git a/sigmastate/src/test/scala/sigmastate/eval/ErgoScriptTestkit.scala b/sigmastate/src/test/scala/sigmastate/eval/ErgoScriptTestkit.scala index 04ce2f5eb5..2abaf1c128 100644 --- a/sigmastate/src/test/scala/sigmastate/eval/ErgoScriptTestkit.scala +++ b/sigmastate/src/test/scala/sigmastate/eval/ErgoScriptTestkit.scala @@ -4,19 +4,17 @@ import org.ergoplatform.ErgoAddressEncoder.TestnetNetworkPrefix import org.ergoplatform.validation.ValidationSpecification import scala.util.Success -import sigmastate.{SInt, AvlTreeData, SLong, SType} -import sigmastate.Values.{LongConstant, Constant, EvaluatedValue, SValue, TrueLeaf, SigmaPropConstant, Value, IntConstant, ErgoTree, BigIntArrayConstant} +import sigmastate.{AvlTreeData, SType} +import sigmastate.Values.{EvaluatedValue, SValue, SigmaPropConstant, Value, BigIntArrayConstant} import org.ergoplatform.{Context => _, _} import sigmastate.utxo.CostTable import scalan.BaseCtxTests import sigmastate.lang.{LangTests, SigmaCompiler} -import sigmastate.helpers.ContextEnrichingTestProvingInterpreter -import sigmastate.helpers.ErgoLikeContextTesting +import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.ContextExtension import sigmastate.interpreter.Interpreter.ScriptEnv -import sigmastate.serialization.ErgoTreeSerializer import special.sigma.{ContractsTestkit, Context => DContext, _} -import sigmastate.eval.Extensions._ import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer import scala.language.implicitConversions @@ -65,16 +63,11 @@ trait ErgoScriptTestkit extends ContractsTestkit with LangTests lazy val projectProver = new ContextEnrichingTestProvingInterpreter lazy val backerPubKey = backerProver.dlogSecrets.head.publicImage lazy val projectPubKey = projectProver.dlogSecrets.head.publicImage - lazy val ctxVars = contextVars(Map( - backerPubKeyId -> backerPubKey.toAnyValue, - projectPubKeyId -> projectPubKey.toAnyValue, - 3.toByte -> toAnyValue(bigIntegerArr1) - )).toArray - val boxToSpend = ErgoBox(10, ErgoScriptPredef.TrueProp, 0, + val boxToSpend = testBox(10, ErgoScriptPredef.TrueProp, 0, additionalRegisters = Map(ErgoBox.R4 -> BigIntArrayConstant(bigIntegerArr1))) - lazy val tx1Output1 = ErgoBox(minToRaise, projectPubKey, 0) - lazy val tx1Output2 = ErgoBox(1, projectPubKey, 0) + lazy val tx1Output1 = testBox(minToRaise, projectPubKey, 0) + lazy val tx1Output2 = testBox(1, projectPubKey, 0) lazy val tx1 = new ErgoLikeTransaction(IndexedSeq(), IndexedSeq(), IndexedSeq(tx1Output1, tx1Output2)) lazy val ergoCtx = ErgoLikeContextTesting( currentHeight = timeout - 1, diff --git a/sigmastate/src/test/scala/sigmastate/eval/EvaluationTest.scala b/sigmastate/src/test/scala/sigmastate/eval/EvaluationTest.scala index 32ff2d3d29..6e794bb7cc 100644 --- a/sigmastate/src/test/scala/sigmastate/eval/EvaluationTest.scala +++ b/sigmastate/src/test/scala/sigmastate/eval/EvaluationTest.scala @@ -3,6 +3,7 @@ package sigmastate.eval import org.ergoplatform.ErgoBox import sigmastate.Values.{ConcreteCollection, IntArrayConstant, IntConstant, SigmaPropConstant, SigmaPropValue} import sigmastate.helpers.ContextEnrichingTestProvingInterpreter +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter._ import scalan.BaseCtxTests import sigmastate.lang.LangTests @@ -37,7 +38,7 @@ class EvaluationTest extends BaseCtxTests test("lazy logical ops") { val prover = new ContextEnrichingTestProvingInterpreter val pk = prover.dlogSecrets.head.publicImage - val self = ErgoBox(1, pk, 0, additionalRegisters = Map(ErgoBox.R4 -> IntConstant(10))) + val self = testBox(1, pk, 0, additionalRegisters = Map(ErgoBox.R4 -> IntConstant(10))) val ctx = newErgoContext(height = 1, self) // guarded register access: existing reg reduce(emptyEnv, "lazy1", "SELF.R4[Int].isDefined && SELF.R4[Int].get == 10", ctx, true) diff --git a/sigmastate/src/test/scala/sigmastate/helpers/ErgoLikeContextTesting.scala b/sigmastate/src/test/scala/sigmastate/helpers/ErgoLikeContextTesting.scala index 257fda7902..97f355331c 100644 --- a/sigmastate/src/test/scala/sigmastate/helpers/ErgoLikeContextTesting.scala +++ b/sigmastate/src/test/scala/sigmastate/helpers/ErgoLikeContextTesting.scala @@ -42,7 +42,7 @@ object ErgoLikeContextTesting { spendingTransaction: ErgoLikeTransactionTemplate[_ <: UnsignedInput], self: ErgoBox, extension: ContextExtension = ContextExtension.empty, - vs: SigmaValidationSettings = ValidationRules.currentSettings) = + vs: SigmaValidationSettings = ValidationRules.currentSettings): ErgoLikeContext = new ErgoLikeContext(lastBlockUtxoRoot, noHeaders, dummyPreHeader(currentHeight, minerPubkey), noBoxes, boxesToSpend, spendingTransaction, boxesToSpend.indexOf(self), extension, vs, ScriptCostLimit.value, 0L) @@ -57,7 +57,7 @@ object ErgoLikeContextTesting { dataBoxes, boxesToSpend, spendingTransaction, selfIndex, ContextExtension.empty, ValidationRules.currentSettings, ScriptCostLimit.value, 0L) - def dummy(selfDesc: ErgoBox) = ErgoLikeContextTesting(currentHeight = 0, + def dummy(selfDesc: ErgoBox): ErgoLikeContext = ErgoLikeContextTesting(currentHeight = 0, lastBlockUtxoRoot = AvlTreeData.dummy, dummyPubkey, boxesToSpend = IndexedSeq(selfDesc), spendingTransaction = ErgoLikeTransaction(IndexedSeq(), IndexedSeq()), self = selfDesc) diff --git a/sigmastate/src/test/scala/sigmastate/helpers/ErgoLikeTestProvingInterpreter.scala b/sigmastate/src/test/scala/sigmastate/helpers/ErgoLikeTestProvingInterpreter.scala index f4984b9fbe..ce147d1850 100644 --- a/sigmastate/src/test/scala/sigmastate/helpers/ErgoLikeTestProvingInterpreter.scala +++ b/sigmastate/src/test/scala/sigmastate/helpers/ErgoLikeTestProvingInterpreter.scala @@ -1,10 +1,10 @@ package sigmastate.helpers +import sigmastate.UnprovenConjecture import sigmastate.basics.DLogProtocol.DLogProverInput import sigmastate.basics.{DiffieHellmanTupleProverInput, SigmaProtocolPrivateInput} import sigmastate.eval.IRContext import sigmastate.interpreter.ProverInterpreter -import sigmastate.utxo.CostTable class ErgoLikeTestProvingInterpreter(implicit override val IR: IRContext) extends ErgoLikeTestInterpreter with ProverInterpreter { @@ -20,4 +20,6 @@ class ErgoLikeTestProvingInterpreter(implicit override val IR: IRContext) lazy val dhSecrets: Seq[DiffieHellmanTupleProverInput] = secrets.filter(_.isInstanceOf[DiffieHellmanTupleProverInput]).asInstanceOf[Seq[DiffieHellmanTupleProverInput]] + // expose for testing + override def setPositions(uc: UnprovenConjecture): UnprovenConjecture = super.setPositions(uc) } diff --git a/sigmastate/src/test/scala/sigmastate/helpers/NegativeTesting.scala b/sigmastate/src/test/scala/sigmastate/helpers/NegativeTesting.scala new file mode 100644 index 0000000000..82f1a11f10 --- /dev/null +++ b/sigmastate/src/test/scala/sigmastate/helpers/NegativeTesting.scala @@ -0,0 +1,36 @@ +package sigmastate.helpers + +import org.scalatest.Matchers + +import scala.annotation.tailrec + +trait NegativeTesting extends Matchers { + + /** Checks that a [[Throwable]] is thrown and satisfies the given predicate. + * @param fun block of code to execute + * @param assertion expected assertion on the thrown exception + * @param clue added to the error message + */ + def assertExceptionThrown(fun: => Any, assertion: Throwable => Boolean, clue: => String = ""): Unit = { + try { + fun + fail("exception is expected") + } + catch { + case e: Throwable => + if (!assertion(e)) + fail( + s"""exception check failed on $e (root cause: ${rootCause(e)}) + |clue: $clue + |trace: + |${e.getStackTrace.mkString("\n")}}""".stripMargin) + } + } + + /** Returns the root cause of the chain of exceptions. */ + @tailrec + final def rootCause(t: Throwable): Throwable = + if (t.getCause == null) t + else rootCause(t.getCause) + +} diff --git a/sigmastate/src/test/scala/sigmastate/helpers/SigmaPPrint.scala b/sigmastate/src/test/scala/sigmastate/helpers/SigmaPPrint.scala index 03721c684c..582c31f9ae 100644 --- a/sigmastate/src/test/scala/sigmastate/helpers/SigmaPPrint.scala +++ b/sigmastate/src/test/scala/sigmastate/helpers/SigmaPPrint.scala @@ -83,6 +83,8 @@ object SigmaPPrint extends PPrinter { private val exceptionHandlers: PartialFunction[Any, Tree] = { case ex: Exception => Tree.Apply(s"new ${ex.getClass.getSimpleName}", treeifySeq(Seq(ex.getMessage))) + case ex: Error => + Tree.Apply(s"new ${ex.getClass.getSimpleName}", treeifySeq(Seq(ex.getMessage))) } /** Generated Scala code which creates the given byte array from a hex string literal. */ @@ -132,6 +134,9 @@ object SigmaPPrint extends PPrinter { val elemTpe = coll.tItem.name Tree.Apply(s"Coll[$elemTpe]", treeifySeq(coll.toArray)) + case tp: TrivialProp => + Tree.Literal(s"TrivialProp.${if (tp.condition) "True" else "False"}Prop") + case t: AvlTreeData => Tree.Apply("AvlTreeData", treeifyMany( Tree.Apply("ADDigest @@ ", treeifyMany(t.digest)), @@ -188,12 +193,19 @@ object SigmaPPrint extends PPrinter { case ConstantNode(v, SCollectionType(elemType)) if elemType.isInstanceOf[SPredefType] => Tree.Apply(tpeName(elemType) + "ArrayConstant", treeifySeq(Seq(v))) + case ConstantNode(true, SBoolean) => + Tree.Literal("TrueLeaf") + + case ConstantNode(false, SBoolean) => + Tree.Literal("FalseLeaf") + case c: ConstantNode[_] if c.tpe.isInstanceOf[SPredefType] => Tree.Apply(tpeName(c.tpe) + "Constant", treeifySeq(Seq(c.value))) case ArithOp(l, r, code) => val args = treeifySeq(Seq(l, r)).toSeq :+ Tree.Apply("OpCode @@ ", treeifySeq(Seq(code))) Tree.Apply("ArithOp", args.iterator) + case mc @ MethodCall(obj, method, args, typeSubst) => val objType = apply(method.objType).plainText val methodTemplate = method.objType.getMethodByName(method.name) diff --git a/sigmastate/src/test/scala/sigmastate/helpers/SigmaPPrintSpec.scala b/sigmastate/src/test/scala/sigmastate/helpers/SigmaPPrintSpec.scala index d629d379a1..11fc160c0e 100644 --- a/sigmastate/src/test/scala/sigmastate/helpers/SigmaPPrintSpec.scala +++ b/sigmastate/src/test/scala/sigmastate/helpers/SigmaPPrintSpec.scala @@ -147,6 +147,8 @@ class SigmaPPrintSpec extends SigmaDslTesting { SelectField.typed[Value[SByte.type]](ValUse(1, STuple(Vector(SByte, SByte))), 1.toByte), "SelectField.typed[Value[SByte.type]](ValUse(1, SPair(SByte, SByte)), 1.toByte)" ) + test(TrueLeaf, "TrueLeaf") + test(FalseLeaf, "FalseLeaf") test(IntConstant(10), "IntConstant(10)") test(ArithOp(IntConstant(1), IntConstant(1), OpCodes.PlusCode), "ArithOp(IntConstant(1), IntConstant(1), OpCode @@ (-102.toByte))") test( diff --git a/sigmastate/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala b/sigmastate/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala index ef94329e31..e41d3cedad 100644 --- a/sigmastate/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala +++ b/sigmastate/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala @@ -1,8 +1,8 @@ package sigmastate.helpers import org.ergoplatform.ErgoAddressEncoder.TestnetNetworkPrefix -import org.ergoplatform.ErgoBox.NonMandatoryRegisterId import org.ergoplatform.ErgoScriptPredef.TrueProp +import org.ergoplatform.SigmaConstants.ScriptCostLimit import org.ergoplatform._ import org.ergoplatform.validation.ValidationRules.{CheckCostFunc, CheckCalcFunc} import org.ergoplatform.validation.ValidationSpecification @@ -11,9 +11,9 @@ import org.scalacheck.Gen import org.scalatest.prop.{PropertyChecks, GeneratorDrivenPropertyChecks} import org.scalatest.{PropSpec, Assertion, Matchers} import scalan.{TestUtils, TestContexts, RType} -import scorex.crypto.hash.{Digest32, Blake2b256} +import scorex.crypto.hash.Blake2b256 import sigma.types.IsPrimView -import sigmastate.Values.{Constant, EvaluatedValue, SValue, Value, ErgoTree, GroupElementConstant} +import sigmastate.Values.{Constant, EvaluatedValue, SValue, Value, GroupElementConstant} import sigmastate.interpreter.Interpreter.{ScriptNameProp, ScriptEnv} import sigmastate.interpreter.{CryptoConstants, Interpreter} import sigmastate.lang.{Terms, TransformingSigmaBuilder, SigmaCompiler} @@ -21,15 +21,17 @@ import sigmastate.serialization.{ValueSerializer, SigmaSerializer} import sigmastate.{SGroupElement, SType} import sigmastate.eval.{CompiletimeCosting, IRContext, Evaluation, _} import sigmastate.interpreter.CryptoConstants.EcPointType +import sigmastate.utils.Helpers._ +import sigmastate.helpers.TestingHelpers._ import special.sigma -import scala.annotation.tailrec import scala.language.implicitConversions trait SigmaTestingCommons extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks - with Matchers with TestUtils with TestContexts with ValidationSpecification { + with Matchers with TestUtils with TestContexts with ValidationSpecification + with NegativeTesting { val fakeSelf: ErgoBox = createBox(0, TrueProp) @@ -60,33 +62,6 @@ trait SigmaTestingCommons extends PropSpec tree } - def createBox(value: Long, - proposition: ErgoTree, - additionalTokens: Seq[(Digest32, Long)] = Seq(), - additionalRegisters: Map[NonMandatoryRegisterId, _ <: EvaluatedValue[_ <: SType]] = Map()) - = ErgoBox(value, proposition, 0, additionalTokens, additionalRegisters) - - def createBox(value: Long, - proposition: ErgoTree, - creationHeight: Int) - = ErgoBox(value, proposition, creationHeight, Seq(), Map(), ErgoBox.allZerosModifierId) - - /** - * Create fake transaction with provided outputCandidates, but without inputs and data inputs. - * Normally, this transaction will be invalid as far as it will break rule that sum of - * coins in inputs should not be less then sum of coins in outputs, but we're not checking it - * in our test cases - */ - def createTransaction(outputCandidates: IndexedSeq[ErgoBoxCandidate]): ErgoLikeTransaction = { - new ErgoLikeTransaction(IndexedSeq(), IndexedSeq(), outputCandidates) - } - - def createTransaction(box: ErgoBoxCandidate): ErgoLikeTransaction = createTransaction(IndexedSeq(box)) - - def createTransaction(dataInputs: IndexedSeq[ErgoBox], - outputCandidates: IndexedSeq[ErgoBoxCandidate]): ErgoLikeTransaction = - new ErgoLikeTransaction(IndexedSeq(), dataInputs.map(b => DataInput(b.id)), outputCandidates) - class TestingIRContext extends TestContext with IRContext with CompiletimeCosting { override def onCostingResult[T](env: ScriptEnv, tree: SValue, res: RCostingResultEx[T]): Unit = { env.get(ScriptNameProp) match { @@ -133,9 +108,9 @@ trait SigmaTestingCommons extends PropSpec } case class CompiledFunc[A,B] - (script: String, bindings: Seq[(Byte, EvaluatedValue[_ <: SType])], expr: SValue, func: A => B) - (implicit val tA: RType[A], val tB: RType[B]) extends Function1[A, B] { - override def apply(x: A): B = func(x) + (script: String, bindings: Seq[(Byte, EvaluatedValue[_ <: SType])], expr: SValue, func: A => (B, Int)) + (implicit val tA: RType[A], val tB: RType[B]) extends Function1[A, (B, Int)] { + override def apply(x: A): (B, Int) = func(x) } /** The same operations are executed as part of Interpreter.verify() */ @@ -163,9 +138,7 @@ trait SigmaTestingCommons extends PropSpec import IR._ import IR.Context._; val tA = RType[A] - val tB = RType[B] val tpeA = Evaluation.rtypeToSType(tA) - val tpeB = Evaluation.rtypeToSType(tB) val code = s"""{ | val func = $funcScript @@ -188,7 +161,7 @@ trait SigmaTestingCommons extends PropSpec } // The following is done as part of Interpreter.verify() - val valueFun = { + val (costF, valueFun) = { val costingRes = getCostingResult(env, compiledTree) val calcF = costingRes.calcF val tree = IR.buildTree(calcF) @@ -198,13 +171,14 @@ trait SigmaTestingCommons extends PropSpec val lA = Liftables.asLiftable[SContext, IR.Context](calcF.elem.eDom.liftable) val lB = Liftables.asLiftable[Any, Any](calcF.elem.eRange.liftable) - IR.compile[SContext, Any, IR.Context, Any](IR.getDataEnv, calcF)(lA, lB) + val vf = IR.compile[SContext, Any, IR.Context, Any](IR.getDataEnv, calcF)(lA, lB) + (costingRes.costF, vf) } val f = (in: A) => { implicit val cA = tA.classTag val x = fromPrimView(in) - val sigmaCtx = in match { + val (costingCtx, sigmaCtx) = in match { case ctx: CostingDataContext => // the context is passed as function argument (this is for testing only) // This is to overcome non-functional semantics of context operations @@ -224,41 +198,27 @@ trait SigmaTestingCommons extends PropSpec } else { ctx.vars.updated(1, ctxVar) } - ctx.copy(vars = newVars) + val calcCtx = ctx.copy(vars = newVars) + val costCtx = calcCtx.copy(isCost = true) + (costCtx, calcCtx) case _ => val ergoCtx = ErgoLikeContextTesting.dummy(createBox(0, TrueProp)) .withBindings(1.toByte -> Constant[SType](x.asInstanceOf[SType#WrappedType], tpeA)) .withBindings(bindings: _*) - ergoCtx.toSigmaContext(IR, isCost = false) + val calcCtx = ergoCtx.toSigmaContext(IR, isCost = false).asInstanceOf[CostingDataContext] + val costCtx = calcCtx.copy(isCost = true) + (costCtx, calcCtx) } + + val estimatedCost = IR.checkCostWithContext(costingCtx, costF, ScriptCostLimit.value, 0L).getOrThrow + val (res, _) = valueFun(sigmaCtx) - res.asInstanceOf[B] + (res.asInstanceOf[B], estimatedCost) } val Terms.Apply(funcVal, _) = compiledTree.asInstanceOf[SValue] CompiledFunc(funcScript, bindings.toSeq, funcVal, f) } - def assertExceptionThrown(fun: => Any, assertion: Throwable => Boolean, clue: => String = ""): Unit = { - try { - fun - fail("exception is expected") - } - catch { - case e: Throwable => - if (!assertion(e)) - fail( - s"""exception check failed on $e (root cause: ${rootCause(e)}) - |clue: $clue - |trace: - |${e.getStackTrace.mkString("\n")}}""".stripMargin) - } - } - - @tailrec - final def rootCause(t: Throwable): Throwable = - if (t.getCause == null) t - else rootCause(t.getCause) - protected def roundTripTest[T](v: T)(implicit serializer: SigmaSerializer[T, T]): Assertion = { // using default sigma reader/writer val bytes = serializer.toBytes(v) diff --git a/sigmastate/src/test/scala/sigmastate/helpers/TestingHelpers.scala b/sigmastate/src/test/scala/sigmastate/helpers/TestingHelpers.scala new file mode 100644 index 0000000000..f6dd44919a --- /dev/null +++ b/sigmastate/src/test/scala/sigmastate/helpers/TestingHelpers.scala @@ -0,0 +1,76 @@ +package sigmastate.helpers + +import scorex.crypto.hash.Digest32 +import special.collection.Coll +import scorex.util.ModifierId +import org.ergoplatform.{DataInput, ErgoLikeTransaction, ErgoBox, ErgoBoxCandidate} +import sigmastate.Values.ErgoTree +import org.ergoplatform.ErgoBox.{AdditionalRegisters, allZerosModifierId, TokenId} +import sigmastate.eval.CostingSigmaDslBuilder +import sigmastate.eval._ + +// TODO refactor: unification is required between two hierarchies of tests +// and as part of it, more methods can be moved to TestingHelpers + +/** A collection of helper methods which can be used across test suites. */ +object TestingHelpers { + + def testBox(value: Long, + ergoTree: ErgoTree, + creationHeight: Int, + additionalTokens: Seq[(TokenId, Long)] = Nil, + additionalRegisters: AdditionalRegisters = Map.empty, + transactionId: ModifierId = allZerosModifierId, + boxIndex: Short = 0): ErgoBox = + new ErgoBox(value, ergoTree, + CostingSigmaDslBuilder.Colls.fromArray(additionalTokens.toArray[(TokenId, Long)]), + additionalRegisters, + transactionId, boxIndex, creationHeight) + + def createBox(value: Long, + proposition: ErgoTree, + additionalTokens: Seq[(Digest32, Long)] = Seq(), + additionalRegisters: AdditionalRegisters = Map()) + = testBox(value, proposition, 0, additionalTokens, additionalRegisters) + + def createBox(value: Long, + proposition: ErgoTree, + creationHeight: Int) + = testBox(value, proposition, creationHeight, Seq(), Map(), ErgoBox.allZerosModifierId) + + /** Copies the given box allowing also to update fields. */ + def copyBox(box: ErgoBox)( + value: Long = box.value, + ergoTree: ErgoTree = box.ergoTree, + additionalTokens: Coll[(TokenId, Long)] = box.additionalTokens, + additionalRegisters: AdditionalRegisters = box.additionalRegisters, + transactionId: ModifierId = box.transactionId, + index: Short = box.index, + creationHeight: Int = box.creationHeight): ErgoBox = { + new ErgoBox(value, ergoTree, additionalTokens, additionalRegisters, transactionId, index, creationHeight) + } + + /** Creates a new box by updating some of the additional registers with the given new bindings. + * @param newBindings a map of the registers to be updated with new values + */ + def updatedRegisters(box: ErgoBox, newBindings: AdditionalRegisters): ErgoBox = { + copyBox(box)(additionalRegisters = box.additionalRegisters ++ newBindings) + } + + /** + * Create fake transaction with provided outputCandidates, but without inputs and data inputs. + * Normally, this transaction will be invalid as far as it will break rule that sum of + * coins in inputs should not be less then sum of coins in outputs, but we're not checking it + * in our test cases + */ + def createTransaction(outputCandidates: IndexedSeq[ErgoBoxCandidate]): ErgoLikeTransaction = { + new ErgoLikeTransaction(IndexedSeq(), IndexedSeq(), outputCandidates) + } + + def createTransaction(box: ErgoBoxCandidate): ErgoLikeTransaction = createTransaction(IndexedSeq(box)) + + def createTransaction(dataInputs: IndexedSeq[ErgoBox], + outputCandidates: IndexedSeq[ErgoBoxCandidate]): ErgoLikeTransaction = + new ErgoLikeTransaction(IndexedSeq(), dataInputs.map(b => DataInput(b.id)), outputCandidates) + +} diff --git a/sigmastate/src/test/scala/sigmastate/lang/LangTests.scala b/sigmastate/src/test/scala/sigmastate/lang/LangTests.scala index fbac4b6e2e..634bb8f103 100644 --- a/sigmastate/src/test/scala/sigmastate/lang/LangTests.scala +++ b/sigmastate/src/test/scala/sigmastate/lang/LangTests.scala @@ -1,7 +1,7 @@ package sigmastate.lang import sigmastate.lang.Terms.{MethodCallLike, Ident} -import sigmastate.Values.{LongConstant, SValue, Value, SigmaBoolean, GroupElementConstant, ConcreteCollection} +import sigmastate.Values.{LongConstant, SValue, Value, SigmaBoolean, ConcreteCollection} import sigmastate._ import java.math.BigInteger @@ -14,9 +14,10 @@ import sigmastate.interpreter.CryptoConstants import sigmastate.interpreter.Interpreter.ScriptEnv import special.sigma._ import sigmastate.eval._ +import sigmastate.helpers.NegativeTesting import special.collection.Coll -trait LangTests extends Matchers { +trait LangTests extends Matchers with NegativeTesting { def BoolIdent(name: String): Value[SBoolean.type] = Ident(name).asValue[SBoolean.type] def IntIdent(name: String): Value[SLong.type] = Ident(name).asValue[SLong.type] @@ -75,8 +76,8 @@ trait LangTests extends Matchers { def assertSrcCtxForAllNodes(tree: SValue): Unit = { import org.bitbucket.inkytonik.kiama.rewriting.Rewriter._ - rewrite(everywherebu(rule[SValue] { - case node => + rewrite(everywherebu(rule[Any] { + case node: SValue => withClue(s"Missing sourceContext for $node") { node.sourceContext.isDefined shouldBe true } node }))(tree) diff --git a/sigmastate/src/test/scala/sigmastate/lang/SigmaBinderTest.scala b/sigmastate/src/test/scala/sigmastate/lang/SigmaBinderTest.scala index 5b7bfbd303..3b84820964 100644 --- a/sigmastate/src/test/scala/sigmastate/lang/SigmaBinderTest.scala +++ b/sigmastate/src/test/scala/sigmastate/lang/SigmaBinderTest.scala @@ -58,6 +58,7 @@ class SigmaBinderTest extends PropSpec with PropertyChecks with Matchers with La bind(env, "g1.exp(n1)") shouldBe Apply(Select(GroupElementConstant(g1), "exp"), IndexedSeq(BigIntConstant(n1))) bind(env, "g1 * g2") shouldBe MethodCallLike(SigmaDsl.GroupElement(ecp1), "*", IndexedSeq(ecp2)) + bind(env, "p1 && p2") shouldBe MethodCallLike(SigmaDsl.SigmaProp(p1), "&&", IndexedSeq(SigmaDsl.SigmaProp(p2))) } property("predefined functions") { @@ -141,6 +142,8 @@ class SigmaBinderTest extends PropSpec with PropertyChecks with Matchers with La If(EQ(IntConstant(10), IntConstant(11)), IntConstant(2), IntConstant(3))) } + // TODO HF: SomeValue and NoneValue are not used in ErgoTree and can be + // either removed or implemented in v4.x property("Option constructors") { bind(env, "None") shouldBe NoneValue(NoType) bind(env, "Some(None)") shouldBe SomeValue(NoneValue(NoType)) diff --git a/sigmastate/src/test/scala/sigmastate/lang/SigmaBuilderTest.scala b/sigmastate/src/test/scala/sigmastate/lang/SigmaBuilderTest.scala index e5b88acf7c..07061743c4 100644 --- a/sigmastate/src/test/scala/sigmastate/lang/SigmaBuilderTest.scala +++ b/sigmastate/src/test/scala/sigmastate/lang/SigmaBuilderTest.scala @@ -4,7 +4,7 @@ import org.scalatest.prop.PropertyChecks import org.scalatest.{Matchers, PropSpec} import sigmastate.Values._ import sigmastate._ -import sigmastate.lang.exceptions.{ArithException, ConstraintFailed} +import sigmastate.lang.exceptions.ConstraintFailed import sigmastate.serialization.OpCodes class SigmaBuilderTest extends PropSpec with PropertyChecks with Matchers with LangTests { diff --git a/sigmastate/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala b/sigmastate/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala index a9dba2bafb..a2f005ba0f 100644 --- a/sigmastate/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala +++ b/sigmastate/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala @@ -81,6 +81,8 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ObjectGe Downcast(ByIndex(ConcreteCollection(Array(IntConstant(1)),SInt),IntConstant(0),None), SByte) comp(env, "allOf(Coll(c1, c2))") shouldBe AND(ConcreteCollection.fromSeq(Array(TrueLeaf, FalseLeaf))) comp(env, "getVar[Byte](10).get") shouldBe GetVarByte(10).get + comp(env, "getVar[Short](10).get") shouldBe GetVarShort(10).get + comp(env, "getVar[Long](10).get") shouldBe GetVarLong(10).get comp(env, "getVar[Coll[Byte]](10).get") shouldBe GetVarByteArray(10).get } @@ -318,7 +320,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ObjectGe property("SOption.map") { comp("getVar[Int](1).map({(i: Int) => i + 1})") shouldBe mkMethodCall(GetVarInt(1), - SOption.MapMethod.withConcreteTypes(Map(SOption.tT -> SInt, SOption.tR -> SInt)), + SOption.MapMethod.withConcreteTypes(Map(SType.tT -> SInt, SType.tR -> SInt)), IndexedSeq(FuncValue( Vector((1, SInt)), Plus(ValUse(1, SInt), IntConstant(1)))), Map() @@ -328,7 +330,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ObjectGe property("SOption.filter") { comp("getVar[Int](1).filter({(i: Int) => i > 0})") shouldBe mkMethodCall(GetVarInt(1), - SOption.FilterMethod.withConcreteTypes(Map(SOption.tT -> SInt)), + SOption.FilterMethod.withConcreteTypes(Map(SType.tT -> SInt)), IndexedSeq(FuncValue( Vector((1, SInt)), GT(ValUse(1, SInt), IntConstant(0)))), Map() @@ -437,7 +439,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ObjectGe Map()) } - // TODO soft-fork: related to https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 + // TODO HF: related to https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 ignore("SCollection.find") { comp("OUTPUTS.find({ (out: Box) => out.value >= 1L })") shouldBe mkMethodCall(Outputs, @@ -496,7 +498,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ObjectGe "Coll(1, 2).mapReduce({ (i: Int) => (i > 0, i.toLong) }, { (tl: (Long, Long)) => tl._1 + tl._2 })") shouldBe mkMethodCall( ConcreteCollection.fromItems(IntConstant(1), IntConstant(2)), - SCollection.MapReduceMethod.withConcreteTypes(Map(SCollection.tIV -> SInt, SCollection.tK -> SBoolean, SCollection.tV -> SLong)), + SCollection.MapReduceMethod.withConcreteTypes(Map(SCollection.tIV -> SInt, SType.tK -> SBoolean, SType.tV -> SLong)), Vector( Lambda(List(), Vector(("i", SInt)), diff --git a/sigmastate/src/test/scala/sigmastate/lang/SigmaTyperTest.scala b/sigmastate/src/test/scala/sigmastate/lang/SigmaTyperTest.scala index 93cad502d1..00286d5f25 100644 --- a/sigmastate/src/test/scala/sigmastate/lang/SigmaTyperTest.scala +++ b/sigmastate/src/test/scala/sigmastate/lang/SigmaTyperTest.scala @@ -14,6 +14,7 @@ import sigmastate.interpreter.Interpreter.ScriptEnv import sigmastate.lang.SigmaPredef._ import sigmastate.lang.Terms.Select import sigmastate.lang.exceptions.TyperException +import sigmastate.lang.syntax.ParserException import sigmastate.serialization.ErgoTreeSerializer import sigmastate.serialization.generators.ObjectGenerators import sigmastate.utxo.{Append, ExtractCreationInfo} @@ -42,16 +43,23 @@ class SigmaTyperTest extends PropSpec with PropertyChecks with Matchers with Lan def typefail(env: ScriptEnv, x: String, expectedLine: Int, expectedCol: Int): Unit = { val builder = TransformingSigmaBuilder - val parsed = SigmaParser(x, builder).get.value - val predefinedFuncRegistry = new PredefinedFuncRegistry(builder) - val binder = new SigmaBinder(env, builder, TestnetNetworkPrefix, predefinedFuncRegistry) - val bound = binder.bind(parsed) - val typer = new SigmaTyper(builder, predefinedFuncRegistry) - val exception = the[TyperException] thrownBy typer.typecheck(bound) - withClue(s"Exception: $exception, is missing source context:") { exception.source shouldBe defined } - val sourceContext = exception.source.get - sourceContext.line shouldBe expectedLine - sourceContext.column shouldBe expectedCol + assertExceptionThrown({ + val parsed = SigmaParser(x, builder).get.value + val predefinedFuncRegistry = new PredefinedFuncRegistry(builder) + val binder = new SigmaBinder(env, builder, TestnetNetworkPrefix, predefinedFuncRegistry) + val bound = binder.bind(parsed) + val typer = new SigmaTyper(builder, predefinedFuncRegistry) + typer.typecheck(bound) + }, { + case te: TyperException => + withClue(s"Exception: $te, is missing source context:") { te.source shouldBe defined } + val sourceContext = te.source.get + sourceContext.line shouldBe expectedLine + sourceContext.column shouldBe expectedCol + true + case pe: ParserException => true + case t => throw t + }) } property("simple expressions") { diff --git a/sigmastate/src/test/scala/sigmastate/serialization/ErgoTreeSerializerSpecification.scala b/sigmastate/src/test/scala/sigmastate/serialization/ErgoTreeSerializerSpecification.scala index 77a3c8b58d..65259dd36e 100644 --- a/sigmastate/src/test/scala/sigmastate/serialization/ErgoTreeSerializerSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/serialization/ErgoTreeSerializerSpecification.scala @@ -1,10 +1,12 @@ package sigmastate.serialization -import sigmastate.Values.{ErgoTree, IntConstant, SigmaPropValue} +import java.math.BigInteger + +import sigmastate.Values.{ShortConstant, LongConstant, BigIntConstant, SigmaPropValue, IntConstant, ErgoTree, ByteConstant} import sigmastate._ -import sigmastate.eval.IRContext +import sigmastate.eval.{IRContext, CBigInt} import sigmastate.helpers.SigmaTestingCommons -import sigmastate.lang.exceptions.{InputSizeLimitExceeded, SerializerException} +import sigmastate.lang.exceptions.{SerializerException, InputSizeLimitExceeded} import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer class ErgoTreeSerializerSpecification extends SerializationSpecification @@ -27,16 +29,23 @@ class ErgoTreeSerializerSpecification extends SerializationSpecification } property("(de)serialization round trip using treeBytes()") { - val tree = EQ(Plus(10, 20), IntConstant(30)).toSigmaProp - val ergoTree = extractConstants(tree) - val bytes = DefaultSerializer.serializeErgoTree(ergoTree) - val (_, _, deserializedConstants, treeBytes) = DefaultSerializer - .deserializeHeaderWithTreeBytes(SigmaSerializer.startReader(bytes)) - deserializedConstants shouldEqual ergoTree.constants - val r = SigmaSerializer.startReader(treeBytes, new ConstantStore(deserializedConstants), - resolvePlaceholdersToConstants = true) - val deserializedTree = ValueSerializer.deserialize(r) - deserializedTree shouldEqual tree + val trees = Seq( + EQ(Plus(10.toByte, 20.toByte), ByteConstant(30)).toSigmaProp, + EQ(Plus(10.toShort, 20.toShort), ShortConstant(30)).toSigmaProp, + EQ(Plus(10, 20), IntConstant(30)).toSigmaProp, + EQ(Plus(CBigInt(BigInteger.valueOf(10L)), BigIntConstant(20L)), BigIntConstant(30L)).toSigmaProp + ) + trees.foreach { tree => + val ergoTree = extractConstants(tree) + val bytes = DefaultSerializer.serializeErgoTree(ergoTree) + val (_, _, deserializedConstants, treeBytes) = DefaultSerializer + .deserializeHeaderWithTreeBytes(SigmaSerializer.startReader(bytes)) + deserializedConstants shouldEqual ergoTree.constants + val r = SigmaSerializer.startReader(treeBytes, new ConstantStore(deserializedConstants), + resolvePlaceholdersToConstants = true) + val deserializedTree = ValueSerializer.deserialize(r) + deserializedTree shouldEqual tree + } } property("Constant extraction via compiler pass: (de)serialization round trip") { diff --git a/sigmastate/src/test/scala/sigmastate/serialization/SigSerializerSpecification.scala b/sigmastate/src/test/scala/sigmastate/serialization/SigSerializerSpecification.scala index c8f716aa1b..751df5c0cf 100644 --- a/sigmastate/src/test/scala/sigmastate/serialization/SigSerializerSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/serialization/SigSerializerSpecification.scala @@ -79,13 +79,20 @@ class SigSerializerSpecification extends SigmaTestingCommons with ObjectGenerato boxesToSpend = IndexedSeq(fakeSelf), spendingTransaction = ErgoLikeTransactionTesting.dummy, self = fakeSelf) - - // get sigma conjectures out of transformers - val prop = prover.reduceToCrypto(ctx, expr).get._1 - - val proof = prover.prove(expr, ctx, challenge).get.proof - val proofTree = SigSerializer.parseAndComputeChallenges(prop, proof) - roundTrip(proofTree, prop) + .withCostLimit(Long.MaxValue) // To avoid occasional cost limit exceptions which are irrelevant here + + try { + // get sigma conjectures out of transformers + val prop = prover.reduceToCrypto(ctx, expr).get._1 + + val proof = prover.prove(expr, ctx, challenge).get.proof + val proofTree = SigSerializer.parseAndComputeChallenges(prop, proof) + roundTrip(proofTree, prop) + } catch { + case t: Throwable => + t.printStackTrace() + throw t + } } } diff --git a/sigmastate/src/test/scala/sigmastate/utils/SpecGen.scala b/sigmastate/src/test/scala/sigmastate/utils/SpecGen.scala index 0263b04399..0b5e1b27e4 100644 --- a/sigmastate/src/test/scala/sigmastate/utils/SpecGen.scala +++ b/sigmastate/src/test/scala/sigmastate/utils/SpecGen.scala @@ -22,7 +22,7 @@ object SpecGenUtils { trait SpecGen { import SpecGenUtils._ - val tT = STypeVar("T") + import SType.tT case class OpInfo( opDesc: ValueCompanion, diff --git a/sigmastate/src/test/scala/sigmastate/utxo/AVLTreeScriptsSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/AVLTreeScriptsSpecification.scala index e6c80cf30e..6b2593b2b7 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/AVLTreeScriptsSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/AVLTreeScriptsSpecification.scala @@ -14,6 +14,7 @@ import sigmastate.eval.{CSigmaProp, IRContext} import sigmastate.eval._ import sigmastate.eval.Extensions._ import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.ScriptNameProp import sigmastate.lang.Terms._ import special.collection.Coll @@ -210,12 +211,12 @@ class AVLTreeScriptsSpecification extends SigmaTestingCommons { suite => ).asBoolValue.toSigmaProp prop shouldBe propTree - val newBox1 = ErgoBox(10, pubkey, 0) + val newBox1 = testBox(10, pubkey, 0) val newBoxes = IndexedSeq(newBox1) val spendingTransaction = createTransaction(newBoxes) - val s = ErgoBox(20, TrueProp, 0, Seq(), Map(reg1 -> AvlTreeConstant(SigmaDsl.avlTree(treeData)))) + val s = testBox(20, TrueProp, 0, Seq(), Map(reg1 -> AvlTreeConstant(SigmaDsl.avlTree(treeData)))) val ctx = ErgoLikeContextTesting( currentHeight = 50, @@ -250,13 +251,13 @@ class AVLTreeScriptsSpecification extends SigmaTestingCommons { suite => |}""".stripMargin).asBoolValue.toSigmaProp val recipientProposition = new ContextEnrichingTestProvingInterpreter().dlogSecrets.head.publicImage - val selfBox = ErgoBox(20, TrueProp, 0, Seq(), Map(reg1 -> AvlTreeConstant(SigmaDsl.avlTree(treeData)))) + val selfBox = testBox(20, TrueProp, 0, Seq(), Map(reg1 -> AvlTreeConstant(SigmaDsl.avlTree(treeData)))) val ctx = ErgoLikeContextTesting( currentHeight = 50, lastBlockUtxoRoot = AvlTreeData.dummy, minerPubkey = ErgoLikeContextTesting.dummyPubkey, boxesToSpend = IndexedSeq(selfBox), - createTransaction(ErgoBox(1, recipientProposition, 0)), + createTransaction(testBox(1, recipientProposition, 0)), self = selfBox) avlProver.performOneOperation(Lookup(treeElements.head._1)) @@ -314,12 +315,12 @@ class AVLTreeScriptsSpecification extends SigmaTestingCommons { suite => ).asBoolValue.toSigmaProp prop shouldBe propTree - val newBox1 = ErgoBox(10, pubkey, 0) + val newBox1 = testBox(10, pubkey, 0) val newBoxes = IndexedSeq(newBox1) val spendingTransaction = createTransaction(newBoxes) - val s = ErgoBox(20, TrueProp, 0, Seq(), Map(reg1 -> AvlTreeConstant(treeData), reg2 -> ByteArrayConstant(key))) + val s = testBox(20, TrueProp, 0, Seq(), Map(reg1 -> AvlTreeConstant(treeData), reg2 -> ByteArrayConstant(key))) val ctx = ErgoLikeContextTesting( currentHeight = 50, @@ -366,12 +367,12 @@ class AVLTreeScriptsSpecification extends SigmaTestingCommons { suite => | sigmaProp(tree.getMany(keys, proof).forall( { (o: Option[Coll[Byte]]) => o.isDefined })) |}""".stripMargin).asBoolValue.toSigmaProp - val newBox1 = ErgoBox(10, pubkey, 0) + val newBox1 = testBox(10, pubkey, 0) val newBoxes = IndexedSeq(newBox1) val spendingTransaction = ErgoLikeTransaction(IndexedSeq(), newBoxes) - val s = ErgoBox(20, TrueProp, 0, Seq(), Map(reg1 -> AvlTreeConstant(treeData))) + val s = testBox(20, TrueProp, 0, Seq(), Map(reg1 -> AvlTreeConstant(treeData))) val ctx = ErgoLikeContextTesting( currentHeight = 50, diff --git a/sigmastate/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index d3253f26f1..5af6798676 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -10,6 +10,7 @@ import sigmastate.Values._ import sigmastate._ import sigmastate.eval.Extensions._ import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter._ import sigmastate.lang.Terms._ import special.sigma.InvalidType @@ -68,12 +69,12 @@ class BasicOpsSpecification extends SigmaTestingCommons { prop shouldBe propExp val p3 = prover.dlogSecrets(2).publicImage - val boxToSpend = ErgoBox(10, prop, additionalRegisters = Map( + val boxToSpend = testBox(10, prop, additionalRegisters = Map( reg1 -> SigmaPropConstant(p3), reg2 -> IntConstant(1)), creationHeight = 5) - val newBox1 = ErgoBox(10, prop, creationHeight = 0, boxIndex = 0, additionalRegisters = Map( + val newBox1 = testBox(10, prop, creationHeight = 0, boxIndex = 0, additionalRegisters = Map( reg1 -> IntConstant(1), reg2 -> IntConstant(10))) val tx = createTransaction(newBox1) @@ -357,7 +358,7 @@ class BasicOpsSpecification extends SigmaTestingCommons { BoolToSigmaProp( EQ( MethodCall(Self, SBox.getRegMethod, - IndexedSeq(Plus(GetVarInt(1).get, IntConstant(4))), Map(SBox.tT -> SInt) + IndexedSeq(Plus(GetVarInt(1).get, IntConstant(4))), Map(SType.tT -> SInt) ).asInstanceOf[Value[SOption[SType]]].get, IntConstant(1) ) diff --git a/sigmastate/src/test/scala/sigmastate/utxo/BlockchainSimulationSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/BlockchainSimulationSpecification.scala index 71f0a06e52..70ebaa4f04 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/BlockchainSimulationSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/BlockchainSimulationSpecification.scala @@ -12,6 +12,7 @@ import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util._ import sigmastate.Values.{IntConstant, LongConstant} import sigmastate.helpers.{BlockchainState, ErgoLikeContextTesting, ErgoLikeTestProvingInterpreter, ErgoTransactionValidator, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.ContextExtension import sigmastate.eval._ import sigmastate.interpreter.Interpreter.{ScriptNameProp, emptyEnv} @@ -212,7 +213,7 @@ object BlockchainSimulationSpecification { val initBlock = Block( (0 until windowSize).map { i => val txId = hash.hash(i.toString.getBytes ++ scala.util.Random.nextString(12).getBytes).toModifierId - val boxes = (1 to 30).map(_ => ErgoBox(10, GE(Height, IntConstant(i)).toSigmaProp, 0, Seq(), Map(heightReg -> IntConstant(i)), txId)) + val boxes = (1 to 30).map(_ => testBox(10, GE(Height, IntConstant(i)).toSigmaProp, 0, Seq(), Map(heightReg -> IntConstant(i)), txId)) ergoplatform.ErgoLikeTransaction(IndexedSeq(), boxes) }, ErgoLikeContextTesting.dummyPubkey diff --git a/sigmastate/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala index 3eebdd1e28..25980f9f11 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala @@ -5,6 +5,7 @@ import org.ergoplatform.ErgoScriptPredef.TrueProp import sigmastate.Values._ import sigmastate._ import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.lang.Terms._ import org.ergoplatform._ import sigmastate.SCollection._ @@ -73,8 +74,8 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { val prop = compile(Map(), code).asBoolValue.toSigmaProp expectedComp.foreach(prop shouldBe _) - val ctx = context(boxesToSpendValues.map(ErgoBox(_, pubkey, 0)), - outputBoxValues.map(ErgoBox(_, pubkey, 0))) + val ctx = context(boxesToSpendValues.map(testBox(_, pubkey, 0)), + outputBoxValues.map(testBox(_, pubkey, 0))) (prover, verifier, prop, ctx) } @@ -92,8 +93,8 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { ).toSigmaProp prop shouldBe expProp - val newBox1 = ErgoBox(16, pubkey, 0) - val newBox2 = ErgoBox(15, pubkey, 0) + val newBox1 = testBox(16, pubkey, 0) + val newBox2 = testBox(15, pubkey, 0) val newBoxes = IndexedSeq(newBox1, newBox2) val spendingTransaction = createTransaction(newBoxes) @@ -124,8 +125,8 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { ).toSigmaProp prop shouldBe propTree - val newBox1 = ErgoBox(10, pubkey, 0) - val newBox2 = ErgoBox(10, pubkey, 0) + val newBox1 = testBox(10, pubkey, 0) + val newBox2 = testBox(10, pubkey, 0) val newBoxes = IndexedSeq(newBox1, newBox2) val spendingTransaction = createTransaction(newBoxes) @@ -154,8 +155,8 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { ).toSigmaProp prop shouldBe propTree - val newBox1 = ErgoBox(10, pubkey, 0) - val newBox2 = ErgoBox(11, pubkey, 0) + val newBox1 = testBox(10, pubkey, 0) + val newBox2 = testBox(11, pubkey, 0) val newBoxes = IndexedSeq(newBox1, newBox2) val spendingTransaction = createTransaction(newBoxes) @@ -192,13 +193,13 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { ).toSigmaProp prop shouldBe propTree - val newBox1 = ErgoBox(10, pubkey, 0, Seq(), Map(reg1 -> LongConstant(3))) - val newBox2 = ErgoBox(10, pubkey, 0, Seq(), Map(reg1 -> LongConstant(6))) + val newBox1 = testBox(10, pubkey, 0, Seq(), Map(reg1 -> LongConstant(3))) + val newBox2 = testBox(10, pubkey, 0, Seq(), Map(reg1 -> LongConstant(6))) val newBoxes = IndexedSeq(newBox1, newBox2) val spendingTransaction = createTransaction(newBoxes) - val s = ErgoBox(20, TrueProp, 0, Seq(), Map(reg1 -> LongConstant(5))) + val s = testBox(20, TrueProp, 0, Seq(), Map(reg1 -> LongConstant(5))) val ctx = ErgoLikeContextTesting( currentHeight = 50, @@ -235,13 +236,13 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { prop shouldBe propTree - val newBox1 = ErgoBox(10, pubkey, 0) - val newBox2 = ErgoBox(10, pubkey, 0, Seq(), Map(reg1 -> LongConstant(6))) + val newBox1 = testBox(10, pubkey, 0) + val newBox2 = testBox(10, pubkey, 0, Seq(), Map(reg1 -> LongConstant(6))) val newBoxes = IndexedSeq(newBox1, newBox2) val spendingTransaction = createTransaction(newBoxes) - val s = ErgoBox(20, TrueProp, 0, Seq(), Map(reg1 -> LongConstant(5))) + val s = testBox(20, TrueProp, 0, Seq(), Map(reg1 -> LongConstant(5))) val ctx = ErgoLikeContextTesting( currentHeight = 50, @@ -266,13 +267,13 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { val propTree = SigmaAnd(pubkey, BoolToSigmaProp(EQ(SizeOf(Outputs), Plus(SizeOf(Inputs), IntConstant(1))))) prop shouldBe propTree - val newBox1 = ErgoBox(11, pubkey, 0) - val newBox2 = ErgoBox(10, pubkey, 0) + val newBox1 = testBox(11, pubkey, 0) + val newBox2 = testBox(10, pubkey, 0) val newBoxes = IndexedSeq(newBox1, newBox2) val spendingTransaction = createTransaction(newBoxes) - val s = ErgoBox(21, pubkey, 0) + val s = testBox(21, pubkey, 0) val ctx = ErgoLikeContextTesting( currentHeight = 50, diff --git a/sigmastate/src/test/scala/sigmastate/utxo/DistributedSigSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/DistributedSigSpecification.scala index 8374057d86..21517dd96e 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/DistributedSigSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/DistributedSigSpecification.scala @@ -1,12 +1,15 @@ package sigmastate.utxo import sigmastate._ -import sigmastate.basics.DLogProtocol.DLogInteractiveProver -import sigmastate.basics.DiffieHellmanTupleInteractiveProver import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeTestProvingInterpreter, SigmaTestingCommons} import sigmastate.interpreter._ import sigmastate.lang.Terms._ +/** + * Distributed signatures examples. + * See EIP-11 for generic signing procedure. + * In some simple generic procedure is simplified. + */ class DistributedSigSpecification extends SigmaTestingCommons { implicit lazy val IR: TestingIRContext = new TestingIRContext @@ -41,15 +44,13 @@ class DistributedSigSpecification extends SigmaTestingCommons { val env = Map("pubkeyA" -> pubkeyAlice, "pubkeyB" -> pubkeyBob) val prop: Values.Value[SSigmaProp.type] = compile(env, """pubkeyA && pubkeyB""").asSigmaProp - val (rBob, aBob) = DLogInteractiveProver.firstMessage() - - val hintFromBob: Hint = RealCommitment(pubkeyBob, aBob) - val bagA = HintsBag(Seq(hintFromBob)) + val hintsFromBob: HintsBag = proverB.generateCommitments(prop.treeWithSegregation, ctx) + val bagA = HintsBag(hintsFromBob.realCommitments) val proofAlice = proverA.prove(prop, ctx, fakeMessage, bagA).get val bagB = proverB.bagForMultisig(ctx, prop, proofAlice.proof, Seq(pubkeyAlice)) - .addHint(OwnCommitment(pubkeyBob, rBob, aBob)) + .addHint(hintsFromBob.ownCommitments.head) val proofBob = proverB.prove(prop, ctx, fakeMessage, bagB).get @@ -60,9 +61,7 @@ class DistributedSigSpecification extends SigmaTestingCommons { verifier.verify(prop, ctx, proofBob, fakeMessage).get._1 shouldBe true } - /** - * 3-out-of-3 AND signature - */ + // 3-out-of-3 AND signature property("distributed AND (3 out of 3)") { val proverA = new ErgoLikeTestProvingInterpreter val proverB = new ErgoLikeTestProvingInterpreter @@ -76,23 +75,23 @@ class DistributedSigSpecification extends SigmaTestingCommons { val env = Map("pubkeyA" -> pubkeyAlice, "pubkeyB" -> pubkeyBob, "pubkeyC" -> pubkeyCarol) val prop: Values.Value[SSigmaProp.type] = compile(env, """pubkeyA && pubkeyB && pubkeyC""").asSigmaProp - val (rBob, aBob) = DLogInteractiveProver.firstMessage() - val (rCarol, aCarol) = DLogInteractiveProver.firstMessage() + val bobHints = proverB.generateCommitments(prop.treeWithSegregation, ctx) + val carolHints = proverC.generateCommitments(prop.treeWithSegregation, ctx) - val dlBKnown: Hint = RealCommitment(pubkeyBob, aBob) - val dlCKnown: Hint = RealCommitment(pubkeyCarol, aCarol) + val dlBKnown: Hint = bobHints.realCommitments.head + val dlCKnown: Hint = carolHints.realCommitments.head val bagA = HintsBag(Seq(dlBKnown, dlCKnown)) val proofAlice = proverA.prove(prop, ctx, fakeMessage, bagA).get val bagC = proverB.bagForMultisig(ctx, prop, proofAlice.proof, Seq(pubkeyAlice)) - .addHint(OwnCommitment(pubkeyCarol, rCarol, aCarol)) + .addHint(carolHints.ownCommitments.head) .addHint(dlBKnown) val proofCarol = proverC.prove(prop, ctx, fakeMessage, bagC).get val bagB = proverB.bagForMultisig(ctx, prop, proofCarol.proof, Seq(pubkeyAlice, pubkeyCarol)) - .addHint(OwnCommitment(pubkeyBob, rBob, aBob)) + .addHint(bobHints.ownCommitments.head) val proofBob = proverB.prove(prop, ctx, fakeMessage, bagB).get @@ -134,14 +133,14 @@ class DistributedSigSpecification extends SigmaTestingCommons { val env = Map("pubkeyA" -> pubkeyAlice, "pubkeyB" -> pubkeyBob, "pubkeyC" -> pubkeyCarol) val prop = compile(env, """atLeast(2, Coll(pubkeyA, pubkeyB, pubkeyC))""").asSigmaProp - val (rBob, aBob) = DLogInteractiveProver.firstMessage() - val dlBKnown: Hint = RealCommitment(pubkeyBob, aBob) + val bobHints = proverB.generateCommitments(prop.treeWithSegregation, ctx) + val dlBKnown: Hint = bobHints.realCommitments.head val bagA = HintsBag(Seq(dlBKnown)) val proofAlice = proverA.prove(prop, ctx, fakeMessage, bagA).get val bagB = proverB.bagForMultisig(ctx, prop, proofAlice.proof, Seq(pubkeyAlice), Seq(pubkeyCarol)) - .addHint(OwnCommitment(pubkeyBob, rBob, aBob)) + .addHint(bobHints.ownCommitments.head) val proofBob = proverB.prove(prop, ctx, fakeMessage, bagB).get @@ -170,23 +169,23 @@ class DistributedSigSpecification extends SigmaTestingCommons { val env = Map("pubkeyA" -> pubkeyAlice, "pubkeyB" -> pubkeyBob, "pubkeyC" -> pubkeyCarol, "pubkeyD" -> pubkeyDave) val prop = compile(env, """atLeast(3, Coll(pubkeyA, pubkeyB, pubkeyC, pubkeyD))""").asSigmaProp - //Alice, Bob and Carol are signing - val (rBob, aBob) = DLogInteractiveProver.firstMessage() - val dlBKnown: Hint = RealCommitment(pubkeyBob, aBob) + // Alice, Bob and Carol are signing + val bobHints = proverB.generateCommitments(prop.treeWithSegregation, ctx) + val dlBKnown: Hint = bobHints.realCommitments.head - val (rCarol, aCarol) = DLogInteractiveProver.firstMessage() - val dlCKnown: Hint = RealCommitment(pubkeyCarol, aCarol) + val carolHints = proverC.generateCommitments(prop.treeWithSegregation, ctx) + val dlCKnown: Hint = carolHints.realCommitments.head val bagA = HintsBag(Seq(dlBKnown, dlCKnown)) val proofAlice = proverA.prove(prop, ctx, fakeMessage, bagA).get val bagC = proverC.bagForMultisig(ctx, prop, proofAlice.proof, Seq(pubkeyAlice), Seq(pubkeyDave)) ++ - HintsBag(Seq(dlBKnown, OwnCommitment(pubkeyCarol, rCarol, aCarol))) + HintsBag(Seq(dlBKnown, carolHints.ownCommitments.head)) val proofCarol = proverC.prove(prop, ctx, fakeMessage, bagC).get val bagB = (proverB.bagForMultisig(ctx, prop, proofAlice.proof, Seq(pubkeyAlice), Seq(pubkeyDave)) ++ proverB.bagForMultisig(ctx, prop, proofCarol.proof, Seq(pubkeyCarol))) - .addHint(OwnCommitment(pubkeyBob, rBob, aBob)) + .addHint(bobHints.ownCommitments.head) val proofBob = proverB.prove(prop, ctx, fakeMessage, bagB).get @@ -219,28 +218,31 @@ class DistributedSigSpecification extends SigmaTestingCommons { val prop = compile(env, """atLeast(3, Coll(pubkeyA, pubkeyB, pubkeyC, pubkeyD))""").asSigmaProp // Alice, Bob and Carol are signing - val (rBob, aBob) = DiffieHellmanTupleInteractiveProver.firstMessage(pubkeyBob) - val dlBKnown: Hint = RealCommitment(pubkeyBob, aBob) + val bobHints = proverB.generateCommitments(prop.treeWithSegregation, ctx) + val dlBKnown: Hint = bobHints.realCommitments.head - val (rCarol, aCarol) = DiffieHellmanTupleInteractiveProver.firstMessage(pubkeyCarol) - val dlCKnown: Hint = RealCommitment(pubkeyCarol, aCarol) + val carolHints = proverC.generateCommitments(prop.treeWithSegregation, ctx) + val dlCKnown: Hint = carolHints.realCommitments.head val bagA = HintsBag(Seq(dlBKnown, dlCKnown)) val proofAlice = proverA.prove(prop, ctx, fakeMessage, bagA).get val bagC = proverC.bagForMultisig(ctx, prop, proofAlice.proof, Seq(pubkeyAlice), Seq(pubkeyDave)) ++ - HintsBag(Seq(dlBKnown, OwnCommitment(pubkeyCarol, rCarol, aCarol))) + HintsBag(Seq(dlBKnown, carolHints.ownCommitments.head)) val proofCarol = proverC.prove(prop, ctx, fakeMessage, bagC).get val bagB = (proverB.bagForMultisig(ctx, prop, proofAlice.proof, Seq(pubkeyAlice), Seq(pubkeyDave)) ++ proverB.bagForMultisig(ctx, prop, proofCarol.proof, Seq(pubkeyCarol))) - .addHint(OwnCommitment(pubkeyBob, rBob, aBob)) + .addHint(bobHints.ownCommitments.head) val proofBob = proverB.prove(prop, ctx, fakeMessage, bagB).get // Proof generated by Alice without getting Bob's part is not correct verifier.verify(prop, ctx, proofAlice, fakeMessage).get._1 shouldBe false + // Proof generated by Alice without getting Bob's part is not correct + verifier.verify(prop, ctx, proofCarol, fakeMessage).get._1 shouldBe false + // Compound proof from Bob is correct verifier.verify(prop, ctx, proofBob, fakeMessage).get._1 shouldBe true } @@ -265,8 +267,8 @@ class DistributedSigSpecification extends SigmaTestingCommons { val prop = compile(env, """atLeast(2, Coll(pubkeyA, pubkeyB, pubkeyC, pubkeyD, pubkeyE))""").asSigmaProp //Alice and Dave are signing - val (rDave, aDave) = DLogInteractiveProver.firstMessage() - val dlDKnown: Hint = RealCommitment(pubkeyDave, aDave) + val daveHints = proverD.generateCommitments(prop.treeWithSegregation, ctx) + val dlDKnown: Hint = daveHints.realCommitments.head val bagA = HintsBag(Seq(dlDKnown)) val proofAlice = proverA.prove(prop, ctx, fakeMessage, bagA).get @@ -276,7 +278,7 @@ class DistributedSigSpecification extends SigmaTestingCommons { val bagD = proverD .bagForMultisig(ctx, prop, proofAlice.proof, Seq(pubkeyAlice), Seq(pubkeyBob, pubkeyCarol, pubkeyEmma)) - .addHint(OwnCommitment(pubkeyDave, rDave, aDave)) + .addHint(daveHints.ownCommitments.head) val proofDave = proverD.prove(prop, ctx, fakeMessage, bagD).get verifier.verify(prop, ctx, proofDave, fakeMessage).get._1 shouldBe true @@ -309,23 +311,24 @@ class DistributedSigSpecification extends SigmaTestingCommons { val script = """atLeast(4, Coll(pubkeyA, pubkeyB, pubkeyC, pubkeyD, pubkeyE, pubkeyF, pubkeyG, pubkeyH))""" val prop = compile(env, script).asSigmaProp - //Alice, Bob, Gerard, and Hannah are signing, others are simulated + // Alice, Bob, Gerard, and Hannah are signing, others are simulated - //first, commitments are needed from real signers - val (rAlice, aAlice) = DiffieHellmanTupleInteractiveProver.firstMessage(pubkeyAlice) - val dlAKnown: Hint = RealCommitment(pubkeyAlice, aAlice) - val secretCmtA = OwnCommitment(pubkeyAlice, rAlice, aAlice) + // first, commitments are needed from real signers + + val aliceHints = proverA.generateCommitments(prop.treeWithSegregation, ctx) + val dlAKnown: Hint = aliceHints.realCommitments.head + val secretCmtA: Hint = aliceHints.ownCommitments.head - val (rBob, aBob) = DiffieHellmanTupleInteractiveProver.firstMessage(pubkeyBob) - val dlBKnown: Hint = RealCommitment(pubkeyBob, aBob) - val secretCmtB = OwnCommitment(pubkeyBob, rBob, aBob) + val bobHints = proverB.generateCommitments(prop.treeWithSegregation, ctx) + val dlBKnown: Hint = bobHints.realCommitments.head + val secretCmtB: Hint = bobHints.ownCommitments.head - val (rGerard, aGerard) = DiffieHellmanTupleInteractiveProver.firstMessage(pubkeyGerard) - val dlGKnown: Hint = RealCommitment(pubkeyGerard, aGerard) - val secretCmtG = OwnCommitment(pubkeyGerard, rGerard, aGerard) + val gerardHints = proverG.generateCommitments(prop.treeWithSegregation, ctx) + val dlGKnown: Hint = gerardHints.realCommitments.head + val secretCmtG: Hint = gerardHints.ownCommitments.head - val (rHannah, aHannah) = DiffieHellmanTupleInteractiveProver.firstMessage(pubkeyHannah) - val secretCmtH = OwnCommitment(pubkeyHannah, rHannah, aHannah) + val hannahHints = proverH.generateCommitments(prop.treeWithSegregation, ctx) + val secretCmtH: Hint = hannahHints.ownCommitments.head val bagH = HintsBag(Seq(dlAKnown, dlBKnown, dlGKnown, secretCmtH)) val proofHannah = proverH.prove(prop, ctx, fakeMessage, bagH).get @@ -340,15 +343,15 @@ class DistributedSigSpecification extends SigmaTestingCommons { Seq(pubkeyCarol, pubkeyDave, pubkeyEmma, pubkeyFrank)) //now real proofs can be done in any order - val bagB = bag1.addHints(OwnCommitment(pubkeyBob, rBob, aBob), dlAKnown, dlGKnown) + val bagB = bag1.addHints(secretCmtB, dlAKnown, dlGKnown) val proofBob = proverB.prove(prop, ctx, fakeMessage, bagB).get val partialBobProofBag = proverB.bagForMultisig(ctx, prop, proofBob.proof, Seq(pubkeyBob), Seq.empty).realProofs.head - val bagA = bag1.addHints(OwnCommitment(pubkeyAlice, rAlice, aAlice), dlBKnown, dlGKnown) + val bagA = bag1.addHints(secretCmtA, dlBKnown, dlGKnown) val proofAlice = proverA.prove(prop, ctx, fakeMessage, bagA).get val partialAliceProofBag = proverA.bagForMultisig(ctx, prop, proofAlice.proof, Seq(pubkeyAlice), Seq.empty).realProofs.head - val bagG = bag1.addHints(OwnCommitment(pubkeyGerard, rGerard, aGerard), dlAKnown, dlBKnown) + val bagG = bag1.addHints(secretCmtG, dlAKnown, dlBKnown) val proofGerard = proverG.prove(prop, ctx, fakeMessage, bagG).get val partialGerardProofBag = proverG.bagForMultisig(ctx, prop, proofGerard.proof, Seq(pubkeyGerard), Seq.empty).realProofs.head @@ -393,15 +396,17 @@ class DistributedSigSpecification extends SigmaTestingCommons { //Alice and Dave are signing //first, commitments are needed from real signers - val (rAlice, aAlice) = DLogInteractiveProver.firstMessage() - val secretCmtA = OwnCommitment(pubkeyAlice, rAlice, aAlice) + val aliceHints = proverA.generateCommitments(prop.treeWithSegregation, ctx) + println(aliceHints) + val secretCmtA: Hint = aliceHints.ownCommitments.head - val (rDave, aDave) = DiffieHellmanTupleInteractiveProver.firstMessage(pubkeyDave) - val dlDKnown: Hint = RealCommitment(pubkeyDave, aDave) - val secretCmtD = OwnCommitment(pubkeyDave, rDave, aDave) + val daveHints = proverD.generateCommitments(prop.treeWithSegregation, ctx) + val dlDKnown: Hint = daveHints.realCommitments.head + val secretCmtD: Hint = daveHints.ownCommitments.head val bagA = HintsBag(Seq(secretCmtA, dlDKnown)) val proofAlice = proverA.prove(prop, ctx, fakeMessage, bagA).get + // Proof generated by Alice only is not correct verifier.verify(prop, ctx, proofAlice, fakeMessage).get._1 shouldBe false @@ -412,4 +417,73 @@ class DistributedSigSpecification extends SigmaTestingCommons { verifier.verify(prop, ctx, validProofD, fakeMessage).get._1 shouldBe true } + property("distributed THRESHOLD mixed via AND") { + // atLeast(3, Coll(proveDlog(pkA), proveDlog(pkB), proveDlog(pkC), proveDlog(pkD), proveDlog(pkE))) && (proveDlog(pkB) || proveDlog(pkF)) + val proverA = new ErgoLikeTestProvingInterpreter + val proverB = new ErgoLikeTestProvingInterpreter + val proverC = new ErgoLikeTestProvingInterpreter + val proverD = new ErgoLikeTestProvingInterpreter + val proverE = new ErgoLikeTestProvingInterpreter + val proverF = new ErgoLikeTestProvingInterpreter + + val verifier = new ContextEnrichingTestProvingInterpreter + + val pubkeyAlice = proverA.dlogSecrets.head.publicImage + val pubkeyBob = proverB.dlogSecrets.head.publicImage + val pubkeyCarol = proverC.dlogSecrets.head.publicImage + val pubkeyDave = proverD.dlogSecrets.head.publicImage + val pubkeyEmma = proverE.dlogSecrets.head.publicImage + val pubkeyFrank = proverF.dlogSecrets.head.publicImage + + val env = Map("pubkeyA" -> pubkeyAlice, "pubkeyB" -> pubkeyBob, "pubkeyC" -> pubkeyCarol, + "pubkeyD" -> pubkeyDave, "pubkeyE" -> pubkeyEmma, "pubkeyF" -> pubkeyFrank) + val script = + """atLeast(3, Coll(pubkeyA, pubkeyB, pubkeyC, pubkeyD, pubkeyE)) && (pubkeyB || pubkeyF)""".stripMargin + val prop = compile(env, script).asSigmaProp + // Alice, Bob and Emma are signing + + // first, commitments are needed from real signers + val aliceHints = proverA.generateCommitments(prop.treeWithSegregation, ctx) + val dlAKnown: Hint = aliceHints.realCommitments.head + val secretCmtA: Hint = aliceHints.ownCommitments.head + + val bobHints = proverB.generateCommitments(prop.treeWithSegregation, ctx) + val dlBKnown: Seq[Hint] = bobHints.realCommitments + val secretCmtB: Seq[Hint] = bobHints.ownCommitments + + val emmaHints = proverE.generateCommitments(prop.treeWithSegregation, ctx) + val dlEKnown: Hint = emmaHints.realCommitments.head + val secretCmtE: Hint = emmaHints.ownCommitments.head + + val bagA = HintsBag(Seq(secretCmtA, dlEKnown) ++ dlBKnown) + val proofAlice = proverA.prove(prop, ctx, fakeMessage, bagA).get + + // Proof generated by Alice only is not correct + verifier.verify(prop, ctx, proofAlice, fakeMessage).get._1 shouldBe false + + val bag0 = proverA + .bagForMultisig(ctx, prop, proofAlice.proof, + Seq(pubkeyAlice), + Seq(pubkeyCarol, pubkeyDave, pubkeyFrank)) + + //now real proofs can be done in any order + val bagB = bag0.addHints(dlAKnown, dlEKnown).addHints(secretCmtB :_*) + val proofBob = proverB.prove(prop, ctx, fakeMessage, bagB).get + val partialBobProofBag = proverB.bagForMultisig(ctx, prop, proofBob.proof, Seq(pubkeyBob), Seq.empty).realProofs + + val bagE = bag0.addHints(secretCmtE, dlAKnown).addHints(dlBKnown :_*) + val proofEmma = proverE.prove(prop, ctx, fakeMessage, bagE).get + val partialEmmaProofBag = proverE.bagForMultisig(ctx, prop, proofEmma.proof, Seq(pubkeyEmma), Seq.empty).realProofs.head + + val bag = bag0 + .addHints(partialBobProofBag: _*) + .addHints(partialEmmaProofBag) + .addHints(dlAKnown, dlEKnown).addHints(dlBKnown :_*) + .addHints(secretCmtB :_*) + + // Bob is generating a valid signature + val validProofB = proverB.prove(prop, ctx, fakeMessage, bag).get + verifier.verify(prop, ctx, validProofB, fakeMessage).get._1 shouldBe true + } + } diff --git a/sigmastate/src/test/scala/sigmastate/utxo/ErgoLikeInterpreterSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/ErgoLikeInterpreterSpecification.scala index 99533b2521..da779b4668 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/ErgoLikeInterpreterSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/ErgoLikeInterpreterSpecification.scala @@ -15,8 +15,8 @@ import sigmastate.interpreter.Interpreter._ import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.basics.ProveDHTuple import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, ErgoLikeTransactionTesting, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.lang.Terms._ -import sigmastate.lang.exceptions.InterpreterException import sigmastate.serialization.{SerializationSpecification, ValueSerializer} import sigmastate.utils.Helpers._ @@ -239,8 +239,8 @@ class ErgoLikeInterpreterSpecification extends SigmaTestingCommons ) prop shouldBe propExp - val newBox1 = ErgoBox(11, pubkey, 0) - val newBox2 = ErgoBox(10, pubkey, 0) + val newBox1 = testBox(11, pubkey, 0) + val newBox2 = testBox(10, pubkey, 0) val newBoxes = IndexedSeq(newBox1, newBox2) val spendingTransaction = createTransaction(newBoxes) @@ -269,8 +269,8 @@ class ErgoLikeInterpreterSpecification extends SigmaTestingCommons val prop = SigmaAnd(pubkey, BoolToSigmaProp(GT(ExtractAmount(ByIndex(Outputs, 0)), LongConstant(10)))) compiledProp shouldBe prop - val newBox1 = ErgoBox(11, pubkey, 0) - val newBox2 = ErgoBox(10, pubkey, 0) + val newBox1 = testBox(11, pubkey, 0) + val newBox2 = testBox(10, pubkey, 0) val newBoxes = IndexedSeq(newBox1, newBox2) val spendingTransaction = createTransaction(newBoxes) @@ -310,13 +310,13 @@ class ErgoLikeInterpreterSpecification extends SigmaTestingCommons val prop = SigmaAnd(hashEquals, scriptIsCorrect) val recipientProposition = SigmaPropConstant(new ContextEnrichingTestProvingInterpreter().dlogSecrets.head.publicImage) - val selfBox = ErgoBox(20, ErgoScriptPredef.TrueProp, 0, Seq(), Map()) + val selfBox = testBox(20, ErgoScriptPredef.TrueProp, 0, Seq(), Map()) val ctx = ErgoLikeContextTesting( currentHeight = 50, lastBlockUtxoRoot = AvlTreeData.dummy, minerPubkey = ErgoLikeContextTesting.dummyPubkey, boxesToSpend = IndexedSeq(selfBox), - createTransaction(ErgoBox(1, recipientProposition, 0)), + createTransaction(testBox(1, recipientProposition, 0)), self = selfBox) val proof = prover.prove(prop, ctx, fakeMessage).get @@ -347,11 +347,11 @@ class ErgoLikeInterpreterSpecification extends SigmaTestingCommons CreateProveDlog(ExtractRegisterAs[SGroupElement.type](Self, regPubkey2).get)) prop shouldBe propTree - val newBox1 = ErgoBox(10, pubkey3, 0) + val newBox1 = testBox(10, pubkey3, 0) val newBoxes = IndexedSeq(newBox1) val spendingTransaction = createTransaction(newBoxes) - val s1 = ErgoBox(20, ErgoScriptPredef.TrueProp, 0, Seq(), + val s1 = testBox(20, ErgoScriptPredef.TrueProp, 0, Seq(), Map(regPubkey1 -> GroupElementConstant(pubkey1.value), regPubkey2 -> GroupElementConstant(pubkey2.value))) @@ -368,7 +368,7 @@ class ErgoLikeInterpreterSpecification extends SigmaTestingCommons //make sure that wrong case couldn't be proved - val s2 = ErgoBox(20, ErgoScriptPredef.TrueProp, 0, Seq(), + val s2 = testBox(20, ErgoScriptPredef.TrueProp, 0, Seq(), Map(regPubkey1 -> GroupElementConstant(pubkey1.value))) val wrongCtx = ErgoLikeContextTesting( currentHeight = 50, @@ -404,13 +404,13 @@ class ErgoLikeInterpreterSpecification extends SigmaTestingCommons val prop = SigmaAnd(hashEquals.toSigmaProp, scriptIsCorrect) val recipientProposition = new ContextEnrichingTestProvingInterpreter().dlogSecrets.head.publicImage - val selfBox = ErgoBox(20, TrueProp, 0, Seq(), Map()) + val selfBox = testBox(20, TrueProp, 0, Seq(), Map()) val ctx = ErgoLikeContextTesting( currentHeight = 50, lastBlockUtxoRoot = AvlTreeData.dummy, minerPubkey = ErgoLikeContextTesting.dummyPubkey, boxesToSpend = IndexedSeq(selfBox), - createTransaction(ErgoBox(1, recipientProposition, 0)), + createTransaction(testBox(1, recipientProposition, 0)), self = selfBox) val proof = prover.prove(prop, ctx, fakeMessage).get @@ -430,10 +430,10 @@ class ErgoLikeInterpreterSpecification extends SigmaTestingCommons val pubkey1 = prover.dlogSecrets.head.publicImage val pubkey2 = prover.dlogSecrets(1).publicImage - val brother = ErgoBox(10, pubkey1, 0) - val brotherWithWrongId = ErgoBox(10, pubkey1, 0, boxIndex = 120: Short) + val brother = testBox(10, pubkey1, 0) + val brotherWithWrongId = testBox(10, pubkey1, 0, boxIndex = 120: Short) - val newBox = ErgoBox(20, pubkey2, 0) + val newBox = testBox(20, pubkey2, 0) val newBoxes = IndexedSeq(newBox) val spendingTransaction = createTransaction(newBoxes) @@ -456,7 +456,7 @@ class ErgoLikeInterpreterSpecification extends SigmaTestingCommons val altProp = compile(altEnv, """INPUTS.size == 2 && INPUTS(0).id == friend.id""").asBoolValue.toSigmaProp altProp shouldBe prop - val s = ErgoBox(10, prop, 0, Seq(), Map()) + val s = testBox(10, prop, 0, Seq(), Map()) val ctx = ErgoLikeContextTesting( currentHeight = 50, @@ -504,10 +504,10 @@ class ErgoLikeInterpreterSpecification extends SigmaTestingCommons val pubkey1 = prover.dlogSecrets.head.publicImage val pubkey2 = prover.dlogSecrets(1).publicImage - val friend = ErgoBox(10, pubkey1, 0) - val friendWithWrongId = ErgoBox(10, pubkey1, 0, boxIndex = 120: Short) + val friend = testBox(10, pubkey1, 0) + val friendWithWrongId = testBox(10, pubkey1, 0, boxIndex = 120: Short) - val newBox = ErgoBox(20, pubkey2, 0) + val newBox = testBox(20, pubkey2, 0) val newBoxes = IndexedSeq(newBox) val spendingTransaction = createTransaction(newBoxes) @@ -536,7 +536,7 @@ class ErgoLikeInterpreterSpecification extends SigmaTestingCommons val altProp = compile(env, "INPUTS.exists ({ (inputBox: Box) => inputBox.id == friend.id })").asBoolValue.toSigmaProp altProp shouldBe prop - val s = ErgoBox(10, prop, 0, Seq(), Map()) + val s = testBox(10, prop, 0, Seq(), Map()) val ctx1 = ErgoLikeContextTesting( currentHeight = 50, @@ -604,12 +604,12 @@ class ErgoLikeInterpreterSpecification extends SigmaTestingCommons ExtractRegisterAs[SByteArray](ByIndex(Inputs, 1), reg1).get))).toSigmaProp prop shouldBe propExpected - val input0 = ErgoBox(10, pubkey, 0, Seq(), Map()) - val input1 = ErgoBox(1, pubkey, 0, Seq(), Map(reg1 -> ByteArrayConstant(preimageHello))) - val input2 = ErgoBox(1, pubkey, 0, Seq(), Map(reg1 -> ByteArrayConstant(preimageWrong))) - val input3 = ErgoBox(10, prop, 0, Seq(), Map()) + val input0 = testBox(10, pubkey, 0, Seq(), Map()) + val input1 = testBox(1, pubkey, 0, Seq(), Map(reg1 -> ByteArrayConstant(preimageHello))) + val input2 = testBox(1, pubkey, 0, Seq(), Map(reg1 -> ByteArrayConstant(preimageWrong))) + val input3 = testBox(10, prop, 0, Seq(), Map()) - val output = ErgoBox(22, pubkey, 0, Seq(), Map()) + val output = testBox(22, pubkey, 0, Seq(), Map()) val spendingTransaction = createTransaction(output) @@ -633,7 +633,7 @@ class ErgoLikeInterpreterSpecification extends SigmaTestingCommons val sigmaProp = SigmaPropConstant(prover.dlogSecrets.head.publicImage) // put SigmaProp into the register val regValue = ByteArrayConstant(ValueSerializer.serialize(sigmaProp)) - val box = ErgoBox(20, TrueProp, 0, Seq(), Map(R4 -> regValue)) + val box = testBox(20, TrueProp, 0, Seq(), Map(R4 -> regValue)) // expect SBoolean in the register val prop = DeserializeRegister(R4, SBoolean).toSigmaProp @@ -643,7 +643,7 @@ class ErgoLikeInterpreterSpecification extends SigmaTestingCommons lastBlockUtxoRoot = AvlTreeData.dummy, minerPubkey = ErgoLikeContextTesting.dummyPubkey, boxesToSpend = IndexedSeq(box), - createTransaction(IndexedSeq(ErgoBox(10, TrueProp, 0))), + createTransaction(IndexedSeq(testBox(10, TrueProp, 0))), self = box) an[RuntimeException] should be thrownBy @@ -662,13 +662,13 @@ class ErgoLikeInterpreterSpecification extends SigmaTestingCommons DeserializeContext(scriptId, scriptProp.tpe) ).toSigmaProp - val box = ErgoBox(20, ErgoScriptPredef.TrueProp, 0, Seq(), Map()) + val box = testBox(20, ErgoScriptPredef.TrueProp, 0, Seq(), Map()) val ctx = ErgoLikeContextTesting( currentHeight = 50, lastBlockUtxoRoot = AvlTreeData.dummy, minerPubkey = ErgoLikeContextTesting.dummyPubkey, boxesToSpend = IndexedSeq(box), - createTransaction(IndexedSeq(ErgoBox(10, TrueProp, 0))), + createTransaction(IndexedSeq(testBox(10, TrueProp, 0))), self = box) val pr = prover.prove(prop, ctx, fakeMessage).get diff --git a/sigmastate/src/test/scala/sigmastate/utxo/ProverSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/ProverSpecification.scala new file mode 100644 index 0000000000..695e579e98 --- /dev/null +++ b/sigmastate/src/test/scala/sigmastate/utxo/ProverSpecification.scala @@ -0,0 +1,107 @@ +package sigmastate.utxo + +import sigmastate.{CAND, CAndUnproven, COR, COrUnproven, CTHRESHOLD, CThresholdUnproven, NodePosition, UnprovenSchnorr} +import sigmastate.Values.SigmaBoolean +import sigmastate.basics.DLogProtocol.FirstDLogProverMessage +import sigmastate.basics.{FirstDiffieHellmanTupleProverMessage, SecP256K1} +import sigmastate.helpers.{ErgoLikeTestProvingInterpreter, SigmaTestingCommons} + +class ProverSpecification extends SigmaTestingCommons { + + implicit lazy val IR: TestingIRContext = new TestingIRContext + + property("generateCommitments") { + + val prover = new ErgoLikeTestProvingInterpreter + + val pk: SigmaBoolean = prover.dlogSecrets.head.publicImage + val pk2: SigmaBoolean = prover.dhSecrets.head.publicImage + + prover.generateCommitmentsFor(pk, Seq(pk2)).hints.isEmpty shouldBe true + + val pk3 = CAND(Seq(pk, pk2)) + prover.generateCommitmentsFor(pk, Seq(pk3)).hints.isEmpty shouldBe true + prover.generateCommitmentsFor(pk3, Seq(pk3)).hints.isEmpty shouldBe true + + val h = prover.generateCommitmentsFor(pk, Seq(pk)) + h.hints.nonEmpty shouldBe true + h.realCommitments.head.position shouldBe NodePosition.CryptoTreePrefix + h.ownCommitments.head.position shouldBe NodePosition.CryptoTreePrefix + + h.realCommitments.head.commitment shouldBe h.ownCommitments.head.commitment + + val a = h.realCommitments.head.commitment.asInstanceOf[FirstDLogProverMessage] + val r = h.ownCommitments.head.secretRandomness + + // g^r == a + SecP256K1.exponentiate(SecP256K1.generator, r) shouldBe a.ecData + + val h2 = prover.generateCommitmentsFor(pk3, Seq(pk)) + h2.hints.size shouldBe 2 + + h2.realCommitments.head.position shouldBe NodePosition(Seq(0,0)) + h2.ownCommitments.head.position shouldBe NodePosition(Seq(0,0)) + + //DH + val h3 = prover.generateCommitmentsFor(pk2, Seq(pk2)) + h3.hints.nonEmpty shouldBe true + h3.realCommitments.head.position shouldBe NodePosition.CryptoTreePrefix + h3.ownCommitments.head.position shouldBe NodePosition.CryptoTreePrefix + + h3.realCommitments.head.commitment shouldBe h3.ownCommitments.head.commitment + + h3.realCommitments.head.commitment.isInstanceOf[FirstDiffieHellmanTupleProverMessage] shouldBe true + } + + property("setPositions - and") { + val prover = new ErgoLikeTestProvingInterpreter + val pk0 = prover.dlogSecrets(0).publicImage + val pk1 = prover.dlogSecrets(1).publicImage + + val parentPos = NodePosition(Seq(0,0)) + val child0 = UnprovenSchnorr(pk0, None, None, None, false, NodePosition(Seq(1))) + val child1 = UnprovenSchnorr(pk1, None, None, None, false, NodePosition(Seq(0))) + + val c0 = CAndUnproven(CAND(Seq(pk0, pk1)), None, false, Seq(child0, child1), parentPos) + val c1 = prover.setPositions(c0) + + c1.children.head.asInstanceOf[UnprovenSchnorr].position shouldBe NodePosition(Seq(0, 0, 0)) + + c1.children(1).asInstanceOf[UnprovenSchnorr].position shouldBe NodePosition(Seq(0, 0, 1)) + } + + property("setPositions - or") { + val prover = new ErgoLikeTestProvingInterpreter + val pk0 = prover.dlogSecrets(0).publicImage + val pk1 = prover.dlogSecrets(1).publicImage + + val parentPos = NodePosition(Seq(0,0)) + val child0 = UnprovenSchnorr(pk0, None, None, None, false, NodePosition(Seq(1))) + val child1 = UnprovenSchnorr(pk1, None, None, None, false, NodePosition(Seq(0))) + + val c0 = COrUnproven(COR(Seq(pk0, pk1)), None, false, Seq(child0, child1), parentPos) + val c1 = prover.setPositions(c0) + + c1.children.head.asInstanceOf[UnprovenSchnorr].position shouldBe NodePosition(Seq(0, 0, 0)) + + c1.children(1).asInstanceOf[UnprovenSchnorr].position shouldBe NodePosition(Seq(0, 0, 1)) + } + + property("setPositions - threshold") { + val prover = new ErgoLikeTestProvingInterpreter + val pk0 = prover.dlogSecrets(0).publicImage + val pk1 = prover.dlogSecrets(1).publicImage + + val parentPos = NodePosition(Seq(0,0)) + val child0 = UnprovenSchnorr(pk0, None, None, None, false, NodePosition(Seq(1))) + val child1 = UnprovenSchnorr(pk1, None, None, None, false, NodePosition(Seq(0))) + + val c0 = CThresholdUnproven(CTHRESHOLD(1, Seq(pk0, pk1)), None, false, 1, Seq(child0, child1), None, parentPos) + val c1 = prover.setPositions(c0) + + c1.children.head.asInstanceOf[UnprovenSchnorr].position shouldBe NodePosition(Seq(0, 0, 0)) + + c1.children(1).asInstanceOf[UnprovenSchnorr].position shouldBe NodePosition(Seq(0, 0, 1)) + } + +} diff --git a/sigmastate/src/test/scala/sigmastate/utxo/benchmarks/CrowdfundingBenchmark.scala b/sigmastate/src/test/scala/sigmastate/utxo/benchmarks/CrowdfundingBenchmark.scala index c24a80983a..26f1948312 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/benchmarks/CrowdfundingBenchmark.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/benchmarks/CrowdfundingBenchmark.scala @@ -1,19 +1,20 @@ package sigmastate.utxo.benchmarks -import org.ergoplatform.{ErgoBox, ErgoLikeContext, ErgoLikeTransaction, ErgoScriptPredef} +import org.ergoplatform.{ErgoLikeContext, ErgoScriptPredef} import sigmastate.Values._ import sigmastate._ import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import scalan.util.BenchmarkUtil._ class CrowdfundingBenchmark extends SigmaTestingCommons { implicit lazy val IR = new TestingIRContext def createTestContext(contract: CrowdFundingContract): ErgoLikeContext = { - val outputToSpend = ErgoBox(10, ErgoScriptPredef.TrueProp, 0) + val outputToSpend = testBox(10, ErgoScriptPredef.TrueProp, 0) //First case: height < timeout, project is able to claim amount of tokens not less than required threshold - val tx1Output1 = ErgoBox(contract.minToRaise, contract.projectPubKey, 0) - val tx1Output2 = ErgoBox(1, contract.projectPubKey, 0) + val tx1Output1 = testBox(contract.minToRaise, contract.projectPubKey, 0) + val tx1Output2 = testBox(1, contract.projectPubKey, 0) //normally this transaction would invalid, but we're not checking it in this test val tx = createTransaction(IndexedSeq(tx1Output1, tx1Output2)) val ctx = ErgoLikeContextTesting( diff --git a/sigmastate/src/test/scala/sigmastate/utxo/blockchain/BlockchainSimulationTestingCommons.scala b/sigmastate/src/test/scala/sigmastate/utxo/blockchain/BlockchainSimulationTestingCommons.scala index 7f19aaa06c..4612112f85 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/blockchain/BlockchainSimulationTestingCommons.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/blockchain/BlockchainSimulationTestingCommons.scala @@ -5,10 +5,11 @@ import org.ergoplatform._ import scorex.crypto.authds.{ADDigest, ADKey, ADValue} import scorex.crypto.authds.avltree.batch.{BatchAVLProver, Insert, Remove} import scorex.crypto.hash.{Blake2b256, Digest32} -import sigmastate.{AvlTreeData, AvlTreeFlags, GE, Values} +import sigmastate.{AvlTreeData, AvlTreeFlags, Values} import sigmastate.Values.{ErgoTree, LongConstant} import sigmastate.eval._ import sigmastate.helpers.{BlockchainState, ErgoLikeContextTesting, ErgoLikeTestProvingInterpreter, ErgoTransactionValidator, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.utils.Helpers._ import scala.collection.mutable import scala.util.{Random, Try} @@ -147,7 +148,7 @@ object BlockchainSimulationTestingCommons extends SigmaTestingCommons { val initBlock = FullBlock( (0 until 10).map { i => val txId = Blake2b256.hash(i.toString.getBytes ++ scala.util.Random.nextString(12).getBytes).toModifierId - val boxes = (1 to 50).map(_ => ErgoBox(10, Values.TrueLeaf.toSigmaProp, i, Seq(), Map(), txId)) + val boxes = (1 to 50).map(_ => testBox(10, Values.TrueLeaf.toSigmaProp, i, Seq(), Map(), txId)) createTransaction(boxes) }, ErgoLikeContextTesting.dummyPubkey diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/CoinEmissionSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/CoinEmissionSpecification.scala index cd7a743cda..31251cbb93 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/examples/CoinEmissionSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/CoinEmissionSpecification.scala @@ -4,6 +4,7 @@ import org.ergoplatform._ import scorex.util.ScorexLogging import sigmastate.Values.IntConstant import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.ContextExtension import sigmastate.interpreter.Interpreter.{ScriptNameProp, emptyEnv} import sigmastate.lang.Terms._ @@ -135,7 +136,7 @@ block 1600 in 1622 ms, 30000000000 coins remain, defs: 61661 val minerPubkey = minerImage.pkBytes val minerProp = minerImage - val initialBoxCandidate: ErgoBox = ErgoBox(coinsTotal / 4, prop, 0, Seq(), Map(register -> IntConstant(-1))) + val initialBoxCandidate: ErgoBox = testBox(coinsTotal / 4, prop, 0, Seq(), Map(register -> IntConstant(-1))) val initBlock = FullBlock(IndexedSeq(createTransaction(initialBoxCandidate)), minerPubkey) val genesisState = ValidationState.initialState(initBlock) val fromState = genesisState.boxesReader.byId(genesisState.boxesReader.allIds.head).get diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/ColdWalletAdvContractExampleSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/ColdWalletAdvContractExampleSpecification.scala index 1933f73c0d..fa03a9904d 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/examples/ColdWalletAdvContractExampleSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/ColdWalletAdvContractExampleSpecification.scala @@ -5,6 +5,7 @@ import org.ergoplatform._ import sigmastate.AvlTreeData import sigmastate.Values.{IntConstant, LongConstant} import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, ErgoLikeTestProvingInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.ScriptNameProp import sigmastate.lang.Terms._ @@ -96,7 +97,7 @@ class ColdWalletAdvContractExampleSpecification extends SigmaTestingCommons { val avbl1Key = depositAmount * percent1Key/100 val avbl2Key = depositAmount * percent2Key/100 - val depositOutput = ErgoBox(depositAmount, address.script, depositHeight, Nil, + val depositOutput = testBox(depositAmount, address.script, depositHeight, Nil, Map( R4 -> IntConstant(depositHeight), // can keep value in R4 initially R5 -> LongConstant(avbl1Key), // keeping it below min will make UTXO unspendable @@ -115,14 +116,14 @@ class ColdWalletAdvContractExampleSpecification extends SigmaTestingCommons { val firstWithdrawAmount1Key = depositAmount * percent1Key / 100 // less than or equal to percent val firstChangeAmount1Key = depositAmount - firstWithdrawAmount1Key - val firstChangeOutput1Key = ErgoBox(firstChangeAmount1Key, address.script, firstWithdrawHeight, Nil, + val firstChangeOutput1Key = testBox(firstChangeAmount1Key, address.script, firstWithdrawHeight, Nil, Map( R4 -> IntConstant(depositHeight), // newStart (= old start) R5 -> LongConstant(avbl1Key - firstWithdrawAmount1Key), // new avbl1Key (= 0) R6 -> LongConstant(avbl2Key) // new avbl2Key (= old avbl2Key) ) ) - val firstWithdrawOutput1Key = ErgoBox(firstWithdrawAmount1Key, carolPubKey, firstWithdrawHeight) + val firstWithdrawOutput1Key = testBox(firstWithdrawAmount1Key, carolPubKey, firstWithdrawHeight) //normally this transaction would be invalid, but we're not checking it in this test val firstWithdrawTx1Key = ErgoLikeTransaction(IndexedSeq(), IndexedSeq(firstChangeOutput1Key, firstWithdrawOutput1Key)) @@ -151,14 +152,14 @@ class ColdWalletAdvContractExampleSpecification extends SigmaTestingCommons { val firstWithdrawAmount2Key = depositAmount * percent2Key / 100 // less than or equal to percent val firstChangeAmount2Key = depositAmount - firstWithdrawAmount2Key - val firstChangeOutput2Key = ErgoBox(firstChangeAmount2Key, address.script, firstWithdrawHeight, Nil, + val firstChangeOutput2Key = testBox(firstChangeAmount2Key, address.script, firstWithdrawHeight, Nil, Map( R4 -> IntConstant(depositHeight), // newStart (= old start) R5 -> LongConstant(avbl1Key), // new avbl1Key (= 0) R6 -> LongConstant(avbl2Key - firstWithdrawAmount2Key) // new avbl2Key (= old avbl2Key) ) ) - val firstWithdrawOutput2Key = ErgoBox(firstWithdrawAmount2Key, carolPubKey, firstWithdrawHeight) + val firstWithdrawOutput2Key = testBox(firstWithdrawAmount2Key, carolPubKey, firstWithdrawHeight) //normally this transaction would be invalid, but we're not checking it in this test val firstWithdrawTx2Key = ErgoLikeTransaction(IndexedSeq(), IndexedSeq(firstChangeOutput2Key, firstWithdrawOutput2Key)) diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/ColdWalletContractExampleSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/ColdWalletContractExampleSpecification.scala index 64b0bf4b1c..3987f84dfd 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/examples/ColdWalletContractExampleSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/ColdWalletContractExampleSpecification.scala @@ -3,6 +3,7 @@ package sigmastate.utxo.examples import org.ergoplatform.ErgoBox.{R4, R5} import org.ergoplatform._ import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.AvlTreeData import sigmastate.Values.{IntConstant, LongConstant} import sigmastate.interpreter.Interpreter.ScriptNameProp @@ -72,7 +73,7 @@ class ColdWalletContractExampleSpecification extends SigmaTestingCommons { val depositHeight = 50 val min = depositAmount - depositAmount * percent/100 // should be 99000 (99k) - val depositOutput = ErgoBox(depositAmount, address.script, depositHeight, Nil, + val depositOutput = testBox(depositAmount, address.script, depositHeight, Nil, Map( R4 -> IntConstant(depositHeight), // can keep any value in R4 initially R5 -> LongConstant(min) // keeping it below min will make UTXO unspendable @@ -89,7 +90,7 @@ class ColdWalletContractExampleSpecification extends SigmaTestingCommons { // Both Alice ane Bob withdraw val withdrawAmountFull = depositAmount // full amount is withdrawn - val withdrawOutputAliceAndBob = ErgoBox(withdrawAmountFull, carolPubKey, firstWithdrawHeight) + val withdrawOutputAliceAndBob = testBox(withdrawAmountFull, carolPubKey, firstWithdrawHeight) val withdrawTxAliceAndBob = ErgoLikeTransaction(IndexedSeq(), IndexedSeq(withdrawOutputAliceAndBob)) @@ -112,13 +113,13 @@ class ColdWalletContractExampleSpecification extends SigmaTestingCommons { val firstWithdrawAmount = depositAmount * percent / 100 // less than or eqaul to percent (1000) val firstChangeAmount = depositAmount - firstWithdrawAmount // 99000 - val firstChangeOutput = ErgoBox(firstChangeAmount, address.script, firstWithdrawHeight, Nil, + val firstChangeOutput = testBox(firstChangeAmount, address.script, firstWithdrawHeight, Nil, Map( R4 -> IntConstant(depositHeight), // newStart (= old start) = 50 R5 -> LongConstant(min) // newMin (= old min) = 99000 ) ) - val firstWithdrawOutput = ErgoBox(firstWithdrawAmount, carolPubKey, firstWithdrawHeight) + val firstWithdrawOutput = testBox(firstWithdrawAmount, carolPubKey, firstWithdrawHeight) //normally this transaction would be invalid, but we're not checking it in this test val firstWithdrawTx = ErgoLikeTransaction(IndexedSeq(), IndexedSeq(firstChangeOutput, firstWithdrawOutput)) @@ -141,13 +142,13 @@ class ColdWalletContractExampleSpecification extends SigmaTestingCommons { // invalid (amount greater than allowed) val withdrawAmountInvalid = depositAmount * percent / 100 + 1 // more than percent val changeAmountInvalid = depositAmount - withdrawAmountInvalid - val changeOutputInvalid = ErgoBox(changeAmountInvalid, address.script, firstWithdrawHeight, Nil, + val changeOutputInvalid = testBox(changeAmountInvalid, address.script, firstWithdrawHeight, Nil, Map( R4 -> IntConstant(depositHeight), // newStart (= old start) R5 -> LongConstant(min) // newMin (= old min) ) ) - val withdrawOutputInvalid = ErgoBox(withdrawAmountInvalid, carolPubKey, firstWithdrawHeight) + val withdrawOutputInvalid = testBox(withdrawAmountInvalid, carolPubKey, firstWithdrawHeight) // normally this transaction would be invalid, but we're not checking it in this test val withdrawTxInvalid = ErgoLikeTransaction(IndexedSeq(), IndexedSeq(changeOutputInvalid, withdrawOutputInvalid)) @@ -175,13 +176,13 @@ class ColdWalletContractExampleSpecification extends SigmaTestingCommons { val secondChangeAmount = firstChangeAmount - secondWithdrawAmount val secondMin = firstChangeAmount - firstChangeAmount * percent/100 - val secondChangeOutput = ErgoBox(secondChangeAmount, address.script, secondWithdrawHeight, Nil, + val secondChangeOutput = testBox(secondChangeAmount, address.script, secondWithdrawHeight, Nil, Map( R4 -> IntConstant(secondWithdrawHeight), // newStart R5 -> LongConstant(secondMin) // newMin ) ) - val secondWithdrawOutput = ErgoBox(secondWithdrawAmount, carolPubKey, secondWithdrawHeight) + val secondWithdrawOutput = testBox(secondWithdrawAmount, carolPubKey, secondWithdrawHeight) //normally this transaction would be invalid, but we're not checking it in this test val secondWithdrawTx = ErgoLikeTransaction(IndexedSeq(), IndexedSeq(secondChangeOutput, secondWithdrawOutput)) diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/CoopExampleSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/CoopExampleSpecification.scala index b058028605..c44e5acb35 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/examples/CoopExampleSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/CoopExampleSpecification.scala @@ -1,14 +1,15 @@ package sigmastate.utxo.examples -import org.ergoplatform.{ErgoBox, ErgoLikeContext, ErgoLikeTransaction, ErgoScriptPredef} +import org.ergoplatform.{ErgoLikeContext, ErgoLikeTransaction, ErgoBox, ErgoScriptPredef} import org.scalatest.Assertion import org.scalatest.TryValues._ import sigmastate.basics.DLogProtocol.ProveDlog import scorex.crypto.hash.Blake2b256 -import sigmastate.Values.{ByteArrayConstant, SigmaPropValue, Value} -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.Values.{ByteArrayConstant, SigmaPropValue, BooleanConstant} +import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, SigmaTestingCommons, ErgoLikeTestInterpreter} +import sigmastate.helpers.TestingHelpers._ import sigmastate.lang.Terms._ -import sigmastate.{AvlTreeData, SBoolean} +import sigmastate.AvlTreeData class CoopExampleSpecification extends SigmaTestingCommons { implicit lazy val IR = new TestingIRContext @@ -127,11 +128,11 @@ class CoopExampleSpecification extends SigmaTestingCommons { """.stripMargin).asSigmaProp { - val self = ErgoBox(totalValue, spendingProp1, 0) - val output1 = ErgoBox(totalValue / 4, pubkeyA, 0) - val output2 = ErgoBox(totalValue / 4, pubkeyB, 0) - val output3 = ErgoBox(totalValue / 4, pubkeyC, 0) - val output4 = ErgoBox(totalValue / 4, pubkeyD, 0) + val self = testBox(totalValue, spendingProp1, 0) + val output1 = testBox(totalValue / 4, pubkeyA, 0) + val output2 = testBox(totalValue / 4, pubkeyB, 0) + val output3 = testBox(totalValue / 4, pubkeyC, 0) + val output4 = testBox(totalValue / 4, pubkeyD, 0) val tx = mkTxFromOutputs(output1, output2, output3, output4) val ctx = mkCtx(5001, tx, self) successProofTest(spendingProp1, ctx, coopA, verifier) @@ -143,10 +144,10 @@ class CoopExampleSpecification extends SigmaTestingCommons { "pubkeyC" -> pubkeyC, "pubkeyD" -> pubkeyD, "businessKey" -> businessKey, - "pubkeyTool1" -> SBoolean.mkConstant(false), - "pubkeyTool2" -> SBoolean.mkConstant(false), - "pubkeyTool3" -> SBoolean.mkConstant(false), - "pubkeyTool4" -> SBoolean.mkConstant(false), + "pubkeyTool1" -> BooleanConstant(false), + "pubkeyTool2" -> BooleanConstant(false), + "pubkeyTool3" -> BooleanConstant(false), + "pubkeyTool4" -> BooleanConstant(false), "pubkeyConstr1" -> constructionRing(0), "pubkeyConstr2" -> constructionRing(1), "pubkeyConstr3" -> constructionRing(2) @@ -167,11 +168,11 @@ class CoopExampleSpecification extends SigmaTestingCommons { * Withdraw successfully */ { - val self = ErgoBox(totalValue, spendingProp2, 0) - val output1 = ErgoBox(totalValue / 4, pubkeyA, 0) - val output2 = ErgoBox(totalValue / 4, pubkeyB, 0) - val output3 = ErgoBox(totalValue / 4, pubkeyC, 0) - val output4 = ErgoBox(totalValue / 4, pubkeyD, 0) + val self = testBox(totalValue, spendingProp2, 0) + val output1 = testBox(totalValue / 4, pubkeyA, 0) + val output2 = testBox(totalValue / 4, pubkeyB, 0) + val output3 = testBox(totalValue / 4, pubkeyC, 0) + val output4 = testBox(totalValue / 4, pubkeyD, 0) val tx = mkTxFromOutputs(output1, output2, output3, output4) val ctx = mkCtx(5001, tx, self) successProofTest(spendingProp2, ctx, coopA, verifier) @@ -181,9 +182,9 @@ class CoopExampleSpecification extends SigmaTestingCommons { * Won't spend more then defined share */ { - val self = ErgoBox(totalValue, spendingProp2, 0) - val output1 = ErgoBox(totalValue / 2, pubkeyB, 0) - val output2 = ErgoBox(totalValue / 2, pubkeyC, 0) + val self = testBox(totalValue, spendingProp2, 0) + val output1 = testBox(totalValue / 2, pubkeyB, 0) + val output2 = testBox(totalValue / 2, pubkeyC, 0) val tx = mkTxFromOutputs(output1, output2) val ctx = mkCtx(5001, tx, self = self) failingProofTest(spendingProp2, ctx, coopA) @@ -193,11 +194,11 @@ class CoopExampleSpecification extends SigmaTestingCommons { * Won't spend before minimal height */ { - val self = ErgoBox(totalValue, spendingProp2, 0) - val output1 = ErgoBox(totalValue / 4, pubkeyA, 0) - val output2 = ErgoBox(totalValue / 4, pubkeyB, 0) - val output3 = ErgoBox(totalValue / 4, pubkeyC, 0) - val output4 = ErgoBox(totalValue / 4, pubkeyD, 0) + val self = testBox(totalValue, spendingProp2, 0) + val output1 = testBox(totalValue / 4, pubkeyA, 0) + val output2 = testBox(totalValue / 4, pubkeyB, 0) + val output3 = testBox(totalValue / 4, pubkeyC, 0) + val output4 = testBox(totalValue / 4, pubkeyD, 0) val tx = mkTxFromOutputs(output1, output2, output3, output4) val ctx = mkCtx(5000, tx, self) failingProofTest(spendingProp2, ctx, coopA) @@ -220,11 +221,11 @@ class CoopExampleSpecification extends SigmaTestingCommons { * Will spend correctly if all the conditions are satisfied */ { - val self = ErgoBox(totalValue, spendingProp3, 0) - val output1 = ErgoBox(totalValue / 4, pubkeyA, 0) - val output2 = ErgoBox(totalValue / 4, pubkeyB, 0) - val output3 = ErgoBox(totalValue / 4, pubkeyC, 0) - val output4 = ErgoBox(totalValue / 4, pubkeyD, 0) + val self = testBox(totalValue, spendingProp3, 0) + val output1 = testBox(totalValue / 4, pubkeyA, 0) + val output2 = testBox(totalValue / 4, pubkeyB, 0) + val output3 = testBox(totalValue / 4, pubkeyC, 0) + val output4 = testBox(totalValue / 4, pubkeyD, 0) val tx = mkTxFromOutputs(output1, output2, output3, output4) val ctx = mkCtx(5001, tx, self) successProofTest(spendingProp2, ctx, coopA, verifier) @@ -236,13 +237,13 @@ class CoopExampleSpecification extends SigmaTestingCommons { "pubkeyC" -> pubkeyC, "pubkeyD" -> pubkeyD, "businessKey" -> businessKey, - "pubkeyTool1" -> SBoolean.mkConstant(false), - "pubkeyTool2" -> SBoolean.mkConstant(false), - "pubkeyTool3" -> SBoolean.mkConstant(false), - "pubkeyTool4" -> SBoolean.mkConstant(false), + "pubkeyTool1" -> BooleanConstant(false), + "pubkeyTool2" -> BooleanConstant(false), + "pubkeyTool3" -> BooleanConstant(false), + "pubkeyTool4" -> BooleanConstant(false), "pubkeyConstr1" -> constructionRing(0), "pubkeyConstr2" -> constructionRing(1), - "pubkeyConstr3" -> SBoolean.mkConstant(false) + "pubkeyConstr3" -> BooleanConstant(false) ) val spendingProp4 = compile(spendingEnv3, @@ -258,11 +259,11 @@ class CoopExampleSpecification extends SigmaTestingCommons { """.stripMargin).asSigmaProp { - val self = ErgoBox(totalValue, spendingProp4, 0) - val output1 = ErgoBox(totalValue / 4, pubkeyA, 0) - val output2 = ErgoBox(totalValue / 4, pubkeyB, 0) - val output3 = ErgoBox(totalValue / 4, pubkeyC, 0) - val output4 = ErgoBox(totalValue / 4, pubkeyD, 0) + val self = testBox(totalValue, spendingProp4, 0) + val output1 = testBox(totalValue / 4, pubkeyA, 0) + val output2 = testBox(totalValue / 4, pubkeyB, 0) + val output3 = testBox(totalValue / 4, pubkeyC, 0) + val output4 = testBox(totalValue / 4, pubkeyD, 0) val tx = mkTxFromOutputs(output1, output2, output3, output4) val ctx = mkCtx(5001, tx, self) successProofTest(spendingProp4, ctx, coopA, verifier) @@ -271,8 +272,8 @@ class CoopExampleSpecification extends SigmaTestingCommons { val spendingProp5 = compile(spendingEnv, "businessKey").asSigmaProp { - val self = ErgoBox(totalValue, spendingProp5, 0) - val output = ErgoBox(totalValue, businessKey, 0) + val self = testBox(totalValue, spendingProp5, 0) + val output = testBox(totalValue, businessKey, 0) val tx = mkTxFromOutputs(output) val ctx = mkCtx(1, tx, self) failingProofTest(spendingProp5, ctx, coopA) @@ -311,12 +312,12 @@ class CoopExampleSpecification extends SigmaTestingCommons { * Check votingSuccess && properSpending case */ { - val self = ErgoBox(totalValue + 1L, thresholdProp, 0) - val output1 = ErgoBox(toolValue, spendingProp1, 0) - val output2 = ErgoBox(constructionValue, spendingProp3, 0) - val output3 = ErgoBox(totalValue - toolValue - constructionValue, spendingProp5, 0) + val self = testBox(totalValue + 1L, thresholdProp, 0) + val output1 = testBox(toolValue, spendingProp1, 0) + val output2 = testBox(constructionValue, spendingProp3, 0) + val output3 = testBox(totalValue - toolValue - constructionValue, spendingProp5, 0) //hack for avoiding None.get exception. - val dummy = ErgoBox(0L, ErgoScriptPredef.TrueProp, 0) + val dummy = testBox(0L, ErgoScriptPredef.TrueProp, 0) val tx = mkTxFromOutputs(output1, output2, output3, dummy) val ctx = mkCtx(2000, tx, self) @@ -331,11 +332,11 @@ class CoopExampleSpecification extends SigmaTestingCommons { * Check withdraw success */ { - val self = ErgoBox(totalValue + 1L, thresholdProp, 0) - val output0 = ErgoBox(totalValue / 4, pubkeyA, 0) - val output1 = ErgoBox(totalValue / 4, pubkeyB, 0) - val output2 = ErgoBox(totalValue / 4, pubkeyC, 0) - val output3 = ErgoBox(totalValue / 4, pubkeyD, 0) + val self = testBox(totalValue + 1L, thresholdProp, 0) + val output0 = testBox(totalValue / 4, pubkeyA, 0) + val output1 = testBox(totalValue / 4, pubkeyB, 0) + val output2 = testBox(totalValue / 4, pubkeyC, 0) + val output3 = testBox(totalValue / 4, pubkeyD, 0) val tx = mkTxFromOutputs(output0, output1, output2, output3) val ctx = mkCtx(2000, tx, self) successProofTest(thresholdProp, ctx, business, verifier) @@ -345,11 +346,11 @@ class CoopExampleSpecification extends SigmaTestingCommons { * Check withdraw failure. Not enough height case. */ { - val self = ErgoBox(totalValue + 1L, thresholdProp, 0) - val output0 = ErgoBox(totalValue / 4, pubkeyA, 0) - val output1 = ErgoBox(totalValue / 4, pubkeyB, 0) - val output2 = ErgoBox(totalValue / 4, pubkeyC, 0) - val output3 = ErgoBox(totalValue / 4, pubkeyD, 0) + val self = testBox(totalValue + 1L, thresholdProp, 0) + val output0 = testBox(totalValue / 4, pubkeyA, 0) + val output1 = testBox(totalValue / 4, pubkeyB, 0) + val output2 = testBox(totalValue / 4, pubkeyC, 0) + val output3 = testBox(totalValue / 4, pubkeyD, 0) val tx = mkTxFromOutputs(output0, output1, output2, output3) val ctx = mkCtx(1000, tx, self) failingProofTest(thresholdProp, ctx, business) @@ -370,8 +371,8 @@ class CoopExampleSpecification extends SigmaTestingCommons { * height not higher, total value is equal */ { - val self = ErgoBox(totalValue, inputProp, 0) - val output = ErgoBox(totalValue, thresholdProp, 0) + val self = testBox(totalValue, inputProp, 0) + val output = testBox(totalValue, thresholdProp, 0) val tx = mkTxFromOutputs(output) val ctx = mkCtx(1000, tx, self) successProofTest(inputProp, ctx, coopA, verifier) @@ -381,8 +382,8 @@ class CoopExampleSpecification extends SigmaTestingCommons { * total value is lower, height is higher */ { - val self = ErgoBox(totalValue - 1L, inputProp, 0) - val output = ErgoBox(totalValue - 1L, thresholdProp, 0) + val self = testBox(totalValue - 1L, inputProp, 0) + val output = testBox(totalValue - 1L, thresholdProp, 0) val tx = mkTxFromOutputs(output) val ctx = mkCtx(1001, tx, self) successProofTest(inputProp, ctx, coopA, verifier) @@ -392,8 +393,8 @@ class CoopExampleSpecification extends SigmaTestingCommons { * negative condition */ { - val self = ErgoBox(totalValue - 1L, inputProp, 0) - val output = ErgoBox(totalValue - 1L, thresholdProp, 0) + val self = testBox(totalValue - 1L, inputProp, 0) + val output = testBox(totalValue - 1L, thresholdProp, 0) val tx = mkTxFromOutputs(output) val ctx = mkCtx(1000, tx, self) failingProofTest(inputProp, ctx, coopA) diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/DHTupleExampleSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/DHTupleExampleSpecification.scala index 3a44b051bf..7d6382ad34 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/examples/DHTupleExampleSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/DHTupleExampleSpecification.scala @@ -4,16 +4,15 @@ package sigmastate.utxo.examples import java.math.BigInteger import org.ergoplatform.ErgoBox.{R4, R5} -import org.ergoplatform.{ErgoBox, ErgoLikeContext, ErgoLikeTransaction} import sigmastate.AvlTreeData import sigmastate.Values.GroupElementConstant import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.basics.{DiffieHellmanTupleProverInput, ProveDHTuple} import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.CryptoConstants import sigmastate.interpreter.Interpreter._ import sigmastate.lang.Terms._ -import sigmastate.eval._ class DHTupleExampleSpecification extends SigmaTestingCommons { private implicit lazy val IR = new TestingIRContext @@ -53,7 +52,7 @@ class DHTupleExampleSpecification extends SigmaTestingCommons { |}""".stripMargin ).asSigmaProp - val inBox = ErgoBox(10, script, 50) + val inBox = testBox(10, script, 50) // a blockchain node verifying a block containing a spending transaction val verifier = new ErgoLikeTestInterpreter @@ -70,7 +69,7 @@ class DHTupleExampleSpecification extends SigmaTestingCommons { val carol = new ContextEnrichingTestProvingInterpreter val carolPubKey:ProveDlog = carol.dlogSecrets.head.publicImage - val outBox = ErgoBox(10, carolPubKey, 70, Nil, + val outBox = testBox(10, carolPubKey, 70, Nil, Map( R4 -> g_y, R5 -> g_xy diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/DemurrageExampleSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/DemurrageExampleSpecification.scala index 65befc4e8b..f8f7edd717 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/examples/DemurrageExampleSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/DemurrageExampleSpecification.scala @@ -5,6 +5,7 @@ import org.ergoplatform._ import sigmastate.Values.ShortConstant import sigmastate._ import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.ContextExtension import sigmastate.lang.Terms._ diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/FsmExampleSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/FsmExampleSpecification.scala index 1a2ddc93c1..d24fe63bed 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/examples/FsmExampleSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/FsmExampleSpecification.scala @@ -11,6 +11,7 @@ import sigmastate._ import sigmastate.eval._ import sigmastate.lang.Terms._ import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.{ScriptNameProp, emptyEnv} import sigmastate.serialization.ValueSerializer import sigmastate.utxo._ @@ -133,11 +134,11 @@ class FsmExampleSpecification extends SigmaTestingCommons { //creating a box in an initial state - val fsmBox1 = ErgoBox(100, fsmScript, 0, Seq(), Map(fsmDescRegister -> AvlTreeConstant(treeData), + val fsmBox1 = testBox(100, fsmScript, 0, Seq(), Map(fsmDescRegister -> AvlTreeConstant(treeData), currentStateRegister -> ByteConstant(state1Id))) //successful transition from state1 to state2 - val fsmBox2 = ErgoBox(100, fsmScript, 0, Seq(), Map(fsmDescRegister -> AvlTreeConstant(treeData), + val fsmBox2 = testBox(100, fsmScript, 0, Seq(), Map(fsmDescRegister -> AvlTreeConstant(treeData), currentStateRegister -> ByteConstant(state2Id))) avlProver.performOneOperation(Lookup(ADKey @@ (transition12 ++ script1Hash))) @@ -181,7 +182,7 @@ class FsmExampleSpecification extends SigmaTestingCommons { //Box for state3 - val fsmBox3 = ErgoBox(100, fsmScript, 0, Seq(), Map(fsmDescRegister -> AvlTreeConstant(treeData), + val fsmBox3 = testBox(100, fsmScript, 0, Seq(), Map(fsmDescRegister -> AvlTreeConstant(treeData), currentStateRegister -> ByteConstant(state3Id))) //transition from state1 to state3 is impossible @@ -235,7 +236,7 @@ class FsmExampleSpecification extends SigmaTestingCommons { //clearing FSM out of the box in the final state - val freeBox = ErgoBox(100, ErgoScriptPredef.TrueProp, 0) + val freeBox = testBox(100, ErgoScriptPredef.TrueProp, 0) avlProver.performOneOperation(Lookup(ADKey @@ (transition30 ++ script4Hash))) val transition30Proof = avlProver.generateProof() diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/IcoExample.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/IcoExample.scala index 7089cb0f3c..b38726f016 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/examples/IcoExample.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/IcoExample.scala @@ -7,10 +7,11 @@ import org.ergoplatform._ import scorex.crypto.authds.{ADKey, ADValue} import scorex.crypto.authds.avltree.batch._ import scorex.crypto.hash.{Digest32, Blake2b256} -import sigmastate.Values.{AvlTreeConstant, IntArrayConstant, CollectionConstant, ByteArrayConstant, SigmaPropValue} +import sigmastate.Values.{AvlTreeConstant, IntArrayConstant, CollectionConstant, ByteArrayConstant, SigmaPropValue, GroupElementConstant} import sigmastate._ import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeTestProvingInterpreter, SigmaTestingCommons} import sigmastate.helpers.ErgoLikeContextTesting +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.ScriptNameProp import sigmastate.lang.Terms._ import sigmastate.serialization.ErgoTreeSerializer @@ -380,13 +381,13 @@ class IcoExample extends SigmaTestingCommons { suite => val digest = avlProver.digest val initTreeData = SigmaDsl.avlTree(new AvlTreeData(digest, AvlTreeFlags.AllOperationsAllowed, 32, None)) - val projectBoxBefore = ErgoBox(10, fundingScript, 0, Seq(), + val projectBoxBefore = testBox(10, fundingScript, 0, Seq(), Map(R4 -> ByteArrayConstant(Array.fill(1)(0: Byte)), R5 -> AvlTreeConstant(initTreeData))) val funderBoxCount = 2000 val funderBoxes = (1 to funderBoxCount).map { _ => - ErgoBox(10, Values.TrueLeaf.asSigmaProp, 0, Seq(), + testBox(10, Values.TrueLeaf.asSigmaProp, 0, Seq(), Map(R4 -> ByteArrayConstant(Array.fill(32)(Random.nextInt(Byte.MaxValue).toByte)))) } @@ -401,9 +402,9 @@ class IcoExample extends SigmaTestingCommons { suite => val proof = avlProver.generateProof() val endTree = SigmaDsl.avlTree(new AvlTreeData(avlProver.digest, AvlTreeFlags.AllOperationsAllowed, 32, None)) - val projectBoxAfter = ErgoBox(funderBoxCount * 10 - 1, fundingScript, 0, Seq(), + val projectBoxAfter = testBox(funderBoxCount * 10 - 1, fundingScript, 0, Seq(), Map(R4 -> ByteArrayConstant(Array.fill(1)(0: Byte)), R5 -> AvlTreeConstant(endTree))) - val feeBox = ErgoBox(1, feeProp, 0, Seq(), Map()) + val feeBox = testBox(1, feeProp, 0, Seq(), Map()) val fundingTx = ErgoLikeTransaction(IndexedSeq(), IndexedSeq(projectBoxAfter, feeBox)) @@ -430,17 +431,17 @@ class IcoExample extends SigmaTestingCommons { suite => val digest = avlProver.digest val openTreeData = SigmaDsl.avlTree(new AvlTreeData(digest, AvlTreeFlags.AllOperationsAllowed, 32, None)) - val projectBoxBeforeClosing = ErgoBox(10, issuanceScript, 0, Seq(), + val projectBoxBeforeClosing = testBox(10, issuanceScript, 0, Seq(), Map(R4 -> ByteArrayConstant(Array.emptyByteArray), R5 -> AvlTreeConstant(openTreeData))) val tokenId = Digest32 @@ projectBoxBeforeClosing.id val closedTreeData = SigmaDsl.avlTree(new AvlTreeData(digest, AvlTreeFlags.RemoveOnly, 32, None)) - val projectBoxAfterClosing = ErgoBox(1, withdrawalScript, 0, Seq(tokenId -> projectBoxBeforeClosing.value), + val projectBoxAfterClosing = testBox(1, withdrawalScript, 0, Seq(tokenId -> projectBoxBeforeClosing.value), Map(R4 -> ByteArrayConstant(tokenId), R5 -> AvlTreeConstant(closedTreeData))) - val ergoWithdrawalBox = ErgoBox(8, Values.TrueLeaf.asSigmaProp, 0, Seq(), Map()) - val feeBox = ErgoBox(1, feeProp, 0, Seq(), Map()) + val ergoWithdrawalBox = testBox(8, Values.TrueLeaf.asSigmaProp, 0, Seq(), Map()) + val feeBox = testBox(1, feeProp, 0, Seq(), Map()) val issuanceTx = ErgoLikeTransaction(IndexedSeq(), IndexedSeq(projectBoxAfterClosing, ergoWithdrawalBox, feeBox)) @@ -462,7 +463,7 @@ class IcoExample extends SigmaTestingCommons { suite => val funderBoxCount = 2000 val funderProps = (1 to funderBoxCount).map { _ => val keyPoint = CryptoConstants.dlogGroup.createRandomElement() - val prop = CreateProveDlog(SGroupElement.mkConstant(keyPoint)).asSigmaProp + val prop = CreateProveDlog(GroupElementConstant(keyPoint)).asSigmaProp val propBytes = DefaultSerializer.serializeErgoTree(prop) propBytes -> Longs.toByteArray(Random.nextInt(Int.MaxValue).toLong) } @@ -502,16 +503,16 @@ class IcoExample extends SigmaTestingCommons { suite => } val withdrawBoxes = withdrawalAmounts.map { case (prop, tv) => - ErgoBox(1, DefaultSerializer.deserializeErgoTree(prop), 0, Seq(tokenId -> tv)) + testBox(1, DefaultSerializer.deserializeErgoTree(prop), 0, Seq(tokenId -> tv)) } val totalTokenAmount = withdrawalAmounts.map(_._2).sum + 1 - val projectBoxBefore = ErgoBox(11, withdrawalScript, 0, Seq(tokenId -> totalTokenAmount), + val projectBoxBefore = testBox(11, withdrawalScript, 0, Seq(tokenId -> totalTokenAmount), Map(R4 -> ByteArrayConstant(tokenId), R5 -> AvlTreeConstant(fundersTree))) - val projectBoxAfter = ErgoBox(1, withdrawalScript, 0, Seq(tokenId -> 1), + val projectBoxAfter = testBox(1, withdrawalScript, 0, Seq(tokenId -> 1), Map(R4 -> ByteArrayConstant(tokenId), R5 -> AvlTreeConstant(finalTree))) - val feeBox = ErgoBox(1, feeProp, 0, Seq(), Map()) + val feeBox = testBox(1, feeProp, 0, Seq(), Map()) val outputs = IndexedSeq(projectBoxAfter) ++ withdrawBoxes ++ IndexedSeq(feeBox) val fundingTx = ErgoLikeTransaction(IndexedSeq(), outputs) diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/LetsSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/LetsSpecification.scala index 3268eabad7..c3af822427 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/examples/LetsSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/LetsSpecification.scala @@ -9,6 +9,7 @@ import sigmastate.{AvlTreeData, AvlTreeFlags, TrivialProp} import sigmastate.Values.{AvlTreeConstant, ByteArrayConstant, LongConstant, SigmaPropConstant} import sigmastate.eval.{IRContext, SigmaDsl} import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestProvingInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.ScriptNameProp import sigmastate.serialization.ErgoTreeSerializer import sigmastate.lang.Terms._ @@ -283,7 +284,7 @@ class LetsSpecification extends SigmaTestingCommons { val digest = avlProver.digest val initTreeData = new AvlTreeData(digest, AvlTreeFlags.InsertOnly, 32, None) - val projectBoxBefore = ErgoBox(10, managementScript, 0, + val projectBoxBefore = testBox(10, managementScript, 0, Seq(letsTokenId -> 1L), Map(R4 -> AvlTreeConstant(SigmaDsl.avlTree(initTreeData)), R5 -> SigmaPropConstant(TrivialProp.TrueProp))) @@ -294,11 +295,11 @@ class LetsSpecification extends SigmaTestingCommons { val proof = avlProver.generateProof() val endTree = new AvlTreeData(avlProver.digest, AvlTreeFlags.InsertOnly, 32, None) - val projectBoxAfter = ErgoBox(9, managementScript, 0, + val projectBoxAfter = testBox(9, managementScript, 0, Seq(letsTokenId -> 1L), Map(R4 -> AvlTreeConstant(SigmaDsl.avlTree(endTree)), R5 -> SigmaPropConstant(TrivialProp.TrueProp))) - val feeBox = ErgoBox(1, feeProp, 0, Seq(), Map()) - val userBox = ErgoBox(1, exchangeScript, 0, Seq(userTokenId -> 1L), Map(R4 -> LongConstant(0))) + val feeBox = testBox(1, feeProp, 0, Seq(), Map()) + val userBox = testBox(1, exchangeScript, 0, Seq(userTokenId -> 1L), Map(R4 -> LongConstant(0))) val issuanceTx = ErgoLikeTransaction(IndexedSeq(), IndexedSeq(projectBoxAfter, userBox, feeBox)) @@ -333,19 +334,19 @@ class LetsSpecification extends SigmaTestingCommons { avlProver.performOneOperation(Lookup(ADKey @@ userTokenId1)) val proof = avlProver.generateProof() - val directoryBox = ErgoBox(10, managementScript, 0, + val directoryBox = testBox(10, managementScript, 0, Seq(letsTokenId -> 1L), Map(R4 -> AvlTreeConstant(SigmaDsl.avlTree(initTreeData)), R5 -> SigmaPropConstant(TrivialProp.TrueProp))) val directoryDataInput = DataInput(directoryBox.id) - val userBoxBefore0 = ErgoBox(1, exchangeScript, 0, Seq(userTokenId0 -> 1L), + val userBoxBefore0 = testBox(1, exchangeScript, 0, Seq(userTokenId0 -> 1L), Map(R4 -> LongConstant(0), R5 -> SigmaPropConstant(TrivialProp.TrueProp))) - val userBoxBefore1 = ErgoBox(1, exchangeScript, 0, Seq(userTokenId1 -> 1L), + val userBoxBefore1 = testBox(1, exchangeScript, 0, Seq(userTokenId1 -> 1L), Map(R4 -> LongConstant(0), R5 -> SigmaPropConstant(TrivialProp.TrueProp))) - val userBoxAfter0 = ErgoBox(1, exchangeScript, 0, Seq(userTokenId0 -> 1L), Map(R4 -> LongConstant(-5))) - val userBoxAfter1 = ErgoBox(1, exchangeScript, 0, Seq(userTokenId1 -> 1L), Map(R4 -> LongConstant(5))) + val userBoxAfter0 = testBox(1, exchangeScript, 0, Seq(userTokenId0 -> 1L), Map(R4 -> LongConstant(-5))) + val userBoxAfter1 = testBox(1, exchangeScript, 0, Seq(userTokenId1 -> 1L), Map(R4 -> LongConstant(5))) val issuanceTx = new ErgoLikeTransaction(IndexedSeq(), IndexedSeq(directoryDataInput), IndexedSeq(userBoxAfter0, userBoxAfter1)) diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/MASTExampleSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/MASTExampleSpecification.scala index b705017af3..a66e50993c 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/examples/MASTExampleSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/MASTExampleSpecification.scala @@ -8,6 +8,7 @@ import sigmastate.SCollection.SByteArray import sigmastate.Values._ import sigmastate._ import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.lang.Terms._ import sigmastate.interpreter.Interpreter._ import sigmastate.serialization.ValueSerializer @@ -45,9 +46,9 @@ class MASTExampleSpecification extends SigmaTestingCommons { val prop = AND(scriptIsCorrect, If(EQ(SizeOf(Inputs), 1), EQ(scriptHash, script1Hash), EQ(scriptHash, script2Hash))).toSigmaProp - val input1 = ErgoBox(20, prop, 0) + val input1 = testBox(20, prop, 0) val tx = UnsignedErgoLikeTransaction(IndexedSeq(input1).map(i => new UnsignedInput(i.id)), - IndexedSeq(ErgoBox(1, ErgoScriptPredef.TrueProp, 0))) + IndexedSeq(testBox(1, ErgoScriptPredef.TrueProp, 0))) val ctx = ErgoLikeContextTesting( currentHeight = 50, lastBlockUtxoRoot = AvlTreeData.dummy, @@ -101,13 +102,13 @@ class MASTExampleSpecification extends SigmaTestingCommons { val prop = AND(merklePathToScript, scriptIsCorrect).toSigmaProp val recipientProposition = new ContextEnrichingTestProvingInterpreter().dlogSecrets.head.publicImage - val selfBox = ErgoBox(20, ErgoScriptPredef.TrueProp, 0, Seq(), Map(reg1 -> AvlTreeConstant(treeData))) + val selfBox = testBox(20, ErgoScriptPredef.TrueProp, 0, Seq(), Map(reg1 -> AvlTreeConstant(treeData))) val ctx = ErgoLikeContextTesting( currentHeight = 50, lastBlockUtxoRoot = AvlTreeData.dummy, minerPubkey = ErgoLikeContextTesting.dummyPubkey, boxesToSpend = IndexedSeq(selfBox), - createTransaction(ErgoBox(1, recipientProposition, 0)), + createTransaction(testBox(1, recipientProposition, 0)), self = selfBox) avlProver.performOneOperation(Lookup(knownSecretTreeKey)) diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/MixExampleSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/MixExampleSpecification.scala index 98098e43c9..507f8bb10d 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/examples/MixExampleSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/MixExampleSpecification.scala @@ -2,7 +2,6 @@ package sigmastate.utxo.examples import java.math.BigInteger -import org.ergoplatform.{ErgoBox, ErgoLikeContext} import org.ergoplatform.ErgoBox.{R4, R5} import scorex.crypto.hash.Blake2b256 import sigmastate.AvlTreeData @@ -10,6 +9,7 @@ import sigmastate.Values.GroupElementConstant import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.basics.{DiffieHellmanTupleProverInput, ProveDHTuple} import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.CryptoConstants import sigmastate.interpreter.Interpreter._ import sigmastate.lang.Terms._ @@ -100,7 +100,7 @@ class MixExampleSpecification extends SigmaTestingCommons { val halfMixCreationHeight = 70 val mixAmount = 10 - val halfMixOutput = ErgoBox(mixAmount, halfMixScript, halfMixCreationHeight) + val halfMixOutput = testBox(mixAmount, halfMixScript, halfMixCreationHeight) // above halMixOutput is a Half-Mix box created by Alice. // a blockchain node verifying a block containing a spending transaction @@ -138,7 +138,7 @@ class MixExampleSpecification extends SigmaTestingCommons { val fullMixCreationHeight = 80 // if randomBit is 0 (i.e., false) below box is spendable by Alice, else by Bob - val fullMixOutput0 = ErgoBox(mixAmount, fullMixScript, fullMixCreationHeight, Nil, + val fullMixOutput0 = testBox(mixAmount, fullMixScript, fullMixCreationHeight, Nil, Map( R4 -> c0, R5 -> c1 @@ -146,7 +146,7 @@ class MixExampleSpecification extends SigmaTestingCommons { ) // if randomBit is 1 (i.e., true) below box is spendable by Alice, else by Bob - val fullMixOutput1 = ErgoBox(mixAmount, fullMixScript, fullMixCreationHeight, Nil, + val fullMixOutput1 = testBox(mixAmount, fullMixScript, fullMixCreationHeight, Nil, Map( R4 -> c1, R5 -> c0 @@ -184,7 +184,7 @@ class MixExampleSpecification extends SigmaTestingCommons { val carolPubKey: ProveDlog = carol.dlogSecrets.head.publicImage val spendHeight = 90 - val carolOutput = ErgoBox(mixAmount, carolPubKey, spendHeight) + val carolOutput = testBox(mixAmount, carolPubKey, spendHeight) // normally this transaction would be invalid, but we're not checking it in this test val spendingTx = createTransaction(carolOutput) diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala index 9e67015e57..edaf3cc23b 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala @@ -13,6 +13,7 @@ import sigmastate._ import sigmastate.eval._ import sigmastate.lang.Terms._ import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.CryptoConstants import org.ergoplatform._ import org.ergoplatform.dsl.{ContractSpec, SigmaContractSyntax, StdContracts, TestContractSpec} @@ -98,7 +99,7 @@ class OracleExamplesSpecification extends SigmaTestingCommons { suite => val z = (r + e.bigInteger.multiply(oraclePrivKey.w)).mod(group.order).bigInteger - val oracleBox = ErgoBox( + val oracleBox = testBox( value = 1L, ergoTree = oraclePubKey, creationHeight = 0, @@ -154,7 +155,7 @@ class OracleExamplesSpecification extends SigmaTestingCommons { suite => avlProver.performOneOperation(Lookup(ADKey @@ oracleBox.id)) val proof = avlProver.generateProof() - val newBox1 = ErgoBox(20, alicePubKey, 0, boxIndex = 2) + val newBox1 = testBox(20, alicePubKey, 0, boxIndex = 2) val newBoxes = IndexedSeq(newBox1) val spendingTransaction = createTransaction(newBoxes) @@ -163,14 +164,14 @@ class OracleExamplesSpecification extends SigmaTestingCommons { suite => val propAlice = withinTimeframe(sinceHeight, timeout, alicePubKey.isProven)(oracleProp).toSigmaProp - val sAlice = ErgoBox(10, propAlice, 0, Seq(), Map(), boxIndex = 3) + val sAlice = testBox(10, propAlice, 0, Seq(), Map(), boxIndex = 3) //"along with a brother" script val propAlong = AND( EQ(SizeOf(Inputs), IntConstant(2)), EQ(ExtractId(ByIndex(Inputs, 0)), ByteArrayConstant(sAlice.id))) val propBob = withinTimeframe(sinceHeight, timeout, bobPubKey.isProven)(propAlong).toSigmaProp - val sBob = ErgoBox(10, propBob, 0, Seq(), Map(), boxIndex = 4) + val sBob = testBox(10, propBob, 0, Seq(), Map(), boxIndex = 4) val ctx = ErgoLikeContextTesting( currentHeight = 50, @@ -223,7 +224,7 @@ class OracleExamplesSpecification extends SigmaTestingCommons { suite => val temperature: Long = 18 - val oracleBox = ErgoBox( + val oracleBox = testBox( value = 1L, ergoTree = oraclePubKey, creationHeight = 0, @@ -242,10 +243,10 @@ class OracleExamplesSpecification extends SigmaTestingCommons { suite => ).toSigmaProp val sOracle = oracleBox - val sAlice = ErgoBox(10, prop, 0, Seq(), Map()) - val sBob = ErgoBox(10, prop, 0, Seq(), Map()) + val sAlice = testBox(10, prop, 0, Seq(), Map()) + val sBob = testBox(10, prop, 0, Seq(), Map()) - val newBox1 = ErgoBox(20, alicePubKey, 0) + val newBox1 = testBox(20, alicePubKey, 0) val newBoxes = IndexedSeq(newBox1) val spendingTransaction = createTransaction(newBoxes) diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/RPSGameExampleSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/RPSGameExampleSpecification.scala index be1b72a08d..2679ab1aa1 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/examples/RPSGameExampleSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/RPSGameExampleSpecification.scala @@ -2,16 +2,15 @@ package sigmastate.utxo.examples import org.ergoplatform.ErgoBox.{R4, R5, R6, R7} -import org.ergoplatform._ import scorex.crypto.hash.Blake2b256 import scorex.utils.Random -import sigmastate.Values.{ByteArrayConstant, ByteConstant, IntConstant, SigmaBoolean, SigmaPropConstant} +import sigmastate.Values.{ByteArrayConstant, ByteConstant, IntConstant, SigmaPropConstant} import sigmastate._ import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter._ import sigmastate.lang.Terms._ -import sigmastate.utxo._ class RPSGameExampleSpecification extends SigmaTestingCommons { implicit lazy val IR = new TestingIRContext @@ -113,7 +112,7 @@ class RPSGameExampleSpecification extends SigmaTestingCommons { val halfGameCreationHeight = 70 val playAmount = 10 // LongConstant(10) - val halfGameOutput = ErgoBox(playAmount, halfGameScript, halfGameCreationHeight) + val halfGameOutput = testBox(playAmount, halfGameScript, halfGameCreationHeight) ///////////////////////////////////////////////////////// //// above halfGameOutput is a Half-Game "box" created by Alice. @@ -134,7 +133,7 @@ class RPSGameExampleSpecification extends SigmaTestingCommons { val bobDeadline = 120 // height after which it become's Bob's money val b:Byte = (scala.util.Random.nextInt.abs % 3).toByte - val fullGameOutput0 = ErgoBox(playAmount, fullGameScript, fullGameCreationHeight, Nil, + val fullGameOutput0 = testBox(playAmount, fullGameScript, fullGameCreationHeight, Nil, Map( R4 -> ByteConstant(b), R5 -> SigmaPropConstant(bobPubKey), @@ -143,7 +142,7 @@ class RPSGameExampleSpecification extends SigmaTestingCommons { ) ) - val fullGameOutput1 = ErgoBox(playAmount, fullGameScript, fullGameCreationHeight, Nil, + val fullGameOutput1 = testBox(playAmount, fullGameScript, fullGameCreationHeight, Nil, Map( R4 -> ByteConstant(b), R5 -> SigmaPropConstant(bobPubKey), @@ -183,7 +182,7 @@ class RPSGameExampleSpecification extends SigmaTestingCommons { val carolPubKey:ProveDlog = carol.dlogSecrets.head.publicImage // note that playAmount below is not checked. It could be anything. - val gameOverOutput = ErgoBox(playAmount, carolPubKey, gameOverHeight) + val gameOverOutput = testBox(playAmount, carolPubKey, gameOverHeight) // normally this transaction would be invalid, but we're not checking it in this test val gameOverTx = createTransaction(gameOverOutput) @@ -272,7 +271,7 @@ class RPSGameExampleSpecification extends SigmaTestingCommons { // assume Bob is paying to Carol // note that playAmount*2 below is not checked. It could be anything. - val defaultWinOutput = ErgoBox(playAmount*2, carolPubKey, defaultWinHeight) + val defaultWinOutput = testBox(playAmount*2, carolPubKey, defaultWinHeight) //normally this transaction would invalid (why?), but we're not checking it in this test val defaultWinTx = createTransaction(defaultWinOutput) diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/ReversibleTxExampleSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/ReversibleTxExampleSpecification.scala index ca736e18dc..60a07f4999 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/examples/ReversibleTxExampleSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/ReversibleTxExampleSpecification.scala @@ -6,6 +6,7 @@ import scorex.crypto.hash.Blake2b256 import sigmastate.Values.{IntConstant, SigmaPropConstant} import sigmastate._ import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.ScriptNameProp import sigmastate.lang.Terms._ @@ -125,7 +126,7 @@ class ReversibleTxExampleSpecification extends SigmaTestingCommons { // In the example, we don't create the transaction; we just create a box below - val depositOutput = ErgoBox(depositAmount, depositAddress.script, depositHeight) + val depositOutput = testBox(depositAmount, depositAddress.script, depositHeight) // Now Alice wants to give Bob some amount from the wallet in a "reversible" way. @@ -133,7 +134,7 @@ class ReversibleTxExampleSpecification extends SigmaTestingCommons { val withdrawHeight = 101 val bobDeadline = withdrawHeight+blocksIn24h - val reversibleWithdrawOutput = ErgoBox(withdrawAmount, withdrawScript, withdrawHeight, Nil, + val reversibleWithdrawOutput = testBox(withdrawAmount, withdrawScript, withdrawHeight, Nil, Map( R4 -> SigmaPropConstant(bobPubKey), R5 -> IntConstant(bobDeadline) @@ -167,7 +168,7 @@ class ReversibleTxExampleSpecification extends SigmaTestingCommons { val bobSpendAmount = 10 val bobSpendHeight = bobDeadline+1 - val bobSpendOutput = ErgoBox(bobSpendAmount, davePubKey, bobSpendHeight) + val bobSpendOutput = testBox(bobSpendAmount, davePubKey, bobSpendHeight) //normally this transaction would be invalid (why?), but we're not checking it in this test val bobSpendTx = createTransaction(bobSpendOutput) @@ -194,7 +195,7 @@ class ReversibleTxExampleSpecification extends SigmaTestingCommons { val carolSpendHeight = bobDeadline - 1 // Carol sends to Dave - val carolSpendOutput = ErgoBox(carolSpendAmount, davePubKey, carolSpendHeight) + val carolSpendOutput = testBox(carolSpendAmount, davePubKey, carolSpendHeight) //normally this transaction would be invalid (why?), but we're not checking it in this test val carolSpendTx = createTransaction(carolSpendOutput) diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/Rule110Specification.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/Rule110Specification.scala index 10e391a893..ae80e4311d 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/examples/Rule110Specification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/Rule110Specification.scala @@ -7,6 +7,7 @@ import sigmastate.Values.{BooleanConstant, ByteArrayConstant, ByteConstant, Fals import sigmastate._ import sigmastate.eval._ import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.ContextExtension import sigmastate.lang.Terms._ import sigmastate.serialization.ValueSerializer @@ -51,8 +52,8 @@ class Rule110Specification extends SigmaTestingCommons { | (OUTPUTS(0).propositionBytes == SELF.propositionBytes) }""".stripMargin).asBoolValue.toSigmaProp - val input = ErgoBox(1, prop, 0, Seq(), Map(reg1 -> ByteArrayConstant(Array[Byte](0, 1, 1, 0, 1, 0)))) - val output = ErgoBox(1, prop, 0, Seq(), Map(reg1 -> ByteArrayConstant(Array[Byte](1, 1, 1, 1, 1, 0)))) + val input = testBox(1, prop, 0, Seq(), Map(reg1 -> ByteArrayConstant(Array[Byte](0, 1, 1, 0, 1, 0)))) + val output = testBox(1, prop, 0, Seq(), Map(reg1 -> ByteArrayConstant(Array[Byte](1, 1, 1, 1, 1, 0)))) val tx = UnsignedErgoLikeTransaction(IndexedSeq(new UnsignedInput(input.id)), IndexedSeq(output)) val ctx = ErgoLikeContextTesting( @@ -206,12 +207,12 @@ class Rule110Specification extends SigmaTestingCommons { val prop = AND(scriptIsCorrect, OR(normalCaseConditions, rightmostConditions, nLeftmostConditions, leftmostConditions)).toSigmaProp // test normal case - val nIn0 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-2), YReg -> ByteConstant(0), ValReg -> t)) - val nIn1 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> t, XReg -> ByteConstant(-1), YReg -> ByteConstant(0), ValReg -> f)) - val nIn2 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(0), YReg -> ByteConstant(0), ValReg -> t)) - val nOut0 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-1), YReg -> ByteConstant(-1), ValReg -> t)) - val nOut1 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> t, XReg -> ByteConstant(-1), YReg -> ByteConstant(-1), ValReg -> t)) - val nOut2 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-1), YReg -> ByteConstant(-1), ValReg -> t)) + val nIn0 = testBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-2), YReg -> ByteConstant(0), ValReg -> t)) + val nIn1 = testBox(1, prop, 0, Seq(), Map(MidReg -> t, XReg -> ByteConstant(-1), YReg -> ByteConstant(0), ValReg -> f)) + val nIn2 = testBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(0), YReg -> ByteConstant(0), ValReg -> t)) + val nOut0 = testBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-1), YReg -> ByteConstant(-1), ValReg -> t)) + val nOut1 = testBox(1, prop, 0, Seq(), Map(MidReg -> t, XReg -> ByteConstant(-1), YReg -> ByteConstant(-1), ValReg -> t)) + val nOut2 = testBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-1), YReg -> ByteConstant(-1), ValReg -> t)) val nTx = UnsignedErgoLikeTransaction(IndexedSeq(nIn0, nIn1, nIn2).map(i => new UnsignedInput(i.id)), IndexedSeq(nOut0, nOut1, nOut2)) val nProver = new ContextEnrichingTestProvingInterpreter() @@ -229,11 +230,11 @@ class Rule110Specification extends SigmaTestingCommons { verifier.verify(prop, nCtx, nProof, fakeMessage).get._1 shouldBe true // test rightmost case - val rIn0 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-1), YReg -> ByteConstant(0), ValReg -> t)) - val rIn1 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> t, XReg -> ByteConstant(0), YReg -> ByteConstant(0), ValReg -> t)) - val rOut0 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(0), YReg -> ByteConstant(-1), ValReg -> t)) - val rOut1 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> t, XReg -> ByteConstant(0), YReg -> ByteConstant(-1), ValReg -> t)) - val rOut2 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(0), YReg -> ByteConstant(-1), ValReg -> t)) + val rIn0 = testBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-1), YReg -> ByteConstant(0), ValReg -> t)) + val rIn1 = testBox(1, prop, 0, Seq(), Map(MidReg -> t, XReg -> ByteConstant(0), YReg -> ByteConstant(0), ValReg -> t)) + val rOut0 = testBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(0), YReg -> ByteConstant(-1), ValReg -> t)) + val rOut1 = testBox(1, prop, 0, Seq(), Map(MidReg -> t, XReg -> ByteConstant(0), YReg -> ByteConstant(-1), ValReg -> t)) + val rOut2 = testBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(0), YReg -> ByteConstant(-1), ValReg -> t)) val rTx = UnsignedErgoLikeTransaction(IndexedSeq(rIn0, rIn1).map(i => new UnsignedInput(i.id)), IndexedSeq(rOut0, rOut1, rOut2)) val rProver = new ContextEnrichingTestProvingInterpreter() @@ -251,11 +252,11 @@ class Rule110Specification extends SigmaTestingCommons { verifier.verify(prop, rCtx, rProof, fakeMessage).get._1 shouldBe true // test next to leftmost case - val lnIn0 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> t, XReg -> ByteConstant(-6), YReg -> ByteConstant(-6), ValReg -> t)) - val lnIn1 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-5), YReg -> ByteConstant(-6), ValReg -> t)) - val lnOut0 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-6), YReg -> ByteConstant(-7), ValReg -> t)) - val lnOut1 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> t, XReg -> ByteConstant(-6), YReg -> ByteConstant(-7), ValReg -> t)) - val lnOut2 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-6), YReg -> ByteConstant(-7), ValReg -> t)) + val lnIn0 = testBox(1, prop, 0, Seq(), Map(MidReg -> t, XReg -> ByteConstant(-6), YReg -> ByteConstant(-6), ValReg -> t)) + val lnIn1 = testBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-5), YReg -> ByteConstant(-6), ValReg -> t)) + val lnOut0 = testBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-6), YReg -> ByteConstant(-7), ValReg -> t)) + val lnOut1 = testBox(1, prop, 0, Seq(), Map(MidReg -> t, XReg -> ByteConstant(-6), YReg -> ByteConstant(-7), ValReg -> t)) + val lnOut2 = testBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-6), YReg -> ByteConstant(-7), ValReg -> t)) val lnTx = UnsignedErgoLikeTransaction(IndexedSeq(lnIn0, lnIn1).map(i => new UnsignedInput(i.id)), IndexedSeq(lnOut0, lnOut1, lnOut2)) val lnProver = new ContextEnrichingTestProvingInterpreter() @@ -273,10 +274,10 @@ class Rule110Specification extends SigmaTestingCommons { verifier.verify(prop, lnCtx, lnProof, fakeMessage).get._1 shouldBe true // test leftmost case - val lIn0 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-6), YReg -> ByteConstant(-6), ValReg -> t)) - val lOut0 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-7), YReg -> ByteConstant(-7), ValReg -> t)) - val lOut1 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> t, XReg -> ByteConstant(-7), YReg -> ByteConstant(-7), ValReg -> t)) - val lOut2 = ErgoBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-7), YReg -> ByteConstant(-7), ValReg -> t)) + val lIn0 = testBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-6), YReg -> ByteConstant(-6), ValReg -> t)) + val lOut0 = testBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-7), YReg -> ByteConstant(-7), ValReg -> t)) + val lOut1 = testBox(1, prop, 0, Seq(), Map(MidReg -> t, XReg -> ByteConstant(-7), YReg -> ByteConstant(-7), ValReg -> t)) + val lOut2 = testBox(1, prop, 0, Seq(), Map(MidReg -> f, XReg -> ByteConstant(-7), YReg -> ByteConstant(-7), ValReg -> t)) val lTx = UnsignedErgoLikeTransaction(IndexedSeq(lIn0).map(i => new UnsignedInput(i.id)), IndexedSeq(lOut0, lOut1, lOut2)) val lProver = new ContextEnrichingTestProvingInterpreter() @@ -400,7 +401,7 @@ class Rule110Specification extends SigmaTestingCommons { val row = RowReg -> LongConstant(0) val column = ColumnReg -> LongConstant(col) val value = if (col == 15) ValueReg -> TrueLeaf else ValueReg -> FalseLeaf - ErgoBox(0L, prop, 0, Nil, Map(row, column, value), txId.toModifierId, col.toShort) + testBox(0L, prop, 0, Nil, Map(row, column, value), txId.toModifierId, col.toShort) } val initBlock = FullBlock( diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/TimedPaymentExampleSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/TimedPaymentExampleSpecification.scala index 5b9b1300ac..ad0e561324 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/examples/TimedPaymentExampleSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/TimedPaymentExampleSpecification.scala @@ -1,14 +1,13 @@ package sigmastate.utxo.examples -import org.ergoplatform.ErgoBox.{R4, R5} import org.ergoplatform._ -import sigmastate.Values.{ByteArrayConstant, ByteConstant, IntConstant} +import sigmastate.Values.IntConstant import sigmastate._ -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, SigmaTestingCommons, ErgoLikeTestInterpreter} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.ScriptNameProp import sigmastate.lang.Terms._ import sigmastate.lang.exceptions.InterpreterException -import sigmastate.utxo._ class TimedPaymentExampleSpecification extends SigmaTestingCommons { @@ -45,7 +44,7 @@ class TimedPaymentExampleSpecification extends SigmaTestingCommons { // In the example, we don't create the transaction; we just create a box below - val depositOutput = ErgoBox(depositAmount, address.script, depositHeight) + val depositOutput = testBox(depositAmount, address.script, depositHeight) // Now Alice wants to give Bob (coffee shop owner) some amount from the wallet in a "timed" way. @@ -53,7 +52,7 @@ class TimedPaymentExampleSpecification extends SigmaTestingCommons { val withdrawHeight = 100 val confDeadline = 110 - val timedWithdrawOutput = ErgoBox(withdrawAmount, bobPubKey, withdrawHeight) + val timedWithdrawOutput = testBox(withdrawAmount, bobPubKey, withdrawHeight) //normally this transaction would be invalid, but we're not checking it in this test val withdrawTx = createTransaction(IndexedSeq(timedWithdrawOutput)) diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/TrustlessLETS.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/TrustlessLETS.scala index 5ec77e0717..501e5d10e8 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/examples/TrustlessLETS.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/TrustlessLETS.scala @@ -1,9 +1,9 @@ package sigmastate.utxo.examples -import org.ergoplatform._ import scorex.crypto.hash.Blake2b256 import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter._ import sigmastate.lang.Terms._ @@ -107,7 +107,7 @@ class TrustlessLETS1 extends SigmaTestingCommons { val tokenBoxCreationHeight = 70 val tokenAmount = 10 // LongConstant(10) - val tokenBoxOutput = ErgoBox(tokenAmount, tokenScript, tokenBoxCreationHeight) + val tokenBoxOutput = testBox(tokenAmount, tokenScript, tokenBoxCreationHeight) } @@ -209,7 +209,7 @@ class TrustlessLETS2 extends SigmaTestingCommons { val tokenBoxCreationHeight = 70 val tokenAmount = 10 // LongConstant(10) - val tokenBoxOutput = ErgoBox(tokenAmount, tokenScript, tokenBoxCreationHeight) + val tokenBoxOutput = testBox(tokenAmount, tokenScript, tokenBoxCreationHeight) } @@ -323,7 +323,7 @@ class TrustlessLETS3 extends SigmaTestingCommons { val tokenBoxCreationHeight = 70 val tokenAmount = 10 // LongConstant(10) - val tokenBoxOutput = ErgoBox(tokenAmount, tokenScript, tokenBoxCreationHeight) + val tokenBoxOutput = testBox(tokenAmount, tokenScript, tokenBoxCreationHeight) } @@ -430,7 +430,7 @@ class TrustlessLETS4 extends SigmaTestingCommons { val tokenBoxCreationHeight = 70 val tokenAmount = 10 // LongConstant(10) - val tokenBoxOutput = ErgoBox(tokenAmount, tokenScript, tokenBoxCreationHeight) + val tokenBoxOutput = testBox(tokenAmount, tokenScript, tokenBoxCreationHeight) } diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/XorGameExampleSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/XorGameExampleSpecification.scala index 606d64f17a..46d67b19d3 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/examples/XorGameExampleSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/XorGameExampleSpecification.scala @@ -2,16 +2,15 @@ package sigmastate.utxo.examples import org.ergoplatform.ErgoBox.{R4, R5, R6} -import org.ergoplatform._ import scorex.crypto.hash.Blake2b256 import scorex.utils.Random import sigmastate.Values.{ByteArrayConstant, ByteConstant, IntConstant, SigmaPropConstant} import sigmastate._ import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaTestingCommons} +import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter._ import sigmastate.lang.Terms._ -import sigmastate.utxo._ class XorGameExampleSpecification extends SigmaTestingCommons { private implicit lazy val IR: TestingIRContext = new TestingIRContext @@ -107,7 +106,7 @@ class XorGameExampleSpecification extends SigmaTestingCommons { val halfGameCreationHeight = 70 val playAmount = 10 // LongConstant(10) - val halfGameOutput = ErgoBox(playAmount, halfGameScript, halfGameCreationHeight) + val halfGameOutput = testBox(playAmount, halfGameScript, halfGameCreationHeight) ///////////////////////////////////////////////////////// //// above halfGameOutput is a Half-Game "box" created by Alice. @@ -133,7 +132,7 @@ class XorGameExampleSpecification extends SigmaTestingCommons { // // val abortHalfGameOutput = ErgoBox(playAmount, carolPubKey, abortHalfGameHeight) // gives error - val abortHalfGameOutput = ErgoBox(playAmount, carolPubKey, abortHalfGameHeight, Nil, + val abortHalfGameOutput = testBox(playAmount, carolPubKey, abortHalfGameHeight, Nil, Map( R4 -> ByteConstant(0), // dummy data. Has to be given, even though not needed as per halfGameScript R5 -> SigmaPropConstant((new ContextEnrichingTestProvingInterpreter).dlogSecrets.head.publicImage), // dummy statement @@ -169,7 +168,7 @@ class XorGameExampleSpecification extends SigmaTestingCommons { val bobDeadline = 120 // height after which it become's Bob's money val b:Byte = if (scala.util.Random.nextBoolean) 0x01 else 0x00 - val fullGameOutput = ErgoBox(playAmount*2, fullGameScript, fullGameCreationHeight, Nil, + val fullGameOutput = testBox(playAmount*2, fullGameScript, fullGameCreationHeight, Nil, Map( R4 -> ByteConstant(b), R5 -> SigmaPropConstant(bobPubKey), @@ -225,7 +224,7 @@ class XorGameExampleSpecification extends SigmaTestingCommons { // assume winner is paying to Carol // note that playAmount*2 below is not checked. It could be anything. - val gameOverOutput = ErgoBox(playAmount*2, carolPubKey, gameOverHeight) + val gameOverOutput = testBox(playAmount*2, carolPubKey, gameOverHeight) //normally this transaction would invalid (why?), but we're not checking it in this test val gameOverTx = createTransaction(gameOverOutput) @@ -252,7 +251,7 @@ class XorGameExampleSpecification extends SigmaTestingCommons { // assume Bob is paying to Carol // note that playAmount*2 below is not checked. It could be anything. - val defaultWinOutput = ErgoBox(playAmount*2, carolPubKey, defaultWinHeight) + val defaultWinOutput = testBox(playAmount*2, carolPubKey, defaultWinHeight) //normally this transaction would invalid (why?), but we're not checking it in this test val defaultWinTx = createTransaction(defaultWinOutput) diff --git a/sigmastate/src/test/scala/special/sigma/ContractsTestkit.scala b/sigmastate/src/test/scala/special/sigma/ContractsTestkit.scala index 8e67330bc9..a01720b0d1 100644 --- a/sigmastate/src/test/scala/special/sigma/ContractsTestkit.scala +++ b/sigmastate/src/test/scala/special/sigma/ContractsTestkit.scala @@ -1,12 +1,12 @@ package special.sigma -import org.ergoplatform.ErgoBox import scalan._ import special.collection.{Coll, CollOverArrayBuilder} import scalan.RType import sigmastate.{AvlTreeData, TrivialProp} import sigmastate.eval._ import sigmastate.eval.Extensions._ +import sigmastate.helpers.TestingHelpers._ trait ContractsTestkit { val R0 = 0.toByte; @@ -57,7 +57,7 @@ trait ContractsTestkit { val AliceId = Array[Byte](1) // 0x0001 def newAliceBox(id: Byte, value: Long): Box = { - val ergoBox = ErgoBox(value, TrivialProp.TrueProp.toSigmaProp, 0, Seq(), Map()) + val ergoBox = testBox(value, TrivialProp.TrueProp.toSigmaProp, 0, Seq(), Map()) new CostingBox(false, ergoBox) } diff --git a/sigmastate/src/test/scala/special/sigma/SigmaDslSpec.scala b/sigmastate/src/test/scala/special/sigma/SigmaDslSpecification.scala similarity index 55% rename from sigmastate/src/test/scala/special/sigma/SigmaDslSpec.scala rename to sigmastate/src/test/scala/special/sigma/SigmaDslSpecification.scala index 52c36afa65..86b996700f 100644 --- a/sigmastate/src/test/scala/special/sigma/SigmaDslSpec.scala +++ b/sigmastate/src/test/scala/special/sigma/SigmaDslSpecification.scala @@ -1,5 +1,6 @@ package special.sigma +import java.lang.reflect.InvocationTargetException import java.math.BigInteger import org.ergoplatform.ErgoScriptPredef.TrueProp @@ -27,6 +28,8 @@ import sigmastate.utxo._ import special.collection._ import sigmastate.serialization.OpCodes.OpCode import sigmastate.utils.Helpers +import sigmastate.utils.Helpers._ +import sigmastate.helpers.TestingHelpers._ import scala.reflect.ClassTag import scala.util.{DynamicVariable, Success, Failure, Try} @@ -39,10 +42,12 @@ import scala.math.Ordering /** This suite tests every method of every SigmaDsl type to be equivalent to * the evaluation of the corresponding ErgoScript operation */ -class SigmaDslSpec extends SigmaDslTesting { suite => +class SigmaDslSpecification extends SigmaDslTesting { suite => override implicit val generatorDrivenConfig = PropertyCheckConfiguration(minSuccessful = 30) + implicit def IR = createIR() + ///===================================================== /// Boolean type operations ///----------------------------------------------------- @@ -69,12 +74,12 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) )) val cases = Seq( - ((true, true), Try(false)), - ((true, false), Try(true)), - ((false, false), Try(false)), - ((false, true), Try(true)) + (true, true) -> Success(Expected(false, 36518)), + (true, false) -> Success(Expected(true, 36518)), + (false, false) -> Success(Expected(false, 36518)), + (false, true) -> Success(Expected(true, 36518)) ) - testCases(cases, binXor) + verifyCases(cases, binXor) } property("BinXor(logical XOR) test") { @@ -91,14 +96,14 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) )) val cases = Seq( - ((1095564593, true), Success(true)), - ((-901834021, true), Success(true)), - ((595045530, false), Success(false)), - ((-1157998227, false), Success(false)), - ((0, true), Success(false)), - ((0, false), Success(true)) + (1095564593, true) -> Success(Expected(true, 36865)), + (-901834021, true) -> Success(Expected(true, 36865)), + (595045530, false) -> Success(Expected(false, 36865)), + (-1157998227, false) -> Success(Expected(false, 36865)), + (0, true) -> Success(Expected(false, 36865)), + (0, false) -> Success(Expected(true, 36865)) ) - testCases(cases, xor) + verifyCases(cases, xor) } property("&& boolean equivalence") { @@ -112,12 +117,12 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) )) val cases = Seq( - ((false, true), Success(false)), - ((false, false), Success(false)), - ((true, true), Success(true)), - ((true, false), Success(false)) + (false, true) -> Success(Expected(false, 38241)), + (false, false) -> Success(Expected(false, 38241)), + (true, true) -> Success(Expected(true, 38241)), + (true, false) -> Success(Expected(false, 38241)) ) - testCases(cases, eq) + verifyCases(cases, eq) } property("|| boolean equivalence") { @@ -131,18 +136,18 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) )) val cases = Seq( - ((true, false), Success(true)), - ((true, true), Success(true)), - ((false, false), Success(false)), - ((false, true), Success(true)) + (true, false) -> Success(Expected(true, 38241)), + (true, true) -> Success(Expected(true, 38241)), + (false, false) -> Success(Expected(false, 38241)), + (false, true) -> Success(Expected(true, 38241)) ) - testCases(cases, eq) + verifyCases(cases, eq) } property("lazy || and && boolean equivalence") { - testCases( + verifyCases( Seq( - (true, Success(true)), + (true, Success(Expected(true, 38467))), (false, Failure(new ArithmeticException("/ by zero"))) ), existingFeature((x: Boolean) => x || (1 / 0 == 1), @@ -155,10 +160,10 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ))) - testCases( + verifyCases( Seq( (true, Failure(new ArithmeticException("/ by zero"))), - (false, Success(false)) + (false, Success(Expected(false, 38467))) ), existingFeature((x: Boolean) => x && (1 / 0 == 1), "{ (x: Boolean) => x && (1 / 0 == 1) }", @@ -170,10 +175,10 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ))) - testCases( + verifyCases( Seq( - (false, Success(false)), - (true, Success(true)) + (false, Success(Expected(false, 40480))), + (true, Success(Expected(true, 40480))) ), existingFeature((x: Boolean) => x && (x || (1 / 0 == 1)), "{ (x: Boolean) => x && (x || (1 / 0 == 1)) }", @@ -188,10 +193,10 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ))) - testCases( + verifyCases( Seq( - (false, Success(false)), - (true, Success(true)) + (false, Success(Expected(false, 42493))), + (true, Success(Expected(true, 42493))) ), existingFeature((x: Boolean) => x && (x && (x || (1 / 0 == 1))), "{ (x: Boolean) => x && (x && (x || (1 / 0 == 1))) }", @@ -209,10 +214,10 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ))) - testCases( + verifyCases( Seq( - (false, Success(false)), - (true, Success(true)) + (false, Success(Expected(false, 44506))), + (true, Success(Expected(true, 44506))) ), existingFeature((x: Boolean) => x && (x && (x && (x || (1 / 0 == 1)))), "{ (x: Boolean) => x && (x && (x && (x || (1 / 0 == 1)))) }", @@ -233,10 +238,10 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ))) - testCases( + verifyCases( Seq( (false, Failure(new ArithmeticException("/ by zero"))), - (true, Success(true)) + (true, Success(Expected(true, 43281))) ), existingFeature((x: Boolean) => !(!x && (1 / 0 == 1)) && (x || (1 / 0 == 1)), "{ (x: Boolean) => !(!x && (1 / 0 == 1)) && (x || (1 / 0 == 1)) }", @@ -256,9 +261,9 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ))) - testCases( + verifyCases( Seq( - (true, Success(true)), + (true, Success(Expected(true, 40480))), (false, Failure(new ArithmeticException("/ by zero"))) ), existingFeature((x: Boolean) => (x || (1 / 0 == 1)) && x, @@ -274,9 +279,9 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ))) - testCases( + verifyCases( Seq( - (true, Success(true)), + (true, Success(Expected(true, 43149))), (false, Failure(new ArithmeticException("/ by zero"))) ), existingFeature((x: Boolean) => (x || (1 / 0 == 1)) && (x || (1 / 0 == 1)), @@ -295,9 +300,9 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ))) - testCases( + verifyCases( Seq( - (true, Success(true)), + (true, Success(Expected(true, 45950))), (false, Failure(new ArithmeticException("/ by zero"))) ), existingFeature( @@ -322,10 +327,10 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ))) - testCases( + verifyCases( Seq( (false, Failure(new ArithmeticException("/ by zero"))), - (true, Success(true)) + (true, Success(Expected(true, 48862))) ), existingFeature( (x: Boolean) => (!(!x && (1 / 0 == 1)) || (1 / 0 == 0)) && (!(!x && (1 / 0 == 1)) || (1 / 0 == 1)), @@ -359,110 +364,131 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("Byte methods equivalence") { - testCases( - Seq( - (0.toByte, Success(0.toByte)), - (1.toByte, Success(1.toByte)), - (55.toByte, Success(55.toByte)), - (Byte.MaxValue, Success(Byte.MaxValue)), - (-1.toByte, Success(-1.toByte)), - (-65.toByte, Success(-65.toByte)), - (Byte.MinValue, Success(Byte.MinValue)) - ), + SByte.upcast(0.toByte) shouldBe 0.toByte // boundary test case + SByte.downcast(0.toByte) shouldBe 0.toByte // boundary test case + + verifyCases( + { + def expect(v: Byte) = Success(Expected(v, 35798)) + Seq( + (0.toByte, expect(0.toByte)), + (1.toByte, expect(1.toByte)), + (55.toByte, expect(55.toByte)), + (Byte.MaxValue, expect(Byte.MaxValue)), + (-1.toByte, expect(-1.toByte)), + (-65.toByte, expect(-65.toByte)), + (Byte.MinValue, expect(Byte.MinValue)) + ) + }, existingFeature( (x: Byte) => x.toByte, "{ (x: Byte) => x.toByte }", FuncValue(Vector((1, SByte)), ValUse(1, SByte)))) - testCases( - Seq( - (0.toByte, Success(0.toShort)), - (1.toByte, Success(1.toShort)), - (55.toByte, Success(55.toShort)), - (Byte.MaxValue, Success(Byte.MaxValue.toShort)), - (-1.toByte, Success(-1.toShort)), - (-65.toByte, Success(-65.toShort)), - (Byte.MinValue, Success(Byte.MinValue.toShort)) - ), + verifyCases( + { + def expected(v: Short) = Success(Expected(v, 35902)) + Seq( + (0.toByte, expected(0.toShort)), + (1.toByte, expected(1.toShort)), + (55.toByte, expected(55.toShort)), + (Byte.MaxValue, expected(Byte.MaxValue.toShort)), + (-1.toByte, expected(-1.toShort)), + (-65.toByte, expected(-65.toShort)), + (Byte.MinValue, expected(Byte.MinValue.toShort)) + ) + }, existingFeature( (x: Byte) => x.toShort, "{ (x: Byte) => x.toShort }", FuncValue(Vector((1, SByte)), Upcast(ValUse(1, SByte), SShort)))) - testCases( - Seq( - (0.toByte, Success(0)), - (1.toByte, Success(1)), - (55.toByte, Success(55)), - (Byte.MaxValue, Success(Byte.MaxValue.toInt)), - (-1.toByte, Success(-1)), - (-65.toByte, Success(-65)), - (Byte.MinValue, Success(Byte.MinValue.toInt)) - ), + verifyCases( + { + def expected(v: Int) = Success(Expected(v, 35902)) + Seq( + (0.toByte, expected(0)), + (1.toByte, expected(1)), + (55.toByte, expected(55)), + (Byte.MaxValue, expected(Byte.MaxValue.toInt)), + (-1.toByte, expected(-1)), + (-65.toByte, expected(-65)), + (Byte.MinValue, expected(Byte.MinValue.toInt)) + ) + }, existingFeature( (x: Byte) => x.toInt, "{ (x: Byte) => x.toInt }", FuncValue(Vector((1, SByte)), Upcast(ValUse(1, SByte), SInt)))) - testCases( - Seq( - (0.toByte, Success(0L)), - (1.toByte, Success(1L)), - (55.toByte, Success(55L)), - (Byte.MaxValue, Success(Byte.MaxValue.toLong)), - (-1.toByte, Success(-1L)), - (-65.toByte, Success(-65L)), - (Byte.MinValue, Success(Byte.MinValue.toLong)) - ), + verifyCases( + { + def expected(v: Long) = Success(Expected(v, 35902)) + Seq( + (0.toByte, expected(0L)), + (1.toByte, expected(1L)), + (55.toByte, expected(55L)), + (Byte.MaxValue, expected(Byte.MaxValue.toLong)), + (-1.toByte, expected(-1L)), + (-65.toByte, expected(-65L)), + (Byte.MinValue, expected(Byte.MinValue.toLong)) + ) + }, existingFeature( (x: Byte) => x.toLong, "{ (x: Byte) => x.toLong }", FuncValue(Vector((1, SByte)), Upcast(ValUse(1, SByte), SLong)))) - testCases( - Seq( - (0.toByte, Success(CBigInt(new BigInteger("0", 16)))), - (1.toByte, Success(CBigInt(new BigInteger("1", 16)))), - (-1.toByte, Success(CBigInt(new BigInteger("-1", 16)))), - (127.toByte, Success(CBigInt(new BigInteger("7f", 16)))), - (-128.toByte, Success(CBigInt(new BigInteger("-80", 16)))), - (90.toByte, Success(CBigInt(new BigInteger("5a", 16)))), - (-53.toByte, Success(CBigInt(new BigInteger("-35", 16)))) - ), + verifyCases( + { + def expected(v: BigInt) = Success(Expected(v, 35932)) + Seq( + (0.toByte, expected(CBigInt(new BigInteger("0", 16)))), + (1.toByte, expected(CBigInt(new BigInteger("1", 16)))), + (-1.toByte, expected(CBigInt(new BigInteger("-1", 16)))), + (127.toByte, expected(CBigInt(new BigInteger("7f", 16)))), + (-128.toByte, expected(CBigInt(new BigInteger("-80", 16)))), + (90.toByte, expected(CBigInt(new BigInteger("5a", 16)))), + (-53.toByte, expected(CBigInt(new BigInteger("-35", 16)))) + ) + }, existingFeature( (x: Byte) => x.toBigInt, "{ (x: Byte) => x.toBigInt }", FuncValue(Vector((1, SByte)), Upcast(ValUse(1, SByte), SBigInt)))) val n = ExactNumeric.ByteIsExactNumeric - testCases( - Seq( - ((-128.toByte, -128.toByte), Failure(new ArithmeticException("Byte overflow"))), - ((-128.toByte, 0.toByte), Failure(new ArithmeticException("/ by zero"))), - ((-128.toByte, 17.toByte), Failure(new ArithmeticException("Byte overflow"))), - ((-128.toByte, 127.toByte), Failure(new ArithmeticException("Byte overflow"))), - ((-120.toByte, 82.toByte), Failure(new ArithmeticException("Byte overflow"))), - ((-103.toByte, 1.toByte), Success((-102.toByte, (-104.toByte, (-103.toByte, (-103.toByte, 0.toByte)))))), - ((-90.toByte, 37.toByte), Failure(new ArithmeticException("Byte overflow"))), - ((-78.toByte, -111.toByte), Failure(new ArithmeticException("Byte overflow"))), - ((-71.toByte, -44.toByte), Failure(new ArithmeticException("Byte overflow"))), - ((-53.toByte, 0.toByte), Failure(new ArithmeticException("/ by zero"))), - ((-34.toByte, 8.toByte), Failure(new ArithmeticException("Byte overflow"))), - ((-24.toByte, 127.toByte), Failure(new ArithmeticException("Byte overflow"))), - ((-1.toByte, -1.toByte), Success((-2.toByte, (0.toByte, (1.toByte, (1.toByte, 0.toByte)))))), - ((-1.toByte, 23.toByte), Success((22.toByte, (-24.toByte, (-23.toByte, (0.toByte, -1.toByte)))))), - ((0.toByte, -128.toByte), Failure(new ArithmeticException("Byte overflow"))), - ((0.toByte, -23.toByte), Success((-23.toByte, (23.toByte, (0.toByte, (0.toByte, 0.toByte)))))), - ((0.toByte, -1.toByte), Success((-1.toByte, (1.toByte, (0.toByte, (0.toByte, 0.toByte)))))), - ((0.toByte, 0.toByte), Failure(new ArithmeticException("/ by zero"))), - ((0.toByte, 1.toByte), Success((1.toByte, (-1.toByte, (0.toByte, (0.toByte, 0.toByte)))))), - ((0.toByte, 60.toByte), Success((60.toByte, (-60.toByte, (0.toByte, (0.toByte, 0.toByte)))))), - ((0.toByte, 127.toByte), Success((127.toByte, (-127.toByte, (0.toByte, (0.toByte, 0.toByte)))))), - ((1.toByte, -1.toByte), Success((0.toByte, (2.toByte, (-1.toByte, (-1.toByte, 0.toByte)))))), - ((1.toByte, 0.toByte), Failure(new ArithmeticException("/ by zero"))), - ((1.toByte, 26.toByte), Success((27.toByte, (-25.toByte, (26.toByte, (0.toByte, 1.toByte)))))), - ((7.toByte, -32.toByte), Failure(new ArithmeticException("Byte overflow"))), - ((33.toByte, 1.toByte), Success((34.toByte, (32.toByte, (33.toByte, (33.toByte, 0.toByte)))))), - ((90.toByte, 0.toByte), Failure(new ArithmeticException("/ by zero"))), - ((127.toByte, -128.toByte), Failure(new ArithmeticException("Byte overflow"))), - ((127.toByte, -47.toByte), Failure(new ArithmeticException("Byte overflow"))), - ((127.toByte, 127.toByte), Failure(new ArithmeticException("Byte overflow"))) - ), + verifyCases( + { + def success[T](v: (T, (T, (T, (T, T))))) = Success(Expected(v, 39654)) + Seq( + ((-128.toByte, -128.toByte), Failure(new ArithmeticException("Byte overflow"))), + ((-128.toByte, 0.toByte), Failure(new ArithmeticException("/ by zero"))), + ((-128.toByte, 17.toByte), Failure(new ArithmeticException("Byte overflow"))), + ((-128.toByte, 127.toByte), Failure(new ArithmeticException("Byte overflow"))), + ((-120.toByte, 82.toByte), Failure(new ArithmeticException("Byte overflow"))), + ((-103.toByte, 1.toByte), success((-102.toByte, (-104.toByte, (-103.toByte, (-103.toByte, 0.toByte)))))), + ((-90.toByte, 37.toByte), Failure(new ArithmeticException("Byte overflow"))), + ((-78.toByte, -111.toByte), Failure(new ArithmeticException("Byte overflow"))), + ((-71.toByte, -44.toByte), Failure(new ArithmeticException("Byte overflow"))), + ((-53.toByte, 0.toByte), Failure(new ArithmeticException("/ by zero"))), + ((-34.toByte, 8.toByte), Failure(new ArithmeticException("Byte overflow"))), + ((-24.toByte, 127.toByte), Failure(new ArithmeticException("Byte overflow"))), + ((-1.toByte, -1.toByte), success((-2.toByte, (0.toByte, (1.toByte, (1.toByte, 0.toByte)))))), + ((-1.toByte, 23.toByte), success((22.toByte, (-24.toByte, (-23.toByte, (0.toByte, -1.toByte)))))), + ((0.toByte, -128.toByte), Failure(new ArithmeticException("Byte overflow"))), + ((0.toByte, -23.toByte), success((-23.toByte, (23.toByte, (0.toByte, (0.toByte, 0.toByte)))))), + ((0.toByte, -1.toByte), success((-1.toByte, (1.toByte, (0.toByte, (0.toByte, 0.toByte)))))), + ((0.toByte, 0.toByte), Failure(new ArithmeticException("/ by zero"))), + ((0.toByte, 1.toByte), success((1.toByte, (-1.toByte, (0.toByte, (0.toByte, 0.toByte)))))), + ((0.toByte, 60.toByte), success((60.toByte, (-60.toByte, (0.toByte, (0.toByte, 0.toByte)))))), + ((0.toByte, 127.toByte), success((127.toByte, (-127.toByte, (0.toByte, (0.toByte, 0.toByte)))))), + ((1.toByte, -1.toByte), success((0.toByte, (2.toByte, (-1.toByte, (-1.toByte, 0.toByte)))))), + ((1.toByte, 0.toByte), Failure(new ArithmeticException("/ by zero"))), + ((1.toByte, 26.toByte), success((27.toByte, (-25.toByte, (26.toByte, (0.toByte, 1.toByte)))))), + ((7.toByte, -32.toByte), Failure(new ArithmeticException("Byte overflow"))), + ((33.toByte, 1.toByte), success((34.toByte, (32.toByte, (33.toByte, (33.toByte, 0.toByte)))))), + ((90.toByte, 0.toByte), Failure(new ArithmeticException("/ by zero"))), + ((127.toByte, -128.toByte), Failure(new ArithmeticException("Byte overflow"))), + ((127.toByte, -47.toByte), Failure(new ArithmeticException("Byte overflow"))), + ((127.toByte, 127.toByte), Failure(new ArithmeticException("Byte overflow"))) + ) + }, existingFeature( { (x: (Byte, Byte)) => val a = x._1; val b = x._2 @@ -549,108 +575,129 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("Short methods equivalence") { - testCases( - Seq( - (Short.MinValue, Failure(new ArithmeticException("Byte overflow"))), - (-21626.toShort, Failure(new ArithmeticException("Byte overflow"))), - (Byte.MinValue.toShort, Success(Byte.MinValue)), - (-1.toShort, Success(-1.toByte)), - (0.toShort, Success(0.toByte)), - (1.toShort, Success(1.toByte)), - (Byte.MaxValue.toShort, Success(Byte.MaxValue)), - (11768.toShort, Failure(new ArithmeticException("Byte overflow"))), - (Short.MaxValue, Failure(new ArithmeticException("Byte overflow"))) - ), + SShort.upcast(0.toShort) shouldBe 0.toShort // boundary test case + SShort.downcast(0.toShort) shouldBe 0.toShort // boundary test case + + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35976)) + Seq( + (Short.MinValue, Failure(new ArithmeticException("Byte overflow"))), + (-21626.toShort, Failure(new ArithmeticException("Byte overflow"))), + (Byte.MinValue.toShort, success(Byte.MinValue)), + (-1.toShort, success(-1.toByte)), + (0.toShort, success(0.toByte)), + (1.toShort, success(1.toByte)), + (Byte.MaxValue.toShort, success(Byte.MaxValue)), + (11768.toShort, Failure(new ArithmeticException("Byte overflow"))), + (Short.MaxValue, Failure(new ArithmeticException("Byte overflow"))) + ) + }, existingFeature((x: Short) => x.toByteExact, "{ (x: Short) => x.toByte }", FuncValue(Vector((1, SShort)), Downcast(ValUse(1, SShort), SByte)))) - testCases( - Seq( - (-32768.toShort, Success(-32768.toShort)), - (-27798.toShort, Success(-27798.toShort)), - (-1.toShort, Success(-1.toShort)), - (0.toShort, Success(0.toShort)), - (1.toShort, Success(1.toShort)), - (27929.toShort, Success(27929.toShort)), - (32767.toShort, Success(32767.toShort)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35798)) + Seq( + (-32768.toShort, success(-32768.toShort)), + (-27798.toShort, success(-27798.toShort)), + (-1.toShort, success(-1.toShort)), + (0.toShort, success(0.toShort)), + (1.toShort, success(1.toShort)), + (27929.toShort, success(27929.toShort)), + (32767.toShort, success(32767.toShort)) + ) + }, existingFeature((x: Short) => x.toShort, "{ (x: Short) => x.toShort }", FuncValue(Vector((1, SShort)), ValUse(1, SShort)))) - testCases( - Seq( - (-32768.toShort, Success(-32768)), - (-21064.toShort, Success(-21064)), - (-1.toShort, Success(-1)), - (0.toShort, Success(0)), - (1.toShort, Success(1)), - (18388.toShort, Success(18388)), - (32767.toShort, Success(32767)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35902)) + Seq( + (-32768.toShort, success(-32768)), + (-21064.toShort, success(-21064)), + (-1.toShort, success(-1)), + (0.toShort, success(0)), + (1.toShort, success(1)), + (18388.toShort, success(18388)), + (32767.toShort, success(32767)) + ) + }, existingFeature((x: Short) => x.toInt, "{ (x: Short) => x.toInt }", FuncValue(Vector((1, SShort)), Upcast(ValUse(1, SShort), SInt)))) - testCases( - Seq( - (-32768.toShort, Success(-32768L)), - (-23408.toShort, Success(-23408L)), - (-1.toShort, Success(-1L)), - (0.toShort, Success(0L)), - (1.toShort, Success(1L)), - (23318.toShort, Success(23318L)), - (32767.toShort, Success(32767L)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35902)) + Seq( + (-32768.toShort, success(-32768L)), + (-23408.toShort, success(-23408L)), + (-1.toShort, success(-1L)), + (0.toShort, success(0L)), + (1.toShort, success(1L)), + (23318.toShort, success(23318L)), + (32767.toShort, success(32767L)) + ) + }, existingFeature((x: Short) => x.toLong, "{ (x: Short) => x.toLong }", FuncValue(Vector((1, SShort)), Upcast(ValUse(1, SShort), SLong)))) - testCases( - Seq( - (-32768.toShort, Success(CBigInt(new BigInteger("-8000", 16)))), - (-26248.toShort, Success(CBigInt(new BigInteger("-6688", 16)))), - (-1.toShort, Success(CBigInt(new BigInteger("-1", 16)))), - (0.toShort, Success(CBigInt(new BigInteger("0", 16)))), - (1.toShort, Success(CBigInt(new BigInteger("1", 16)))), - (22845.toShort, Success(CBigInt(new BigInteger("593d", 16)))), - (32767.toShort, Success(CBigInt(new BigInteger("7fff", 16)))) - ), + verifyCases( + { + def success(v: BigInt) = Success(Expected(v, 35932)) + Seq( + (-32768.toShort, success(CBigInt(new BigInteger("-8000", 16)))), + (-26248.toShort, success(CBigInt(new BigInteger("-6688", 16)))), + (-1.toShort, success(CBigInt(new BigInteger("-1", 16)))), + (0.toShort, success(CBigInt(new BigInteger("0", 16)))), + (1.toShort, success(CBigInt(new BigInteger("1", 16)))), + (22845.toShort, success(CBigInt(new BigInteger("593d", 16)))), + (32767.toShort, success(CBigInt(new BigInteger("7fff", 16)))) + ) + }, existingFeature((x: Short) => x.toBigInt, "{ (x: Short) => x.toBigInt }", FuncValue(Vector((1, SShort)), Upcast(ValUse(1, SShort), SBigInt)))) val n = ExactNumeric.ShortIsExactNumeric - testCases( - Seq( - ((-32768.toShort, 1.toShort), Failure(new ArithmeticException("Short overflow"))), - ((-32768.toShort, 4006.toShort), Failure(new ArithmeticException("Short overflow"))), - ((-21384.toShort, 0.toShort), Failure(new ArithmeticException("/ by zero"))), - ((-19027.toShort, 6073.toShort), Failure(new ArithmeticException("Short overflow"))), - ((-16800.toShort, 32767.toShort), Failure(new ArithmeticException("Short overflow"))), - ((-1.toShort, -30005.toShort), Success((-30006.toShort, (30004.toShort, (30005.toShort, (0.toShort, -1.toShort)))))), - ((-1.toShort, 0.toShort), Failure(new ArithmeticException("/ by zero"))), - ((0.toShort, -1.toShort), Success((-1.toShort, (1.toShort, (0.toShort, (0.toShort, 0.toShort)))))), - ((0.toShort, 0.toShort), Failure(new ArithmeticException("/ by zero"))), - ((0.toShort, 1.toShort), Success((1.toShort, (-1.toShort, (0.toShort, (0.toShort, 0.toShort)))))), - ((0.toShort, 25105.toShort), Success((25105.toShort, (-25105.toShort, (0.toShort, (0.toShort, 0.toShort)))))), - ((1.toShort, -32768.toShort), Failure(new ArithmeticException("Short overflow"))), - ((1.toShort, -1.toShort), Success((0.toShort, (2.toShort, (-1.toShort, (-1.toShort, 0.toShort)))))), - ((1.toShort, 0.toShort), Failure(new ArithmeticException("/ by zero"))), - ((605.toShort, 7698.toShort), Failure(new ArithmeticException("Short overflow"))), - ((5094.toShort, -32768.toShort), Failure(new ArithmeticException("Short overflow"))), - ((5350.toShort, -1.toShort), Success((5349.toShort, (5351.toShort, (-5350.toShort, (-5350.toShort, 0.toShort)))))), - ((8115.toShort, -32768.toShort), Failure(new ArithmeticException("Short overflow"))), - ((14217.toShort, 32767.toShort), Failure(new ArithmeticException("Short overflow"))), - ((16223.toShort, -11686.toShort), Failure(new ArithmeticException("Short overflow"))), - ((16989.toShort, 1.toShort), Success((16990.toShort, (16988.toShort, (16989.toShort, (16989.toShort, 0.toShort)))))), - ((20397.toShort, -4450.toShort), Failure(new ArithmeticException("Short overflow"))), - ((20488.toShort, 1.toShort), Success((20489.toShort, (20487.toShort, (20488.toShort, (20488.toShort, 0.toShort)))))), - ((32767.toShort, -32768.toShort), Failure(new ArithmeticException("Short overflow"))), - ((32767.toShort, -13423.toShort), Failure(new ArithmeticException("Short overflow"))), - ((32767.toShort, 32767.toShort), Failure(new ArithmeticException("Short overflow"))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 39654)) + Seq( + ((-32768.toShort, 1.toShort), Failure(new ArithmeticException("Short overflow"))), + ((-32768.toShort, 4006.toShort), Failure(new ArithmeticException("Short overflow"))), + ((-21384.toShort, 0.toShort), Failure(new ArithmeticException("/ by zero"))), + ((-19027.toShort, 6073.toShort), Failure(new ArithmeticException("Short overflow"))), + ((-16800.toShort, 32767.toShort), Failure(new ArithmeticException("Short overflow"))), + ((-1.toShort, -30005.toShort), success((-30006.toShort, (30004.toShort, (30005.toShort, (0.toShort, -1.toShort)))))), + ((-1.toShort, 0.toShort), Failure(new ArithmeticException("/ by zero"))), + ((0.toShort, -1.toShort), success((-1.toShort, (1.toShort, (0.toShort, (0.toShort, 0.toShort)))))), + ((0.toShort, 0.toShort), Failure(new ArithmeticException("/ by zero"))), + ((0.toShort, 1.toShort), success((1.toShort, (-1.toShort, (0.toShort, (0.toShort, 0.toShort)))))), + ((0.toShort, 25105.toShort), success((25105.toShort, (-25105.toShort, (0.toShort, (0.toShort, 0.toShort)))))), + ((1.toShort, -32768.toShort), Failure(new ArithmeticException("Short overflow"))), + ((1.toShort, -1.toShort), success((0.toShort, (2.toShort, (-1.toShort, (-1.toShort, 0.toShort)))))), + ((1.toShort, 0.toShort), Failure(new ArithmeticException("/ by zero"))), + ((605.toShort, 7698.toShort), Failure(new ArithmeticException("Short overflow"))), + ((5094.toShort, -32768.toShort), Failure(new ArithmeticException("Short overflow"))), + ((5350.toShort, -1.toShort), success((5349.toShort, (5351.toShort, (-5350.toShort, (-5350.toShort, 0.toShort)))))), + ((8115.toShort, -32768.toShort), Failure(new ArithmeticException("Short overflow"))), + ((14217.toShort, 32767.toShort), Failure(new ArithmeticException("Short overflow"))), + ((16223.toShort, -11686.toShort), Failure(new ArithmeticException("Short overflow"))), + ((16989.toShort, 1.toShort), success((16990.toShort, (16988.toShort, (16989.toShort, (16989.toShort, 0.toShort)))))), + ((20397.toShort, -4450.toShort), Failure(new ArithmeticException("Short overflow"))), + ((20488.toShort, 1.toShort), success((20489.toShort, (20487.toShort, (20488.toShort, (20488.toShort, 0.toShort)))))), + ((32767.toShort, -32768.toShort), Failure(new ArithmeticException("Short overflow"))), + ((32767.toShort, -13423.toShort), Failure(new ArithmeticException("Short overflow"))), + ((32767.toShort, 32767.toShort), Failure(new ArithmeticException("Short overflow"))) + ) + }, existingFeature( { (x: (Short, Short)) => val a = x._1; val b = x._2 @@ -736,107 +783,128 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("Int methods equivalence") { - testCases( - Seq( - (Int.MinValue, Failure(new ArithmeticException("Byte overflow"))), - (-2014394379, Failure(new ArithmeticException("Byte overflow"))), - (Byte.MinValue.toInt, Success(Byte.MinValue)), - (-1, Success(-1.toByte)), - (0, Success(0.toByte)), - (1, Success(1.toByte)), - (Byte.MaxValue.toInt, Success(Byte.MaxValue)), - (181686429, Failure(new ArithmeticException("Byte overflow"))), - (Int.MaxValue, Failure(new ArithmeticException("Byte overflow"))) - ), + SInt.upcast(0) shouldBe 0 // boundary test case + SInt.downcast(0) shouldBe 0 // boundary test case + + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35976)) + Seq( + (Int.MinValue, Failure(new ArithmeticException("Byte overflow"))), + (-2014394379, Failure(new ArithmeticException("Byte overflow"))), + (Byte.MinValue.toInt, success(Byte.MinValue)), + (-1, success(-1.toByte)), + (0, success(0.toByte)), + (1, success(1.toByte)), + (Byte.MaxValue.toInt, success(Byte.MaxValue)), + (181686429, Failure(new ArithmeticException("Byte overflow"))), + (Int.MaxValue, Failure(new ArithmeticException("Byte overflow"))) + ) + }, existingFeature((x: Int) => x.toByteExact, "{ (x: Int) => x.toByte }", FuncValue(Vector((1, SInt)), Downcast(ValUse(1, SInt), SByte)))) - testCases( - Seq( - (Int.MinValue, Failure(new ArithmeticException("Short overflow"))), - (Short.MinValue - 1, Failure(new ArithmeticException("Short overflow"))), - (Short.MinValue.toInt, Success(Short.MinValue)), - (-1, Success(-1.toShort)), - (0, Success(0.toShort)), - (1, Success(1.toShort)), - (Short.MaxValue.toInt, Success(Short.MaxValue)), - (Short.MaxValue + 1, Failure(new ArithmeticException("Short overflow"))), - (Int.MaxValue, Failure(new ArithmeticException("Short overflow"))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35976)) + Seq( + (Int.MinValue, Failure(new ArithmeticException("Short overflow"))), + (Short.MinValue - 1, Failure(new ArithmeticException("Short overflow"))), + (Short.MinValue.toInt, success(Short.MinValue)), + (-1, success(-1.toShort)), + (0, success(0.toShort)), + (1, success(1.toShort)), + (Short.MaxValue.toInt, success(Short.MaxValue)), + (Short.MaxValue + 1, Failure(new ArithmeticException("Short overflow"))), + (Int.MaxValue, Failure(new ArithmeticException("Short overflow"))) + ) + }, existingFeature((x: Int) => x.toShortExact, "{ (x: Int) => x.toShort }", FuncValue(Vector((1, SInt)), Downcast(ValUse(1, SInt), SShort)))) - testCases( - Seq( - (Int.MinValue, Success(Int.MinValue)), - (-1, Success(-1)), - (0, Success(0)), - (1, Success(1)), - (Int.MaxValue, Success(Int.MaxValue)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35798)) + Seq( + (Int.MinValue, success(Int.MinValue)), + (-1, success(-1)), + (0, success(0)), + (1, success(1)), + (Int.MaxValue, success(Int.MaxValue)) + ) + }, existingFeature((x: Int) => x.toInt, "{ (x: Int) => x.toInt }", FuncValue(Vector((1, SInt)), ValUse(1, SInt)))) - testCases( - Seq( - (Int.MinValue, Success(Int.MinValue.toLong)), - (-1, Success(-1L)), - (0, Success(0L)), - (1, Success(1L)), - (Int.MaxValue, Success(Int.MaxValue.toLong)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35902)) + Seq( + (Int.MinValue, success(Int.MinValue.toLong)), + (-1, success(-1L)), + (0, success(0L)), + (1, success(1L)), + (Int.MaxValue, success(Int.MaxValue.toLong)) + ) + }, existingFeature((x: Int) => x.toLong, "{ (x: Int) => x.toLong }", FuncValue(Vector((1, SInt)), Upcast(ValUse(1, SInt), SLong)))) - testCases( - Seq( - (Int.MinValue, Success(CBigInt(new BigInteger("-80000000", 16)))), - (-1937187314, Success(CBigInt(new BigInteger("-737721f2", 16)))), - (-1, Success(CBigInt(new BigInteger("-1", 16)))), - (0, Success(CBigInt(new BigInteger("0", 16)))), - (1, Success(CBigInt(new BigInteger("1", 16)))), - (1542171288, Success(CBigInt(new BigInteger("5bebaa98", 16)))), - (Int.MaxValue, Success(CBigInt(new BigInteger("7fffffff", 16)))) - ), + verifyCases( + { + def success(v: BigInt) = Success(Expected(v, 35932)) + Seq( + (Int.MinValue, success(CBigInt(new BigInteger("-80000000", 16)))), + (-1937187314, success(CBigInt(new BigInteger("-737721f2", 16)))), + (-1, success(CBigInt(new BigInteger("-1", 16)))), + (0, success(CBigInt(new BigInteger("0", 16)))), + (1, success(CBigInt(new BigInteger("1", 16)))), + (1542171288, success(CBigInt(new BigInteger("5bebaa98", 16)))), + (Int.MaxValue, success(CBigInt(new BigInteger("7fffffff", 16)))) + ) + }, existingFeature((x: Int) => x.toBigInt, "{ (x: Int) => x.toBigInt }", FuncValue(Vector((1, SInt)), Upcast(ValUse(1, SInt), SBigInt)))) val n = ExactNumeric.IntIsExactNumeric - testCases( - Seq( - ((Int.MinValue, 449583993), Failure(new ArithmeticException("integer overflow"))), - ((-1589633733, 2147483647), Failure(new ArithmeticException("integer overflow"))), - ((-1585471506, -1), Success((-1585471507, (-1585471505, (1585471506, (1585471506, 0)))))), - ((-1569005179, 1230236634), Failure(new ArithmeticException("integer overflow"))), - ((-1493733356, -1319619597), Failure(new ArithmeticException("integer overflow"))), - ((-1100263120, -880052091), Failure(new ArithmeticException("integer overflow"))), - ((-1055955857, 309147303), Failure(new ArithmeticException("integer overflow"))), - ((-569807371, 0), Failure(new ArithmeticException("/ by zero"))), - ((-522264843, 2147483647), Failure(new ArithmeticException("integer overflow"))), - ((-109552389, 0), Failure(new ArithmeticException("/ by zero"))), - ((-1, -2147483648), Failure(new ArithmeticException("integer overflow"))), - ((-1, -1), Success((-2, (0, (1, (1, 0)))))), - ((-1, 0), Failure(new ArithmeticException("/ by zero"))), - ((0, -2147483648), Failure(new ArithmeticException("integer overflow"))), - ((1, -1525049432), Success((-1525049431, (1525049433, (-1525049432, (0, 1)))))), - ((1, 0), Failure(new ArithmeticException("/ by zero"))), - ((1, 805353746), Success((805353747, (-805353745, (805353746, (0, 1)))))), - ((1, 2147483647), Failure(new ArithmeticException("integer overflow"))), - ((475797978, 0), Failure(new ArithmeticException("/ by zero"))), - ((782343922, -1448560539), Failure(new ArithmeticException("integer overflow"))), - ((928769361, 542647292), Failure(new ArithmeticException("integer overflow"))), - ((1568062151, 0), Failure(new ArithmeticException("/ by zero"))), - ((1698252401, -1), Success((1698252400, (1698252402, (-1698252401, (-1698252401, 0)))))), - ((1949795740, -1575667037), Failure(new ArithmeticException("integer overflow"))), - ((Int.MaxValue, -1), Failure(new ArithmeticException("integer overflow"))), - ((Int.MaxValue, 1), Failure(new ArithmeticException("integer overflow"))), - ((Int.MaxValue, 1738276576), Failure(new ArithmeticException("integer overflow"))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 39654)) + Seq( + ((Int.MinValue, 449583993), Failure(new ArithmeticException("integer overflow"))), + ((-1589633733, 2147483647), Failure(new ArithmeticException("integer overflow"))), + ((-1585471506, -1), success((-1585471507, (-1585471505, (1585471506, (1585471506, 0)))))), + ((-1569005179, 1230236634), Failure(new ArithmeticException("integer overflow"))), + ((-1493733356, -1319619597), Failure(new ArithmeticException("integer overflow"))), + ((-1100263120, -880052091), Failure(new ArithmeticException("integer overflow"))), + ((-1055955857, 309147303), Failure(new ArithmeticException("integer overflow"))), + ((-569807371, 0), Failure(new ArithmeticException("/ by zero"))), + ((-522264843, 2147483647), Failure(new ArithmeticException("integer overflow"))), + ((-109552389, 0), Failure(new ArithmeticException("/ by zero"))), + ((-1, -2147483648), Failure(new ArithmeticException("integer overflow"))), + ((-1, -1), success((-2, (0, (1, (1, 0)))))), + ((-1, 0), Failure(new ArithmeticException("/ by zero"))), + ((0, -2147483648), Failure(new ArithmeticException("integer overflow"))), + ((1, -1525049432), success((-1525049431, (1525049433, (-1525049432, (0, 1)))))), + ((1, 0), Failure(new ArithmeticException("/ by zero"))), + ((1, 805353746), success((805353747, (-805353745, (805353746, (0, 1)))))), + ((1, 2147483647), Failure(new ArithmeticException("integer overflow"))), + ((475797978, 0), Failure(new ArithmeticException("/ by zero"))), + ((782343922, -1448560539), Failure(new ArithmeticException("integer overflow"))), + ((928769361, 542647292), Failure(new ArithmeticException("integer overflow"))), + ((1568062151, 0), Failure(new ArithmeticException("/ by zero"))), + ((1698252401, -1), success((1698252400, (1698252402, (-1698252401, (-1698252401, 0)))))), + ((1949795740, -1575667037), Failure(new ArithmeticException("integer overflow"))), + ((Int.MaxValue, -1), Failure(new ArithmeticException("integer overflow"))), + ((Int.MaxValue, 1), Failure(new ArithmeticException("integer overflow"))), + ((Int.MaxValue, 1738276576), Failure(new ArithmeticException("integer overflow"))) + ) + }, existingFeature( { (x: (Int, Int)) => val a = x._1; val b = x._2 @@ -920,109 +988,130 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("Long methods equivalence") { - testCases( - Seq( - (Long.MinValue, Failure(new ArithmeticException("Byte overflow"))), - (Byte.MinValue.toLong - 1, Failure(new ArithmeticException("Byte overflow"))), - (Byte.MinValue.toLong, Success(Byte.MinValue)), - (-1L, Success(-1.toByte)), - (0L, Success(0.toByte)), - (1L, Success(1.toByte)), - (Byte.MaxValue.toLong, Success(Byte.MaxValue)), - (Byte.MaxValue.toLong + 1, Failure(new ArithmeticException("Byte overflow"))), - (Long.MinValue, Failure(new ArithmeticException("Byte overflow"))) - ), + SLong.upcast(0L) shouldBe 0L // boundary test case + SLong.downcast(0L) shouldBe 0L // boundary test case + + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35976)) + Seq( + (Long.MinValue, Failure(new ArithmeticException("Byte overflow"))), + (Byte.MinValue.toLong - 1, Failure(new ArithmeticException("Byte overflow"))), + (Byte.MinValue.toLong, success(Byte.MinValue)), + (-1L, success(-1.toByte)), + (0L, success(0.toByte)), + (1L, success(1.toByte)), + (Byte.MaxValue.toLong, success(Byte.MaxValue)), + (Byte.MaxValue.toLong + 1, Failure(new ArithmeticException("Byte overflow"))), + (Long.MinValue, Failure(new ArithmeticException("Byte overflow"))) + ) + }, existingFeature((x: Long) => x.toByteExact, "{ (x: Long) => x.toByte }", FuncValue(Vector((1, SLong)), Downcast(ValUse(1, SLong), SByte)))) - testCases( - Seq( - (Long.MinValue, Failure(new ArithmeticException("Short overflow"))), - (Short.MinValue.toLong - 1, Failure(new ArithmeticException("Short overflow"))), - (Short.MinValue.toLong, Success(Short.MinValue)), - (-1L, Success(-1.toShort)), - (0L, Success(0.toShort)), - (1L, Success(1.toShort)), - (Short.MaxValue.toLong, Success(Short.MaxValue)), - (Short.MaxValue.toLong + 1, Failure(new ArithmeticException("Short overflow"))), - (Long.MinValue, Failure(new ArithmeticException("Short overflow"))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35976)) + Seq( + (Long.MinValue, Failure(new ArithmeticException("Short overflow"))), + (Short.MinValue.toLong - 1, Failure(new ArithmeticException("Short overflow"))), + (Short.MinValue.toLong, success(Short.MinValue)), + (-1L, success(-1.toShort)), + (0L, success(0.toShort)), + (1L, success(1.toShort)), + (Short.MaxValue.toLong, success(Short.MaxValue)), + (Short.MaxValue.toLong + 1, Failure(new ArithmeticException("Short overflow"))), + (Long.MinValue, Failure(new ArithmeticException("Short overflow"))) + ) + }, existingFeature((x: Long) => x.toShortExact, "{ (x: Long) => x.toShort }", FuncValue(Vector((1, SLong)), Downcast(ValUse(1, SLong), SShort)))) - testCases( - Seq( - (Long.MinValue, Failure(new ArithmeticException("Int overflow"))), - (Int.MinValue.toLong - 1, Failure(new ArithmeticException("Int overflow"))), - (Int.MinValue.toLong, Success(Int.MinValue)), - (-1L, Success(-1.toInt)), - (0L, Success(0.toInt)), - (1L, Success(1.toInt)), - (Int.MaxValue.toLong, Success(Int.MaxValue)), - (Int.MaxValue.toLong + 1, Failure(new ArithmeticException("Int overflow"))), - (Long.MinValue, Failure(new ArithmeticException("Int overflow"))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35976)) + Seq( + (Long.MinValue, Failure(new ArithmeticException("Int overflow"))), + (Int.MinValue.toLong - 1, Failure(new ArithmeticException("Int overflow"))), + (Int.MinValue.toLong, success(Int.MinValue)), + (-1L, success(-1.toInt)), + (0L, success(0.toInt)), + (1L, success(1.toInt)), + (Int.MaxValue.toLong, success(Int.MaxValue)), + (Int.MaxValue.toLong + 1, Failure(new ArithmeticException("Int overflow"))), + (Long.MinValue, Failure(new ArithmeticException("Int overflow"))) + ) + }, existingFeature((x: Long) => x.toIntExact, "{ (x: Long) => x.toInt }", FuncValue(Vector((1, SLong)), Downcast(ValUse(1, SLong), SInt)))) - testCases( - Seq( - (Long.MinValue, Success(Long.MinValue)), - (-1L, Success(-1L)), - (0L, Success(0L)), - (1L, Success(1L)), - (Long.MaxValue, Success(Long.MaxValue)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35798)) + Seq( + (Long.MinValue, success(Long.MinValue)), + (-1L, success(-1L)), + (0L, success(0L)), + (1L, success(1L)), + (Long.MaxValue, success(Long.MaxValue)) + ) + }, existingFeature((x: Long) => x.toLong, "{ (x: Long) => x.toLong }", FuncValue(Vector((1, SLong)), ValUse(1, SLong)))) - testCases( - Seq( - (Long.MinValue, Success(CBigInt(new BigInteger("-8000000000000000", 16)))), - (-1074651039980347209L, Success(CBigInt(new BigInteger("-ee9ed6d57885f49", 16)))), - (-1L, Success(CBigInt(new BigInteger("-1", 16)))), - (0L, Success(CBigInt(new BigInteger("0", 16)))), - (1L, Success(CBigInt(new BigInteger("1", 16)))), - (1542942726564696512L, Success(CBigInt(new BigInteger("1569a23c25a951c0", 16)))), - (Long.MaxValue, Success(CBigInt(new BigInteger("7fffffffffffffff", 16)))) - ), + verifyCases( + { + def success(v: BigInt) = Success(Expected(v, 35932)) + Seq( + (Long.MinValue, success(CBigInt(new BigInteger("-8000000000000000", 16)))), + (-1074651039980347209L, success(CBigInt(new BigInteger("-ee9ed6d57885f49", 16)))), + (-1L, success(CBigInt(new BigInteger("-1", 16)))), + (0L, success(CBigInt(new BigInteger("0", 16)))), + (1L, success(CBigInt(new BigInteger("1", 16)))), + (1542942726564696512L, success(CBigInt(new BigInteger("1569a23c25a951c0", 16)))), + (Long.MaxValue, success(CBigInt(new BigInteger("7fffffffffffffff", 16)))) + ) + }, existingFeature((x: Long) => x.toBigInt, "{ (x: Long) => x.toBigInt }", FuncValue(Vector((1, SLong)), Upcast(ValUse(1, SLong), SBigInt)))) val n = ExactNumeric.LongIsExactNumeric - testCases( - Seq( - ((Long.MinValue, -4677100190307931395L), Failure(new ArithmeticException("long overflow"))), - ((Long.MinValue, -1L), Failure(new ArithmeticException("long overflow"))), - ((Long.MinValue, 1L), Failure(new ArithmeticException("long overflow"))), - ((-9223372036854775808L, 0L), Failure(new ArithmeticException("/ by zero"))), - ((-5828066432064138816L, 9105034716270510411L), Failure(new ArithmeticException("long overflow"))), - ((-4564956247298949325L, -1L), Success( - (-4564956247298949326L, (-4564956247298949324L, (4564956247298949325L, (4564956247298949325L, 0L)))) - )), - ((-1499553565058783253L, -3237683216870282569L), Failure(new ArithmeticException("long overflow"))), - ((-1368457031689886112L, 9223372036854775807L), Failure(new ArithmeticException("long overflow"))), - ((-1L, -4354407074688367443L), Success((-4354407074688367444L, (4354407074688367442L, (4354407074688367443L, (0L, -1L)))))), - ((-1L, -1L), Success((-2L, (0L, (1L, (1L, 0L)))))), - ((-1L, 5665019549505434695L), Success((5665019549505434694L, (-5665019549505434696L, (-5665019549505434695L, (0L, -1L)))))), - ((0L, -1L), Success((-1L, (1L, (0L, (0L, 0L)))))), - ((0L, 0L), Failure(new ArithmeticException("/ by zero"))), - ((0L, 2112386634269044172L), Success((2112386634269044172L, (-2112386634269044172L, (0L, (0L, 0L)))))), - ((2254604056782701370L, -5878231674026236574L), Failure(new ArithmeticException("long overflow"))), - ((2903872550238813643L, 1L), Success( - (2903872550238813644L, (2903872550238813642L, (2903872550238813643L, (2903872550238813643L, 0L)))) - )), - ((5091129735284641762L, -427673944382373638L), Failure(new ArithmeticException("long overflow"))), - ((6029085020194630780L, 2261786144956037939L), Failure(new ArithmeticException("long overflow"))), - ((8126382074515995418L, -4746652047588907829L), Failure(new ArithmeticException("long overflow"))), - ((Long.MaxValue, 1L), Failure(new ArithmeticException("long overflow"))), - ((Long.MaxValue, -1L), Failure(new ArithmeticException("long overflow"))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 39654)) + Seq( + ((Long.MinValue, -4677100190307931395L), Failure(new ArithmeticException("long overflow"))), + ((Long.MinValue, -1L), Failure(new ArithmeticException("long overflow"))), + ((Long.MinValue, 1L), Failure(new ArithmeticException("long overflow"))), + ((-9223372036854775808L, 0L), Failure(new ArithmeticException("/ by zero"))), + ((-5828066432064138816L, 9105034716270510411L), Failure(new ArithmeticException("long overflow"))), + ((-4564956247298949325L, -1L), success( + (-4564956247298949326L, (-4564956247298949324L, (4564956247298949325L, (4564956247298949325L, 0L)))) + )), + ((-1499553565058783253L, -3237683216870282569L), Failure(new ArithmeticException("long overflow"))), + ((-1368457031689886112L, 9223372036854775807L), Failure(new ArithmeticException("long overflow"))), + ((-1L, -4354407074688367443L), success((-4354407074688367444L, (4354407074688367442L, (4354407074688367443L, (0L, -1L)))))), + ((-1L, -1L), success((-2L, (0L, (1L, (1L, 0L)))))), + ((-1L, 5665019549505434695L), success((5665019549505434694L, (-5665019549505434696L, (-5665019549505434695L, (0L, -1L)))))), + ((0L, -1L), success((-1L, (1L, (0L, (0L, 0L)))))), + ((0L, 0L), Failure(new ArithmeticException("/ by zero"))), + ((0L, 2112386634269044172L), success((2112386634269044172L, (-2112386634269044172L, (0L, (0L, 0L)))))), + ((2254604056782701370L, -5878231674026236574L), Failure(new ArithmeticException("long overflow"))), + ((2903872550238813643L, 1L), success( + (2903872550238813644L, (2903872550238813642L, (2903872550238813643L, (2903872550238813643L, 0L)))) + )), + ((5091129735284641762L, -427673944382373638L), Failure(new ArithmeticException("long overflow"))), + ((6029085020194630780L, 2261786144956037939L), Failure(new ArithmeticException("long overflow"))), + ((8126382074515995418L, -4746652047588907829L), Failure(new ArithmeticException("long overflow"))), + ((Long.MaxValue, 1L), Failure(new ArithmeticException("long overflow"))), + ((Long.MaxValue, -1L), Failure(new ArithmeticException("long overflow"))) + ) + }, existingFeature( { (x: (Long, Long)) => val a = x._1; val b = x._2 @@ -1107,85 +1196,91 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("BigInt methods equivalence") { - testCases( - Seq( - (CBigInt(new BigInteger("-85102d7f884ca0e8f56193b46133acaf7e4681e1757d03f191ae4f445c8e0", 16)), Success( - CBigInt(new BigInteger("-85102d7f884ca0e8f56193b46133acaf7e4681e1757d03f191ae4f445c8e0", 16)) - )), - (CBigInt(new BigInteger("-8000000000000000", 16)), Success(CBigInt(new BigInteger("-8000000000000000", 16)))), - (CBigInt(new BigInteger("-1", 16)), Success(CBigInt(new BigInteger("-1", 16)))), - (CBigInt(new BigInteger("0", 16)), Success(CBigInt(new BigInteger("0", 16)))), - (CBigInt(new BigInteger("1", 16)), Success(CBigInt(new BigInteger("1", 16)))), - (CBigInt(new BigInteger("7fffffffffffffff", 16)), Success(CBigInt(new BigInteger("7fffffffffffffff", 16)))), - (CBigInt(new BigInteger("bdd56c22eb3eace8bc4e1c38c65dfdb2e4ffdcf421ae78c36b93b9ff37dc0", 16)), Success( - CBigInt(new BigInteger("bdd56c22eb3eace8bc4e1c38c65dfdb2e4ffdcf421ae78c36b93b9ff37dc0", 16)) - )) - ), + verifyCases( + { + def success(v: BigInt) = Success(Expected(v, 35798)) + Seq( + (CBigInt(new BigInteger("-85102d7f884ca0e8f56193b46133acaf7e4681e1757d03f191ae4f445c8e0", 16)), success( + CBigInt(new BigInteger("-85102d7f884ca0e8f56193b46133acaf7e4681e1757d03f191ae4f445c8e0", 16)) + )), + (CBigInt(new BigInteger("-8000000000000000", 16)), success(CBigInt(new BigInteger("-8000000000000000", 16)))), + (CBigInt(new BigInteger("-1", 16)), success(CBigInt(new BigInteger("-1", 16)))), + (CBigInt(new BigInteger("0", 16)), success(CBigInt(new BigInteger("0", 16)))), + (CBigInt(new BigInteger("1", 16)), success(CBigInt(new BigInteger("1", 16)))), + (CBigInt(new BigInteger("7fffffffffffffff", 16)), success(CBigInt(new BigInteger("7fffffffffffffff", 16)))), + (CBigInt(new BigInteger("bdd56c22eb3eace8bc4e1c38c65dfdb2e4ffdcf421ae78c36b93b9ff37dc0", 16)), success( + CBigInt(new BigInteger("bdd56c22eb3eace8bc4e1c38c65dfdb2e4ffdcf421ae78c36b93b9ff37dc0", 16)) + )) + ) + }, existingFeature((x: BigInt) => x, "{ (x: BigInt) => x.toBigInt }", FuncValue(Vector((1, SBigInt)), ValUse(1, SBigInt)))) val n = NumericOps.BigIntIsExactNumeric - testCases( - Seq( - ((CBigInt(new BigInteger("-8683d1cd99d5fcf0e6eff6295c285c36526190e13dbde008c49e5ae6fddc1c", 16)), - CBigInt(new BigInteger("-2ef55db3f245feddacf0182e299dd", 16))), - Failure(new ArithmeticException("BigInteger out of 256 bit range"))), - - ((CBigInt(new BigInteger("-68e1136872f98fb0245ec5aa4bef46e16273e860746c892", 16)), - CBigInt(new BigInteger("-352aaa769b41a327", 16))), - Failure(new ArithmeticException("BigInteger: modulus not positive"))), - - ((CBigInt(new BigInteger("-39fc00ebf09080cbd8408dd38c4b7490bea533447047140", 16)), - CBigInt(new BigInteger("31de9e96177dbd39", 16))), - Success(( - CBigInt(new BigInteger("-39fc00ebf09080cbd8408dd38c4b748da0bb49e2f86b407", 16)), - (CBigInt(new BigInteger("-39fc00ebf09080cbd8408dd38c4b7493dc8f1ca5e822e79", 16)), - (CBigInt(new BigInteger("-b4ba8a17d328dac74ef014d7be35597a1259f8b16f0ff1c9820dea23d97740", 16)), - (CBigInt(new BigInteger("-129a8045376e104f0d3771b6c2c128fc", 16)), - CBigInt(new BigInteger("12fe89836fc97815", 16)))))) )), - - ((CBigInt(new BigInteger("-8000000000000000", 16)), CBigInt(new BigInteger("8000000000000000", 16))), - Success(( - CBigInt(new BigInteger("0", 16)), - (CBigInt(new BigInteger("-10000000000000000", 16)), - (CBigInt(new BigInteger("-40000000000000000000000000000000", 16)), - (CBigInt(new BigInteger("-1", 16)), CBigInt(new BigInteger("0", 16)))))) )), - - ((CBigInt(new BigInteger("-47dede8d3e4804bb", 16)), CBigInt(new BigInteger("-388828eb6dfce683", 16))), - Failure(new ArithmeticException("BigInteger: modulus not positive"))), - - ((CBigInt(new BigInteger("-4fde491150ea00d", 16)), CBigInt(new BigInteger("-80000001", 16))), - Failure(new ArithmeticException("BigInteger: modulus not positive"))), - - ((CBigInt(new BigInteger("-80000001", 16)), CBigInt(new BigInteger("-80000001", 16))), - Failure(new ArithmeticException("BigInteger: modulus not positive"))), - - ((CBigInt(new BigInteger("0", 16)), CBigInt(new BigInteger("-8000000000000000", 16))), - Failure(new ArithmeticException("BigInteger: modulus not positive"))), - - ((CBigInt(new BigInteger("0", 16)), CBigInt(new BigInteger("0", 16))), - Failure(new ArithmeticException("BigInteger divide by zero"))), - - ((CBigInt(new BigInteger("1", 16)), - CBigInt(new BigInteger("-86063f66e06d6d535c95862cd506309a95d10102422fee", 16))), - Failure(new ArithmeticException("BigInteger: modulus not positive"))), - - ((CBigInt(new BigInteger("80000000", 16)), CBigInt(new BigInteger("4e592ce5b544b8f7a91f97ec9ea2f2c3660111360297a4", 16))), - Success(( - CBigInt(new BigInteger("4e592ce5b544b8f7a91f97ec9ea2f2c3660111b60297a4", 16)), - (CBigInt(new BigInteger("-4e592ce5b544b8f7a91f97ec9ea2f2c3660110b60297a4", 16)), - (CBigInt(new BigInteger("272c9672daa25c7bd48fcbf64f517961b300889b014bd200000000", 16)), - (CBigInt(new BigInteger("0", 16)), CBigInt(new BigInteger("80000000", 16)))))) )), - - ((CBigInt(new BigInteger("3d31398dc4783303", 16)), - CBigInt(new BigInteger("-37b381db4e6e927e202a2a421d5a09ca", 16))), - Failure(new ArithmeticException("BigInteger: modulus not positive"))), - - ((CBigInt(new BigInteger("5524814a26357cb71488b6fb26af2d3", 16)), - CBigInt(new BigInteger("c413b7d975a9972427f46996299fe57cfe79479ac954a7", 16))), - Failure(new ArithmeticException("BigInteger out of 256 bit range"))) - ), + verifyCases( + { + def success(v: (BigInt, (BigInt, (BigInt, (BigInt, BigInt))))) = Success(Expected(v, 39774)) + Seq( + ((CBigInt(new BigInteger("-8683d1cd99d5fcf0e6eff6295c285c36526190e13dbde008c49e5ae6fddc1c", 16)), + CBigInt(new BigInteger("-2ef55db3f245feddacf0182e299dd", 16))), + Failure(new ArithmeticException("BigInteger out of 256 bit range"))), + + ((CBigInt(new BigInteger("-68e1136872f98fb0245ec5aa4bef46e16273e860746c892", 16)), + CBigInt(new BigInteger("-352aaa769b41a327", 16))), + Failure(new ArithmeticException("BigInteger: modulus not positive"))), + + ((CBigInt(new BigInteger("-39fc00ebf09080cbd8408dd38c4b7490bea533447047140", 16)), + CBigInt(new BigInteger("31de9e96177dbd39", 16))), + success(( + CBigInt(new BigInteger("-39fc00ebf09080cbd8408dd38c4b748da0bb49e2f86b407", 16)), + (CBigInt(new BigInteger("-39fc00ebf09080cbd8408dd38c4b7493dc8f1ca5e822e79", 16)), + (CBigInt(new BigInteger("-b4ba8a17d328dac74ef014d7be35597a1259f8b16f0ff1c9820dea23d97740", 16)), + (CBigInt(new BigInteger("-129a8045376e104f0d3771b6c2c128fc", 16)), + CBigInt(new BigInteger("12fe89836fc97815", 16)))))) )), + + ((CBigInt(new BigInteger("-8000000000000000", 16)), CBigInt(new BigInteger("8000000000000000", 16))), + success(( + CBigInt(new BigInteger("0", 16)), + (CBigInt(new BigInteger("-10000000000000000", 16)), + (CBigInt(new BigInteger("-40000000000000000000000000000000", 16)), + (CBigInt(new BigInteger("-1", 16)), CBigInt(new BigInteger("0", 16)))))) )), + + ((CBigInt(new BigInteger("-47dede8d3e4804bb", 16)), CBigInt(new BigInteger("-388828eb6dfce683", 16))), + Failure(new ArithmeticException("BigInteger: modulus not positive"))), + + ((CBigInt(new BigInteger("-4fde491150ea00d", 16)), CBigInt(new BigInteger("-80000001", 16))), + Failure(new ArithmeticException("BigInteger: modulus not positive"))), + + ((CBigInt(new BigInteger("-80000001", 16)), CBigInt(new BigInteger("-80000001", 16))), + Failure(new ArithmeticException("BigInteger: modulus not positive"))), + + ((CBigInt(new BigInteger("0", 16)), CBigInt(new BigInteger("-8000000000000000", 16))), + Failure(new ArithmeticException("BigInteger: modulus not positive"))), + + ((CBigInt(new BigInteger("0", 16)), CBigInt(new BigInteger("0", 16))), + Failure(new ArithmeticException("BigInteger divide by zero"))), + + ((CBigInt(new BigInteger("1", 16)), + CBigInt(new BigInteger("-86063f66e06d6d535c95862cd506309a95d10102422fee", 16))), + Failure(new ArithmeticException("BigInteger: modulus not positive"))), + + ((CBigInt(new BigInteger("80000000", 16)), CBigInt(new BigInteger("4e592ce5b544b8f7a91f97ec9ea2f2c3660111360297a4", 16))), + success(( + CBigInt(new BigInteger("4e592ce5b544b8f7a91f97ec9ea2f2c3660111b60297a4", 16)), + (CBigInt(new BigInteger("-4e592ce5b544b8f7a91f97ec9ea2f2c3660110b60297a4", 16)), + (CBigInt(new BigInteger("272c9672daa25c7bd48fcbf64f517961b300889b014bd200000000", 16)), + (CBigInt(new BigInteger("0", 16)), CBigInt(new BigInteger("80000000", 16)))))) )), + + ((CBigInt(new BigInteger("3d31398dc4783303", 16)), + CBigInt(new BigInteger("-37b381db4e6e927e202a2a421d5a09ca", 16))), + Failure(new ArithmeticException("BigInteger: modulus not positive"))), + + ((CBigInt(new BigInteger("5524814a26357cb71488b6fb26af2d3", 16)), + CBigInt(new BigInteger("c413b7d975a9972427f46996299fe57cfe79479ac954a7", 16))), + Failure(new ArithmeticException("BigInteger out of 256 bit range"))) + ) + }, existingFeature( { (x: (BigInt, BigInt)) => val a = x._1; val b = x._2 @@ -1243,10 +1338,26 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ) ) - ), true) + )) } property("BigInt methods equivalence (new features)") { + // TODO HF: the behavior of `upcast` for BigInt is different from all other Numeric types + // The `Upcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree. + // It makes sense to fix this inconsistency as part of HF + assertExceptionThrown( + SBigInt.upcast(CBigInt(new BigInteger("0", 16)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot upcast value") + ) + + // TODO HF: the behavior of `downcast` for BigInt is different from all other Numeric types + // The `Downcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree. + // It makes sense to fix this inconsistency as part of HF + assertExceptionThrown( + SBigInt.downcast(CBigInt(new BigInteger("0", 16)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot downcast value") + ) + val toByte = newFeature((x: BigInt) => x.toByte, "{ (x: BigInt) => x.toByte }", FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SByte))) @@ -1289,16 +1400,19 @@ class SigmaDslSpec extends SigmaDslTesting { suite => val ge2 = "02dba7b94b111f3894e2f9120b577da595ec7d58d488485adf73bf4e153af63575" val ge3 = "0290449814f5671172dd696a61b8aa49aaa4c87013f56165e27d49944e98bc414d" - testCases( - Seq( - (Helpers.decodeGroupElement(ge1), Success(Helpers.decodeBytes(ge1))), - (Helpers.decodeGroupElement(ge2), Success(Helpers.decodeBytes(ge2))), - (Helpers.decodeGroupElement(ge3), Success(Helpers.decodeBytes(ge3))), - (SigmaDsl.groupGenerator, - Success(Helpers.decodeBytes("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), - (SigmaDsl.groupIdentity, - Success(Helpers.decodeBytes("000000000000000000000000000000000000000000000000000000000000000000"))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 37905)) + Seq( + (Helpers.decodeGroupElement(ge1), success(Helpers.decodeBytes(ge1))), + (Helpers.decodeGroupElement(ge2), success(Helpers.decodeBytes(ge2))), + (Helpers.decodeGroupElement(ge3), success(Helpers.decodeBytes(ge3))), + (SigmaDsl.groupGenerator, + success(Helpers.decodeBytes("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), + (SigmaDsl.groupIdentity, + success(Helpers.decodeBytes("000000000000000000000000000000000000000000000000000000000000000000"))) + ) + }, existingFeature((x: GroupElement) => x.getEncoded, "{ (x: GroupElement) => x.getEncoded }", FuncValue( @@ -1306,14 +1420,17 @@ class SigmaDslSpec extends SigmaDslTesting { suite => MethodCall(ValUse(1, SGroupElement), SGroupElement.getMethodByName("getEncoded"), Vector(), Map()) ))) - testCases( - Seq( - (Helpers.decodeGroupElement(ge1), Success(true)), - (Helpers.decodeGroupElement(ge2), Success(true)), - (Helpers.decodeGroupElement(ge3), Success(true)), - (SigmaDsl.groupGenerator, Success(true)), - (SigmaDsl.groupIdentity, Success(true)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 38340)) + Seq( + (Helpers.decodeGroupElement(ge1), success(true)), + (Helpers.decodeGroupElement(ge2), success(true)), + (Helpers.decodeGroupElement(ge3), success(true)), + (SigmaDsl.groupGenerator, success(true)), + (SigmaDsl.groupIdentity, success(true)) + ) + }, existingFeature({ (x: GroupElement) => decodePoint(x.getEncoded) == x }, "{ (x: GroupElement) => decodePoint(x.getEncoded) == x }", FuncValue( @@ -1331,14 +1448,17 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ))) - testCases( - Seq( - (Helpers.decodeGroupElement(ge1), Success(Helpers.decodeGroupElement("02358d53f01276211f92d0aefbd278805121d4ff6eb534b777af1ee8abae5b2056"))), - (Helpers.decodeGroupElement(ge2), Success(Helpers.decodeGroupElement("03dba7b94b111f3894e2f9120b577da595ec7d58d488485adf73bf4e153af63575"))), - (Helpers.decodeGroupElement(ge3), Success(Helpers.decodeGroupElement("0390449814f5671172dd696a61b8aa49aaa4c87013f56165e27d49944e98bc414d"))), - (SigmaDsl.groupGenerator, Success(Helpers.decodeGroupElement("0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), - (SigmaDsl.groupIdentity, Success(Helpers.decodeGroupElement("000000000000000000000000000000000000000000000000000000000000000000"))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36292)) + Seq( + (Helpers.decodeGroupElement(ge1), success(Helpers.decodeGroupElement("02358d53f01276211f92d0aefbd278805121d4ff6eb534b777af1ee8abae5b2056"))), + (Helpers.decodeGroupElement(ge2), success(Helpers.decodeGroupElement("03dba7b94b111f3894e2f9120b577da595ec7d58d488485adf73bf4e153af63575"))), + (Helpers.decodeGroupElement(ge3), success(Helpers.decodeGroupElement("0390449814f5671172dd696a61b8aa49aaa4c87013f56165e27d49944e98bc414d"))), + (SigmaDsl.groupGenerator, success(Helpers.decodeGroupElement("0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), + (SigmaDsl.groupIdentity, success(Helpers.decodeGroupElement("000000000000000000000000000000000000000000000000000000000000000000"))) + ) + }, existingFeature({ (x: GroupElement) => x.negate }, "{ (x: GroupElement) => x.negate }", FuncValue( @@ -1350,17 +1470,20 @@ class SigmaDslSpec extends SigmaDslTesting { suite => // val isIdentity = existingFeature({ (x: GroupElement) => x.isIdentity }, // "{ (x: GroupElement) => x.isIdentity }") - testCases( - Seq( - ((Helpers.decodeGroupElement(ge1), CBigInt(new BigInteger("-25c80b560dd7844e2efd10f80f7ee57d", 16))), - Success(Helpers.decodeGroupElement("023a850181b7b73f92a5bbfa0bfc78f5bbb6ff00645ddde501037017e1a2251e2e"))), - ((Helpers.decodeGroupElement(ge2), CBigInt(new BigInteger("2488741265082fb02b09f992be3dd8d60d2bbe80d9e2630", 16))), - Success(Helpers.decodeGroupElement("032045b928fb7774a4cd9ef5fa8209f4e493cd4cc5bd536b52746a53871bf73431"))), - ((Helpers.decodeGroupElement(ge3), CBigInt(new BigInteger("-33e8fbdb13d2982e92583445e1fdcb5901a178a7aa1e100", 16))), - Success(Helpers.decodeGroupElement("036128efaf14d8ac2812a662f6494dc617b87986a3dc6b4a59440048a7ac7d2729"))), - ((Helpers.decodeGroupElement(ge3), CBigInt(new BigInteger("1", 16))), - Success(Helpers.decodeGroupElement(ge3))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 41484)) + Seq( + ((Helpers.decodeGroupElement(ge1), CBigInt(new BigInteger("-25c80b560dd7844e2efd10f80f7ee57d", 16))), + success(Helpers.decodeGroupElement("023a850181b7b73f92a5bbfa0bfc78f5bbb6ff00645ddde501037017e1a2251e2e"))), + ((Helpers.decodeGroupElement(ge2), CBigInt(new BigInteger("2488741265082fb02b09f992be3dd8d60d2bbe80d9e2630", 16))), + success(Helpers.decodeGroupElement("032045b928fb7774a4cd9ef5fa8209f4e493cd4cc5bd536b52746a53871bf73431"))), + ((Helpers.decodeGroupElement(ge3), CBigInt(new BigInteger("-33e8fbdb13d2982e92583445e1fdcb5901a178a7aa1e100", 16))), + success(Helpers.decodeGroupElement("036128efaf14d8ac2812a662f6494dc617b87986a3dc6b4a59440048a7ac7d2729"))), + ((Helpers.decodeGroupElement(ge3), CBigInt(new BigInteger("1", 16))), + success(Helpers.decodeGroupElement(ge3))) + ) + }, existingFeature({ (x: (GroupElement, BigInt)) => x._1.exp(x._2) }, "{ (x: (GroupElement, BigInt)) => x._1.exp(x._2) }", FuncValue( @@ -1377,17 +1500,20 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ))) - testCases( - Seq( - ((Helpers.decodeGroupElement(ge1), Helpers.decodeGroupElement("03e132ca090614bd6c9f811e91f6daae61f16968a1e6c694ed65aacd1b1092320e")), - Success(Helpers.decodeGroupElement("02bc48937b4a66f249a32dfb4d2efd0743dc88d46d770b8c5d39fd03325ba211df"))), - ((Helpers.decodeGroupElement(ge2), Helpers.decodeGroupElement("03e132ca090614bd6c9f811e91f6daae61f16968a1e6c694ed65aacd1b1092320e")), - Success(Helpers.decodeGroupElement("0359c3bb2ac4ea4dbd7b1e09d7b11198141a3263834fb84a88039629ec1e9311d1"))), - ((Helpers.decodeGroupElement(ge3), Helpers.decodeGroupElement("03e132ca090614bd6c9f811e91f6daae61f16968a1e6c694ed65aacd1b1092320e")), - Success(Helpers.decodeGroupElement("02eca42e28548d3fb9fa77cdd0c983066c3ad141ebb086b5044ce46b9ba9b5a714"))), - ((Helpers.decodeGroupElement(ge3), SigmaDsl.groupIdentity), - Success(Helpers.decodeGroupElement(ge3))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36457)) + Seq( + ((Helpers.decodeGroupElement(ge1), Helpers.decodeGroupElement("03e132ca090614bd6c9f811e91f6daae61f16968a1e6c694ed65aacd1b1092320e")), + success(Helpers.decodeGroupElement("02bc48937b4a66f249a32dfb4d2efd0743dc88d46d770b8c5d39fd03325ba211df"))), + ((Helpers.decodeGroupElement(ge2), Helpers.decodeGroupElement("03e132ca090614bd6c9f811e91f6daae61f16968a1e6c694ed65aacd1b1092320e")), + success(Helpers.decodeGroupElement("0359c3bb2ac4ea4dbd7b1e09d7b11198141a3263834fb84a88039629ec1e9311d1"))), + ((Helpers.decodeGroupElement(ge3), Helpers.decodeGroupElement("03e132ca090614bd6c9f811e91f6daae61f16968a1e6c694ed65aacd1b1092320e")), + success(Helpers.decodeGroupElement("02eca42e28548d3fb9fa77cdd0c983066c3ad141ebb086b5044ce46b9ba9b5a714"))), + ((Helpers.decodeGroupElement(ge3), SigmaDsl.groupIdentity), + success(Helpers.decodeGroupElement(ge3))) + ) + }, existingFeature({ (x: (GroupElement, GroupElement)) => x._1.multiply(x._2) }, "{ (x: (GroupElement, GroupElement)) => x._1.multiply(x._2) }", FuncValue( @@ -1442,72 +1568,93 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ) - testCases( - Seq( - (t1, Success(Helpers.decodeBytes("000183807f66b301530120ff7fc6bd6601ff01ff7f7d2bedbbffff00187fe89094"))), - (t2, Success(Helpers.decodeBytes("ff000d937f80ffd731ed802d24358001ff8080ff71007f00ad37e0a7ae43fff95b"))), - (t3, Success(Helpers.decodeBytes("3100d2e101ff01fc047c7f6f00ff80129df69a5090012f01ffca99f5bfff0c8036"))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36182)) + Seq( + (t1, success(Helpers.decodeBytes("000183807f66b301530120ff7fc6bd6601ff01ff7f7d2bedbbffff00187fe89094"))), + (t2, success(Helpers.decodeBytes("ff000d937f80ffd731ed802d24358001ff8080ff71007f00ad37e0a7ae43fff95b"))), + (t3, success(Helpers.decodeBytes("3100d2e101ff01fc047c7f6f00ff80129df69a5090012f01ffca99f5bfff0c8036"))) + ) + }, existingFeature((t: AvlTree) => t.digest, "{ (t: AvlTree) => t.digest }", expectedExprFor("digest"))) - testCases( - Seq( - (t1, Success(6.toByte)), - (t2, Success(0.toByte)), - (t3, Success(1.toByte)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36260)) + Seq( + (t1, success(6.toByte)), + (t2, success(0.toByte)), + (t3, success(1.toByte)) + ) + }, existingFeature((t: AvlTree) => t.enabledOperations, "{ (t: AvlTree) => t.enabledOperations }", expectedExprFor("enabledOperations"))) - testCases( - Seq( - (t1, Success(1)), - (t2, Success(32)), - (t3, Success(128)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36136)) + Seq( + (t1, success(1)), + (t2, success(32)), + (t3, success(128)) + ) + }, existingFeature((t: AvlTree) => t.keyLength, "{ (t: AvlTree) => t.keyLength }", expectedExprFor("keyLength"))) - testCases( - Seq( - (t1, Success(Some(1))), - (t2, Success(Some(64))), - (t3, Success(None)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 37151)) + Seq( + (t1, success(Some(1))), + (t2, success(Some(64))), + (t3, success(None)) + ) + }, existingFeature((t: AvlTree) => t.valueLengthOpt, "{ (t: AvlTree) => t.valueLengthOpt }", expectedExprFor("valueLengthOpt"))) - testCases( - Seq( - (t1, Success(false)), - (t2, Success(false)), - (t3, Success(true)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36479)) + Seq( + (t1, success(false)), + (t2, success(false)), + (t3, success(true)) + ) + }, existingFeature((t: AvlTree) => t.isInsertAllowed, "{ (t: AvlTree) => t.isInsertAllowed }", expectedExprFor("isInsertAllowed"))) - testCases( - Seq( - (t1, Success(true)), - (t2, Success(false)), - (t3, Success(false)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36096)) + Seq( + (t1, success(true)), + (t2, success(false)), + (t3, success(false)) + ) + }, existingFeature((t: AvlTree) => t.isUpdateAllowed, "{ (t: AvlTree) => t.isUpdateAllowed }", expectedExprFor("isUpdateAllowed"))) - testCases( - Seq( - (t1, Success(true)), - (t2, Success(false)), - (t3, Success(false)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36502)) + Seq( + (t1, success(true)), + (t2, success(false)), + (t3, success(false)) + ) + }, existingFeature((t: AvlTree) => t.isRemoveAllowed, "{ (t: AvlTree) => t.isRemoveAllowed }", expectedExprFor("isRemoveAllowed"))) @@ -1712,24 +1859,56 @@ class SigmaDslSpec extends SigmaDslTesting { suite => val tree = SigmaDsl.avlTree(AvlTreeFlags.ReadOnly.serializeToByte, digest, 32, None) // positive test - contains.checkExpected((tree, (key, proof)), okContains) - get.checkExpected((tree, (key, proof)), valueOpt) + { + val input = (tree, (key, proof)) + contains.checkExpected(input, okContains) + get.checkExpected(input, valueOpt) + contains.checkVerify(input, expectedRes = okContains, expectedCost = 37850) + get.checkVerify(input, expectedRes = valueOpt, expectedCost = 38372) + } val keys = Colls.fromItems(key) val expRes = Colls.fromItems(valueOpt) - getMany.checkExpected((tree, (keys, proof)), expRes) + + { + val input = (tree, (keys, proof)) + getMany.checkExpected(input, expRes) + getMany.checkVerify(input, expectedRes = expRes, expectedCost = 38991) + } + + { + val input = (tree, digest) + val (res, _) = updateDigest.checkEquality(input).getOrThrow + res.digest shouldBe digest + updateDigest.checkVerify(input, expectedRes = res, expectedCost = 36341) + } - updateDigest.checkEquality((tree, digest)).get.digest shouldBe digest val newOps = 1.toByte - updateOperations.checkEquality((tree, newOps)).get.enabledOperations shouldBe newOps - { // negative tests: invalid proof - val invalidProof = proof.map(x => (-x).toByte) // any other different from proof - val resContains = contains.checkEquality((tree, (key, invalidProof))) - resContains shouldBe Success(false) + { + val input = (tree, newOps) + val (res,_) = updateOperations.checkEquality(input).getOrThrow + res.enabledOperations shouldBe newOps + updateOperations.checkVerify(input, expectedRes = res, expectedCost = 36341) + } + + // negative tests: invalid proof + val invalidProof = proof.map(x => (-x).toByte) // any other different from proof + + { + val input = (tree, (key, invalidProof)) + val (res, _) = contains.checkEquality(input).getOrThrow + res shouldBe false + contains.checkVerify(input, expectedRes = res, expectedCost = 37850) + } + + { val resGet = get.checkEquality((tree, (key, invalidProof))) resGet.isFailure shouldBe true + } + + { val resGetMany = getMany.checkEquality((tree, (keys, invalidProof))) resGetMany.isFailure shouldBe true } @@ -1841,22 +2020,28 @@ class SigmaDslSpec extends SigmaDslTesting { suite => { // positive val preInsertTree = createTree(preInsertDigest, insertAllowed = true) - val res = insert.checkEquality((preInsertTree, (kvs, insertProof))) - res.get.isDefined shouldBe true + val input = (preInsertTree, (kvs, insertProof)) + val (res, _) = insert.checkEquality(input).getOrThrow + res.isDefined shouldBe true + insert.checkVerify(input, expectedRes = res, expectedCost = 38501) } { // negative: readonly tree val readonlyTree = createTree(preInsertDigest) - val res = insert.checkEquality((readonlyTree, (kvs, insertProof))) - res.get.isDefined shouldBe false + val input = (readonlyTree, (kvs, insertProof)) + val (res, _) = insert.checkEquality(input).getOrThrow + res.isDefined shouldBe false + insert.checkVerify(input, expectedRes = res, expectedCost = 38501) } { // negative: invalid key val tree = createTree(preInsertDigest, insertAllowed = true) val invalidKey = key.map(x => (-x).toByte) // any other different from key val invalidKvs = Colls.fromItems((invalidKey -> value)) // NOTE, insertProof is based on `key` - val res = insert.checkEquality((tree, (invalidKvs, insertProof))) - res.get.isDefined shouldBe true // TODO HF: should it really be true? (looks like a bug) + val input = (tree, (invalidKvs, insertProof)) + val (res, _) = insert.checkEquality(input).getOrThrow + res.isDefined shouldBe true // TODO HF: should it really be true? (looks like a bug) + insert.checkVerify(input, expectedRes = res, expectedCost = 38501) } { // negative: invalid proof @@ -1936,6 +2121,7 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ) )) + val cost = 40952 forAll(keyCollGen, bytesCollGen) { (key, value) => val (_, avlProver) = createAvlTreeAndProver(key -> value) @@ -1948,39 +2134,53 @@ class SigmaDslSpec extends SigmaDslTesting { suite => { // positive: update to newValue val preUpdateTree = createTree(preUpdateDigest, updateAllowed = true) val endTree = preUpdateTree.updateDigest(endDigest) - update.checkExpected((preUpdateTree, (kvs, updateProof)), Some(endTree)) + val input = (preUpdateTree, (kvs, updateProof)) + val res = Some(endTree) + update.checkExpected(input, res) + update.checkVerify(input, expectedRes = res, expectedCost = cost) } { // positive: update to the same value (identity operation) val tree = createTree(preUpdateDigest, updateAllowed = true) val keys = Colls.fromItems((key -> value)) - update.checkExpected((tree, (keys, updateProof)), Some(tree)) + val input = (tree, (keys, updateProof)) + val res = Some(tree) + update.checkExpected(input, res) + update.checkVerify(input, expectedRes = res, expectedCost = cost) } { // negative: readonly tree val readonlyTree = createTree(preUpdateDigest) - val res = update.checkExpected((readonlyTree, (kvs, updateProof)), None) + val input = (readonlyTree, (kvs, updateProof)) + update.checkExpected(input, None) + update.checkVerify(input, expectedRes = None, expectedCost = cost) } { // negative: invalid key val tree = createTree(preUpdateDigest, updateAllowed = true) val invalidKey = key.map(x => (-x).toByte) // any other different from key val invalidKvs = Colls.fromItems((invalidKey -> newValue)) - update.checkExpected((tree, (invalidKvs, updateProof)), None) + val input = (tree, (invalidKvs, updateProof)) + update.checkExpected(input, None) + update.checkVerify(input, expectedRes = None, expectedCost = cost) } { // negative: invalid value (different from the value in the proof) val tree = createTree(preUpdateDigest, updateAllowed = true) val invalidValue = newValue.map(x => (-x).toByte) val invalidKvs = Colls.fromItems((key -> invalidValue)) - val res = update.checkEquality((tree, (invalidKvs, updateProof))) - res.get.isDefined shouldBe true // TODO HF: should it really be true? (looks like a bug) + val input = (tree, (invalidKvs, updateProof)) + val (res, _) = update.checkEquality(input).getOrThrow + res.isDefined shouldBe true // TODO HF: should it really be true? (looks like a bug) + update.checkVerify(input, expectedRes = res, expectedCost = cost) } { // negative: invalid proof val tree = createTree(preUpdateDigest, updateAllowed = true) val invalidProof = updateProof.map(x => (-x).toByte) // any other different from proof - update.checkExpected((tree, (kvs, invalidProof)), None) + val input = (tree, (kvs, invalidProof)) + update.checkExpected(input, None) + update.checkVerify(input, expectedRes = None, expectedCost = cost) } } } @@ -2028,86 +2228,105 @@ class SigmaDslSpec extends SigmaDslTesting { suite => val removeProof = performRemove(avlProver, key) val endDigest = avlProver.digest.toColl val keys = Colls.fromItems(key) + val cost = 38270 { // positive val preRemoveTree = createTree(preRemoveDigest, removeAllowed = true) val endTree = preRemoveTree.updateDigest(endDigest) - remove.checkExpected((preRemoveTree, (keys, removeProof)), Some(endTree)) + val input = (preRemoveTree, (keys, removeProof)) + val res = Some(endTree) + remove.checkExpected(input, res) + remove.checkVerify(input, expectedRes = res, expectedCost = cost) } { // negative: readonly tree val readonlyTree = createTree(preRemoveDigest) - remove.checkExpected((readonlyTree, (keys, removeProof)), None) + val input = (readonlyTree, (keys, removeProof)) + remove.checkExpected(input, None) + remove.checkVerify(input, expectedRes = None, expectedCost = cost) } { // negative: invalid key val tree = createTree(preRemoveDigest, removeAllowed = true) val invalidKey = key.map(x => (-x).toByte) // any other different from `key` val invalidKeys = Colls.fromItems(invalidKey) - remove.checkExpected((tree, (invalidKeys, removeProof)), None) + val input = (tree, (invalidKeys, removeProof)) + remove.checkExpected(input, None) + remove.checkVerify(input, expectedRes = None, expectedCost = cost) } { // negative: invalid proof val tree = createTree(preRemoveDigest, removeAllowed = true) val invalidProof = removeProof.map(x => (-x).toByte) // any other different from `removeProof` - remove.checkExpected((tree, (keys, invalidProof)), None) + val input = (tree, (keys, invalidProof)) + remove.checkExpected(input, None) + remove.checkVerify(input, expectedRes = None, expectedCost = cost) } } } property("longToByteArray equivalence") { - testCases( - Seq( - (-9223372036854775808L, Success(Helpers.decodeBytes("8000000000000000"))), - (-1148502660425090565L, Success(Helpers.decodeBytes("f00fb2ea55c579fb"))), - (-1L, Success(Helpers.decodeBytes("ffffffffffffffff"))), - (0L, Success(Helpers.decodeBytes("0000000000000000"))), - (1L, Success(Helpers.decodeBytes("0000000000000001"))), - (238790047448232028L, Success(Helpers.decodeBytes("03505a48720cf05c"))), - (9223372036854775807L, Success(Helpers.decodeBytes("7fffffffffffffff"))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36007)) + Seq( + (-9223372036854775808L, success(Helpers.decodeBytes("8000000000000000"))), + (-1148502660425090565L, success(Helpers.decodeBytes("f00fb2ea55c579fb"))), + (-1L, success(Helpers.decodeBytes("ffffffffffffffff"))), + (0L, success(Helpers.decodeBytes("0000000000000000"))), + (1L, success(Helpers.decodeBytes("0000000000000001"))), + (238790047448232028L, success(Helpers.decodeBytes("03505a48720cf05c"))), + (9223372036854775807L, success(Helpers.decodeBytes("7fffffffffffffff"))) + ) + }, existingFeature((x: Long) => SigmaDsl.longToByteArray(x), "{ (x: Long) => longToByteArray(x) }", FuncValue(Vector((1, SLong)), LongToByteArray(ValUse(1, SLong))))) } property("byteArrayToBigInt equivalence") { - testCases( - Seq( - (Helpers.decodeBytes(""), - Failure(new NumberFormatException("Zero length BigInteger"))), - (Helpers.decodeBytes("00"), - Success(CBigInt(new BigInteger("0", 16)))), - (Helpers.decodeBytes("01"), - Success(CBigInt(new BigInteger("1", 16)))), - (Helpers.decodeBytes("ff"), - Success(CBigInt(new BigInteger("-1", 16)))), - (Helpers.decodeBytes("80d6c201"), - Success(CBigInt(new BigInteger("-7f293dff", 16)))), - (Helpers.decodeBytes("70d6c201"), - Success(CBigInt(new BigInteger("70d6c201", 16)))), - (Helpers.decodeBytes( - "80e0ff7f02807fff72807f0a00ff7fb7c57f75c11ba2802970fd250052807fc37f6480ffff007fff18eeba44" - ), Failure(new ArithmeticException("BigInteger out of 256 bit range"))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36536)) + Seq( + (Helpers.decodeBytes(""), + Failure(new NumberFormatException("Zero length BigInteger"))), + (Helpers.decodeBytes("00"), + success(CBigInt(new BigInteger("0", 16)))), + (Helpers.decodeBytes("01"), + success(CBigInt(new BigInteger("1", 16)))), + (Helpers.decodeBytes("ff"), + success(CBigInt(new BigInteger("-1", 16)))), + (Helpers.decodeBytes("80d6c201"), + Success(Expected(CBigInt(new BigInteger("-7f293dff", 16)), 36539))), + (Helpers.decodeBytes("70d6c20170d6c201"), + Success(Expected(CBigInt(new BigInteger("70d6c20170d6c201", 16)), 36543))), + (Helpers.decodeBytes( + "80e0ff7f02807fff72807f0a00ff7fb7c57f75c11ba2802970fd250052807fc37f6480ffff007fff18eeba44" + ), Failure(new ArithmeticException("BigInteger out of 256 bit range"))) + ) + }, existingFeature((x: Coll[Byte]) => SigmaDsl.byteArrayToBigInt(x), "{ (x: Coll[Byte]) => byteArrayToBigInt(x) }", FuncValue(Vector((1, SByteArray)), ByteArrayToBigInt(ValUse(1, SByteArray))))) } property("byteArrayToLong equivalence") { - testCases( - Seq( - (Helpers.decodeBytes(""), Failure(new IllegalArgumentException("array too small: 0 < 8"))), - (Helpers.decodeBytes("81"), Failure(new IllegalArgumentException("array too small: 1 < 8"))), - (Helpers.decodeBytes("812d7f00ff807f"), Failure(new IllegalArgumentException("array too small: 7 < 8"))), - (Helpers.decodeBytes("812d7f00ff807f7f"), Success(-9138508426601529473L)), - (Helpers.decodeBytes("ffffffffffffffff"), Success(-1L)), - (Helpers.decodeBytes("0000000000000000"), Success(0L)), - (Helpers.decodeBytes("0000000000000001"), Success(1L)), - (Helpers.decodeBytes("712d7f00ff807f7f"), Success(8155314142501175167L)), - (Helpers.decodeBytes("812d7f00ff807f7f0101018050757f0580ac009680f2ffc1"), Success(-9138508426601529473L)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36092)) + Seq( + (Helpers.decodeBytes(""), Failure(new IllegalArgumentException("array too small: 0 < 8"))), + (Helpers.decodeBytes("81"), Failure(new IllegalArgumentException("array too small: 1 < 8"))), + (Helpers.decodeBytes("812d7f00ff807f"), Failure(new IllegalArgumentException("array too small: 7 < 8"))), + (Helpers.decodeBytes("812d7f00ff807f7f"), success(-9138508426601529473L)), + (Helpers.decodeBytes("ffffffffffffffff"), success(-1L)), + (Helpers.decodeBytes("0000000000000000"), success(0L)), + (Helpers.decodeBytes("0000000000000001"), success(1L)), + (Helpers.decodeBytes("712d7f00ff807f7f"), success(8155314142501175167L)), + (Helpers.decodeBytes("812d7f00ff807f7f0101018050757f0580ac009680f2ffc1"), success(-9138508426601529473L)) + ) + }, existingFeature((x: Coll[Byte]) => SigmaDsl.byteArrayToLong(x), "{ (x: Coll[Byte]) => byteArrayToLong(x) }", FuncValue(Vector((1, SByteArray)), ByteArrayToLong(ValUse(1, SByteArray))))) @@ -2200,86 +2419,103 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ) - testCases( - Seq( - (b1, Success(Helpers.decodeBytes("5ee78f30ae4e770e44900a46854e9fecb6b12e8112556ef1cd19aef633b4421e"))), - (b2, Success(Helpers.decodeBytes("3a0089be265460e29ca47d26e5b55a6f3e3ffaf5b4aed941410a2437913848ad"))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35984)) + Seq( + (b1, success(Helpers.decodeBytes("5ee78f30ae4e770e44900a46854e9fecb6b12e8112556ef1cd19aef633b4421e"))), + (b2, success(Helpers.decodeBytes("3a0089be265460e29ca47d26e5b55a6f3e3ffaf5b4aed941410a2437913848ad"))) + ) + }, existingFeature({ (x: Box) => x.id }, "{ (x: Box) => x.id }", FuncValue(Vector((1, SBox)), ExtractId(ValUse(1, SBox))))) - testCases( - Seq( - (b1, Success(9223372036854775807L)), - (b2, Success(12345L)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35882)) + Seq( + (b1, success(9223372036854775807L)), + (b2, success(12345L)) + ) + }, existingFeature({ (x: Box) => x.value }, "{ (x: Box) => x.value }", FuncValue(Vector((1, SBox)), ExtractAmount(ValUse(1, SBox))))) - testCases( - Seq( - (b1, Success(Helpers.decodeBytes( - "100108cd0297c44a12f4eb99a85d298fa3ba829b5b42b9f63798c980ece801cc663cc5fc9e7300" - ))), - (b2, Success(Helpers.decodeBytes("00d1968302010100ff83020193040204020100"))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35938)) + Seq( + (b1, success(Helpers.decodeBytes( + "100108cd0297c44a12f4eb99a85d298fa3ba829b5b42b9f63798c980ece801cc663cc5fc9e7300" + ))), + (b2, success(Helpers.decodeBytes("00d1968302010100ff83020193040204020100"))) + ) + }, existingFeature({ (x: Box) => x.propositionBytes }, "{ (x: Box) => x.propositionBytes }", FuncValue(Vector((1, SBox)), ExtractScriptBytes(ValUse(1, SBox))))) - testCases( - Seq( - (b1, Success(Helpers.decodeBytes( - "ffffffffffffffff7f100108cd0297c44a12f4eb99a85d298fa3ba829b5b42b9f63798c980ece801cc663cc5fc9e73009fac29026e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f000180ade204a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600f4030201000e067fc87f7f01ff218301ae8000018008637f0021fb9e00018055486f0b514121016a00ff718080bcb001" - ))), - (b2, Success(Helpers.decodeBytes( - "b96000d1968302010100ff83020193040204020100c0843d000401010e32297000800b80f1d56c809a8c6affbed864b87f007f6f007f00ac00018c01c4fdff011088807f0100657f00f9ab0101ff6d6505a4a7b5a2e7a4a4dd3a05feffffffffffffffff01003bd5c630803cfff6c1ff7f7fb980ff136afc011f8080b8b04ad4dbda2d7f4e01" - ))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36012)) + Seq( + (b1, success(Helpers.decodeBytes( + "ffffffffffffffff7f100108cd0297c44a12f4eb99a85d298fa3ba829b5b42b9f63798c980ece801cc663cc5fc9e73009fac29026e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f000180ade204a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600f4030201000e067fc87f7f01ff218301ae8000018008637f0021fb9e00018055486f0b514121016a00ff718080bcb001" + ))), + (b2, success(Helpers.decodeBytes( + "b96000d1968302010100ff83020193040204020100c0843d000401010e32297000800b80f1d56c809a8c6affbed864b87f007f6f007f00ac00018c01c4fdff011088807f0100657f00f9ab0101ff6d6505a4a7b5a2e7a4a4dd3a05feffffffffffffffff01003bd5c630803cfff6c1ff7f7fb980ff136afc011f8080b8b04ad4dbda2d7f4e01" + ))) + ) + }, existingFeature({ (x: Box) => x.bytes }, "{ (x: Box) => x.bytes }", FuncValue(Vector((1, SBox)), ExtractBytes(ValUse(1, SBox))))) - testCases( - Seq( - (b1, Success(Helpers.decodeBytes( - "ffffffffffffffff7f100108cd0297c44a12f4eb99a85d298fa3ba829b5b42b9f63798c980ece801cc663cc5fc9e73009fac29026e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f000180ade204a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600f4030201000e067fc87f7f01ff" - ))), - (b2, Success(Helpers.decodeBytes( - "b96000d1968302010100ff83020193040204020100c0843d000401010e32297000800b80f1d56c809a8c6affbed864b87f007f6f007f00ac00018c01c4fdff011088807f0100657f00f9ab0101ff6d6505a4a7b5a2e7a4a4dd3a05feffffffffffffffff01" - ))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35954)) + Seq( + (b1, success(Helpers.decodeBytes( + "ffffffffffffffff7f100108cd0297c44a12f4eb99a85d298fa3ba829b5b42b9f63798c980ece801cc663cc5fc9e73009fac29026e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f000180ade204a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600f4030201000e067fc87f7f01ff" + ))), + (b2, success(Helpers.decodeBytes( + "b96000d1968302010100ff83020193040204020100c0843d000401010e32297000800b80f1d56c809a8c6affbed864b87f007f6f007f00ac00018c01c4fdff011088807f0100657f00f9ab0101ff6d6505a4a7b5a2e7a4a4dd3a05feffffffffffffffff01" + ))) + ) + }, existingFeature({ (x: Box) => x.bytesWithoutRef }, "{ (x: Box) => x.bytesWithoutRef }", FuncValue(Vector((1, SBox)), ExtractBytesWithNoRef(ValUse(1, SBox))))) - testCases( - Seq( - (b1, Success(( - 677407, - Helpers.decodeBytes("218301ae8000018008637f0021fb9e00018055486f0b514121016a00ff718080583c") - ))), - (b2, Success(( - 1000000, - Helpers.decodeBytes("003bd5c630803cfff6c1ff7f7fb980ff136afc011f8080b8b04ad4dbda2d7f4e0001") - ))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36074)) + Seq( + (b1, success(( + 677407, + Helpers.decodeBytes("218301ae8000018008637f0021fb9e00018055486f0b514121016a00ff718080583c") + ))), + (b2, success(( + 1000000, + Helpers.decodeBytes("003bd5c630803cfff6c1ff7f7fb980ff136afc011f8080b8b04ad4dbda2d7f4e0001") + ))) + ) + }, existingFeature({ (x: Box) => x.creationInfo }, "{ (x: Box) => x.creationInfo }", FuncValue(Vector((1, SBox)), ExtractCreationInfo(ValUse(1, SBox))))) // TODO HF: fix collections equality and remove map(identity) // (PairOfColl should be equal CollOverArray) - testCases( + verifyCases( Seq( - (b1, Success(Coll[(Coll[Byte], Long)]( - (Helpers.decodeBytes("6e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f0001"), 10000000L), - (Helpers.decodeBytes("a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600"), 500L) - ).map(identity)) - ), - (b2, Success(Coll[(Coll[Byte], Long)]().map(identity))) + b1 -> Success(Expected(Coll[(Coll[Byte], Long)]( + (Helpers.decodeBytes("6e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f0001"), 10000000L), + (Helpers.decodeBytes("a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600"), 500L) + ).map(identity), 36167)), + b2 -> Success(Expected(Coll[(Coll[Byte], Long)]().map(identity), 36157)) ), existingFeature({ (x: Box) => x.tokens }, "{ (x: Box) => x.tokens }", @@ -2292,7 +2528,6 @@ class SigmaDslSpec extends SigmaDslTesting { suite => Map() ) ))) - } property("Box properties equivalence (new features)") { @@ -2308,7 +2543,7 @@ class SigmaDslSpec extends SigmaDslTesting { suite => property("Advanced Box test") { val (tree, _) = createAvlTreeAndProver() - val box1 = SigmaDsl.Box(ErgoBox(20, TrueProp, 0, Seq(), Map( + val box1 = SigmaDsl.Box(testBox(20, TrueProp, 0, Seq(), Map( ErgoBox.R4 -> ByteConstant(1.toByte), ErgoBox.R5 -> ShortConstant(1024.toShort), ErgoBox.R6 -> IntConstant(1024 * 1024), @@ -2317,13 +2552,13 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ErgoBox.R9 -> AvlTreeConstant(tree) ))) - val box2 = SigmaDsl.Box(ErgoBox(20, TrueProp, 0, Seq(), Map( + val box2 = SigmaDsl.Box(testBox(20, TrueProp, 0, Seq(), Map( ErgoBox.R4 -> ByteArrayConstant(Coll(1.toByte)) ))) - testCases( + verifyCases( Seq( - (box1, Success(1.toByte)), + (box1, Success(Expected(1.toByte, cost = 36253))), (box2, Failure(new InvalidType("Cannot getReg[Byte](4): invalid type of value Value(Coll(1)) at id=4"))) ), existingFeature((x: Box) => x.R4[Byte].get, @@ -2333,9 +2568,9 @@ class SigmaDslSpec extends SigmaDslTesting { suite => OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R4, SOption(SByte))) ))) - testCases( + verifyCases( Seq( - (box1, Success(1024.toShort)), + (box1, Success(Expected(1024.toShort, cost = 36253))), (box2, Failure(new NoSuchElementException("None.get"))) ), existingFeature((x: Box) => x.R5[Short].get, @@ -2345,9 +2580,9 @@ class SigmaDslSpec extends SigmaDslTesting { suite => OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R5, SOption(SShort))) ))) - testCases( + verifyCases( Seq( - (box1, Success(1024 * 1024)) + (box1, Success(Expected(1024 * 1024, cost = 36253))) ), existingFeature((x: Box) => x.R6[Int].get, "{ (x: Box) => x.R6[Int].get }", @@ -2356,9 +2591,9 @@ class SigmaDslSpec extends SigmaDslTesting { suite => OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R6, SOption(SInt))) ))) - testCases( + verifyCases( Seq( - (box1, Success(1024.toLong)) + (box1, Success(Expected(1024.toLong, cost = 36253))) ), existingFeature((x: Box) => x.R7[Long].get, "{ (x: Box) => x.R7[Long].get }", @@ -2367,9 +2602,9 @@ class SigmaDslSpec extends SigmaDslTesting { suite => OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R7, SOption(SLong))) ))) - testCases( + verifyCases( Seq( - (box1, Success(CBigInt(BigInteger.valueOf(222L)))) + (box1, Success(Expected(CBigInt(BigInteger.valueOf(222L)), cost = 36253))) ), existingFeature((x: Box) => x.R8[BigInt].get, "{ (x: Box) => x.R8[BigInt].get }", @@ -2378,9 +2613,9 @@ class SigmaDslSpec extends SigmaDslTesting { suite => OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R8, SOption(SBigInt))) ))) - testCases( + verifyCases( Seq( - (box1, Success(CAvlTree( + (box1, Success(Expected(CAvlTree( AvlTreeData( ADDigest @@ ( ErgoAlgos.decodeUnsafe("4ec61f485b98eb87153f7c57db4f5ecd75556fddbc403b41acf8441fde8e160900") @@ -2389,7 +2624,7 @@ class SigmaDslSpec extends SigmaDslTesting { suite => 32, None ) - ))) + ), cost = 36253))) ), existingFeature((x: Box) => x.R9[AvlTree].get, "{ (x: Box) => x.R9[AvlTree].get }", @@ -2422,32 +2657,36 @@ class SigmaDslSpec extends SigmaDslTesting { suite => Helpers.decodeBytes("ff8087") ) - testCases( - Seq((h1, Success(0.toByte))), + verifyCases( + Seq((h1, Success(Expected(0.toByte, cost = 37022)))), existingPropTest("version", { (x: PreHeader) => x.version })) - testCases( - Seq((h1, Success(Helpers.decodeBytes("7fff7fdd6f62018bae0001006d9ca888ff7f56ff8006573700a167f17f2c9f40")))), + verifyCases( + Seq((h1, Success(Expected( + Helpers.decodeBytes("7fff7fdd6f62018bae0001006d9ca888ff7f56ff8006573700a167f17f2c9f40"), + cost = 36121)))), existingPropTest("parentId", { (x: PreHeader) => x.parentId })) - testCases( - Seq((h1, Success(6306290372572472443L))), + verifyCases( + Seq((h1, Success(Expected(6306290372572472443L, cost = 36147)))), existingPropTest("timestamp", { (x: PreHeader) => x.timestamp })) - testCases( - Seq((h1, Success(-3683306095029417063L))), + verifyCases( + Seq((h1, Success(Expected(-3683306095029417063L, cost = 36127)))), existingPropTest("nBits", { (x: PreHeader) => x.nBits })) - testCases( - Seq((h1, Success(1))), + verifyCases( + Seq((h1, Success(Expected(1, cost = 36136)))), existingPropTest("height", { (x: PreHeader) => x.height })) - testCases( - Seq((h1, Success(Helpers.decodeGroupElement("026930cb9972e01534918a6f6d6b8e35bc398f57140d13eb3623ea31fbd069939b")))), + verifyCases( + Seq((h1, Success(Expected( + Helpers.decodeGroupElement("026930cb9972e01534918a6f6d6b8e35bc398f57140d13eb3623ea31fbd069939b"), + cost = 36255)))), existingPropTest("minerPk", { (x: PreHeader) => x.minerPk })) - testCases( - Seq((h1, Success(Helpers.decodeBytes("ff8087")))), + verifyCases( + Seq((h1, Success(Expected(Helpers.decodeBytes("ff8087"), cost = 36249)))), existingPropTest("votes", { (x: PreHeader) => x.votes })) } @@ -2478,64 +2717,84 @@ class SigmaDslSpec extends SigmaDslTesting { suite => Helpers.decodeBytes("7f0180") ) - testCases( - Seq((h1, Success(Helpers.decodeBytes("957f008001808080ffe4ffffc8f3802401df40006aa05e017fa8d3f6004c804a")))), + verifyCases( + Seq((h1, Success(Expected( + Helpers.decodeBytes("957f008001808080ffe4ffffc8f3802401df40006aa05e017fa8d3f6004c804a"), + cost = 36955)))), existingPropTest("id", { (x: Header) => x.id })) - testCases( - Seq((h1, Success(0.toByte))), + verifyCases( + Seq((h1, Success(Expected(0.toByte, cost = 36124)))), existingPropTest("version", { (x: Header) => x.version })) - testCases( - Seq((h1, Success(Helpers.decodeBytes("0180dd805b0000ff5400b997fd7f0b9b00de00fb03c47e37806a8186b94f07ff")))), + verifyCases( + Seq((h1, Success(Expected( + Helpers.decodeBytes("0180dd805b0000ff5400b997fd7f0b9b00de00fb03c47e37806a8186b94f07ff"), + cost = 36097)))), existingPropTest("parentId", { (x: Header) => x.parentId })) - testCases( - Seq((h1, Success(Helpers.decodeBytes("01f07f60d100ffb970c3007f60ff7f24d4070bb8fffa7fca7f34c10001ffe39d")))), + verifyCases( + Seq((h1, Success(Expected( + Helpers.decodeBytes("01f07f60d100ffb970c3007f60ff7f24d4070bb8fffa7fca7f34c10001ffe39d"), + cost = 36111)))), existingPropTest("ADProofsRoot", { (x: Header) => x.ADProofsRoot})) - testCases( - Seq((h1, Success(CAvlTree(treeData)))), + verifyCases( + Seq((h1, Success(Expected(CAvlTree(treeData), cost = 36092)))), existingPropTest("stateRoot", { (x: Header) => x.stateRoot })) - testCases( - Seq((h1, Success(Helpers.decodeBytes("804101ff01000080a3ffbd006ac080098df132a7017f00649311ec0e00000100")))), + verifyCases( + Seq((h1, Success(Expected( + Helpers.decodeBytes("804101ff01000080a3ffbd006ac080098df132a7017f00649311ec0e00000100"), + cost = 36094)))), existingPropTest("transactionsRoot", { (x: Header) => x.transactionsRoot })) - testCases( - Seq((h1, Success(1L))), + verifyCases( + Seq((h1, Success(Expected(1L, cost = 36151)))), existingPropTest("timestamp", { (x: Header) => x.timestamp })) - testCases( - Seq((h1, Success(-1L))), + verifyCases( + Seq((h1, Success(Expected(-1L, cost = 36125)))), existingPropTest("nBits", { (x: Header) => x.nBits })) - testCases( - Seq((h1, Success(1))), + verifyCases( + Seq((h1, Success(Expected(1, cost = 36134)))), existingPropTest("height", { (x: Header) => x.height })) - testCases( - Seq((h1, Success(Helpers.decodeBytes("e57f80885601b8ff348e01808000bcfc767f2dd37f0d01015030ec018080bc62")))), + verifyCases( + Seq((h1, Success(Expected( + Helpers.decodeBytes("e57f80885601b8ff348e01808000bcfc767f2dd37f0d01015030ec018080bc62"), + cost = 36133)))), existingPropTest("extensionRoot", { (x: Header) => x.extensionRoot })) - testCases( - Seq((h1, Success(Helpers.decodeGroupElement("039bdbfa0b49cc6bef58297a85feff45f7bbeb500a9d2283004c74fcedd4bd2904")))), + verifyCases( + Seq((h1, Success(Expected( + Helpers.decodeGroupElement("039bdbfa0b49cc6bef58297a85feff45f7bbeb500a9d2283004c74fcedd4bd2904"), + cost = 36111)))), existingPropTest("minerPk", { (x: Header) => x.minerPk })) - testCases( - Seq((h1, Success(Helpers.decodeGroupElement("0361299207fa392231e23666f6945ae3e867b978e021d8d702872bde454e9abe9c")))), + verifyCases( + Seq((h1, Success(Expected( + Helpers.decodeGroupElement("0361299207fa392231e23666f6945ae3e867b978e021d8d702872bde454e9abe9c"), + cost = 36111)))), existingPropTest("powOnetimePk", { (x: Header) => x.powOnetimePk })) - testCases( - Seq((h1, Success(Helpers.decodeBytes("7f4f09012a807f01")))), + verifyCases( + Seq((h1, Success(Expected( + Helpers.decodeBytes("7f4f09012a807f01"), + cost = 36176)))), existingPropTest("powNonce", { (x: Header) => x.powNonce })) - testCases( - Seq((h1, Success(CBigInt(new BigInteger("-e24990c47e15ed4d0178c44f1790cc72155d516c43c3e8684e75db3800a288", 16))))), + verifyCases( + Seq((h1, Success(Expected( + CBigInt(new BigInteger("-e24990c47e15ed4d0178c44f1790cc72155d516c43c3e8684e75db3800a288", 16)), + cost = 36220)))), existingPropTest("powDistance", { (x: Header) => x.powDistance })) - testCases( - Seq((h1, Success(Helpers.decodeBytes("7f0180")))), + verifyCases( + Seq((h1, Success(Expected( + Helpers.decodeBytes("7f0180"), + cost = 36100)))), existingPropTest("votes", { (x: Header) => x.votes })) } @@ -2706,75 +2965,103 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ), _minerPubKey = Helpers.decodeBytes("0227a58e9b2537103338c237c52c1213bf44bdb344fa07d9df8ab826cca26ca08f"), - vars = Coll[AnyValue](null, TestValue(Helpers.decodeBytes("00"), CollType(RType.ByteType)), TestValue(true, RType.BooleanType)), + vars = Colls + .replicate[AnyValue](10, null) // reserve 10 vars + .append(Coll[AnyValue]( + TestValue(Helpers.decodeBytes("00"), CollType(RType.ByteType)), + TestValue(true, RType.BooleanType))), false ) + val ctx2 = ctx.copy(vars = Coll[AnyValue](null, null, null)) + val ctx3 = ctx.copy(vars = Coll[AnyValue]()) test(samples, existingPropTest("dataInputs", { (x: Context) => x.dataInputs })) - testCases( + verifyCases( Seq( - (ctx, Success(dataBox)), + (ctx, Success(Expected(dataBox, cost = 37087))), (ctx.copy(_dataInputs = Coll()), Failure(new ArrayIndexOutOfBoundsException("0"))) ), existingFeature({ (x: Context) => x.dataInputs(0) }, "{ (x: Context) => x.dataInputs(0) }", FuncValue( - Vector((1, SContext)), - ByIndex( - MethodCall.typed[Value[SCollection[SBox.type]]]( - ValUse(1, SContext), - SContext.getMethodByName("dataInputs"), - Vector(), - Map() - ), - IntConstant(0), - None - ) - )), + Vector((1, SContext)), + ByIndex( + MethodCall.typed[Value[SCollection[SBox.type]]]( + ValUse(1, SContext), + SContext.getMethodByName("dataInputs"), + Vector(), + Map() + ), + IntConstant(0), + None + ) + )), preGeneratedSamples = Some(samples)) - testCases( + verifyCases( Seq( - (ctx, Success(Helpers.decodeBytes("7da4b55971f19a78d007638464580f91a020ab468c0dbe608deb1f619e245bc3"))) + (ctx, Success(Expected( + Helpers.decodeBytes("7da4b55971f19a78d007638464580f91a020ab468c0dbe608deb1f619e245bc3"), + cost = 37193))) ), existingFeature({ (x: Context) => x.dataInputs(0).id }, "{ (x: Context) => x.dataInputs(0).id }", FuncValue( - Vector((1, SContext)), - ExtractId( - ByIndex( - MethodCall.typed[Value[SCollection[SBox.type]]]( - ValUse(1, SContext), - SContext.getMethodByName("dataInputs"), - Vector(), - Map() - ), - IntConstant(0), - None - ) - ) - )), + Vector((1, SContext)), + ExtractId( + ByIndex( + MethodCall.typed[Value[SCollection[SBox.type]]]( + ValUse(1, SContext), + SContext.getMethodByName("dataInputs"), + Vector(), + Map() + ), + IntConstant(0), + None + ) + ) + )), preGeneratedSamples = Some(samples)) - test(samples, existingPropTest("preHeader", { (x: Context) => x.preHeader })) + // NOTE: testCases2 is not used below because PreHeader/Header cannot be put in + // registers and context vars + testCases( + Seq(ctx -> Success(ctx.preHeader)), + existingPropTest("preHeader", { (x: Context) => x.preHeader }), + preGeneratedSamples = Some(samples)) - test(samples, existingPropTest("headers", { (x: Context) => x.headers })) + testCases( + Seq(ctx -> Success(ctx.headers)), + existingPropTest("headers", { (x: Context) => x.headers }), + preGeneratedSamples = Some(samples)) - test(samples, existingFeature({ (x: Context) => x.OUTPUTS }, - "{ (x: Context) => x.OUTPUTS }", FuncValue(Vector((1, SContext)), Outputs))) + // TODO: testCases2 doesn't work because of equality (check the reason) + testCases( + Seq(ctx -> Success(ctx.OUTPUTS)), + existingFeature( + { (x: Context) => x.OUTPUTS }, + "{ (x: Context) => x.OUTPUTS }", + FuncValue(Vector((1, SContext)), Outputs)), + preGeneratedSamples = Some(samples)) + // NOTE: testCases2 is not supported because SELF modified to pass input test(samples, existingFeature({ (x: Context) => x.INPUTS }, "{ (x: Context) => x.INPUTS }", FuncValue(Vector((1, SContext)), Inputs))) - test(samples, existingFeature({ (x: Context) => x.HEIGHT }, - "{ (x: Context) => x.HEIGHT }", FuncValue(Vector((1, SContext)), Height))) - test(samples, existingFeature({ (x: Context) => x.SELF }, - "{ (x: Context) => x.SELF }", FuncValue(Vector((1, SContext)), Self))) + "{ (x: Context) => x.SELF }", FuncValue(Vector((1, SContext)), Self))) - testCases( - Seq((ctx, Success(Coll[Long](80946L)))), + verifyCases( + Seq(ctx -> Success(Expected(ctx.HEIGHT, cost = 35885))), + existingFeature( + { (x: Context) => x.HEIGHT }, + "{ (x: Context) => x.HEIGHT }", + FuncValue(Vector((1, SContext)), Height)), + preGeneratedSamples = Some(samples)) + + verifyCases( + Seq((ctx, Success(Expected(Coll[Long](80946L), cost = 39152)))), existingFeature( { (x: Context) => x.INPUTS.map { (b: Box) => b.value } }, "{ (x: Context) => x.INPUTS.map { (b: Box) => b.value } }", @@ -2784,30 +3071,29 @@ class SigmaDslSpec extends SigmaDslTesting { suite => )), preGeneratedSamples = Some(samples)) - testCases( - Seq((ctx, Success(Coll((80946L, 80946L))))), + verifyCases( + Seq((ctx, Success(Expected(Coll((80946L, 80946L)), cost = 39959)))), existingFeature( { (x: Context) => x.INPUTS.map { (b: Box) => (b.value, b.value) } }, """{ (x: Context) => | x.INPUTS.map { (b: Box) => (b.value, b.value) } |}""".stripMargin, FuncValue( - Vector((1, SContext)), - MapCollection( - Inputs, - FuncValue( - Vector((3, SBox)), - BlockValue( - Vector(ValDef(5, List(), ExtractAmount(ValUse(3, SBox)))), - Tuple(Vector(ValUse(5, SLong), ValUse(5, SLong))) - ) - ) - ) - )), + Vector((1, SContext)), + MapCollection( + Inputs, + FuncValue( + Vector((3, SBox)), + BlockValue( + Vector(ValDef(5, List(), ExtractAmount(ValUse(3, SBox)))), + Tuple(Vector(ValUse(5, SLong), ValUse(5, SLong))) + ) + ) + ) + )), preGeneratedSamples = Some(samples)) - - testCases( + verifyCases( Seq((ctx, Failure(new InvalidType("Cannot getReg[Int](4): invalid type of value Value(Coll(52)) at id=4")))), existingFeature( { (x: Context) => @@ -2825,35 +3111,35 @@ class SigmaDslSpec extends SigmaDslTesting { suite => | } |}""".stripMargin, FuncValue( - Vector((1, SContext)), - MapCollection( - Inputs, - FuncValue( - Vector((3, SBox)), - Tuple( - Vector( - OptionGet(ExtractRegisterAs(ValUse(3, SBox), ErgoBox.R4, SOption(SInt))), - LongToByteArray(ExtractAmount(ValUse(3, SBox))) - ) - ) - ) - ) - )), + Vector((1, SContext)), + MapCollection( + Inputs, + FuncValue( + Vector((3, SBox)), + Tuple( + Vector( + OptionGet(ExtractRegisterAs(ValUse(3, SBox), ErgoBox.R4, SOption(SInt))), + LongToByteArray(ExtractAmount(ValUse(3, SBox))) + ) + ) + ) + ) + )), preGeneratedSamples = Some(samples)) - testCases( - Seq((ctx, Success(-1))), + verifyCases( + Seq((ctx, Success(Expected(-1, cost = 36318)))), existingFeature({ (x: Context) => x.selfBoxIndex }, "{ (x: Context) => x.selfBoxIndex }", FuncValue( - Vector((1, SContext)), - MethodCall.typed[Value[SInt.type]]( - ValUse(1, SContext), - SContext.getMethodByName("selfBoxIndex"), - Vector(), - Map() - ) - )), + Vector((1, SContext)), + MethodCall.typed[Value[SInt.type]]( + ValUse(1, SContext), + SContext.getMethodByName("selfBoxIndex"), + Vector(), + Map() + ) + )), preGeneratedSamples = Some(samples)) // TODO HF: see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/603 @@ -2861,155 +3147,200 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ctx.selfBoxIndex shouldBe -1 } - test(samples, - existingPropTest("LastBlockUtxoRootHash", { (x: Context) => x.LastBlockUtxoRootHash })) + verifyCases( + Seq(ctx -> Success(Expected(ctx.LastBlockUtxoRootHash, cost = 35990))), + existingPropTest("LastBlockUtxoRootHash", { (x: Context) => x.LastBlockUtxoRootHash }), + preGeneratedSamples = Some(samples)) - test(samples, existingFeature( - { (x: Context) => x.LastBlockUtxoRootHash.isUpdateAllowed }, - "{ (x: Context) => x.LastBlockUtxoRootHash.isUpdateAllowed }", - FuncValue( - Vector((1, SContext)), - MethodCall.typed[Value[SBoolean.type]]( - MethodCall.typed[Value[SAvlTree.type]]( - ValUse(1, SContext), - SContext.getMethodByName("LastBlockUtxoRootHash"), + verifyCases( + Seq(ctx -> Success(Expected(ctx.LastBlockUtxoRootHash.isUpdateAllowed, cost = 36288))), + existingFeature( + { (x: Context) => x.LastBlockUtxoRootHash.isUpdateAllowed }, + "{ (x: Context) => x.LastBlockUtxoRootHash.isUpdateAllowed }", + FuncValue( + Vector((1, SContext)), + MethodCall.typed[Value[SBoolean.type]]( + MethodCall.typed[Value[SAvlTree.type]]( + ValUse(1, SContext), + SContext.getMethodByName("LastBlockUtxoRootHash"), + Vector(), + Map() + ), + SAvlTree.getMethodByName("isUpdateAllowed"), Vector(), Map() - ), - SAvlTree.getMethodByName("isUpdateAllowed"), - Vector(), - Map() - ) - ))) + ) + )), + preGeneratedSamples = Some(samples)) - test(samples, existingPropTest("minerPubKey", { (x: Context) => x.minerPubKey })) + verifyCases( + Seq(ctx -> Success(Expected(ctx.minerPubKey, cost = 36047))), + existingPropTest("minerPubKey", { (x: Context) => x.minerPubKey }), + preGeneratedSamples = Some(samples)) +// TODO HF: implement support of Option[T] in DataSerializer +// this will allow passing optional values in registers and also in constants +// testCases2( +// Seq( +// ctx -> Success(Expected(Some(true), cost = 0)), +// ctx2 -> Success(Expected(None, cost = 0)), +// ctx3 -> Success(Expected(None, cost = 0)) +// ), testCases( - Seq((ctx, Failure(new InvalidType("Cannot getVar[Int](2): invalid type of value Value(true) at id=2")))), - existingFeature((x: Context) => x.getVar[Int](2).get, - "{ (x: Context) => getVar[Int](2).get }", - FuncValue(Vector((1, SContext)), OptionGet(GetVar(2.toByte, SOption(SInt))))), + Seq( + ctx -> Success(Some(true)), + ctx2 -> Success(None), + ctx3 -> Success(None) + ), + existingFeature((x: Context) => x.getVar[Boolean](11), + "{ (x: Context) => getVar[Boolean](11) }", + FuncValue(Vector((1, SContext)), GetVar(11.toByte, SOption(SBoolean)))), preGeneratedSamples = Some(samples)) - testCases( - Seq((ctx, Success(true))), - existingFeature((x: Context) => x.getVar[Boolean](2).get, - "{ (x: Context) => getVar[Boolean](2).get }", - FuncValue(Vector((1, SContext)), OptionGet(GetVar(2.toByte, SOption(SBoolean))))), + verifyCases( + Seq((ctx, Failure(new InvalidType("Cannot getVar[Int](11): invalid type of value Value(true) at id=2")))), + existingFeature((x: Context) => x.getVar[Int](11).get, + "{ (x: Context) => getVar[Int](11).get }", + FuncValue(Vector((1, SContext)), OptionGet(GetVar(11.toByte, SOption(SInt))))), + preGeneratedSamples = Some(samples)) + + verifyCases( + Seq((ctx, Success(Expected(true, cost = 36750)))), + existingFeature((x: Context) => x.getVar[Boolean](11).get, + "{ (x: Context) => getVar[Boolean](11).get }", + FuncValue(Vector((1, SContext)), OptionGet(GetVar(11.toByte, SOption(SBoolean))))), preGeneratedSamples = Some(samples)) } property("xorOf equivalence") { // TODO HF: see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/640 - testCases( - Seq( - (Coll[Boolean](false), Success(false)), - (Coll[Boolean](true), Success(false)), - (Coll[Boolean](false, false), Success(false)), - (Coll[Boolean](false, true), Success(true)), - (Coll[Boolean](true, false), Success(true)), - (Coll[Boolean](true, true), Success(false)), - (Coll[Boolean](false, false, false), Success(false)), - (Coll[Boolean](false, false, true), Success(true)), - (Coll[Boolean](false, true, false), Success(true)), - (Coll[Boolean](false, true, true), Success(true)), - (Coll[Boolean](true, false, false), Success(true)), - (Coll[Boolean](true, false, true), Success(true)), - (Coll[Boolean](true, true, false), Success(true)), - (Coll[Boolean](true, true, true), Success(false)), - (Coll[Boolean](false, false, false, false), Success(false)), - (Coll[Boolean](false, false, false, true), Success(true)), - (Coll[Boolean](false, false, true, false), Success(true)), - (Coll[Boolean](false, false, true, true), Success(true)) - ), + verifyCases( + { + def success[T](v: T, c: Int) = Success(Expected(v, c)) + Seq( + (Coll[Boolean](false), success(false, 37071)), + (Coll[Boolean](true), success(false, 37071)), + (Coll[Boolean](false, false), success(false, 37081)), + (Coll[Boolean](false, true), success(true, 37081)), + (Coll[Boolean](true, false), success(true, 37081)), + (Coll[Boolean](true, true), success(false, 37081)), + (Coll[Boolean](false, false, false), success(false, 37091)), + (Coll[Boolean](false, false, true), success(true, 37091)), + (Coll[Boolean](false, true, false), success(true, 37091)), + (Coll[Boolean](false, true, true), success(true, 37091)), + (Coll[Boolean](true, false, false), success(true, 37091)), + (Coll[Boolean](true, false, true), success(true, 37091)), + (Coll[Boolean](true, true, false), success(true, 37091)), + (Coll[Boolean](true, true, true), success(false, 37091)), + (Coll[Boolean](false, false, false, false), success(false, 37101)), + (Coll[Boolean](false, false, false, true), success(true, 37101)), + (Coll[Boolean](false, false, true, false), success(true, 37101)), + (Coll[Boolean](false, false, true, true), success(true, 37101)) + ) + }, existingFeature((x: Coll[Boolean]) => SigmaDsl.xorOf(x), "{ (x: Coll[Boolean]) => xorOf(x) }", FuncValue(Vector((1, SBooleanArray)), XorOf(ValUse(1, SBooleanArray))))) } property("LogicalNot equivalence") { - testCases( + verifyCases( Seq( - (true, Success(false)), - (false, Success(true))), + (true, Success(Expected(false, 35864))), + (false, Success(Expected(true, 35864)))), existingFeature((x: Boolean) => !x, "{ (x: Boolean) => !x }", - FuncValue(Vector((1, SBoolean)), LogicalNot(ValUse(1, SBoolean)))), true) + FuncValue(Vector((1, SBoolean)), LogicalNot(ValUse(1, SBoolean))))) } property("Numeric Negation equivalence") { - testCases( - Seq( - (Byte.MinValue, Success(Byte.MinValue)), // !!! - (-40.toByte, Success(40.toByte)), - (-1.toByte, Success(1.toByte)), - (0.toByte, Success(0.toByte)), - (1.toByte, Success(-1.toByte)), - (45.toByte, Success(-45.toByte)), - (127.toByte, Success(-127.toByte))), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36136)) + Seq( + (Byte.MinValue, success(Byte.MinValue)), // !!! + (-40.toByte, success(40.toByte)), + (-1.toByte, success(1.toByte)), + (0.toByte, success(0.toByte)), + (1.toByte, success(-1.toByte)), + (45.toByte, success(-45.toByte)), + (127.toByte, success(-127.toByte))) + }, existingFeature((x: Byte) => (-x).toByte, "{ (x: Byte) => -x }", FuncValue(Vector((1, SByte)), Negation(ValUse(1, SByte))))) - testCases( - Seq( - (Short.MinValue, Success(Short.MinValue)), // special case! - ((Short.MinValue + 1).toShort, Success(32767.toShort)), - (-1528.toShort, Success(1528.toShort)), - (-1.toShort, Success(1.toShort)), - (0.toShort, Success(0.toShort)), - (1.toShort, Success(-1.toShort)), - (7586.toShort, Success(-7586.toShort)), - (Short.MaxValue, Success(-32767.toShort))), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36136)) + Seq( + (Short.MinValue, success(Short.MinValue)), // special case! + ((Short.MinValue + 1).toShort, success(32767.toShort)), + (-1528.toShort, success(1528.toShort)), + (-1.toShort, success(1.toShort)), + (0.toShort, success(0.toShort)), + (1.toShort, success(-1.toShort)), + (7586.toShort, success(-7586.toShort)), + (Short.MaxValue, success(-32767.toShort))) + }, existingFeature((x: Short) => (-x).toShort, "{ (x: Short) => -x }", FuncValue(Vector((1, SShort)), Negation(ValUse(1, SShort))))) - testCases( - Seq( - (Int.MinValue, Success(Int.MinValue)), // special case! - (Int.MinValue + 1, Success(2147483647)), - (-63509744, Success(63509744)), - (-1, Success(1)), - (0, Success(0)), - (1, Success(-1)), - (677062351, Success(-677062351)), - (Int.MaxValue, Success(-2147483647))), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36136)) + Seq( + (Int.MinValue, success(Int.MinValue)), // special case! + (Int.MinValue + 1, success(2147483647)), + (-63509744, success(63509744)), + (-1, success(1)), + (0, success(0)), + (1, success(-1)), + (677062351, success(-677062351)), + (Int.MaxValue, success(-2147483647))) + }, existingFeature((x: Int) => -x, "{ (x: Int) => -x }", FuncValue(Vector((1, SInt)), Negation(ValUse(1, SInt))))) - testCases( - Seq( - (Long.MinValue, Success(Long.MinValue)), // special case! - (Long.MinValue + 1, Success(9223372036854775807L)), - (-957264171003115006L, Success(957264171003115006L)), - (-1L, Success(1L)), - (0L, Success(0L)), - (1L, Success(-1L)), - (340835904095777627L, Success(-340835904095777627L)), - (9223372036854775807L, Success(-9223372036854775807L))), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36136)) + Seq( + (Long.MinValue, success(Long.MinValue)), // special case! + (Long.MinValue + 1, success(9223372036854775807L)), + (-957264171003115006L, success(957264171003115006L)), + (-1L, success(1L)), + (0L, success(0L)), + (1L, success(-1L)), + (340835904095777627L, success(-340835904095777627L)), + (9223372036854775807L, success(-9223372036854775807L))) + }, existingFeature((x: Long) => -x, "{ (x: Long) => -x }", FuncValue(Vector((1, SLong)), Negation(ValUse(1, SLong))))) - testCases( - Seq( - (CBigInt(new BigInteger("-1655a05845a6ad363ac88ea21e88b97e436a1f02c548537e12e2d9667bf0680", 16)), Success(CBigInt(new BigInteger("1655a05845a6ad363ac88ea21e88b97e436a1f02c548537e12e2d9667bf0680", 16)))), - (CBigInt(new BigInteger("-1b24ba8badba8abf347cce054d9b9f14f229321507245b8", 16)), Success(CBigInt(new BigInteger("1b24ba8badba8abf347cce054d9b9f14f229321507245b8", 16)))), - (CBigInt(new BigInteger("-1ec9cca2c346cb72a1e65481eaa0627d", 16)), Success(CBigInt(new BigInteger("1ec9cca2c346cb72a1e65481eaa0627d", 16)))), - (CBigInt(new BigInteger("-8000000000000001", 16)), Success(CBigInt(new BigInteger("8000000000000001", 16)))), - (CBigInt(new BigInteger("-8000000000000000", 16)), Success(CBigInt(new BigInteger("8000000000000000", 16)))), - (CBigInt(new BigInteger("-48afe3e059821cd6", 16)), Success(CBigInt(new BigInteger("48afe3e059821cd6", 16)))), - (CBigInt(new BigInteger("-80000001", 16)), Success(CBigInt(new BigInteger("80000001", 16)))), - (CBigInt(new BigInteger("-80000000", 16)), Success(CBigInt(new BigInteger("80000000", 16)))), - (CBigInt(new BigInteger("-1", 16)), Success(CBigInt(new BigInteger("1", 16)))), - (CBigInt(new BigInteger("0", 16)), Success(CBigInt(new BigInteger("0", 16)))), - (CBigInt(new BigInteger("1", 16)), Success(CBigInt(new BigInteger("-1", 16)))), - (CBigInt(new BigInteger("7fffffff", 16)), Success(CBigInt(new BigInteger("-7fffffff", 16)))), - (CBigInt(new BigInteger("80000000", 16)), Success(CBigInt(new BigInteger("-80000000", 16)))), - (CBigInt(new BigInteger("90e8c3b6e8df65c", 16)), Success(CBigInt(new BigInteger("-90e8c3b6e8df65c", 16)))), - (CBigInt(new BigInteger("36aa93288257dcca141d0c01c5cef14c9d1c0f8507872e3fdd839a759636c78", 16)), Success(CBigInt(new BigInteger("-36aa93288257dcca141d0c01c5cef14c9d1c0f8507872e3fdd839a759636c78", 16))))), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36136)) + Seq( + (CBigInt(new BigInteger("-1655a05845a6ad363ac88ea21e88b97e436a1f02c548537e12e2d9667bf0680", 16)), success(CBigInt(new BigInteger("1655a05845a6ad363ac88ea21e88b97e436a1f02c548537e12e2d9667bf0680", 16)))), + (CBigInt(new BigInteger("-1b24ba8badba8abf347cce054d9b9f14f229321507245b8", 16)), success(CBigInt(new BigInteger("1b24ba8badba8abf347cce054d9b9f14f229321507245b8", 16)))), + (CBigInt(new BigInteger("-1ec9cca2c346cb72a1e65481eaa0627d", 16)), success(CBigInt(new BigInteger("1ec9cca2c346cb72a1e65481eaa0627d", 16)))), + (CBigInt(new BigInteger("-8000000000000001", 16)), success(CBigInt(new BigInteger("8000000000000001", 16)))), + (CBigInt(new BigInteger("-8000000000000000", 16)), success(CBigInt(new BigInteger("8000000000000000", 16)))), + (CBigInt(new BigInteger("-48afe3e059821cd6", 16)), success(CBigInt(new BigInteger("48afe3e059821cd6", 16)))), + (CBigInt(new BigInteger("-80000001", 16)), success(CBigInt(new BigInteger("80000001", 16)))), + (CBigInt(new BigInteger("-80000000", 16)), success(CBigInt(new BigInteger("80000000", 16)))), + (CBigInt(new BigInteger("-1", 16)), success(CBigInt(new BigInteger("1", 16)))), + (CBigInt(new BigInteger("0", 16)), success(CBigInt(new BigInteger("0", 16)))), + (CBigInt(new BigInteger("1", 16)), success(CBigInt(new BigInteger("-1", 16)))), + (CBigInt(new BigInteger("7fffffff", 16)), success(CBigInt(new BigInteger("-7fffffff", 16)))), + (CBigInt(new BigInteger("80000000", 16)), success(CBigInt(new BigInteger("-80000000", 16)))), + (CBigInt(new BigInteger("90e8c3b6e8df65c", 16)), success(CBigInt(new BigInteger("-90e8c3b6e8df65c", 16)))), + (CBigInt(new BigInteger("36aa93288257dcca141d0c01c5cef14c9d1c0f8507872e3fdd839a759636c78", 16)), success(CBigInt(new BigInteger("-36aa93288257dcca141d0c01c5cef14c9d1c0f8507872e3fdd839a759636c78", 16))))) + }, existingFeature((x: BigInt) => x.negate(), "{ (x: BigInt) => -x }", FuncValue(Vector((1, SBigInt)), Negation(ValUse(1, SBigInt))))) @@ -3017,10 +3348,13 @@ class SigmaDslSpec extends SigmaDslTesting { suite => property("global functions equivalence") { - testCases( - Seq( - (-1, Success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), - (1, Success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")))), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35981)) + Seq( + (-1, success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), + (1, success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")))) + }, existingFeature({ (x: Int) => SigmaDsl.groupGenerator }, "{ (x: Int) => groupGenerator }", FuncValue( @@ -3033,10 +3367,13 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ))) - testCases( - Seq( - (-1, Success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), - (1, Success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")))), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35981)) + Seq( + (-1, success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), + (1, success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")))) + }, existingFeature({ (x: Int) => SigmaDsl.groupGenerator }, "{ (x: Int) => Global.groupGenerator }", FuncValue( @@ -3049,21 +3386,24 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ))) - testCases( - Seq( - (CBigInt(new BigInteger("-e5c1a54694c85d644fa30a6fc5f3aa209ed304d57f72683a0ebf21038b6a9d", 16)), Success(Helpers.decodeGroupElement("023395bcba3d7cf21d73c50f8af79d09a8c404c15ce9d04f067d672823bae91a54"))), - (CBigInt(new BigInteger("-bc2d08f935259e0eebf272c66c6e1dbd484c6706390215", 16)), Success(Helpers.decodeGroupElement("02ddcf4c48105faf3c16f7399b5dbedd82ab0bb50ae292d8f88f49a3f86e78974e"))), - (CBigInt(new BigInteger("-35cbe9a7a652e5fe85f735ee9909fdd8", 16)), Success(Helpers.decodeGroupElement("03b110ec9c7a8c20ed873818e976a0e96e5a17be979d3422d59b362de2a3ae043e"))), - (CBigInt(new BigInteger("-3f05ffca6bd4b15c", 16)), Success(Helpers.decodeGroupElement("02acf2657d0714cef8d65ae15c362faa09c0934c0bce872a23398e564c090b85c8"))), - (CBigInt(new BigInteger("-80000001", 16)), Success(Helpers.decodeGroupElement("0391b418fd1778356ce947a5cbb46539fd29842aea168486fae91fc5317177a575"))), - (CBigInt(new BigInteger("-80000000", 16)), Success(Helpers.decodeGroupElement("025318f9b1a2697010c5ac235e9af475a8c7e5419f33d47b18d33feeb329eb99a4"))), - (CBigInt(new BigInteger("-1", 16)), Success(Helpers.decodeGroupElement("0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), - (CBigInt(new BigInteger("0", 16)), Success(Helpers.decodeGroupElement("000000000000000000000000000000000000000000000000000000000000000000"))), - (CBigInt(new BigInteger("1", 16)), Success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), - (CBigInt(new BigInteger("80000000", 16)), Success(Helpers.decodeGroupElement("035318f9b1a2697010c5ac235e9af475a8c7e5419f33d47b18d33feeb329eb99a4"))), - (CBigInt(new BigInteger("1251b7fcd8a01e95", 16)), Success(Helpers.decodeGroupElement("030fde7238b8dddfafab8f5481dc17b880505d6bacbe3cdf2ce975afdcadf66354"))), - (CBigInt(new BigInteger("12f6bd76d8fe1d035bdb14bf2f696e52", 16)), Success(Helpers.decodeGroupElement("028f2ccf13669461cb3cfbea281e2db08fbb67b38493a1628855203d3f69b82763"))), - (CBigInt(new BigInteger("102bb404f5e36bdba004fdefa34df8cfa02e7912f3caf79", 16)), Success(Helpers.decodeGroupElement("03ce82f431d115d45ad555084f8b2861ce5c4561d154e931e9f778594896e46a25")))), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 41237)) + Seq( + (CBigInt(new BigInteger("-e5c1a54694c85d644fa30a6fc5f3aa209ed304d57f72683a0ebf21038b6a9d", 16)), success(Helpers.decodeGroupElement("023395bcba3d7cf21d73c50f8af79d09a8c404c15ce9d04f067d672823bae91a54"))), + (CBigInt(new BigInteger("-bc2d08f935259e0eebf272c66c6e1dbd484c6706390215", 16)), success(Helpers.decodeGroupElement("02ddcf4c48105faf3c16f7399b5dbedd82ab0bb50ae292d8f88f49a3f86e78974e"))), + (CBigInt(new BigInteger("-35cbe9a7a652e5fe85f735ee9909fdd8", 16)), success(Helpers.decodeGroupElement("03b110ec9c7a8c20ed873818e976a0e96e5a17be979d3422d59b362de2a3ae043e"))), + (CBigInt(new BigInteger("-3f05ffca6bd4b15c", 16)), success(Helpers.decodeGroupElement("02acf2657d0714cef8d65ae15c362faa09c0934c0bce872a23398e564c090b85c8"))), + (CBigInt(new BigInteger("-80000001", 16)), success(Helpers.decodeGroupElement("0391b418fd1778356ce947a5cbb46539fd29842aea168486fae91fc5317177a575"))), + (CBigInt(new BigInteger("-80000000", 16)), success(Helpers.decodeGroupElement("025318f9b1a2697010c5ac235e9af475a8c7e5419f33d47b18d33feeb329eb99a4"))), + (CBigInt(new BigInteger("-1", 16)), success(Helpers.decodeGroupElement("0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), + (CBigInt(new BigInteger("0", 16)), success(Helpers.decodeGroupElement("000000000000000000000000000000000000000000000000000000000000000000"))), + (CBigInt(new BigInteger("1", 16)), success(Helpers.decodeGroupElement("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"))), + (CBigInt(new BigInteger("80000000", 16)), success(Helpers.decodeGroupElement("035318f9b1a2697010c5ac235e9af475a8c7e5419f33d47b18d33feeb329eb99a4"))), + (CBigInt(new BigInteger("1251b7fcd8a01e95", 16)), success(Helpers.decodeGroupElement("030fde7238b8dddfafab8f5481dc17b880505d6bacbe3cdf2ce975afdcadf66354"))), + (CBigInt(new BigInteger("12f6bd76d8fe1d035bdb14bf2f696e52", 16)), success(Helpers.decodeGroupElement("028f2ccf13669461cb3cfbea281e2db08fbb67b38493a1628855203d3f69b82763"))), + (CBigInt(new BigInteger("102bb404f5e36bdba004fdefa34df8cfa02e7912f3caf79", 16)), success(Helpers.decodeGroupElement("03ce82f431d115d45ad555084f8b2861ce5c4561d154e931e9f778594896e46a25")))) + }, existingFeature({ (n: BigInt) => SigmaDsl.groupGenerator.exp(n) }, "{ (n: BigInt) => groupGenerator.exp(n) }", FuncValue( @@ -3080,17 +3420,20 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ))) // TODO HF: fix semantics when the left collection is longer - testCases( - Seq( - ((Helpers.decodeBytes(""), Helpers.decodeBytes("")), Success(Helpers.decodeBytes(""))), - ((Helpers.decodeBytes("01"), Helpers.decodeBytes("01")), Success(Helpers.decodeBytes("00"))), - ((Helpers.decodeBytes("0100"), Helpers.decodeBytes("0101")), Success(Helpers.decodeBytes("0001"))), - ((Helpers.decodeBytes("01"), Helpers.decodeBytes("0101")), Success(Helpers.decodeBytes("00"))), - ((Helpers.decodeBytes("0100"), Helpers.decodeBytes("01")), Failure(new ArrayIndexOutOfBoundsException("1"))), - ((Helpers.decodeBytes("800136fe89afff802acea67128a0ff007fffe3498c8001806080012b"), - Helpers.decodeBytes("648018010a5d5800f5b400a580e7b4809b0cd273ff1230bfa800017f7fdb002749b3ac2b86ff")), - Success(Helpers.decodeBytes("e4812eff83f2a780df7aa6d4a8474b80e4f3313a7392313fc8800054"))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36903)) + Seq( + ((Helpers.decodeBytes(""), Helpers.decodeBytes("")), success(Helpers.decodeBytes(""))), + ((Helpers.decodeBytes("01"), Helpers.decodeBytes("01")), success(Helpers.decodeBytes("00"))), + ((Helpers.decodeBytes("0100"), Helpers.decodeBytes("0101")), success(Helpers.decodeBytes("0001"))), + ((Helpers.decodeBytes("01"), Helpers.decodeBytes("0101")), success(Helpers.decodeBytes("00"))), + ((Helpers.decodeBytes("0100"), Helpers.decodeBytes("01")), Failure(new ArrayIndexOutOfBoundsException("1"))), + ((Helpers.decodeBytes("800136fe89afff802acea67128a0ff007fffe3498c8001806080012b"), + Helpers.decodeBytes("648018010a5d5800f5b400a580e7b4809b0cd273ff1230bfa800017f7fdb002749b3ac2b86ff")), + success(Helpers.decodeBytes("e4812eff83f2a780df7aa6d4a8474b80e4f3313a7392313fc8800054"))) + ) + }, existingFeature((x: (Coll[Byte], Coll[Byte])) => SigmaDsl.xor(x._1, x._2), "{ (x: (Coll[Byte], Coll[Byte])) => xor(x._1, x._2) }", FuncValue( @@ -3163,12 +3506,15 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ) - testCases( - Seq( - (Coll[Box](), Success(Coll[Box]())), - (Coll[Box](b1), Success(Coll[Box]())), - (Coll[Box](b1, b2), Success(Coll[Box](b2))) - ), + verifyCases( + { + def success[T](v: T, c: Int) = Success(Expected(v, c)) + Seq( + (Coll[Box](), success(Coll[Box](), 37297)), + (Coll[Box](b1), success(Coll[Box](), 37397)), + (Coll[Box](b1, b2), success(Coll[Box](b2), 37537)) + ) + }, existingFeature({ (x: Coll[Box]) => x.filter({ (b: Box) => b.value > 1 }) }, "{ (x: Coll[Box]) => x.filter({(b: Box) => b.value > 1 }) }", FuncValue( @@ -3180,16 +3526,19 @@ class SigmaDslSpec extends SigmaDslTesting { suite => )), preGeneratedSamples = Some(samples)) - testCases( - Seq( - (Coll[Box](), Success(Coll[Byte]())), - (Coll[Box](b1), Success(Helpers.decodeBytes( - "0008ce02c1a9311ecf1e76c787ba4b1c0e10157b4f6d1e4db3ef0d84f411c99f2d4d2c5b027d1bd9a437e73726ceddecc162e5c85f79aee4798505bc826b8ad1813148e4190257cff6d06fe15d1004596eeb97a7f67755188501e36adc49bd807fe65e9d8281033c6021cff6ba5fdfc4f1742486030d2ebbffd9c9c09e488792f3102b2dcdabd5" - ))), - (Coll[Box](b1, b2), Success(Helpers.decodeBytes( - "0008ce02c1a9311ecf1e76c787ba4b1c0e10157b4f6d1e4db3ef0d84f411c99f2d4d2c5b027d1bd9a437e73726ceddecc162e5c85f79aee4798505bc826b8ad1813148e4190257cff6d06fe15d1004596eeb97a7f67755188501e36adc49bd807fe65e9d8281033c6021cff6ba5fdfc4f1742486030d2ebbffd9c9c09e488792f3102b2dcdabd500d197830201010096850200" - ))) - ), + verifyCases( + { + def success[T](v: T, c: Int) = Success(Expected(v, c)) + Seq( + (Coll[Box](), success(Coll[Byte](), 38126)), + (Coll[Box](b1), success(Helpers.decodeBytes( + "0008ce02c1a9311ecf1e76c787ba4b1c0e10157b4f6d1e4db3ef0d84f411c99f2d4d2c5b027d1bd9a437e73726ceddecc162e5c85f79aee4798505bc826b8ad1813148e4190257cff6d06fe15d1004596eeb97a7f67755188501e36adc49bd807fe65e9d8281033c6021cff6ba5fdfc4f1742486030d2ebbffd9c9c09e488792f3102b2dcdabd5" + ), 38206)), + (Coll[Box](b1, b2), success(Helpers.decodeBytes( + "0008ce02c1a9311ecf1e76c787ba4b1c0e10157b4f6d1e4db3ef0d84f411c99f2d4d2c5b027d1bd9a437e73726ceddecc162e5c85f79aee4798505bc826b8ad1813148e4190257cff6d06fe15d1004596eeb97a7f67755188501e36adc49bd807fe65e9d8281033c6021cff6ba5fdfc4f1742486030d2ebbffd9c9c09e488792f3102b2dcdabd500d197830201010096850200" + ), 38286)) + ) + }, existingFeature({ (x: Coll[Box]) => x.flatMap({ (b: Box) => b.propositionBytes }) }, "{ (x: Coll[Box]) => x.flatMap({(b: Box) => b.propositionBytes }) }", FuncValue( @@ -3205,12 +3554,15 @@ class SigmaDslSpec extends SigmaDslTesting { suite => )), preGeneratedSamples = Some(samples)) - testCases( - Seq( - (Coll[Box](), Success(Coll[(Box, Box)]())), - (Coll[Box](b1), Success(Coll[(Box, Box)]((b1, b1)))), - (Coll[Box](b1, b2), Success(Coll[(Box, Box)]((b1, b1), (b2, b2)))) - ), + verifyCases( + { + def success[T](v: T, c: Int) = Success(Expected(v, c)) + Seq( + (Coll[Box](), success(Coll[(Box, Box)](), 37399)), + (Coll[Box](b1), success(Coll[(Box, Box)]((b1, b1)), 37559)), + (Coll[Box](b1, b2), success(Coll[(Box, Box)]((b1, b1), (b2, b2)), 37719)) + ) + }, existingFeature({ (x: Coll[Box]) => x.zip(x) }, "{ (x: Coll[Box]) => x.zip(x) }", FuncValue( @@ -3226,23 +3578,29 @@ class SigmaDslSpec extends SigmaDslTesting { suite => )), preGeneratedSamples = Some(samples)) - testCases( - Seq( - (Coll[Box](), Success(0)), - (Coll[Box](b1), Success(1)), - (Coll[Box](b1, b2), Success(2)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35954)) + Seq( + (Coll[Box](), success(0)), + (Coll[Box](b1), success(1)), + (Coll[Box](b1, b2), success(2)) + ) + }, existingFeature({ (x: Coll[Box]) => x.size }, "{ (x: Coll[Box]) => x.size }", FuncValue(Vector((1, SCollectionType(SBox))), SizeOf(ValUse(1, SCollectionType(SBox))))), preGeneratedSamples = Some(samples)) - testCases( - Seq( - (Coll[Box](), Success(Coll[Int]())), - (Coll[Box](b1), Success(Coll[Int](0))), - (Coll[Box](b1, b2), Success(Coll[Int](0, 1))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36036)) + Seq( + (Coll[Box](), success(Coll[Int]())), + (Coll[Box](b1), success(Coll[Int](0))), + (Coll[Box](b1, b2), success(Coll[Int](0, 1))) + ) + }, existingFeature({ (x: Coll[Box]) => x.indices }, "{ (x: Coll[Box]) => x.indices }", FuncValue( @@ -3256,12 +3614,15 @@ class SigmaDslSpec extends SigmaDslTesting { suite => )), preGeneratedSamples = Some(samples)) - testCases( - Seq( - (Coll[Box](), Success(true)), - (Coll[Box](b1), Success(false)), - (Coll[Box](b1, b2), Success(false)) - ), + verifyCases( + { + def success[T](v: T, c: Int) = Success(Expected(v, c)) + Seq( + (Coll[Box](), success(true, 37909)), + (Coll[Box](b1), success(false, 37969)), + (Coll[Box](b1, b2), success(false, 38029)) + ) + }, existingFeature({ (x: Coll[Box]) => x.forall({ (b: Box) => b.value > 1 }) }, "{ (x: Coll[Box]) => x.forall({(b: Box) => b.value > 1 }) }", FuncValue( @@ -3273,12 +3634,15 @@ class SigmaDslSpec extends SigmaDslTesting { suite => )), preGeneratedSamples = Some(samples)) - testCases( - Seq( - (Coll[Box](), Success(false)), - (Coll[Box](b1), Success(false)), - (Coll[Box](b1, b2), Success(true)) - ), + verifyCases( + { + def success[T](v: T, c: Int) = Success(Expected(v, c)) + Seq( + (Coll[Box](), success(false, 38455)), + (Coll[Box](b1), success(false, 38515)), + (Coll[Box](b1, b2), success(true, 38575)) + ) + }, existingFeature({ (x: Coll[Box]) => x.exists({ (b: Box) => b.value > 1 }) }, "{ (x: Coll[Box]) => x.exists({(b: Box) => b.value > 1 }) }", FuncValue( @@ -3296,23 +3660,84 @@ class SigmaDslSpec extends SigmaDslTesting { suite => l <- Gen.choose(0, arr.length - 1) r <- Gen.choose(l, arr.length - 1) } yield (arr, (l, r)) + property("Coll flatMap method equivalence") { + verifyCases( + { + def success[T](v: T, c: Int) = Success(Expected(v, c)) + Seq( + Coll[GroupElement]() -> success(Coll[Byte](), 40133), + Coll[GroupElement]( + Helpers.decodeGroupElement("02d65904820f8330218cf7318b3810d0c9ab9df86f1ee6100882683f23c0aee587"), + Helpers.decodeGroupElement("0390e9daa9916f30d0bc61a8e381c6005edfb7938aee5bb4fc9e8a759c7748ffaa")) -> + success(Helpers.decodeBytes( + "02d65904820f8330218cf7318b3810d0c9ab9df86f1ee6100882683f23c0aee5870390e9daa9916f30d0bc61a8e381c6005edfb7938aee5bb4fc9e8a759c7748ffaa" + ), 40213), + Coll[GroupElement]( + Helpers.decodeGroupElement("02d65904820f8330218cf7318b3810d0c9ab9df86f1ee6100882683f23c0aee587"), + Helpers.decodeGroupElement("0390e9daa9916f30d0bc61a8e381c6005edfb7938aee5bb4fc9e8a759c7748ffaa"), + Helpers.decodeGroupElement("03bd839b969b02d218fd1192f2c80cbda9c6ce9c7ddb765f31b748f4666203df85")) -> + success(Helpers.decodeBytes( + "02d65904820f8330218cf7318b3810d0c9ab9df86f1ee6100882683f23c0aee5870390e9daa9916f30d0bc61a8e381c6005edfb7938aee5bb4fc9e8a759c7748ffaa03bd839b969b02d218fd1192f2c80cbda9c6ce9c7ddb765f31b748f4666203df85" + ), 40253) + ) + }, + existingFeature( + { (x: Coll[GroupElement]) => x.flatMap({ (b: GroupElement) => b.getEncoded }) }, + "{ (x: Coll[GroupElement]) => x.flatMap({ (b: GroupElement) => b.getEncoded }) }", + FuncValue( + Vector((1, SCollectionType(SGroupElement))), + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(1, SCollectionType(SGroupElement)), + SCollection.getMethodByName("flatMap").withConcreteTypes( + Map(STypeVar("IV") -> SGroupElement, STypeVar("OV") -> SByte) + ), + Vector( + FuncValue( + Vector((3, SGroupElement)), + MethodCall.typed[Value[SCollection[SByte.type]]]( + ValUse(3, SGroupElement), + SGroupElement.getMethodByName("getEncoded"), + Vector(), + Map() + ) + ) + ), + Map() + ) + ))) + + val f = existingFeature( + { (x: Coll[GroupElement]) => x.flatMap({ (b: GroupElement) => b.getEncoded.append(b.getEncoded) }) }, + "{ (x: Coll[GroupElement]) => x.flatMap({ (b: GroupElement) => b.getEncoded.append(b.getEncoded) }) }" ) + assertExceptionThrown( + f.oldF, + t => t match { + case e: InvocationTargetException => + e.getTargetException.getMessage.contains("Unsupported lambda in flatMap") + } + ) + } + property("Coll patch method equivalence") { val samples = genSamples(collWithRangeGen, MinSuccessful(50)) - testCases( - Seq( - ((Coll[Int](), (0, 0)), Success(Coll[Int]())), - ((Coll[Int](1), (0, 0)), Success(Coll[Int](1, 1))), - ((Coll[Int](1), (0, 1)), Success(Coll[Int](1))), - ((Coll[Int](1, 2), (0, 0)), Success(Coll[Int](1, 2, 1, 2))), - ((Coll[Int](1, 2), (1, 0)), Success(Coll[Int](1, 1, 2, 2))), - ((Coll[Int](1, 2), (0, 2)), Success(Coll[Int](1, 2))), - ((Coll[Int](1, 2), (0, 3)), Success(Coll[Int](1, 2))), - ((Coll[Int](1, 2), (1, 2)), Success(Coll[Int](1, 1, 2))), - ((Coll[Int](1, 2), (2, 0)), Success(Coll[Int](1, 2, 1, 2))), - ((Coll[Int](1, 2), (3, 0)), Success(Coll[Int](1, 2, 1, 2))), - ((Coll[Int](1, 2), (3, 1)), Success(Coll[Int](1, 2, 1, 2))), - ((Coll[Int](1, 2), (-1, 1)), Success(Coll[Int](1, 2, 2))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 37514)) + Seq( + ((Coll[Int](), (0, 0)), success(Coll[Int]())), + ((Coll[Int](1), (0, 0)), success(Coll[Int](1, 1))), + ((Coll[Int](1), (0, 1)), success(Coll[Int](1))), + ((Coll[Int](1, 2), (0, 0)), success(Coll[Int](1, 2, 1, 2))), + ((Coll[Int](1, 2), (1, 0)), success(Coll[Int](1, 1, 2, 2))), + ((Coll[Int](1, 2), (0, 2)), success(Coll[Int](1, 2))), + ((Coll[Int](1, 2), (0, 3)), success(Coll[Int](1, 2))), + ((Coll[Int](1, 2), (1, 2)), success(Coll[Int](1, 1, 2))), + ((Coll[Int](1, 2), (2, 0)), success(Coll[Int](1, 2, 1, 2))), + ((Coll[Int](1, 2), (3, 0)), success(Coll[Int](1, 2, 1, 2))), + ((Coll[Int](1, 2), (3, 1)), success(Coll[Int](1, 2, 1, 2))), + ((Coll[Int](1, 2), (-1, 1)), success(Coll[Int](1, 2, 2))) + ) + }, existingFeature( { (x: (Coll[Int], (Int, Int))) => val coll = x._1 @@ -3362,18 +3787,21 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("Coll updated method equivalence") { - testCases( + verifyCases( // (coll, (index, elem)) - Seq( - ((Coll[Int](), (0, 0)), Failure(new IndexOutOfBoundsException("0"))), - ((Coll[Int](1), (0, 0)), Success(Coll[Int](0))), - ((Coll[Int](1, 2), (0, 0)), Success(Coll[Int](0, 2))), - ((Coll[Int](1, 2), (1, 0)), Success(Coll[Int](1, 0))), - ((Coll[Int](1, 2, 3), (2, 0)), Success(Coll[Int](1, 2, 0))), - ((Coll[Int](1, 2), (2, 0)), Failure(new IndexOutOfBoundsException("2"))), - ((Coll[Int](1, 2), (3, 0)), Failure(new IndexOutOfBoundsException("3"))), - ((Coll[Int](1, 2), (-1, 0)), Failure(new IndexOutOfBoundsException("-1"))) - ), + { + def success[T](v: T) = Success(Expected(v, 37180)) + Seq( + ((Coll[Int](), (0, 0)), Failure(new IndexOutOfBoundsException("0"))), + ((Coll[Int](1), (0, 0)), success(Coll[Int](0))), + ((Coll[Int](1, 2), (0, 0)), success(Coll[Int](0, 2))), + ((Coll[Int](1, 2), (1, 0)), success(Coll[Int](1, 0))), + ((Coll[Int](1, 2, 3), (2, 0)), success(Coll[Int](1, 2, 0))), + ((Coll[Int](1, 2), (2, 0)), Failure(new IndexOutOfBoundsException("2"))), + ((Coll[Int](1, 2), (3, 0)), Failure(new IndexOutOfBoundsException("3"))), + ((Coll[Int](1, 2), (-1, 0)), Failure(new IndexOutOfBoundsException("-1"))) + ) + }, existingFeature( (x: (Coll[Int], (Int, Int))) => x._1.updated(x._2._1, x._2._2), "{ (x: (Coll[Int], (Int, Int))) => x._1.updated(x._2._1, x._2._2) }", @@ -3415,26 +3843,29 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } yield (coll, (is.toColl, vs)), MinSuccessful(20)) - testCases( + verifyCases( // (coll, (indexes, values)) - Seq( - ((Coll[Int](), (Coll(0), Coll(0))), Failure(new IndexOutOfBoundsException("0"))), - ((Coll[Int](), (Coll(0, 1), Coll(0, 0))), Failure(new IndexOutOfBoundsException("0"))), - ((Coll[Int](), (Coll(0, 1), Coll(0))), Failure(new IllegalArgumentException("requirement failed: Collections should have same length but was 2 and 1:\n xs=Coll(0,1);\n ys=Coll(0)"))), - ((Coll[Int](1), (Coll(0), Coll(0))), Success(Coll[Int](0))), - ((Coll[Int](1), (Coll(0, 1), Coll(0, 0))), Failure(new IndexOutOfBoundsException("1"))), - ((Coll[Int](1, 2), (Coll(0), Coll(0))), Success(Coll[Int](0, 2))), - ((Coll[Int](1, 2), (Coll(0, 1), Coll(0, 0))), Success(Coll[Int](0, 0))), - ((Coll[Int](1, 2), (Coll(0, 1, 2), Coll(0, 0, 0))), Failure(new IndexOutOfBoundsException("2"))), - ((Coll[Int](1, 2), (Coll(1), Coll(0))), Success(Coll[Int](1, 0))), - ((Coll[Int](1, 2, 3), (Coll(2), Coll(0))), Success(Coll[Int](1, 2, 0))), - ((Coll[Int](1, 2), (Coll(2), Coll(0))), Failure(new IndexOutOfBoundsException("2"))), - ((Coll[Int](1, 2), (Coll(3), Coll(0))), Failure(new IndexOutOfBoundsException("3"))), - ((Coll[Int](1, 2), (Coll(-1), Coll(0))), Failure(new IndexOutOfBoundsException("-1"))), - ((Coll[Int](10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140), - (Coll[Int](12, 12, 4, 11, 1, 8, 0, 1), Coll[Int](-10, -20, -30, -40, -50, -60, -70, -80))), - Success(Coll[Int](-70, -80, 30, 40, -30, 60, 70, 80, -60, 100, 110, -40, -20, 140))) - ), + { + def success[T](v: T) = Success(Expected(v, 37817)) + Seq( + ((Coll[Int](), (Coll(0), Coll(0))), Failure(new IndexOutOfBoundsException("0"))), + ((Coll[Int](), (Coll(0, 1), Coll(0, 0))), Failure(new IndexOutOfBoundsException("0"))), + ((Coll[Int](), (Coll(0, 1), Coll(0))), Failure(new IllegalArgumentException("requirement failed: Collections should have same length but was 2 and 1:\n xs=Coll(0,1);\n ys=Coll(0)"))), + ((Coll[Int](1), (Coll(0), Coll(0))), success(Coll[Int](0))), + ((Coll[Int](1), (Coll(0, 1), Coll(0, 0))), Failure(new IndexOutOfBoundsException("1"))), + ((Coll[Int](1, 2), (Coll(0), Coll(0))), success(Coll[Int](0, 2))), + ((Coll[Int](1, 2), (Coll(0, 1), Coll(0, 0))), success(Coll[Int](0, 0))), + ((Coll[Int](1, 2), (Coll(0, 1, 2), Coll(0, 0, 0))), Failure(new IndexOutOfBoundsException("2"))), + ((Coll[Int](1, 2), (Coll(1), Coll(0))), success(Coll[Int](1, 0))), + ((Coll[Int](1, 2, 3), (Coll(2), Coll(0))), success(Coll[Int](1, 2, 0))), + ((Coll[Int](1, 2), (Coll(2), Coll(0))), Failure(new IndexOutOfBoundsException("2"))), + ((Coll[Int](1, 2), (Coll(3), Coll(0))), Failure(new IndexOutOfBoundsException("3"))), + ((Coll[Int](1, 2), (Coll(-1), Coll(0))), Failure(new IndexOutOfBoundsException("-1"))), + ((Coll[Int](10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140), + (Coll[Int](12, 12, 4, 11, 1, 8, 0, 1), Coll[Int](-10, -20, -30, -40, -50, -60, -70, -80))), + success(Coll[Int](-70, -80, 30, 40, -30, 60, 70, 80, -60, 100, 110, -40, -20, 140))) + ) + }, existingFeature( (x: (Coll[Int], (Coll[Int], Coll[Int]))) => x._1.updateMany(x._2._1, x._2._2), "{ (x: (Coll[Int], (Coll[Int], Coll[Int]))) => x._1.updateMany(x._2._1, x._2._2) }", @@ -3509,19 +3940,22 @@ class SigmaDslSpec extends SigmaDslTesting { suite => property("Coll fold method equivalence") { val n = ExactNumeric.IntIsExactNumeric - testCases( + verifyCases( // (coll, initState) - Seq( - ((Coll[Byte](), 0), Success(0)), - ((Coll[Byte](), Int.MaxValue), Success(Int.MaxValue)), - ((Coll[Byte](1), Int.MaxValue - 1), Success(Int.MaxValue)), - ((Coll[Byte](1), Int.MaxValue), Failure(new ArithmeticException("integer overflow"))), - ((Coll[Byte](-1), Int.MinValue + 1), Success(Int.MinValue)), - ((Coll[Byte](-1), Int.MinValue), Failure(new ArithmeticException("integer overflow"))), - ((Coll[Byte](1, 2), 0), Success(3)), - ((Coll[Byte](1, -1), 0), Success(0)), - ((Coll[Byte](1, -1, 1), 0), Success(1)) - ), + { + def success[T](v: T, c: Int) = Success(Expected(v, c)) + Seq( + ((Coll[Byte](), 0), success(0, 41266)), + ((Coll[Byte](), Int.MaxValue), success(Int.MaxValue, 41266)), + ((Coll[Byte](1), Int.MaxValue - 1), success(Int.MaxValue, 41396)), + ((Coll[Byte](1), Int.MaxValue), Failure(new ArithmeticException("integer overflow"))), + ((Coll[Byte](-1), Int.MinValue + 1), success(Int.MinValue, 41396)), + ((Coll[Byte](-1), Int.MinValue), Failure(new ArithmeticException("integer overflow"))), + ((Coll[Byte](1, 2), 0), success(3, 41526)), + ((Coll[Byte](1, -1), 0), success(0, 41526)), + ((Coll[Byte](1, -1, 1), 0), success(1, 41656)) + ) + }, existingFeature( { (x: (Coll[Byte], Int)) => x._1.foldLeft(x._2, { i: (Int, Byte) => n.plus(i._1, i._2) }) }, "{ (x: (Coll[Byte], Int)) => x._1.fold(x._2, { (i1: Int, i2: Byte) => i1 + i2 }) }", @@ -3543,28 +3977,31 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("Coll indexOf method equivalence") { - testCases( + verifyCases( // (coll, (elem: Byte, from: Int)) - Seq( - ((Coll[Byte](), (0.toByte, 0)), Success(-1)), - ((Coll[Byte](), (0.toByte, -1)), Success(-1)), - ((Coll[Byte](), (0.toByte, 1)), Success(-1)), - ((Coll[Byte](1), (0.toByte, 0)), Success(-1)), - ((Coll[Byte](1), (1.toByte, 0)), Success(0)), - ((Coll[Byte](1), (1.toByte, 1)), Success(-1)), - ((Coll[Byte](1, 1), (0.toByte, -1)), Success(-1)), - ((Coll[Byte](1, 1), (0.toByte, 0)), Success(-1)), - ((Coll[Byte](1, 1), (1.toByte, -1)), Success(0)), - ((Coll[Byte](1, 1), (1.toByte, 0)), Success(0)), - ((Coll[Byte](1, 1), (1.toByte, 1)), Success(1)), - ((Coll[Byte](1, 1), (1.toByte, 2)), Success(-1)), - ((Coll[Byte](1, 1), (1.toByte, 3)), Success(-1)), - ((Coll[Byte](1, 2, 3), (3.toByte, 0)), Success(2)), - ((Coll[Byte](1, 2, 3), (3.toByte, 1)), Success(2)), - ((Coll[Byte](1, 2, 3), (3.toByte, 2)), Success(2)), - ((Coll[Byte](1, 2, 3), (3.toByte, 3)), Success(-1)), - ((Helpers.decodeBytes("8085623fb7cd6b7f01801f00800100"), (0.toByte, -1)), Success(11)) - ), + { + def success[T](v: T) = Success(Expected(v, 37649)) + Seq( + ((Coll[Byte](), (0.toByte, 0)), success(-1)), + ((Coll[Byte](), (0.toByte, -1)), success(-1)), + ((Coll[Byte](), (0.toByte, 1)), success(-1)), + ((Coll[Byte](1), (0.toByte, 0)), success(-1)), + ((Coll[Byte](1), (1.toByte, 0)), success(0)), + ((Coll[Byte](1), (1.toByte, 1)), success(-1)), + ((Coll[Byte](1, 1), (0.toByte, -1)), success(-1)), + ((Coll[Byte](1, 1), (0.toByte, 0)), success(-1)), + ((Coll[Byte](1, 1), (1.toByte, -1)), success(0)), + ((Coll[Byte](1, 1), (1.toByte, 0)), success(0)), + ((Coll[Byte](1, 1), (1.toByte, 1)), success(1)), + ((Coll[Byte](1, 1), (1.toByte, 2)), success(-1)), + ((Coll[Byte](1, 1), (1.toByte, 3)), success(-1)), + ((Coll[Byte](1, 2, 3), (3.toByte, 0)), success(2)), + ((Coll[Byte](1, 2, 3), (3.toByte, 1)), success(2)), + ((Coll[Byte](1, 2, 3), (3.toByte, 2)), success(2)), + ((Coll[Byte](1, 2, 3), (3.toByte, 3)), success(-1)), + ((Helpers.decodeBytes("8085623fb7cd6b7f01801f00800100"), (0.toByte, -1)), success(11)) + ) + }, existingFeature( { (x: (Coll[Byte], (Byte, Int))) => x._1.indexOf(x._2._1, x._2._2) }, "{ (x: (Coll[Byte], (Byte, Int))) => x._1.indexOf(x._2._1, x._2._2) }", @@ -3595,17 +4032,20 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("Coll apply method equivalence") { - testCases( - Seq( - ((Coll[Int](), 0), Failure(new ArrayIndexOutOfBoundsException("0"))), - ((Coll[Int](), -1), Failure(new ArrayIndexOutOfBoundsException("-1"))), - ((Coll[Int](1), 0), Success(1)), - ((Coll[Int](1), 1), Failure(new ArrayIndexOutOfBoundsException("1"))), - ((Coll[Int](1), -1), Failure(new ArrayIndexOutOfBoundsException("-1"))), - ((Coll[Int](1, 2), 1), Success(2)), - ((Coll[Int](1, 2), 1), Success(2)), - ((Coll[Int](1, 2), 2), Failure(new ArrayIndexOutOfBoundsException("2"))) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36410)) + Seq( + ((Coll[Int](), 0), Failure(new ArrayIndexOutOfBoundsException("0"))), + ((Coll[Int](), -1), Failure(new ArrayIndexOutOfBoundsException("-1"))), + ((Coll[Int](1), 0), success(1)), + ((Coll[Int](1), 1), Failure(new ArrayIndexOutOfBoundsException("1"))), + ((Coll[Int](1), -1), Failure(new ArrayIndexOutOfBoundsException("-1"))), + ((Coll[Int](1, 2), 1), success(2)), + ((Coll[Int](1, 2), 1), success(2)), + ((Coll[Int](1, 2), 2), Failure(new ArrayIndexOutOfBoundsException("2"))) + ) + }, existingFeature((x: (Coll[Int], Int)) => x._1(x._2), "{ (x: (Coll[Int], Int)) => x._1(x._2) }", FuncValue( @@ -3623,19 +4063,22 @@ class SigmaDslSpec extends SigmaDslTesting { suite => property("Coll getOrElse method equivalence") { val default = 10 - testCases( + verifyCases( // (coll, (index, default)) - Seq( - ((Coll[Int](), (0, default)), Success(default)), - ((Coll[Int](), (-1, default)), Success(default)), - ((Coll[Int](1), (0, default)), Success(1)), - ((Coll[Int](1), (1, default)), Success(default)), - ((Coll[Int](1), (-1, default)), Success(default)), - ((Coll[Int](1, 2), (0, default)), Success(1)), - ((Coll[Int](1, 2), (1, default)), Success(2)), - ((Coll[Int](1, 2), (2, default)), Success(default)), - ((Coll[Int](1, 2), (-1, default)), Success(default)) - ), + { + def success[T](v: T) = Success(Expected(v, 37020)) + Seq( + ((Coll[Int](), (0, default)), success(default)), + ((Coll[Int](), (-1, default)), success(default)), + ((Coll[Int](1), (0, default)), success(1)), + ((Coll[Int](1), (1, default)), success(default)), + ((Coll[Int](1), (-1, default)), success(default)), + ((Coll[Int](1, 2), (0, default)), success(1)), + ((Coll[Int](1, 2), (1, default)), success(2)), + ((Coll[Int](1, 2), (2, default)), success(default)), + ((Coll[Int](1, 2), (-1, default)), success(default)) + ) + }, existingFeature((x: (Coll[Int], (Int, Int))) => x._1.getOrElse(x._2._1, x._2._2), "{ (x: (Coll[Int], (Int, Int))) => x._1.getOrElse(x._2._1, x._2._2) }", FuncValue( @@ -3664,11 +4107,14 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("Tuple size method equivalence") { - testCases( - Seq( - ((0, 0), Success(2)), - ((1, 2), Success(2)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 35905)) + Seq( + ((0, 0), success(2)), + ((1, 2), success(2)) + ) + }, existingFeature((x: (Int, Int)) => 2, "{ (x: (Int, Int)) => x.size }", FuncValue(Vector((1, SPair(SInt, SInt))), IntConstant(2)))) @@ -3676,8 +4122,8 @@ class SigmaDslSpec extends SigmaDslTesting { suite => property("Tuple apply method equivalence") { val samples = genSamples[(Int, Int)](DefaultMinSuccessful) - testCases( - Seq(((1, 2), Success(1))), + verifyCases( + Seq(((1, 2), Success(Expected(1, cost = 36013)))), existingFeature((x: (Int, Int)) => x._1, "{ (x: (Int, Int)) => x(0) }", FuncValue( @@ -3685,8 +4131,8 @@ class SigmaDslSpec extends SigmaDslTesting { suite => SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SInt, SInt)), 1.toByte) )), preGeneratedSamples = Some(samples)) - testCases( - Seq(((1, 2), Success(2))), + verifyCases( + Seq(((1, 2), Success(Expected(2, cost = 36013)))), existingFeature((x: (Int, Int)) => x._2, "{ (x: (Int, Int)) => x(1) }", FuncValue( @@ -3698,13 +4144,16 @@ class SigmaDslSpec extends SigmaDslTesting { suite => property("Coll map method equivalence") { val n = ExactNumeric.IntIsExactNumeric - testCases( - Seq( - (Coll[Int](), Success(Coll[Int]())), - (Coll[Int](1), Success(Coll[Int](2))), - (Coll[Int](1, 2), Success(Coll[Int](2, 3))), - (Coll[Int](1, 2, Int.MaxValue), Failure(new ArithmeticException("integer overflow"))) - ), + verifyCases( + { + def success[T](v: T, c: Int) = Success(Expected(v, c)) + Seq( + (Coll[Int](), success(Coll[Int](), 38886)), + (Coll[Int](1), success(Coll[Int](2), 38936)), + (Coll[Int](1, 2), success(Coll[Int](2, 3), 38986)), + (Coll[Int](1, 2, Int.MaxValue), Failure(new ArithmeticException("integer overflow"))) + ) + }, existingFeature((x: Coll[Int]) => x.map({ (v: Int) => n.plus(v, 1) }), "{ (x: Coll[Int]) => x.map({ (v: Int) => v + 1 }) }", FuncValue( @@ -3718,20 +4167,23 @@ class SigmaDslSpec extends SigmaDslTesting { suite => property("Coll slice method equivalence") { val samples = genSamples(collWithRangeGen, DefaultMinSuccessful) - testCases( + verifyCases( // (coll, (from, until)) - Seq( - ((Coll[Int](), (-1, 0)), Success(Coll[Int]())), - ((Coll[Int](), (0, 0)), Success(Coll[Int]())), - ((Coll[Int](1), (0, 0)), Success(Coll[Int]())), - ((Coll[Int](1), (0, -1)), Success(Coll[Int]())), - ((Coll[Int](1), (1, 1)), Success(Coll[Int]())), - ((Coll[Int](1), (-1, 1)), Success(Coll[Int](1))), - ((Coll[Int](1, 2), (1, 1)), Success(Coll[Int]())), - ((Coll[Int](1, 2), (1, 0)), Success(Coll[Int]())), - ((Coll[Int](1, 2), (1, 2)), Success(Coll[Int](2))), - ((Coll[Int](1, 2, 3, 4), (1, 3)), Success(Coll[Int](2, 3))) - ), + { + def success[T](v: T) = Success(Expected(v, 36964)) + Seq( + ((Coll[Int](), (-1, 0)), success(Coll[Int]())), + ((Coll[Int](), (0, 0)), success(Coll[Int]())), + ((Coll[Int](1), (0, 0)), success(Coll[Int]())), + ((Coll[Int](1), (0, -1)), success(Coll[Int]())), + ((Coll[Int](1), (1, 1)), success(Coll[Int]())), + ((Coll[Int](1), (-1, 1)), success(Coll[Int](1))), + ((Coll[Int](1, 2), (1, 1)), success(Coll[Int]())), + ((Coll[Int](1, 2), (1, 0)), success(Coll[Int]())), + ((Coll[Int](1, 2), (1, 2)), success(Coll[Int](2))), + ((Coll[Int](1, 2, 3, 4), (1, 3)), success(Coll[Int](2, 3))) + ) + }, existingFeature((x: (Coll[Int], (Int, Int))) => x._1.slice(x._2._1, x._2._2), "{ (x: (Coll[Int], (Int, Int))) => x._1.slice(x._2._1, x._2._2) }", FuncValue( @@ -3761,16 +4213,19 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("Coll append method equivalence") { - testCases( - Seq( - (Coll[Int](), Coll[Int]()) -> Success(Coll[Int]()), - (Coll[Int](), Coll[Int](1)) -> Success(Coll[Int](1)), - (Coll[Int](1), Coll[Int]()) -> Success(Coll[Int](1)), - (Coll[Int](1), Coll[Int](2)) -> Success(Coll[Int](1, 2)), - (Coll[Int](1), Coll[Int](2, 3)) -> Success(Coll[Int](1, 2, 3)), - (Coll[Int](1, 2), Coll[Int](3)) -> Success(Coll[Int](1, 2, 3)), - (Coll[Int](1, 2), Coll[Int](3, 4)) -> Success(Coll[Int](1, 2, 3, 4)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 37765)) + Seq( + (Coll[Int](), Coll[Int]()) -> success(Coll[Int]()), + (Coll[Int](), Coll[Int](1)) -> success(Coll[Int](1)), + (Coll[Int](1), Coll[Int]()) -> success(Coll[Int](1)), + (Coll[Int](1), Coll[Int](2)) -> success(Coll[Int](1, 2)), + (Coll[Int](1), Coll[Int](2, 3)) -> success(Coll[Int](1, 2, 3)), + (Coll[Int](1, 2), Coll[Int](3)) -> success(Coll[Int](1, 2, 3)), + (Coll[Int](1, 2), Coll[Int](3, 4)) -> success(Coll[Int](1, 2, 3, 4)) + ) + }, existingFeature( { (x: (Coll[Int], Coll[Int])) => x._1.append(x._2) }, "{ (x: (Coll[Int], Coll[Int])) => x._1.append(x._2) }", @@ -3790,35 +4245,37 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("Option methods equivalence") { - testCases( + def success[T](v: T, c: Int) = Success(Expected(v, c)) + + verifyCases( Seq( (None -> Failure(new NoSuchElementException("None.get"))), - (Some(10L) -> Success(10L))), + (Some(10L) -> success(10L, 36046))), existingFeature({ (x: Option[Long]) => x.get }, "{ (x: Option[Long]) => x.get }", FuncValue(Vector((1, SOption(SLong))), OptionGet(ValUse(1, SOption(SLong)))))) - testCases( + verifyCases( Seq( - (None -> Success(false)), - (Some(10L) -> Success(true))), + (None -> success(false, 36151)), + (Some(10L) -> success(true, 36151))), existingFeature({ (x: Option[Long]) => x.isDefined }, "{ (x: Option[Long]) => x.isDefined }", FuncValue(Vector((1, SOption(SLong))), OptionIsDefined(ValUse(1, SOption(SLong)))))) - testCases( + verifyCases( Seq( - (None -> Success(1L)), - (Some(10L) -> Success(10L))), + (None -> success(1L, 36367)), + (Some(10L) -> success(10L, 36367))), existingFeature({ (x: Option[Long]) => x.getOrElse(1L) }, "{ (x: Option[Long]) => x.getOrElse(1L) }", FuncValue(Vector((1, SOption(SLong))), OptionGetOrElse(ValUse(1, SOption(SLong)), LongConstant(1L))))) - testCases( + verifyCases( Seq( - (None -> Success(None)), - (Some(10L) -> Success(None)), - (Some(1L) -> Success(Some(1L)))), + (None -> success(None, 38239)), + (Some(10L) -> success(None, 38239)), + (Some(1L) -> success(Some(1L), 38239))), existingFeature({ (x: Option[Long]) => x.filter({ (v: Long) => v == 1} ) }, "{ (x: Option[Long]) => x.filter({ (v: Long) => v == 1 }) }", FuncValue( @@ -3832,10 +4289,10 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ))) val n = ExactNumeric.LongIsExactNumeric - testCases( + verifyCases( Seq( - (None -> Success(None)), - (Some(10L) -> Success(Some(11L))), + (None -> success(None, 38575)), + (Some(10L) -> success(Some(11L), 38575)), (Some(Long.MaxValue) -> Failure(new ArithmeticException("long overflow")))), existingFeature({ (x: Option[Long]) => x.map( (v: Long) => n.plus(v, 1) ) }, "{ (x: Option[Long]) => x.map({ (v: Long) => v + 1 }) }", @@ -3873,13 +4330,13 @@ class SigmaDslSpec extends SigmaDslTesting { suite => property("Option fold workaround method") { val n = ExactNumeric.LongIsExactNumeric - testCases( + verifyCases( Seq( - (None -> Success(5L)), - (Some(0L) -> Success(1L)), + (None -> Failure(new NoSuchElementException("None.get"))), + (Some(0L) -> Success(Expected(1L, 39012))), (Some(Long.MaxValue) -> Failure(new ArithmeticException("long overflow"))) ), - existingFeature({ (x: Option[Long]) => x.fold(5.toLong)( (v: Long) => n.plus(v, 1) ) }, + existingFeature({ (x: Option[Long]) => x.fold(throw new NoSuchElementException("None.get"))( (v: Long) => n.plus(v, 1) ) }, """{(x: Option[Long]) => | def f(opt: Long): Long = opt + 1 | if (x.isDefined) f(x.get) else 5L @@ -3901,21 +4358,33 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("blake2b256, sha256 equivalence") { - testCases( + def success[T](v: T, c: Int) = Success(Expected(v, c)) + + verifyCases( Seq( - Coll[Byte]() -> Success(Helpers.decodeBytes("0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8")), + Coll[Byte]() -> + success( + Helpers.decodeBytes("0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8"), + 36269), Helpers.decodeBytes("e0ff0105ffffac31010017ff33") -> - Success(Helpers.decodeBytes("33707eed9aab64874ff2daa6d6a378f61e7da36398fb36c194c7562c9ff846b5")) + success( + Helpers.decodeBytes("33707eed9aab64874ff2daa6d6a378f61e7da36398fb36c194c7562c9ff846b5"), + 36269) ), existingFeature((x: Coll[Byte]) => SigmaDsl.blake2b256(x), "{ (x: Coll[Byte]) => blake2b256(x) }", FuncValue(Vector((1, SByteArray)), CalcBlake2b256(ValUse(1, SByteArray))))) - testCases( + verifyCases( Seq( - Coll[Byte]() -> Success(Helpers.decodeBytes("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")), + Coll[Byte]() -> + success( + Helpers.decodeBytes("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"), + 36393), Helpers.decodeBytes("e0ff0105ffffac31010017ff33") -> - Success(Helpers.decodeBytes("367d0ec2cdc14aac29d5beb60c2bfc86d5a44a246308659af61c1b85fa2ca2cc")) + success( + Helpers.decodeBytes("367d0ec2cdc14aac29d5beb60c2bfc86d5a44a246308659af61c1b85fa2ca2cc"), + 36393) ), existingFeature((x: Coll[Byte]) => SigmaDsl.sha256(x), "{ (x: Coll[Byte]) => sha256(x) }", @@ -3927,17 +4396,19 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("sigmaProp equivalence") { - testCases( + verifyCases( Seq( - (false, Success(CSigmaProp(TrivialProp.FalseProp))), - (true, Success(CSigmaProp(TrivialProp.TrueProp)))), + (false, Success(Expected(CSigmaProp(TrivialProp.FalseProp), 35892))), + (true, Success(Expected(CSigmaProp(TrivialProp.TrueProp), 35892)))), existingFeature((x: Boolean) => sigmaProp(x), "{ (x: Boolean) => sigmaProp(x) }", FuncValue(Vector((1, SBoolean)), BoolToSigmaProp(ValUse(1, SBoolean))))) } property("atLeast equivalence") { - testCases( + def success[T](v: T) = Success(Expected(v, 36462)) + + verifyCases( Seq( Coll[SigmaProp]( CSigmaProp( @@ -3947,7 +4418,7 @@ class SigmaDslSpec extends SigmaDslTesting { suite => Helpers.decodeECPoint("02614b14a8c6c6b4b7ce017d72fbca7f9218b72c16bdd88f170ffb300b106b9014"), Helpers.decodeECPoint("034cc5572276adfa3e283a3f1b0f0028afaadeaa362618c5ec43262d8cefe7f004") ) - )) -> Success(CSigmaProp(TrivialProp.TrueProp)), + )) -> success(CSigmaProp(TrivialProp.TrueProp)), Coll[SigmaProp]( CSigmaProp( ProveDHTuple( @@ -3959,7 +4430,7 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ), CSigmaProp(ProveDlog(Helpers.decodeECPoint("03f7eacae7476a9ef082513a6a70ed6b208aafad0ade5f614ac6cfa2176edd0d69"))), CSigmaProp(ProveDlog(Helpers.decodeECPoint("023bddd50b917388cd2c4f478f3ea9281bf03a252ee1fefe9c79f800afaa8d86ad"))) - ) -> Success( + ) -> success( CSigmaProp( CTHRESHOLD( 2, @@ -3975,7 +4446,9 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ) ) - ) + ), + Colls.replicate[SigmaProp](AtLeast.MaxChildrenCount + 1, CSigmaProp(TrivialProp.TrueProp)) -> + Failure(new IllegalArgumentException("Expected input elements count should not exceed 255, actual: 256")) ), existingFeature((x: Coll[SigmaProp]) => SigmaDsl.atLeast(x.size - 1, x), "{ (x: Coll[SigmaProp]) => atLeast(x.size - 1, x) }", @@ -3989,33 +4462,37 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("&& sigma equivalence") { - testCases( - Seq( - (CSigmaProp(ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798"))), - CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> - Success( - CSigmaProp( - CAND( - Seq( - ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798")), - ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")) + + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36428)) + Seq( + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798"))), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + success( + CSigmaProp( + CAND( + Seq( + ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798")), + ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")) + ) ) ) - ) - ), - (CSigmaProp(TrivialProp.TrueProp), - CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> - Success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), - (CSigmaProp(TrivialProp.FalseProp), - CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> - Success(CSigmaProp(TrivialProp.FalseProp)), - (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), - CSigmaProp(TrivialProp.TrueProp)) -> - Success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), - (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), - CSigmaProp(TrivialProp.FalseProp)) -> - Success(CSigmaProp(TrivialProp.FalseProp)) - ), + ), + (CSigmaProp(TrivialProp.TrueProp), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), + (CSigmaProp(TrivialProp.FalseProp), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + success(CSigmaProp(TrivialProp.FalseProp)), + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), + CSigmaProp(TrivialProp.TrueProp)) -> + success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), + CSigmaProp(TrivialProp.FalseProp)) -> + success(CSigmaProp(TrivialProp.FalseProp)) + ) + }, existingFeature( (x: (SigmaProp, SigmaProp)) => x._1 && x._2, "{ (x:(SigmaProp, SigmaProp)) => x._1 && x._2 }", @@ -4029,13 +4506,16 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ))) - testCases( - Seq( - (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), true) -> - Success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), - (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), false) -> - Success(CSigmaProp(TrivialProp.FalseProp)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36522)) + Seq( + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), true) -> + success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), false) -> + success(CSigmaProp(TrivialProp.FalseProp)) + ) + }, existingFeature( (x: (SigmaProp, Boolean)) => x._1 && sigmaProp(x._2), "{ (x:(SigmaProp, Boolean)) => x._1 && sigmaProp(x._2) }", @@ -4053,33 +4533,36 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("|| sigma equivalence") { - testCases( - Seq( - (CSigmaProp(ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798"))), - CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> - Success( - CSigmaProp( - COR( - Seq( - ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798")), - ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")) + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36494)) + Seq( + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798"))), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + success( + CSigmaProp( + COR( + Seq( + ProveDlog(Helpers.decodeECPoint("02ea9bf6da7f512386c6ca509d40f8c5e7e0ffb3eea5dc3c398443ea17f4510798")), + ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")) + ) ) ) - ) - ), - (CSigmaProp(TrivialProp.FalseProp), - CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> - Success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), - (CSigmaProp(TrivialProp.TrueProp), - CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> - Success(CSigmaProp(TrivialProp.TrueProp)), - (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), - CSigmaProp(TrivialProp.FalseProp)) -> - Success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), - (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), - CSigmaProp(TrivialProp.TrueProp)) -> - Success(CSigmaProp(TrivialProp.TrueProp)) - ), + ), + (CSigmaProp(TrivialProp.FalseProp), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), + (CSigmaProp(TrivialProp.TrueProp), + CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))) -> + success(CSigmaProp(TrivialProp.TrueProp)), + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), + CSigmaProp(TrivialProp.FalseProp)) -> + success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), + CSigmaProp(TrivialProp.TrueProp)) -> + success(CSigmaProp(TrivialProp.TrueProp)) + ) + }, existingFeature( (x: (SigmaProp, SigmaProp)) => x._1 || x._2, "{ (x:(SigmaProp, SigmaProp)) => x._1 || x._2 }", @@ -4093,13 +4576,16 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ) ))) - testCases( - Seq( - (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), false) -> - Success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), - (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), true) -> - Success(CSigmaProp(TrivialProp.TrueProp)) - ), + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 36588)) + Seq( + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), false) -> + success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606")))), + (CSigmaProp(ProveDlog(Helpers.decodeECPoint("03a426a66fc1af2792b35d9583904c3fb877b49ae5cea45b7a2aa105ffa4c68606"))), true) -> + success(CSigmaProp(TrivialProp.TrueProp)) + ) + }, existingFeature( (x: (SigmaProp, Boolean)) => x._1 || sigmaProp(x._2), "{ (x:(SigmaProp, Boolean)) => x._1 || sigmaProp(x._2) }", @@ -4117,10 +4603,12 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("SigmaProp.propBytes equivalence") { - testCases( + verifyCases( Seq( - CSigmaProp(ProveDlog(Helpers.decodeECPoint("039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6f"))) -> - Success(Helpers.decodeBytes("0008cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6f")) + CSigmaProp(ProveDlog(Helpers.decodeECPoint("039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6f"))) + -> Success(Expected( + Helpers.decodeBytes("0008cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6f"), + cost = 35902)) ), existingFeature((x: SigmaProp) => x.propBytes, "{ (x: SigmaProp) => x.propBytes }", @@ -4146,19 +4634,21 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("allOf equivalence") { - testCases( + def success[T](v: T, c: Int) = Success(Expected(v, c)) + + verifyCases( Seq( - (Coll[Boolean]() -> Success(true)), - (Coll[Boolean](true) -> Success(true)), - (Coll[Boolean](false) -> Success(false)), - (Coll[Boolean](false, false) -> Success(false)), - (Coll[Boolean](false, true) -> Success(false)), - (Coll[Boolean](true, false) -> Success(false)), - (Coll[Boolean](true, true) -> Success(true)), - (Coll[Boolean](true, false, false) -> Success(false)), - (Coll[Boolean](true, false, true) -> Success(false)), - (Coll[Boolean](true, true, false) -> Success(false)), - (Coll[Boolean](true, true, true) -> Success(true)) + (Coll[Boolean]() -> success(true, 36018)), + (Coll[Boolean](true) -> success(true, 36028)), + (Coll[Boolean](false) -> success(false, 36028)), + (Coll[Boolean](false, false) -> success(false, 36038)), + (Coll[Boolean](false, true) -> success(false, 36038)), + (Coll[Boolean](true, false) -> success(false, 36038)), + (Coll[Boolean](true, true) -> success(true, 36038)), + (Coll[Boolean](true, false, false) -> success(false, 36048)), + (Coll[Boolean](true, false, true) -> success(false, 36048)), + (Coll[Boolean](true, true, false) -> success(false, 36048)), + (Coll[Boolean](true, true, true) -> success(true, 36048)) ), existingFeature((x: Coll[Boolean]) => SigmaDsl.allOf(x), "{ (x: Coll[Boolean]) => allOf(x) }", @@ -4166,19 +4656,21 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("anyOf equivalence") { - testCases( + def success[T](v: T, c: Int) = Success(Expected(v, c)) + + verifyCases( Seq( - (Coll[Boolean]() -> Success(false)), - (Coll[Boolean](true) -> Success(true)), - (Coll[Boolean](false) -> Success(false)), - (Coll[Boolean](false, false) -> Success(false)), - (Coll[Boolean](false, true) -> Success(true)), - (Coll[Boolean](true, false) -> Success(true)), - (Coll[Boolean](true, true) -> Success(true)), - (Coll[Boolean](true, false, false) -> Success(true)), - (Coll[Boolean](true, false, true) -> Success(true)), - (Coll[Boolean](true, true, false) -> Success(true)), - (Coll[Boolean](true, true, true) -> Success(true)) + (Coll[Boolean]() -> success(false, 36062)), + (Coll[Boolean](true) -> success(true, 36072)), + (Coll[Boolean](false) -> success(false, 36072)), + (Coll[Boolean](false, false) -> success(false, 36082)), + (Coll[Boolean](false, true) -> success(true, 36082)), + (Coll[Boolean](true, false) -> success(true, 36082)), + (Coll[Boolean](true, true) -> success(true, 36082)), + (Coll[Boolean](true, false, false) -> success(true, 36092)), + (Coll[Boolean](true, false, true) -> success(true, 36092)), + (Coll[Boolean](true, true, false) -> success(true, 36092)), + (Coll[Boolean](true, true, true) -> success(true, 36092)) ), existingFeature((x: Coll[Boolean]) => SigmaDsl.anyOf(x), "{ (x: Coll[Boolean]) => anyOf(x) }", @@ -4186,10 +4678,12 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("proveDlog equivalence") { - testCases( + verifyCases( Seq( - (Helpers.decodeGroupElement("02288f0e55610c3355c89ed6c5de43cf20da145b8c54f03a29f481e540d94e9a69") -> - Success(CSigmaProp(ProveDlog(Helpers.decodeECPoint("02288f0e55610c3355c89ed6c5de43cf20da145b8c54f03a29f481e540d94e9a69"))))) + (Helpers.decodeGroupElement("02288f0e55610c3355c89ed6c5de43cf20da145b8c54f03a29f481e540d94e9a69") + -> Success(Expected( + CSigmaProp(ProveDlog(Helpers.decodeECPoint("02288f0e55610c3355c89ed6c5de43cf20da145b8c54f03a29f481e540d94e9a69"))), + cost = 45935))) ), existingFeature({ (x: GroupElement) => SigmaDsl.proveDlog(x) }, "{ (x: GroupElement) => proveDlog(x) }", @@ -4197,18 +4691,20 @@ class SigmaDslSpec extends SigmaDslTesting { suite => } property("proveDHTuple equivalence") { - testCases( + verifyCases( Seq( - (Helpers.decodeGroupElement("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a") -> Success( - CSigmaProp( - ProveDHTuple( - Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), - Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), - Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), - Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a") - ) - ) - )) + (Helpers.decodeGroupElement("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a") + -> Success(Expected( + CSigmaProp( + ProveDHTuple( + Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), + Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), + Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a"), + Helpers.decodeECPoint("039c15221a318d27c186eba84fa8d986c1f63bbd9f8060380c9bfc2ef455d8346a") + ) + ), + cost = 76215 + ))) ), existingFeature({ (x: GroupElement) => SigmaDsl.proveDHTuple(x, x, x, x) }, "{ (x: GroupElement) => proveDHTuple(x, x, x, x) }", @@ -4223,4 +4719,55 @@ class SigmaDslSpec extends SigmaDslTesting { suite => ))) } + property("substConstants equivalence") { + // tree without constant segregation + val t1 = ErgoTree(ErgoTree.DefaultHeader, Vector(), TrueSigmaProp) + // tree with constant segregation, but without constants + val t2 = ErgoTree(ErgoTree.ConstantSegregationHeader, Vector(), TrueSigmaProp) + // tree with one segregated constant + val t3 = ErgoTree(ErgoTree.ConstantSegregationHeader, Vector(TrueSigmaProp), ConstantPlaceholder(0, SSigmaProp)) + // tree with one segregated constant of different type + val t4 = ErgoTree( + ErgoTree.ConstantSegregationHeader, + Vector(IntConstant(10)), + BoolToSigmaProp(EQ(ConstantPlaceholder(0, SInt), IntConstant(20)))) + + verifyCases( + { + def success[T](v: T) = Success(Expected(v, 37694)) + Seq( + (Helpers.decodeBytes(""), 0) -> Failure(new java.nio.BufferUnderflowException()), + + // TODO HF: fix for trees without segregation flag + // NOTE: constants count is serialized erroneously in the following 2 cases + (Coll(t1.bytes:_*), 0) -> success(Helpers.decodeBytes("000008d3")), + (Helpers.decodeBytes("000008d3"), 0) -> success(Helpers.decodeBytes("00000008d3")), + // tree with segregation flag, empty constants array + (Coll(t2.bytes:_*), 0) -> success(Helpers.decodeBytes("100008d3")), + (Helpers.decodeBytes("100008d3"), 0) -> success(Helpers.decodeBytes("100008d3")), + // tree with one segregated constant + (Coll(t3.bytes:_*), 0) -> success(Helpers.decodeBytes("100108d27300")), + (Helpers.decodeBytes("100108d37300"), 0) -> success(Helpers.decodeBytes("100108d27300")), + (Coll(t3.bytes:_*), 1) -> success(Helpers.decodeBytes("100108d37300")), + (Coll(t4.bytes:_*), 0) -> Failure(new AssertionError("assertion failed: expected new constant to have the same SInt$ tpe, got SSigmaProp")) + ) + }, + existingFeature( + { (x: (Coll[Byte], Int)) => + SigmaDsl.substConstants(x._1, Coll[Int](x._2), Coll[Any](SigmaDsl.sigmaProp(false))(RType.AnyType))(RType.AnyType) + }, + "{ (x: (Coll[Byte], Int)) => substConstants[Any](x._1, Coll[Int](x._2), Coll[Any](sigmaProp(false))) }", + FuncValue( + Vector((1, SPair(SByteArray, SInt))), + SubstConstants( + SelectField.typed[Value[SCollection[SByte.type]]](ValUse(1, SPair(SByteArray, SInt)), 1.toByte), + ConcreteCollection( + Array(SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SByteArray, SInt)), 2.toByte)), + SInt + ), + ConcreteCollection(Array(BoolToSigmaProp(FalseLeaf)), SSigmaProp) + ) + ))) + } + } diff --git a/sigmastate/src/test/scala/special/sigma/SigmaDslTesting.scala b/sigmastate/src/test/scala/special/sigma/SigmaDslTesting.scala index 1d0e4a8c0a..2d27ab1523 100644 --- a/sigmastate/src/test/scala/special/sigma/SigmaDslTesting.scala +++ b/sigmastate/src/test/scala/special/sigma/SigmaDslTesting.scala @@ -1,16 +1,33 @@ package special.sigma +import java.util + +import org.ergoplatform.{ErgoAddressEncoder, ErgoLikeTransaction, ErgoLikeContext, ErgoLikeInterpreter, Input, ErgoBox, DataInput, ErgoScriptPredef} import org.scalatest.prop.PropertyChecks import sigmastate.interpreter.Interpreter.ScriptEnv import org.scalacheck.{Arbitrary, Gen} import org.scalatest.{PropSpec, Matchers} import scala.util.{Success, Failure, Try} -import sigmastate.Values.SValue +import sigmastate.Values.{Constant, SValue, ConstantNode, ByteArrayConstant, IntConstant, ErgoTree} import scalan.RType -import org.ergoplatform.dsl.{SigmaContractSyntax, TestContractSpec} -import sigmastate.eval.{IRContext, SigmaDsl} -import sigmastate.helpers.SigmaPPrint +import scalan.util.Extensions._ +import org.ergoplatform.dsl.{SigmaContractSyntax, TestContractSpec, ContractSpec} +import org.ergoplatform.validation.{ValidationRules, SigmaValidationSettings} +import sigmastate.{eval, SSigmaProp, SType} +import SType.AnyOps +import org.ergoplatform.SigmaConstants.ScriptCostLimit +import sigmastate.basics.DLogProtocol.{ProveDlog, DLogProverInput} +import sigmastate.basics.{SigmaProtocol, SigmaProtocolPrivateInput, SigmaProtocolCommonInput} +import sigmastate.eval.{CompiletimeIRContext, Evaluation, CostingBox, SigmaDsl, IRContext, CostingDataContext} +import sigmastate.eval.Extensions._ +import sigmastate.utils.Helpers._ +import sigmastate.lang.Terms.ValueOps +import sigmastate.helpers.{ErgoLikeContextTesting, SigmaPPrint} +import sigmastate.helpers.TestingHelpers._ +import sigmastate.interpreter.{ProverResult, ContextExtension, ProverInterpreter} +import sigmastate.serialization.ValueSerializer +import sigmastate.utxo.{DeserializeContext, DeserializeRegister} import special.collection.Coll import scala.math.Ordering @@ -22,22 +39,22 @@ class SigmaDslTesting extends PropSpec with SigmaTestingData with SigmaContractSyntax with SigmaTypeGens { suite => - lazy val spec = TestContractSpec(suite)(new TestingIRContext) + lazy val spec: ContractSpec = TestContractSpec(suite)(new TestingIRContext) override def contractEnv: ScriptEnv = Map() - implicit lazy val IR = new TestingIRContext { + def createIR(): IRContext = new TestingIRContext { override val okPrintEvaluatedEntries: Boolean = false override val okMeasureOperationTime: Boolean = true } - def checkEq[A,B](f: A => B)(g: A => B): A => Try[B] = { x: A => - val b1 = Try(f(x)); val b2 = Try(g(x)) + def checkEq[A,B](scalaFunc: A => B)(g: A => (B, Int)): A => Try[(B, Int)] = { x: A => + val b1 = Try(scalaFunc(x)); val b2 = Try(g(x)) (b1, b2) match { - case (res @ Success(b1), Success(b2)) => + case (Success(b1), res @ Success((b2, _))) => assert(b1 == b2) res - case (res @ Failure(t1), Failure(t2)) => + case (Failure(t1), res @ Failure(t2)) => val c1 = rootCause(t1).getClass val c2 = rootCause(t2).getClass c1 shouldBe c2 @@ -77,10 +94,25 @@ class SigmaDslTesting extends PropSpec indices <- Gen.containerOfN[Array, Int](nIndexes, Gen.choose(0, arrLength - 1)) } yield indices + class FeatureProvingInterpreter extends ErgoLikeInterpreter()(new CompiletimeIRContext) with ProverInterpreter { + override type CTX = ErgoLikeContext + + def decodeSecretInput(decimalStr: String): DLogProverInput = DLogProverInput(BigInt(decimalStr).bigInteger) - case class EqualityChecker[T: RType](obj: T) { - def apply[R: RType](dslFunc: T => R)(script: String) = - checkEq(func[T, R](script))(dslFunc)(obj) + val sk1: DLogProverInput = decodeSecretInput("416167686186183758173232992934554728075978573242452195968805863126437865059") + val sk2: DLogProverInput = decodeSecretInput("34648336872573478681093104997365775365807654884817677358848426648354905397359") + val sk3: DLogProverInput = decodeSecretInput("50415569076448343263191022044468203756975150511337537963383000142821297891310") + + val secrets: Seq[SigmaProtocolPrivateInput[_ <: SigmaProtocol[_], _ <: SigmaProtocolCommonInput[_]]] = { + // Note, not all secrets are used, which is required by checkVerify + // This is to make AtLeast to be unproved and thus the verify is successfull + // because of the other condition in SigmaOr (see checkVerify) + val dlogs: IndexedSeq[DLogProverInput] = Vector(sk1) + dlogs + } + + val pubKeys: Seq[ProveDlog] = Vector(sk1, sk2, sk3) + .collect { case in: DLogProverInput => in.publicImage } } /** Type of the language feature to be tested. */ @@ -107,8 +139,6 @@ class SigmaDslTesting extends PropSpec printExpectedExpr: Boolean = true, logScript: Boolean = LogScriptDefault ) { - def printExpectedExprOff = copy(printExpectedExpr = false) - /** Called to print test case expression (when it is not given). * Can be used to create regression test cases. */ def printSuggestion(cf: CompiledFunc[_,_]): Unit = { @@ -136,7 +166,7 @@ class SigmaDslTesting extends PropSpec } /** v3 implementation*/ - private var _oldF: CompiledFunc[A, B] = null + private var _oldF: CompiledFunc[A, B] = _ def oldF: CompiledFunc[A, B] = { if (_oldF == null) { _oldF = oldImpl() @@ -146,7 +176,7 @@ class SigmaDslTesting extends PropSpec } /** v4 implementation*/ - private var _newF: CompiledFunc[A, B] = null + private var _newF: CompiledFunc[A, B] = _ def newF: CompiledFunc[A, B] = { if (_newF == null) { _newF = newImpl() @@ -159,7 +189,7 @@ class SigmaDslTesting extends PropSpec * semantic function (scalaFunc) on the given input. * @param input data which is used to execute feature * @return result of feature execution */ - def checkEquality(input: A, logInputOutput: Boolean = false): Try[B] = featureType match { + def checkEquality(input: A, logInputOutput: Boolean = false): Try[(B, Int)] = featureType match { case ExistingFeature => // check both implementations with Scala semantic val oldRes = checkEq(scalaFunc)(oldF)(input) @@ -188,24 +218,183 @@ class SigmaDslTesting extends PropSpec featureType match { case ExistingFeature => // check both implementations with Scala semantic - val oldRes = checkEq(scalaFunc)(oldF)(input) - oldRes.get shouldBe expectedResult + val (oldRes, _) = checkEq(scalaFunc)(oldF)(input).get + oldRes shouldBe expectedResult if (!(newImpl eq oldImpl)) { - val newRes = checkEq(scalaFunc)(newF)(input) - newRes.get shouldBe expectedResult + val (newRes, _) = checkEq(scalaFunc)(newF)(input).get + newRes shouldBe expectedResult } case AddedFeature => Try(oldF(input)).isFailure shouldBe true if (!(newImpl eq oldImpl)) { - val newRes = checkEq(scalaFunc)(newF)(input) - newRes.get shouldBe expectedResult + val (newRes, _) = checkEq(scalaFunc)(newF)(input).get + newRes shouldBe expectedResult } } } + /** Creates a new ErgoLikeContext using given [[CostingDataContext]] as template. + * Copies most of the data from ctx and the missing data is taken from the args. + * This is a helper method to be used in tests only. + */ + def createErgoLikeContext(ctx: CostingDataContext, + validationSettings: SigmaValidationSettings, + costLimit: Long, + initCost: Long + ): ErgoLikeContext = { + val treeData = SigmaDsl.toAvlTreeData(ctx.lastBlockUtxoRootHash) + val dataBoxes = ctx.dataInputs.toArray.map(SigmaDsl.toErgoBox) + val boxesToSpend = ctx.inputs.toArray.map(SigmaDsl.toErgoBox) + val txInputs = boxesToSpend.map(b => Input(b.id, ProverResult.empty)) + val txDataInputs = dataBoxes.map(b => DataInput(b.id)) + val txOutputCandidates = ctx.outputs.toArray.map(SigmaDsl.toErgoBox) + val tx = new ErgoLikeTransaction( + txInputs, txDataInputs, txOutputCandidates.toIndexedSeq) + val selfIndex = boxesToSpend.indexWhere(b => util.Arrays.equals(b.id, ctx.selfBox.id.toArray)) + + val extension = ContextExtension( + values = ctx.vars.toArray.zipWithIndex.collect { + case (v, i) if v != null => + val tpe = Evaluation.rtypeToSType(v.tVal) + i.toByte -> ConstantNode(v.value.asWrappedType, tpe) + }.toMap + ) + new ErgoLikeContext( + treeData, ctx.headers, ctx.preHeader, + dataBoxes, boxesToSpend, tx, selfIndex, + extension, validationSettings, costLimit, initCost) + } + + /** Executes the default feature verification wrapper script using: + * 1) the given input + * 2) the given expected intermediate result + * 3) the total expected execution cost of the verification + */ + def checkVerify(input: A, expectedRes: B, expectedCost: Int): Unit = { + val tpeA = Evaluation.rtypeToSType(oldF.tA) + val tpeB = Evaluation.rtypeToSType(oldF.tB) + + val prover = new FeatureProvingInterpreter() + + // Create synthetic ErgoTree which uses all main capabilities of evaluation machinery. + // 1) first-class functions (lambdas); 2) Context variables; 3) Registers; 4) Equality + // for all types; 5) Embedding of boolean to SigmaProp; 6) Sigma propositions (&&, ||, AtLeast) + // 7) Deserialization from SELF and Context + // Every language Feature is tested as part of this wrapper script. + // Inclusion of all the features influences the expected cost estimation values + val compiledTree = { + val code = + s"""{ + | val func = ${oldF.script} + | val res1 = func(getVar[${oldF.tA.name}](1).get) + | val res2 = SELF.R4[${oldF.tB.name}].get + | sigmaProp(res1 == res2) && pkAlice + |} + """.stripMargin + + val IR = new CompiletimeIRContext + val pkAlice = prover.pubKeys.head.toSigmaProp + val env = Map("pkAlice" -> pkAlice) + + // Compile script the same way it is performed by applications (i.e. via Ergo Appkit) + val prop = ErgoScriptPredef.compileWithCosting( + env, code, ErgoAddressEncoder.MainnetNetworkPrefix)(IR).asSigmaProp + + // Add additional oparations which are not yet implemented in ErgoScript compiler + val multisig = sigmastate.AtLeast( + IntConstant(2), + Seq( + pkAlice, + DeserializeRegister(ErgoBox.R5, SSigmaProp), // deserialize pkBob + DeserializeContext(2, SSigmaProp))) // deserialize pkCarol + ErgoTree.fromProposition(sigmastate.SigmaOr(prop, multisig)) + } + + val pkBobBytes = ValueSerializer.serialize(prover.pubKeys(1).toSigmaProp) + val pkCarolBytes = ValueSerializer.serialize(prover.pubKeys(2).toSigmaProp) + val newRegisters = Map( + ErgoBox.R4 -> Constant[SType](expectedRes.asInstanceOf[SType#WrappedType], tpeB), + ErgoBox.R5 -> ByteArrayConstant(pkBobBytes) + ) + + val ergoCtx = input match { + case ctx: CostingDataContext => + // the context is passed as function argument (see func in the script) + // Since Context is singleton, we should use this instance as the basis + // for execution of verify instead of a new dummy context. + val self = ctx.selfBox.asInstanceOf[CostingBox] + val newSelf = self.copy( + ebox = updatedRegisters(self.ebox, newRegisters) + ) + + // We add ctx as it's own variable with id = 1 + val ctxVar = eval.Extensions.toAnyValue[special.sigma.Context](ctx)(special.sigma.ContextRType) + val carolVar = eval.Extensions.toAnyValue[Coll[Byte]](pkCarolBytes.toColl)(RType[Coll[Byte]]) + val newCtx = ctx + .withUpdatedVars(1 -> ctxVar, 2 -> carolVar) + .copy( + selfBox = newSelf, + inputs = { + val selfIndex = ctx.inputs.indexWhere(b => b.id == ctx.selfBox.id, 0) + ctx.inputs.updated(selfIndex, newSelf) + }) + + createErgoLikeContext( + newCtx, + ValidationRules.currentSettings, + ScriptCostLimit.value, + initCost = 0L + ) + + case _ => + ErgoLikeContextTesting.dummy( + createBox(0, compiledTree, additionalRegisters = newRegisters) + ).withBindings( + 1.toByte -> Constant[SType](input.asInstanceOf[SType#WrappedType], tpeA), + 2.toByte -> ByteArrayConstant(pkCarolBytes) + ).asInstanceOf[ErgoLikeContext] + } + + val pr = prover.prove(compiledTree, ergoCtx, fakeMessage).getOrThrow + + implicit val IR: IRContext = createIR() + + val verifier = new ErgoLikeInterpreter() { type CTX = ErgoLikeContext } + + val verificationCtx = ergoCtx.withExtension(pr.extension) + + val vres = verifier.verify(compiledTree, verificationCtx, pr, fakeMessage) + vres match { + case Success((ok, cost)) => + ok shouldBe true + val verificationCost = cost.toIntExact +// NOTE: you can uncomment this line and comment the assertion in order to +// simplify adding new test vectors for cost estimation +// if (expectedCost != verificationCost) { +// println(s"Script: $script") +// println(s"Cost: $verificationCost\n") +// } + assertResult(expectedCost, + s"Actual verify() cost $cost != expected $expectedCost")(verificationCost) + + case Failure(t) => throw t + } + } } + object FeatureTest { + /** Cost of the feature verify script. + * @see checkVerify() */ + val VerifyScriptCost = 6317 + } + + /** Represents expected result of successful feature test exectuion. + * @param value value returned by feature function (and the corresponding Scala function) + * @param cost expected cost value of the verification execution + * @see [[testCases]] + */ + case class Expected[+A](value: A, cost: Int) /** Describes existing language feature which should be equally supported in both v3 and * v4 of the language. @@ -241,8 +430,8 @@ class SigmaDslTesting extends PropSpec FeatureTest(AddedFeature, script, scalaFunc, Option(expectedExpr), oldImpl, newImpl) } - val contextGen: Gen[Context] = ergoLikeContextGen.map(c => c.toSigmaContext(IR, false)) - implicit val arbContext = Arbitrary(contextGen) + val contextGen: Gen[Context] = ergoLikeContextGen.map(c => c.toSigmaContext(createIR(), isCost = false)) + implicit val arbContext: Arbitrary[Context] = Arbitrary(contextGen) /** NOTE, this should be `def` to allow overriding of generatorDrivenConfig in derived Spec classes. */ def DefaultMinSuccessful: MinSuccessful = MinSuccessful(generatorDrivenConfig.minSuccessful) @@ -250,8 +439,25 @@ class SigmaDslTesting extends PropSpec val PrintTestCasesDefault: Boolean = false val FailOnTestVectorsDefault: Boolean = true + private def checkResult[B](res: Try[B], expectedRes: Try[B], failOnTestVectors: Boolean): Unit = { + (res, expectedRes) match { + case (Failure(exception), Failure(expectedException)) => + rootCause(exception).getClass shouldBe expectedException.getClass + case _ => + if (failOnTestVectors) { + assertResult(expectedRes, s"Actual: ${SigmaPPrint(res, height = 150).plainText}")(res) + } + else { + if (expectedRes != res) { + print("\nSuggested Expected Result: ") + SigmaPPrint.pprintln(res, height = 150) + } + } + } + } + /** Test the given test cases with expected results (aka test vectors). - * NOTE, is some cases (such as Context, Box, etc) sample generation is time consuming, so it + * NOTE, in some cases (such as Context, Box, etc) sample generation is time consuming, so it * makes sense to factor it out. * @param preGeneratedSamples optional pre-generated samples to reduce execution time */ @@ -264,32 +470,58 @@ class SigmaDslTesting extends PropSpec val table = Table(("x", "y"), cases:_*) forAll(table) { (x: A, expectedRes: Try[B]) => - val res = f.checkEquality(x, printTestCases) + val res = f.checkEquality(x, printTestCases).map(_._1) // TODO HF: remove this `if` once newImpl is implemented - if (f.featureType == ExistingFeature) { - (res, expectedRes) match { - case (Failure(exception), Failure(expectedException)) => - exception.getClass shouldBe expectedException.getClass - case _ => - if (failOnTestVectors) { - assertResult(expectedRes, s"Actual: ${SigmaPPrint(res, height = 150).plainText}")(res) - } - else { - if (expectedRes != res) { - print("\nSuggested Expected Result: ") - SigmaPPrint.pprintln(res, height = 150) - } - } - } + f.featureType match { + case ExistingFeature => + checkResult(res, expectedRes, failOnTestVectors) + case AddedFeature => + res.isFailure shouldBe true + Try(f.scalaFunc(x)) shouldBe expectedRes } } - preGeneratedSamples match { - case Some(samples) => - test(samples, f, printTestCases) - case None => - test(f, printTestCases) + test(preGeneratedSamples, f, printTestCases) + } + + /** Test the given test cases with expected results AND costs (aka test vectors). + * For all Success cases `f.checkVerify` is executed to exercise the whole + * `Interpreter.verify` execution and assert the expected cost. + * + * NOTE, in some cases (such as Context, Box, etc) sample generation is time consuming, so it + * makes sense to factor it out. + * + * @param preGeneratedSamples optional pre-generated samples to reduce execution time + * if None, then the given Arbitrary is used to generate samples + */ + def verifyCases[A: Ordering : Arbitrary : ClassTag, B] + (cases: Seq[(A, Try[Expected[B]])], + f: FeatureTest[A, B], + printTestCases: Boolean = PrintTestCasesDefault, + failOnTestVectors: Boolean = FailOnTestVectorsDefault, + preGeneratedSamples: Option[Seq[A]] = None): Unit = { + + val table = Table(("x", "y"), cases:_*) + forAll(table) { (x: A, expectedRes: Try[Expected[B]]) => + val funcRes = f.checkEquality(x, printTestCases) + + val expectedResValue = expectedRes.map(_.value) + // TODO HF: remove this `match` once newImpl is implemented + f.featureType match { + case ExistingFeature => + checkResult(funcRes.map(_._1), expectedResValue, failOnTestVectors) + + (funcRes, expectedRes) match { + case (Success((y, _)), Success(Expected(_, expectedCost))) => + f.checkVerify(x, y, expectedCost) + case _ => + } + case AddedFeature => + funcRes.isFailure shouldBe true + Try(f.scalaFunc(x)) shouldBe expectedResValue + } } + test(preGeneratedSamples, f, printTestCases) } /** Generate samples in sorted order. @@ -298,7 +530,7 @@ class SigmaDslTesting extends PropSpec * @return array-backed ordered sequence of samples */ def genSamples[A: Ordering: ClassTag](gen: Gen[A], config: PropertyCheckConfigParam): Seq[A] = { - implicit val arb = Arbitrary(gen) + implicit val arb: Arbitrary[A] = Arbitrary(gen) genSamples[A](config) } @@ -308,123 +540,133 @@ class SigmaDslTesting extends PropSpec */ def genSamples[A: Arbitrary: Ordering: ClassTag](config: PropertyCheckConfigParam): Seq[A] = { val inputs = scala.collection.mutable.ArrayBuilder.make[A]() - forAll(config) { (x: A) => + forAll(config) { x: A => inputs += x } inputs.result().sorted } - def test[A: Ordering : ClassTag, B] - (samples: Seq[A], + /** Test the given samples or generate new samples using the given Arbitrary. + * For each sample `f.checkEquality` is executed. + */ + def test[A: Arbitrary: Ordering : ClassTag, B] + (preGeneratedSamples: Option[Seq[A]], f: FeatureTest[A, B], printTestCases: Boolean): Unit = { + // either get provides or generate new samples (in sorted order) + val samples = preGeneratedSamples.getOrElse(genSamples[A](DefaultMinSuccessful)) - // then tests them in the sorted order, this will output a nice log of test cases + // then tests them, this will output a nice log of test cases (provided printTestCases == true) samples.foreach { x => f.checkEquality(x, printTestCases) } } - def test[A: Ordering : ClassTag, B](samples: Seq[A], f: FeatureTest[A, B]): Unit = { - test(samples, f, PrintTestCasesDefault) + def test[A: Arbitrary : Ordering : ClassTag, B](samples: Seq[A], f: FeatureTest[A, B]): Unit = { + test(Some(samples), f, PrintTestCasesDefault) } def test[A: Arbitrary : Ordering : ClassTag, B] (f: FeatureTest[A, B], printTestCases: Boolean = PrintTestCasesDefault): Unit = { - // first generate all test inputs - val samples = genSamples[A](DefaultMinSuccessful) - // then test them - test(samples, f, printTestCases) + test(None, f, printTestCases) } + /** Helper implementation for ordering samples. */ trait GroupElementOrdering extends Ordering[GroupElement] { /** Compares `x: ECPoint` string representation with `y: ECPoint` string for order. - * @returns a negative integer, zero, or a positive integer as the + * @return a negative integer, zero, or a positive integer as the * `x` is less than, equal to, or greater than `y`. */ - def compare(x: GroupElement, y: GroupElement) = { + def compare(x: GroupElement, y: GroupElement): Int = { SigmaDsl.toECPoint(x).toString.compareTo(SigmaDsl.toECPoint(y).toString) } } implicit object GroupElementOrdering extends GroupElementOrdering + /** Helper implementation for ordering samples. */ trait AvlTreeOrdering extends Ordering[AvlTree] { /** Compares this `x: AvlTree` string representation with `y: AvlTree` string for order. - * @returns a negative integer, zero, or a positive integer as the + * @return a negative integer, zero, or a positive integer as the * `x` is less than, equal to, or greater than `y`. */ - def compare(x: AvlTree, y: AvlTree) = { + def compare(x: AvlTree, y: AvlTree): Int = { x.toString.compareTo(y.toString) } } implicit object AvlTreeOrdering extends AvlTreeOrdering + /** Helper implementation for ordering samples. */ class CollOrdering[T: Ordering] extends Ordering[Coll[T]] { - implicit val O = implicitly[Ordering[Iterable[T]]] + val O = Ordering[Iterable[T]] /** Compares this `x: Coll` with `y: Coll` using Ordering for underlying Array. - * @returns a negative integer, zero, or a positive integer as the + * @return a negative integer, zero, or a positive integer as the * `x` is less than, equal to, or greater than `y`. */ - def compare(x: Coll[T], y: Coll[T]) = { + def compare(x: Coll[T], y: Coll[T]): Int = { O.compare(x.toArray, y.toArray) } } implicit def collOrdering[T: Ordering]: Ordering[Coll[T]] = new CollOrdering[T] + /** Helper implementation for ordering samples. */ trait BoxOrdering extends Ordering[Box] { /** Compares this `x: Box` string representation with `y: Box` string for order. - * @returns a negative integer, zero, or a positive integer as the + * @return a negative integer, zero, or a positive integer as the * `x` is less than, equal to, or greater than `y`. */ - def compare(x: Box, y: Box) = { + def compare(x: Box, y: Box): Int = { x.toString.compareTo(y.toString) } } implicit object BoxOrdering extends BoxOrdering + /** Helper implementation for ordering samples. */ trait PreHeaderOrdering extends Ordering[PreHeader] { /** Compares this `x: PreHeader` with `y: PreHeader` using block height. - * @returns a negative integer, zero, or a positive integer as the + * @return a negative integer, zero, or a positive integer as the * `x` is less than, equal to, or greater than `y`. */ - def compare(x: PreHeader, y: PreHeader) = { + def compare(x: PreHeader, y: PreHeader): Int = { Ordering.Int.compare(x.height, y.height) } } implicit object PreHeaderOrdering extends PreHeaderOrdering + /** Helper implementation for ordering samples. */ trait HeaderOrdering extends Ordering[Header] { /** Compares this `x: Header` with `y: Header` using block height. - * @returns a negative integer, zero, or a positive integer as the + * @return a negative integer, zero, or a positive integer as the * `x` is less than, equal to, or greater than `y`. */ - def compare(x: Header, y: Header) = { + def compare(x: Header, y: Header): Int = { Ordering.Int.compare(x.height, y.height) } } implicit object HeaderOrdering extends HeaderOrdering + /** Helper implementation for ordering samples. */ trait ContextOrdering extends Ordering[Context] { - val O = Ordering[(Int, Coll[Byte])] + val O: Ordering[(Int, Coll[Byte])] = Ordering[(Int, Coll[Byte])] /** Compares this `x: Context` with `y: Context` using block height and SELF.id. - * @returns a negative integer, zero, or a positive integer as the + * @return a negative integer, zero, or a positive integer as the * `x` is less than, equal to, or greater than `y`. */ - def compare(x: Context, y: Context) = { + def compare(x: Context, y: Context): Int = { O.compare((x.HEIGHT, x.SELF.id), (y.HEIGHT, y.SELF.id)) } } implicit object ContextOrdering extends ContextOrdering + /** Helper implementation for ordering samples. */ trait SigmaPropOrdering extends Ordering[SigmaProp] { /** Compares this `x: SigmaProp` with `y: SigmaProp` using string representation. - * @returns a negative integer, zero, or a positive integer as the + * @return a negative integer, zero, or a positive integer as the * `x` is less than, equal to, or greater than `y`. */ - def compare(x: SigmaProp, y: SigmaProp) = { + def compare(x: SigmaProp, y: SigmaProp): Int = { x.toString.compareTo(y.toString) } } diff --git a/sigmastate/src/test/scala/special/sigma/SigmaTestingData.scala b/sigmastate/src/test/scala/special/sigma/SigmaTestingData.scala index da555c8165..cd0be01249 100644 --- a/sigmastate/src/test/scala/special/sigma/SigmaTestingData.scala +++ b/sigmastate/src/test/scala/special/sigma/SigmaTestingData.scala @@ -8,6 +8,7 @@ import sigmastate.{AvlTreeFlags, TrivialProp} import sigmastate.Values.{BooleanConstant, IntConstant} import org.scalacheck.{Arbitrary, Gen} import sigmastate.helpers.SigmaTestingCommons +import sigmastate.helpers.TestingHelpers._ import sigmastate.eval._ import sigmastate.eval.Extensions._ import org.ergoplatform.{ErgoLikeContext, DataInput, ErgoLikeTransaction, ErgoBox}