Skip to content

Commit 3dae10d

Browse files
authored
Merge pull request #64 from input-output-hk/manifest-ser
Manifest (de-)serialization fix
2 parents b8c6f57 + 7d18c6a commit 3dae10d

File tree

9 files changed

+48
-39
lines changed

9 files changed

+48
-39
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ resolvers += "Sonatype Releases" at "https://oss.sonatype.org/content/repositori
1818

1919
You can use Scrypto in your sbt project by simply adding the following dependency to your build file:
2020
```scala
21-
libraryDependencies += "org.scorexfoundation" %% "scrypto" % "2.1.5"
21+
libraryDependencies += "org.scorexfoundation" %% "scrypto" % "2.1.6"
2222
```
2323

2424
### Hash functions

build.sbt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ name := "scrypto"
44

55
lazy val commonSettings = Seq(
66
organization := "org.scorexfoundation",
7-
version := "2.1.5",
8-
scalaVersion := "2.12.7",
7+
version := "2.1.6",
8+
scalaVersion := "2.12.8",
99
resolvers += Resolver.sonatypeRepo("public"),
1010
licenses := Seq("CC0" -> url("https://creativecommons.org/publicdomain/zero/1.0/legalcode")),
1111
homepage := Some(url("https://github.com/input-output-hk/scrypto")),

lock.sbt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ dependencyOverrides in ThisBuild ++= Seq(
66
"ch.qos.logback" % "logback-core" % "1.3.0-alpha4",
77
"com.google.guava" % "guava" % "21.0",
88
"com.sun.mail" % "javax.mail" % "1.6.0",
9-
"com.typesafe.scala-logging" % "scala-logging_2.12" % "3.9.0",
9+
"com.typesafe.scala-logging" % "scala-logging_2.12" % "3.9.2",
1010
"javax.activation" % "activation" % "1.1",
1111
"org.bouncycastle" % "bcprov-jdk15on" % "1.60",
1212
"org.rudogma" % "supertagged_2.12" % "1.4",
1313
"org.scorexfoundation" % "scorex-util_2.12" % "0.1.1",
1414
"org.slf4j" % "slf4j-api" % "1.8.0-beta1",
1515
"org.whispersystems" % "curve25519-java" % "0.5.0"
1616
)
17-
// LIBRARY_DEPENDENCIES_HASH 5bccd31374d5a2878cfcd4fc35339324fc38aea3
17+
// LIBRARY_DEPENDENCIES_HASH 09aa9b478d26dc10e237ea6c0f62986425561937

release-notes.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
**2.1.6**
2+
---------
3+
4+
* Manifest deserialization now checks that valueLength is not negative
5+
6+
17
**2.1.5**
28
---------
39

src/main/scala/scorex/crypto/authds/avltree/batch/BatchAVLProver.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import scorex.utils.ByteArray
88

99
import scala.annotation.tailrec
1010
import scala.collection.mutable
11-
import scala.collection.mutable.ArrayBuffer
1211
import scala.util.{Failure, Random, Success, Try}
1312

1413

src/main/scala/scorex/crypto/authds/avltree/batch/serialization/BatchAVLProverManifest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ import scorex.crypto.hash.{CryptographicHash, Digest}
99
*/
1010
case class BatchAVLProverManifest[D <: Digest, HF <: CryptographicHash[D]](keyLength: Int,
1111
valueLengthOpt: Option[Int],
12-
oldRootAndHeight: (ProverNodes[D], Int))
12+
rootAndHeight: (ProverNodes[D], Int))

