Skip to content

Commit

Permalink
tx-signing-js: implemented Fleet transaction reduction and signing
Browse files Browse the repository at this point in the history
  • Loading branch information
aslesarenko committed Aug 5, 2023
1 parent 4c5b84c commit c4369ac
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 55 deletions.
89 changes: 65 additions & 24 deletions sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package org.ergoplatform.sdk.js

import org.ergoplatform.ErgoBox._
import org.ergoplatform.{DataInput, ErgoBox, ErgoBoxCandidate, UnsignedErgoLikeTransaction, UnsignedInput}
import org.ergoplatform.{DataInput, ErgoBox, ErgoBoxCandidate, ErgoLikeTransaction, Input, UnsignedErgoLikeTransaction, UnsignedInput}
import org.ergoplatform.sdk.{ExtendedInputBox, Iso}
import org.ergoplatform.sdk.JavaHelpers.UniversalConverter
import org.ergoplatform.sdk.wallet.protocol.context
import scalan.RType
import scorex.crypto.authds.{ADDigest, ADKey}
import scorex.crypto.authds.ADKey
import scorex.util.ModifierId
import scorex.util.encode.Base16
import sigmastate.{AvlTreeData, AvlTreeFlags, SType}
import sigmastate.Values.{Constant, GroupElementConstant}
import sigmastate.eval.Extensions.ArrayOps
import sigmastate.eval.{CAvlTree, CBigInt, CHeader, CPreHeader, Colls, Digest32Coll, Evaluation}
import sigmastate.fleetSdkCommon.{distEsmTypesBoxesMod => boxesMod, distEsmTypesCommonMod => commonMod, distEsmTypesContextExtensionMod => contextExtensionMod, distEsmTypesInputsMod => inputsMod, distEsmTypesRegistersMod => registersMod, distEsmTypesTokenMod => tokenMod}
import sigmastate.interpreter.ContextExtension
import sigmastate.fleetSdkCommon.{distEsmTypesBoxesMod => boxesMod, distEsmTypesCommonMod => commonMod, distEsmTypesContextExtensionMod => contextExtensionMod, distEsmTypesInputsMod => inputsMod, distEsmTypesProverResultMod => proverResultMod, distEsmTypesRegistersMod => registersMod, distEsmTypesTokenMod => tokenMod}
import sigmastate.interpreter.{ContextExtension, ProverResult}
import sigmastate.serialization.{ErgoTreeSerializer, ValueSerializer}
import special.collection.Coll
import special.collection.Extensions.CollBytesOps
Expand All @@ -24,7 +24,7 @@ import sigmastate.fleetSdkCommon.distEsmTypesBoxesMod.Box
import sigmastate.fleetSdkCommon.distEsmTypesCommonMod.HexString
import sigmastate.fleetSdkCommon.distEsmTypesRegistersMod.NonMandatoryRegisters
import sigmastate.fleetSdkCommon.distEsmTypesTokenMod.TokenAmount
import sigmastate.fleetSdkCommon.distEsmTypesTransactionsMod.UnsignedTransaction
import sigmastate.fleetSdkCommon.distEsmTypesTransactionsMod.{SignedTransaction, UnsignedTransaction}

import java.math.BigInteger
import scala.collection.immutable.ListMap
Expand Down Expand Up @@ -227,6 +227,28 @@ object Isos {
inputsMod.UnsignedInput(x.boxId.convertTo[boxesMod.BoxId], isoContextExtension.from(x.extension))
}

implicit val isoProverResult: Iso[proverResultMod.ProverResult, ProverResult] = new Iso[proverResultMod.ProverResult, ProverResult] {
override def to(a: proverResultMod.ProverResult): ProverResult = {
ProverResult(
proof = isoStringToArray.to(a.proofBytes),
extension = isoContextExtension.to(a.extension)
)
}
override def from(b: ProverResult): proverResultMod.ProverResult = {
proverResultMod.ProverResult(
isoContextExtension.from(b.extension),
isoStringToArray.from(b.proof)
)
}
}

implicit val isoSignedInput: Iso[inputsMod.SignedInput, Input] = new Iso[inputsMod.SignedInput, Input] {
override def to(x: inputsMod.SignedInput): Input =
Input(x.boxId.convertTo[ErgoBox.BoxId], isoProverResult.to(x.spendingProof))
override def from(x: Input): inputsMod.SignedInput =
inputsMod.SignedInput(x.boxId.convertTo[boxesMod.BoxId], isoProverResult.from(x.spendingProof))
}

