Skip to content

Commit

Permalink
Merge pull request #1013 from ScorexFoundation/i993
Browse files Browse the repository at this point in the history
[6.0] Global.fromBigEndianBytes implementation
  • Loading branch information
kushti authored Oct 1, 2024
2 parents 43db8df + af792b5 commit 6fa801c
Show file tree
Hide file tree
Showing 13 changed files with 303 additions and 9 deletions.
6 changes: 5 additions & 1 deletion core/shared/src/main/scala/sigma/SigmaDsl.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package sigma

import java.math.BigInteger
import sigma.ast.SType

import java.math.BigInteger
import sigma.data._

/**
Expand Down Expand Up @@ -763,5 +764,8 @@ trait SigmaDslBuilder {

/** Returns a byte-wise XOR of the two collections of bytes. */
def xor(l: Coll[Byte], r: Coll[Byte]): Coll[Byte]

/** Returns a number decoded from provided big-endian bytes array. */
def fromBigEndianBytes[T](bytes: Coll[Byte])(implicit cT: RType[T]): T
}

Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,9 @@ object ReflectionData {
},
mkMethod(clazz, "decodePoint", Array[Class[_]](cColl)) { (obj, args) =>
obj.asInstanceOf[SigmaDslBuilder].decodePoint(args(0).asInstanceOf[Coll[Byte]])
},
mkMethod(clazz, "fromBigEndianBytes", Array[Class[_]](cColl, classOf[RType[_]])) { (obj, args) =>
obj.asInstanceOf[SigmaDslBuilder].fromBigEndianBytes(args(0).asInstanceOf[Coll[Byte]])(args(1).asInstanceOf[RType[_]])
}
)
)
Expand Down
22 changes: 21 additions & 1 deletion data/shared/src/main/scala/sigma/ast/SigmaPredef.scala
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,25 @@ object SigmaPredef {
)
)

val FromBigEndianBytesFunc = PredefinedFunc("fromBigEndianBytes",
Lambda(Seq(paramT), Array("bytes" -> SByteArray), tT, None),
irInfo = PredefFuncInfo(
irBuilder = { case (u, args) =>
val resType = u.opType.tRange.asInstanceOf[SFunc].tRange
MethodCall(
Global,
SGlobalMethods.fromBigEndianBytesMethod.withConcreteTypes(Map(tT -> resType)),
args.toIndexedSeq,
Map(tT -> resType)
)
}),
docInfo = OperationInfo(MethodCall,
"""Deserializes provided big endian bytes into a numeric value of given type.
""".stripMargin,
Seq(ArgInfo("bytes", "bytes to deserialize"))
)
)

val globalFuncs: Map[String, PredefinedFunc] = Seq(
AllOfFunc,
AnyOfFunc,
Expand Down Expand Up @@ -448,7 +467,8 @@ object SigmaPredef {
SubstConstantsFunc,
ExecuteFromVarFunc,
ExecuteFromSelfRegFunc,
SerializeFunc
SerializeFunc,
FromBigEndianBytesFunc
).map(f => f.name -> f).toMap

def comparisonOp(symbolName: String, opDesc: ValueCompanion, desc: String, args: Seq[ArgInfo]) = {
Expand Down
17 changes: 15 additions & 2 deletions data/shared/src/main/scala/sigma/ast/methods.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sigma.ast

import org.ergoplatform._
import org.ergoplatform.validation._
import sigma.Evaluation.stypeToRType
import sigma._
import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray}
import sigma.ast.SMethod.{MethodCallIrBuilder, MethodCostFunc, javaMethodOf}
Expand Down Expand Up @@ -480,7 +481,6 @@ case object SBigIntMethods extends SNumericTypeMethods {
}
}


}

/** Methods of type `String`. */
Expand Down Expand Up @@ -1691,6 +1691,18 @@ case object SGlobalMethods extends MonoTypeMethods {
Xor.xorWithCosting(ls, rs)
}

private val BigEndianBytesCostKind = FixedCost(JitCost(10))

// id = 4 is reserved for deserializeTo ()
lazy val fromBigEndianBytesMethod = SMethod(
this, "fromBigEndianBytes", SFunc(Array(SGlobal, SByteArray), tT, Array(paramT)), 5, BigEndianBytesCostKind, Seq(tT))
.withIRInfo(MethodCallIrBuilder,
javaMethodOf[SigmaDslBuilder, Coll[Byte], RType[_]]("fromBigEndianBytes"),
{ mtype => Array(mtype.tRange) })
.withInfo(MethodCall,
"Decode a number from big endian bytes.",
ArgInfo("first", "Bytes which are big-endian encoded number."))

