Skip to content

Commit

Permalink
This PR includes two fixes:
Browse files Browse the repository at this point in the history
- When Root node is not found, a different error message is thrown
- There was a bad comparison between an Array[Byte] and a ByteString
  • Loading branch information
AlanVerbner committed Nov 22, 2017
1 parent af76788 commit 11f25aa
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 12 deletions.
29 changes: 18 additions & 11 deletions src/main/scala/io/iohk/ethereum/mpt/MerklePatriciaTrie.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,16 @@ object MerklePatriciaTrie {
}
}

private def getNode(nodeId: Array[Byte], source: NodesKeyValueStorage)(implicit nodeDec: RLPDecoder[MptNode]): MptNode = {
val nodeEncoded =
if (nodeId.length < 32) nodeId
else source.get(ByteString(nodeId)).getOrElse(throw MPTException(s"Node not found ${Hex.toHexString(nodeId)}, trie is inconsistent"))
decodeRLP[MptNode](nodeEncoded).withCachedHash(nodeId).withCachedRlpEncoded(nodeEncoded)
private def getNode(nodeId: Array[Byte], source: NodesKeyValueStorage)(implicit nodeDec: RLPDecoder[MptNode]): Option[MptNode] = {
val maybeNodeEncoded =
if (nodeId.length < 32) Some(nodeId)
else source.get(ByteString(nodeId))
maybeNodeEncoded.map(nodeEncoded => decodeRLP[MptNode](nodeEncoded).withCachedHash(nodeId).withCachedRlpEncoded(nodeEncoded))
}

private def getRootNode(rootId: Array[Byte], source: NodesKeyValueStorage): MptNode =
getNode(rootId, source).getOrElse(throw MPTException(s"Root node not found ${Hex.toHexString(rootId)}"))

private def matchingLength(a: Array[Byte], b: Array[Byte]): Int = a.zip(b).takeWhile(t => t._1 == t._2).length

private def updateNodesInStorage(
Expand All @@ -81,7 +84,7 @@ object MerklePatriciaTrie {
toRemove: Seq[MptNode],
toUpdate: Seq[MptNode],
nodeStorage: NodesKeyValueStorage): NodesKeyValueStorage = {
val rootCapped = newRoot.map(_.capped).getOrElse(Array.emptyByteArray)
val rootCapped = newRoot.map(_.capped).getOrElse(ByteString.empty)
val toBeRemoved = toRemove.filter { node =>
val nCapped = node.capped
nCapped.length == 32 || (node.hash sameElements previousRootHash)
Expand All @@ -96,13 +99,17 @@ object MerklePatriciaTrie {
private def getNextNode(extensionNode: ExtensionNode, nodeStorage: NodesKeyValueStorage)(implicit nodeDec: RLPDecoder[MptNode]): MptNode =
extensionNode.next match {
case Right(node) => node
case Left(hash) => MerklePatriciaTrie.getNode(hash.toArray[Byte], nodeStorage)
case Left(hash) =>
val nodeId = hash.toArray[Byte]
MerklePatriciaTrie.getNode(nodeId, nodeStorage).getOrElse(throw MPTException(s"Node not found ${Hex.toHexString(nodeId)}, trie is inconsistent"))
}

private def getChild(branchNode: BranchNode, pos: Int, nodeStorage: NodesKeyValueStorage)(implicit nodeDec: RLPDecoder[MptNode]): Option[MptNode] =
branchNode.children(pos) map {
case Right(node) => node
case Left(hash) => MerklePatriciaTrie.getNode(hash.toArray[Byte], nodeStorage)
case Left(hash) =>
val nodeId = hash.toArray[Byte]
MerklePatriciaTrie.getNode(nodeId, nodeStorage).getOrElse(throw MPTException(s"Node not found ${Hex.toHexString(nodeId)}, trie is inconsistent"))
}

implicit val nodeEnc: RLPEncoder[MptNode] = new RLPEncoder[MptNode] {
Expand Down Expand Up @@ -151,7 +158,7 @@ class MerklePatriciaTrie[K, V] private (private val rootHash: Option[Array[Byte]
def get(key: K): Option[V] = {
rootHash flatMap { rootId =>
val keyNibbles = HexPrefix.bytesToNibbles(bytes = kSerializer.toBytes(key))
val rootNode = getNode(rootId, nodeStorage)
val rootNode = getRootNode(rootId, nodeStorage)
get(rootNode, keyNibbles).map(bytes => vSerializer.fromBytes(bytes))
}
}
Expand All @@ -167,7 +174,7 @@ class MerklePatriciaTrie[K, V] private (private val rootHash: Option[Array[Byte]
override def put(key: K, value: V): MerklePatriciaTrie[K, V] = {
val keyNibbles = HexPrefix.bytesToNibbles(kSerializer.toBytes(key))
rootHash map { rootId =>
val root = getNode(rootId, nodeStorage)
val root = getRootNode(rootId, nodeStorage)
val NodeInsertResult(newRoot, nodesToRemoveFromStorage, nodesToUpdateInStorage) = put(root, keyNibbles, vSerializer.toBytes(value))
val newRootHash = newRoot.hash
val newSource = updateNodesInStorage(
Expand Down Expand Up @@ -195,7 +202,7 @@ class MerklePatriciaTrie[K, V] private (private val rootHash: Option[Array[Byte]
override def remove(key: K): MerklePatriciaTrie[K, V] = {
rootHash map { rootId =>
val keyNibbles = HexPrefix.bytesToNibbles(bytes = kSerializer.toBytes(key))
val root = getNode(rootId, nodeStorage)
val root = getRootNode(rootId, nodeStorage)
remove(root, keyNibbles) match {
case NodeRemoveResult(true, Some(newRoot), nodesToRemoveFromStorage, nodesToUpdateInStorage) =>
val newRootHash = newRoot.hash
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import akka.util.ByteString
import io.iohk.ethereum.ObjectGenerators
import io.iohk.ethereum.db.dataSource.EphemDataSource
import io.iohk.ethereum.db.storage.{ArchiveNodeStorage, NodeStorage, ReferenceCountNodeStorage}
import io.iohk.ethereum.mpt.MerklePatriciaTrie.defaultByteArraySerializable
import io.iohk.ethereum.mpt.MerklePatriciaTrie.{MPTException, defaultByteArraySerializable}
import org.scalacheck.{Arbitrary, Gen}
import org.scalatest.FunSuite
import org.scalatest.prop.PropertyChecks
Expand Down Expand Up @@ -347,6 +347,17 @@ class MerklePatriciaTrieSuite extends FunSuite
assert(trieAfterDelete.getRootHash sameElements trieWithBranch.getRootHash)
}

test("Invalid root hash should return an error accordingly") {
val rootId = "2" * 64 // 32 Bytes, should be stored
val invalidTrie = MerklePatriciaTrie[Array[Byte], Array[Byte]](Hex.decode(rootId), EmptyEphemNodeStorage)
val thrown = intercept[MPTException] {
invalidTrie.get(Hex.decode("1111")) // Try to get anything
}

assert(thrown.getMessage === s"Root node not found $rootId")

}

/* EthereumJ tests */
test("testDeleteCompletellyDiferentItems") {
val val1: String = "1000000000000000000000000000000000000000000000000000000000000000"
Expand Down

0 comments on commit 11f25aa

Please sign in to comment.