Skip to content

Commit

Permalink
Merge branch 'tx-builders' into tx-signing-js
Browse files Browse the repository at this point in the history
# Conflicts:
#	sdk/shared/src/main/scala/org/ergoplatform/sdk/AppkitProvingInterpreter.scala
  • Loading branch information
aslesarenko committed Aug 5, 2023
2 parents 9595070 + 7d41fd2 commit a9fe1c5
Show file tree
Hide file tree
Showing 27 changed files with 844 additions and 192 deletions.
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
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
@@ -1,34 +1,30 @@
package org.ergoplatform.sdk.js

import org.ergoplatform.sdk.wallet.protocol.context.ErgoLikeParameters
import org.ergoplatform.sdk

import scala.scalajs.js.UndefOr
import scala.scalajs.js.annotation.JSExportTopLevel
import org.ergoplatform.sdk.Iso._

/** JS exported version of the [[sdk.BlockchainParameters]] class with the same fields.
* @see sdk.BlockchainParameters
*/
@JSExportTopLevel("BlockchainParameters")
class BlockchainParameters(
val storageFeeFactor: Int,
val minValuePerByte: Int,
val maxBlockSize: Int,
val tokenAccessCost: Int,
val inputCost: Int,
val dataInputCost: Int,
val outputCost: Int,
val maxBlockCost: Int,
val _softForkStartingHeight: UndefOr[Int],
val _softForkVotesCollected: UndefOr[Int],
val blockVersion: Byte
) extends ErgoLikeParameters {
import org.ergoplatform.sdk.Iso._
/**
* @return height when voting for a soft-fork had been started
*/
override def softForkStartingHeight: Option[Int] =
Isos.isoUndefOr[Int, Int](identityIso).to(_softForkStartingHeight)

/**
* @return votes for soft-fork collected in previous epochs
*/
override def softForkVotesCollected: Option[Int] =
Isos.isoUndefOr[Int, Int](identityIso).to(_softForkVotesCollected)
}
storageFeeFactor: Int,
minValuePerByte: Int,
maxBlockSize: Int,
tokenAccessCost: Int,
inputCost: Int,
dataInputCost: Int,
outputCost: Int,
maxBlockCost: Int,
_softForkStartingHeight: UndefOr[Int],
_softForkVotesCollected: UndefOr[Int],
blockVersion: Byte
) extends sdk.BlockchainParameters(
storageFeeFactor, minValuePerByte, maxBlockSize, tokenAccessCost, inputCost, dataInputCost,
outputCost, maxBlockCost,
Isos.isoUndefOr[Int, Int](identityIso).to(_softForkStartingHeight),
Isos.isoUndefOr[Int, Int](identityIso).to(_softForkVotesCollected), blockVersion
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package org.ergoplatform.sdk.js
import scala.scalajs.js
import scala.scalajs.js.annotation.JSExportTopLevel

/** Equivalent of [[org.ergoplatform.sdk.wallet.protocol.context.ErgoLikeStateContext]] available from JS. */
/** Equivalent of [[org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext]] available from JS. */
@JSExportTopLevel("BlockchainStateContext")
class BlockchainStateContext(
val sigmaLastHeaders: js.Array[Header],
Expand Down
10 changes: 5 additions & 5 deletions sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import org.ergoplatform.ErgoBox._
import org.ergoplatform.{DataInput, ErgoBox, ErgoBoxCandidate, UnsignedErgoLikeTransaction, UnsignedInput}
import org.ergoplatform.sdk.{ExtendedInputBox, Iso}
import org.ergoplatform.sdk.JavaHelpers.UniversalConverter
import org.ergoplatform.sdk.wallet.protocol.context.{CErgoLikeStateContext, ErgoLikeStateContext}
import org.ergoplatform.sdk.wallet.protocol.context
import scalan.RType
import scorex.crypto.authds.{ADDigest, ADKey}
import scorex.util.ModifierId
Expand Down Expand Up @@ -179,16 +179,16 @@ object Isos {
}
}

implicit val isoBlockchainStateContext: Iso[BlockchainStateContext, ErgoLikeStateContext] = new Iso[BlockchainStateContext, ErgoLikeStateContext] {
override def to(a: BlockchainStateContext): ErgoLikeStateContext = {
CErgoLikeStateContext(
implicit val isoBlockchainStateContext: Iso[BlockchainStateContext, context.BlockchainStateContext] = new Iso[BlockchainStateContext, context.BlockchainStateContext] {
override def to(a: BlockchainStateContext): context.BlockchainStateContext = {
context.BlockchainStateContext(
sigmaLastHeaders = isoArrayToColl(isoHeader).to(a.sigmaLastHeaders),
previousStateDigest = isoStringToColl.to(a.previousStateDigest),
sigmaPreHeader = isoPreHeader.to(a.sigmaPreHeader)
)
}

override def from(b: ErgoLikeStateContext): BlockchainStateContext = {
override def from(b: context.BlockchainStateContext): BlockchainStateContext = {
new BlockchainStateContext(
sigmaLastHeaders = isoArrayToColl(isoHeader).from(b.sigmaLastHeaders),
previousStateDigest = isoStringToColl.from(b.previousStateDigest),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package org.ergoplatform.sdk.js

import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix
import org.ergoplatform.sdk.wallet.protocol.context.ErgoLikeParameters
import org.ergoplatform.sdk
import org.ergoplatform.sdk.SecretString
import org.ergoplatform.sdk.{BlockchainParameters, SecretString}

import scala.scalajs.js
import scala.scalajs.js.annotation.JSExportTopLevel
Expand All @@ -12,7 +11,7 @@ import sigmastate.eval.SigmaDsl

/** Equivalent of [[sdk.ProverBuilder]] available from JS. */
@JSExportTopLevel("ProverBuilder")
class ProverBuilder(parameters: ErgoLikeParameters, networkPrefix: NetworkPrefix) extends js.Object {
class ProverBuilder(parameters: BlockchainParameters, networkPrefix: NetworkPrefix) extends js.Object {
val _builder = new sdk.ProverBuilder(parameters, networkPrefix)

/** Configure this builder to use the given seed when building a new prover.
Expand Down
11 changes: 5 additions & 6 deletions sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package org.ergoplatform.sdk.js

import org.ergoplatform.ErgoBox.{AdditionalRegisters, BoxId, TokenId}
import org.ergoplatform.sdk.{ExtendedInputBox, Iso}
import org.ergoplatform._
import org.ergoplatform.sdk.wallet.protocol.context.{CErgoLikeStateContext, ErgoLikeStateContext}
import org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext
import org.ergoplatform.sdk.{ExtendedInputBox, Iso}
import org.scalacheck.{Arbitrary, Gen}
import org.scalatest.matchers.should.Matchers
import org.scalatest.propspec.AnyPropSpec
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
import scorex.crypto.authds.ADDigest
import sigmastate.SType
import sigmastate.Values.Constant
import sigmastate.eval.Colls
Expand All @@ -27,11 +26,11 @@ class IsosSpec extends AnyPropSpec with Matchers with ObjectGenerators with Sca
extension <- contextExtensionGen
} yield ExtendedInputBox(box, extension)

lazy val ergoLikeStateContextGen: Gen[ErgoLikeStateContext] = for {
lazy val blockchainStateContextGen: Gen[BlockchainStateContext] = for {
stateRoot <- avlTreeGen
headers <- headersGen(stateRoot)
preHeader <- preHeaderGen(headers.headOption.map(_.id).getOrElse(modifierIdBytesGen.sample.get))
} yield CErgoLikeStateContext(
} yield BlockchainStateContext(
sigmaLastHeaders = Colls.fromItems(headers:_*),
previousStateDigest = stateRoot.digest,
sigmaPreHeader = preHeader
Expand Down Expand Up @@ -93,7 +92,7 @@ class IsosSpec extends AnyPropSpec with Matchers with ObjectGenerators with Sca
}

property("Iso.isoBlockchainStateContext") {
forAll(ergoLikeStateContextGen) { (c: ErgoLikeStateContext) =>
forAll(blockchainStateContextGen) { (c: BlockchainStateContext) =>
roundtrip(Isos.isoBlockchainStateContext)(c)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import org.ergoplatform._
import org.ergoplatform.sdk.Extensions.{CollOps, PairCollOps}
import org.ergoplatform.sdk.JavaHelpers.{TokenColl, UniversalConverter}
import org.ergoplatform.sdk.utils.ArithUtils
import org.ergoplatform.sdk.wallet.protocol.context.{ErgoLikeParameters, ErgoLikeStateContext, TransactionContext}
import org.ergoplatform.sdk.wallet.protocol.context.{BlockchainStateContext, TransactionContext}
import org.ergoplatform.sdk.wallet.secrets.ExtendedSecretKey
import org.ergoplatform.validation.ValidationRules
import scalan.util.Extensions.LongOps
import scorex.crypto.authds.ADDigest
import sigmastate.Values.SigmaBoolean
import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog}
import sigmastate.basics.{DiffieHellmanTupleProverInput, SigmaProtocolPrivateInput}
Expand All @@ -35,7 +36,7 @@ class AppkitProvingInterpreter(
val secretKeys: IndexedSeq[ExtendedSecretKey],
val dLogInputs: IndexedSeq[DLogProverInput],
val dhtInputs: IndexedSeq[DiffieHellmanTupleProverInput],
params: ErgoLikeParameters)
params: BlockchainParameters)
extends ReducingInterpreter(params) with ProverInterpreter {

override type CTX = ErgoLikeContext
Expand Down Expand Up @@ -79,7 +80,7 @@ class AppkitProvingInterpreter(
* The returned cost doesn't include `baseCost`.
*/
def sign(unreducedTx: UnreducedTransaction,
stateContext: ErgoLikeStateContext,
stateContext: BlockchainStateContext,
baseCost: Int): Try[SignedTransaction] = Try {
val maxCost = params.maxBlockCost
var currentCost: Long = baseCost
Expand Down Expand Up @@ -112,7 +113,7 @@ class AppkitProvingInterpreter(
unsignedTx: UnsignedErgoLikeTransaction,
boxesToSpend: IndexedSeq[ExtendedInputBox],
dataBoxes: IndexedSeq[ErgoBox],
stateContext: ErgoLikeStateContext,
stateContext: BlockchainStateContext,
baseCost: Int,
tokensToBurn: IndexedSeq[ErgoToken]): ReducedErgoLikeTransaction = {
if (unsignedTx.inputs.length != boxesToSpend.length) throw new Exception("Not enough boxes to spend")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.ergoplatform.sdk

import org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext
import special.collection.Coll
import special.sigma.Header

/** Represents a specific context of blockchain for execution
* of transaction building scenario.
* It contains methods for accessing blockchain data, current blockchain state,
* node information etc.
* An instance of this class can also be used to create new builders
* for creating new transactions and provers (used for transaction signing).
*/
case class BlockchainContext(
networkType: NetworkType,
parameters: BlockchainParameters,
stateContext: BlockchainStateContext
) {
def headers: Coll[Header] = stateContext.sigmaLastHeaders

def height: Int = headers(0).height
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.ergoplatform.sdk

/** Blockchain parameters re-adjustable via miners voting and voting-related data.
* All these fields are included into extension section of a first block of a voting epoch.
*
* @param storageFeeFactor cost of storing 1 byte in UTXO for four years, in nanoErgs
* @param minValuePerByte cost of a transaction output, in computation unit
* @param maxBlockSize max block size, in bytes
* @param tokenAccessCost cost of a token contained in a transaction, in computation unit
* @param inputCost cost of a transaction input, in computation unit
* @param dataInputCost cost of a transaction data input, in computation unit
* @param outputCost cost of a transaction output, in computation unit
* @param maxBlockCost computation units limit per block
* @param softForkStartingHeight height when voting for a soft-fork had been started
* @param softForkVotesCollected votes for soft-fork collected in previous epochs
* @param blockVersion Protocol version activated on the network
*/
case class BlockchainParameters(
storageFeeFactor: Int,
minValuePerByte: Int,
maxBlockSize: Int,
tokenAccessCost: Int,
inputCost: Int,
dataInputCost: Int,
outputCost: Int,
maxBlockCost: Int,
softForkStartingHeight: Option[Int],
softForkVotesCollected: Option[Int],
blockVersion: Byte
)

/** Global parameters used by SDK */
object BlockchainParameters {
/** A number of blocks a miner should wait before he/she can spend block reward.
* This is part of Ergo protocol and cannot be changed.
*/
val MinerRewardDelay_Mainnet = 720

val MinerRewardDelay_Testnet = 720

/** One Erg is 10^9 NanoErg */
val OneErg: Long = 1000 * 1000 * 1000

/** Minimum transaction fee in NanoErgs as it is defined in Ergo protocol. */
val MinFee: Long = 1000 * 1000

/** Minimum value for a change. It can be used to compute change output value.
* If computed change is less than this value, it is added to the fee
* and `change` output in not added to the transaction.
*/
val MinChangeValue: Long = 1000 * 1000
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.ergoplatform.sdk

import org.ergoplatform.ErgoBoxAssets

/**
* Containter for box selector output
*
* @param inputBoxes - transaction inputs chosen by a selector
* @param changeBoxes - change outputs
* @param payToReemissionBox - pay-to-reemission output mde according to EIP-27, if needed
*/
class BoxSelectionResult[T <: ErgoBoxAssets](
val inputBoxes: Seq[T],
val changeBoxes: Seq[ErgoBoxAssets],
val payToReemissionBox: Option[ErgoBoxAssets])
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.ergoplatform.sdk

import org.ergoplatform.{ErgoBox, UnsignedInput}
import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, UnsignedInput}
import scorex.util.ModifierId
import sigmastate.interpreter.ContextExtension

/** Input ErgoBox paired with context variables (aka ContextExtensions).
Expand All @@ -16,4 +17,25 @@ case class ExtendedInputBox(
extension: ContextExtension
) {
def toUnsignedInput: UnsignedInput = new UnsignedInput(box.id, extension)
def value: Long = box.value
}

case class OutBox(candidate: ErgoBoxCandidate) {
/**
* Converts this box candidate into a new instance of {@link ExtendedInputBox} by
* associating it with the given transaction and output position.
* This method can be used to create input boxed from scratch, without
* retrieving them from the UTXOs. Thus created boxes can be indistinguishable from those
* loaded from blockchain node, and as result can be used to create new transactions.
* This method can also be used to create chains of transactions in advance
*
* @param txId the id of the transaction of which created the box which will be returned
* @param outputIndex zero-based position (index) of the box in the outputs of the transaction.
* @return a new {@link ExtendedInputBox} representing UTXO box as an input of a next transaction.
*/
def convertToInputWith(txId: String, boxIndex: Short): ExtendedInputBox = {
val box = candidate.toBox(ModifierId @@ txId, boxIndex)
ExtendedInputBox(box, ContextExtension.empty)
}
}

Loading

0 comments on commit a9fe1c5

Please sign in to comment.