lazy val serializeMethod = SMethod(this, "serialize",
SFunc(Array(SGlobal, tT), SByteArray, Array(paramT)), 3, DynamicCost)
.withIRInfo(MethodCallIrBuilder)
Expand Down Expand Up @@ -1725,7 +1737,8 @@ case object SGlobalMethods extends MonoTypeMethods {
Seq(
groupGeneratorMethod,
xorMethod,
serializeMethod
serializeMethod,
fromBigEndianBytesMethod
)
} else {
Seq(
Expand Down
37 changes: 37 additions & 0 deletions data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ import debox.cfor
import org.ergoplatform.ErgoBox
import org.ergoplatform.validation.ValidationRules
import scorex.crypto.hash.{Blake2b256, Sha256}
import scorex.utils.{Ints, Longs}
import sigma.ast.{AtLeast, SBigInt, SubstConstants}
import scorex.utils.Longs
import sigma.ast.{AtLeast, SType, SubstConstants}
import sigma.crypto.{CryptoConstants, EcPointType, Ecp}
import sigma.eval.Extensions.EvalCollOps
import sigma.serialization.{DataSerializer, GroupElementSerializer, SigmaSerializer}
import sigma.serialization.{GroupElementSerializer, SerializerException, SigmaSerializer}
import sigma.util.Extensions.BigIntegerOps
import sigma.validation.SigmaValidationSettings
import sigma.{AvlTree, BigInt, Box, Coll, CollBuilder, Evaluation, GroupElement, SigmaDslBuilder, SigmaProp, VersionContext}
Expand Down Expand Up @@ -201,6 +204,40 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl =>
this.GroupElement(p)
}

override def fromBigEndianBytes[T](bytes: Coll[Byte])(implicit cT: RType[T]): T = {
cT match {
case sigma.ByteType => if (bytes.length != 1) {
throw new IllegalArgumentException("To deserialize SByte with fromBigEndianBytes, exactly one byte should be provided")
} else {
bytes.apply(0).asInstanceOf[T]
}
case sigma.ShortType => if (bytes.length != 2) {
throw new IllegalArgumentException("To deserialize SShort with fromBigEndianBytes, exactly two bytes should be provided")
} else {
val b0 = bytes(0)
val b1 = bytes(1)
((b0 & 0xFF) << 8 | (b1 & 0xFF)).toShort.asInstanceOf[T]
}
case sigma.IntType => if (bytes.length != 4) {
throw new IllegalArgumentException("To deserialize SInt with fromBigEndianBytes, exactly four bytes should be provided")
} else {
Ints.fromByteArray(bytes.toArray).asInstanceOf[T]
}
case sigma.LongType => if (bytes.length != 8) {
throw new IllegalArgumentException("To deserialize SLong with fromBigEndianBytes, exactly eight bytes should be provided")
} else {
Longs.fromByteArray(bytes.toArray).asInstanceOf[T]
}
case sigma.BigIntRType =>
if (bytes.length > SBigInt.MaxSizeInBytes) {
throw SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes in fromBigEndianBytes")
}
CBigInt(new BigInteger(bytes.toArray).to256BitValueExact).asInstanceOf[T]
// todo: UnsignedBitInt
case _ => throw new IllegalArgumentException("Unsupported type provided in fromBigEndianBytes")
}
}

/** Serializes the given `value` into bytes using the default serialization format. */
override def serialize[T](value: T)(implicit cT: RType[T]): Coll[Byte] = {
val tpe = Evaluation.rtypeToSType(cT)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,10 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext =>
case SGlobalMethods.serializeMethod.name =>
val value = asRep[Any](argsV(0))
g.serialize(value)
case SGlobalMethods.fromBigEndianBytesMethod.name =>
val bytes = asRep[Coll[Byte]](argsV(0))
val cT = stypeToElem(method.stype.tRange.withSubstTypes(typeSubst))
g.fromBigEndianBytes(bytes)(cT)
case _ => throwError()
}
case (x: Ref[tNum], _: SNumericTypeMethods) => method.name match {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package sigma.compiler.ir

import sigma.ast.SType
import sigma.compiler.ir.primitives.Thunks
import sigma.data.RType
import sigma.reflection.ReflectionData.registerClassEntry
Expand Down Expand Up @@ -510,6 +511,9 @@ object GraphIRReflection {
},
mkMethod(clazz, "serialize", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
obj.asInstanceOf[ctx.SigmaDslBuilder].serialize(args(0).asInstanceOf[ctx.Ref[Any]])
},
mkMethod(clazz, "fromBigEndianBytes", Array[Class[_]](classOf[Base#Ref[_]], classOf[TypeDescs#Elem[_]])) { (obj, args) =>
obj.asInstanceOf[ctx.SigmaDslBuilder].fromBigEndianBytes(args(0).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]])(args(1).asInstanceOf[ctx.Elem[SType]])
}
)
)
Expand Down
2 changes: 2 additions & 0 deletions sc/shared/src/main/scala/sigma/compiler/ir/TreeBuilding.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package sigma.compiler.ir