implicit val isoDataInput: Iso[inputsMod.DataInput, DataInput] = new Iso[inputsMod.DataInput, DataInput] {
override def to(x: inputsMod.DataInput): DataInput = DataInput(x.boxId.convertTo[ErgoBox.BoxId])

Expand Down Expand Up @@ -350,25 +372,6 @@ object Isos {
}
}

// Implements Iso between UnsignedTransaction and UnsignedErgoLikeTransaction
val isoUnsignedTransaction: Iso[UnsignedTransaction, UnsignedErgoLikeTransaction] =
new Iso[UnsignedTransaction, UnsignedErgoLikeTransaction] {
override def to(a: UnsignedTransaction): UnsignedErgoLikeTransaction = {
new UnsignedErgoLikeTransaction(
inputs = isoArrayToIndexed(isoUnsignedInput).to(a.inputs),
dataInputs = isoArrayToIndexed(isoDataInput).to(a.dataInputs),
outputCandidates = isoArrayToIndexed(isoBoxCandidate).to(a.outputs),
)
}
override def from(b: UnsignedErgoLikeTransaction): UnsignedTransaction = {
UnsignedTransaction(
inputs = isoArrayToIndexed(isoUnsignedInput).from(b.inputs),
dataInputs = isoArrayToIndexed(isoDataInput).from(b.dataInputs),
outputs = isoArrayToIndexed(isoBoxCandidate).from(b.outputCandidates)
)
}
}

val isoBox: Iso[Box[commonMod.Amount], ErgoBox] = new Iso[Box[commonMod.Amount], ErgoBox] {
override def to(x: Box[commonMod.Amount]): ErgoBox = {
val ergoBox = new ErgoBox(
Expand Down Expand Up @@ -436,4 +439,42 @@ object Isos {
)
}
}

val isoUnsignedTransaction: Iso[UnsignedTransaction, UnsignedErgoLikeTransaction] =
new Iso[UnsignedTransaction, UnsignedErgoLikeTransaction] {
override def to(a: UnsignedTransaction): UnsignedErgoLikeTransaction = {
new UnsignedErgoLikeTransaction(
inputs = isoArrayToIndexed(isoUnsignedInput).to(a.inputs),
dataInputs = isoArrayToIndexed(isoDataInput).to(a.dataInputs),
outputCandidates = isoArrayToIndexed(isoBoxCandidate).to(a.outputs),
)
}

override def from(b: UnsignedErgoLikeTransaction): UnsignedTransaction = {
UnsignedTransaction(
inputs = isoArrayToIndexed(isoUnsignedInput).from(b.inputs),
dataInputs = isoArrayToIndexed(isoDataInput).from(b.dataInputs),
outputs = isoArrayToIndexed(isoBoxCandidate).from(b.outputCandidates)
)
}
}

val isoSignedTransaction: Iso[SignedTransaction, ErgoLikeTransaction] =
new Iso[SignedTransaction, ErgoLikeTransaction] {
override def to(a: SignedTransaction): ErgoLikeTransaction = {
new ErgoLikeTransaction(
inputs = isoArrayToIndexed(isoSignedInput).to(a.inputs),
dataInputs = isoArrayToIndexed(isoDataInput).to(a.dataInputs),
outputCandidates = isoArrayToIndexed(isoBox).to(a.outputs),
)
}

override def from(tx: ErgoLikeTransaction): SignedTransaction = {
val inputs = isoArrayToIndexed(isoSignedInput).from(tx.inputs)
val dataInputs = isoArrayToIndexed(isoDataInput).from(tx.dataInputs)
val outputs = isoArrayToIndexed(isoBox).from(tx.outputs)
SignedTransaction(dataInputs, tx.id, inputs, outputs)
}
}

}
50 changes: 26 additions & 24 deletions sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.ergoplatform.sdk.js

import org.ergoplatform.sdk
import org.ergoplatform.sdk.SignedTransaction
import sigmastate.fleetSdkCommon.distEsmTypesBoxesMod.Box
import sigmastate.fleetSdkCommon.{distEsmTypesCommonMod => commonMod, distEsmTypesInputsMod => inputsMod, distEsmTypesTokenMod => tokenMod, distEsmTypesTransactionsMod => transactionsMod}

