From 8436c3d7bbd0eb97ec1850b1e3e0e66b814df513 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Mon, 13 Nov 2023 13:07:37 +0100 Subject: [PATCH 01/25] new-sigma-js: exported Address JS class --- .../scala/sigma/data/CollsOverArrays.scala | 2 +- .../scala/sigma/util/CollectionUtil.scala | 4 +- .../src/main/scala/sigma/util/package.scala | 2 +- .../sigma/util/CollectionUtilTests.scala | 8 +- .../scala/org/ergoplatform/js/Address.scala | 82 +++++++++++++++++++ .../scala/org/ergoplatform/ErgoAddress.scala | 19 ++++- .../main/scala/org/ergoplatform/ErgoBox.scala | 4 +- .../src/main/scala/sigma/ast/ErgoTree.scala | 21 ++++- .../sigmastate/interpreter/Interpreter.scala | 2 +- .../interpreter/ProverInterpreter.scala | 2 +- .../org/ergoplatform/sdk/JavaHelpers.scala | 8 -- sigma-js/sigmastate-js.d.ts | 12 +++ sigma-js/tests/js/Address.spec.js | 39 +++++++++ 13 files changed, 180 insertions(+), 25 deletions(-) create mode 100644 data/js/src/main/scala/org/ergoplatform/js/Address.scala create mode 100644 sigma-js/tests/js/Address.spec.js diff --git a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala index a5bb543e1b..2413f7f427 100644 --- a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala +++ b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala @@ -45,7 +45,7 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil // in v5.0 and above this fixes the ClassCastException problem safeConcatArrays_v5(toArray, other.toArray)(tA.classTag) } else { - CollectionUtil.concatArrays(toArray, other.toArray) + CollectionUtil.concatArrays_v4(toArray, other.toArray) } builder.fromArray(result) } diff --git a/core/shared/src/main/scala/sigma/util/CollectionUtil.scala b/core/shared/src/main/scala/sigma/util/CollectionUtil.scala index 357fd3b9ef..ba506dec09 100644 --- a/core/shared/src/main/scala/sigma/util/CollectionUtil.scala +++ b/core/shared/src/main/scala/sigma/util/CollectionUtil.scala @@ -8,7 +8,7 @@ import scala.collection.compat._ object CollectionUtil { /** @deprecated shouldn't be used other than for backwards compatibility with v3.x, v4.x. */ - def concatArrays[T](xs: Array[T], ys: Array[T]): Array[T] = { + def concatArrays_v4[T](xs: Array[T], ys: Array[T]): Array[T] = { val len = xs.length + ys.length val result = (xs match { case _: Array[AnyRef] => new Array[AnyRef](len) // creates an array with invalid type descriptor (i.e. when T == Tuple2) @@ -31,7 +31,7 @@ object CollectionUtil { * This method takes ClassTag to create proper resulting array. * Can be used in v5.0 and above. */ - def concatArrays_v5[T:ClassTag](arr1: Array[T], arr2: Array[T]): Array[T] = { + def concatArrays[T:ClassTag](arr1: Array[T], arr2: Array[T]): Array[T] = { val l1 = arr1.length val l2 = arr2.length val length: Int = l1 + l2 diff --git a/core/shared/src/main/scala/sigma/util/package.scala b/core/shared/src/main/scala/sigma/util/package.scala index 87b3b6a805..a2a5a73472 100644 --- a/core/shared/src/main/scala/sigma/util/package.scala +++ b/core/shared/src/main/scala/sigma/util/package.scala @@ -25,6 +25,6 @@ package object util { final def safeConcatArrays_v5[A](arr1: Array[A], arr2: Array[A]) (implicit tA: ClassTag[A]): Array[A] = { checkLength[A](arr1.length + arr2.length) - CollectionUtil.concatArrays_v5(arr1, arr2) + CollectionUtil.concatArrays(arr1, arr2) } } diff --git a/core/shared/src/test/scala/sigma/util/CollectionUtilTests.scala b/core/shared/src/test/scala/sigma/util/CollectionUtilTests.scala index 24b03f9d88..f273568193 100644 --- a/core/shared/src/test/scala/sigma/util/CollectionUtilTests.scala +++ b/core/shared/src/test/scala/sigma/util/CollectionUtilTests.scala @@ -21,18 +21,18 @@ class CollectionUtilTests extends BaseTests { test("concatArrays") { val xs = Array[Byte](1,2,3) val ys = Array[Byte](4,5,6) - val zs = concatArrays(xs, ys) + val zs = concatArrays_v4(xs, ys) assertResult(Array[Byte](1, 2, 3, 4, 5, 6))(zs) val pairs = xs.zip(ys) // this reproduces the problem which takes place in v3.x, v4.x (ErgoTree v0, v1) - an[Throwable] should be thrownBy(concatArrays(pairs, pairs)) + an[Throwable] should be thrownBy(concatArrays_v4(pairs, pairs)) // and this is the fix in v5.0 - concatArrays_v5(pairs, pairs) shouldBe Array((1, 4), (2, 5), (3, 6), (1, 4), (2, 5), (3, 6)) + concatArrays(pairs, pairs) shouldBe Array((1, 4), (2, 5), (3, 6), (1, 4), (2, 5), (3, 6)) val xOpts = xs.map(Option(_)) - concatArrays_v5(xOpts, xOpts) shouldBe Array(Some(1), Some(2), Some(3), Some(1), Some(2), Some(3)) + concatArrays(xOpts, xOpts) shouldBe Array(Some(1), Some(2), Some(3), Some(1), Some(2), Some(3)) } def joinSeqs(l: Seq[Int], r: Seq[Int]) = diff --git a/data/js/src/main/scala/org/ergoplatform/js/Address.scala b/data/js/src/main/scala/org/ergoplatform/js/Address.scala new file mode 100644 index 0000000000..d3853f7c18 --- /dev/null +++ b/data/js/src/main/scala/org/ergoplatform/js/Address.scala @@ -0,0 +1,82 @@ +package org.ergoplatform.js + +import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix +import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress, Pay2SAddress} +import scorex.util.encode.Base58 +import sigma.js.SigmaProp + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExportTopLevel +import scala.scalajs.js.{UndefOr, undefined} +import scala.util.{Failure, Success} + +/** An exported JavaScript class wrapping the Scala `ErgoAddress` type. */ +@JSExportTopLevel("Address") +class Address( + ergoAddress: org.ergoplatform.ErgoAddress, + _addrBytes: Array[Byte] +) extends js.Object { + def this(ergoAddress: org.ergoplatform.ErgoAddress) = { + this(ergoAddress, ErgoAddressEncoder(ergoAddress.networkPrefix).toBytes(ergoAddress)) + } + private lazy val _base58String: String = Base58.encode(_addrBytes) + + /** First byte is used to encode network type and address type. + * + * @see ErgoAddressEncoder + */ + private def headByte: Byte = _addrBytes(0) + + /** @return true if this address from Ergo mainnet. */ + def isMainnet(): Boolean = Address.isMainnet(headByte) + + /** @return true if this address has Pay-To-Public-Key type. */ + def isP2PK(): Boolean = ergoAddress.isInstanceOf[P2PKAddress] + + /** @return true if this address has Pay-To-Script type. */ + def isP2S(): Boolean = ergoAddress.isInstanceOf[Pay2SAddress] + + def toSigmaPropOpt(): UndefOr[sigma.js.SigmaProp] = { + ergoAddress.script.toSigmaBooleanOpt match { + case Some(sb) => new SigmaProp(sb) + case _ => undefined + } + } + + /** Converts the given [[Address]] to Base58 string. */ + override def toString(): String = ergoAddress.toString +} + +/** An exported JavaScript object providing utility methods for working with Address instances. */ +@JSExportTopLevel("AddressObj") +object Address extends js.Object { + + /** @return true if this address from Ergo mainnet. */ + private def isMainnet(headByte: Byte): Boolean = headByte < ErgoAddressEncoder.TestnetNetworkPrefix + + private def getNetworkType(headByte: Byte): NetworkPrefix = { + if (isMainnet(headByte)) ErgoAddressEncoder.MainnetNetworkPrefix else ErgoAddressEncoder.TestnetNetworkPrefix + } + + /** Deserializes an ErgoTree instance from a hexadecimal string. + * + * @param hex a hexadecimal string representing the serialized ErgoTree + */ + def fromString(base58String: String): Address = { + Base58.decode(base58String) match { + case Success(bytes) => + val headByte = bytes(0) + val encoder = ErgoAddressEncoder(getNetworkType(headByte)) + encoder.fromBytes(bytes, base58String) match { + case Success(ergoAddress) => + new Address(ergoAddress, bytes) + case Failure(t) => + throw new RuntimeException( + "Invalid address encoding, expected base58 string: " + base58String, t) + } + case Failure(t) => + throw new RuntimeException( + "Invalid address encoding, expected base58 string: " + base58String, t) + } + } +} \ No newline at end of file diff --git a/data/shared/src/main/scala/org/ergoplatform/ErgoAddress.scala b/data/shared/src/main/scala/org/ergoplatform/ErgoAddress.scala index 134a30bc83..270f32d35d 100644 --- a/data/shared/src/main/scala/org/ergoplatform/ErgoAddress.scala +++ b/data/shared/src/main/scala/org/ergoplatform/ErgoAddress.scala @@ -12,6 +12,7 @@ import sigma.ast.ErgoTree.{ZeroHeader, setVersionBits} import sigma.ast._ import sigma.ast.syntax._ import sigma.serialization._ +import sigma.util.CollectionUtil import scala.util.Try @@ -270,12 +271,17 @@ case class ErgoAddressEncoder(networkPrefix: NetworkPrefix) { /** This value is be used implicitly in the methods below. */ implicit private def ergoAddressEncoder: ErgoAddressEncoder = this + /** Converts the given [[ErgoAddress]] to array of bytes. */ + def toBytes(address: ErgoAddress): Array[Byte] = { + val prefixByte = (networkPrefix + address.addressTypePrefix).toByte + val withNetworkByte = prefixByte +: address.contentBytes + val checksum = hash256(withNetworkByte).take(ChecksumLength) + CollectionUtil.concatArrays(withNetworkByte,checksum) + } + /** Converts the given [[ErgoAddress]] to Base58 string. */ def toString(address: ErgoAddress): String = { - val withNetworkByte = (networkPrefix + address.addressTypePrefix).toByte +: address.contentBytes - - val checksum = hash256(withNetworkByte).take(ChecksumLength) - Base58.encode(withNetworkByte ++ checksum) + Base58.encode(toBytes(address)) } /** Returns true if the given `addrHeadByte` is a header byte of a testnet address, false otherwise. */ @@ -286,6 +292,11 @@ case class ErgoAddressEncoder(networkPrefix: NetworkPrefix) { /** Converts the given Base58 string to [[ErgoAddress]] or an error packed in Try. */ def fromString(addrBase58Str: String): Try[ErgoAddress] = Base58.decode(addrBase58Str).flatMap { bytes => + fromBytes(bytes, addrBase58Str) + } + + /** Converts the given Base58 string to [[ErgoAddress]] or an error packed in Try. */ + private [ergoplatform] def fromBytes(bytes: Array[Byte], addrBase58Str: String): Try[ErgoAddress] = { Try { val headByte = bytes.head networkPrefix match { diff --git a/data/shared/src/main/scala/org/ergoplatform/ErgoBox.scala b/data/shared/src/main/scala/org/ergoplatform/ErgoBox.scala index c6c91450ec..4c240267c4 100644 --- a/data/shared/src/main/scala/org/ergoplatform/ErgoBox.scala +++ b/data/shared/src/main/scala/org/ergoplatform/ErgoBox.scala @@ -75,7 +75,7 @@ class ErgoBox private ( override def get(identifier: RegisterId): Option[Value[SType]] = { identifier match { case ReferenceRegId => - val tupleVal = (creationHeight, CollectionUtil.concatArrays_v5(transactionId.toBytes, Shorts.toByteArray(index)).toColl) + val tupleVal = (creationHeight, CollectionUtil.concatArrays(transactionId.toBytes, Shorts.toByteArray(index)).toColl) Some(Constant(tupleVal.asWrappedType, SReferenceRegType)) case _ => super.get(identifier) } @@ -182,7 +182,7 @@ object ErgoBox { .ensuring(_ == mandatoryRegisters.last.number + 1) val allRegisters: Seq[RegisterId] = - CollectionUtil.concatArrays_v5[RegisterId]( + CollectionUtil.concatArrays[RegisterId]( CollectionUtil.castArray(_mandatoryRegisters): Array[RegisterId], CollectionUtil.castArray(_nonMandatoryRegisters): Array[RegisterId]).ensuring(_.length == maxRegisters) diff --git a/data/shared/src/main/scala/sigma/ast/ErgoTree.scala b/data/shared/src/main/scala/sigma/ast/ErgoTree.scala index 1340ca4128..58e10b9399 100644 --- a/data/shared/src/main/scala/sigma/ast/ErgoTree.scala +++ b/data/shared/src/main/scala/sigma/ast/ErgoTree.scala @@ -5,7 +5,7 @@ import sigma.VersionContext import sigma.ast.ErgoTree.{HeaderType, substConstants} import sigma.ast.SigmaPropConstant import sigma.ast.syntax._ -import sigma.data.SigmaBoolean +import sigma.data.{CSigmaProp, SigmaBoolean} import sigma.kiama.rewriting.Rewriter.{everywherebu, strategy} import sigma.validation.ValidationException import sigma.ast.syntax.ValueOps @@ -193,6 +193,25 @@ case class ErgoTree private[sigma]( throw error } + /** This method attempts to convert the current instance into a `SigmaBoolean`. + * It does so by first converting the instance to a `SigmaPropValue` using the + * `toProposition` method, and then checks if the resulting expression is a + * `SigmaPropConstant`. If it is, the method extracts the `SigmaBoolean` from the + * `SigmaPropConstant`. Otherwise, the method returns `None`. + * + * @note This method relies on constant segregation flag in the header to determine the + * behavior of `toProposition` method. + * + * @return `Some(SigmaBoolean)` if conversion is successful, `None` otherwise. + */ + def toSigmaBooleanOpt: Option[SigmaBoolean] = { + val prop = this.toProposition(this.isConstantSegregation) + prop match { + case SigmaPropConstant(p) => Some(p.asInstanceOf[CSigmaProp].wrappedValue) + case _ => None + } + } + /** The default equality of case class is overridden to exclude `complexity`. */ override def canEqual(that: Any): Boolean = that.isInstanceOf[ErgoTree] diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala index 55f159b7e1..ea66d363ec 100644 --- a/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala +++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala @@ -382,7 +382,7 @@ trait Interpreter { private def checkCommitments(sp: UncheckedSigmaTree, message: Array[Byte])(implicit E: CErgoTreeEvaluator): Boolean = { // Perform Verifier Step 4 val newRoot = computeCommitments(sp).get.asInstanceOf[UncheckedSigmaTree] - val bytes = CollectionUtil.concatArrays_v5(FiatShamirTree.toBytes(newRoot), message) + val bytes = CollectionUtil.concatArrays(FiatShamirTree.toBytes(newRoot), message) /** * Verifier Steps 5-6: Convert the tree to a string `s` for input to the Fiat-Shamir hash function, * using the same conversion as the prover in 7 diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala index 37ed4a916c..ed2349eb0f 100644 --- a/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala +++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala @@ -99,7 +99,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils { // Prover Step 8: compute the challenge for the root of the tree as the Fiat-Shamir hash of propBytes // and the message being signed. - val rootChallenge = Challenge @@ CryptoFunctions.hashFn(CollectionUtil.concatArrays_v5(propBytes, message)).toColl + val rootChallenge = Challenge @@ CryptoFunctions.hashFn(CollectionUtil.concatArrays(propBytes, message)).toColl val step8 = step6.withChallenge(rootChallenge) // Prover Step 9: complete the proof by computing challenges at real nodes and additionally responses at real leaves diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala index ce139ef6c7..14cdf7f6bb 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala @@ -260,14 +260,6 @@ object JavaHelpers { ErgoAlgos.encode(ErgoAlgos.hash(s)) } - - def toSigmaBoolean(ergoTree: ErgoTree): SigmaBoolean = { - val prop = ergoTree.toProposition(ergoTree.isConstantSegregation) - prop match { - case SigmaPropConstant(p) => SigmaDsl.toSigmaBoolean(p) - } - } - def toErgoTree(sigmaBoolean: SigmaBoolean): ErgoTree = ErgoTree.fromSigmaBoolean(sigmaBoolean) def getStateDigest(tree: AvlTree): Array[Byte] = { diff --git a/sigma-js/sigmastate-js.d.ts b/sigma-js/sigmastate-js.d.ts index 775f585570..57878595e3 100644 --- a/sigma-js/sigmastate-js.d.ts +++ b/sigma-js/sigmastate-js.d.ts @@ -11,6 +11,18 @@ declare module "sigmastate-js/main" { type HexString = string; type ByteArray = { u: Int8Array }; + export declare class Address { + isMainnet(): boolean; + isP2PK(): boolean; + isP2S(): boolean; + toSigmaPropOpt(): SigmaProp | undefined; + toString(): string; + } + + export declare class AddressObj { + static fromString(value: HexString): Address; + } + export declare class ErgoTree { toHex(): HexString; bytes(): ByteArray; diff --git a/sigma-js/tests/js/Address.spec.js b/sigma-js/tests/js/Address.spec.js new file mode 100644 index 0000000000..1f6770f712 --- /dev/null +++ b/sigma-js/tests/js/Address.spec.js @@ -0,0 +1,39 @@ +const { Address, AddressObj } = require("sigmastate-js/main"); + +describe("Smoke tests for API exporting", () => { + it("Should export Address object", () => { + expect(Address).not.toBeUndefined(); + }); +}); + +describe("Address", () => { + let addrStr = "9iJd9drp1KR3R7HLi7YmQbB5sJ5HFKZoPb5MxGepamggJs5vDHm"; + let p2sStr = "JryiCXrZf5VDetH1PM7rKDX3q4sLF34AdErWJFqG87Hf5ikTDf636b35Nr7goWMdRUKA3ZPxdeqFNbQzBjhnDR9SUMYwDX1tdV8ZuGgXwQPaRVcB9" + + it("roundtrip for P2PK", () => { + let addr = AddressObj.fromString(addrStr); + expect(addr.isP2PK()).toEqual(true) + expect(addr.isP2S()).toEqual(false) + + expect(addr.toString()).not.toBeUndefined(); + expect(addr.toString()).toEqual(addrStr) + }); + + it("roundtrip for P2S", () => { + let addr = AddressObj.fromString(p2sStr); + expect(addr.isP2S()).toEqual(true) + expect(addr.isP2PK()).toEqual(false) + + expect(addr.toString()).not.toBeUndefined(); + expect(addr.toString()).toEqual(p2sStr) + }); + + it("toSigmaPropOpt", () => { + let addr = AddressObj.fromString(addrStr); + + expect(addr.isMainnet()).toEqual(true) + expect(addr.toSigmaPropOpt()).not.toBeUndefined() + }); + +}); + From bccb03af22bcc04a59a6ad955b9f881070c3fbd5 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Mon, 13 Nov 2023 21:40:00 +0100 Subject: [PATCH 02/25] new-sigma-js: implemented all types of addresses --- .../scala/org/ergoplatform/js/Address.scala | 152 ++++++++++++++++-- .../main/scala/sigma/ast/js/ErgoTree.scala | 2 +- 2 files changed, 138 insertions(+), 16 deletions(-) diff --git a/data/js/src/main/scala/org/ergoplatform/js/Address.scala b/data/js/src/main/scala/org/ergoplatform/js/Address.scala index d3853f7c18..47ca0fefd0 100644 --- a/data/js/src/main/scala/org/ergoplatform/js/Address.scala +++ b/data/js/src/main/scala/org/ergoplatform/js/Address.scala @@ -1,9 +1,10 @@ package org.ergoplatform.js import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix -import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress, Pay2SAddress} +import org.ergoplatform.ErgoAddressEncoder import scorex.util.encode.Base58 -import sigma.js.SigmaProp +import sigma.ast.js.ErgoTree +import sigma.js.{GroupElement, SigmaProp} import scala.scalajs.js import scala.scalajs.js.annotation.JSExportTopLevel @@ -12,30 +13,68 @@ import scala.util.{Failure, Success} /** An exported JavaScript class wrapping the Scala `ErgoAddress` type. */ @JSExportTopLevel("Address") -class Address( - ergoAddress: org.ergoplatform.ErgoAddress, - _addrBytes: Array[Byte] -) extends js.Object { - def this(ergoAddress: org.ergoplatform.ErgoAddress) = { - this(ergoAddress, ErgoAddressEncoder(ergoAddress.networkPrefix).toBytes(ergoAddress)) +abstract class Address extends js.Object { + def ergoAddress: org.ergoplatform.ErgoAddress + + private lazy val _addressBytes: Array[Byte] = { + ErgoAddressEncoder(ergoAddress.networkPrefix).toBytes(ergoAddress) } - private lazy val _base58String: String = Base58.encode(_addrBytes) + + def addressBytes: Array[Byte] = _addressBytes + + /** Address type code used to differentiate between pay-to-public-key, pay-to-script, + * pay-to-script-hash addresses. + * + * @see [[P2PKAddress]], [[P2SAddress]], [[P2SHAddress]] + */ + def addressTypePrefix: Byte = ergoAddress.addressTypePrefix /** First byte is used to encode network type and address type. * * @see ErgoAddressEncoder */ - private def headByte: Byte = _addrBytes(0) + private def headByte: Byte = _addressBytes(0) /** @return true if this address from Ergo mainnet. */ def isMainnet(): Boolean = Address.isMainnet(headByte) /** @return true if this address has Pay-To-Public-Key type. */ - def isP2PK(): Boolean = ergoAddress.isInstanceOf[P2PKAddress] + def isP2PK(): Boolean = ergoAddress.isInstanceOf[org.ergoplatform.P2PKAddress] + + /** @return underlying {@link P2PKAddress}. + * @throws IllegalArgumentException if this instance is not P2PK address + */ + def asP2PK(): P2PKAddress = { + require(isP2PK(), s"This instance $this is not P2PKAddress") + new P2PKAddress(ergoAddress.asInstanceOf[org.ergoplatform.P2PKAddress]) + } + + /** @return true if this address has Pay-To-Script type. */ + def isP2S(): Boolean = ergoAddress.isInstanceOf[org.ergoplatform.Pay2SAddress] + + /** @return underlying {@link P2SAddress}. + * @throws IllegalArgumentException if this instance is not P2S address + */ + def asP2S(): P2SAddress = { + require(isP2PK(), s"This instance $this is not P2PKAddress") + new P2SAddress(ergoAddress.asInstanceOf[org.ergoplatform.Pay2SAddress]) + } /** @return true if this address has Pay-To-Script type. */ - def isP2S(): Boolean = ergoAddress.isInstanceOf[Pay2SAddress] + def isP2SH(): Boolean = ergoAddress.isInstanceOf[org.ergoplatform.Pay2SHAddress] + + /** @return underlying {@link P2SHAddress}. + * @throws IllegalArgumentException if this instance is not P2S address + */ + def asP2SH(): P2SHAddress = { + require(isP2SH(), s"This instance $this is not P2SHAddress") + new P2SHAddress(ergoAddress.asInstanceOf[org.ergoplatform.Pay2SHAddress]) + } + /** Extracts a [[sigma.js.SigmaProp]] from this address of the underlying ErgoTree if of + * specific form. + * @see ErgoTree.toSigmaBooleanOpt() + */ def toSigmaPropOpt(): UndefOr[sigma.js.SigmaProp] = { ergoAddress.script.toSigmaBooleanOpt match { case Some(sb) => new SigmaProp(sb) @@ -43,13 +82,34 @@ class Address( } } + /** ErgoTree which corresponds to the address (depending on the address type). + * + * @see [[P2PKAddress]], [[P2SAddress]], [[P2SHAddress]] + */ + def toErgoTree(): ErgoTree = new ErgoTree(ergoAddress.script) + + /** @return this addresses ErgoTree's proposition bytes. Use this to store this address + * on Box registers. + */ + def toPropositionBytes(): Array[Byte] = ergoAddress.script.bytes + /** Converts the given [[Address]] to Base58 string. */ - override def toString(): String = ergoAddress.toString + override def toString() = ergoAddress.toString } /** An exported JavaScript object providing utility methods for working with Address instances. */ @JSExportTopLevel("AddressObj") object Address extends js.Object { + def fromErgoAddress(ergoAddress: org.ergoplatform.ErgoAddress): Address = { + ergoAddress match { + case p2pk: org.ergoplatform.P2PKAddress => + new P2PKAddress(p2pk) + case p2s: org.ergoplatform.Pay2SAddress => + new P2SAddress(p2s) + case p2sh: org.ergoplatform.Pay2SHAddress => + new P2SHAddress(p2sh) + } + } /** @return true if this address from Ergo mainnet. */ private def isMainnet(headByte: Byte): Boolean = headByte < ErgoAddressEncoder.TestnetNetworkPrefix @@ -69,7 +129,7 @@ object Address extends js.Object { val encoder = ErgoAddressEncoder(getNetworkType(headByte)) encoder.fromBytes(bytes, base58String) match { case Success(ergoAddress) => - new Address(ergoAddress, bytes) + Address.fromErgoAddress(ergoAddress) case Failure(t) => throw new RuntimeException( "Invalid address encoding, expected base58 string: " + base58String, t) @@ -79,4 +139,66 @@ object Address extends js.Object { "Invalid address encoding, expected base58 string: " + base58String, t) } } -} \ No newline at end of file + + /** Creates an `Address` instance from an `ErgoTree` and a network prefix. + * + * @param ergoTree The `ErgoTree` instance to be converted into an `Address`. + * @param networkPrefix The network prefix indicating the network for which the address is valid. + * @return An `Address` instance corresponding to the given `ErgoTree` and network prefix. + */ + def fromErgoTree(ergoTree: ErgoTree, networkPrefix: NetworkPrefix): Address = { + val encoder = ErgoAddressEncoder(networkPrefix) + val ergoAddress = encoder.fromProposition(ergoTree.tree).get + Address.fromErgoAddress(ergoAddress) + } + + /** + * Creates an `Address` from a `SigmaProp` and a network prefix. + * + * @param sigmaProp The `SigmaProp` to be converted into an `Address`. + * @param networkPrefix The network prefix indicating the network for which the address is valid. + * @return An `Address` instance corresponding to the given `SigmaProp` and network prefix. + */ + def fromSigmaProp( + sigmaProp: SigmaProp, + networkPrefix: NetworkPrefix): Address = { + val ergoTree = sigma.ast.ErgoTree.fromSigmaBoolean(sigmaProp.sigmaBoolean) + fromErgoTree(new ErgoTree(ergoTree), networkPrefix) + } + + /** Creates address from given ergovalue containing an ErgoTree proposition bytes. + * Use this to convert a box register containing an ErgoTree into its address. + * + * @param networkPrefix mainnet or testnet network + * @param propositionBytes ErgoTree proposition bytes + */ + def fromPropositionBytes(networkPrefix: NetworkPrefix, propositionBytes: Array[Byte]): Address = { + fromErgoTree(ErgoTree.fromBytes(propositionBytes), networkPrefix) + } + +} + +/** An exported JavaScript class wrapping the Scala `P2PKAddress` type. */ +@JSExportTopLevel("P2PKAddress") +class P2PKAddress( + override val ergoAddress: org.ergoplatform.P2PKAddress +) extends Address { + + /** Converts this address to the underlying ProveDlog sigma proposition wrapped in SigmaProp. */ + def toSigmaProp(): sigma.js.SigmaProp = new SigmaProp(ergoAddress.pubkey) + + /** Extract the underlying [[sigma.js.GroupElement]] of this address. */ + def getPublicKeyGE(): sigma.js.GroupElement = new GroupElement(ergoAddress.pubkey.value) +} + +/** An exported JavaScript class wrapping the Scala `P2SAddress` type. */ +@JSExportTopLevel("P2SAddress") +class P2SAddress( + override val ergoAddress: org.ergoplatform.Pay2SAddress +) extends Address + +/** An exported JavaScript class wrapping the Scala `P2SHAddress` type. */ +@JSExportTopLevel("P2SHAddress") +class P2SHAddress( + override val ergoAddress: org.ergoplatform.Pay2SHAddress +) extends Address diff --git a/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala b/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala index 1e5ec37d11..22f189fba2 100644 --- a/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala +++ b/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala @@ -9,7 +9,7 @@ import scala.scalajs.js.annotation.JSExportTopLevel /** An exported JavaScript class wrapping the Scala `ErgoTree` type. */ @JSExportTopLevel("ErgoTree") -class ErgoTree(tree: ast.ErgoTree) extends js.Object { +class ErgoTree(val tree: ast.ErgoTree) extends js.Object { /** The first byte of serialized byte array which determines interpretation of the rest of the array. */ def header(): Byte = tree.header From 4efd5d4d644e378a88432b75a79513fa178ccfff Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Mon, 13 Nov 2023 22:10:03 +0100 Subject: [PATCH 03/25] new-sigma-js: typescript definitions in sigmastate-js.d.ts --- .../scala/org/ergoplatform/js/Address.scala | 16 +- sigma-js/sigmastate-js.d.ts | 742 ++++++++++-------- 2 files changed, 443 insertions(+), 315 deletions(-) diff --git a/data/js/src/main/scala/org/ergoplatform/js/Address.scala b/data/js/src/main/scala/org/ergoplatform/js/Address.scala index 47ca0fefd0..85a90d4574 100644 --- a/data/js/src/main/scala/org/ergoplatform/js/Address.scala +++ b/data/js/src/main/scala/org/ergoplatform/js/Address.scala @@ -20,14 +20,17 @@ abstract class Address extends js.Object { ErgoAddressEncoder(ergoAddress.networkPrefix).toBytes(ergoAddress) } - def addressBytes: Array[Byte] = _addressBytes + /** Serialize this address to bytes. + * @see ErgoAddressEncoder.toBytes() + */ + def addressBytes(): Array[Byte] = _addressBytes /** Address type code used to differentiate between pay-to-public-key, pay-to-script, * pay-to-script-hash addresses. * * @see [[P2PKAddress]], [[P2SAddress]], [[P2SHAddress]] */ - def addressTypePrefix: Byte = ergoAddress.addressTypePrefix + def addressTypePrefix(): Byte = ergoAddress.addressTypePrefix /** First byte is used to encode network type and address type. * @@ -60,11 +63,11 @@ abstract class Address extends js.Object { new P2SAddress(ergoAddress.asInstanceOf[org.ergoplatform.Pay2SAddress]) } - /** @return true if this address has Pay-To-Script type. */ + /** @return true if this address has Pay-To-Script-Hash type. */ def isP2SH(): Boolean = ergoAddress.isInstanceOf[org.ergoplatform.Pay2SHAddress] /** @return underlying {@link P2SHAddress}. - * @throws IllegalArgumentException if this instance is not P2S address + * @throws IllegalArgumentException if this instance is not P2SH address */ def asP2SH(): P2SHAddress = { require(isP2SH(), s"This instance $this is not P2SHAddress") @@ -100,6 +103,7 @@ abstract class Address extends js.Object { /** An exported JavaScript object providing utility methods for working with Address instances. */ @JSExportTopLevel("AddressObj") object Address extends js.Object { + /** Creates JS wrapper over given [[ErgoAddress]]. */ def fromErgoAddress(ergoAddress: org.ergoplatform.ErgoAddress): Address = { ergoAddress match { case p2pk: org.ergoplatform.P2PKAddress => @@ -118,9 +122,9 @@ object Address extends js.Object { if (isMainnet(headByte)) ErgoAddressEncoder.MainnetNetworkPrefix else ErgoAddressEncoder.TestnetNetworkPrefix } - /** Deserializes an ErgoTree instance from a hexadecimal string. + /** Deserializes an ErgoTree instance from an address string. * - * @param hex a hexadecimal string representing the serialized ErgoTree + * @param base58String a Base58 string representing the serialized ErgoTree */ def fromString(base58String: String): Address = { Base58.decode(base58String) match { diff --git a/sigma-js/sigmastate-js.d.ts b/sigma-js/sigmastate-js.d.ts index 57878595e3..a6699136f6 100644 --- a/sigma-js/sigmastate-js.d.ts +++ b/sigma-js/sigmastate-js.d.ts @@ -1,316 +1,440 @@ declare module "sigmastate-js/main" { - import { - Amount, - Box, - EIP12UnsignedInput, - NonMandatoryRegisters, SignedTransaction, TokenAmount, - UnsignedTransaction - } from "@fleet-sdk/common"; - - type SigmaCompilerNamedConstantsMap = { [key: string]: Value }; - type HexString = string; - type ByteArray = { u: Int8Array }; - - export declare class Address { - isMainnet(): boolean; - isP2PK(): boolean; - isP2S(): boolean; - toSigmaPropOpt(): SigmaProp | undefined; - toString(): string; - } - - export declare class AddressObj { - static fromString(value: HexString): Address; - } - - export declare class ErgoTree { - toHex(): HexString; - bytes(): ByteArray; - header(): number; - version(): number; - isConstantSegregation(): boolean; - hasSize(): boolean; - constants(): Value[]; - template(): ByteArray; - templateHex(): HexString; - toString(): string; - } - - export declare class ErgoTreeObj { - static fromHex(value: HexString): ErgoTree; - } - - export declare class GroupElement { - toPointHex(): HexString; - } - - export declare class GroupElementObj { - static fromPointHex(value: HexString): GroupElement; - } - - export declare class SigmaProp { - } - - export declare class SigmaPropObj { - static fromPointHex(value: HexString): SigmaProp; - } - - export declare class AvlTree { - digest: HexString; - insertAllowed: Boolean; - updateAllowed: Boolean; - removeAllowed: Boolean; - keyLength: number; - valueLengthOpt: number | undefined; - } - - export declare class PreHeader { - /** Block version, to be increased on every soft and hardfork. */ - version: number; - /** Hex of id of parent block */ - parentId: HexString; - /** Block timestamp (in milliseconds since beginning of Unix Epoch) */ - timestamp: bigint; - /** Current difficulty in a compressed view. - * NOTE: actually it is unsigned integer */ - nBits: bigint; - /** Block height */ - height: number; - /** Miner public key (hex of EC Point). Should be used to collect block rewards. */ - minerPk: GroupElement; - /** Hex of miner votes bytes for changing system parameters. */ - votes: HexString; - } - - export declare class Header { - /** Hex representation of ModifierId of this Header */ - id: HexString; - /** Block version, to be increased on every soft and hardfork. */ - version: number; - /** Hex representation of ModifierId of the parent block */ - parentId: HexString; - /** Hex hash of ADProofs for transactions in a block */ - ADProofsRoot: HexString; - /** AvlTree of a state after block application */ - stateRoot: AvlTree; - /** Hex of root hash (for a Merkle tree) of transactions in a block. */ - transactionsRoot: HexString; - /** Block timestamp (in milliseconds since beginning of Unix Epoch) */ - timestamp: bigint; - /** Current difficulty in a compressed view. - * NOTE: actually it is unsigned Int */ - nBits: bigint; - /** Block height */ - height: number; - /** Hex of root hash of extension section */ - extensionRoot: HexString; - - /** Miner public key (hex of EC Point). Should be used to collect block rewards. - * Part of Autolykos solution. - */ - minerPk: GroupElement; - - /** One-time public key (hex of EC Point). Prevents revealing of miners secret. */ - powOnetimePk: GroupElement; - - /** Hex of nonce bytes */ - powNonce: HexString; - - /** Distance between pseudo-random number, corresponding to nonce `powNonce` and a secret, - * corresponding to `minerPk`. The lower `powDistance` is, the harder it was to find this solution. */ - powDistance: bigint; - - /** Miner votes for changing system parameters. */ - votes: HexString; - } - - export declare class BlockchainParameters { - storageFeeFactor: number; - minValuePerByte: number; - maxBlockSize: number; - tokenAccessCost: number; - inputCost: number; - dataInputCost: number; - outputCost: number; - maxBlockCost: number; - softForkStartingHeight: number | undefined; - softForkVotesCollected: number | undefined; - blockVersion: number; - } - - export declare class BlockchainStateContext { - sigmaLastHeaders: Header[]; - previousStateDigest: HexString; - sigmaPreHeader: PreHeader; - } - - export declare class Type { - name: string; - toString(): string; - } - - export declare class TypeObj { - static Byte: Type; - static Short: Type; - static Int: Type; - static Long: Type; - static BigInt: Type; - static GroupElement: Type; - static SigmaProp: Type; - static Box: Type; - static AvlTree: Type; - static Context: Type; - static Header: Type; - static PreHeader: Type; - static SigmaDslBuilder: Type; - static pairType(left: Type, right: Type): Type; - static collType(elemType: Type): Type; - } - - export declare class Value { - data: T; - tpe: Type; - toHex(): HexString; - } - - export declare class ValueObj { - static ofByte(value: number): Value; - static ofShort(value: number): Value; - static ofInt(value: number): Value; - static ofLong(value: bigint): Value; - static ofBigInt(value: bigint): Value; - static ofGroupElement(pointHex: string): Value; - static ofSigmaProp(pointHex: string): Value; - static pairOf(left: Value, right: Value): Value<[R, L]>; - static collOf(items: T[], elemType: Type): Value; - static fromHex(hex: HexString): Value; - } - - export declare class SigmaCompiler { - compile( - namedConstants: SigmaCompilerNamedConstantsMap, - segregateConstants: boolean, - additionalHeaderFlags: number, - ergoScript: string - ): ErgoTree; - } - - export declare class SigmaCompilerObj { - static forMainnet(): SigmaCompiler; - static forTestnet(): SigmaCompiler; - } - - /** Represents results for transaction reduction by {@link SigmaProver}. */ - export declare class ReducedTransaction { - /** Serialized bytes of this transaction in hex format. */ - toHex(): HexString; - } - - export declare class ReducedTransactionObj { - /** Creates a {@link ReducedTransaction} from serialized bytes in hex format. */ - fromHex(hex: HexString): ReducedTransaction; - } - - /** Represents a prover for signing Ergo transactions and messages. - * - * Equivalent of [[org.ergoplatform.sdk.SigmaProver]] available from JS. - */ - export declare class SigmaProver { - /** Returns the Pay-to-Public-Key (P2PK) address associated with the prover's public key. - * The returned address corresponds to the master secret derived from the mnemonic - * phrase configured in the [[ProverBuilder]]. - */ - getP2PKAddress(): HexString; - - /** Returns the prover's secret key. */ - getSecretKey(): bigint; - - /** Returns an array of EIP-3 addresses associated with the prover's secret keys. */ - getEip3Addresses(): HexString[]; - - /** Reduces the transaction to the reduced form, which is ready to be signed. - * @param stateCtx blockchain state context - * @param unsignedTx unsigned transaction to be reduced (created by Fleet builders) - * @param boxesToSpend boxes to be spent by the transaction - * @param dataInputs data inputs to be used by the transaction - * @param tokensToBurn tokens to be burned by the transaction - * @param baseCost base cost of the transaction - * @return reduced transaction - */ - reduce( - stateCtx: BlockchainStateContext, - unsignedTx: UnsignedTransaction, - boxesToSpend: EIP12UnsignedInput[], - dataInputs: Box[], - tokensToBurn: TokenAmount[], - baseCost: number): ReducedTransaction; - - /** Signs the reduced transaction. - * @param reducedTx reduced transaction to be signed - * @return signed transaction containting all the required proofs (signatures) - */ - signReduced(reducedTx: ReducedTransaction): SignedTransaction; - } - - /** Equivalent of [[sdk.ProverBuilder]] available from JS. - * - * @param parameters Blockchain parameters re-adjustable via miners voting and - * voting-related data. All of them are included into extension - * section of a first block of a voting epoch. - * @param network Network prefix to use for addresses. - */ - export declare class ProverBuilder { - /** Configure this builder to use the given seed when building a new prover. - * - * @param mnemonicPhrase secret seed phrase to be used in prover for generating proofs. - * @param mnemonicPass password to protect secret seed phrase. - */ - withMnemonic(mnemonicPhrase: HexString, mnemonicPass: HexString): ProverBuilder; - /** Configure this builder to derive the new EIP-3 secret key with the given index. - * The derivation uses master key derived from the mnemonic configured using - * [[ErgoProverBuilder.withMnemonic]]. - * - * @param index last index in the EIP-3 derivation path. - */ - withEip3Secret(index: number): ProverBuilder; + import { + Amount, + Box, + EIP12UnsignedInput, + NonMandatoryRegisters, SignedTransaction, TokenAmount, + UnsignedTransaction + } from "@fleet-sdk/common"; + + type SigmaCompilerNamedConstantsMap = { [key: string]: Value }; + type HexString = string; + type ByteArray = { u: Int8Array }; + + export declare class Address { + /** Serialize this address to bytes. */ + addressBytes(): ByteArray; + + /** Address type code used to differentiate between pay-to-public-key, pay-to-script, + * pay-to-script-hash addresses. + * + * @see [[P2PKAddress]], [[P2SAddress]], [[P2SHAddress]] + */ + addressTypePrefix(): number; + + /** @return true if this address from Ergo mainnet. */ + isMainnet(): boolean; + + /** @return true if this address has Pay-To-Public-Key type. */ + isP2PK(): boolean; + + /** @return underlying {@link P2PKAddress}. + * @throws IllegalArgumentException if this instance is not P2PK address + */ + asP2PK(): P2PKAddress; + + /** @return true if this address has Pay-To-Script type. */ + isP2S(): boolean; + + /** @return underlying {@link P2PKAddress}. + * @throws IllegalArgumentException if this instance is not P2PK address + */ + asP2S(): P2SAddress; + + /** @return true if this address has Pay-To-Script-Hash type. */ + isP2SH(): boolean; + + /** @return underlying {@link P2SHAddress}. + * @throws IllegalArgumentException if this instance is not P2SH address + */ + asP2SH(): P2SHAddress; + + /** Extracts a {@link SigmaProp} from this address of the underlying ErgoTree if of + * specific form. + * @see ErgoTree.toSigmaBooleanOpt() + */ + toSigmaPropOpt(): SigmaProp | undefined; + + /** ErgoTree which corresponds to the address (depending on the address type). + * + * @see P2PKAddress, P2SAddress, P2SHAddress + */ + toErgoTree(): ErgoTree; + + /** @return this addresses ErgoTree's proposition bytes. Use this to store this address + * on Box registers. + */ + toPropositionBytes(): ByteArray; + + /** Converts the given {@link Address} to Base58 string. */ + toString(): string; + } + + export declare class AddressObj { + /** Creates JS wrapper over given [[ErgoAddress]]. */ + static fromErgoAddress(ergoAddress: any /*org.ergoplatform.ErgoAddress*/): Address; + + /** Deserializes an ErgoTree instance from an address string. + * + * @param base58String a Base58 string representing the serialized ErgoTree + */ + static fromString(base58String: string): Address; + + /** Creates an `Address` instance from an `ErgoTree` and a network prefix. + * + * @param ergoTree The `ErgoTree` instance to be converted into an `Address`. + * @param networkPrefix The network prefix indicating the network for which the address is valid. + * @return An `Address` instance corresponding to the given `ErgoTree` and network prefix. + */ + static fromErgoTree(ergoTree: ErgoTree, networkPrefix: number): Address; + + /** + * Creates an `Address` from a `SigmaProp` and a network prefix. + * + * @param sigmaProp The `SigmaProp` to be converted into an `Address`. + * @param networkPrefix The network prefix indicating the network for which the address is valid. + * @return An `Address` instance corresponding to the given `SigmaProp` and network prefix. + */ + static fromSigmaProp(sigmaProp: SigmaProp, networkPrefix: number): Address; + + /** Creates address from given ergovalue containing an ErgoTree proposition bytes. + * Use this to convert a box register containing an ErgoTree into its address. + * + * @param networkPrefix mainnet or testnet network + * @param propositionBytes ErgoTree proposition bytes + */ + static fromPropositionBytes(networkPrefix: number, propositionBytes: ByteArray): Address + } + + /** Implementation of pay-to-public-key {@link Address}. */ + export declare class P2PKAddress extends Address { + /** Converts this address to the underlying ProveDlog sigma proposition wrapped in {@link SigmaProp}. */ + toSigmaProp(): SigmaProp; + + /** Extract the underlying {@link GroupElement} of this address. */ + getPublicKeyGE(): GroupElement; + } + + /** Implementation of pay-to-script {@link Address}. */ + export declare class P2SAddress extends Address { + } + + /** Implementation of pay-to-script-hash {@link Address}. */ + export declare class P2SHAddress extends Address { + } + + export declare class ErgoTree { + toHex(): HexString; + + bytes(): ByteArray; + + header(): number; + + version(): number; + + isConstantSegregation(): boolean; + + hasSize(): boolean; + + constants(): Value[]; + + template(): ByteArray; + + templateHex(): HexString; + + toString(): string; + } + + export declare class ErgoTreeObj { + static fromHex(value: HexString): ErgoTree; + } + + export declare class GroupElement { + toPointHex(): HexString; + } + + export declare class GroupElementObj { + static fromPointHex(value: HexString): GroupElement; + } + + export declare class SigmaProp { + } + + export declare class SigmaPropObj { + static fromPointHex(value: HexString): SigmaProp; + } + + export declare class AvlTree { + digest: HexString; + insertAllowed: Boolean; + updateAllowed: Boolean; + removeAllowed: Boolean; + keyLength: number; + valueLengthOpt: number | undefined; + } + + export declare class PreHeader { + /** Block version, to be increased on every soft and hardfork. */ + version: number; + /** Hex of id of parent block */ + parentId: HexString; + /** Block timestamp (in milliseconds since beginning of Unix Epoch) */ + timestamp: bigint; + /** Current difficulty in a compressed view. + * NOTE: actually it is unsigned integer */ + nBits: bigint; + /** Block height */ + height: number; + /** Miner public key (hex of EC Point). Should be used to collect block rewards. */ + minerPk: GroupElement; + /** Hex of miner votes bytes for changing system parameters. */ + votes: HexString; + } + + export declare class Header { + /** Hex representation of ModifierId of this Header */ + id: HexString; + /** Block version, to be increased on every soft and hardfork. */ + version: number; + /** Hex representation of ModifierId of the parent block */ + parentId: HexString; + /** Hex hash of ADProofs for transactions in a block */ + ADProofsRoot: HexString; + /** AvlTree of a state after block application */ + stateRoot: AvlTree; + /** Hex of root hash (for a Merkle tree) of transactions in a block. */ + transactionsRoot: HexString; + /** Block timestamp (in milliseconds since beginning of Unix Epoch) */ + timestamp: bigint; + /** Current difficulty in a compressed view. + * NOTE: actually it is unsigned Int */ + nBits: bigint; + /** Block height */ + height: number; + /** Hex of root hash of extension section */ + extensionRoot: HexString; + + /** Miner public key (hex of EC Point). Should be used to collect block rewards. + * Part of Autolykos solution. + */ + minerPk: GroupElement; + + /** One-time public key (hex of EC Point). Prevents revealing of miners secret. */ + powOnetimePk: GroupElement; + + /** Hex of nonce bytes */ + powNonce: HexString; + + /** Distance between pseudo-random number, corresponding to nonce `powNonce` and a secret, + * corresponding to `minerPk`. The lower `powDistance` is, the harder it was to find this solution. */ + powDistance: bigint; + + /** Miner votes for changing system parameters. */ + votes: HexString; + } + + export declare class BlockchainParameters { + storageFeeFactor: number; + minValuePerByte: number; + maxBlockSize: number; + tokenAccessCost: number; + inputCost: number; + dataInputCost: number; + outputCost: number; + maxBlockCost: number; + softForkStartingHeight: number | undefined; + softForkVotesCollected: number | undefined; + blockVersion: number; + } + + export declare class BlockchainStateContext { + sigmaLastHeaders: Header[]; + previousStateDigest: HexString; + sigmaPreHeader: PreHeader; + } + + export declare class Type { + name: string; + + toString(): string; + } + + export declare class TypeObj { + static Byte: Type; + static Short: Type; + static Int: Type; + static Long: Type; + static BigInt: Type; + static GroupElement: Type; + static SigmaProp: Type; + static Box: Type; + static AvlTree: Type; + static Context: Type; + static Header: Type; + static PreHeader: Type; + static SigmaDslBuilder: Type; + + static pairType(left: Type, right: Type): Type; + + static collType(elemType: Type): Type; + } + + export declare class Value { + data: T; + tpe: Type; + + toHex(): HexString; + } + + export declare class ValueObj { + static ofByte(value: number): Value; + + static ofShort(value: number): Value; + + static ofInt(value: number): Value; + + static ofLong(value: bigint): Value; + + static ofBigInt(value: bigint): Value; + + static ofGroupElement(pointHex: string): Value; + + static ofSigmaProp(pointHex: string): Value; + + static pairOf(left: Value, right: Value): Value<[R, L]>; + + static collOf(items: T[], elemType: Type): Value; + + static fromHex(hex: HexString): Value; + } + + export declare class SigmaCompiler { + compile( + namedConstants: SigmaCompilerNamedConstantsMap, + segregateConstants: boolean, + additionalHeaderFlags: number, + ergoScript: string + ): ErgoTree; + } - /** Configures this builder to use group elements (g, h, u, v) and secret x for a - * ProveDHTuple statement when building a new prover. - * - * ProveDHTuple is a statement consisting of 4 group elements (g, h, u, v) and - * requires the prover to prove knowledge of secret integer x such that. - * - * u = g^x - * and - * y = h^x - * - * @param g [[GroupElement]] instance defining g - * @param h [[GroupElement]] instance defining h - * @param u [[GroupElement]] instance defining u - * @param v [[GroupElement]] instance defining v - * @param x [[BigInteger]] instance defining x - * @see - * example - * @see - * implementation - */ - withDHTSecret(g: HexString, h: HexString, u: HexString, v: HexString, x: bigint): ProverBuilder; + export declare class SigmaCompilerObj { + static forMainnet(): SigmaCompiler; + + static forTestnet(): SigmaCompiler; + } + + /** Represents results for transaction reduction by {@link SigmaProver}. */ + export declare class ReducedTransaction { + /** Serialized bytes of this transaction in hex format. */ + toHex(): HexString; + } - /** This allows adding additional secret for use in proveDlog, when the secret is not - * part of the wallet. + export declare class ReducedTransactionObj { + /** Creates a {@link ReducedTransaction} from serialized bytes in hex format. */ + fromHex(hex: HexString): ReducedTransaction; + } + + /** Represents a prover for signing Ergo transactions and messages. * - * Multiple secrets can be added by calling this method multiple times. + * Equivalent of [[org.ergoplatform.sdk.SigmaProver]] available from JS. + */ + export declare class SigmaProver { + /** Returns the Pay-to-Public-Key (P2PK) address associated with the prover's public key. + * The returned address corresponds to the master secret derived from the mnemonic + * phrase configured in the [[ProverBuilder]]. + */ + getP2PKAddress(): HexString; + + /** Returns the prover's secret key. */ + getSecretKey(): bigint; + + /** Returns an array of EIP-3 addresses associated with the prover's secret keys. */ + getEip3Addresses(): HexString[]; + + /** Reduces the transaction to the reduced form, which is ready to be signed. + * @param stateCtx blockchain state context + * @param unsignedTx unsigned transaction to be reduced (created by Fleet builders) + * @param boxesToSpend boxes to be spent by the transaction + * @param dataInputs data inputs to be used by the transaction + * @param tokensToBurn tokens to be burned by the transaction + * @param baseCost base cost of the transaction + * @return reduced transaction + */ + reduce( + stateCtx: BlockchainStateContext, + unsignedTx: UnsignedTransaction, + boxesToSpend: EIP12UnsignedInput[], + dataInputs: Box[], + tokensToBurn: TokenAmount[], + baseCost: number): ReducedTransaction; + + /** Signs the reduced transaction. + * @param reducedTx reduced transaction to be signed + * @return signed transaction containting all the required proofs (signatures) + */ + signReduced(reducedTx: ReducedTransaction): SignedTransaction; + } + + /** Equivalent of [[sdk.ProverBuilder]] available from JS. * - * Multiple secrets are necessary for statements that need multiple proveDlogs, such - * as proveDlog(a) && proveDlog(b), where a and b are two group elements. + * @param parameters Blockchain parameters re-adjustable via miners voting and + * voting-related data. All of them are included into extension + * section of a first block of a voting epoch. + * @param network Network prefix to use for addresses. */ - withDLogSecret(x: bigint): ProverBuilder; - - /** Builds a new prover using provided configuration. */ - build(): SigmaProver; - } - - export declare class ProverBuilderObj { - static create(parameters: BlockchainParameters, network: number): ProverBuilder; - } + export declare class ProverBuilder { + /** Configure this builder to use the given seed when building a new prover. + * + * @param mnemonicPhrase secret seed phrase to be used in prover for generating proofs. + * @param mnemonicPass password to protect secret seed phrase. + */ + withMnemonic(mnemonicPhrase: HexString, mnemonicPass: HexString): ProverBuilder; + + /** Configure this builder to derive the new EIP-3 secret key with the given index. + * The derivation uses master key derived from the mnemonic configured using + * [[ErgoProverBuilder.withMnemonic]]. + * + * @param index last index in the EIP-3 derivation path. + */ + withEip3Secret(index: number): ProverBuilder; + + /** Configures this builder to use group elements (g, h, u, v) and secret x for a + * ProveDHTuple statement when building a new prover. + * + * ProveDHTuple is a statement consisting of 4 group elements (g, h, u, v) and + * requires the prover to prove knowledge of secret integer x such that. + * + * u = g^x + * and + * y = h^x + * + * @param g [[GroupElement]] instance defining g + * @param h [[GroupElement]] instance defining h + * @param u [[GroupElement]] instance defining u + * @param v [[GroupElement]] instance defining v + * @param x [[BigInteger]] instance defining x + * @see + * example + * @see + * implementation + */ + withDHTSecret(g: HexString, h: HexString, u: HexString, v: HexString, x: bigint): ProverBuilder; + + /** This allows adding additional secret for use in proveDlog, when the secret is not + * part of the wallet. + * + * Multiple secrets can be added by calling this method multiple times. + * + * Multiple secrets are necessary for statements that need multiple proveDlogs, such + * as proveDlog(a) && proveDlog(b), where a and b are two group elements. + */ + withDLogSecret(x: bigint): ProverBuilder; + + /** Builds a new prover using provided configuration. */ + build(): SigmaProver; + } + + export declare class ProverBuilderObj { + static create(parameters: BlockchainParameters, network: number): ProverBuilder; + } } From b745c5fd2257abc6d4317d9761394eb0ea0f3f4e Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Mon, 13 Nov 2023 23:48:05 +0100 Subject: [PATCH 04/25] new-sigma-js: more js tests and fixes --- .../main/scala/org/ergoplatform/js/Address.scala | 2 +- sigma-js/tests/js/Address.spec.js | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/data/js/src/main/scala/org/ergoplatform/js/Address.scala b/data/js/src/main/scala/org/ergoplatform/js/Address.scala index 85a90d4574..bdb520f153 100644 --- a/data/js/src/main/scala/org/ergoplatform/js/Address.scala +++ b/data/js/src/main/scala/org/ergoplatform/js/Address.scala @@ -59,7 +59,7 @@ abstract class Address extends js.Object { * @throws IllegalArgumentException if this instance is not P2S address */ def asP2S(): P2SAddress = { - require(isP2PK(), s"This instance $this is not P2PKAddress") + require(isP2S(), s"This instance $this is not P2SAddress") new P2SAddress(ergoAddress.asInstanceOf[org.ergoplatform.Pay2SAddress]) } diff --git a/sigma-js/tests/js/Address.spec.js b/sigma-js/tests/js/Address.spec.js index 1f6770f712..3cdfe2b001 100644 --- a/sigma-js/tests/js/Address.spec.js +++ b/sigma-js/tests/js/Address.spec.js @@ -17,6 +17,7 @@ describe("Address", () => { expect(addr.toString()).not.toBeUndefined(); expect(addr.toString()).toEqual(addrStr) + expect(addr.asP2PK()).not.toBeUndefined(); }); it("roundtrip for P2S", () => { @@ -26,6 +27,7 @@ describe("Address", () => { expect(addr.toString()).not.toBeUndefined(); expect(addr.toString()).toEqual(p2sStr) + expect(addr.asP2S()).not.toBeUndefined(); }); it("toSigmaPropOpt", () => { @@ -35,5 +37,17 @@ describe("Address", () => { expect(addr.toSigmaPropOpt()).not.toBeUndefined() }); + it("other properties", () => { + let addr = AddressObj.fromString(addrStr); + expect(addr.toErgoTree()).not.toBeUndefined() + expect(addr.asP2PK()).not.toBeUndefined() + expect(addr.asP2PK().isP2PK()).toEqual(true) + expect(addr.asP2PK().toErgoTree()).not.toBeUndefined() + expect(addr.asP2PK().toSigmaProp()).not.toBeUndefined() + expect(addr.asP2PK().toPropositionBytes()).not.toBeUndefined() + expect(addr.asP2PK().addressTypePrefix()).not.toBeUndefined() + expect(addr.asP2PK().getPublicKeyGE()).not.toBeUndefined() + }); + }); From 58e24d00c874aa715660aab9128acd3385cc052b Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 15 Nov 2023 00:26:11 +0100 Subject: [PATCH 05/25] new-sigma-js: exported js.ContractTemplate --- build.sbt | 1 + .../sdk/js/ContractTemplate.scala | 36 +++++++++++++++++++ sigma-js/tests/js/ContractTemplate.spec.js | 24 +++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala create mode 100644 sigma-js/tests/js/ContractTemplate.spec.js diff --git a/build.sbt b/build.sbt index 7abbedd07c..85c59512d6 100644 --- a/build.sbt +++ b/build.sbt @@ -298,6 +298,7 @@ lazy val sdk = crossProject(JVMPlatform, JSPlatform) commonDependenies2, testingDependencies2, scodecBitsDependency, + circeDependency, publish / skip := true ) .jvmSettings( diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala new file mode 100644 index 0000000000..3828ff6252 --- /dev/null +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala @@ -0,0 +1,36 @@ +package org.ergoplatform.sdk.js + +import org.ergoplatform.sdk + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExportTopLevel + +trait JsWrapper[T] extends js.Object { + def wrappedValue: T +} + +@JSExportTopLevel("ContractTemplate") +class ContractTemplate(override val wrappedValue: sdk.ContractTemplate) extends JsWrapper[sdk.ContractTemplate] { + /** @return JSON representation of this contract template pretty-printed to a string + * indentation of two spaces. + */ + def toJsonString: String = + sdk.ContractTemplate.jsonEncoder.encoder(wrappedValue).spaces2 +} + +@JSExportTopLevel("ContractTemplateObj") +object ContractTemplate { + /** Create a new contract template from a JSON string. + * + * @param json JSON string representing a contract template. + * @return a new contract template. + */ + def fromJsonString(json: String): ContractTemplate = { + io.circe.parser.parse(json) match { + case Left(err) => throw err + case Right(json) => + val ct = sdk.ContractTemplate.jsonEncoder.decoder(json.hcursor).right.get + new ContractTemplate(ct) + } + } +} \ No newline at end of file diff --git a/sigma-js/tests/js/ContractTemplate.spec.js b/sigma-js/tests/js/ContractTemplate.spec.js new file mode 100644 index 0000000000..a6a3193605 --- /dev/null +++ b/sigma-js/tests/js/ContractTemplate.spec.js @@ -0,0 +1,24 @@ +const { ContractTemplate, ContractTemplateObj } = require("sigmastate-js/main"); + +describe("Smoke tests for API exporting", () => { + it("Should export ContractTempate object", () => { + expect(ContractTemplate).not.toBeUndefined(); + }); +}); + +describe("ContractTemplate", () => { + let addrStr = "9iJd9drp1KR3R7HLi7YmQbB5sJ5HFKZoPb5MxGepamggJs5vDHm"; + let p2sStr = "JryiCXrZf5VDetH1PM7rKDX3q4sLF34AdErWJFqG87Hf5ikTDf636b35Nr7goWMdRUKA3ZPxdeqFNbQzBjhnDR9SUMYwDX1tdV8ZuGgXwQPaRVcB9" + + // it("roundtrip for P2PK", () => { + // let addr = AddressObj.fromString(addrStr); + // expect(addr.isP2PK()).toEqual(true) + // expect(addr.isP2S()).toEqual(false) + // + // expect(addr.toString()).not.toBeUndefined(); + // expect(addr.toString()).toEqual(addrStr) + // expect(addr.asP2PK()).not.toBeUndefined(); + // }); + +}); + From b26930c5e7aa58b6d76dda96ab56db59825f8638 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 15 Nov 2023 13:52:13 +0100 Subject: [PATCH 06/25] new-sigma-js: js.ContractTemplate: iso with Scala class, from/to Json string --- .../src/main/scala/sigma/js/JsWrapper.scala | 8 ++ core/js/src/main/scala/sigma/js/Type.scala | 8 +- .../main/scala/sigma/ast/js/ErgoTree.scala | 8 ++ .../js/src/main/scala/sigma/ast/js/Expr.scala | 15 +++ .../src/main/scala/sigma/ast/ErgoTree.scala | 3 + .../sdk/js/ContractTemplate.scala | 107 ++++++++++++++++-- .../scala/org/ergoplatform/sdk/js/Isos.scala | 15 ++- .../ergoplatform/sdk/ContractTemplate.scala | 10 +- .../sdk/ContractTemplateSpecification.scala | 13 ++- sigma-js/sigmastate-js.d.ts | 36 ++++++ sigma-js/tests/js/ContractTemplate.spec.js | 54 +++++++-- 11 files changed, 244 insertions(+), 33 deletions(-) create mode 100644 core/js/src/main/scala/sigma/js/JsWrapper.scala create mode 100644 data/js/src/main/scala/sigma/ast/js/Expr.scala diff --git a/core/js/src/main/scala/sigma/js/JsWrapper.scala b/core/js/src/main/scala/sigma/js/JsWrapper.scala new file mode 100644 index 0000000000..e62cb70c11 --- /dev/null +++ b/core/js/src/main/scala/sigma/js/JsWrapper.scala @@ -0,0 +1,8 @@ +package sigma.js + +import scala.scalajs.js + +/** Base trait for all JS wrappers over some Scala type T. */ +trait JsWrapper[T] extends js.Object { + def wrappedValue: T +} diff --git a/core/js/src/main/scala/sigma/js/Type.scala b/core/js/src/main/scala/sigma/js/Type.scala index 67e24736b9..6cfbf842a2 100644 --- a/core/js/src/main/scala/sigma/js/Type.scala +++ b/core/js/src/main/scala/sigma/js/Type.scala @@ -1,6 +1,7 @@ package sigma.js -import sigma.data.RType +import sigma.Evaluation +import sigma.data.{Iso, RType} import scala.scalajs.js import scala.scalajs.js.annotation.JSExportTopLevel @@ -75,4 +76,9 @@ object Type extends js.Object { def collType(elemType: Type): Type = { new Type(sigma.collRType(elemType.rtype)) } + + implicit val isoToSType: Iso[Type, sigma.ast.SType] = new Iso[Type, sigma.ast.SType] { + override def to(x: Type): sigma.ast.SType = Evaluation.rtypeToSType(x.rtype) + override def from(x: sigma.ast.SType): Type = new Type(Evaluation.stypeToRType(x)) + } } \ No newline at end of file diff --git a/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala b/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala index 22f189fba2..6553167f0e 100644 --- a/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala +++ b/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala @@ -10,13 +10,21 @@ import scala.scalajs.js.annotation.JSExportTopLevel /** An exported JavaScript class wrapping the Scala `ErgoTree` type. */ @JSExportTopLevel("ErgoTree") class ErgoTree(val tree: ast.ErgoTree) extends js.Object { + /** Root of the contract which is a valid expression of `SigmaProp` type. Must have constants + * segregated into `constTypes` and optionally `constValues` + */ + val root: Expr = new Expr(tree.root.toOption.get) + /** The first byte of serialized byte array which determines interpretation of the rest of the array. */ def header(): Byte = tree.header + /** Version of this tree (== BlockVersion - 1). */ def version(): Byte = tree.version + /** @return true, if constant segregation bit is set in the header. */ def isConstantSegregation(): Boolean = tree.isConstantSegregation + /** @return true, if size bit is set in the header. */ def hasSize(): Boolean = tree.hasSize /** Serializes the ErgoTree instance to an array of bytes. */ diff --git a/data/js/src/main/scala/sigma/ast/js/Expr.scala b/data/js/src/main/scala/sigma/ast/js/Expr.scala new file mode 100644 index 0000000000..0757a83f02 --- /dev/null +++ b/data/js/src/main/scala/sigma/ast/js/Expr.scala @@ -0,0 +1,15 @@ +package sigma.ast.js + +import sigma.ast.syntax.SValue +import sigma.js.JsWrapper + +import scala.scalajs.js.annotation.JSExportTopLevel + +/** An exported JavaScript class wrapping the Scala `Value`. */ +@JSExportTopLevel("Expr") +class Expr(override val wrappedValue: SValue) extends JsWrapper[SValue]{ +} + +@JSExportTopLevel("ExprObj") +object Expr { +} diff --git a/data/shared/src/main/scala/sigma/ast/ErgoTree.scala b/data/shared/src/main/scala/sigma/ast/ErgoTree.scala index 58e10b9399..b5b3d390e8 100644 --- a/data/shared/src/main/scala/sigma/ast/ErgoTree.scala +++ b/data/shared/src/main/scala/sigma/ast/ErgoTree.scala @@ -114,12 +114,15 @@ case class ErgoTree private[sigma]( @deprecated("Use toProposition instead", "v2.1") lazy val proposition: SigmaPropValue = toProposition(isConstantSegregation) + /** Version of this tree (== BlockVersion - 1). */ @inline final def version: Byte = ErgoTree.getVersion(header) @inline final def isRightParsed: Boolean = root.isRight + /** @return true, if constant segregation bit is set in the header. */ @inline final def isConstantSegregation: Boolean = ErgoTree.isConstantSegregation(header) + /** @return true, if size bit is set in the header. */ @inline final def hasSize: Boolean = ErgoTree.hasSize(header) private[sigma] var _bytes: Array[Byte] = propositionBytes diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala index 3828ff6252..598665bc98 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala @@ -1,25 +1,83 @@ package org.ergoplatform.sdk.js import org.ergoplatform.sdk +import sigma.ast.Constant +import sigma.ast.js.Expr +import sigma.ast.syntax.SigmaPropValue +import sigma.data.Iso +import sigma.js.Type import scala.scalajs.js +import scala.scalajs.js.UndefOr import scala.scalajs.js.annotation.JSExportTopLevel -trait JsWrapper[T] extends js.Object { - def wrappedValue: T +/** + * Represents a ContractTemplate parameter. + */ +@JSExportTopLevel("Parameter") +class Parameter( + /** User readable parameter name (string bytes in UTF-8 encoding) */ + val name: String, + /** User readable parameter description (string bytes in UTF-8 encoding) */ + val description: String, + /** Index in the ErgoTree.constants array */ + val constantIndex: Int +) extends js.Object + +@JSExportTopLevel("ParameterObj") +object Parameter extends js.Object { + implicit val isoToSdk: sigma.data.Iso[Parameter, sdk.Parameter] = + new sigma.data.Iso[Parameter, sdk.Parameter] { + override def to(p: Parameter): sdk.Parameter = + sdk.Parameter(p.name, p.description, p.constantIndex) + + override def from(p: sdk.Parameter): Parameter = + new Parameter(p.name, p.description, p.constantIndex) + } } + +/** Represents a reusable ContractTemplate with support to generate ErgoTree based on provided parameters. + * + * @param treeVersion the optional version of ErgoTree which should be used. If this value is not provided here then + * it must be provided while generating the `ErgoTree` by calling `applyTemplate`. + * @param name user readable name (non-empty string bytes in UTF-8 encoding) + * @param description user readable contract description (string bytes in UTF-8 encoding) + * @param constTypes list denoting the type of ConstantPlaceholders in the expressionTree + * @param constValues optional list of optional default values for the ConstantPlaceholders in the expressionTree. + * If an entry in the sequence is None, it must have a corresponding entry in parameters and its + * value must be provided while generating the `ErgoTree` by calling `applyTemplate`. If all the + * entries are None, the whole `constValues` field can be set to None. + * @param parameters typed template parameters of the contract template. It must have an entry for each + * `ConstantPlaceholder` which has a `None` in the `constValues` field. Other fields which do have + * a value defined in `constValues` can also be allowed to be optionally overridden by accepting + * it in `parameters`. + * @param expressionTree root of the contract which is a valid expression of `SigmaProp` type. Must have constants + * segregated into `constTypes` and optionally `constValues` + */ @JSExportTopLevel("ContractTemplate") -class ContractTemplate(override val wrappedValue: sdk.ContractTemplate) extends JsWrapper[sdk.ContractTemplate] { +class ContractTemplate( + val treeVersion: UndefOr[Byte], + val name: String, + val description: String, + val constTypes: js.Array[sigma.js.Type], + val constValues: UndefOr[js.Array[UndefOr[sigma.js.Value]]], + val parameters: js.Array[Parameter], + val expressionTree: Expr +) extends js.Object { /** @return JSON representation of this contract template pretty-printed to a string * indentation of two spaces. */ - def toJsonString: String = - sdk.ContractTemplate.jsonEncoder.encoder(wrappedValue).spaces2 + def toJsonString(): String = { + val template = ContractTemplate.isoToSdk.to(this) + template.toJsonString + } } @JSExportTopLevel("ContractTemplateObj") -object ContractTemplate { +object ContractTemplate extends js.Object { + import sigma.js.Isos._ + /** Create a new contract template from a JSON string. * * @param json JSON string representing a contract template. @@ -29,8 +87,41 @@ object ContractTemplate { io.circe.parser.parse(json) match { case Left(err) => throw err case Right(json) => - val ct = sdk.ContractTemplate.jsonEncoder.decoder(json.hcursor).right.get - new ContractTemplate(ct) + val ct = sdk.ContractTemplate.jsonEncoder.decoder(json.hcursor).toOption.get + ContractTemplate.isoToSdk.from(ct) } } + + private val constsIso = isoUndefOr(isoArrayToIndexed(isoUndefOr(sigma.ast.js.isoValueToConstant))) + + implicit val isoToSdk: sigma.data.Iso[ContractTemplate, sdk.ContractTemplate] = + new sigma.data.Iso[ContractTemplate, sdk.ContractTemplate] { + override def to(ct: ContractTemplate): sdk.ContractTemplate = { + new sdk.ContractTemplate( + isoUndefOr(Iso.identityIso[Byte]).to(ct.treeVersion), + ct.name, + ct.description, + isoArrayToIndexed(Type.isoToSType).to(ct.constTypes), + constsIso.to(ct.constValues).map(_.map(costOpt => costOpt.map(_.value))), + isoArrayToIndexed(Parameter.isoToSdk).to(ct.parameters), + ct.expressionTree.wrappedValue.asInstanceOf[SigmaPropValue] + ) + } + + override def from(ct: sdk.ContractTemplate): ContractTemplate = { + // optionally, match each value with its type + val constants = ct.constValues.map(values => values.zip(ct.constTypes).map { + case (value, tpe) => value.map(v => Constant(v, tpe)) + }) + new ContractTemplate( + isoUndefOr(Iso.identityIso[Byte]).from(ct.treeVersion), + ct.name, + ct.description, + isoArrayToIndexed(Type.isoToSType).from(ct.constTypes), + constsIso.from(constants), + isoArrayToIndexed(Parameter.isoToSdk).from(ct.parameters), + new Expr(ct.expressionTree) + ) + } + } } \ No newline at end of file diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala index 54e0a6c602..a0aa528891 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala @@ -9,13 +9,14 @@ import scorex.crypto.authds.ADKey import scorex.util.ModifierId import scorex.util.encode.Base16 import sigma.Extensions.CollBytesOps -import sigma.ast.SType import sigma.ast.syntax.GroupElementConstant +import sigma.ast.{Constant, GroupElementConstant, SType} import sigma.data.Iso.{isoStringToArray, isoStringToColl} -import sigma.data.{CBigInt, CGroupElement, Digest32Coll, Iso} -import sigma.js.{AvlTree, GroupElement, Type, Value} -import sigma.{Coll, Colls, Evaluation} -import sigma.ast.{Constant, GroupElementConstant} +import sigma.data.{CBigInt, CGroupElement, Digest32Coll, Digest32CollRType, Iso} +import sigma.interpreter.{ContextExtension, ProverResult} +import sigma.js.{AvlTree, GroupElement} +import sigma.serialization.{ErgoTreeSerializer, ValueSerializer} +import sigma.{Coll, Colls} import sigmastate.eval.{CHeader, CPreHeader} import sigmastate.fleetSdkCommon.distEsmTypesBoxesMod.Box import sigmastate.fleetSdkCommon.distEsmTypesCommonMod.HexString @@ -23,9 +24,7 @@ import sigmastate.fleetSdkCommon.distEsmTypesRegistersMod.NonMandatoryRegisters import sigmastate.fleetSdkCommon.distEsmTypesTokenMod.TokenAmount import sigmastate.fleetSdkCommon.distEsmTypesTransactionsMod.{SignedTransaction, UnsignedTransaction} import sigmastate.fleetSdkCommon.{distEsmTypesBoxesMod => boxesMod, distEsmTypesCommonMod => commonMod, distEsmTypesContextExtensionMod => contextExtensionMod, distEsmTypesInputsMod => inputsMod, distEsmTypesProverResultMod => proverResultMod, distEsmTypesRegistersMod => registersMod, distEsmTypesTokenMod => tokenMod} -import sigma.interpreter.{ContextExtension, ProverResult} -import sigma.serialization.{ErgoTreeSerializer, ValueSerializer} -import sigma.data.Digest32CollRType + import java.math.BigInteger import scala.collection.immutable.ListMap import scala.scalajs.js diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index d0ab8f5e5e..4862a2a01f 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -1,6 +1,5 @@ package org.ergoplatform.sdk -import cats.syntax.either._ import debox.cfor import io.circe._ import io.circe.syntax.EncoderOps @@ -16,7 +15,6 @@ import sigma.ast.syntax.SigmaPropValue import sigma.serialization._ import java.util.Objects import scala.collection.mutable -import scala.language.implicitConversions /** * Represents a ContractTemplate parameter. @@ -179,6 +177,14 @@ case class ContractTemplate( ) } + /** @return Json representation of this contract template */ + def toJson: Json = ContractTemplate.jsonEncoder.encoder(this) + + /** @return JSON representation of this contract template pretty-printed to a string + * indentation of two spaces. + */ + def toJsonString: String = toJson.spaces2 + override def hashCode(): Int = Objects.hash(treeVersion, name, description, constTypes, constValues, parameters, expressionTree) override def equals(obj: Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala index f81db87371..64eabf4615 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala @@ -7,7 +7,7 @@ import sigma.ast._ import sigmastate._ import sigmastate.helpers.NegativeTesting import sigma.serialization.{SerializationSpecification, SigmaSerializer} -import sigma.ContractsTestkit +import sigma.{ContractsTestkit, VersionContext} import sigma.ast.syntax.SigmaPropValue import sigma.ast.{SByte, SInt, SType} import sigma.data.CBigInt @@ -234,8 +234,15 @@ class ContractTemplateSpecification extends SerializationSpecification ) ) - templates.indices.foreach(i => - templates(i).applyTemplate(Some(ergoTreeVersionInTests), templateValues(i)) shouldEqual expectedErgoTree(i) + templates.indices.foreach(i => { + val template = templates(i) + val applied = template.applyTemplate(Some(ergoTreeVersionInTests), templateValues(i)) + applied shouldEqual expectedErgoTree(i) +// if (ergoTreeVersionInTests == VersionContext.JitActivationVersion) { +// println("Template: " + template.toJsonString) +// println("Applied ErgoTree: " + applied.bytesHex) +// } + } ) } diff --git a/sigma-js/sigmastate-js.d.ts b/sigma-js/sigmastate-js.d.ts index a6699136f6..798c537858 100644 --- a/sigma-js/sigmastate-js.d.ts +++ b/sigma-js/sigmastate-js.d.ts @@ -437,4 +437,40 @@ declare module "sigmastate-js/main" { export declare class ProverBuilderObj { static create(parameters: BlockchainParameters, network: number): ProverBuilder; } + + + /** + * Represents a reusable ContractTemplate with support to generate ErgoTree based on provided parameters. + * + * @param treeVersion the optional version of ErgoTree which should be used. If this value is not provided here then + * it must be provided while generating the `ErgoTree` by calling `applyTemplate`. + * @param name user readable name (non-empty string bytes in UTF-8 encoding) + * @param description user readable contract description (string bytes in UTF-8 encoding) + * @param constTypes list denoting the type of ConstantPlaceholders in the expressionTree + * @param constValues optional list of optional default values for the ConstantPlaceholders in the expressionTree. + * If an entry in the sequence is None, it must have a corresponding entry in parameters and its + * value must be provided while generating the `ErgoTree` by calling `applyTemplate`. If all the + * entries are None, the whole `constValues` field can be set to None. + * @param parameters typed template parameters of the contract template. It must have an entry for each + * `ConstantPlaceholder` which has a `None` in the `constValues` field. Other fields which do have + * a value defined in `constValues` can also be allowed to be optionally overridden by accepting + * it in `parameters`. + * @param expressionTree root of the contract which is a valid expression of `SigmaProp` type. Must have constants + * segregated into `constTypes` and optionally `constValues` + */ + export declare class ContractTemplate { + /** @return JSON representation of this contract template pretty-printed to a string + * indentation of two spaces. + */ + toJsonString(): String + } + + export declare class ContractTemplateObj { + /** Create a new contract template from a JSON string. + * + * @param json JSON string representing a contract template. + * @return a new contract template. + */ + fromJsonString(json: String): ContractTemplate + } } diff --git a/sigma-js/tests/js/ContractTemplate.spec.js b/sigma-js/tests/js/ContractTemplate.spec.js index a6a3193605..54e9550966 100644 --- a/sigma-js/tests/js/ContractTemplate.spec.js +++ b/sigma-js/tests/js/ContractTemplate.spec.js @@ -7,18 +7,50 @@ describe("Smoke tests for API exporting", () => { }); describe("ContractTemplate", () => { - let addrStr = "9iJd9drp1KR3R7HLi7YmQbB5sJ5HFKZoPb5MxGepamggJs5vDHm"; - let p2sStr = "JryiCXrZf5VDetH1PM7rKDX3q4sLF34AdErWJFqG87Hf5ikTDf636b35Nr7goWMdRUKA3ZPxdeqFNbQzBjhnDR9SUMYwDX1tdV8ZuGgXwQPaRVcB9" + let templateJsonStr = "{\n" + + " \"treeVersion\" : null,\n" + + " \"name\" : \"TestContractTemplate\",\n" + + " \"description\" : \"TestContractTemplateDescription\",\n" + + " \"constTypes\" : [\n" + + " \"02\",\n" + + " \"02\",\n" + + " \"02\"\n" + + " ],\n" + + " \"constValues\" : [\n" + + " 10,\n" + + " 20,\n" + + " 30\n" + + " ],\n" + + " \"parameters\" : [\n" + + " {\n" + + " \"name\" : \"p1\",\n" + + " \"description\" : \"p1_description\",\n" + + " \"constantIndex\" : 0\n" + + " },\n" + + " {\n" + + " \"name\" : \"p2\",\n" + + " \"description\" : \"p2_description\",\n" + + " \"constantIndex\" : 1\n" + + " },\n" + + " {\n" + + " \"name\" : \"p3\",\n" + + " \"description\" : \"p3_description\",\n" + + " \"constantIndex\" : 2\n" + + " }\n" + + " ],\n" + + " \"expressionTree\" : \"d1939a730073017302\"\n" + + "}"; - // it("roundtrip for P2PK", () => { - // let addr = AddressObj.fromString(addrStr); - // expect(addr.isP2PK()).toEqual(true) - // expect(addr.isP2S()).toEqual(false) - // - // expect(addr.toString()).not.toBeUndefined(); - // expect(addr.toString()).toEqual(addrStr) - // expect(addr.asP2PK()).not.toBeUndefined(); - // }); + it("roundtrip for P2PK", () => { + let template = ContractTemplateObj.fromJsonString(templateJsonStr); + expect(template).not.toBeUndefined(); + expect(template.toJsonString()).toEqual(templateJsonStr); + // expect(addr.isP2PK()).toEqual(true) + // + // expect(addr.isP2S()).toEqual(false) + // expect(addr.toString()).toEqual(addrStr) + // expect(addr.asP2PK()).not.toBeUndefined(); + }); }); From 4438109ca5964ea3a40d168f8b6afa4a8abf8614 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 15 Nov 2023 14:10:03 +0100 Subject: [PATCH 07/25] new-sigma-js: fix Scala 2.11 compilation --- .../src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index 4862a2a01f..5e491630bd 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -1,5 +1,6 @@ package org.ergoplatform.sdk +import cats.syntax.either._ // required for Scala 2.11 import debox.cfor import io.circe._ import io.circe.syntax.EncoderOps From 6d774a34118b6fac4e70b58c29343afb1b261460 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 15 Nov 2023 18:41:51 +0100 Subject: [PATCH 08/25] new-sigma-js: implemented js.ContractTemplate.applyTemplate method --- .../js/src/main/scala/sigma/ast/js/Expr.scala | 2 +- .../sdk/js/ContractTemplate.scala | 32 +++++- .../org/ergoplatform/sdk/js/IsosSpec.scala | 43 +++----- .../ergoplatform/sdk/js/IsosSpecBase.scala | 32 ++++++ .../sdk/ContractTemplateSpecification.scala | 4 - sigma-js/sigmastate-js.d.ts | 88 ++++++++++++--- sigma-js/tests/js/ContractTemplate.spec.js | 100 ++++++++++-------- 7 files changed, 201 insertions(+), 100 deletions(-) create mode 100644 sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpecBase.scala diff --git a/data/js/src/main/scala/sigma/ast/js/Expr.scala b/data/js/src/main/scala/sigma/ast/js/Expr.scala index 0757a83f02..960aed45d0 100644 --- a/data/js/src/main/scala/sigma/ast/js/Expr.scala +++ b/data/js/src/main/scala/sigma/ast/js/Expr.scala @@ -5,7 +5,7 @@ import sigma.js.JsWrapper import scala.scalajs.js.annotation.JSExportTopLevel -/** An exported JavaScript class wrapping the Scala `Value`. */ +/** An exported JavaScript class wrapping the Scala [[sigma.ast.Value]]. */ @JSExportTopLevel("Expr") class Expr(override val wrappedValue: SValue) extends JsWrapper[SValue]{ } diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala index 598665bc98..392cd32175 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala @@ -1,11 +1,14 @@ package org.ergoplatform.sdk.js import org.ergoplatform.sdk +import org.ergoplatform.sdk.js.ContractTemplate.isoToSdk +import org.scalablytyped.runtime.StringDictionary import sigma.ast.Constant -import sigma.ast.js.Expr +import sigma.ast.js.{ErgoTree, Expr} import sigma.ast.syntax.SigmaPropValue import sigma.data.Iso import sigma.js.Type +import sigma.js.Isos._ import scala.scalajs.js import scala.scalajs.js.UndefOr @@ -65,6 +68,32 @@ class ContractTemplate( val parameters: js.Array[Parameter], val expressionTree: Expr ) extends js.Object { + /** + * Generate the ErgoTree from the template by providing the values for parameters. + * + * @param version the version of the `ErgoTree` to use. Must be provided if the `treeVersion` was not provided in the + * template. + * @param paramValues the name-value map for the parameters accepted by the `ContractTemplate`. Must contain an entry + * for each parameter for which no default value was provided in the template. Optionally, can also + * provide values to override for parameters which do have a default value defined in the template. + * The type of the provided value must match with the corresponding entry in the `constTypes` + * provided in the template. + * @return `ErgoTree` generated by replacing the template parameters with the value provided in `paramValues`. + */ + def applyTemplate( + version: UndefOr[Byte], + paramValues: StringDictionary[sigma.js.Value]): sigma.ast.js.ErgoTree = { + val params = StringDictionary + .wrapStringDictionary(paramValues) + .view.mapValues(v => sigma.ast.js.isoValueToConstant.to(v)) + .toMap + val tree = isoToSdk.to(this).applyTemplate( + version = isoUndefOr(Iso.identityIso[Byte]).to(version), + paramValues = params + ) + new ErgoTree(tree) + } + /** @return JSON representation of this contract template pretty-printed to a string * indentation of two spaces. */ @@ -76,7 +105,6 @@ class ContractTemplate( @JSExportTopLevel("ContractTemplateObj") object ContractTemplate extends js.Object { - import sigma.js.Isos._ /** Create a new contract template from a JSON string. * diff --git a/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala b/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala index ddd0655dea..5b7f0515cc 100644 --- a/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala +++ b/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala @@ -2,42 +2,18 @@ package org.ergoplatform.sdk.js import org.ergoplatform.ErgoBox.{AdditionalRegisters, BoxId, TokenId} import org.ergoplatform._ -import org.ergoplatform.sdk.wallet.protocol.context.{BlockchainStateContext, CBlockchainStateContext} import org.ergoplatform.sdk.ExtendedInputBox -import org.scalacheck.{Arbitrary, Gen} -import org.scalatest.matchers.should.Matchers -import org.scalatest.propspec.AnyPropSpec -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import sigma.ast.SType +import org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext +import org.scalacheck.Arbitrary +import sigma.ast.{Constant, SType} import sigma.data.Iso -import sigma.js.AvlTree -import sigma.{Coll, Colls, GroupElement} -import sigma.ast.Constant import sigma.interpreter.{ContextExtension, ProverResult} -import sigma.serialization.generators.ObjectGenerators +import sigma.js.AvlTree +import sigma.{Coll, GroupElement} import scala.scalajs.js -class IsosSpec extends AnyPropSpec with Matchers with ObjectGenerators with ScalaCheckPropertyChecks{ - - lazy val extendedInputBoxGen: Gen[ExtendedInputBox] = for { - box <- ergoBoxGen - extension <- contextExtensionGen - } yield ExtendedInputBox(box, extension) - - lazy val blockchainStateContextGen: Gen[BlockchainStateContext] = for { - stateRoot <- avlTreeGen - headers <- headersGen(stateRoot) - preHeader <- preHeaderGen(headers.headOption.map(_.id).getOrElse(modifierIdBytesGen.sample.get)) - } yield CBlockchainStateContext( - sigmaLastHeaders = Colls.fromItems(headers:_*), - previousStateDigest = stateRoot.digest, - sigmaPreHeader = preHeader - ) - - def roundtrip[A,B](iso: Iso[A,B])(b: B): Unit = { - iso.to(iso.from(b)) shouldBe b - } +class IsosSpec extends IsosSpecBase with sdk.generators.ObjectGenerators { override implicit val generatorDrivenConfig: PropertyCheckConfiguration = PropertyCheckConfiguration(minSuccessful = 30) @@ -197,4 +173,11 @@ class IsosSpec extends AnyPropSpec with Matchers with ObjectGenerators with Sca roundtrip(Isos.isoSignedTransaction)(tx) } } + + property("Iso.isoContractTemplate") { + forAll(contractTemplateGen) { (tx: sdk.ContractTemplate) => + roundtrip(ContractTemplate.isoToSdk)(tx) + } + } + } diff --git a/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpecBase.scala b/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpecBase.scala new file mode 100644 index 0000000000..ca15ed6853 --- /dev/null +++ b/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpecBase.scala @@ -0,0 +1,32 @@ +package org.ergoplatform.sdk.js + +import org.ergoplatform.sdk.ExtendedInputBox +import org.ergoplatform.sdk.wallet.protocol.context.{BlockchainStateContext, CBlockchainStateContext} +import org.scalacheck.Gen +import org.scalatest.matchers.should.Matchers +import org.scalatest.propspec.AnyPropSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import sigma.Colls +import sigma.data.Iso +import sigma.serialization.generators.ObjectGenerators + +class IsosSpecBase extends AnyPropSpec with Matchers with ObjectGenerators with ScalaCheckPropertyChecks { + lazy val extendedInputBoxGen: Gen[ExtendedInputBox] = for { + box <- ergoBoxGen + extension <- contextExtensionGen + } yield ExtendedInputBox(box, extension) + + lazy val blockchainStateContextGen: Gen[BlockchainStateContext] = for { + stateRoot <- avlTreeGen + headers <- headersGen(stateRoot) + preHeader <- preHeaderGen(headers.headOption.map(_.id).getOrElse(modifierIdBytesGen.sample.get)) + } yield CBlockchainStateContext( + sigmaLastHeaders = Colls.fromItems(headers: _*), + previousStateDigest = stateRoot.digest, + sigmaPreHeader = preHeader + ) + + def roundtrip[A, B](iso: Iso[A, B])(b: B): Unit = { + iso.to(iso.from(b)) shouldBe b + } +} diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala index 64eabf4615..b7b1a6c14b 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala @@ -238,10 +238,6 @@ class ContractTemplateSpecification extends SerializationSpecification val template = templates(i) val applied = template.applyTemplate(Some(ergoTreeVersionInTests), templateValues(i)) applied shouldEqual expectedErgoTree(i) -// if (ergoTreeVersionInTests == VersionContext.JitActivationVersion) { -// println("Template: " + template.toJsonString) -// println("Applied ErgoTree: " + applied.bytesHex) -// } } ) } diff --git a/sigma-js/sigmastate-js.d.ts b/sigma-js/sigmastate-js.d.ts index 798c537858..3f9859e57a 100644 --- a/sigma-js/sigmastate-js.d.ts +++ b/sigma-js/sigmastate-js.d.ts @@ -438,31 +438,85 @@ declare module "sigmastate-js/main" { static create(parameters: BlockchainParameters, network: number): ProverBuilder; } + /** + * Represents a ContractTemplate parameter. + */ + export declare class Parameter { + /** User readable parameter name (string bytes in UTF-8 encoding) */ + name: String + /** User readable parameter description (string bytes in UTF-8 encoding) */ + description: String + /** Index in the ErgoTree.constants array */ + constantIndex: number + } + + /** JavaScript class wrapping the Scala [[sigma.ast.Value]]. */ + export declare class Expr { + } /** - * Represents a reusable ContractTemplate with support to generate ErgoTree based on provided parameters. - * - * @param treeVersion the optional version of ErgoTree which should be used. If this value is not provided here then - * it must be provided while generating the `ErgoTree` by calling `applyTemplate`. - * @param name user readable name (non-empty string bytes in UTF-8 encoding) - * @param description user readable contract description (string bytes in UTF-8 encoding) - * @param constTypes list denoting the type of ConstantPlaceholders in the expressionTree - * @param constValues optional list of optional default values for the ConstantPlaceholders in the expressionTree. - * If an entry in the sequence is None, it must have a corresponding entry in parameters and its - * value must be provided while generating the `ErgoTree` by calling `applyTemplate`. If all the - * entries are None, the whole `constValues` field can be set to None. - * @param parameters typed template parameters of the contract template. It must have an entry for each - * `ConstantPlaceholder` which has a `None` in the `constValues` field. Other fields which do have - * a value defined in `constValues` can also be allowed to be optionally overridden by accepting - * it in `parameters`. - * @param expressionTree root of the contract which is a valid expression of `SigmaProp` type. Must have constants - * segregated into `constTypes` and optionally `constValues` + * Represents a reusable ContractTemplate with support to generate ErgoTree based on + * provided parameters. */ export declare class ContractTemplate { + /** + * The optional version of ErgoTree which should be used. If this value is not + * provided here then it must be provided while generating the `ErgoTree` by + * calling `applyTemplate`. + */ + treeVersion: number | undefined; + + /** User readable name (non-empty string bytes in UTF-8 encoding). */ + name: string; + + /** User readable contract description (string bytes in UTF-8 encoding). */ + description: string; + + /** List denoting the type of ConstantPlaceholders in the expressionTree. */ + constTypes: Type[]; + + /** + * Optional list of optional default values for the ConstantPlaceholders in the + * expressionTree. If an entry in the sequence is None, it must have a + * corresponding entry in parameters and its value must be provided while + * generating the `ErgoTree` by calling `applyTemplate`. If all the entries are + * None, the whole `constValues` field can be set to None. + */ + constValues: (Value | undefined)[] | undefined; + + /** + * Typed template parameters of the contract template. It must have an entry for + * each `ConstantPlaceholder` which has a `None` in the `constValues` field. Other + * fields which do have a value defined in `constValues` can also be allowed to be + * optionally overridden by accepting it in `parameters`. + */ + parameters: Parameter[]; + + /** Root of the contract which is a valid expression of `SigmaProp` type. Must + * have constants segregated into `constTypes` and optionally `constValues` + */ + expressionTree: Expr + /** @return JSON representation of this contract template pretty-printed to a string * indentation of two spaces. */ toJsonString(): String + + /** + * Generate the ErgoTree from the template by providing the values for parameters. + * + * @param version the version of the `ErgoTree` to use. Must be provided if the `treeVersion` was not provided in the + * template. + * @param paramValues the name-value map for the parameters accepted by the `ContractTemplate`. Must contain an entry + * for each parameter for which no default value was provided in the template. Optionally, can also + * provide values to override for parameters which do have a default value defined in the template. + * The type of the provided value must match with the corresponding entry in the `constTypes` + * provided in the template. + * @return `ErgoTree` generated by replacing the template parameters with the value provided in `paramValues`. + */ + applyTemplate( + version: number | undefined, + paramValues: SigmaCompilerNamedConstantsMap): ErgoTree } export declare class ContractTemplateObj { diff --git a/sigma-js/tests/js/ContractTemplate.spec.js b/sigma-js/tests/js/ContractTemplate.spec.js index 54e9550966..4130aae4b1 100644 --- a/sigma-js/tests/js/ContractTemplate.spec.js +++ b/sigma-js/tests/js/ContractTemplate.spec.js @@ -1,56 +1,64 @@ -const { ContractTemplate, ContractTemplateObj } = require("sigmastate-js/main"); +const {ContractTemplate, ContractTemplateObj, ValueObj} = require("sigmastate-js/main"); describe("Smoke tests for API exporting", () => { - it("Should export ContractTempate object", () => { - expect(ContractTemplate).not.toBeUndefined(); - }); + it("Should export ContractTempate object", () => { + expect(ContractTemplate).not.toBeUndefined(); + }); }); describe("ContractTemplate", () => { - let templateJsonStr = "{\n" + - " \"treeVersion\" : null,\n" + - " \"name\" : \"TestContractTemplate\",\n" + - " \"description\" : \"TestContractTemplateDescription\",\n" + - " \"constTypes\" : [\n" + - " \"02\",\n" + - " \"02\",\n" + - " \"02\"\n" + - " ],\n" + - " \"constValues\" : [\n" + - " 10,\n" + - " 20,\n" + - " 30\n" + - " ],\n" + - " \"parameters\" : [\n" + - " {\n" + - " \"name\" : \"p1\",\n" + - " \"description\" : \"p1_description\",\n" + - " \"constantIndex\" : 0\n" + - " },\n" + - " {\n" + - " \"name\" : \"p2\",\n" + - " \"description\" : \"p2_description\",\n" + - " \"constantIndex\" : 1\n" + - " },\n" + - " {\n" + - " \"name\" : \"p3\",\n" + - " \"description\" : \"p3_description\",\n" + - " \"constantIndex\" : 2\n" + - " }\n" + - " ],\n" + - " \"expressionTree\" : \"d1939a730073017302\"\n" + - "}"; + let templateJsonStr = "{\n" + + " \"treeVersion\" : null,\n" + + " \"name\" : \"TestContractTemplate\",\n" + + " \"description\" : \"TestContractTemplateDescription\",\n" + + " \"constTypes\" : [\n" + + " \"02\",\n" + + " \"02\",\n" + + " \"02\"\n" + + " ],\n" + + " \"constValues\" : [\n" + + " 10,\n" + + " 20,\n" + + " 30\n" + + " ],\n" + + " \"parameters\" : [\n" + + " {\n" + + " \"name\" : \"p1\",\n" + + " \"description\" : \"p1_description\",\n" + + " \"constantIndex\" : 0\n" + + " },\n" + + " {\n" + + " \"name\" : \"p2\",\n" + + " \"description\" : \"p2_description\",\n" + + " \"constantIndex\" : 1\n" + + " },\n" + + " {\n" + + " \"name\" : \"p3\",\n" + + " \"description\" : \"p3_description\",\n" + + " \"constantIndex\" : 2\n" + + " }\n" + + " ],\n" + + " \"expressionTree\" : \"d1939a730073017302\"\n" + + "}"; - it("roundtrip for P2PK", () => { let template = ContractTemplateObj.fromJsonString(templateJsonStr); - expect(template).not.toBeUndefined(); - expect(template.toJsonString()).toEqual(templateJsonStr); - // expect(addr.isP2PK()).toEqual(true) - // - // expect(addr.isP2S()).toEqual(false) - // expect(addr.toString()).toEqual(addrStr) - // expect(addr.asP2PK()).not.toBeUndefined(); - }); + + it("Json encoding roundtrip", () => { + expect(template).not.toBeUndefined(); + expect(template.toJsonString()).toEqual(templateJsonStr); + } + ); + + it("applyTemplate", () => { + let templateValues = { + "p1": ValueObj.ofByte(10), + "p2": ValueObj.ofByte(40), + "p3": ValueObj.ofByte(50) + }; + let tree = template.applyTemplate(2, templateValues); + expect(tree.toHex()).toEqual("1a1003020a02280232d1939a730073017302"); + } + ); }); From c18c88e1e72bd226985ad4923ff819103f6a3199 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 15 Nov 2023 22:24:51 +0100 Subject: [PATCH 09/25] new-sigma-js: README.md updated --- sigma-js/README.md | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/sigma-js/README.md b/sigma-js/README.md index 1531088cbd..8e0c2f9bb7 100644 --- a/sigma-js/README.md +++ b/sigma-js/README.md @@ -1,5 +1,4 @@ [![CI](https://github.com/ScorexFoundation/sigmastate-interpreter/actions/workflows/ci.yml/badge.svg)](https://github.com/ScorexFoundation/sigmastate-interpreter/actions/workflows/ci.yml) -[![codecov](https://codecov.io/gh/ScorexFoundation/sigmastate-interpreter/branch/develop/graph/badge.svg?token=HNu2ZEOoV6)](https://codecov.io/gh/ScorexFoundation/sigmastate-interpreter) # ErgoScript compiler and ErgoTree interpreter @@ -21,6 +20,48 @@ Run following command to add Sigma.JS as a project dependency: npm install sigmastate-js ``` +# Package organization + +All classes of this package are separated into several modules (which can also be thought +as layers). Each module contains a subset of all the class exported to JavaScript. You can +decide which modules to import in your application depending on which classes from this +package you want to use. +Each subsequent module contains all the classes from the previous modules and some new +classes thus forming a layering of modules. + +NOTE, you only need to import only one of the modules, the one which contains all the +classes you need. + +The modules are: +- [sigma-core module](https://github.com/ScorexFoundation/sigmastate-interpreter/tree/b26930c5e7aa58b6d76dda96ab56db59825f8638/core) - contains core classes of the library + - [Type](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/b26930c5e7aa58b6d76dda96ab56db59825f8638/core/js/src/main/scala/sigma/js/Type.scala) + - [Value](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/2d767ae75ab233deefeba25e42ca22ae22be8952/core/js/src/main/scala/sigma/js/Value.scala) + - [GroupElement](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/4fcd2a71f94d6a0e5a1922817dba02e5657558e1/core/js/src/main/scala/sigma/js/GroupElement.scala) + - [SigmaProp](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/4fcd2a71f94d6a0e5a1922817dba02e5657558e1/core/js/src/main/scala/sigma/js/SigmaProp.scala) + - [AvlTree](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/4fcd2a71f94d6a0e5a1922817dba02e5657558e1/core/js/src/main/scala/sigma/js/AvlTree.scala) + +- [sigma-data module](https://github.com/ScorexFoundation/sigmastate-interpreter/tree/b26930c5e7aa58b6d76dda96ab56db59825f8638/data) - contains classes for working with ErgoTree, addresses and all related serializers + - all classes from sigma-core module + - [ErgoTree](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/b26930c5e7aa58b6d76dda96ab56db59825f8638/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala) + - [Address](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/b745c5fd2257abc6d4317d9761394eb0ea0f3f4e/data/js/src/main/scala/org/ergoplatform/js/Address.scala) + +- [sigma-interpreter module]() - contains classes for working with ErgoTree interpreter + - all classes from sigma-data module + +- [sigma-sdk module](https://github.com/ScorexFoundation/sigmastate-interpreter/tree/6d774a34118b6fac4e70b58c29343afb1b261460/sdk) - contains classes for working with ErgoTree interpreter + - all classes from sigma-interpreter module + - [BlockchainParameters](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/ce203cca487c0a2476504f8a11e7a94ba8ef61b5/sdk/js/src/main/scala/org/ergoplatform/sdk/js/BlockchainParameters.scala) + - [BlockchainStateContext](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/ce203cca487c0a2476504f8a11e7a94ba8ef61b5/sdk/js/src/main/scala/org/ergoplatform/sdk/js/BlockchainStateContext.scala) + - [ContractTemplate](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/6d774a34118b6fac4e70b58c29343afb1b261460/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala) + - [Header](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/4fcd2a71f94d6a0e5a1922817dba02e5657558e1/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Header.scala) + - [PreHeader](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/4fcd2a71f94d6a0e5a1922817dba02e5657558e1/sdk/js/src/main/scala/org/ergoplatform/sdk/js/PreHeader.scala) + - [ProverBuilder](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/2a77625cd65a39f29fa56aa0e3c9c46cbe038363/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala) + - [ReducedTransaction](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/fff394ff28ec5530a6535effedd927f2eb297fc0/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ReducedTransaction.scala) + - [SigmaProver](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/9cdcbde6c77436f154e256c846e8f54aa00bff15/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala) + +- [sigma-compiler module]() - contains classes for working with ErgoScript compiler + - [SigmaCompiler](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/aae4118fed18f6587413d9a6330e449b05d8d5ad/sc/js/src/main/scala/sigmastate/lang/js/SigmaCompiler.scala) + # Examples ### How to create Sigma type descriptors From df95bee3c80f35e38cafc5d45bfc4f8e4ed7c8b2 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 22 Nov 2023 00:23:18 +0100 Subject: [PATCH 10/25] transaction-signer: towards js.SigmaPropProver --- core/js/src/main/scala/sigma/js/Isos.scala | 20 ++++++++++- .../sigma/interpreter/js/ProverSecret.scala | 29 +++++++++++++++ .../sigma/interpreter/js/SigmaLeaf.scala | 6 ++++ .../interpreter/js/SigmaPropProver.scala | 20 +++++++++++ .../org/ergoplatform/SigmaPropProver.scala | 14 ++++++++ .../scala/org/ergoplatform/sdk/js/Isos.scala | 36 ++++++------------- .../ergoplatform/sdk/js/ProverBuilder.scala | 4 +-- .../org/ergoplatform/sdk/js/SigmaProver.scala | 2 +- .../org/ergoplatform/sdk/js/IsosSpec.scala | 4 +-- 9 files changed, 103 insertions(+), 32 deletions(-) create mode 100644 interpreter/js/src/main/scala/sigma/interpreter/js/ProverSecret.scala create mode 100644 interpreter/js/src/main/scala/sigma/interpreter/js/SigmaLeaf.scala create mode 100644 interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala create mode 100644 interpreter/shared/src/main/scala/org/ergoplatform/SigmaPropProver.scala diff --git a/core/js/src/main/scala/sigma/js/Isos.scala b/core/js/src/main/scala/sigma/js/Isos.scala index b349406d2b..b28a3337b8 100644 --- a/core/js/src/main/scala/sigma/js/Isos.scala +++ b/core/js/src/main/scala/sigma/js/Isos.scala @@ -1,8 +1,9 @@ package sigma.js import sigma.{Coll, Colls} -import sigma.data.{Iso, RType} +import sigma.data.{CBigInt, Iso, RType} +import java.math.BigInteger import scala.reflect.ClassTag import scala.scalajs.js import scala.scalajs.js.JSConverters.JSRichOption @@ -26,4 +27,21 @@ object Isos { override def from(x: IndexedSeq[B]): js.Array[A] = js.Array(x.map(iso.from): _*) } + implicit val isoBigInt: Iso[js.BigInt, sigma.BigInt] = new Iso[js.BigInt, sigma.BigInt] { + override def to(x: js.BigInt): sigma.BigInt = { + CBigInt(new BigInteger(x.toString(10))) + } + + override def from(x: sigma.BigInt): js.BigInt = { + val bi = x.asInstanceOf[CBigInt].wrappedValue + val s = bi.toString(10) + js.BigInt(s) + } + } + + implicit val isoBigIntToLong: Iso[js.BigInt, Long] = new Iso[js.BigInt, Long] { + override def to(x: js.BigInt): Long = java.lang.Long.parseLong(x.toString(10)) + + override def from(x: Long): js.BigInt = js.BigInt(x.toString) + } } diff --git a/interpreter/js/src/main/scala/sigma/interpreter/js/ProverSecret.scala b/interpreter/js/src/main/scala/sigma/interpreter/js/ProverSecret.scala new file mode 100644 index 0000000000..d9c8a50031 --- /dev/null +++ b/interpreter/js/src/main/scala/sigma/interpreter/js/ProverSecret.scala @@ -0,0 +1,29 @@ +package sigma.interpreter.js + +import sigma.data.{ProveDHTuple, WrapperOf} +import sigma.js.{Isos, SigmaProp} +import sigma.util.Extensions.BigIntOps +import sigmastate.crypto.DLogProtocol.DLogProverInput +import sigmastate.crypto.{DiffieHellmanTupleProverInput, SigmaProtocolPrivateInput} + +import scala.scalajs.js + +class ProverSecret( + override val wrappedValue: SigmaProtocolPrivateInput[_] +) extends WrapperOf[SigmaProtocolPrivateInput[_]] { +} + +object ProverSecret { + def dlog(w: js.BigInt): ProverSecret = { + val input = DLogProverInput(Isos.isoBigInt.to(w).toBigInteger) + new ProverSecret(input) + } + def dht(w: js.BigInt, dhtProp: SigmaProp): ProverSecret = { + dhtProp.sigmaBoolean match { + case dht: ProveDHTuple => + val input = DiffieHellmanTupleProverInput(Isos.isoBigInt.to(w).toBigInteger, dht) + new ProverSecret(input) + case _ => throw new Exception("Expected ProveDHTuple sigma proposition") + } + } +} \ No newline at end of file diff --git a/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaLeaf.scala b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaLeaf.scala new file mode 100644 index 0000000000..bafd84577f --- /dev/null +++ b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaLeaf.scala @@ -0,0 +1,6 @@ +package sigma.interpreter.js + +import sigma.data.WrapperOf + +class SigmaLeaf(override val wrappedValue: sigma.data.SigmaLeaf) extends WrapperOf[sigma.data.SigmaLeaf]{ +} diff --git a/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala new file mode 100644 index 0000000000..925f6587b4 --- /dev/null +++ b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala @@ -0,0 +1,20 @@ +package sigma.interpreter.js + +import sigma.data.{Iso, WrapperOf} +import sigma.js.Isos + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExportTopLevel + +@JSExportTopLevel("SigmaPropProver") +class SigmaPropProver(override val wrappedValue: org.ergoplatform.SigmaPropProver) + extends WrapperOf[org.ergoplatform.SigmaPropProver] { +} + +@JSExportTopLevel("SigmaPropProverObj") +object SigmaPropProver { + def withSecretes(secrets: js.Array[ProverSecret]): SigmaPropProver = { + val privateInputs = Isos.isoArrayToIndexed(Iso.identityIso[ProverSecret]).to(secrets).map(_.wrappedValue) + new SigmaPropProver(new org.ergoplatform.SigmaPropProver(privateInputs)) + } +} \ No newline at end of file diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/SigmaPropProver.scala b/interpreter/shared/src/main/scala/org/ergoplatform/SigmaPropProver.scala new file mode 100644 index 0000000000..09843554fb --- /dev/null +++ b/interpreter/shared/src/main/scala/org/ergoplatform/SigmaPropProver.scala @@ -0,0 +1,14 @@ +package org.ergoplatform + +import sigmastate.crypto.SigmaProtocolPrivateInput +import sigmastate.interpreter.ProverInterpreter + +/** Prover which can reduce ErgoTrees and prove sigma propositions using provided secrets. + * + * @param secrets All secrets available for this prover. + */ +class SigmaPropProver(override val secrets: Seq[SigmaProtocolPrivateInput[_]]) + extends ErgoLikeInterpreter + with ProverInterpreter { + override type CTX = ErgoLikeContext +} diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala index a0aa528891..f6393f62bb 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala @@ -79,14 +79,14 @@ object Isos { ADProofsRoot = isoStringToColl.to(a.ADProofsRoot), stateRoot = AvlTree.isoAvlTree.to(a.stateRoot), transactionsRoot = isoStringToColl.to(a.transactionsRoot), - timestamp = isoBigIntToLong.to(a.timestamp), - nBits = isoBigIntToLong.to(a.nBits), + timestamp = sigma.js.Isos.isoBigIntToLong.to(a.timestamp), + nBits = sigma.js.Isos.isoBigIntToLong.to(a.nBits), height = a.height, extensionRoot = isoStringToColl.to(a.extensionRoot), minerPk = isoGroupElement.to(a.minerPk), powOnetimePk = isoGroupElement.to(a.powOnetimePk), powNonce = isoStringToColl.to(a.powNonce), - powDistance = isoBigInt.to(a.powDistance), + powDistance = sigma.js.Isos.isoBigInt.to(a.powDistance), votes = isoStringToColl.to(a.votes) ) } @@ -99,14 +99,14 @@ object Isos { ADProofsRoot = isoStringToColl.from(header.ADProofsRoot), stateRoot = AvlTree.isoAvlTree.from(header.stateRoot), transactionsRoot = isoStringToColl.from(header.transactionsRoot), - timestamp = isoBigIntToLong.from(header.timestamp), - nBits = isoBigIntToLong.from(header.nBits), + timestamp = sigma.js.Isos.isoBigIntToLong.from(header.timestamp), + nBits = sigma.js.Isos.isoBigIntToLong.from(header.nBits), height = header.height, extensionRoot = isoStringToColl.from(header.extensionRoot), minerPk = isoGroupElement.from(header.minerPk), powOnetimePk = isoGroupElement.from(header.powOnetimePk), powNonce = isoStringToColl.from(header.powNonce), - powDistance = isoBigInt.from(header.powDistance), + powDistance = sigma.js.Isos.isoBigInt.from(header.powDistance), votes = isoStringToColl.from(header.votes) ) } @@ -117,8 +117,8 @@ object Isos { CPreHeader( version = a.version, parentId = isoStringToColl.to(a.parentId), - timestamp = isoBigIntToLong.to(a.timestamp), - nBits = isoBigIntToLong.to(a.nBits), + timestamp = sigma.js.Isos.isoBigIntToLong.to(a.timestamp), + nBits = sigma.js.Isos.isoBigIntToLong.to(a.nBits), height = a.height, minerPk = isoGroupElement.to(a.minerPk), votes = isoStringToColl.to(a.votes) @@ -129,8 +129,8 @@ object Isos { new PreHeader( version = header.version, parentId = isoStringToColl.from(header.parentId), - timestamp = isoBigIntToLong.from(header.timestamp), - nBits = isoBigIntToLong.from(header.nBits), + timestamp = sigma.js.Isos.isoBigIntToLong.from(header.timestamp), + nBits = sigma.js.Isos.isoBigIntToLong.from(header.nBits), height = header.height, minerPk = isoGroupElement.from(header.minerPk), votes = isoStringToColl.from(header.votes) @@ -247,22 +247,6 @@ object Isos { override def from(x: DataInput): inputsMod.DataInput = inputsMod.DataInput(x.boxId.convertTo[boxesMod.BoxId]) } - implicit val isoBigInt: Iso[js.BigInt, sigma.BigInt] = new Iso[js.BigInt, sigma.BigInt] { - override def to(x: js.BigInt): sigma.BigInt = { - CBigInt(new BigInteger(x.toString(10))) - } - override def from(x: sigma.BigInt): js.BigInt = { - val bi = x.asInstanceOf[CBigInt].wrappedValue - val s = bi.toString(10) - js.BigInt(s) - } - } - - implicit val isoBigIntToLong: Iso[js.BigInt, Long] = new Iso[js.BigInt, Long] { - override def to(x: js.BigInt): Long = java.lang.Long.parseLong(x.toString(10)) - override def from(x: Long): js.BigInt = js.BigInt(x.toString) - } - implicit val isoAmount: Iso[commonMod.Amount, Long] = new Iso[commonMod.Amount, Long] { override def to(x: commonMod.Amount): Long = x.asInstanceOf[Any] match { case s: String => BigInt(s).toLong diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala index e0f4f8f190..1a27c6a655 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala @@ -71,7 +71,7 @@ class ProverBuilder(parameters: BlockchainParameters, network: Byte) extends js. isoStringToGroupElement.to(h), isoStringToGroupElement.to(u), isoStringToGroupElement.to(v), - SigmaDsl.toBigInteger(isoBigInt.to(x)) + SigmaDsl.toBigInteger(sigma.js.Isos.isoBigInt.to(x)) ) this } @@ -85,7 +85,7 @@ class ProverBuilder(parameters: BlockchainParameters, network: Byte) extends js. * as proveDlog(a) && proveDlog(b), where a and b are two group elements. */ def withDLogSecret(x: js.BigInt): ProverBuilder = { - _builder.withDLogSecret(SigmaDsl.toBigInteger(isoBigInt.to(x))) + _builder.withDLogSecret(SigmaDsl.toBigInteger(sigma.js.Isos.isoBigInt.to(x))) this } diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala index 162d23dcf6..0a6aca4830 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala @@ -24,7 +24,7 @@ class SigmaProver(_prover: sdk.SigmaProver) extends js.Object { /** Returns the prover's secret key. */ def getSecretKey(): js.BigInt = - isoBigInt.from(_prover.getSecretKey) + sigma.js.Isos.isoBigInt.from(_prover.getSecretKey) /** Returns an array of EIP-3 addresses associated with the prover's secret keys. */ def getEip3Addresses(): js.Array[String] = { diff --git a/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala b/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala index 5b7f0515cc..a366e33d3c 100644 --- a/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala +++ b/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala @@ -103,13 +103,13 @@ class IsosSpec extends IsosSpecBase with sdk.generators.ObjectGenerators { property("Iso.isoBigInt") { forAll { (c: sigma.BigInt) => - roundtrip(Isos.isoBigInt)(c) + roundtrip(sigma.js.Isos.isoBigInt)(c) } } property("Iso.isoBigIntToLong") { forAll { (c: Long) => - roundtrip(Isos.isoBigIntToLong)(c) + roundtrip(sigma.js.Isos.isoBigIntToLong)(c) } } From f154eb852a62c0bd76c092f05df7aff1a8d6ab9c Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 22 Nov 2023 16:35:28 +0100 Subject: [PATCH 11/25] transaction-signer: finised js.SigmaPropProver + tests --- .../src/main/scala/sigma/js/SigmaProp.scala | 18 ++- .../sigma/interpreter/js/ProverHints.scala | 23 ++++ .../sigma/interpreter/js/ProverSecret.scala | 31 +++++- .../sigma/interpreter/js/SigmaLeaf.scala | 4 +- .../interpreter/js/SigmaPropProver.scala | 98 ++++++++++++++++- .../crypto/SigmaProtocolFunctions.scala | 2 +- sigma-js/sigmastate-js.d.ts | 103 ++++++++++++++++++ sigma-js/tests/js/SigmaPropProver.spec.js | 26 +++++ 8 files changed, 289 insertions(+), 16 deletions(-) create mode 100644 interpreter/js/src/main/scala/sigma/interpreter/js/ProverHints.scala create mode 100644 sigma-js/tests/js/SigmaPropProver.spec.js diff --git a/core/js/src/main/scala/sigma/js/SigmaProp.scala b/core/js/src/main/scala/sigma/js/SigmaProp.scala index dd89929ca9..882b345eee 100644 --- a/core/js/src/main/scala/sigma/js/SigmaProp.scala +++ b/core/js/src/main/scala/sigma/js/SigmaProp.scala @@ -1,6 +1,6 @@ package sigma.js -import sigma.data.{ProveDlog, SigmaBoolean} +import sigma.data.{ProveDHTuple, ProveDlog, SigmaBoolean} import scala.scalajs.js import scala.scalajs.js.annotation.JSExportTopLevel @@ -17,7 +17,19 @@ object SigmaProp extends js.Object { * @see CryptoFacade.getASN1Encoding, GroupElement.fromPointHex, Point */ def fromPointHex(pointHex: String): SigmaProp = { - val point = GroupElement.fromPointHex(pointHex).point - new SigmaProp(ProveDlog(point)) + val ge = GroupElement.fromPointHex(pointHex) + dlog(ge) + } + + /** @param publicKey a [[GroupElement]] representing public key of discrete logarithm signature protocol + * @return a new [[SigmaProp]] value representing public key of discrete logarithm signature protocol. + */ + def dlog(publicKey: GroupElement): SigmaProp = { + new SigmaProp(ProveDlog(publicKey.point)) + } + + /** Construct a new [[SigmaProp]] value representing public key of Diffie Hellman signature protocol. */ + def dht(g: GroupElement, h: GroupElement, u: GroupElement, v: GroupElement): SigmaProp = { + new SigmaProp(ProveDHTuple(g.point, h.point, u.point, v.point)) } } diff --git a/interpreter/js/src/main/scala/sigma/interpreter/js/ProverHints.scala b/interpreter/js/src/main/scala/sigma/interpreter/js/ProverHints.scala new file mode 100644 index 0000000000..f349e05726 --- /dev/null +++ b/interpreter/js/src/main/scala/sigma/interpreter/js/ProverHints.scala @@ -0,0 +1,23 @@ +package sigma.interpreter.js + +import sigma.js.JsWrapper +import sigmastate.interpreter.HintsBag + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExportTopLevel + +/** Represents hints used by [[SigmaPropProver]] to perform operations as part of + * multi-signature scheme. See [EIP-11](https://github.com/ergoplatform/eips/pull/8). + */ +@JSExportTopLevel("ProverHints") +class ProverHints(override val wrappedValue: HintsBag) + extends JsWrapper[HintsBag] { +} + +@JSExportTopLevel("ProverHintsObj") +object ProverHints extends js.Object { + private lazy val _empty = new ProverHints(HintsBag.empty) + + /** Empty bag of hints. Immutable value can be reused where necessary. */ + def empty(): ProverHints = _empty +} diff --git a/interpreter/js/src/main/scala/sigma/interpreter/js/ProverSecret.scala b/interpreter/js/src/main/scala/sigma/interpreter/js/ProverSecret.scala index d9c8a50031..4b8c9741cf 100644 --- a/interpreter/js/src/main/scala/sigma/interpreter/js/ProverSecret.scala +++ b/interpreter/js/src/main/scala/sigma/interpreter/js/ProverSecret.scala @@ -1,23 +1,44 @@ package sigma.interpreter.js -import sigma.data.{ProveDHTuple, WrapperOf} -import sigma.js.{Isos, SigmaProp} +import sigma.data.{CBigInt, ProveDHTuple} +import sigma.js.{Isos, JsWrapper, SigmaProp} import sigma.util.Extensions.BigIntOps import sigmastate.crypto.DLogProtocol.DLogProverInput import sigmastate.crypto.{DiffieHellmanTupleProverInput, SigmaProtocolPrivateInput} import scala.scalajs.js +import scala.scalajs.js.annotation.JSExportTopLevel +/** Represents one secret (aka SigmaProtocolPrivateInput) used by [[SigmaPropProver]]. */ +@JSExportTopLevel("ProverSecret") class ProverSecret( - override val wrappedValue: SigmaProtocolPrivateInput[_] -) extends WrapperOf[SigmaProtocolPrivateInput[_]] { + override val wrappedValue: SigmaProtocolPrivateInput[sigma.data.SigmaLeaf] +) extends JsWrapper[SigmaProtocolPrivateInput[sigma.data.SigmaLeaf]] { + /** Public key generated from the secret. + * Represents proof of knowledge sigma proposition. + */ + def publicKey(): SigmaProp = new SigmaProp(wrappedValue.publicImage) + + /** Secret random number stored in this instance. */ + def secret(): js.BigInt = Isos.isoBigInt.from(CBigInt(wrappedValue.w)) } -object ProverSecret { +@JSExportTopLevel("ProverSecretObj") +object ProverSecret extends js.Object { + /** Creates a new [[ProverSecret]] instance for the given secret of descrete logarithm + * sigma protocol. + * @param w secret exponent value + */ def dlog(w: js.BigInt): ProverSecret = { val input = DLogProverInput(Isos.isoBigInt.to(w).toBigInteger) new ProverSecret(input) } + + /** Creates a new [[ProverSecret]] instance for the given secret of Diffie Hellman tuple + * sigma protocol. + * @param w secret exponent value used to compute `u = g^w` and `v = h^w`, where `g` and `h` are generators + * @param dhtProp a [[SigmaProp]] representing public key of Diffie Hellman tuple sigma protocol, should be created using `w` + */ def dht(w: js.BigInt, dhtProp: SigmaProp): ProverSecret = { dhtProp.sigmaBoolean match { case dht: ProveDHTuple => diff --git a/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaLeaf.scala b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaLeaf.scala index bafd84577f..100f6c73d8 100644 --- a/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaLeaf.scala +++ b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaLeaf.scala @@ -1,6 +1,6 @@ package sigma.interpreter.js -import sigma.data.WrapperOf +import sigma.js.JsWrapper -class SigmaLeaf(override val wrappedValue: sigma.data.SigmaLeaf) extends WrapperOf[sigma.data.SigmaLeaf]{ +class SigmaLeaf(override val wrappedValue: sigma.data.SigmaLeaf) extends JsWrapper[sigma.data.SigmaLeaf] { } diff --git a/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala index 925f6587b4..30a79e9cbd 100644 --- a/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala +++ b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala @@ -1,19 +1,107 @@ package sigma.interpreter.js -import sigma.data.{Iso, WrapperOf} -import sigma.js.Isos +import sigma.data.{Iso, SigmaBoolean} +import sigma.exceptions.InterpreterException +import sigma.js.{Isos, JsWrapper, SigmaProp} import scala.scalajs.js import scala.scalajs.js.annotation.JSExportTopLevel +import scala.scalajs.js.typedarray.Int8Array +import scala.util.{Failure, Success} +/** Prover which can sign messages (generate proofs) for arbitrary sigma propositions + * represented by [[SigmaProp]] values. + * + * See [EIP-11](https://github.com/ergoplatform/eips/pull/8) for details of multi-signature scheme. + * + * @see SigmaPropVerifier + */ @JSExportTopLevel("SigmaPropProver") class SigmaPropProver(override val wrappedValue: org.ergoplatform.SigmaPropProver) - extends WrapperOf[org.ergoplatform.SigmaPropProver] { + extends JsWrapper[org.ergoplatform.SigmaPropProver] { + private val isoSP = Isos.isoArrayToIndexed(Iso.identityIso[SigmaProp]) + + private def toSigmaBooleanSeq(sps: js.Array[SigmaProp]): Seq[SigmaBoolean] = { + isoSP.to(sps).map(_.sigmaBoolean) + } + + /** + * A method which is generating commitments for all the public keys provided. + * This is used as part of multi-signature scheme. + * + * Currently only keys in form of ProveDlog and ProveDiffieHellman are supported, not more complex subtrees. + * + * @param sigmaTree - crypto-tree which is being signed + * @param generateFor - public keys for which commitments should be generated + * @return generated commitments in a form of prover hints + * - private, containing secret randomness + * - public, containing only commitments + */ + def generateCommitmentsFor( + sigmaTree: SigmaProp, + generateFor: js.Array[SigmaProp]): ProverHints = { + val pks = toSigmaBooleanSeq(generateFor) + val reduced = wrappedValue.generateCommitmentsFor(sigmaTree.sigmaBoolean, pks) + new ProverHints(reduced) + } + + /** + * A method which is extracting partial proofs of secret knowledge for particular secrets with their + * respective public images given. Useful for distributed signature applications. + * + * See DistributedSigSpecification for examples of usage. + * + * @param sigmaTree - public key (in form of a sigma-tree) + * @param proof - signature for the key + * @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 hintsForMultisig( + sigmaTree: SigmaProp, + proof: Int8Array, + realSecretsToExtract: js.Array[SigmaProp], + simulatedSecretsToExtract: js.Array[SigmaProp]): ProverHints = { + val realsToExtract = toSigmaBooleanSeq(realSecretsToExtract) + val simsToExtract = toSigmaBooleanSeq(simulatedSecretsToExtract) + val hints = wrappedValue.bagForMultisig( + context = null, + sigmaTree = sigmaTree.sigmaBoolean, proof.toArray, + realSecretsToExtract = realsToExtract, + simulatedSecretsToExtract = simsToExtract) + new ProverHints(hints) + } + + /** + * Generate commitments for given crypto-tree (sigma-tree) for prover's secrets. + */ + def generateCommitments(sigmaTree: SigmaProp): ProverHints = { + val hints = wrappedValue.generateCommitments(sigmaTree.sigmaBoolean) + new ProverHints(hints) + } + + /** Sign arbitrary message under a key representing a statement provable via a sigma-protocol. + * + * @param sigmaProp - public key + * @param message - message to sign + * @param hintsBag - additional hints for a signer (useful for distributed signing) + * @return - signature or error + */ + def signMessage( + sigmaProp: SigmaProp, + message: Int8Array, + hintsBag: ProverHints): Int8Array = { + wrappedValue.signMessage(sigmaProp.sigmaBoolean, message.toArray, hintsBag.wrappedValue) match { + case Success(signature) => Int8Array.of(signature:_*) + case Failure(t) => throw new InterpreterException("Failed to sign message", Some(t)) + } + } } @JSExportTopLevel("SigmaPropProverObj") -object SigmaPropProver { - def withSecretes(secrets: js.Array[ProverSecret]): SigmaPropProver = { +object SigmaPropProver extends js.Object { + /** Creates a new [[SigmaPropProver]] with the given secrets. */ + def withSecrets(secrets: js.Array[ProverSecret]): SigmaPropProver = { val privateInputs = Isos.isoArrayToIndexed(Iso.identityIso[ProverSecret]).to(secrets).map(_.wrappedValue) new SigmaPropProver(new org.ergoplatform.SigmaPropProver(privateInputs)) } diff --git a/interpreter/shared/src/main/scala/sigmastate/crypto/SigmaProtocolFunctions.scala b/interpreter/shared/src/main/scala/sigmastate/crypto/SigmaProtocolFunctions.scala index 3473f1e017..78f45afaa9 100644 --- a/interpreter/shared/src/main/scala/sigmastate/crypto/SigmaProtocolFunctions.scala +++ b/interpreter/shared/src/main/scala/sigmastate/crypto/SigmaProtocolFunctions.scala @@ -42,7 +42,7 @@ trait FirstProverMessage extends ProverMessage { /** Second message from the prover (message `z` of `SigmaProtocol`)*/ trait SecondProverMessage extends ProverMessage -trait SigmaProtocolPrivateInput[CI <: SigmaLeaf] { +trait SigmaProtocolPrivateInput[+CI <: SigmaLeaf] { /** Public image generated from the secret. * Represents proof of knowledge proposition. */ diff --git a/sigma-js/sigmastate-js.d.ts b/sigma-js/sigmastate-js.d.ts index 3f9859e57a..285b0a90f5 100644 --- a/sigma-js/sigmastate-js.d.ts +++ b/sigma-js/sigmastate-js.d.ts @@ -334,6 +334,109 @@ declare module "sigmastate-js/main" { fromHex(hex: HexString): ReducedTransaction; } + /** Represents hints used by [[SigmaPropProver]] to perform operations as part of + * multi-signature scheme. See [EIP-11](https://github.com/ergoplatform/eips/pull/8). + */ + export declare class ProverHints { + + } + + export declare class ProverHintsObj { + /** Empty bag of hints. Immutable value can be reused where necessary. */ + empty(): ProverHints + } + + /** Represents one secret (aka SigmaProtocolPrivateInput) used by [[SigmaPropProver]]. */ + export declare class ProverSecret { + /** Public key generated from the secret. + * Represents proof of knowledge sigma proposition. + */ + publicKey(): SigmaProp + + /** Secret random number stored in this instance. */ + secret(): bigint + } + + export declare class ProverSecretObj { + /** Creates a new [[ProverSecret]] instance for the given secret of descrete logarithm + * sigma protocol. + * @param w secret exponent value + */ + dlog(w: bigint): ProverSecret + + /** Creates a new [[ProverSecret]] instance for the given secret of Diffie Hellman tuple + * sigma protocol. + * @param w secret exponent value used to compute `u = g^w` and `v = h^w`, where `g` and `h` are generators + * @param dhtProp a [[SigmaProp]] representing public key of Diffie Hellman tuple sigma protocol, should be created using `w` + */ + dht(w: bigint, dhtProp: SigmaProp): ProverSecret + } + + /** Prover which can sign messages (generate proofs) for arbitrary sigma propositions + * represented by [[SigmaProp]] values. + * + * See [EIP-11](https://github.com/ergoplatform/eips/pull/8) for details of multi-signature scheme. + * + * @see SigmaPropVerifier + */ + export declare class SigmaPropProver { + /** + * A method which is generating commitments for all the public keys provided. + * This is used as part of multi-signature scheme. + * + * Currently only keys in form of ProveDlog and ProveDiffieHellman are supported, not more complex subtrees. + * + * @param sigmaTree - crypto-tree which is being signed + * @param generateFor - public keys for which commitments should be generated + * @return generated commitments in a form of prover hints + * - private, containing secret randomness + * - public, containing only commitments + */ + generateCommitmentsFor( + sigmaTree: SigmaProp, + generateFor: SigmaProp[]): ProverHints + + /** + * A method which is extracting partial proofs of secret knowledge for particular secrets with their + * respective public images given. Useful for distributed signature applications. + * + * See DistributedSigSpecification for examples of usage. + * + * @param sigmaTree - public key (in form of a sigma-tree) + * @param proof - signature for the key + * @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 + */ + hintsForMultisig( + sigmaTree: SigmaProp, + proof: Int8Array, + realSecretsToExtract: SigmaProp[], + simulatedSecretsToExtract: SigmaProp[]): ProverHints + + /** + * Generate commitments for given crypto-tree (sigma-tree) for prover's secrets. + */ + generateCommitments(sigmaTree: SigmaProp): ProverHints + + /** Sign arbitrary message under a key representing a statement provable via a sigma-protocol. + * + * @param sigmaProp - public key + * @param message - message to sign + * @param hintsBag - additional hints for a signer (useful for distributed signing) + * @return - signature or error + */ + signMessage( + sigmaProp: SigmaProp, + message: Int8Array, + hintsBag: ProverHints): Int8Array + } + + export declare class SigmaPropProverObj { + /** Creates a new [[SigmaPropProver]] with the given secrets. */ + withSecrets(secrets: ProverSecret[]): SigmaPropProver + } + /** Represents a prover for signing Ergo transactions and messages. * * Equivalent of [[org.ergoplatform.sdk.SigmaProver]] available from JS. diff --git a/sigma-js/tests/js/SigmaPropProver.spec.js b/sigma-js/tests/js/SigmaPropProver.spec.js new file mode 100644 index 0000000000..a8d8e5b1be --- /dev/null +++ b/sigma-js/tests/js/SigmaPropProver.spec.js @@ -0,0 +1,26 @@ +const { GroupElementObj, ValueObj, ProverSecret, ProverSecretObj, SigmaPropProverObj, + ProverHints, ProverHintsObj +} = require("sigmastate-js/main"); + +let pointAsn1Hex = "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"; + +describe("SigmaPropProver", () => { + let w = 0xadf47e32000fc75e2923dba482c843c7f6b684cbf2ceec5bfdf5fe6d13cabe5dn + let secret = ProverSecretObj.dlog(w) + expect(secret.secret()).toEqual(w) + + let p = SigmaPropProverObj.withSecrets([secret]) + expect(p).not.toBeUndefined() + + it("generateCommitments", () => { + let hints = p.generateCommitments(secret.publicKey()) + expect(hints).not.toBeUndefined() + }); + + it("signMessage", () => { + let message = Int8Array.of(1, 2, 3) + let signature = p.signMessage(secret.publicKey(), message, ProverHintsObj.empty()) + expect(signature).not.toBeUndefined() + expect(signature.length).toBeGreaterThan(0) + }); +}); \ No newline at end of file From 2becae354525c818c677ba7eab0f4e47c00f7e01 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Tue, 28 Nov 2023 16:19:53 +0100 Subject: [PATCH 12/25] new-sigma-js: exported js.SigmaPropVerifier + tests --- core/js/src/main/scala/sigma/js/Utils.scala | 15 +++++++ .../interpreter/js/SigmaPropVerifier.scala | 43 +++++++++++++++++++ .../org/ergoplatform/SigmaPropVerifier.scala | 6 +++ .../ErgoLikeTransactionSpec.scala | 2 +- sigma-js/sigmastate-js.d.ts | 31 +++++++++++++ sigma-js/tests/js/SigmaPropProver.spec.js | 12 ++++-- 6 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 core/js/src/main/scala/sigma/js/Utils.scala create mode 100644 interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropVerifier.scala create mode 100644 interpreter/shared/src/main/scala/org/ergoplatform/SigmaPropVerifier.scala diff --git a/core/js/src/main/scala/sigma/js/Utils.scala b/core/js/src/main/scala/sigma/js/Utils.scala new file mode 100644 index 0000000000..d19ef68d92 --- /dev/null +++ b/core/js/src/main/scala/sigma/js/Utils.scala @@ -0,0 +1,15 @@ +package sigma.js + +import scorex.util.encode.Base16 + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExportTopLevel +import scala.scalajs.js.typedarray.Int8Array + +@JSExportTopLevel("Utils") +object Utils extends js.Object { + /** Convert an Int8Array to a hex string. */ + def int8ArrayToHex(arr: Int8Array): String = { + Base16.encode(arr.toArray) + } +} diff --git a/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropVerifier.scala b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropVerifier.scala new file mode 100644 index 0000000000..9c58fac8a8 --- /dev/null +++ b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropVerifier.scala @@ -0,0 +1,43 @@ +package sigma.interpreter.js + +import sigma.js.{JsWrapper, SigmaProp} + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExportTopLevel +import scala.scalajs.js.typedarray.Int8Array + +/** Verifier which can verify signature (proof) for arbitrary sigma propositions + * represented by [[SigmaProp]] values. + * + * See [EIP-11](https://github.com/ergoplatform/eips/pull/8) for details of multi-signature scheme. + * + * @see SigmaPropProver + */ +@JSExportTopLevel("SigmaPropVerifier") +class SigmaPropVerifier(override val wrappedValue: org.ergoplatform.SigmaPropVerifier) + extends JsWrapper[org.ergoplatform.SigmaPropVerifier] { + /** + * Verify a signature on given (arbitrary) message for a given sigma proposition (public key). + * + * @param sigmaProp public key (represented as a sigma proposition) + * @param message message + * @param signature signature for the message + * @return whether signature is valid or not (valid signature contains proofs for the sigma proposition) + */ + def verifySignature( + sigmaProp: SigmaProp, + message: Int8Array, + signature: Int8Array): Boolean = { + val ok = wrappedValue.verifySignature(sigmaProp.sigmaBoolean, message.toArray, signature.toArray)(null) + ok + } +} + +@JSExportTopLevel("SigmaPropVerifierObj") +object SigmaPropVerifier extends js.Object { + /** Create a new instance of SigmaPropVerifier. */ + def create(): SigmaPropVerifier = { + new SigmaPropVerifier(new org.ergoplatform.SigmaPropVerifier()) + } +} + diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/SigmaPropVerifier.scala b/interpreter/shared/src/main/scala/org/ergoplatform/SigmaPropVerifier.scala new file mode 100644 index 0000000000..382dd9a470 --- /dev/null +++ b/interpreter/shared/src/main/scala/org/ergoplatform/SigmaPropVerifier.scala @@ -0,0 +1,6 @@ +package org.ergoplatform + +/** Verifier which can verify proofs generated by [[SigmaPropProver]]. */ +class SigmaPropVerifier + extends ErgoLikeInterpreter { +} diff --git a/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala index 6849d4ee22..0f49d22fcd 100644 --- a/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala +++ b/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala @@ -129,7 +129,7 @@ class ErgoLikeTransactionSpec extends SigmaDslTesting { } property("ErgoLikeTransaction with same token in different outputs : Serializer round trip") { - forAll { txIn: ErgoLikeTransaction => + forAll(MinSuccessful(50)) { txIn: ErgoLikeTransaction => whenever(txIn.outputCandidates.head.additionalTokens.nonEmpty) { val out = txIn.outputCandidates.head // clone tokenIds so that same id have different references diff --git a/sigma-js/sigmastate-js.d.ts b/sigma-js/sigmastate-js.d.ts index 285b0a90f5..51166cee86 100644 --- a/sigma-js/sigmastate-js.d.ts +++ b/sigma-js/sigmastate-js.d.ts @@ -437,6 +437,37 @@ declare module "sigmastate-js/main" { withSecrets(secrets: ProverSecret[]): SigmaPropProver } + /** Verifier which can verify signature (proof) for arbitrary sigma propositions + * represented by [[SigmaProp]] values. + * + * See [EIP-11](https://github.com/ergoplatform/eips/pull/8) for details of multi-signature scheme. + * + * @see SigmaPropProver + */ + export declare class SigmaPropVerifier { + /** + * Verify a signature on given (arbitrary) message for a given sigma proposition (public key). + * + * @param sigmaProp public key (represented as a sigma proposition) + * @param message message + * @param signature signature for the message + * @return whether signature is valid or not (valid signature contains proofs for the sigma proposition) + */ + verifySignature( + sigmaProp: SigmaProp, + message: Int8Array, + signature: Int8Array): boolean + } + export declare class SigmaPropVerifierObj { + /** Create a new instance of [[SigmaPropVerifier]]. */ + create(): SigmaPropVerifier + } + + export declare class Utils { + /** Convert an Int8Array to a hex string. */ + int8ArrayToHex(arr: Int8Array): string + } + /** Represents a prover for signing Ergo transactions and messages. * * Equivalent of [[org.ergoplatform.sdk.SigmaProver]] available from JS. diff --git a/sigma-js/tests/js/SigmaPropProver.spec.js b/sigma-js/tests/js/SigmaPropProver.spec.js index a8d8e5b1be..d776de7477 100644 --- a/sigma-js/tests/js/SigmaPropProver.spec.js +++ b/sigma-js/tests/js/SigmaPropProver.spec.js @@ -1,9 +1,8 @@ -const { GroupElementObj, ValueObj, ProverSecret, ProverSecretObj, SigmaPropProverObj, - ProverHints, ProverHintsObj +const { + ProverSecretObj, SigmaPropProverObj, + ProverHintsObj, SigmaPropVerifierObj } = require("sigmastate-js/main"); -let pointAsn1Hex = "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"; - describe("SigmaPropProver", () => { let w = 0xadf47e32000fc75e2923dba482c843c7f6b684cbf2ceec5bfdf5fe6d13cabe5dn let secret = ProverSecretObj.dlog(w) @@ -22,5 +21,10 @@ describe("SigmaPropProver", () => { let signature = p.signMessage(secret.publicKey(), message, ProverHintsObj.empty()) expect(signature).not.toBeUndefined() expect(signature.length).toBeGreaterThan(0) + + let V = SigmaPropVerifierObj.create() + let ok = V.verifySignature(secret.publicKey(), message, signature) + expect(ok).toEqual(true) }); + }); \ No newline at end of file From bc8e874181a318fc82d0d91d65288d8b82b05981 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Tue, 28 Nov 2023 17:27:33 +0100 Subject: [PATCH 13/25] new-sigma-js: exported js.Expr --- .../main/scala/sigma/ast/js/ErgoTree.scala | 4 +--- .../js/src/main/scala/sigma/ast/js/Expr.scala | 19 +++++++++++++++++-- sigma-js/README.md | 10 +++++++++- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala b/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala index 6553167f0e..746e73da59 100644 --- a/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala +++ b/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala @@ -10,9 +10,7 @@ import scala.scalajs.js.annotation.JSExportTopLevel /** An exported JavaScript class wrapping the Scala `ErgoTree` type. */ @JSExportTopLevel("ErgoTree") class ErgoTree(val tree: ast.ErgoTree) extends js.Object { - /** Root of the contract which is a valid expression of `SigmaProp` type. Must have constants - * segregated into `constTypes` and optionally `constValues` - */ + /** Root of the contract which is a valid expression of `SigmaProp` type. */ val root: Expr = new Expr(tree.root.toOption.get) /** The first byte of serialized byte array which determines interpretation of the rest of the array. */ diff --git a/data/js/src/main/scala/sigma/ast/js/Expr.scala b/data/js/src/main/scala/sigma/ast/js/Expr.scala index 960aed45d0..8bd087a16c 100644 --- a/data/js/src/main/scala/sigma/ast/js/Expr.scala +++ b/data/js/src/main/scala/sigma/ast/js/Expr.scala @@ -2,14 +2,29 @@ package sigma.ast.js import sigma.ast.syntax.SValue import sigma.js.JsWrapper +import sigma.serialization.ValueSerializer +import scala.scalajs.js import scala.scalajs.js.annotation.JSExportTopLevel +import scala.scalajs.js.typedarray.Int8Array -/** An exported JavaScript class wrapping the Scala [[sigma.ast.Value]]. */ +/** Represents a node of ErgoTree. + * An exported JavaScript class wrapping the Scala [[sigma.ast.Value]]. + */ @JSExportTopLevel("Expr") class Expr(override val wrappedValue: SValue) extends JsWrapper[SValue]{ + /** Serialize this expression using sigma serializer [[ValueSerializer]]. */ + def toBytes: Int8Array = { + val bytes = ValueSerializer.serialize(wrappedValue) + Int8Array.of(bytes:_*) + } } @JSExportTopLevel("ExprObj") -object Expr { +object Expr extends js.Object { + /** Deserialize an expression from bytes using sigma serializer [[ValueSerializer]]. */ + def fromBytes(bytes: Int8Array): Expr = { + val value = ValueSerializer.deserialize(bytes.toArray) + new Expr(value) + } } diff --git a/sigma-js/README.md b/sigma-js/README.md index 8e0c2f9bb7..3819d1ac71 100644 --- a/sigma-js/README.md +++ b/sigma-js/README.md @@ -29,8 +29,11 @@ package you want to use. Each subsequent module contains all the classes from the previous modules and some new classes thus forming a layering of modules. +See TypeScript [definitions](sigmastate-js.d.ts) for the list of all exported classes, +methods and documentation. + NOTE, you only need to import only one of the modules, the one which contains all the -classes you need. +classes you need. This will allow optimizing the size of the final bundle. The modules are: - [sigma-core module](https://github.com/ScorexFoundation/sigmastate-interpreter/tree/b26930c5e7aa58b6d76dda96ab56db59825f8638/core) - contains core classes of the library @@ -44,9 +47,14 @@ The modules are: - all classes from sigma-core module - [ErgoTree](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/b26930c5e7aa58b6d76dda96ab56db59825f8638/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala) - [Address](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/b745c5fd2257abc6d4317d9761394eb0ea0f3f4e/data/js/src/main/scala/org/ergoplatform/js/Address.scala) + - [Expr]() - [sigma-interpreter module]() - contains classes for working with ErgoTree interpreter - all classes from sigma-data module + - [ProverHints]() + - [ProverSecret]() + - [SigmaPropProver]() + - [SigmaPropVerifier]() - [sigma-sdk module](https://github.com/ScorexFoundation/sigmastate-interpreter/tree/6d774a34118b6fac4e70b58c29343afb1b261460/sdk) - contains classes for working with ErgoTree interpreter - all classes from sigma-interpreter module From 1f919d94f2dab7072bc153c02e854a34c576bd13 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 29 Nov 2023 10:48:39 +0100 Subject: [PATCH 14/25] new-sigma-js: renamed TypeObj -> Type$, ValueObj -> Value$ --- core/js/src/main/scala/sigma/js/Type.scala | 2 +- core/js/src/main/scala/sigma/js/Value.scala | 2 +- sigma-js/README.md | 2 +- sigma-js/sigmastate-js.d.ts | 4 +- sigma-js/tests/js/ContractTemplate.spec.js | 8 +-- sigma-js/tests/js/GroupElement.spec.js | 4 +- sigma-js/tests/js/SigmaCompiler.spec.js | 4 +- sigma-js/tests/js/SigmaProp.spec.js | 4 +- sigma-js/tests/js/Type.spec.js | 34 ++++++------ sigma-js/tests/js/Value.spec.js | 58 ++++++++++----------- 10 files changed, 61 insertions(+), 61 deletions(-) diff --git a/core/js/src/main/scala/sigma/js/Type.scala b/core/js/src/main/scala/sigma/js/Type.scala index 6cfbf842a2..b323273a0c 100644 --- a/core/js/src/main/scala/sigma/js/Type.scala +++ b/core/js/src/main/scala/sigma/js/Type.scala @@ -18,7 +18,7 @@ class Type(final val rtype: RType[_]) extends js.Object { override def toString = s"Type($rtype)" } -@JSExportTopLevel("TypeObj") +@JSExportTopLevel("Type$") object Type extends js.Object { /** Descriptor of ErgoScript type Byte. */ val Byte = new Type(sigma.ByteType) diff --git a/core/js/src/main/scala/sigma/js/Value.scala b/core/js/src/main/scala/sigma/js/Value.scala index b962198dec..13b63c2abc 100644 --- a/core/js/src/main/scala/sigma/js/Value.scala +++ b/core/js/src/main/scala/sigma/js/Value.scala @@ -62,7 +62,7 @@ class Value(val data: Any, val tpe: Type) extends js.Object { } } -@JSExportTopLevel("ValueObj") +@JSExportTopLevel("Value$") object Value extends js.Object { /** Maximal positive value of ES type Long */ val MaxLong = js.BigInt("0x7fffffffffffffff") diff --git a/sigma-js/README.md b/sigma-js/README.md index 3819d1ac71..0fe8c175ed 100644 --- a/sigma-js/README.md +++ b/sigma-js/README.md @@ -84,7 +84,7 @@ See examples in tests [Type.spec.js](https://github.com/ScorexFoundation/sigmast ### How to create Sigma values -Import `ValueObj` module, then use its methods. +Import `Value$` module, then use its methods. See examples in tests [Value.spec.js](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/933acd7a3753725c8b41994c2126a20279b6809b/sigma-js/tests/js/Value.spec.js) ### How to work with ErgoTree diff --git a/sigma-js/sigmastate-js.d.ts b/sigma-js/sigmastate-js.d.ts index 51166cee86..82c21f6cdc 100644 --- a/sigma-js/sigmastate-js.d.ts +++ b/sigma-js/sigmastate-js.d.ts @@ -259,7 +259,7 @@ declare module "sigmastate-js/main" { toString(): string; } - export declare class TypeObj { + export declare class Type$ { static Byte: Type; static Short: Type; static Int: Type; @@ -286,7 +286,7 @@ declare module "sigmastate-js/main" { toHex(): HexString; } - export declare class ValueObj { + export declare class Value$ { static ofByte(value: number): Value; static ofShort(value: number): Value; diff --git a/sigma-js/tests/js/ContractTemplate.spec.js b/sigma-js/tests/js/ContractTemplate.spec.js index 4130aae4b1..1b2c5c3ef5 100644 --- a/sigma-js/tests/js/ContractTemplate.spec.js +++ b/sigma-js/tests/js/ContractTemplate.spec.js @@ -1,4 +1,4 @@ -const {ContractTemplate, ContractTemplateObj, ValueObj} = require("sigmastate-js/main"); +const {ContractTemplate, ContractTemplateObj, Value$} = require("sigmastate-js/main"); describe("Smoke tests for API exporting", () => { it("Should export ContractTempate object", () => { @@ -51,9 +51,9 @@ describe("ContractTemplate", () => { it("applyTemplate", () => { let templateValues = { - "p1": ValueObj.ofByte(10), - "p2": ValueObj.ofByte(40), - "p3": ValueObj.ofByte(50) + "p1": Value$.ofByte(10), + "p2": Value$.ofByte(40), + "p3": Value$.ofByte(50) }; let tree = template.applyTemplate(2, templateValues); expect(tree.toHex()).toEqual("1a1003020a02280232d1939a730073017302"); diff --git a/sigma-js/tests/js/GroupElement.spec.js b/sigma-js/tests/js/GroupElement.spec.js index 6d860691be..1002945ce5 100644 --- a/sigma-js/tests/js/GroupElement.spec.js +++ b/sigma-js/tests/js/GroupElement.spec.js @@ -1,4 +1,4 @@ -const { GroupElementObj, ValueObj } = require("sigmastate-js/main"); +const { GroupElementObj, Value$ } = require("sigmastate-js/main"); let pointAsn1Hex = "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"; @@ -7,7 +7,7 @@ describe("GroupElement", () => { let ge = GroupElementObj.fromPointHex(pointAsn1Hex) expect(ge.toPointHex()).toEqual(pointAsn1Hex) - let v = ValueObj.ofGroupElement(pointAsn1Hex) + let v = Value$.ofGroupElement(pointAsn1Hex) expect(v.toHex()).toEqual("07"/* GroupElement type id */ + pointAsn1Hex) }); }); \ No newline at end of file diff --git a/sigma-js/tests/js/SigmaCompiler.spec.js b/sigma-js/tests/js/SigmaCompiler.spec.js index 6e531d63aa..495ea09f51 100644 --- a/sigma-js/tests/js/SigmaCompiler.spec.js +++ b/sigma-js/tests/js/SigmaCompiler.spec.js @@ -1,4 +1,4 @@ -const { ValueObj, SigmaCompilerObj } = require("sigmastate-js/main"); +const { Value$, SigmaCompilerObj } = require("sigmastate-js/main"); describe("Smoke tests for API exporting", () => { let compiler = SigmaCompilerObj.forMainnet(); @@ -21,7 +21,7 @@ describe("Smoke tests for API exporting", () => { it("SigmaCompiler should compile with named constants", () => { let treeWithSegregation = compiler.compile( - {"deadline": ValueObj.ofInt(100)}, + {"deadline": Value$.ofInt(100)}, true, 0, "sigmaProp(HEIGHT > deadline)"); expect(treeWithSegregation).not.toBeUndefined(); expect(treeWithSegregation.toHex()).toEqual(segregatedTreeHex) diff --git a/sigma-js/tests/js/SigmaProp.spec.js b/sigma-js/tests/js/SigmaProp.spec.js index 89568120c7..4c44f0f3aa 100644 --- a/sigma-js/tests/js/SigmaProp.spec.js +++ b/sigma-js/tests/js/SigmaProp.spec.js @@ -1,4 +1,4 @@ -const { SigmaPropObj, ValueObj } = require("sigmastate-js/main"); +const { SigmaPropObj, Value$ } = require("sigmastate-js/main"); let pointAsn1Hex = "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"; @@ -7,7 +7,7 @@ describe("SigmaProp", () => { let ge = SigmaPropObj.fromPointHex(pointAsn1Hex) expect(ge).not.toBeUndefined() - let v = ValueObj.ofSigmaProp(pointAsn1Hex) + let v = Value$.ofSigmaProp(pointAsn1Hex) expect(v.toHex()) .toEqual("08"/* SigmaProp type id */ + "cd"/* ProveDlog.opCode */ + pointAsn1Hex) }); diff --git a/sigma-js/tests/js/Type.spec.js b/sigma-js/tests/js/Type.spec.js index fe1b0c0383..83d60481b7 100644 --- a/sigma-js/tests/js/Type.spec.js +++ b/sigma-js/tests/js/Type.spec.js @@ -1,27 +1,27 @@ -const { TypeObj } = require("sigmastate-js/main"); +const { Type$ } = require("sigmastate-js/main"); describe("Smoke tests for Types", () => { it("Should create primitive types", () => { - expect(TypeObj.Byte.name).toEqual("Byte"); - expect(TypeObj.Short.name).toEqual("Short"); - expect(TypeObj.Int.name).toEqual("Int"); - expect(TypeObj.Long.name).toEqual("Long"); - expect(TypeObj.BigInt.name).toEqual("BigInt"); - expect(TypeObj.GroupElement.name).toEqual("GroupElement"); - expect(TypeObj.SigmaProp.name).toEqual("SigmaProp"); - expect(TypeObj.Box.name).toEqual("Box"); - expect(TypeObj.AvlTree.name).toEqual("AvlTree"); - expect(TypeObj.Context.name).toEqual("Context"); - expect(TypeObj.Header.name).toEqual("Header"); - expect(TypeObj.PreHeader.name).toEqual("PreHeader"); - expect(TypeObj.SigmaDslBuilder.name).toEqual("SigmaDslBuilder"); + expect(Type$.Byte.name).toEqual("Byte"); + expect(Type$.Short.name).toEqual("Short"); + expect(Type$.Int.name).toEqual("Int"); + expect(Type$.Long.name).toEqual("Long"); + expect(Type$.BigInt.name).toEqual("BigInt"); + expect(Type$.GroupElement.name).toEqual("GroupElement"); + expect(Type$.SigmaProp.name).toEqual("SigmaProp"); + expect(Type$.Box.name).toEqual("Box"); + expect(Type$.AvlTree.name).toEqual("AvlTree"); + expect(Type$.Context.name).toEqual("Context"); + expect(Type$.Header.name).toEqual("Header"); + expect(Type$.PreHeader.name).toEqual("PreHeader"); + expect(Type$.SigmaDslBuilder.name).toEqual("SigmaDslBuilder"); }); it("Should create complex types", () => { - expect(TypeObj.pairType(TypeObj.Int, TypeObj.Long).name).toEqual("(Int, Long)"); - expect(TypeObj.collType(TypeObj.Int).name).toEqual("Coll[Int]"); - expect(TypeObj.collType(TypeObj.pairType(TypeObj.Int, TypeObj.Long)).name) + expect(Type$.pairType(Type$.Int, Type$.Long).name).toEqual("(Int, Long)"); + expect(Type$.collType(Type$.Int).name).toEqual("Coll[Int]"); + expect(Type$.collType(Type$.pairType(Type$.Int, Type$.Long)).name) .toEqual("Coll[(Int, Long)]"); }); }); diff --git a/sigma-js/tests/js/Value.spec.js b/sigma-js/tests/js/Value.spec.js index 7ff5a62f78..5cd2fad21b 100644 --- a/sigma-js/tests/js/Value.spec.js +++ b/sigma-js/tests/js/Value.spec.js @@ -1,4 +1,4 @@ -const { TypeObj, ValueObj, SigmaPropObj, SigmaProp} = require("sigmastate-js/main"); +const { Type$, Value$, SigmaPropObj, SigmaProp} = require("sigmastate-js/main"); function testRange(factory, min, max) { expect(factory(max).data).toEqual(max); @@ -10,20 +10,20 @@ function testRange(factory, min, max) { describe("Smoke tests for Values", () => { it("Should create values of primitive types", () => { - expect(ValueObj.ofByte(0).data).toEqual(0); - expect(ValueObj.ofByte(0).tpe).toEqual(TypeObj.Byte); - testRange(function(v) { return ValueObj.ofByte(v); }, -128, 127); - testRange(function(v) { return ValueObj.ofShort(v); }, -32768, 32767); - testRange(function(v) { return ValueObj.ofInt(v); }, -0x7FFFFFFF - 1, 0x7FFFFFFF); - testRange(function(v) { return ValueObj.ofLong(v); }, -0x8000000000000000n, 0x7fffffffffffffffn); + expect(Value$.ofByte(0).data).toEqual(0); + expect(Value$.ofByte(0).tpe).toEqual(Type$.Byte); + testRange(function(v) { return Value$.ofByte(v); }, -128, 127); + testRange(function(v) { return Value$.ofShort(v); }, -32768, 32767); + testRange(function(v) { return Value$.ofInt(v); }, -0x7FFFFFFF - 1, 0x7FFFFFFF); + testRange(function(v) { return Value$.ofLong(v); }, -0x8000000000000000n, 0x7fffffffffffffffn); }); it("Should create values of complex types", () => { - let pair = ValueObj.pairOf(ValueObj.ofByte(10), ValueObj.ofLong(20n)); + let pair = Value$.pairOf(Value$.ofByte(10), Value$.ofLong(20n)); expect(pair.data).toEqual([10, 20n]); expect(pair.tpe.name).toEqual("(Byte, Long)"); - let coll = ValueObj.collOf([-10, 0, 10], TypeObj.Byte) + let coll = Value$.collOf([-10, 0, 10], Type$.Byte) expect(coll.tpe.name).toEqual("Coll[Byte]"); }); @@ -42,65 +42,65 @@ describe("Smoke tests for Values", () => { let pairHex = "3e050a28" it("Unit Value.toHex", () => { - let v = ValueObj.fromHex(unitHex) + let v = Value$.fromHex(unitHex) expect(v.toHex()).toEqual(unitHex) }); it("Boolean Value.toHex", () => { - let v = ValueObj.fromHex(booleanHex) + let v = Value$.fromHex(booleanHex) expect(v.toHex()).toEqual(booleanHex) }); it("Byte Value.toHex", () => { - let v = ValueObj.fromHex(byteHex) + let v = Value$.fromHex(byteHex) expect(v.toHex()).toEqual(byteHex) }); it("Short Value.toHex", () => { - let v = ValueObj.fromHex(shortHex) + let v = Value$.fromHex(shortHex) expect(v.toHex()).toEqual(shortHex) }); it("Int Value.toHex", () => { - let v = ValueObj.fromHex(intHex) + let v = Value$.fromHex(intHex) expect(v.toHex()).toEqual(intHex) }); it("Long Value.toHex", () => { - let v = ValueObj.ofLong(1200n) + let v = Value$.ofLong(1200n) expect(v.toHex()).toEqual(longHex) }); it("BigInt Value.toHex", () => { - let v = ValueObj.ofBigInt(0xfffffffffffffffen) + let v = Value$.ofBigInt(0xfffffffffffffffen) expect(v.toHex()).toEqual(bigIntHex) }); it("GroupElement Value.toHex", () => { - let v = ValueObj.fromHex(groupElementHex) + let v = Value$.fromHex(groupElementHex) expect(v.toHex()).toEqual(groupElementHex) }); it("SigmaProp Value.toHex", () => { - let v = ValueObj.fromHex(sigmaPropHex) + let v = Value$.fromHex(sigmaPropHex) expect(v.toHex()).toEqual(sigmaPropHex) }); it("AvlTree Value.toHex", () => { - let v = ValueObj.fromHex(avlTreeHex) + let v = Value$.fromHex(avlTreeHex) expect(v.toHex()).toEqual(avlTreeHex) }); // TODO uncomment when Box is implemented // ignore("Box Value.toHex", () => { - // let v = ValueObj.fromHex(boxHex) + // let v = Value$.fromHex(boxHex) // expect(v.toHex()).toEqual(boxHex) // }); it("Coll Value.toHex", () => { let arr = [ [1, 2, 3], [10, 20] ] - let t = TypeObj.collType(TypeObj.Byte) - let collV = ValueObj.collOf(arr, t) + let t = Type$.collType(Type$.Byte) + let collV = Value$.collOf(arr, t) expect(collV.tpe.name).toEqual("Coll[Coll[Byte]]"); expect(collV.toHex()).toEqual(collHex) @@ -109,33 +109,33 @@ describe("Smoke tests for Values", () => { it("Value of type Coll[SigmaProp]", () => { let sp1 = SigmaPropObj.fromPointHex(groupElementHex.substring(2)) let sp2 = SigmaPropObj.fromPointHex(sigmaPropHex.substring(4)) - let collV = ValueObj.collOf([sp1, sp2], TypeObj.SigmaProp) + let collV = Value$.collOf([sp1, sp2], Type$.SigmaProp) expect(collV.tpe.name).toEqual("Coll[SigmaProp]"); }); it("Pair Value.toHex", () => { - let fst = ValueObj.ofByte(10) - let snd = ValueObj.ofLong(20) - let pair = ValueObj.pairOf(fst, snd) + let fst = Value$.ofByte(10) + let snd = Value$.ofLong(20) + let pair = Value$.pairOf(fst, snd) expect(pair.tpe.name).toEqual("(Byte, Long)"); expect(pair.toHex()).toEqual(pairHex) }); it("Long Value.fromHex", () => { - let v = ValueObj.fromHex(longHex) + let v = Value$.fromHex(longHex) expect(v.data).toEqual(1200n) expect(v.tpe.name).toEqual("Long") }); it("Coll Value.fromHex", () => { - let coll = ValueObj.fromHex(collHex) + let coll = Value$.fromHex(collHex) expect(coll.tpe.name).toEqual("Coll[Coll[Byte]]"); expect(coll.toHex()).toEqual(collHex) }); it("Pair Value.fromHex", () => { - let p = ValueObj.fromHex(pairHex) + let p = Value$.fromHex(pairHex) expect(p.tpe.name).toEqual("(Byte, Long)"); expect(p.toHex()).toEqual(pairHex) }); From 643e79970ca0495f93c1cec343be6acf28052a46 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 29 Nov 2023 10:51:28 +0100 Subject: [PATCH 15/25] new-sigma-js: renamed GroupElementObj -> GroupElement$ --- core/js/src/main/scala/sigma/js/GroupElement.scala | 2 +- sigma-js/sigmastate-js.d.ts | 2 +- sigma-js/tests/js/GroupElement.spec.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/js/src/main/scala/sigma/js/GroupElement.scala b/core/js/src/main/scala/sigma/js/GroupElement.scala index e6e472347e..21b53b265d 100644 --- a/core/js/src/main/scala/sigma/js/GroupElement.scala +++ b/core/js/src/main/scala/sigma/js/GroupElement.scala @@ -17,7 +17,7 @@ class GroupElement(val point: Ecp) extends js.Object { } } -@JSExportTopLevel("GroupElementObj") +@JSExportTopLevel("GroupElement$") object GroupElement extends js.Object { /** Creates a new [[GroupElement]] from the given hex string (ASN.1 encoding) * representation of the underlying [[sigmastate.crypto.Platform.Point]]. diff --git a/sigma-js/sigmastate-js.d.ts b/sigma-js/sigmastate-js.d.ts index 82c21f6cdc..a71620b890 100644 --- a/sigma-js/sigmastate-js.d.ts +++ b/sigma-js/sigmastate-js.d.ts @@ -153,7 +153,7 @@ declare module "sigmastate-js/main" { toPointHex(): HexString; } - export declare class GroupElementObj { + export declare class GroupElement$ { static fromPointHex(value: HexString): GroupElement; } diff --git a/sigma-js/tests/js/GroupElement.spec.js b/sigma-js/tests/js/GroupElement.spec.js index 1002945ce5..f408728602 100644 --- a/sigma-js/tests/js/GroupElement.spec.js +++ b/sigma-js/tests/js/GroupElement.spec.js @@ -1,10 +1,10 @@ -const { GroupElementObj, Value$ } = require("sigmastate-js/main"); +const { GroupElement$, Value$ } = require("sigmastate-js/main"); let pointAsn1Hex = "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"; describe("GroupElement", () => { it("should implement toPointHex/fromPointHex", () => { - let ge = GroupElementObj.fromPointHex(pointAsn1Hex) + let ge = GroupElement$.fromPointHex(pointAsn1Hex) expect(ge.toPointHex()).toEqual(pointAsn1Hex) let v = Value$.ofGroupElement(pointAsn1Hex) From f106debce67000fce28335bb7858b63ab6d659e6 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 29 Nov 2023 10:58:17 +0100 Subject: [PATCH 16/25] new-sigma-js: renamed SigmaPropObj -> SigmaProp$, ErgoTreeObj -> ErgoTree$ --- core/js/src/main/scala/sigma/js/SigmaProp.scala | 2 +- data/js/src/main/scala/sigma/ast/js/ErgoTree.scala | 2 +- sigma-js/README.md | 2 +- sigma-js/sigmastate-js.d.ts | 4 ++-- sigma-js/tests/js/ErgoTree.spec.js | 10 +++++----- sigma-js/tests/js/SigmaProp.spec.js | 4 ++-- sigma-js/tests/js/Value.spec.js | 6 +++--- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/core/js/src/main/scala/sigma/js/SigmaProp.scala b/core/js/src/main/scala/sigma/js/SigmaProp.scala index 882b345eee..40d311717d 100644 --- a/core/js/src/main/scala/sigma/js/SigmaProp.scala +++ b/core/js/src/main/scala/sigma/js/SigmaProp.scala @@ -10,7 +10,7 @@ import scala.scalajs.js.annotation.JSExportTopLevel class SigmaProp(val sigmaBoolean: SigmaBoolean) extends js.Object { } -@JSExportTopLevel("SigmaPropObj") +@JSExportTopLevel("SigmaProp$") object SigmaProp extends js.Object { /** Creates a new [[SigmaProp]] from the given hex string of public key. * @param pointHex hex representation of elliptic curve point (ASN.1 encoded) diff --git a/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala b/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala index 746e73da59..73b8fbfb00 100644 --- a/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala +++ b/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala @@ -48,7 +48,7 @@ class ErgoTree(val tree: ast.ErgoTree) extends js.Object { } /** An exported JavaScript object providing utility methods for working with ErgoTree instances. */ -@JSExportTopLevel("ErgoTreeObj") +@JSExportTopLevel("ErgoTree$") object ErgoTree extends js.Object { /** Deserializes an ErgoTree instance from a hexadecimal string. diff --git a/sigma-js/README.md b/sigma-js/README.md index 0fe8c175ed..e89acf963b 100644 --- a/sigma-js/README.md +++ b/sigma-js/README.md @@ -89,7 +89,7 @@ See examples in tests [Value.spec.js](https://github.com/ScorexFoundation/sigmas ### How to work with ErgoTree -Import `ErgoTreeObj` module, and `ErgoTree` class then use its methods. +Import `ErgoTree$` module, and `ErgoTree` class then use its methods. See examples in tests [ErgoTree.spec.js](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/79df4ca171a77233947d835042ce5c82ee520469/sigma-js/tests/js/ErgoTree.spec.js) ### Compile ErgoScript to ErgoTree diff --git a/sigma-js/sigmastate-js.d.ts b/sigma-js/sigmastate-js.d.ts index a71620b890..bacb52c197 100644 --- a/sigma-js/sigmastate-js.d.ts +++ b/sigma-js/sigmastate-js.d.ts @@ -145,7 +145,7 @@ declare module "sigmastate-js/main" { toString(): string; } - export declare class ErgoTreeObj { + export declare class ErgoTree$ { static fromHex(value: HexString): ErgoTree; } @@ -160,7 +160,7 @@ declare module "sigmastate-js/main" { export declare class SigmaProp { } - export declare class SigmaPropObj { + export declare class SigmaProp$ { static fromPointHex(value: HexString): SigmaProp; } diff --git a/sigma-js/tests/js/ErgoTree.spec.js b/sigma-js/tests/js/ErgoTree.spec.js index 688ea9bfa3..48058bc788 100644 --- a/sigma-js/tests/js/ErgoTree.spec.js +++ b/sigma-js/tests/js/ErgoTree.spec.js @@ -1,4 +1,4 @@ -const { ErgoTree, ErgoTreeObj } = require("sigmastate-js/main"); +const { ErgoTree, ErgoTree$ } = require("sigmastate-js/main"); describe("Smoke tests for API exporting", () => { it("Should export ErgoTree object", () => { @@ -10,13 +10,13 @@ describe("Smoke tests for ErgoTree", () => { let hex = "100604000e000400040005000500d803d601e30004d602e4c6a70408d603e4c6a7050595e67201d804d604b2a5e4720100d605b2db63087204730000d606db6308a7d60799c1a7c17204d1968302019683050193c27204c2a7938c720501730193e4c672040408720293e4c672040505720393e4c67204060ec5a796830201929c998c7205029591b1720673028cb272067303000273047203720792720773057202"; it("Should create fromHex", () => { - let tree = ErgoTreeObj.fromHex(hex); + let tree = ErgoTree$.fromHex(hex); expect(tree.toString()).not.toBeUndefined(); expect(tree.toHex()).toEqual(hex) }); it("Has properties", () => { - let tree = ErgoTreeObj.fromHex(hex); + let tree = ErgoTree$.fromHex(hex); expect(tree.header()).toEqual(0x10) expect(tree.version()).toEqual(0) @@ -25,13 +25,13 @@ describe("Smoke tests for ErgoTree", () => { }); it("Has constants", () => { - let tree = ErgoTreeObj.fromHex(hex); + let tree = ErgoTree$.fromHex(hex); let constants = tree.constants().map(c => c.toHex()) expect(constants).toEqual(["0400", "0e00", "0400", "0400", "0500", "0500"]) }); it("Has template", () => { - let tree = ErgoTreeObj.fromHex(hex); + let tree = ErgoTree$.fromHex(hex); let templateHex = tree.templateHex(); expect(templateHex).toEqual("d803d601e30004d602e4c6a70408d603e4c6a7050595e67201d804d604b2a5e4720100d605b2db63087204730000d606db6308a7d60799c1a7c17204d1968302019683050193c27204c2a7938c720501730193e4c672040408720293e4c672040505720393e4c67204060ec5a796830201929c998c7205029591b1720673028cb272067303000273047203720792720773057202") }); diff --git a/sigma-js/tests/js/SigmaProp.spec.js b/sigma-js/tests/js/SigmaProp.spec.js index 4c44f0f3aa..8191b24ccd 100644 --- a/sigma-js/tests/js/SigmaProp.spec.js +++ b/sigma-js/tests/js/SigmaProp.spec.js @@ -1,10 +1,10 @@ -const { SigmaPropObj, Value$ } = require("sigmastate-js/main"); +const { SigmaProp$, Value$ } = require("sigmastate-js/main"); let pointAsn1Hex = "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"; describe("SigmaProp", () => { it("should implement fromPointHex", () => { - let ge = SigmaPropObj.fromPointHex(pointAsn1Hex) + let ge = SigmaProp$.fromPointHex(pointAsn1Hex) expect(ge).not.toBeUndefined() let v = Value$.ofSigmaProp(pointAsn1Hex) diff --git a/sigma-js/tests/js/Value.spec.js b/sigma-js/tests/js/Value.spec.js index 5cd2fad21b..694ba2064b 100644 --- a/sigma-js/tests/js/Value.spec.js +++ b/sigma-js/tests/js/Value.spec.js @@ -1,4 +1,4 @@ -const { Type$, Value$, SigmaPropObj, SigmaProp} = require("sigmastate-js/main"); +const { Type$, Value$, SigmaProp$, SigmaProp} = require("sigmastate-js/main"); function testRange(factory, min, max) { expect(factory(max).data).toEqual(max); @@ -107,8 +107,8 @@ describe("Smoke tests for Values", () => { }); it("Value of type Coll[SigmaProp]", () => { - let sp1 = SigmaPropObj.fromPointHex(groupElementHex.substring(2)) - let sp2 = SigmaPropObj.fromPointHex(sigmaPropHex.substring(4)) + let sp1 = SigmaProp$.fromPointHex(groupElementHex.substring(2)) + let sp2 = SigmaProp$.fromPointHex(sigmaPropHex.substring(4)) let collV = Value$.collOf([sp1, sp2], Type$.SigmaProp) expect(collV.tpe.name).toEqual("Coll[SigmaProp]"); From bfefcd13349058a157a7a04fe6ae92a30aafeae9 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 29 Nov 2023 11:00:12 +0100 Subject: [PATCH 17/25] new-sigma-js: renamed SigmaPropProverObj -> SigmaPropProver$ --- .../src/main/scala/sigma/interpreter/js/SigmaPropProver.scala | 2 +- sigma-js/sigmastate-js.d.ts | 2 +- sigma-js/tests/js/SigmaPropProver.spec.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala index 30a79e9cbd..a04d32fa54 100644 --- a/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala +++ b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala @@ -98,7 +98,7 @@ class SigmaPropProver(override val wrappedValue: org.ergoplatform.SigmaPropProve } } -@JSExportTopLevel("SigmaPropProverObj") +@JSExportTopLevel("SigmaPropProver$") object SigmaPropProver extends js.Object { /** Creates a new [[SigmaPropProver]] with the given secrets. */ def withSecrets(secrets: js.Array[ProverSecret]): SigmaPropProver = { diff --git a/sigma-js/sigmastate-js.d.ts b/sigma-js/sigmastate-js.d.ts index bacb52c197..00c64b3d6a 100644 --- a/sigma-js/sigmastate-js.d.ts +++ b/sigma-js/sigmastate-js.d.ts @@ -432,7 +432,7 @@ declare module "sigmastate-js/main" { hintsBag: ProverHints): Int8Array } - export declare class SigmaPropProverObj { + export declare class SigmaPropProver$ { /** Creates a new [[SigmaPropProver]] with the given secrets. */ withSecrets(secrets: ProverSecret[]): SigmaPropProver } diff --git a/sigma-js/tests/js/SigmaPropProver.spec.js b/sigma-js/tests/js/SigmaPropProver.spec.js index d776de7477..4ee6918b56 100644 --- a/sigma-js/tests/js/SigmaPropProver.spec.js +++ b/sigma-js/tests/js/SigmaPropProver.spec.js @@ -1,5 +1,5 @@ const { - ProverSecretObj, SigmaPropProverObj, + ProverSecretObj, SigmaPropProver$, ProverHintsObj, SigmaPropVerifierObj } = require("sigmastate-js/main"); @@ -8,7 +8,7 @@ describe("SigmaPropProver", () => { let secret = ProverSecretObj.dlog(w) expect(secret.secret()).toEqual(w) - let p = SigmaPropProverObj.withSecrets([secret]) + let p = SigmaPropProver$.withSecrets([secret]) expect(p).not.toBeUndefined() it("generateCommitments", () => { From a60f999537312f4304f69a8d296dd36338f115bd Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 29 Nov 2023 11:01:57 +0100 Subject: [PATCH 18/25] new-sigma-js: renamed SigmaCompilerObj -> SigmaCompiler$ --- sc/js/src/main/scala/sigmastate/lang/js/SigmaCompiler.scala | 2 +- sigma-js/README.md | 2 +- sigma-js/sigmastate-js.d.ts | 2 +- sigma-js/tests/js/SigmaCompiler.spec.js | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sc/js/src/main/scala/sigmastate/lang/js/SigmaCompiler.scala b/sc/js/src/main/scala/sigmastate/lang/js/SigmaCompiler.scala index 07cd094bf8..8fe26a30b0 100644 --- a/sc/js/src/main/scala/sigmastate/lang/js/SigmaCompiler.scala +++ b/sc/js/src/main/scala/sigmastate/lang/js/SigmaCompiler.scala @@ -45,7 +45,7 @@ class SigmaCompiler(_compiler: sigmastate.lang.SigmaCompiler) extends js.Object } } -@JSExportTopLevel("SigmaCompilerObj") +@JSExportTopLevel("SigmaCompiler$") object SigmaCompiler extends js.Object { /** Creates a new instance of SigmaCompiler for the mainnet. */ def forMainnet(): SigmaCompiler = create(ErgoAddressEncoder.MainnetNetworkPrefix) diff --git a/sigma-js/README.md b/sigma-js/README.md index e89acf963b..f1db675fb4 100644 --- a/sigma-js/README.md +++ b/sigma-js/README.md @@ -94,5 +94,5 @@ See examples in tests [ErgoTree.spec.js](https://github.com/ScorexFoundation/sig ### Compile ErgoScript to ErgoTree -Import `SigmaCompilerObj` module and `SigmaCompiler` class, then use its methods. +Import `SigmaCompiler$` module and `SigmaCompiler` class, then use its methods. See compiler tests in [SigmaCompiler.spec.js](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/933acd7a3753725c8b41994c2126a20279b6809b/sigma-js/tests/js/SigmaCompiler.spec.js) diff --git a/sigma-js/sigmastate-js.d.ts b/sigma-js/sigmastate-js.d.ts index 00c64b3d6a..06d383f71d 100644 --- a/sigma-js/sigmastate-js.d.ts +++ b/sigma-js/sigmastate-js.d.ts @@ -317,7 +317,7 @@ declare module "sigmastate-js/main" { ): ErgoTree; } - export declare class SigmaCompilerObj { + export declare class SigmaCompiler$ { static forMainnet(): SigmaCompiler; static forTestnet(): SigmaCompiler; diff --git a/sigma-js/tests/js/SigmaCompiler.spec.js b/sigma-js/tests/js/SigmaCompiler.spec.js index 495ea09f51..312747d504 100644 --- a/sigma-js/tests/js/SigmaCompiler.spec.js +++ b/sigma-js/tests/js/SigmaCompiler.spec.js @@ -1,7 +1,7 @@ -const { Value$, SigmaCompilerObj } = require("sigmastate-js/main"); +const { Value$, SigmaCompiler$ } = require("sigmastate-js/main"); describe("Smoke tests for API exporting", () => { - let compiler = SigmaCompilerObj.forMainnet(); + let compiler = SigmaCompiler$.forMainnet(); it("Should create SigmaCompiler", () => { expect(compiler).not.toBeUndefined(); From f9999b731e6cc17e2301fc1be22084c9af960e15 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 29 Nov 2023 11:04:07 +0100 Subject: [PATCH 19/25] new-sigma-js: renamed ContractTemplateObj -> ContractTemplate$ --- .../main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala | 2 +- sigma-js/sigmastate-js.d.ts | 2 +- sigma-js/tests/js/ContractTemplate.spec.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala index 392cd32175..47d58ad8ec 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala @@ -103,7 +103,7 @@ class ContractTemplate( } } -@JSExportTopLevel("ContractTemplateObj") +@JSExportTopLevel("ContractTemplate$") object ContractTemplate extends js.Object { /** Create a new contract template from a JSON string. diff --git a/sigma-js/sigmastate-js.d.ts b/sigma-js/sigmastate-js.d.ts index 06d383f71d..16cd0939cd 100644 --- a/sigma-js/sigmastate-js.d.ts +++ b/sigma-js/sigmastate-js.d.ts @@ -653,7 +653,7 @@ declare module "sigmastate-js/main" { paramValues: SigmaCompilerNamedConstantsMap): ErgoTree } - export declare class ContractTemplateObj { + export declare class ContractTemplate$ { /** Create a new contract template from a JSON string. * * @param json JSON string representing a contract template. diff --git a/sigma-js/tests/js/ContractTemplate.spec.js b/sigma-js/tests/js/ContractTemplate.spec.js index 1b2c5c3ef5..fc45198549 100644 --- a/sigma-js/tests/js/ContractTemplate.spec.js +++ b/sigma-js/tests/js/ContractTemplate.spec.js @@ -1,4 +1,4 @@ -const {ContractTemplate, ContractTemplateObj, Value$} = require("sigmastate-js/main"); +const {ContractTemplate, ContractTemplate$, Value$} = require("sigmastate-js/main"); describe("Smoke tests for API exporting", () => { it("Should export ContractTempate object", () => { @@ -41,7 +41,7 @@ describe("ContractTemplate", () => { " \"expressionTree\" : \"d1939a730073017302\"\n" + "}"; - let template = ContractTemplateObj.fromJsonString(templateJsonStr); + let template = ContractTemplate$.fromJsonString(templateJsonStr); it("Json encoding roundtrip", () => { expect(template).not.toBeUndefined(); From 288f3c23295af779520c2065c02fa60106cea691 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 29 Nov 2023 11:06:43 +0100 Subject: [PATCH 20/25] new-sigma-js: renamed AddressObj -> Address$ --- .../src/main/scala/org/ergoplatform/js/Address.scala | 2 +- sigma-js/sigmastate-js.d.ts | 2 +- sigma-js/tests/js/Address.spec.js | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/data/js/src/main/scala/org/ergoplatform/js/Address.scala b/data/js/src/main/scala/org/ergoplatform/js/Address.scala index bdb520f153..0978db5494 100644 --- a/data/js/src/main/scala/org/ergoplatform/js/Address.scala +++ b/data/js/src/main/scala/org/ergoplatform/js/Address.scala @@ -101,7 +101,7 @@ abstract class Address extends js.Object { } /** An exported JavaScript object providing utility methods for working with Address instances. */ -@JSExportTopLevel("AddressObj") +@JSExportTopLevel("Address$") object Address extends js.Object { /** Creates JS wrapper over given [[ErgoAddress]]. */ def fromErgoAddress(ergoAddress: org.ergoplatform.ErgoAddress): Address = { diff --git a/sigma-js/sigmastate-js.d.ts b/sigma-js/sigmastate-js.d.ts index 16cd0939cd..09f3339e26 100644 --- a/sigma-js/sigmastate-js.d.ts +++ b/sigma-js/sigmastate-js.d.ts @@ -70,7 +70,7 @@ declare module "sigmastate-js/main" { toString(): string; } - export declare class AddressObj { + export declare class Address$ { /** Creates JS wrapper over given [[ErgoAddress]]. */ static fromErgoAddress(ergoAddress: any /*org.ergoplatform.ErgoAddress*/): Address; diff --git a/sigma-js/tests/js/Address.spec.js b/sigma-js/tests/js/Address.spec.js index 3cdfe2b001..0e78434cf0 100644 --- a/sigma-js/tests/js/Address.spec.js +++ b/sigma-js/tests/js/Address.spec.js @@ -1,4 +1,4 @@ -const { Address, AddressObj } = require("sigmastate-js/main"); +const { Address, Address$ } = require("sigmastate-js/main"); describe("Smoke tests for API exporting", () => { it("Should export Address object", () => { @@ -11,7 +11,7 @@ describe("Address", () => { let p2sStr = "JryiCXrZf5VDetH1PM7rKDX3q4sLF34AdErWJFqG87Hf5ikTDf636b35Nr7goWMdRUKA3ZPxdeqFNbQzBjhnDR9SUMYwDX1tdV8ZuGgXwQPaRVcB9" it("roundtrip for P2PK", () => { - let addr = AddressObj.fromString(addrStr); + let addr = Address$.fromString(addrStr); expect(addr.isP2PK()).toEqual(true) expect(addr.isP2S()).toEqual(false) @@ -21,7 +21,7 @@ describe("Address", () => { }); it("roundtrip for P2S", () => { - let addr = AddressObj.fromString(p2sStr); + let addr = Address$.fromString(p2sStr); expect(addr.isP2S()).toEqual(true) expect(addr.isP2PK()).toEqual(false) @@ -31,14 +31,14 @@ describe("Address", () => { }); it("toSigmaPropOpt", () => { - let addr = AddressObj.fromString(addrStr); + let addr = Address$.fromString(addrStr); expect(addr.isMainnet()).toEqual(true) expect(addr.toSigmaPropOpt()).not.toBeUndefined() }); it("other properties", () => { - let addr = AddressObj.fromString(addrStr); + let addr = Address$.fromString(addrStr); expect(addr.toErgoTree()).not.toBeUndefined() expect(addr.asP2PK()).not.toBeUndefined() expect(addr.asP2PK().isP2PK()).toEqual(true) From 355f3f0baccea37bf1a97d10462219d9daa7a4fc Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 29 Nov 2023 11:17:56 +0100 Subject: [PATCH 21/25] new-sigma-js: renamed Obj -> $ for ProverHints, ProverSecret, SigmaPropVerifier, ProverBuilder, Expr, ReducedTransaction --- data/js/src/main/scala/sigma/ast/js/Expr.scala | 2 +- .../main/scala/sigma/interpreter/js/ProverHints.scala | 2 +- .../main/scala/sigma/interpreter/js/ProverSecret.scala | 2 +- .../scala/sigma/interpreter/js/SigmaPropVerifier.scala | 2 +- .../scala/org/ergoplatform/sdk/js/ProverBuilder.scala | 2 +- .../org/ergoplatform/sdk/js/ReducedTransaction.scala | 2 +- sigma-js/sigmastate-js.d.ts | 10 +++++----- sigma-js/tests/js/SigmaPropProver.spec.js | 10 +++++----- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/data/js/src/main/scala/sigma/ast/js/Expr.scala b/data/js/src/main/scala/sigma/ast/js/Expr.scala index 8bd087a16c..54a78f4025 100644 --- a/data/js/src/main/scala/sigma/ast/js/Expr.scala +++ b/data/js/src/main/scala/sigma/ast/js/Expr.scala @@ -20,7 +20,7 @@ class Expr(override val wrappedValue: SValue) extends JsWrapper[SValue]{ } } -@JSExportTopLevel("ExprObj") +@JSExportTopLevel("Expr$") object Expr extends js.Object { /** Deserialize an expression from bytes using sigma serializer [[ValueSerializer]]. */ def fromBytes(bytes: Int8Array): Expr = { diff --git a/interpreter/js/src/main/scala/sigma/interpreter/js/ProverHints.scala b/interpreter/js/src/main/scala/sigma/interpreter/js/ProverHints.scala index f349e05726..45047d4ec8 100644 --- a/interpreter/js/src/main/scala/sigma/interpreter/js/ProverHints.scala +++ b/interpreter/js/src/main/scala/sigma/interpreter/js/ProverHints.scala @@ -14,7 +14,7 @@ class ProverHints(override val wrappedValue: HintsBag) extends JsWrapper[HintsBag] { } -@JSExportTopLevel("ProverHintsObj") +@JSExportTopLevel("ProverHints$") object ProverHints extends js.Object { private lazy val _empty = new ProverHints(HintsBag.empty) diff --git a/interpreter/js/src/main/scala/sigma/interpreter/js/ProverSecret.scala b/interpreter/js/src/main/scala/sigma/interpreter/js/ProverSecret.scala index 4b8c9741cf..176341587c 100644 --- a/interpreter/js/src/main/scala/sigma/interpreter/js/ProverSecret.scala +++ b/interpreter/js/src/main/scala/sigma/interpreter/js/ProverSecret.scala @@ -23,7 +23,7 @@ class ProverSecret( def secret(): js.BigInt = Isos.isoBigInt.from(CBigInt(wrappedValue.w)) } -@JSExportTopLevel("ProverSecretObj") +@JSExportTopLevel("ProverSecret$") object ProverSecret extends js.Object { /** Creates a new [[ProverSecret]] instance for the given secret of descrete logarithm * sigma protocol. diff --git a/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropVerifier.scala b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropVerifier.scala index 9c58fac8a8..caddc37e1a 100644 --- a/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropVerifier.scala +++ b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropVerifier.scala @@ -33,7 +33,7 @@ class SigmaPropVerifier(override val wrappedValue: org.ergoplatform.SigmaPropVer } } -@JSExportTopLevel("SigmaPropVerifierObj") +@JSExportTopLevel("SigmaPropVerifier$") object SigmaPropVerifier extends js.Object { /** Create a new instance of SigmaPropVerifier. */ def create(): SigmaPropVerifier = { diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala index 1a27c6a655..3f22a6d59e 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala @@ -96,7 +96,7 @@ class ProverBuilder(parameters: BlockchainParameters, network: Byte) extends js. } } -@JSExportTopLevel("ProverBuilderObj") +@JSExportTopLevel("ProverBuilder$") object ProverBuilder extends js.Object { /** Creates a new [[ProverBuilder]] instance with the given blockchain parameters * and network prefix. diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ReducedTransaction.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ReducedTransaction.scala index a27c61d261..c01a61adb8 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ReducedTransaction.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ReducedTransaction.scala @@ -12,7 +12,7 @@ class ReducedTransaction(val _tx: sdk.ReducedTransaction) extends js.Object { def toHex(): String = _tx.toHex } -@JSExportTopLevel("ReducedTransactionObj") +@JSExportTopLevel("ReducedTransaction$") object ReducedTransaction extends js.Object { /** Creates a [[ReducedTransaction]] from serialized bytes in hex format. */ def fromHex(hex: String): ReducedTransaction = { diff --git a/sigma-js/sigmastate-js.d.ts b/sigma-js/sigmastate-js.d.ts index 09f3339e26..b70ca39258 100644 --- a/sigma-js/sigmastate-js.d.ts +++ b/sigma-js/sigmastate-js.d.ts @@ -329,7 +329,7 @@ declare module "sigmastate-js/main" { toHex(): HexString; } - export declare class ReducedTransactionObj { + export declare class ReducedTransaction$ { /** Creates a {@link ReducedTransaction} from serialized bytes in hex format. */ fromHex(hex: HexString): ReducedTransaction; } @@ -341,7 +341,7 @@ declare module "sigmastate-js/main" { } - export declare class ProverHintsObj { + export declare class ProverHints$ { /** Empty bag of hints. Immutable value can be reused where necessary. */ empty(): ProverHints } @@ -357,7 +357,7 @@ declare module "sigmastate-js/main" { secret(): bigint } - export declare class ProverSecretObj { + export declare class ProverSecret$ { /** Creates a new [[ProverSecret]] instance for the given secret of descrete logarithm * sigma protocol. * @param w secret exponent value @@ -458,7 +458,7 @@ declare module "sigmastate-js/main" { message: Int8Array, signature: Int8Array): boolean } - export declare class SigmaPropVerifierObj { + export declare class SigmaPropVerifier$ { /** Create a new instance of [[SigmaPropVerifier]]. */ create(): SigmaPropVerifier } @@ -568,7 +568,7 @@ declare module "sigmastate-js/main" { build(): SigmaProver; } - export declare class ProverBuilderObj { + export declare class ProverBuilder$ { static create(parameters: BlockchainParameters, network: number): ProverBuilder; } diff --git a/sigma-js/tests/js/SigmaPropProver.spec.js b/sigma-js/tests/js/SigmaPropProver.spec.js index 4ee6918b56..4703f75b57 100644 --- a/sigma-js/tests/js/SigmaPropProver.spec.js +++ b/sigma-js/tests/js/SigmaPropProver.spec.js @@ -1,11 +1,11 @@ const { - ProverSecretObj, SigmaPropProver$, - ProverHintsObj, SigmaPropVerifierObj + ProverSecret$, SigmaPropProver$, + ProverHints$, SigmaPropVerifier$ } = require("sigmastate-js/main"); describe("SigmaPropProver", () => { let w = 0xadf47e32000fc75e2923dba482c843c7f6b684cbf2ceec5bfdf5fe6d13cabe5dn - let secret = ProverSecretObj.dlog(w) + let secret = ProverSecret$.dlog(w) expect(secret.secret()).toEqual(w) let p = SigmaPropProver$.withSecrets([secret]) @@ -18,11 +18,11 @@ describe("SigmaPropProver", () => { it("signMessage", () => { let message = Int8Array.of(1, 2, 3) - let signature = p.signMessage(secret.publicKey(), message, ProverHintsObj.empty()) + let signature = p.signMessage(secret.publicKey(), message, ProverHints$.empty()) expect(signature).not.toBeUndefined() expect(signature.length).toBeGreaterThan(0) - let V = SigmaPropVerifierObj.create() + let V = SigmaPropVerifier$.create() let ok = V.verifySignature(secret.publicKey(), message, signature) expect(ok).toEqual(true) }); From c11c707e89ed49a23cf8a0b6daed1b39fa2ced3b Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 29 Nov 2023 11:34:43 +0100 Subject: [PATCH 22/25] new-sigma-js: sigma-js/README.md updated --- sigma-js/README.md | 90 ++++++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/sigma-js/README.md b/sigma-js/README.md index f1db675fb4..ff867e1cb6 100644 --- a/sigma-js/README.md +++ b/sigma-js/README.md @@ -20,7 +20,7 @@ Run following command to add Sigma.JS as a project dependency: npm install sigmastate-js ``` -# Package organization +## Package organization All classes of this package are separated into several modules (which can also be thought as layers). Each module contains a subset of all the class exported to JavaScript. You can @@ -35,64 +35,76 @@ methods and documentation. NOTE, you only need to import only one of the modules, the one which contains all the classes you need. This will allow optimizing the size of the final bundle. -The modules are: -- [sigma-core module](https://github.com/ScorexFoundation/sigmastate-interpreter/tree/b26930c5e7aa58b6d76dda96ab56db59825f8638/core) - contains core classes of the library - - [Type](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/b26930c5e7aa58b6d76dda96ab56db59825f8638/core/js/src/main/scala/sigma/js/Type.scala) - - [Value](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/2d767ae75ab233deefeba25e42ca22ae22be8952/core/js/src/main/scala/sigma/js/Value.scala) - - [GroupElement](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/4fcd2a71f94d6a0e5a1922817dba02e5657558e1/core/js/src/main/scala/sigma/js/GroupElement.scala) - - [SigmaProp](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/4fcd2a71f94d6a0e5a1922817dba02e5657558e1/core/js/src/main/scala/sigma/js/SigmaProp.scala) - - [AvlTree](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/4fcd2a71f94d6a0e5a1922817dba02e5657558e1/core/js/src/main/scala/sigma/js/AvlTree.scala) +The modules are compiled from Scala classes, which are exported to JavaScript (here is an +[example](../core/js/src/main/scala/sigma/js/Type.scala)). +The Scala declarations correspond to the TypeScript definitions. +Each exported type have two parts: the first part is a Scala class, the second part is a +companion object. In Scala the companion object is used to declare static methods and has +the same name as the corresponding class. In TypeScript the companion object is exported +with `$` suffix, thus if X is the JS class, then X$ is the JS object, which corresponds to X. + +## The list of modules and their exported classes +- [sigma-core module](../core/js) - contains core classes of Sigma.js library + - [Type](../core/js/src/main/scala/sigma/js/Type.scala) + - [Value](../core/js/src/main/scala/sigma/js/Value.scala) + - [GroupElement](../core/js/src/main/scala/sigma/js/GroupElement.scala) + - [SigmaProp](../core/js/src/main/scala/sigma/js/SigmaProp.scala) + - [AvlTree](../core/js/src/main/scala/sigma/js/AvlTree.scala) -- [sigma-data module](https://github.com/ScorexFoundation/sigmastate-interpreter/tree/b26930c5e7aa58b6d76dda96ab56db59825f8638/data) - contains classes for working with ErgoTree, addresses and all related serializers +- [sigma-data module](../data/js) - contains classes for working with ErgoTree, addresses and all related serializers - all classes from sigma-core module - - [ErgoTree](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/b26930c5e7aa58b6d76dda96ab56db59825f8638/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala) - - [Address](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/b745c5fd2257abc6d4317d9761394eb0ea0f3f4e/data/js/src/main/scala/org/ergoplatform/js/Address.scala) - - [Expr]() + - [ErgoTree](../data/js/src/main/scala/sigma/ast/js/ErgoTree.scala) + - [Address](../data/js/src/main/scala/org/ergoplatform/js/Address.scala) + - [Expr](../data/js/src/main/scala/sigma/ast/js/Expr.scala) -- [sigma-interpreter module]() - contains classes for working with ErgoTree interpreter +- [sigma-interpreter module](../interpreter/js) - contains classes for proving sigma proposition and their verification - all classes from sigma-data module - - [ProverHints]() - - [ProverSecret]() - - [SigmaPropProver]() - - [SigmaPropVerifier]() + - [ProverHints](../interpreter/js/src/main/scala/sigma/interpreter/js/ProverHints.scala) + - [ProverSecret](../interpreter/js/src/main/scala/sigma/interpreter/js/ProverSecret.scala) + - [SigmaPropProver](../interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala) + - [SigmaPropVerifier](../interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropVerifier.scala) -- [sigma-sdk module](https://github.com/ScorexFoundation/sigmastate-interpreter/tree/6d774a34118b6fac4e70b58c29343afb1b261460/sdk) - contains classes for working with ErgoTree interpreter +- [sigma-sdk module](../sdk/js) - contains classes for reducing and signing transactions - all classes from sigma-interpreter module - - [BlockchainParameters](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/ce203cca487c0a2476504f8a11e7a94ba8ef61b5/sdk/js/src/main/scala/org/ergoplatform/sdk/js/BlockchainParameters.scala) - - [BlockchainStateContext](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/ce203cca487c0a2476504f8a11e7a94ba8ef61b5/sdk/js/src/main/scala/org/ergoplatform/sdk/js/BlockchainStateContext.scala) - - [ContractTemplate](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/6d774a34118b6fac4e70b58c29343afb1b261460/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala) - - [Header](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/4fcd2a71f94d6a0e5a1922817dba02e5657558e1/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Header.scala) - - [PreHeader](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/4fcd2a71f94d6a0e5a1922817dba02e5657558e1/sdk/js/src/main/scala/org/ergoplatform/sdk/js/PreHeader.scala) - - [ProverBuilder](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/2a77625cd65a39f29fa56aa0e3c9c46cbe038363/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala) - - [ReducedTransaction](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/fff394ff28ec5530a6535effedd927f2eb297fc0/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ReducedTransaction.scala) - - [SigmaProver](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/9cdcbde6c77436f154e256c846e8f54aa00bff15/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala) - -- [sigma-compiler module]() - contains classes for working with ErgoScript compiler - - [SigmaCompiler](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/aae4118fed18f6587413d9a6330e449b05d8d5ad/sc/js/src/main/scala/sigmastate/lang/js/SigmaCompiler.scala) + - [BlockchainParameters](../sdk/js/src/main/scala/org/ergoplatform/sdk/js/BlockchainParameters.scala) + - [BlockchainStateContext](../sdk/js/src/main/scala/org/ergoplatform/sdk/js/BlockchainStateContext.scala) + - [ContractTemplate](../sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala) + - [Header](../sdk/js/src/main/scala/org/ergoplatform/sdk/js/Header.scala) + - [PreHeader](../sdk/js/src/main/scala/org/ergoplatform/sdk/js/PreHeader.scala) + - [ProverBuilder](../sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala) + - [ReducedTransaction](../sdk/js/src/main/scala/org/ergoplatform/sdk/js/ReducedTransaction.scala) + - [SigmaProver](../sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala) + +- [sigma-compiler module](../sc/js) - contains classes for working with ErgoScript compiler + - [SigmaCompiler](../sc/js/src/main/scala/sigmastate/lang/js/SigmaCompiler.scala) -# Examples +## Examples ### How to create Sigma type descriptors -Import `TypeObj` module, then use: +Import `Type$` module, then use its fields to access pre-defined descriptors of simple +types (e.g. `Type$.Int`). -- fields to create simple types (e.g. `TypeObj.Int`) -- method `TypeObj.pairType` (e.g. `TypeObj.pairType(TypeObj.Int, TypeObj.Long)`) -- method `TypeObj.collType` (e.g. `TypeObj.collType(TypeObj.Int)`) +Use factory methods like `Type$.pairType` to create more complex type descriptors. For +example,`Type$.pairType(Type$.Int, Type$.Long)` will create a descriptor of a pair of Int +and Long types `(Int, Long)`. -See examples in tests [Type.spec.js](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/933acd7a3753725c8b41994c2126a20279b6809b/sigma-js/tests/js/Type.spec.js) +See also examples in tests [Type.spec.js](tests/js/Type.spec.js) ### How to create Sigma values -Import `Value$` module, then use its methods. -See examples in tests [Value.spec.js](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/933acd7a3753725c8b41994c2126a20279b6809b/sigma-js/tests/js/Value.spec.js) +Import `Value$` module, then use its factory methods. +See examples in tests [Value.spec.js](tests/js/Value.spec.js) ### How to work with ErgoTree Import `ErgoTree$` module, and `ErgoTree` class then use its methods. -See examples in tests [ErgoTree.spec.js](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/79df4ca171a77233947d835042ce5c82ee520469/sigma-js/tests/js/ErgoTree.spec.js) +See examples in tests [ErgoTree.spec.js](tests/js/ErgoTree.spec.js) ### Compile ErgoScript to ErgoTree Import `SigmaCompiler$` module and `SigmaCompiler` class, then use its methods. -See compiler tests in [SigmaCompiler.spec.js](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/933acd7a3753725c8b41994c2126a20279b6809b/sigma-js/tests/js/SigmaCompiler.spec.js) +See compiler tests in [SigmaCompiler.spec.js](tests/js/SigmaCompiler.spec.js) + +### Other examples +See tests in [tests/js](tests/js) folder. From 0bf3eb0ec43db9b8fee0f9c57fda74f385be7afb Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 29 Nov 2023 15:09:18 +0100 Subject: [PATCH 23/25] new-sigma-js: cleanups --- .../js/src/main/scala/sigma/interpreter/js/SigmaLeaf.scala | 6 ------ .../main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala | 1 - .../ergoplatform/sdk/ContractTemplateSpecification.scala | 3 +-- 3 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 interpreter/js/src/main/scala/sigma/interpreter/js/SigmaLeaf.scala diff --git a/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaLeaf.scala b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaLeaf.scala deleted file mode 100644 index 100f6c73d8..0000000000 --- a/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaLeaf.scala +++ /dev/null @@ -1,6 +0,0 @@ -package sigma.interpreter.js - -import sigma.js.JsWrapper - -class SigmaLeaf(override val wrappedValue: sigma.data.SigmaLeaf) extends JsWrapper[sigma.data.SigmaLeaf] { -} diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala index 3f22a6d59e..12ecf1d03f 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala @@ -6,7 +6,6 @@ import org.ergoplatform.sdk.SecretString import scala.scalajs.js import scala.scalajs.js.annotation.JSExportTopLevel import Isos._ -import sigma.js.GroupElement import sigma.eval.SigmaDsl /** Equivalent of [[sdk.ProverBuilder]] available from JS. diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala index b7b1a6c14b..dace012c9a 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala @@ -234,12 +234,11 @@ class ContractTemplateSpecification extends SerializationSpecification ) ) - templates.indices.foreach(i => { + templates.indices.foreach { i => val template = templates(i) val applied = template.applyTemplate(Some(ergoTreeVersionInTests), templateValues(i)) applied shouldEqual expectedErgoTree(i) } - ) } property("applyTemplate num(parameters) < num(constants)") { From 84174fc442b95f1d71b6d2c39e27da7830fd3ce5 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Tue, 12 Dec 2023 21:12:34 +0100 Subject: [PATCH 24/25] new-sigma-js: updated README.md --- README.md | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 64f08f0277..6003daf55a 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,10 @@ For development of Ergo applications using JVM languages (Java/Scala/Kotlin/etc) a better alternative is to use [Appkit](https://github.com/ergoplatform/ergo-appkit). +The library is cross-compiled to JS using Scala.js and the main abstractions can be used +from JS directly by importing [NPM module](https://www.npmjs.com/package/sigmastate-js). +See [README](sigma-js/README.md) for details. + ## Sigma Language Background Every coin in Bitcoin is protected by a program in the stack-based Script @@ -93,19 +97,21 @@ This library is on Maven repository and can be added to the SBT configuration of Scala project. ```scala -libraryDependencies += "org.scorexfoundation" %% "sigma-state" % "5.0.6" +libraryDependencies += "org.scorexfoundation" %% "sigma-state" % "5.0.14" ``` ## Repository Organization -| sub-module | description | -|-------------|-------------------------------------------------------------------------------------------| -| common | Used in all other submodules and contain basic utility classes | -| core-lib | Contains core classes such as Coll, BigInt used by interpreter | -| docs | Collection of documents | -| graph-ir | Implementation of graph-based intermediate representation of ErgoTree, which is used in by ErgoScript compiler | -| interpreter | Implementation of ErgoTree Interpreter | -| sc | Implementation of ErgoScript compiler | +| sub-module | description | +|-------------|------------------------------------------------------------------------------------| +| core | contains core classes of Sigma library | +| data | contains classes for working with ErgoTree, addresses and all related serializers | +| docs | Collection of documents | +| interpreter | contains an implementation of ErgoTree Interpreter | +| sdk | contains and implementation of transaction reduction and signing | +| parsers | contains an implementation of ErgoScript parsers using FastParse library | +| sc | contains an implementation of ErgoScript compiler | +| sigma-js | root directory of sigmastate-js JS module (see [package.json](sigma-js/README.md)) | ## Contributing @@ -151,12 +157,13 @@ innovative and intelligent tools for profiling Java and .NET applications. - [Ergo Site](https://ergoplatform.org/en/) - [Ergo Sources](https://github.com/ergoplatform/ergo) +- [Sigma-js](https://www.npmjs.com/package/sigmastate-js) - [Ergo Appkit](https://github.com/ergoplatform/ergo-appkit) - [Ergo Appkit Examples](https://github.com/aslesarenko/ergo-appkit-examples) - [ergo-android](https://github.com/aslesarenko/ergo-android) - [ergo-wallet-android](https://github.com/MrStahlfelge/ergo-wallet-android) - [ErgoTree Specification](https://ergoplatform.org/docs/ErgoTree.pdf) -- [Ergo Documents](https://ergoplatform.org/en/documents/) +- [Ergo Documents](https://docs.ergoplatform.org/) From db726771cda5edbacc158fe581429077c12e7897 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Tue, 12 Dec 2023 21:13:22 +0100 Subject: [PATCH 25/25] new-sigma-js: fixed export of Parameter --- .../main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala index 47d58ad8ec..7b3d1dd19a 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala @@ -27,7 +27,7 @@ class Parameter( val constantIndex: Int ) extends js.Object -@JSExportTopLevel("ParameterObj") +@JSExportTopLevel("Parameter$") object Parameter extends js.Object { implicit val isoToSdk: sigma.data.Iso[Parameter, sdk.Parameter] = new sigma.data.Iso[Parameter, sdk.Parameter] {