import org.ergoplatform._
import sigma.Evaluation.{rtypeToSType, stypeToRType}
import sigma.ast.SType.tT
import sigma.ast._
import sigma.ast.syntax.{ValueOps, _}
import sigma.data.{ProveDHTuple, ProveDlog}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ import scalan._
def avlTree(operationFlags: Ref[Byte], digest: Ref[Coll[Byte]], keyLength: Ref[Int], valueLengthOpt: Ref[WOption[Int]]): Ref[AvlTree];
def xor(l: Ref[Coll[Byte]], r: Ref[Coll[Byte]]): Ref[Coll[Byte]]
def serialize[T](value: Ref[T]): Ref[Coll[Byte]]
def fromBigEndianBytes[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T]
};
trait CostModelCompanion;
trait BigIntCompanion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1971,6 +1971,13 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") {
true, false, element[Coll[Byte]]))
}

override def fromBigEndianBytes[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T] = {
asRep[T](mkMethodCall(self,
SigmaDslBuilderClass.getMethod("fromBigEndianBytes", classOf[Sym], classOf[Elem[T]]),
Array[AnyRef](bytes, cT, Map(tT -> Evaluation.rtypeToSType(cT.sourceType))),
true, false, cT))
}

}

implicit object LiftableSigmaDslBuilder
Expand Down Expand Up @@ -2137,6 +2144,13 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") {
Array[AnyRef](value),
true, true, element[Coll[Byte]]))
}

def fromBigEndianBytes[T](bytes: Ref[Coll[Byte]])(implicit cT: Elem[T]): Ref[T] = {
asRep[T](mkMethodCall(source,
SigmaDslBuilderClass.getMethod("fromBigEndianBytes", classOf[Sym], classOf[Elem[T]]),
Array[AnyRef](bytes, cT),
true, true, cT, Map(tT -> Evaluation.rtypeToSType(cT.sourceType))))
}
}

// entityUnref: single unref method for each type family
Expand All @@ -2154,7 +2168,9 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") {
override protected def collectMethods: Map[RMethod, MethodDesc] = {
super.collectMethods ++
Elem.declaredMethods(RClass(classOf[SigmaDslBuilder]), RClass(classOf[SSigmaDslBuilder]), Set(
"Colls", "verifyZK", "atLeast", "allOf", "allZK", "anyOf", "anyZK", "xorOf", "sigmaProp", "blake2b256", "sha256", "byteArrayToBigInt", "longToByteArray", "byteArrayToLong", "proveDlog", "proveDHTuple", "groupGenerator", "substConstants", "decodePoint", "avlTree", "xor"
"Colls", "verifyZK", "atLeast", "allOf", "allZK", "anyOf", "anyZK", "xorOf", "sigmaProp", "blake2b256", "sha256",
"byteArrayToBigInt", "longToByteArray", "byteArrayToLong", "proveDlog", "proveDHTuple", "groupGenerator", "substConstants",
"decodePoint", "avlTree", "xor", "serialize", "fromBigEndianBytes"
))
}
}
Expand Down
94 changes: 91 additions & 3 deletions sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ import sigma.ast.ErgoTree.ZeroHeader
import sigma.ast.SCollection.SByteArray
import sigma.ast.syntax.TrueSigmaProp
import sigma.ast.{SInt, _}
import sigma.data.{CBigInt, CBox, CHeader, ExactNumeric}
import sigma.data.{CBigInt, CBox, CHeader, CSigmaDslBuilder, ExactNumeric, RType}
import sigma.eval.{CostDetails, SigmaDsl, TracedCost}
import sigma.serialization.ValueCodes.OpCode
import sigma.data.{RType}
import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps}
import sigma.util.Extensions.{BooleanOps, IntOps}
import sigmastate.exceptions.MethodNotFound
import sigmastate.utils.Extensions.ByteOpsForSigma
import sigmastate.utils.Helpers
Expand Down Expand Up @@ -1523,4 +1522,93 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite =>
)
}


