all files / merkle-trie/ store.js

98.11% Statements 52/53
95.65% Branches 22/23
100% Functions 6/6
98.04% Lines 50/51
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144                           32× 32× 32× 32×   32× 32×                                     30×     26×     22× 22× 18×                     38× 38× 38×     38×                       36× 36×     35×   21× 21×   14×           10× 10×             35×   35× 31× 31× 28×         35× 35×     32×                          
const Vertex = require('./index.js')
const Readable = require('./readStream.js')
const IPLDResolver = require('ipld-resolver')
const Block = require('ipfs-block')
 
/**
 * Store for merkle tries
 * @param {IPLDResolver} a [IPLDResolver](https://github.com/ipld/js-ipld-resolver) instance used to store the store
 * @param {Object} resolvers a map of multiformat perfixes to deserializtion function
 */
module.exports = class Store extends IPLDResolver {
 
  /**
   * Stores a vertex in the db, returning its merkle link
   * @param {Vertex}
   * @return {Promise}
   */
  set (vertex) {
    let buffer
    return vertex.serialize().then(b => {
      buffer = b
      return vertex.constructor.hash(buffer)
    }).then(hash => {
      return new Promise((resolve, reject) => {
        this.bs.put({
          cid: hash,
          block: new Block(buffer)
        }, resolve.bind(resolve, hash))
      })
    })
  }
 
  /**
   * Fetches a Vertex from the db
   * @param {Vertex} rootVertex the vertex to start fetching from
   * @param {Array} path the path to fetch
   * @return {Promise}
   */
  getPath (rootVertex, path) {
    path = path.slice(0)
    return new Promise((resolve, reject) => {
      this._getPath(rootVertex, path, resolve, reject)
    })
  }
 
  _getPath (rootVertex, path, resolve, reject) {
    if (!rootVertex) {
      return reject('no vertex was found')
    }
 
    if (!path.length) {
      return resolve(rootVertex)
    }
 
    let edge = rootVertex.edges.get(path.shift())
    if (edge) {
      this.getCID(edge).then(vertex => this._getPath(vertex, path, resolve, reject))
    } else {
      this._getPath(edge, path, resolve, reject)
    }
  }
 
  /**
   * resolves a [CID](https://github.com/ipfs/js-cid) to a Vertex
   * @param {CID} CID
   * @return {Promise}
   */
  getCID (cid) {
    return new Promise((resolve, reject) => {
      this.get(cid, (err, [value, edges]) => {
        Iif (err) {
          reject(err)
        } else {
          resolve(new Vertex({value: value, edges: edges, store: this}))
        }
      })
    })
  }
 
  /**
   * flush a cache trie to the db returning a promise that resolves to a merkle link
   * @param {Cache}
   * @return {Promise}
   */
  batch (cache) {
    let promise
    if (cache.op === 'del' && cache.isLeaf) {
      return false
    }
 
    if (!cache.vertex) {
      // this vertex has never existed before!
      cache.vertex = new Vertex({store: this, cache: cache})
      promise = Promise.all([...cache.edges].map(([, vertex]) => this.batch(vertex)))
    } else {
      promise = Promise.all([...cache.edges].map(([name, nextedCache]) => {
        // if the edge of the vertex is merkle link and the also has the same path
        // but doesn't have a new vertex then resolve the that edge
        //     a <-- we are here in the trie
        //     |
        //     b <-- we are going here next, do we already know about this vertex?
        const link = cache.vertex.edges.get(name)
        if (link && nextedCache.op !== 'del' && !nextedCache.vertex) {
          return this.getCID(link).then(foundVertex => {
            nextedCache.vertex = foundVertex
            return this.batch(nextedCache)
          })
        } else {
          return this.batch(nextedCache)
        }
      }))
    }
 
    return promise
      .then(hashes => {
        for (const [edge] of cache.edges) {
          const hash = hashes.shift()
          if (hash) {
            cache.vertex.edges.set(edge, hash)
          } else {
            cache.vertex.edges.delete(edge)
          }
        }
 
        cache.clear()
        if (cache.vertex.isEmpty) {
          // dont save empty trie nodes
          return false
        } else {
          return this.set(cache.vertex)
        }
      })
  }
 
  /**
   * Creates a read stream returning all the Vertices in a trie given a root merkle link
   * @param {Link} link
   * @return {ReadStream}
   */
  createReadStream (link) {
    return new Readable({}, link, this)
  }
}