Skip to content

Commit

Permalink
Merge pull request #996 from ScorexFoundation/i991-fix-bigint-serialize
Browse files Browse the repository at this point in the history
Check BigInt size on serialization
  • Loading branch information
aslesarenko authored Jun 6, 2024
2 parents 1896118 + 14d9525 commit 4e32261
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package sigma.serialization
import debox.cfor
import sigma.ast._
import sigma.data._
import sigma.util.Extensions.{CoreAvlTreeOps, BigIntOps, GroupElementOps, SigmaPropOps}
import sigma.util.Extensions.{BigIntOps, BigIntegerOps, CoreAvlTreeOps, GroupElementOps, SigmaPropOps}
import sigma.validation.ValidationRules.CheckSerializableTypeCode
import sigma.{Evaluation, _}

Expand All @@ -30,7 +30,9 @@ class CoreDataSerializer {
w.putUInt(bytes.length)
w.putBytes(bytes)
case SBigInt =>
val data = v.asInstanceOf[BigInt].toBigInteger.toByteArray
val bi = v.asInstanceOf[BigInt].toBigInteger
require(bi.fitsIn256Bits, s"BigInt value $bi doesn't fit into 256 bits")
val data = bi.toByteArray
w.putUShort(data.length)
w.putBytes(data)
case SGroupElement =>
Expand Down
23 changes: 14 additions & 9 deletions core/shared/src/main/scala/sigma/util/Extensions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,19 @@ object Extensions {
throw new ArithmeticException("BigInteger out of byte range")
}

/** Checks this {@code BigInteger} can be cust to 256 bit two's-compliment representation,
/** Checks this {@code BigInteger} can be cast to 256 bit two's-compliment representation. */
@inline def fitsIn256Bits: Boolean = {
// Comparing with 255 is correct because bitLength() method excludes the sign bit.
// For example, these are the boundary values:
// (new BigInteger("80" + "00" * 31, 16)).bitLength() = 256
// (new BigInteger("7F" + "ff" * 31, 16)).bitLength() = 255
// (new BigInteger("-7F" + "ff" * 31, 16)).bitLength() = 255
// (new BigInteger("-80" + "00" * 31, 16)).bitLength() = 255
// (new BigInteger("-80" + "00" * 30 + "01", 16)).bitLength() = 256
x.bitLength() <= 255
}

/** Checks this {@code BigInteger} can be cast to 256 bit two's-compliment representation,
* checking for lost information. If the value of this {@code BigInteger}
* is out of the range of the 256 bits, then an {@code ArithmeticException} is thrown.
*
Expand All @@ -205,14 +217,7 @@ object Extensions {
* @see BigInteger#longValueExact
*/
@inline final def to256BitValueExact: BigInteger = {
// Comparing with 255 is correct because bitLength() method excludes the sign bit.
// For example, these are the boundary values:
// (new BigInteger("80" + "00" * 31, 16)).bitLength() = 256
// (new BigInteger("7F" + "ff" * 31, 16)).bitLength() = 255
// (new BigInteger("-7F" + "ff" * 31, 16)).bitLength() = 255
// (new BigInteger("-80" + "00" * 31, 16)).bitLength() = 255
// (new BigInteger("-80" + "00" * 30 + "01", 16)).bitLength() = 256
if (x.bitLength() <= 255) x
if (fitsIn256Bits) x
else
throw new ArithmeticException("BigInteger out of 256 bit range");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,20 @@ class DataSerializerSpecification extends SerializationSpecification {
t.getMessage.contains(s"Length of tuple $len exceeds ${0xFFFF} limit.")
})

val tooBig = SigmaDsl.BigInt(new BigInteger(Helpers.decodeBytes(
"80e0ff7f02807fff72807f0a00ff7fb7c57f75c11ba2802970fd250052807fc37f6480ffff007fff18eeba44").toArray))
val tooBigBytes = Helpers.decodeBytes(
"80e0ff7f02807fff72807f0a00ff7fb7c57f75c11ba2802970fd250052807fc37f6480ffff007fff18eeba44").toArray
val tooBig = SigmaDsl.BigInt(new BigInteger(tooBigBytes))

assertExceptionThrown({
val w = SigmaSerializer.startWriter()
DataSerializer.serialize(tooBig.asWrappedType, SBigInt, w)
val r = SigmaSerializer.startReader(w.toBytes)
val w = SigmaSerializer.startWriter()
DataSerializer.serialize(tooBig.asWrappedType, SBigInt, w)
},
exceptionLike[IllegalArgumentException]("doesn't fit into 256 bits")
)

assertExceptionThrown({
val bytes = SigmaSerializer.startWriter().putUShort(tooBigBytes.length).toBytes ++ tooBigBytes
val r = SigmaSerializer.startReader(bytes)
DataSerializer.deserialize(SBigInt, r)
},
{ t =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,12 @@ class OracleExamplesSpecification extends CompilerTestingCommons
*
*
*/
property("oracle example") {
// TODO v6.0: re-implement this example using UBigInt type
// Note, the value `z` computed in the test doesn't fit into BigInt type.
// This makes the oracleBox.bytes fail deserialization and thus, such box cannot be
// accepted by the blockchain (see assertExceptionThrown in the test).
// This test is `ignored` after fitsIn256Bits check is added to SBigInt serialization.
ignore("oracle example") {
val oracle = new ContextEnrichingTestProvingInterpreter
val aliceTemplate = new ContextEnrichingTestProvingInterpreter
val bob = new ContextEnrichingTestProvingInterpreter
Expand Down Expand Up @@ -113,6 +118,11 @@ class OracleExamplesSpecification extends CompilerTestingCommons
boxIndex = 1
)

assertExceptionThrown(
oracleBox.bytes,
exceptionLike[IllegalArgumentException]("doesn't fit into 256 bits")
)

val avlProver = new BatchAVLProver[Digest32, Blake2b256.type](keyLength = 32, None)

avlProver.performOneOperation(Insert(ADKey @@@ oracleBox.id, ADValue @@ oracleBox.bytes))
Expand Down

0 comments on commit 4e32261

Please sign in to comment.