Skip to content

Commit

Permalink
merging w 6.0.0, fixing ErgoTreeSpec
Browse files Browse the repository at this point in the history
  • Loading branch information
kushti committed Aug 27, 2024
2 parents 9519ef6 + 2cfd2ff commit 3037f1a
Show file tree
Hide file tree
Showing 20 changed files with 1,396 additions and 961 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
*.fdb_latexmk
*.gz


yarn.lock
*.log
docs/spec/out/
test-out/
Expand Down
2 changes: 1 addition & 1 deletion core/shared/src/main/scala/sigma/VersionContext.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ case class VersionContext(activatedVersion: Byte, ergoTreeVersion: Byte) {
def isJitActivated: Boolean = activatedVersion >= JitActivationVersion

/** @return true, if the activated script version of Ergo protocol on the network is
* including Evolution update. */
* including v6.0 update. */
def isV6SoftForkActivated: Boolean = activatedVersion >= V6SoftForkVersion
}

Expand Down
30 changes: 17 additions & 13 deletions core/shared/src/main/scala/sigma/ast/SType.scala
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ case object SByte extends SPrimType with SEmbeddable with SNumericType with SMon
case s: Short => s.toByteExact
case i: Int => i.toByteExact
case l: Long => l.toByteExact
case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toByte // toByteExact from int is called under the hood
case _ => sys.error(s"Cannot downcast value $v to the type $this")
}
}
Expand All @@ -417,6 +418,7 @@ case object SShort extends SPrimType with SEmbeddable with SNumericType with SMo
case s: Short => s
case i: Int => i.toShortExact
case l: Long => l.toShortExact
case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toShort // toShortExact from int is called under the hood
case _ => sys.error(s"Cannot downcast value $v to the type $this")
}
}
Expand All @@ -440,6 +442,7 @@ case object SInt extends SPrimType with SEmbeddable with SNumericType with SMono
case s: Short => s.toInt
case i: Int => i
case l: Long => l.toIntExact
case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toInt
case _ => sys.error(s"Cannot downcast value $v to the type $this")
}
}
Expand All @@ -465,11 +468,12 @@ case object SLong extends SPrimType with SEmbeddable with SNumericType with SMon
case s: Short => s.toLong
case i: Int => i.toLong
case l: Long => l
case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toLong
case _ => sys.error(s"Cannot downcast value $v to the type $this")
}
}