src/main/scala/scorex/crypto/authds/avltree/batch/serialization/BatchAVLProverSerializer.scala

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class BatchAVLProverSerializer[D <: Digest, HF <: CryptographicHash[D]](implicit
5555
* Combine tree pieces into one big tree
5656
*/
5757
def combine(sliced: SlicedTree): Try[BatchAVLProver[D, HF]] = Try {
58-
sliced._1.oldRootAndHeight._1 match {
58+
sliced._1.rootAndHeight._1 match {
5959
case tn: InternalProverNode[D] =>
6060
def mutateLoop(n: ProverNodes[D]): Unit = n match {
6161
case n: ProxyInternalNode[D] if n.isEmpty =>
@@ -70,26 +70,27 @@ class BatchAVLProverSerializer[D <: Digest, HF <: CryptographicHash[D]](implicit
7070
}
7171

7272
mutateLoop(tn)
73-
new BatchAVLProver[D, HF](sliced._1.keyLength, sliced._1.valueLengthOpt, Some(sliced._1.oldRootAndHeight))
74-
case l: ProverLeaf[D] =>
75-
new BatchAVLProver[D, HF](sliced._1.keyLength, sliced._1.valueLengthOpt, Some(sliced._1.oldRootAndHeight))
73+
new BatchAVLProver[D, HF](sliced._1.keyLength, sliced._1.valueLengthOpt, Some(sliced._1.rootAndHeight))
74+
case _: ProverLeaf[D] =>
75+
new BatchAVLProver[D, HF](sliced._1.keyLength, sliced._1.valueLengthOpt, Some(sliced._1.rootAndHeight))
7676
}
7777
}
7878

79-
def manifestToBytes(m: BatchAVLProverManifest[D, HF]): Array[Byte] = {
80-
Bytes.concat(Ints.toByteArray(m.keyLength),
81-
Ints.toByteArray(m.valueLengthOpt.getOrElse(-1)),
82-
Ints.toByteArray(m.oldRootAndHeight._2),
83-
nodesToBytes(m.oldRootAndHeight._1)
79+
def manifestToBytes(manifest: BatchAVLProverManifest[D, HF]): Array[Byte] = {
80+
Bytes.concat(Ints.toByteArray(manifest.keyLength),
81+
Ints.toByteArray(manifest.valueLengthOpt.getOrElse(-1)),
82+
Ints.toByteArray(manifest.rootAndHeight._2),
83+
nodesToBytes(manifest.rootAndHeight._1)
8484
)
8585
}
8686

87-
def manifestFromBytes(b: Array[Byte]): Try[BatchAVLProverManifest[D, HF]] = Try {
88-
val keyLength = Ints.fromByteArray(b.slice(0, 4))
89-
val valueLength = Ints.fromByteArray(b.slice(4, 8))
87+
def manifestFromBytes(bytes: Array[Byte]): Try[BatchAVLProverManifest[D, HF]] = Try {
88+
val keyLength = Ints.fromByteArray(bytes.slice(0, 4))
89+
val valueLength = Ints.fromByteArray(bytes.slice(4, 8))
90+
if (valueLength < -1) throw new Error(s"Wrong valueLength: $valueLength")
9091
val valueLengthOpt = if (valueLength == -1) None else Some(valueLength)
91-
val oldHeight = Ints.fromByteArray(b.slice(8, 12))
92-
val oldTop = nodesFromBytes(b.slice(12, b.length), keyLength).get
92+
val oldHeight = Ints.fromByteArray(bytes.slice(8, 12))
93+
val oldTop = nodesFromBytes(bytes.slice(12, bytes.length), keyLength).get
9394
BatchAVLProverManifest[D, HF](keyLength, valueLengthOpt, (oldTop, oldHeight))
9495
}
9596

@@ -113,7 +114,7 @@ class BatchAVLProverSerializer[D <: Digest, HF <: CryptographicHash[D]](implicit
113114
loop(obj)
114115
}
115116

116-
def nodesFromBytes(bytesIN: Array[Byte], keyLength: Int): Try[ProverNodes[D]] = Try {
117+
def nodesFromBytes(bytesIn: Array[Byte], keyLength: Int): Try[ProverNodes[D]] = Try {
117118
def loop(bytes: Array[Byte]): ProverNodes[D] = bytes.head match {
118119
case 0 =>
119120
val key = ADKey @@ bytes.slice(1, keyLength + 1)
@@ -140,7 +141,7 @@ class BatchAVLProverSerializer[D <: Digest, HF <: CryptographicHash[D]](implicit
140141
???
141142
}
142143

143-
loop(bytesIN)
144+
loop(bytesIn)
144145
}
145146
}
146147

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
11
package scorex.crypto.authds.avltree.batch.serialization
22

3-
import scorex.crypto.authds.avltree.batch.{InternalProverNode, ProverLeaf, ProverNodes}
3+
import scorex.crypto.authds.avltree.batch.ProverNodes
44
import scorex.crypto.hash.{CryptographicHash, Digest}
55

6-
import scala.util.Try
7-
86
/**
97
* AVL subtree, starting from Manifests FinalInternalNode and ending with Leafs
108
*/
11-
case class BatchAVLProverSubtree[D <: Digest, HF <: CryptographicHash[D]](subtreeTop: ProverNodes[D])
12-
13-
object BatchAVLProverSubtreeSerializer {
14-
15-
}
9+
case class BatchAVLProverSubtree[D <: Digest, HF <: CryptographicHash[D]](subtreeTop: ProverNodes[D])

src/test/scala/scorex/crypto/authds/avltree/batch/serialization/AVLBatchSerializationSpecification.scala

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,23 +27,23 @@ class AVLBatchSerializationSpecification extends PropSpec with GeneratorDrivenPr
2727
private def generateProver(size: Int = InitialTreeSize): BatchAVLProver[D, HF] = {
2828
val prover = new BatchAVLProver[D, HF](KL, None)
2929
val keyValues = (0 until size) map { i =>
30-
(ADKey @@ Blake2b256(i.toString.getBytes("UTF-8")).take(KL), ADValue @@ (i.toString.getBytes("UTF-8")))
30+
(ADKey @@ Blake2b256(i.toString.getBytes("UTF-8")).take(KL), ADValue @@ i.toString.getBytes("UTF-8"))
3131
}
3232
keyValues.foreach(kv => prover.performOneOperation(Insert(kv._1, kv._2)))
3333
prover.generateProof()
3434
prover
3535
}
3636

3737
property("slice to pieces and combine tree back") {
38-
forAll(Gen.choose(100, 100000)) { treeSize: Int =>
39-
whenever(treeSize >= 100) {
38+
forAll(Gen.choose(10, 100000)) { treeSize: Int =>
39+
whenever(treeSize >= 10) {
4040
val tree = generateProver(treeSize)
4141
val height = tree.rootNodeHeight
4242
val digest = tree.digest
4343
val serializer = new BatchAVLProverSerializer[D, HF]
4444
val sliced = serializer.slice(tree)
4545

46-
val manifestLeftTree = leftTree(sliced._1.oldRootAndHeight._1)
46+
val manifestLeftTree = leftTree(sliced._1.rootAndHeight._1)
4747
val subtreeLeftTree = leftTree(sliced._2.head.subtreeTop)
4848

4949
manifestLeftTree.length should be < height
@@ -56,15 +56,14 @@ class AVLBatchSerializationSpecification extends PropSpec with GeneratorDrivenPr
5656
}
5757

5858
property("slice to Array[Byte] pieces and combine tree back") {
59-
forAll(Gen.choose(100, 100000)) { treeSize: Int =>
59+
forAll(Gen.choose(0, 100000)) { treeSize: Int =>
6060
val serializer = new BatchAVLProverSerializer[D, HF]
6161
val tree = generateProver(treeSize)
6262
val kl = tree.keyLength
6363
val digest = tree.digest
6464

6565
val sliced = serializer.slice(tree)
6666

67-
6867
val manifestBytes = serializer.manifestToBytes(sliced._1)
6968
val subtreeBytes = sliced._2.map(t => serializer.subtreeToBytes(t))
7069

@@ -83,7 +82,7 @@ class AVLBatchSerializationSpecification extends PropSpec with GeneratorDrivenPr
8382

8483
property("manifest serialization") {
8584
val serializer = new BatchAVLProverSerializer[D, HF]
86-
forAll(Gen.choose(100, 100000)) { treeSize: Int =>
85+
forAll(Gen.choose(0, 100000)) { treeSize: Int =>
8786
val tree = generateProver(treeSize)
8887
val kl = tree.keyLength
8988
val digest = tree.digest
@@ -93,10 +92,20 @@ class AVLBatchSerializationSpecification extends PropSpec with GeneratorDrivenPr
9392
val manifestBytes = serializer.manifestToBytes(manifest)
9493
val deserializedManifest = serializer.manifestFromBytes(manifestBytes).get
9594

96-
deserializedManifest.oldRootAndHeight._1.label shouldBe manifest.oldRootAndHeight._1.label
95+
deserializedManifest.rootAndHeight._1.label shouldBe manifest.rootAndHeight._1.label
9796
}
9897
}
9998

99+
property("wrong manifest") {
100+
val tree = generateProver()
101+
val serializer = new BatchAVLProverSerializer[D, HF]
102+
val sliced = serializer.slice(tree)
103+
val wrongManifest: BatchAVLProverManifest[D, HF] = sliced._1.copy(valueLengthOpt = Some(-2))
104+
105+
val manifestBytes = serializer.manifestToBytes(wrongManifest)
106+
serializer.manifestFromBytes(manifestBytes).isFailure shouldBe true
107+
}
108+
100109
def leftTree(n: ProverNodes[D]): Seq[ProverNodes[D]] = n match {
101110
case n: ProxyInternalNode[D] if n.isEmpty =>
102111
Seq(n)

0 commit comments

Comments
 (0)