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

Reduction and signing of Fleet transactions #900

Merged
merged 29 commits into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2f8100e
multisig: rename ErgoLikeParameters -> BlockchainParameters
aslesarenko Jul 10, 2023
de8d477
multisig: rename ErgoLikeStateContext -> BlockchainStateContext
aslesarenko Jul 10, 2023
b067284
multisig: implemented OutBoxBuilder, BlockchainContext
aslesarenko Jul 10, 2023
b1ca88d
multisig: implemented UnsignedTransactionBuilder
aslesarenko Jul 11, 2023
7d41fd2
multisig: creation of ReducedTransaction
aslesarenko Jul 11, 2023
a9fe1c5
Merge branch 'tx-builders' into tx-signing-js
aslesarenko Aug 5, 2023
4c5b84c
tx-signing-js: fixed test for isoUnsignedTransaction
aslesarenko Aug 5, 2023
c4369ac
tx-signing-js: implemented Fleet transaction reduction and signing
aslesarenko Aug 5, 2023
964feda
tx-signing-js: fixed tests
aslesarenko Aug 6, 2023
ae39af3
tx-signing-js: fixed tests (2)
aslesarenko Aug 6, 2023
6746d72
tx-signing-js: upgrade fleed-sdk to 0.1.0
aslesarenko Aug 7, 2023
f953e7d
tx-signing-js: upgrade fleed-sdk to 0.1.3
aslesarenko Aug 7, 2023
843777e
Merge remote-tracking branch 'origin/v5.0.11-RC' into tx-signing-js
aslesarenko Aug 14, 2023
b948a31
tx-signing-js: fixes after merge
aslesarenko Aug 14, 2023
4ec1b49
tx-signing-js: ReducedTransaction exported to JS and can be serialise…
aslesarenko Aug 17, 2023
1bdbceb
tx-signing-js: fixed CryptoContextJs.decodePoint
aslesarenko Aug 18, 2023
5fac6e7
tx-signing-js: added js.GroupElement class
aslesarenko Aug 18, 2023
58140c1
tx-signing-js: js tests for GroupElement
aslesarenko Aug 18, 2023
9917c2b
tx-signing-js: implemented fromPointHex for js.SigmaProp
aslesarenko Aug 18, 2023
3d5c498
tx-signing-js: added test for Value of type Coll[SigmaProp]
aslesarenko Aug 18, 2023
748962d
tx-signing-js: fix equality of ValidationSettings
aslesarenko Aug 18, 2023
1bf6205
todo-v5.x: fix undeterministic behaviour in JsonCodecs
aslesarenko Aug 20, 2023
b0d6202
todo-v5.x: fix contextExtensionEncoder
aslesarenko Aug 20, 2023
ace1cdb
tx-signing-js: cleanups
aslesarenko Aug 20, 2023
627214f
tx-signing-js: downcastCollType
aslesarenko Aug 21, 2023
71f4b7e
tx-signing-js: removed Signer
aslesarenko Aug 25, 2023
ac3c6b8
tx-signing-js: rollback changes in MapSigmaValidationSettings
aslesarenko Aug 25, 2023
d1f0a6d
tx-signing-js: remove SigningSpec.scala
aslesarenko Aug 25, 2023
ce203cc
tx-signing-js: fix Header props, more ScalaDocs
aslesarenko Aug 26, 2023
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
4 changes: 2 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ lazy val commonSettings = Seq(
scalacOptions ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, 13)) =>
Seq("-Ywarn-unused:_,imports", "-Ywarn-unused:imports", "-release", "8")
Seq("-Ywarn-unused:_,imports", "-Ywarn-unused:imports", "-Wconf:src=src_managed/.*:silent", "-release", "8")
case Some((2, 12)) =>
Seq("-Ywarn-unused:_,imports", "-Ywarn-unused:imports", "-release", "8")
case Some((2, 11)) =>
Expand Down Expand Up @@ -282,7 +282,7 @@ lazy val interpreterJS = interpreter.js
},
Compile / npmDependencies ++= Seq(
"sigmajs-crypto-facade" -> sigmajsCryptoFacadeVersion,
"@fleet-sdk/common" -> "0.1.0-alpha.14"
"@fleet-sdk/common" -> "0.1.3"
)
)