/** Type of 256 bit integet values. Implemented using [[java.math.BigInteger]]. */
/** Type of 256 bit integer values. Implemented using [[java.math.BigInteger]]. */
case object SBigInt extends SPrimType with SEmbeddable with SNumericType with SMonoType {
override type WrappedType = BigInt
override val typeCode: TypeCode = 6: Byte
Expand All @@ -486,24 +490,24 @@ case object SBigInt extends SPrimType with SEmbeddable with SNumericType with SM
override def numericTypeIndex: Int = 4

override def upcast(v: AnyVal): BigInt = {
val bi = v match {
case x: Byte => BigInteger.valueOf(x.toLong)
case x: Short => BigInteger.valueOf(x.toLong)
case x: Int => BigInteger.valueOf(x.toLong)
case x: Long => BigInteger.valueOf(x)
v match {
case x: Byte => CBigInt(BigInteger.valueOf(x.toLong))
case x: Short => CBigInt(BigInteger.valueOf(x.toLong))
case x: Int => CBigInt(BigInteger.valueOf(x.toLong))
case x: Long => CBigInt(BigInteger.valueOf(x))
case x: BigInt if VersionContext.current.isV6SoftForkActivated => x
case _ => sys.error(s"Cannot upcast value $v to the type $this")
}
CBigInt(bi)
}
override def downcast(v: AnyVal): BigInt = {
val bi = v match {
case x: Byte => BigInteger.valueOf(x.toLong)
case x: Short => BigInteger.valueOf(x.toLong)
case x: Int => BigInteger.valueOf(x.toLong)
case x: Long => BigInteger.valueOf(x)
v match {
case x: Byte => CBigInt(BigInteger.valueOf(x.toLong))
case x: Short => CBigInt(BigInteger.valueOf(x.toLong))
case x: Int => CBigInt(BigInteger.valueOf(x.toLong))
case x: Long => CBigInt(BigInteger.valueOf(x))
case x: BigInt if VersionContext.current.isV6SoftForkActivated => x
case _ => sys.error(s"Cannot downcast value $v to the type $this")
}
CBigInt(bi)
}
}

Expand Down
1 change: 1 addition & 0 deletions data/shared/src/main/scala/sigma/ast/SMethod.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ case class MethodIRInfo(
* @param docInfo optional human readable method description data
* @param costFunc optional specification of how the cost should be computed for the
* given method call (See ErgoTreeEvaluator.calcCost method).
* @param userDefinedInvoke optional custom method evaluation function
*/
case class SMethod(
objType: MethodsContainer,
Expand Down
1 change: 0 additions & 1 deletion data/shared/src/main/scala/sigma/ast/SigmaPredef.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress}
import scorex.util.encode.{Base16, Base58, Base64}
import sigma.ast.SCollection.{SByteArray, SIntArray}
import sigma.ast.SOption.SIntOption
import sigma.ast.SigmaPropConstant
import sigma.ast.syntax._
import sigma.data.Nullable
import sigma.exceptions.InvalidArguments
Expand Down
50 changes: 17 additions & 33 deletions data/shared/src/main/scala/sigma/ast/methods.scala
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,13 @@ trait SNumericTypeMethods extends MonoTypeMethods {
private val subst = Map(tNum -> this.ownerType)

private val v5Methods = {
SNumericTypeMethods.methods.map { m =>
SNumericTypeMethods.v5Methods.map { m =>
m.copy(stype = applySubst(m.stype, subst).asFunc)
}
}

private val v6Methods = {
SNumericTypeMethods.methods.map { m =>
SNumericTypeMethods.v6Methods.map { m =>
m.copy(
objType = this, // associate the method with the concrete numeric type
stype = applySubst(m.stype, subst).asFunc
Expand Down Expand Up @@ -379,14 +379,17 @@ object SNumericTypeMethods extends MethodsContainer {
| Each boolean corresponds to one bit.
""".stripMargin)

protected override def getMethods(): Seq[SMethod] = Array(
lazy val v5Methods = Array(
ToByteMethod, // see Downcast
ToShortMethod, // see Downcast
ToIntMethod, // see Downcast
ToLongMethod, // see Downcast
ToBigIntMethod, // see Downcast
ToBytesMethod,
ToBitsMethod,
ToBitsMethod
)

lazy val v6Methods = v5Methods ++ Array(
BitwiseInverseMethod,
BitwiseOrMethod,
BitwiseAndMethod,
Expand All @@ -395,7 +398,16 @@ object SNumericTypeMethods extends MethodsContainer {
ShiftRightMethod
)

protected override def getMethods(): Seq[SMethod] = {
if (VersionContext.current.isV6SoftForkActivated) {
v6Methods
} else {
v5Methods
}
}

/** Collection of names of numeric casting methods (like `toByte`, `toInt`, etc). */
// todo: add unsigned big int
val castMethods: Array[String] =
Array(ToByteMethod, ToShortMethod, ToIntMethod, ToLongMethod, ToBigIntMethod)
.map(_.name)
Expand Down Expand Up @@ -460,31 +472,9 @@ case object SBigIntMethods extends SNumericTypeMethods {
/** Type for which this container defines methods. */
override def ownerType: SMonoType = SBigInt

final val ToNBitsCostInfo = OperationCostInfo(
FixedCost(JitCost(5)), NamedDesc("NBitsMethodCall"))

//id = 20 to make it after toBits and reserve space for future methods at SNumericTypeMethods
val ToNBits = SMethod(this, "nbits", SFunc(this.ownerType, SLong), 20, ToNBitsCostInfo.costKind)
.withInfo(ModQ, "Encode this big integer value as NBits")

/** The following `modQ` methods are not fully implemented in v4.x and this descriptors.
* This descritors are remain here in the code and are waiting for full implementation
* is upcoming soft-forks at which point the cost parameters should be calculated and
* changed.
*/
val ModQMethod = SMethod(this, "modQ", SFunc(this.ownerType, SBigInt), 1, FixedCost(JitCost(1)))
.withInfo(ModQ, "Returns this \\lst{mod} Q, i.e. remainder of division by Q, where Q is an order of the cryprographic group.")
val PlusModQMethod = SMethod(this, "plusModQ", SFunc(IndexedSeq(this.ownerType, SBigInt), SBigInt), 2, FixedCost(JitCost(1)))
.withInfo(ModQArithOp.PlusModQ, "Adds this number with \\lst{other} by module Q.", ArgInfo("other", "Number to add to this."))
val MinusModQMethod = SMethod(this, "minusModQ", SFunc(IndexedSeq(this.ownerType, SBigInt), SBigInt), 3, FixedCost(JitCost(1)))
.withInfo(ModQArithOp.MinusModQ, "Subtracts \\lst{other} number from this by module Q.", ArgInfo("other", "Number to subtract from this."))
val MultModQMethod = SMethod(this, "multModQ", SFunc(IndexedSeq(this.ownerType, SBigInt), SBigInt), 4, FixedCost(JitCost(1)))
.withIRInfo(MethodCallIrBuilder)
.withInfo(MethodCall, "Multiply this number with \\lst{other} by module Q.", ArgInfo("other", "Number to multiply with this."))

protected override def getMethods(): Seq[SMethod] = {
if (VersionContext.current.isV6SoftForkActivated) {
super.getMethods() ++ Seq(ToNBits)
super.getMethods()
// ModQMethod,
// PlusModQMethod,
// MinusModQMethod,
Expand All @@ -495,12 +485,6 @@ case object SBigIntMethods extends SNumericTypeMethods {
}
}

/**
*
*/
def nbits_eval(mc: MethodCall, bi: sigma.BigInt)(implicit E: ErgoTreeEvaluator): Long = {
???
}

}

Expand Down
2 changes: 0 additions & 2 deletions data/shared/src/main/scala/sigma/ast/values.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1298,8 +1298,6 @@ case class MethodCall(
method: SMethod,
args: IndexedSeq[Value[SType]],
typeSubst: Map[STypeVar, SType]) extends Value[SType] {
require(method.explicitTypeArgs.forall(tyArg => typeSubst.contains(tyArg)),
s"Runtime Generic method call should have concrete type for each runtime type parameter, but was: $this")
override def companion = if (args.isEmpty) PropertyCall else MethodCall

override def opType: SFunc = SFunc(obj.tpe +: args.map(_.tpe), tpe)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@ class ErgoTreeSerializer {
* allow to use serialized scripts as pre-defined templates.
* See [[SubstConstants]] for details.
*
* Note, this operation doesn't require (de)serialization of ErgoTree expression,
* thus it is more efficient than serialization roundtrip.
*
* @param scriptBytes serialized ErgoTree with ConstantSegregationFlag set to 1.
* @param positions zero based indexes in ErgoTree.constants array which
* should be replaced with new values
Expand All @@ -304,39 +307,62 @@ class ErgoTreeSerializer {
s"expected positions and newVals to have the same length, got: positions: ${positions.toSeq},\n newVals: ${newVals.toSeq}")
val r = SigmaSerializer.startReader(scriptBytes)
val (header, _, constants, treeBytes) = deserializeHeaderWithTreeBytes(r)
val w = SigmaSerializer.startWriter()
w.put(header)
val nConstants = constants.length

val resBytes = if (VersionContext.current.isJitActivated) {
// need to measure the serialized size of the new constants
// by serializing them into a separate writer
val constW = SigmaSerializer.startWriter()

if (VersionContext.current.isJitActivated) {
// The following `constants.length` should not be serialized when segregation is off
// in the `header`, because in this case there is no `constants` section in the
// ErgoTree serialization format. Thus, applying this `substituteConstants` for
// non-segregated trees will return non-parsable ErgoTree bytes (when
// `constants.length` is put in `w`).
if (ErgoTree.isConstantSegregation(header)) {
w.putUInt(constants.length)
constW.putUInt(constants.length)
}

// The following is optimized O(nConstants + position.length) implementation
val nConstants = constants.length
if (nConstants > 0) {
val backrefs = getPositionsBackref(positions, nConstants)
cfor(0)(_ < nConstants, _ + 1) { i =>
val c = constants(i)
val iPos = backrefs(i) // index to `positions`
if (iPos == -1) {
// no position => no substitution, serialize original constant
constantSerializer.serialize(c, w)
constantSerializer.serialize(c, constW)
} else {
assert(positions(iPos) == i) // INV: backrefs and positions are mutually inverse
require(positions(iPos) == i) // INV: backrefs and positions are mutually inverse
val newConst = newVals(iPos)
require(c.tpe == newConst.tpe,
s"expected new constant to have the same ${c.tpe} tpe, got ${newConst.tpe}")
constantSerializer.serialize(newConst, w)
constantSerializer.serialize(newConst, constW)
}
}
}

val constBytes = constW.toBytes // nConstants + serialized new constants

// start composing the resulting tree bytes
val w = SigmaSerializer.startWriter()
w.put(header) // header byte

if (VersionContext.current.isV6SoftForkActivated) {
// fix in v6.0 to save tree size to respect size bit of the original tree
if (ErgoTree.hasSize(header)) {
val size = constBytes.length + treeBytes.length
w.putUInt(size) // tree size
}
}

w.putBytes(constBytes) // constants section
w.putBytes(treeBytes) // tree section
w.toBytes
} else {
val w = SigmaSerializer.startWriter()
w.put(header)

// for v4.x compatibility we save constants.length here (see the above comment to
// understand the consequences)
w.putUInt(constants.length)
Expand All @@ -357,10 +383,12 @@ class ErgoTreeSerializer {
case (c, _) =>
constantSerializer.serialize(c, w)
}

w.putBytes(treeBytes)
w.toBytes
}

w.putBytes(treeBytes)
(w.toBytes, constants.length)
(resBytes, nConstants)
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,12 @@ trait Interpreter {
val currCost = addCostChecked(context.initCost, deserializeSubstitutionCost, context.costLimit)
val context1 = context.withInitCost(currCost).asInstanceOf[CTX]
val (propTree, context2) = trySoftForkable[(SigmaPropValue, CTX)](whenSoftFork = (TrueSigmaProp, context1)) {
applyDeserializeContextJITC(context, prop)
// Before ErgoTree V3 the deserialization cost was not added to the total cost
applyDeserializeContextJITC(if (VersionContext.current.activatedVersion >= VersionContext.V6SoftForkVersion) {
context1
} else {
context
}, prop)
}

// here we assume that when `propTree` is TrueProp then `reduceToCrypto` always succeeds
Expand Down Expand Up @@ -593,4 +598,4 @@ object Interpreter {
case x => throw new Error(s"Context-dependent pre-processing should produce tree of type Boolean or SigmaProp but was $x")
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,4 @@ class MethodCallSerializerSpecification extends SerializationSpecification {
roundTripTest(expr)
}

property("MethodCall deserialization round trip for BigInt.nbits") {
def code = {
val bi = BigIntConstant(5)
val expr = MethodCall(bi,
SBigIntMethods.ToNBits,
Vector(),
Map()
)
roundTripTest(expr)
}

VersionContext.withVersions(VersionContext.V6SoftForkVersion, 1) {
code
}

an[ValidationException] should be thrownBy (
VersionContext.withVersions((VersionContext.V6SoftForkVersion - 1).toByte, 1) {
code
}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package sigma

import org.scalatest.BeforeAndAfterAll
import sigma.ast.{Apply, FixedCostItem, FuncValue, GetVar, JitCost, OptionGet, ValUse}
import sigma.eval.{EvalSettings, Profiler}
import sigma.eval.{CostDetails, EvalSettings, Profiler}
import sigmastate.CompilerCrossVersionProps
import sigmastate.interpreter.CErgoTreeEvaluator

Expand Down Expand Up @@ -137,4 +137,10 @@ abstract class LanguageSpecificationBase extends SigmaDslTesting
FixedCostItem(ValUse)
)

/** Helper method to create the given expected results for all tree versions. */
def expectedSuccessForAllTreeVersions[A](value: A, cost: Int, costDetails: CostDetails) = {
val res = ExpectedResult(Success(value), Some(cost)) -> Some(costDetails)
Seq(0, 1, 2, 3).map(version => version -> res)
}

}
Loading

0 comments on commit 3037f1a

Please sign in to comment.