exonum / exonum-client

JavaScript client for Exonum blockchain
Apache License 2.0
65 stars 33 forks source link

Merkle (Patricia) proof interface and implementation #10

Open slowli opened 7 years ago

slowli commented 7 years ago

Merkle (Patricia) tree proofs essentially prove that a certain element (or several elements) belongs to a certain data collection (list in the case of Merkle trees; map in the case of MPTs). Essentially, a MTs and MPTs are views of lists or maps, in which some elements are unknown. So, it could be logical to implement them as such.

Interface

Thus, it could be logical to have the following interface for MTs and MPTs:

Internal construction

Merkle tree

Recursive definition:

MT<ItemType> = oneOf({
  stub: Hash, // "collapsed" part of the tree
  branch: { left: MT<ItemType>, right: MT<ItemType> },
  sprout: { child: MT<ItemType> }, // a non-branching intermediate node 
  val: ItemType, // value
});

ListView<ItemType> = {
  root: MT<ItemType>,
  length: Uint64
};

Consistency checks:

Methods:

Merkle Patricia tree

Recursive definition:

MPT<ItemType> = {
  bits: BitSlice, // key bits connecting the node to its parent
  node: oneOf({
    stub: Hash, // "collapsed" part of the tree
    branch: { left: MPT<ItemType>, right: MPT<ItemType> },
    val: ItemType // value
  })
};

MapView<ItemType> = MPT<ItemType>;

bits may be woven into all type variants if deemed necessary.

Consistency checks:

Methods:

slowli commented 7 years ago

@boguslavsky @defuz @gisochre Are you OK with this interface? Maybe, ListView or MapView need to have other methods? As for JSON for constructors, I see it slightly more verbose than it is currently, in order to be parseable within generic Exonum type spec:

ListView (3 items, proofs for 1st and 3rd):

{
  "root": {
    "@type": "branch",
    "left": {
      "@type": "branch",
      "left": {
        "val": ...
      },
      "right": {
        "stub": "abcdef..."
      }
    },
    "right": {
      "@type": "sprout",
      "child": {
        "val": ...
      }
    }
  },
  "length": 3
}

MapView (proof for key 01101...; the bits field is embedded into variants):

{
  "@type": "branch",
  "bits": "",
  "left": {
    "@type": "branch",
    "bits": "0",
    "left": {
      "stub": {
        "bits": "01",
        "hash": "..."
      }
    },
    "right": {
      "val": {
        "bits": "1101...",
        "item": ...
      }
    }
  },
  "right": {
    "stub": {
      "bits": "1",
      "hash": "..."
    }
  }
}

This assumes that oneOf can be encoded either as {<tag>: <value>} or {"@type": <tag>, ...<value>}.

As for implementation, it could be first implemented with custom JSON parsing logic, and then integrated into the Exonum type system.

slowli commented 7 years ago

I've implemented a PoC for type defs, which includes possibility of tree implementation, among other things. Very rough, but you probably get the general idea.