Skip to content

Latest commit

 

History

History
340 lines (242 loc) · 15.6 KB

IPLD_EXPLORER.md

File metadata and controls

340 lines (242 loc) · 15.6 KB

IPLD Explorers

Abstract: IPLD is the underlying data structure for IPFS. It is a huge hash-linked directed acyclic graph, with all files, git repos, blockchains, etc., within it. IPLD is the heart of IPFS. The tooling for manipulating IPLD directly has recently landed into both go-ipfs and js-ipfs under the API/commands ipfs dag. What we need next is to be able to make it nicer for humans to interact with IPLD directly. For this purpose, we have a few different "graph explorers" in mind. You can think of them similar to a visual explorer for a git repo (eg rendering the commit graph), or file explorers, or blockchain explorers. We aim to create several graph explorers, (1) one based on a traditional programming REPL, (2) another based on JSON tree explorers, (3) another based on a column-based tree viewer, (4) and a graphical version using d3 graph plotting, and (5) one in 3D which allows VR exploration.

Table of Contents

Introduction

To learn more about IPLD, please check out:

The "Relevant API" section in this document gives examples on ipfs node calls to make.

Explorers

Explorer 1. IPLD REPL

Examples:

The first idea is to part from a very simple REPL. The REPL would be primarily about hashes and content. It would have a few "builtin" functions:

  • resolve(<path>) - walk down a path and return the object found there
  • ls(<path>) - list the entries in a path
  • tree(<path>, <depth>) - list all entries starting from path, and up to <depth> graph depth. (may be useful)
  • get(<path>) - walks down a path and returns that path

Entering <path> is the equivalent of get <path>, and every non-path value is added to ipfs, hashed, and its CID is returned.

A more advanced version would allow:

  • eval(<path>), which would load the value at <path>, interpret it as javascript, and eval it, adding it to the current context \o/.

Explorer 2. tree explorers

Examples:

Structured explorers:

The tree explorer builds a very similar editor to traditional JSON tree explorers. This may be overkill for now.

Explorer 3. column-based viewer

File browsers / explorers:

The column-based file system exploration fits our graphs very well, as well. This should perhaps be the easiest to get working well. There's many examples and libraries out there.

Explorer 4. graphical version

D3

D3 was already used in some of our prior art and probably will end up using it directly or indirectly.

Open technical questions that probably do not belong to design discussion: what will be the data format? should D3 be used in raw form, or via a library? are there any alternatives to D3 ecosystem? We will answer them during the implementation, raising them here just for the record.

D3: Quick Introduction

D3→Shapes→Links: https://github.com/d3/d3-shape#links
Live demo with annotated sources: https://bl.ocks.org/mbostock/4339184

D3: Prior Art in IPFS Ecosystem

This version visualizes the IPLD graph using d3:

old ipfs dataviz demo

ipfs dataviz is currently working, and showing files. It should also show the raw data. Now that IPLD is out, the project could be updated. Though because having access to the file graph is useful, we may want to copy the visualization and remix it to navigate on nodes, instead of files.

Live Demo: https://ipfs.io/ipfs/QmX5smVTZfF8p1VC8Y3VtjGqjvDVPWvyBk24JgvnMwHtjC/viz#QmX5smVTZfF8p1VC8Y3VtjGqjvDVPWvyBk24JgvnMwHtjC

screenshot-2018-3-29 http 127 0 0 1 1

Dead Demo (trying to load big flat directory with XKCD archive): https://ipfs.io/ipfs/QmX5smVTZfF8p1VC8Y3VtjGqjvDVPWvyBk24JgvnMwHtjC/viz#Qmb8wsGZNXt5VXZh1pEmYynjB6Euqpq3HYyeAdw2vScTkQ

Given enough time it will eventually render this mess.

GraphViz

(Does not really support dynamic transformations in web browser, but mentioning it for the record)
Why mentioning GraphViz and Dot format? People can easily reuse it in papers, publications etc.

Dot: Prior Art in IPFS Ecosystem

Prior art exists in IPFS ecosystem, see notes on Graphing Objects:

graph

GraphViz in D3

Web Version implemented in D3 exists: https://github.com/mstefaniuk/graph-viz-d3-js

Examples:

Explorer 5. 3D VR Explorer

Blockchain explorers:

This explorer is a native way to look at the bitcoin blockchain; it's just done on a 3D environment! This is possibly super cool, but we'd love to let users navigate and explore, jumping from block to block.

How to Implement

All of these explorers do one thing: traverse the graph. Therefore, we only need a couple of api calls to do this: ipfs.dag.ls, and ipfs.dag.get. See more in the Relevant API section below.

Path in the anchor and history

All explorers have to do with a given path. It is important to keep this path in the hash/anchor of the html page, as we want to be able to link to exact locations in the graph.

