From ac4bbbce2a1f8696356bbc1cce79129523b51b5d Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 6 Nov 2024 00:41:28 +0300 Subject: [PATCH] unsigned encoding, .toBytes & .toBits tests passing --- .../main/scala/sigma/crypto/BigIntegers.scala | 16 +++--- .../src/main/scala/sigma/data/CBigInt.scala | 3 +- .../serialization/CoreDataSerializer.scala | 9 ++-- .../scala/sigma/data/CSigmaDslBuilder.scala | 4 +- .../sigma/data/UnsignedBigIntegerOps.scala | 2 +- .../sigma/compiler/ir/GraphBuilding.scala | 51 ++++++++++--------- .../utxo/BasicOpsSpecification.scala | 20 ++++---- 7 files changed, 57 insertions(+), 48 deletions(-) rename {data => core}/shared/src/main/scala/sigma/crypto/BigIntegers.scala (90%) diff --git a/data/shared/src/main/scala/sigma/crypto/BigIntegers.scala b/core/shared/src/main/scala/sigma/crypto/BigIntegers.scala similarity index 90% rename from data/shared/src/main/scala/sigma/crypto/BigIntegers.scala rename to core/shared/src/main/scala/sigma/crypto/BigIntegers.scala index 4465184580..54a6927924 100644 --- a/data/shared/src/main/scala/sigma/crypto/BigIntegers.scala +++ b/core/shared/src/main/scala/sigma/crypto/BigIntegers.scala @@ -13,8 +13,9 @@ object BigIntegers { private val MAX_ITERATIONS = 1000 /** Create the given number of random bits. + * * @param bitLength the number of random bits to create. - * @param random a source of randomness. + * @param random a source of randomness. * @return a byte array containing random bits. */ @throws[IllegalArgumentException] @@ -38,8 +39,8 @@ object BigIntegers { * @return a positive BigInteger */ def createRandomBigInteger( - bitLength: Int, - random: SecureRandom): BigInteger = { + bitLength: Int, + random: SecureRandom): BigInteger = { new BigInteger(1, createRandom(bitLength, random)) } @@ -52,9 +53,9 @@ object BigIntegers { * @return a random BigInteger value in the range [min,max] */ def createRandomInRange( - min: BigInteger, - max: BigInteger, - random: SecureRandom): BigInteger = { + min: BigInteger, + max: BigInteger, + random: SecureRandom): BigInteger = { val cmp = min.compareTo(max) if (cmp >= 0) { if (cmp > 0) throw new IllegalArgumentException("'min' may not be greater than 'max'") @@ -64,7 +65,7 @@ object BigIntegers { if (min.bitLength > max.bitLength / 2) return createRandomInRange(ZERO, max.subtract(min), random).add(min) - for ( _ <- 0 until MAX_ITERATIONS ) { + for (_ <- 0 until MAX_ITERATIONS) { val x = createRandomBigInteger(max.bitLength, random) if (x.compareTo(min) >= 0 && x.compareTo(max) <= 0) return x } @@ -96,6 +97,7 @@ object BigIntegers { /** Converts a byte array to a BigInteger, treating the array as bits of the unsigned * integer. + * * @param buf the byte array to convert * @return the resulting positive BigInteger */ diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala index 0313c197e5..9292ada68a 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -1,5 +1,6 @@ package sigma.data +import sigma.crypto.BigIntegers import sigma.util.Extensions.BigIntegerOps import sigma.{BigInt, Coll, Colls, UnsignedBigInt} @@ -83,7 +84,7 @@ case class CUnsignedBigInt(override val wrappedValue: BigInteger) extends Unsign override def toLong: Long = wrappedValue.toLongExact - override def toBytes: Coll[Byte] = Colls.fromArray(wrappedValue.toByteArray) + override def toBytes: Coll[Byte] = Colls.fromArray(BigIntegers.asUnsignedByteArray(wrappedValue)) override def compareTo(that: UnsignedBigInt): Int = wrappedValue.compareTo(that.asInstanceOf[CUnsignedBigInt].wrappedValue) diff --git a/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala b/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala index 5aa7d7600a..0d705412fc 100644 --- a/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala +++ b/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala @@ -2,8 +2,9 @@ package sigma.serialization import debox.cfor import sigma.ast._ +import sigma.crypto.BigIntegers import sigma.data._ -import sigma.util.Extensions.{CoreAvlTreeOps, BigIntOps, GroupElementOps, SigmaPropOps} +import sigma.util.Extensions.{BigIntOps, CoreAvlTreeOps, GroupElementOps, SigmaPropOps} import sigma.validation.ValidationRules.CheckSerializableTypeCode import sigma.{Evaluation, _} @@ -34,7 +35,7 @@ class CoreDataSerializer { w.putUShort(data.length) w.putBytes(data) case SUnsignedBigInt => // todo: versioning - val data = v.asInstanceOf[CUnsignedBigInt].wrappedValue.toByteArray + val data = BigIntegers.asUnsignedByteArray(v.asInstanceOf[CUnsignedBigInt].wrappedValue) w.putUShort(data.length) w.putBytes(data) case SGroupElement => @@ -114,11 +115,11 @@ class CoreDataSerializer { CBigInt(new BigInteger(valueBytes)) case SUnsignedBigInt => // todo: versioning val size: Short = r.getUShort().toShort - if (size > SBigInt.MaxSizeInBytes + 1) { //todo: use encoding with no sign bit + if (size > SBigInt.MaxSizeInBytes) { throw new SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes: $size") } val valueBytes = r.getBytes(size) - CUnsignedBigInt(new BigInteger(valueBytes)) + CUnsignedBigInt(BigIntegers.fromUnsignedByteArray(valueBytes)) case SGroupElement => CGroupElement(GroupElementSerializer.parse(r)) case SSigmaProp => diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index 661cd183a1..89fcffb531 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -7,7 +7,7 @@ import scorex.crypto.hash.{Blake2b256, Sha256} import scorex.utils.{Ints, Longs} import sigma.ast.{AtLeast, SBigInt, SType, SUnsignedBigInt, SubstConstants} import scorex.utils.Longs -import sigma.crypto.{CryptoConstants, EcPointType, Ecp} +import sigma.crypto.{BigIntegers, CryptoConstants, EcPointType, Ecp} import sigma.eval.Extensions.EvalCollOps import sigma.serialization.{DataSerializer, GroupElementSerializer, SigmaSerializer} import sigma.serialization.{GroupElementSerializer, SerializerException, SigmaSerializer} @@ -238,7 +238,7 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => if (bytes.length > SUnsignedBigInt.MaxSizeInBytes) { throw SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes in fromBigEndianBytes") } - CUnsignedBigInt(new BigInteger(bytes.toArray).toSignedBigIntValueExact).asInstanceOf[T] + CUnsignedBigInt(BigIntegers.fromUnsignedByteArray(bytes.toArray)).asInstanceOf[T] case _ => throw new IllegalArgumentException("Unsupported type provided in fromBigEndianBytes") } } diff --git a/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala index abbee376a5..e66740b1ea 100644 --- a/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/UnsignedBigIntegerOps.scala @@ -95,7 +95,7 @@ object UnsignedBigIntNumericOps { * For example, the `Int` value `0x12131415` would yield the * collection of bytes [0x12, 0x13, 0x14, 0x15] */ - override def toBigEndianBytes(x: UnsignedBigInt): Coll[Byte] = ??? + override def toBigEndianBytes(x: UnsignedBigInt): Coll[Byte] = x.toBytes /** * @return a numeric value which is inverse of `x` (every bit, including sign, is flipped) diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala index 6048d884d1..caeded999c 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala @@ -1032,28 +1032,6 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => opt.filter(asRep[t => Boolean](argsV(0))) case _ => throwError() } - case (ubi: Ref[UnsignedBigInt]@unchecked, SUnsignedBigIntMethods) => method.name match { - case SUnsignedBigIntMethods.ModMethod.name => - val m = asRep[UnsignedBigInt](argsV(0)) - ubi.mod(m) - case SUnsignedBigIntMethods.ModInverseMethod.name => - val m = asRep[UnsignedBigInt](argsV(0)) - ubi.modInverse(m) - case SUnsignedBigIntMethods.PlusModMethod.name => - val that = asRep[UnsignedBigInt](argsV(0)) - val m = asRep[UnsignedBigInt](argsV(1)) - ubi.plusMod(that, m) - case SUnsignedBigIntMethods.SubtractModMethod.name => - val that = asRep[UnsignedBigInt](argsV(0)) - val m = asRep[UnsignedBigInt](argsV(1)) - ubi.subtractMod(that, m) - case SUnsignedBigIntMethods.MultiplyModMethod.name => - val that = asRep[UnsignedBigInt](argsV(0)) - val m = asRep[UnsignedBigInt](argsV(1)) - ubi.multiplyMod(that, m) - case SUnsignedBigIntMethods.ToSignedMethod.name => - ubi.toSigned - } case (ge: Ref[GroupElement]@unchecked, SGroupElementMethods) => method.name match { case SGroupElementMethods.GetEncodedMethod.name => ge.getEncoded @@ -1226,7 +1204,7 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => g.fromBigEndianBytes(bytes)(cT) case _ => throwError() } - case (x: Ref[tNum], _: SNumericTypeMethods) => method.name match { + case (x: Ref[tNum], ms: SNumericTypeMethods) => method.name match { case SNumericTypeMethods.ToBytesMethod.name => val op = NumericToBigEndianBytes(elemToExactNumeric(x.elem)) ApplyUnOp(op, x) @@ -1263,6 +1241,33 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext => val bi = asRep[BigInt](x) val m = asRep[UnsignedBigInt](argsV(0)) bi.toUnsignedMod(m) + + case SUnsignedBigIntMethods.ModMethod.name if ms.isInstanceOf[SUnsignedBigIntMethods.type] => + val ubi = asRep[UnsignedBigInt](x) + val m = asRep[UnsignedBigInt](argsV(0)) + ubi.mod(m) + case SUnsignedBigIntMethods.ModInverseMethod.name if ms.isInstanceOf[SUnsignedBigIntMethods.type] => + val ubi = asRep[UnsignedBigInt](x) + val m = asRep[UnsignedBigInt](argsV(0)) + ubi.modInverse(m) + case SUnsignedBigIntMethods.PlusModMethod.name if ms.isInstanceOf[SUnsignedBigIntMethods.type] => + val ubi = asRep[UnsignedBigInt](x) + val that = asRep[UnsignedBigInt](argsV(0)) + val m = asRep[UnsignedBigInt](argsV(1)) + ubi.plusMod(that, m) + case SUnsignedBigIntMethods.SubtractModMethod.name if ms.isInstanceOf[SUnsignedBigIntMethods.type] => + val ubi = asRep[UnsignedBigInt](x) + val that = asRep[UnsignedBigInt](argsV(0)) + val m = asRep[UnsignedBigInt](argsV(1)) + ubi.subtractMod(that, m) + case SUnsignedBigIntMethods.MultiplyModMethod.name if ms.isInstanceOf[SUnsignedBigIntMethods.type] => + val ubi = asRep[UnsignedBigInt](x) + val that = asRep[UnsignedBigInt](argsV(0)) + val m = asRep[UnsignedBigInt](argsV(1)) + ubi.multiplyMod(that, m) + case SUnsignedBigIntMethods.ToSignedMethod.name if ms.isInstanceOf[SUnsignedBigIntMethods.type] => + val ubi = asRep[UnsignedBigInt](x) + ubi.toSigned() case _ => throwError() } case _ => throwError(s"Type ${stypeToRType(obj.tpe).name} doesn't have methods") diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index dc95969b08..9a7afd40a4 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -30,6 +30,7 @@ import sigma.eval.EvalSettings import sigma.exceptions.InvalidType import sigma.serialization.{ErgoTreeSerializer, SerializerException} import sigma.interpreter.{ContextExtension, ProverResult} +import sigma.validation.ValidationException import sigmastate.utils.Helpers import sigmastate.utils.Helpers._ @@ -797,7 +798,7 @@ class BasicOpsSpecification extends CompilerTestingCommons property("UnsignedBigInt.toBits") { def toBitsTest() = test("UnsignedBigInt.toBits", env, ext, s"""{ - | val b = bigInt("${CryptoConstants.groupOrder}") + | val b = unsignedBigInt("${CryptoConstants.groupOrder}") | val ba = b.toBits | ba.size == 256 |}""".stripMargin, @@ -807,7 +808,7 @@ class BasicOpsSpecification extends CompilerTestingCommons if (VersionContext.current.isV6SoftForkActivated) { toBitsTest() } else { - an[SerializerException] shouldBe thrownBy(toBitsTest()) + an[ValidationException] shouldBe thrownBy(toBitsTest()) } } @@ -1516,18 +1517,17 @@ class BasicOpsSpecification extends CompilerTestingCommons } property("UnsignedBigInt.toBytes") { - def toBytesTest() = test("UnsignedBigInt.toBytes", env, ext, - s"""{ - | val l = bigInt("${CryptoConstants.groupOrder}") - | l.toBytes.size == 32 - | }""".stripMargin, - null - ) + val script = s"""{ + | val l = unsignedBigInt("${CryptoConstants.groupOrder}") + | l.toBytes.size == 32 + | }""".stripMargin + + def toBytesTest() = test("UnsignedBigInt.toBytes", env, ext, script, null) if (VersionContext.current.isV6SoftForkActivated) { toBytesTest() } else { - an[SerializerException] shouldBe thrownBy(toBytesTest()) + an[ValidationException] shouldBe thrownBy(toBytesTest()) } }