Expand Down
3 changes: 3 additions & 0 deletions common/shared/src/main/scala/scalan/util/CollectionUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ object CollectionUtil {
}

implicit class AnyOps[A](val x: A) extends AnyVal {
/** Performs a specified action on the source value and returns the result. */
def perform(action: A => A): A = action(x)

/** Traverses the tree structure in a depth-first manner using the provided function to generate child nodes.
*
* @param f a function that takes a node of type A and returns a list of its children
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,14 @@ package object collection {
/** Implicit resolution of `Coll[A]` type descriptor, given a descriptor of `A`. */
implicit def collRType[A](implicit tA: RType[A]): RType[Coll[A]] = CollType[A](tA)

/** Conversion to underlying descriptor class.
* Allows syntax like
*
* ```val tColl: RType[Coll[A]] = ...; tColl.tItem```
*
* where `tItem` is a method of `CollType`, but is not defined on `RType`.
*/
implicit def downcastCollType[A](ct: RType[Coll[A]]): CollType[A] = ct.asInstanceOf[CollType[A]]

implicit val collBuilderRType: RType[CollBuilder] = RType.fromClassTag(classTag[CollBuilder])
}
14 changes: 4 additions & 10 deletions core-lib/shared/src/main/scala/special/sigma/SigmaDsl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,7 @@ trait BigInt {
def |(that: BigInt): BigInt = or(that)
}

