Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transaction Signing with multisig #1000

Draft
wants to merge 24 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9d5f577
sign-tx-multisig: added Wallet with generateCommitments
aslesarenko Jun 4, 2024
4767546
sign-tx-multisig: passing signature hints to proveReduced
aslesarenko Jun 4, 2024
c272365
sign-tx-multisig: cleanup
aslesarenko Jun 4, 2024
89e69a0
sign-tx-multisig: more cleanup
aslesarenko Jun 4, 2024
a98616f
sign-tx-multisig: use TransactionHintsBag in js.SigmaProver
aslesarenko Jun 4, 2024
e2aebb5
sign-tx-multisig: fix JS compilation
aslesarenko Jun 4, 2024
8e96bea
sign-tx-multisig: TransactionHintsBag iso tests
aslesarenko Jun 4, 2024
d11bcac
sign-tx-multisig: updateed sigmastate-js.d.ts and bumped version v0.4.3
aslesarenko Jun 5, 2024
b7b61de
sign-tx-multisig: impemented signReduced for single input
aslesarenko Jun 5, 2024
04e7cb6
sign-tx-multisig: implemented reduceTransactionInput
aslesarenko Jun 5, 2024
5d74e00
sign-tx-multisig: declare methods in sigmastate-js.d.ts
aslesarenko Jun 5, 2024
b1a87e2
sign-tx-multisig: fix compilation
aslesarenko Jun 5, 2024
3f472fd
sign-tx-multisig: fix liftableFromElem for SigmaProp
aslesarenko Jun 5, 2024
bb1cc23
sign-tx-multisig: bump sigma-js version v0.4.4
aslesarenko Jun 8, 2024
5dbe46a
sign-tx-multisig: added js.AvlTree.fromDigest method
aslesarenko Jun 8, 2024
3978771
sign-tx-multisig: tests for js.AvlTree.fromDigest
aslesarenko Jun 8, 2024
b3f3204
sign-tx-multisig: bump sigma-js version v0.4.5
aslesarenko Jun 8, 2024
4e37dff
sign-tx-multisig: updated links in sigma-js/README
aslesarenko Jun 8, 2024
732c007
sign-tx-multisig: fix for Scala 2.11
aslesarenko Jun 8, 2024
f44818f
sigma-js-reflection: add missing metadata
aslesarenko Jun 11, 2024
7017faf
sigma-js-reflection: fix compilation Scala 2.11
aslesarenko Jun 11, 2024
f1697d9
sigma-js-reflection: cleanup
aslesarenko Jun 11, 2024
9a939a0
Merge remote-tracking branch 'refs/remotes/origin/sigma-js-reflection…
aslesarenko Jun 11, 2024
ab41548
sign-tx-multisig: bump sigma-js version v0.4.6
aslesarenko Jun 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions core/js/src/main/scala/sigma/js/AvlTree.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package sigma.js

import sigma.Extensions.ArrayOps
import scorex.util.encode.Base16
import sigma.Extensions.{ArrayOps, TryOps}
import sigma.data.Iso.{isoStringToArray, isoStringToColl}
import sigma.data.{AvlTreeData, AvlTreeFlags, CAvlTree, Iso}
import sigmastate.fleetSdkCommon.distEsmTypesCommonMod.HexString

import scala.scalajs.js
import scala.scalajs.js.UndefOr
Expand All @@ -19,7 +21,23 @@ class AvlTree(
val valueLengthOpt: UndefOr[Int]
) extends js.Object