Expand All @@ -12,45 +13,46 @@ import scala.scalajs.js.annotation.JSExportTopLevel
class SigmaProver(_prover: sdk.SigmaProver) extends js.Object {
import Isos._

//TODO finish implementation
/** 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
*/
def reduce(
stateCtx: BlockchainStateContext,
unsignedTx: transactionsMod.UnsignedTransaction,
boxesToSpend: js.Array[inputsMod.EIP12UnsignedInput],
dataInputs: js.Array[Box[commonMod.Amount]],
tokensToBurn: js.Array[tokenMod.TokenAmount[commonMod.Amount]],
baseCost: Int): ReducedTransaction = {
val tx = sdk.UnreducedTransaction(
val unreducedTx = sdk.UnreducedTransaction(
unsignedTx = isoUnsignedTransaction.to(unsignedTx),
boxesToSpend = isoArrayToIndexed(isoEIP12UnsignedInput).to(boxesToSpend),
dataInputs = IndexedSeq.empty,
tokensToBurn = IndexedSeq.empty
dataInputs = isoArrayToIndexed(isoBox).to(dataInputs),
tokensToBurn = isoArrayToIndexed(isoToken.andThen(sdk.Iso.isoErgoTokenToPair.inverse)).to(tokensToBurn)
)
_prover.reduce(
isoBlockchainStateContext.to(stateCtx),
tx,
baseCost
)
new ReducedTransaction
val ctx = isoBlockchainStateContext.to(stateCtx)
val reducedTx = _prover.reduce(ctx, unreducedTx, baseCost)
new ReducedTransaction(reducedTx)
}

def reduceTransaction(
unsignedTx: transactionsMod.UnsignedTransaction,
boxesToSpend: js.Array[inputsMod.EIP12UnsignedInput],
dataBoxes: js.Array[Box[commonMod.Amount]],
stateDigest: String,
baseCost: Int,
tokensToBurn: js.Array[tokenMod.TokenAmount[commonMod.Amount]]
): (ReducedTransaction, Int) = {
val tx = Isos.isoUnsignedTransaction.to(unsignedTx)
// val inputs: = boxesToSpend.map(isoEIP12UnsignedInput.to).toArray

(new ReducedTransaction, 0)
/** Signs the reduced transaction.
* @param reducedTx reduced transaction to be signed
* @return signed transaction containting all the required proofs (signatures)
*/
def signReduced(reducedTx: ReducedTransaction): transactionsMod.SignedTransaction = {
val signed = _prover.signReduced(reducedTx._tx)
isoSignedTransaction.from(signed.ergoTx)
}

}

//TODO finish implementation
@JSExportTopLevel("ReducedTransaction")
class ReducedTransaction
class ReducedTransaction(private [js] val _tx: sdk.ReducedTransaction)

//TODO finish implementation
@JSExportTopLevel("SigmaProverObj")
Expand Down
31 changes: 24 additions & 7 deletions sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
import sigmastate.SType
import sigmastate.Values.Constant
import sigmastate.eval.Colls
import sigmastate.interpreter.ContextExtension
import sigmastate.interpreter.{ContextExtension, ProverResult}
import sigmastate.serialization.generators.ObjectGenerators
import special.collection.Coll
import special.sigma
Expand Down Expand Up @@ -102,12 +102,24 @@ class IsosSpec extends AnyPropSpec with Matchers with ObjectGenerators with Sca
}
}

property("Iso.isoProverResult") {
forAll { (c: ProverResult) =>
roundtrip(Isos.isoProverResult)(c)
}
}

property("Iso.isoUnsignedInput") {
forAll { (c: UnsignedInput) =>
roundtrip(Isos.isoUnsignedInput)(c)
}
}

property("Iso.isoSignedInput") {
forAll { (c: Input) =>
roundtrip(Isos.isoSignedInput)(c)
}
}

property("Iso.isoDataInput") {
forAll { (c: DataInput) =>
roundtrip(Isos.isoDataInput)(c)
Expand Down Expand Up @@ -163,12 +175,6 @@ class IsosSpec extends AnyPropSpec with Matchers with ObjectGenerators with Sca
}
}

property("Iso.isoUnsignedTransaction") {
forAll { (tx: UnsignedErgoLikeTransaction) =>
roundtrip(Isos.isoUnsignedTransaction)(tx)
}
}

property("Iso.isoBox") {
forAll { (b: ErgoBox) =>
roundtrip(Isos.isoBox)(b)
Expand All @@ -181,4 +187,15 @@ class IsosSpec extends AnyPropSpec with Matchers with ObjectGenerators with Sca
}
}

property("Iso.isoUnsignedTransaction") {
forAll { (tx: UnsignedErgoLikeTransaction) =>
roundtrip(Isos.isoUnsignedTransaction)(tx)
}
}

property("Iso.isoSignedTransaction") {
forAll { (tx: ErgoLikeTransaction) =>
roundtrip(Isos.isoSignedTransaction)(tx)
}
}
}

0 comments on commit c4369ac

Please sign in to comment.