/** Base class for points on elliptic curves.
*/
/** Base class for points on elliptic curves. */
trait GroupElement {
/** Checks if the provided element is an identity element. */
def isIdentity: Boolean
Expand Down Expand Up @@ -381,7 +380,6 @@ trait Box {
trait AvlTree {
/** Returns digest of the state represented by this tree.
* Authenticated tree digest = root hash bytes ++ tree height
* @since 2.0
*/
def digest: Coll[Byte]

Expand Down Expand Up @@ -529,10 +527,8 @@ trait AvlTreeVerifier {
}


/** Only header fields that can be predicted by a miner.
* @since 2.0
*/
trait PreHeader { // Testnet2
/** Only header fields that can be predicted by a miner. */
trait PreHeader {
/** Block version, to be increased on every soft and hardfork. */
def version: Byte

Expand All @@ -556,9 +552,7 @@ trait PreHeader { // Testnet2
def votes: Coll[Byte]
}

/** Represents data of the block header available in Sigma propositions.
* @since 2.0
*/
/** Represents data of the block header available in Sigma propositions. */
trait Header {
/** Bytes representation of ModifierId of this Header */
def id: Coll[Byte]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,12 @@ object Platform {
override def infinity(): crypto.Ecp =
new Ecp(ctx.getInfinity())

override def decodePoint(encoded: Array[Byte]): crypto.Ecp =
override def decodePoint(encoded: Array[Byte]): crypto.Ecp = {
if (encoded(0) == 0) {
return infinity()
}
new Ecp(ctx.decodePoint(Base16.encode(encoded)))
}

override def generator: crypto.Ecp =
new Ecp(ctx.getGenerator())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package sigmastate.crypto

import org.scalatest.matchers.should.Matchers
import org.scalatest.propspec.AnyPropSpec
import scorex.util.encode.Base16

import scala.scalajs.js
import scala.scalajs.js.typedarray.Uint8Array
Expand Down
14 changes: 6 additions & 8 deletions interpreter/shared/src/main/scala/org/ergoplatform/ErgoBox.scala
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
package org.ergoplatform

import scorex.utils.{Ints, Shorts}
import org.ergoplatform.ErgoBox.{NonMandatoryRegisterId, Token}
import org.ergoplatform.ErgoBox.{AdditionalRegisters, Token}
import org.ergoplatform.settings.ErgoAlgos
import scorex.crypto.authds.ADKey
import scorex.crypto.hash.{Blake2b256, Digest32}
import scorex.crypto.hash.Blake2b256
import scorex.util._
import scorex.utils.{Ints, Shorts}
import sigmastate.SCollection.SByteArray
import sigmastate.SType.AnyOps
import sigmastate.Values._
import sigmastate._
import sigmastate.eval.Extensions._
import sigmastate.eval._
import sigmastate.serialization.SigmaSerializer
import sigmastate.utils.{SigmaByteReader, SigmaByteWriter, Helpers}
import sigmastate.utils.{Helpers, SigmaByteReader, SigmaByteWriter}
import sigmastate.utxo.ExtractCreationInfo
import special.collection._

import scala.runtime.ScalaRunTime

/**
* Box (aka coin, or an unspent output) is a basic concept of a UTXO-based cryptocurrency. In Bitcoin, such an object
* is associated with some monetary value (arbitrary, but with predefined precision, so we use integer arithmetic to
Expand Down Expand Up @@ -52,7 +50,7 @@ class ErgoBox(
override val value: Long,
override val ergoTree: ErgoTree,
override val additionalTokens: Coll[Token] = Colls.emptyColl[Token],
override val additionalRegisters: Map[NonMandatoryRegisterId, _ <: EvaluatedValue[_ <: SType]] = Map.empty,
override val additionalRegisters: AdditionalRegisters = Map.empty,
val transactionId: ModifierId,
val index: Short,
override val creationHeight: Int
Expand Down Expand Up @@ -136,7 +134,7 @@ object ErgoBox {
/** Represents id of optional registers of a box. */
sealed abstract class NonMandatoryRegisterId(override val number: Byte) extends RegisterId

type AdditionalRegisters = Map[NonMandatoryRegisterId, _ <: EvaluatedValue[_ <: SType]]
type AdditionalRegisters = scala.collection.Map[NonMandatoryRegisterId, EvaluatedValue[_ <: SType]]

object R0 extends MandatoryRegisterId(0, "Monetary value, in Ergo tokens")
object R1 extends MandatoryRegisterId(1, "Guarding script")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class ErgoBoxCandidate(val value: Long,
val ergoTree: ErgoTree,
val creationHeight: Int,
val additionalTokens: Coll[Token] = Colls.emptyColl,
val additionalRegisters: Map[NonMandatoryRegisterId, _ <: EvaluatedValue[_ <: SType]] = Map())
val additionalRegisters: AdditionalRegisters = Map())
extends ErgoBoxAssets {

/** Transforms this tree to a proposition, substituting the constants if the constant
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,11 @@ class ErgoLikeContext(val lastBlockUtxoRoot: AvlTreeData,
lastBlockUtxoRoot, headers, preHeader, dataBoxes, boxesToSpend, spendingTransaction,
selfIndex, extension, validationSettings, costLimit, initCost,
activatedScriptVersion)
var hashCode = 0
var h = 0
cfor(0)(_ < state.length, _ + 1) { i =>
hashCode = 31 * hashCode + state(i).hashCode
h = 31 * h + state(i).hashCode
}
hashCode
h
}

override def toString = s"ErgoLikeContext(lastBlockUtxoRoot=$lastBlockUtxoRoot, headers=$headers, preHeader=$preHeader, dataBoxes=$dataBoxes, boxesToSpend=$boxesToSpend, spendingTransaction=$spendingTransaction, selfIndex=$selfIndex, extension=$extension, validationSettings=$validationSettings, costLimit=$costLimit, initCost=$initCost, activatedScriptVersion=$activatedScriptVersion)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ trait ErgoLikeTransactionTemplate[IT <: UnsignedInput] {

lazy val inputIds: IndexedSeq[ADKey] = inputs.map(_.boxId)

override def toString = s"ErgoLikeTransactionTemplate(dataInputs=$dataInputs, inputs=$inputs, outputCandidates=$outputCandidates)"
}


Expand All @@ -76,6 +75,8 @@ class UnsignedErgoLikeTransaction(override val inputs: IndexedSeq[UnsignedInput]
}

override def hashCode(): Int = id.hashCode()

override def toString = s"UnsignedErgoLikeTransaction(dataInputs=$dataInputs, inputs=$inputs, outputCandidates=$outputCandidates)"
}

object UnsignedErgoLikeTransaction {
Expand Down Expand Up @@ -105,6 +106,7 @@ class ErgoLikeTransaction(override val inputs: IndexedSeq[Input],
}

override def hashCode(): Int = id.hashCode()
override def toString = s"ErgoLikeTransaction(dataInputs=$dataInputs, inputs=$inputs, outputCandidates=$outputCandidates)"
}

object ErgoLikeTransactionSerializer extends SigmaSerializer[ErgoLikeTransaction, ErgoLikeTransaction] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ abstract class SigmaValidationSettings extends Iterable[(Short, (ValidationRule,
def isSoftFork(ruleId: Short, ve: ValidationException): Boolean = {
val infoOpt = get(ruleId)
infoOpt match {
case Some((_, ReplacedRule(newRuleId))) => true
case Some((_, ReplacedRule(_))) => true
case Some((rule, status)) => rule.isSoftFork(this, rule.id, status, ve.args)
case None => false
}
Expand All @@ -72,10 +72,12 @@ sealed class MapSigmaValidationSettings(private val map: Map[Short, (ValidationR
}

override def equals(obj: Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || (obj match {
case that: MapSigmaValidationSettings => map == that.map
case that: MapSigmaValidationSettings =>
map.size == that.map.size &&
map.iterator.forall { case (id, v) => that.map.get(id).exists(_ == v) }
case _ => false
})

override def hashCode(): Int = map.hashCode()
override def hashCode(): Int = map.toIndexedSeq.sortBy(_._1).hashCode()
}

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package org.ergoplatform.validation

import sigmastate.serialization.SigmaSerializer
import sigmastate.utils.{SigmaByteReader, SigmaByteWriter}
import scalan.util.Extensions.{IntOps,LongOps}
import scalan.util.Extensions.{IntOps, LongOps}
import sigmastate.exceptions.SerializerException

// TODO v5.x: remove unused class and related json encoders
/** The rules are serialized ordered by ruleId.
Expand All @@ -13,9 +14,9 @@ object SigmaValidationSettingsSerializer extends SigmaSerializer[SigmaValidation
override def serialize(settings: SigmaValidationSettings, w: SigmaByteWriter): Unit = {
val rules = settings.toArray.sortBy(_._1)
w.putUInt(rules.length)
rules.foreach { r =>
w.putUShort(r._1)
RuleStatusSerializer.serialize(r._2._2, w)
rules.foreach { case (id, (_, status)) =>
w.putUShort(id)
RuleStatusSerializer.serialize(status, w)
}
}

Expand All @@ -27,10 +28,12 @@ object SigmaValidationSettingsSerializer extends SigmaSerializer[SigmaValidation
val status = RuleStatusSerializer.parse(r)
ruleId -> status
}
val initVs = ValidationRules.currentSettings
val res = parsed
.filter(pair => initVs.get(pair._1).isDefined)
.foldLeft(initVs) { (vs, rule) => vs.updated(rule._1, rule._2) }
val map = parsed.map { case (id, status) =>
val (rule, _) = ValidationRules.currentSettings.get(id)
.getOrElse(throw SerializerException(s"Rule with id $id is not registered"))
kushti marked this conversation as resolved.
Show resolved Hide resolved
id -> (rule, status)
}.toMap
val res = new MapSigmaValidationSettings(map)
res
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package org.ergoplatform.validation

import scalan.util.Extensions.toUByte
import sigmastate.Values.{SValue, ErgoTree}
import sigmastate.Values.{ErgoTree, SValue}
import sigmastate._
import sigmastate.exceptions.{InvalidOpCode, SerializerException, ReaderPositionLimitExceeded, SigmaException, InterpreterException}
import sigmastate.exceptions.{InterpreterException, InvalidOpCode, ReaderPositionLimitExceeded, SerializerException, SigmaException}
import sigmastate.serialization.OpCodes.OpCode
import sigmastate.serialization.TypeSerializer.embeddableIdToType
import sigmastate.serialization.{OpCodes, ValueSerializer}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import debox.{cfor, Buffer => DBuffer}
import org.ergoplatform.ErgoBox
import org.ergoplatform.ErgoBox.TokenId
import scalan.{Nullable, RType}
import scorex.util.encode.Base16
import sigmastate.SType.AnyOps
import sigmastate.Values.{Constant, ConstantNode}
import sigmastate.crypto.{CryptoFacade, Ecp}
Expand Down Expand Up @@ -41,6 +42,8 @@ object Extensions {
implicit class ArrayByteOps(val arr: Array[Byte]) extends AnyVal {
/** Wraps array into TokenId instance. The source array in not cloned. */
@inline def toTokenId: TokenId = Digest32Coll @@ Colls.fromArray(arr)
/** Encodes array into hex string */
@inline def toHex: String = Base16.encode(arr)
}

implicit class EvalIterableOps[T: RType](seq: Iterable[T]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import sigmastate.utils.{SigmaByteReader, SigmaByteWriter}
import special.sigma
import special.sigma.AnyValue

import scala.collection.mutable

/**
* User-defined variables to be put into context.
* Each variable is identified by `id: Byte` and can be accessed from a script
Expand All @@ -20,7 +22,7 @@ import special.sigma.AnyValue
*
* @param values internal container of the key-value pairs
*/
case class ContextExtension(values: Map[Byte, EvaluatedValue[_ <: SType]]) {
case class ContextExtension(values: scala.collection.Map[Byte, EvaluatedValue[_ <: SType]]) {
def add(bindings: VarBinding*): ContextExtension =
ContextExtension(values ++ bindings)
}
Expand Down Expand Up @@ -49,8 +51,7 @@ object ContextExtension {
error(s"Negative amount of context extension values: $extSize")
val ext = (0 until extSize)
.map(_ => (r.getByte(), r.getValue().asInstanceOf[EvaluatedValue[_ <: SType]]))
.toMap[Byte, EvaluatedValue[_ <: SType]]
ContextExtension(ext)
ContextExtension(mutable.LinkedHashMap(ext:_*))
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import java.math.BigInteger

class CryptoFacadeSpecification extends AnyPropSpec with Matchers with ScalaCheckPropertyChecks {

val G_hex = "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"

property("CryptoFacade.HashHmacSHA512") {
val cases = Table(
("string", "hash"),
Expand Down Expand Up @@ -54,21 +56,37 @@ class CryptoFacadeSpecification extends AnyPropSpec with Matchers with ScalaChec
}
}

property("CryptoFacade.encodePoint") {
property("CryptoFacade.getASN1Encoding") {
val ctx = CryptoFacade.createCryptoContext()
val G = ctx.generator
val Q = ctx.order
val vectors = Table(
("point", "expectedHex"),
(ctx.infinity(), "00"),
(CryptoFacade.exponentiatePoint(G, Q), "00"),
(G, "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"),
(CryptoFacade.exponentiatePoint(G, BigInteger.ONE), "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"),
(G, G_hex),
(CryptoFacade.exponentiatePoint(G, BigInteger.ONE), G_hex),
(CryptoFacade.exponentiatePoint(G, Q.subtract(BigInteger.ONE)), "0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")
)
forAll (vectors) { (point, expectedHex) =>
val res = ErgoAlgos.encode(CryptoFacade.getASN1Encoding(point, true))
res shouldBe expectedHex
}
}

property("CryptoContext.decodePoint") {
val ctx = CryptoFacade.createCryptoContext()

val inf = ctx.decodePoint(Array[Byte](0))
CryptoFacade.isInfinityPoint(inf) shouldBe true

val G = ctx.generator
ctx.decodePoint(ErgoAlgos.decode(G_hex).get) shouldBe G

val Q = ctx.order
val Q_minus_1 = Q.subtract(BigInteger.ONE)
val maxExp = CryptoFacade.exponentiatePoint(G, Q_minus_1)
val maxExpBytes = ErgoAlgos.decode("0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798").get
ctx.decodePoint(maxExpBytes) shouldBe maxExp
}
}
Loading
Loading