property("Global - fromBigEndianBytes") {
import sigma.data.OrderingOps.BigIntOrdering

def byteFromBigEndianBytes: Feature[Byte, Boolean] = {
newFeature(
{ (x: Byte) => CSigmaDslBuilder.fromBigEndianBytes[Byte](Colls.fromArray(Array(x))) == x},
"{ (x: Byte) => fromBigEndianBytes[Byte](x.toBytes) == x }",
sinceVersion = VersionContext.V6SoftForkVersion
)
}

verifyCases(
Seq(
5.toByte -> new Expected(ExpectedResult(Success(true), None)),
Byte.MaxValue -> new Expected(ExpectedResult(Success(true), None)),
Byte.MinValue -> new Expected(ExpectedResult(Success(true), None))
),
byteFromBigEndianBytes
)

def shortFromBigEndianBytes: Feature[Short, Boolean] = {
newFeature(
{ (x: Short) => CSigmaDslBuilder.fromBigEndianBytes[Short](Colls.fromArray(Shorts.toByteArray(x))) == x},
"{ (x: Short) => fromBigEndianBytes[Short](x.toBytes) == x }",
sinceVersion = VersionContext.V6SoftForkVersion
)
}

verifyCases(
Seq(
5.toShort -> new Expected(ExpectedResult(Success(true), None)),
Short.MaxValue -> new Expected(ExpectedResult(Success(true), None)),
Short.MinValue -> new Expected(ExpectedResult(Success(true), None))
),
shortFromBigEndianBytes
)

def intFromBigEndianBytes: Feature[Int, Boolean] = {
newFeature(
{ (x: Int) => CSigmaDslBuilder.fromBigEndianBytes[Int](Colls.fromArray(Ints.toByteArray(x))) == x},
"{ (x: Int) => fromBigEndianBytes[Int](x.toBytes) == x }",
sinceVersion = VersionContext.V6SoftForkVersion
)
}

verifyCases(
Seq(
5 -> new Expected(ExpectedResult(Success(true), None)),
Int.MaxValue -> new Expected(ExpectedResult(Success(true), None))
),
intFromBigEndianBytes
)

def longFromBigEndianBytes: Feature[Long, Boolean] = {
newFeature(
{ (x: Long) => CSigmaDslBuilder.fromBigEndianBytes[Long](Colls.fromArray(Longs.toByteArray(x))) == x},
"{ (x: Long) => fromBigEndianBytes[Long](x.toBytes) == x }",
sinceVersion = VersionContext.V6SoftForkVersion
)
}

verifyCases(
Seq(
5L -> new Expected(ExpectedResult(Success(true), None)),
Long.MinValue -> new Expected(ExpectedResult(Success(true), None))
),
longFromBigEndianBytes
)

def bigIntFromBigEndianBytes: Feature[BigInt, Boolean] = {
newFeature(
{ (x: BigInt) => CSigmaDslBuilder.fromBigEndianBytes[BigInt](x.toBytes) == x},
"{ (x: BigInt) => Global.fromBigEndianBytes[BigInt](x.toBytes) == x }",
sinceVersion = VersionContext.V6SoftForkVersion
)
}

verifyCases(
Seq(
CBigInt(BigInteger.valueOf(50)) -> new Expected(ExpectedResult(Success(true), None)),
CBigInt(BigInteger.valueOf(-500000000000L)) -> new Expected(ExpectedResult(Success(true), None)),
CBigInt(sigma.crypto.CryptoConstants.groupOrder.divide(BigInteger.valueOf(2))) -> new Expected(ExpectedResult(Success(true), None))
),
bigIntFromBigEndianBytes
)

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,8 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit with C
(SGlobal.typeId, Seq(
MInfo(1, groupGeneratorMethod), MInfo(2, xorMethod)
) ++ (if (isV6Activated) {
Seq(MInfo(3, serializeMethod)) // methods added in v6.0
// id = 4 reserved for deserializeTo method
Seq(MInfo(3, serializeMethod), MInfo(5, fromBigEndianBytesMethod)) // methods added in v6.0
} else {
Seq.empty[MInfo]
}), true)
Expand Down
Loading

0 comments on commit 6fa801c

Please sign in to comment.