Navigation path

Another thing all explorers track is navigation path. While this may be multiple nodes open in some explorers, it is enough to keep track of a single string path for the others. It would be useful if this navigation path is also given through the URL bar, so that people can link each other to the exact same location and "path used to get there".

Relevant API

All of the explorers here will use the following api calls:

  • ipfs.dag.ls
  • ipfs.dag.get

See the DAG API, which works in both js-ipfs and js-ipfs-api.

Example: ipfs.dag.ls

var IPFSAPI = require('ipfs-api')
var ipfs = IPFSApi('/ip4/127.0.0.1/tcp/5001')

// given an object such as: 
//
// var obj = {
//     "a": 1,
//     "b": [1, 2, 3],
//     "c": {
//         "ca": [5, 6, 7],
//         "cb": "foo"
//     }
// }
// 
// ipfs.dag.put(obj, function(err, cid2) {
//     assert(cid == zdpuAkxd9KzGwJFGhymCZRkPCXtBmBW7mB2tTuEH11HLbES9Y)
// })

ipfs.dag.ls('zdpuAkxd9KzGwJFGhymCZRkPCXtBmBW7mB2tTuEH11HLbES9Y', function(err, result) {
    for (var i in result.entries) {
        console.log(result.entries[i])
    }
})
// Returns:
// a
// b
// c

ipfs.dag.ls('zdpuAkxd9KzGwJFGhymCZRkPCXtBmBW7mB2tTuEH11HLbES9Y/c', function(err, result) {
    for (var i in result.entries) {
        console.log(result.entries[i])
    }
})
// Returns:
// ca
// cb

ipfs.dag.ls('zdpuAkxd9KzGwJFGhymCZRkPCXtBmBW7mB2tTuEH11HLbES9Y/c/ca', function(err, result) {
    for (var i in result.entries) {
        console.log(result.entries[i])
    }
})
// Returns:
// 0
// 1
// 2

Example: ipfs.dag.tree

var IPFSAPI = require('ipfs-api')
var ipfs = IPFSApi('/ip4/127.0.0.1/tcp/5001')

// given an object such as: 
//
// var obj = {
//     "a": 1,
//     "b": [1, 2, 3],
//     "c": {
//         "ca": [5, 6, 7],
//         "cb": "foo"
//     }
// }
// 
// ipfs.dag.put(obj, function(err, cid2) {
//     assert(cid == zdpuAkxd9KzGwJFGhymCZRkPCXtBmBW7mB2tTuEH11HLbES9Y)
// })

ipfs.dag.tree('zdpuAkxd9KzGwJFGhymCZRkPCXtBmBW7mB2tTuEH11HLbES9Y', function(err, result) {
    for (var i in result) {
        console.log(result[i])
    }
})
// Returns:
// a
// b
// b/0
// b/1
// b/2
// c
// c/ca
// c/ca/0
// c/ca/1
// c/ca/2
// c/cb

ipfs.dag.tree('zdpuAkxd9KzGwJFGhymCZRkPCXtBmBW7mB2tTuEH11HLbES9Y/c', function(err, result) {
    for (var i in result) {
        console.log(result[i])
    }
})
// Returns:
// ca
// ca/0
// ca/1
// ca/2
// cb

Example: ipfs.dag.get

var IPFSAPI = require('ipfs-api')
var ipfs = IPFSApi('/ip4/127.0.0.1/tcp/5001')

// given an object such as: 
//
// var obj = {
//     "a": 1,
//     "b": [1, 2, 3],
//     "c": {
//         "ca": [5, 6, 7],
//         "cb": "foo"
//     }
// }
// 
// ipfs.dag.put(obj, function(err, cid2) {
//     assert(cid == zdpuAkxd9KzGwJFGhymCZRkPCXtBmBW7mB2tTuEH11HLbES9Y)
// })

function errOrLog(err, result) {
    if (err) console.error('error: ' + err)
    else console.log(result)
}

ipfs.dag.get('zdpuAkxd9KzGwJFGhymCZRkPCXtBmBW7mB2tTuEH11HLbES9Y/a', errOrLog)
// Returns:
// 1

ipfs.dag.get('zdpuAkxd9KzGwJFGhymCZRkPCXtBmBW7mB2tTuEH11HLbES9Y/b', errOrLog)
// Returns:
// [1, 2, 3]

ipfs.dag.get('zdpuAkxd9KzGwJFGhymCZRkPCXtBmBW7mB2tTuEH11HLbES9Y/c', errOrLog)
// Returns:
// {
//   "ca": [5, 6, 7],
//   "cb": "foo"
// }

ipfs.dag.get('zdpuAkxd9KzGwJFGhymCZRkPCXtBmBW7mB2tTuEH11HLbES9Y/c/ca/1', errOrLog)
// Returns:
// 6