ethereumjs / merkle-patricia-tree

Project is in active development and has been moved to the EthereumJS VM monorepo.

Home Page:https://github.com/ethereumjs/ethereumjs-monorepo

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Trie.prototype._updateNode(); TypeError: Cannot read property 'pop' of undefined

Ryanmtate opened this issue · comments

_updateNode is receiving undefined stack

Receiving TypeError: Cannot read property 'pop' of undefined

patricia-tree/baseTrie.js:359
  var lastNode = stack.pop()

                      ^

TypeError: Cannot read property 'pop' of undefined

Suspect WalkController is not passing stack in the callback of processNode();

Trie.prototype._findPath = function (targetKey, cb) {
  var self = this
  var root = self.root
  var stack = []
  targetKey = TrieNode.stringToNibbles(targetKey)

  this._walkTrie(root, processNode, cb)

  function processNode (root, node, keyProgress, walkController) {
    var nodeKey = node.key || []
    var keyRemainder = targetKey.slice(matchingNibbleLength(keyProgress, targetKey))
    var matchingLen = matchingNibbleLength(keyRemainder, nodeKey)

    stack.push(node)

    if (node.type === 'branch') {
      if (keyRemainder.length === 0) {
        walkController.return(null, node, [], stack)
      // we exhausted the key without finding a node
      } else {
        var branchIndex = keyRemainder[0]
        var branchNode = node.getValue(branchIndex)
        if (!branchNode) {
          // there are no more nodes to find and we didn't find the key
          walkController.return(null, null, keyRemainder, stack)
        } else {
          // node found, continuing search
          walkController.only(branchIndex)
        }
      }
    } else if (node.type === 'leaf') {
      if (doKeysMatch(keyRemainder, nodeKey)) {
        // keys match, return node with empty key
        walkController.return(null, node, [], stack)
      } else {
        // reached leaf but keys dont match
        walkController.return(null, null, keyRemainder, stack)
      }
    } else if (node.type === 'extention') {
      if (matchingLen !== nodeKey.length) {
        // keys dont match, fail
        walkController.return(null, null, keyRemainder, stack)
      } else {
        // keys match, continue search
        walkController.next()
      }
    }
  }
}

@Ryanmtate do you have the code that generates this error?

I've seen this a bunch with the TestRPC. Was too intermittent to figure out the root cause however.

This problem still exists with TestRPC, I've spent a few hours trying to track down how to fix it, but so far haven't figured out how. Callback hell...

I've tried making it pass an error in the callback from watchController.only in walkTrie helper processNode but there seems to be a lot of code elsewhere that doesn't follow the callback(error, value) convention, causing all kinds of strange behaviour. I squashed many of those, but I don't think calling the continuation in only with an error if node is undefined is the right fix. It seems to put the server's state manager in a broken state, all subsequent calls return the same error. Don't fully understand all the surrounding code yet.

Giving up for now, might look at it again later if I have time. But I'm documenting my findings below if anyone else has the patience:

The symptoms start with this line in Trie._findPath's processNode

walkController.only(branchIndex)

which then runs this function

only: function (childIndex) {
    var childRoot = node.getValue(childIndex)
    self._lookupNode(childRoot, function (node) { // node is undefined here
        var childKey = key.slice()
        childKey.push(childIndex)
        processNode(childRoot, node, childKey, cb)
    })
}

In the recursive call for processNode from above it calls this line

if (!node) return cb() <-- no arguments

this is cb from above

if (err) {
  return onDone(err)
}
// returnValues is an empty array from the walkTrie closure, so essentially onDone()
onDone.apply(null, returnValues) 

onDone from above is the callback in Trie.put at this line

self._findPath(key, function (err, foundValue, keyRemainder, stack) {
    // all arguments here are undefined because it gets called with no arguments
    if (err) {
        return cb(err);
    }
    // then update
    // this call crashes the program because stack is undefined
    self._updateNode(key, value, keyRemainder, stack, cb)
})

Will cautiously close this since the issue is pretty old, there hasn't been any reports lately and the code base changed significantly since. There has also been a refactor of the WalkController in #135 by @jochem-brouwer. Feel free to reopen though if you still think this is an issue.