object AvlTree {
@JSExportTopLevel("AvlTree$")
object AvlTree extends js.Object {

/** Size of the digest in bytes = hash size + 1 byte for the tree height */
val DigestSize: Int = AvlTreeData.DigestSize

/**
* Creates an [[AvlTree]] instance.
*
* @param digestHex A hexadecimal string representing the digest of the [[AvlTree]].
* @returns An AvlTree instance with the specified digest and all operations (insert, update, remove) enabled.
*/
def fromDigest(digestHex: HexString): AvlTree = {
val digestBytes = Base16.decode(digestHex).getOrThrow.toColl
val treeData = AvlTreeData.avlTreeFromDigest(digestBytes)
isoAvlTree.from(CAvlTree(treeData))
}

implicit val isoAvlTree: Iso[AvlTree, sigma.AvlTree] = new Iso[AvlTree, sigma.AvlTree] {
override def to(x: AvlTree): sigma.AvlTree = {
Expand Down
12 changes: 11 additions & 1 deletion core/js/src/main/scala/sigma/js/SigmaProp.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package sigma.js

import sigma.data.{ProveDHTuple, ProveDlog, SigmaBoolean}
import sigma.data.{CSigmaProp, Iso, ProveDHTuple, ProveDlog, SigmaBoolean}
import sigma.util.Extensions.SigmaPropOps

import scala.scalajs.js
import scala.scalajs.js.annotation.JSExportTopLevel
Expand Down Expand Up @@ -32,4 +33,13 @@ object SigmaProp extends js.Object {
def dht(g: GroupElement, h: GroupElement, u: GroupElement, v: GroupElement): SigmaProp = {
new SigmaProp(ProveDHTuple(g.point, h.point, u.point, v.point))
}

val isoToSdk = new Iso[SigmaProp, sigma.SigmaProp] {
override def to(a: SigmaProp): sigma.SigmaProp = {
CSigmaProp(a.sigmaBoolean)
}
override def from(b: sigma.SigmaProp): SigmaProp = {
new SigmaProp(b.toSigmaBoolean)
}
}
}
2 changes: 1 addition & 1 deletion core/jvm/src/main/scala/sigma/reflection/Platform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ object Platform {
// Should be used only for debugging and never in production.
// /** Check class registration. Should be used only for debugging. */
// def checkRegisteredClass[T](clazz: Class[T]): Unit = {
// CommonReflection.classes.get(clazz) match {
// ReflectionData.classes.get(clazz) match {
// case Some(c) =>
// assert(c.clazz == clazz)
// case _ =>
Expand Down
72 changes: 72 additions & 0 deletions core/shared/src/main/scala/sigma/Extensions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import scorex.util.encode.Base16
import scorex.util.{ModifierId, bytesToId}
import sigma.data.RType

import scala.util.control.NonFatal
import scala.util.{Failure, Success, Try}

/** Declaration of extension methods introduced in `sigma-core` module.
* See `implicit class ...` wrappers below.
*/
Expand Down Expand Up @@ -56,4 +59,73 @@ object Extensions {
}
}

/** Extensions methods on [[Try]]. */
implicit class TryOps[+A](val source: Try[A]) extends AnyVal {

/** This extension is required only for Scala 2.11 where this method is not available in the standard library.
* Applies `fa` if this is a `Failure` or `fb` if this is a `Success`.
* If `fb` is initially applied and throws an exception,
* then `fa` is applied with this exception.
*
* @example {{{
* val result: Try[Int] = Try { string.toInt }
* log(result.fold(
* ex => "Operation failed with " + ex,
* v => "Operation produced value: " + v
* ))
* }}}
*
* @param fa the function to apply if this is a `Failure`
* @param fb the function to apply if this is a `Success`
* @return the results of applying the function
*/
def fold[B](fa: Throwable => B, fb: A => B) = source match {
case Success(value) =>
try { fb(value) } catch { case NonFatal(e) => fa(e) }
case Failure(t) => fa(t)
}

/**
* Returns `Left` with `Throwable` if this is a `Failure`, otherwise returns `Right` with `Success` value.
* This extension is required only for Scala 2.11 where this method is not available in the standard library.
*/
def toEither: Either[Throwable, A] = source match {
case Success(value) => Right(value)
case Failure(t) => Left(t)
}

/** Maps [[Success]] or throws the exception in the `source` instance. */
def mapOrThrow[B](f: A => B): B = source.fold(t => throw t, f)

/** Gets the [[Success]] value or throws the exception in the `source` instance. */
def getOrThrow: A = source.fold(t => throw t, identity)
}

implicit class EitherOps[+A, +B](val source: Either[A, B]) extends AnyVal {
/** The given function is applied if this is a `Right`.
*
* {{{
* Right(12).map(x => "flower") // Result: Right("flower")
* Left(12).map(x => "flower") // Result: Left(12)
* }}}
*/
def mapRight[B1](f: B => B1): Either[A, B1] = source match {
case Right(b) => Right(f(b))
case _ => this.asInstanceOf[Either[A, B1]]
}

/** Returns a `Some` containing the `Right` value
* if it exists or a `None` if this is a `Left`.
*
* {{{
* Right(12).toOption // Some(12)
* Left(12).toOption // None
* }}}
*/
def toOption: Option[B] = source match {
case Right(value) => Some(value)
case _ => None
}
}

}
2 changes: 2 additions & 0 deletions core/shared/src/main/scala/sigma/data/AvlTreeData.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ case class AvlTreeData(digest: Coll[Byte],
valueLengthOpt: Option[Int] = None)

object AvlTreeData {
/** Size of the digest in bytes = hash size + 1 byte for the tree height */
val DigestSize: Int = crypto.hashLength + 1 //please read class comments above for details

val TreeDataSize = DigestSize + 3 + 4 + 4

val dummy = new AvlTreeData(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ object ReflectionData {
*/
def registerClassEntry[T](clazz: Class[T],
constructors: Seq[SRConstructor[_]] = ArraySeq.empty,
fields: Map[String, SRField] = Map.empty,
fields: Map[String, RField] = Map.empty,
methods: Map[(String, Seq[Class[_]]), RMethod] = Map.empty): Unit = classes.synchronized {
classes.put(clazz, new SRClass(clazz, constructors, fields, methods))
}
Expand Down
9 changes: 5 additions & 4 deletions core/shared/src/main/scala/sigma/reflection/StaticImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ package sigma.reflection
* Extends [[RField]] by providing a concrete implementation without relying on Java reflection.
* The instances of this class are used as parameters of `registerClassEntry` method.
*
* @param declaringClass the class that declares the field
* @param name the name of the field
* @param tpe the type of the field as runtime [[java.lang.Class]]
*/
class SRField(val name: String, tpe: Class[_]) extends RField {
class SRField(val declaringClass: Class[_], val name: String, tpe: Class[_]) extends RField {
override def getType: Class[_] = tpe

override def equals(other: Any): Boolean = (this eq other.asInstanceOf[AnyRef]) || (other match {
case that: SRField => name == that.name
case that: SRField => declaringClass == that.declaringClass && name == that.name
case _ => false
})
override def hashCode(): Int = name.hashCode()
override def hashCode(): Int = 31 * name.hashCode() + declaringClass.hashCode()
}

/** Represents a constructor in an Sigma Reflection metadata.
Expand Down Expand Up @@ -60,7 +61,7 @@ abstract class SRMethod(declaringClass: Class[_], name: String, parameterTypes:
*/
class SRClass[T](val clazz: Class[T],
constructors: Seq[SRConstructor[_]],
fields: Map[String, SRField],
fields: Map[String, RField],
methods: Map[(String, Seq[Class[_]]), RMethod]) extends RClass[T] {

override def getField(fieldName: String): RField = fields.get(fieldName) match {
Expand Down
14 changes: 14 additions & 0 deletions core/shared/src/main/scala/sigma/reflection/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ package object reflection {
}
}

/** Creates a new SRField instance with the given parameters.
* This is analogous to the Java Reflection API's [[java.lang.reflect.Field]] class.
*
* @param clazz the [[java.lang.Class]] that declares the field
* @param name the name of the field
* @param fieldType the type of the field value
* @return a tuple containing the field's name as its first element,
* and an SRField instance as its second element
* @see [[SRField]]
*/
def mkField(clazz: Class[_], name: String, fieldType: Class[_]): (String, RField) = {
name -> new SRField(clazz, name, fieldType)
}

/** Creates a new SRMethod instance with the given parameters and handler function.
* This is analogous to the Java Reflection API's [[java.lang.reflect.Method]] class.
*
Expand Down
130 changes: 129 additions & 1 deletion data/shared/src/main/scala/sigma/SigmaDataReflection.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import sigma.ast.syntax._
import sigma.data.KeyValueColl
import sigma.eval.ErgoTreeEvaluator
import sigma.reflection.ReflectionData.registerClassEntry
import sigma.reflection.{ReflectionData, mkConstructor, mkMethod}
import sigma.reflection.{ReflectionData, mkConstructor, mkField, mkMethod}
import sigma.serialization.ValueCodes.OpCode

/** Reflection metadata for `interpreter` module.
Expand Down Expand Up @@ -636,4 +636,132 @@ object SigmaDataReflection {
}
)
)

registerClassEntry(classOf[GetVar[_]],
constructors = Array(
mkConstructor(Array(classOf[Byte], classOf[SOption[_]])) { args =>
new GetVar[SType](args(0).asInstanceOf[Byte], args(1).asInstanceOf[SOption[SType]])
}
)
)

registerClassEntry(classOf[LongToByteArray],
constructors = Array(
mkConstructor(Array(classOf[Value[_]])) { args =>
new LongToByteArray(args(0).asInstanceOf[Value[SLong.type]])
}
)
)

registerClassEntry(classOf[ValUse[_]],
constructors = Array(
mkConstructor(Array(classOf[Int], classOf[SType])) { args =>
new ValUse(args(0).asInstanceOf[Int], args(1).asInstanceOf[SType])
}
)
)

registerClassEntry(classOf[ByteArrayToLong],
constructors = Array(
mkConstructor(Array(classOf[Value[_]])) { args =>
new ByteArrayToLong(args(0).asInstanceOf[Value[SByteArray]])
}
)
)

registerClassEntry(classOf[ConstantNode[_]],
constructors = Array(
mkConstructor(Array(classOf[java.lang.Object], classOf[SType])) { args =>
ConstantNode(args(0).asInstanceOf[SType#WrappedType], args(1).asInstanceOf[SType])
}
)
)

registerClassEntry(classOf[CreateProveDlog],
constructors = Array(
mkConstructor(Array(classOf[Value[_]])) { args =>
new CreateProveDlog(args(0).asInstanceOf[Value[SGroupElement.type]])
}
)
)

registerClassEntry(classOf[DecodePoint],
constructors = Array(
mkConstructor(Array(classOf[Value[_]])) { args =>
new DecodePoint(args(0).asInstanceOf[Value[SByteArray]])
}
)
)

registerClassEntry(classOf[ExtractBytes],
constructors = Array(
mkConstructor(Array(classOf[Value[_]])) { args =>
new ExtractBytes(args(0).asInstanceOf[Value[SBox.type]])
}
)
)

{ val clazz = sigma.ast.Global.getClass
registerClassEntry(clazz,
fields = Map( mkField(clazz, "MODULE$", clazz) )
)
}

{ val clazz = sigma.ast.GroupGenerator.getClass
registerClassEntry(clazz,
fields = Map( mkField(clazz, "MODULE$", clazz) )
)
}

{ val clazz = Height.getClass
registerClassEntry(clazz,
fields = Map( mkField(clazz, "MODULE$", clazz) )
)
}

{ val clazz = sigma.ast.Inputs.getClass
registerClassEntry(clazz,
fields = Map( mkField(clazz, "MODULE$", clazz) )
)
}

{ val clazz = sigma.ast.LastBlockUtxoRootHash.getClass
registerClassEntry(clazz,
fields = Map( mkField(clazz, "MODULE$", clazz) )
)
}

{ val clazz = MinerPubkey.getClass
registerClassEntry(clazz,
fields = Map( mkField(clazz, "MODULE$", clazz) )
)
}

{ val clazz = sigma.ast.Outputs.getClass
registerClassEntry(clazz,
fields = Map( mkField(clazz, "MODULE$", clazz) )
)
}

{ val clazz = sigma.ast.Self.getClass
registerClassEntry(clazz,
fields = Map( mkField(clazz, "MODULE$", clazz) )
)
}

registerClassEntry(classOf[Xor],
constructors = Array(
mkConstructor(Array(classOf[ast.Value[_]], classOf[ast.Value[_]])) { args =>
new Xor(args(0).asInstanceOf[Value[SByteArray]], args(1).asInstanceOf[Value[SByteArray]])
}
)
)

registerClassEntry(classOf[XorOf],
constructors = Array(
mkConstructor(Array(classOf[Value[_]])) { args =>
new XorOf(args(0).asInstanceOf[Value[SCollection[SBoolean.type]]])
}
)
)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package sigma.interpreter.js

import sigma.data.Iso
import sigma.js.JsWrapper
import sigmastate.interpreter.HintsBag

Expand All @@ -20,4 +21,9 @@ object ProverHints extends js.Object {

/** Empty bag of hints. Immutable value can be reused where necessary. */
def empty(): ProverHints = _empty

val isoProverHints: Iso[ProverHints, HintsBag] = new Iso[ProverHints, HintsBag] {
override def to(p: ProverHints): HintsBag = p.wrappedValue
override def from(p: HintsBag): ProverHints = new ProverHints(p)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package sigmastate.interpreter
import debox.cfor
import org.ergoplatform.ErgoLikeContext
import org.ergoplatform.validation.ValidationRules._
import sigma.Extensions.TryOps
import sigma.VersionContext
import sigma.ast.SCollection.SByteArray
import sigma.ast.syntax._
Expand Down
Loading
Loading