├── .browserslistrc
├── .dockerignore
├── .gitignore
├── Dockerfile
├── Notes.md
├── README.md
├── docker-compose.yml
├── graph.html
├── graph.js
├── index.html
├── package-lock.json
├── package.json
└── src
├── admin
├── app
│ ├── App.js
│ ├── bootstrap.css
│ ├── components
│ │ ├── ToggleButtonGroup
│ │ │ └── index.js
│ │ ├── nav
│ │ │ └── index.js
│ │ ├── pie
│ │ │ └── index.js
│ │ └── stackedArea
│ │ │ └── index.js
│ ├── index.css
│ ├── index.js
│ └── views
│ │ ├── SidePanel.js
│ │ └── graphBuilder
│ │ ├── graph.js
│ │ ├── index.js
│ │ └── layout-peerId.js
├── engine.js
├── index.js
├── old.js
├── simulation.js
├── utils
│ └── copy-to-clipboard.js
└── viz
│ ├── graph
│ ├── base.js
│ ├── blocks.js
│ ├── ebt.js
│ ├── mesh.js
│ ├── normal.js
│ ├── pie-transport-rx.js
│ ├── pie-transport-tx.js
│ ├── pie.js
│ └── pubsub.js
│ └── pie.js
├── client
├── index.js
└── libp2p
│ ├── createNode.js
│ └── peerConnectionTracker.js
├── experiments
├── common
│ ├── BaseForceGraph.js
│ ├── graph-viz.js
│ ├── json.js
│ └── obs-store.js
├── debug
│ ├── admin.js
│ ├── client.js
│ ├── uptime.js
│ └── versions.js
├── dht
│ ├── admin.js
│ ├── client.js
│ ├── getDhtStats.js
│ └── graphs
│ │ └── routing.js
├── errors
│ ├── admin.js
│ ├── client.js
│ └── error-log.js
├── peers
│ └── client.js
├── platform
│ ├── admin.js
│ └── client.js
└── traffic
│ ├── admin.js
│ ├── basic.js
│ └── client.js
├── partialRollout.js
├── server
└── index.js
├── swarm
├── browser.js
├── node.js
├── package-lock.json
└── package.json
└── util
└── colorUtils.js
/.browserslistrc:
--------------------------------------------------------------------------------
1 | # Browsers that we support
2 |
3 | last 2 Chrome versions
4 | last 2 Firefox versions
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | .git
2 | node_modules
3 | admin-bundle.js
4 | client-bundle.js
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### app specific
2 |
3 | dist
4 |
5 |
6 | # Created by https://www.gitignore.io/api/osx,node
7 |
8 | ### Node ###
9 | # Logs
10 | logs
11 | *.log
12 | npm-debug.log*
13 | yarn-debug.log*
14 | yarn-error.log*
15 |
16 | # Runtime data
17 | pids
18 | *.pid
19 | *.seed
20 | *.pid.lock
21 |
22 | # Directory for instrumented libs generated by jscoverage/JSCover
23 | lib-cov
24 |
25 | # Coverage directory used by tools like istanbul
26 | coverage
27 |
28 | # nyc test coverage
29 | .nyc_output
30 |
31 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
32 | .grunt
33 |
34 | # Bower dependency directory (https://bower.io/)
35 | bower_components
36 |
37 | # node-waf configuration
38 | .lock-wscript
39 |
40 | # Compiled binary addons (http://nodejs.org/api/addons.html)
41 | build/Release
42 |
43 | # Dependency directories
44 | node_modules/
45 | jspm_packages/
46 |
47 | # Typescript v1 declaration files
48 | typings/
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Optional REPL history
57 | .node_repl_history
58 |
59 | # Output of 'npm pack'
60 | *.tgz
61 |
62 | # Yarn Integrity file
63 | .yarn-integrity
64 |
65 | # dotenv environment variables file
66 | .env
67 |
68 |
69 | ### OSX ###
70 | *.DS_Store
71 | .AppleDouble
72 | .LSOverride
73 |
74 | # Icon must end with two \r
75 | Icon
76 |
77 | # Thumbnails
78 | ._*
79 |
80 | # Files that might appear in the root of a volume
81 | .DocumentRevisions-V100
82 | .fseventsd
83 | .Spotlight-V100
84 | .TemporaryItems
85 | .Trashes
86 | .VolumeIcon.icns
87 | .com.apple.timemachine.donotpresent
88 |
89 | # Directories potentially created on remote AFP share
90 | .AppleDB
91 | .AppleDesktop
92 | Network Trash Folder
93 | Temporary Items
94 | .apdisk
95 |
96 | .vscode/
97 |
98 |
99 | # End of https://www.gitignore.io/api/osx,node
100 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:8
2 | MAINTAINER kumavis
3 |
4 | # setup app dir
5 | RUN mkdir -p /www/
6 | WORKDIR /www/
7 |
8 | # install dependencies
9 | COPY ./package.json /www/package.json
10 | RUN npm install
11 |
12 | # copy over app dir
13 | COPY ./ /www/
14 |
15 | # start server
16 | # CMD npm run server
17 | CMD npm run server:telemetry
18 |
19 | # expose server
20 | EXPOSE 9000
21 |
--------------------------------------------------------------------------------
/Notes.md:
--------------------------------------------------------------------------------
1 | - how do i prevent new connections?
2 | - how to tell if a peer supports my protocol? (just attempt dial?)
3 |
4 | - clients are both dialing kitsunet at eachother
5 | - how will we ever get wrtc stable?
6 | - per-protocol connection limits
7 |
8 | - stream each peer's networkState to cnc
9 | - use peerBook for connections list
10 | libp2p.switch.muxedConn
11 | libp2p.getPeers (?)
12 | - on connect, start timeout to check for kitsunet connection
13 | - track pubsub
14 |
15 | - opened https://github.com/libp2p/js-libp2p/issues/175
16 | - we want more low level hooks for rejecting connection attempts
17 | - how will we ever get wrtc stable?
18 | - per-protocol connection limits
19 |
20 | - [ ] network research
21 | - [ ] what to measure to determine network health?
22 | - [ ] what infrastructure to point at (instead of ipfs defaults)
23 | - [ ] how to stand up `ws-star.cloud.ipfs.team`
24 | - [ ] how do we limit peers to avoid crashing
25 |
26 | - [ ] metamask light client
27 | - [ ] block sync
28 | - [ ] how to setup custom protocol/rpc over libp2p
29 | - [ ] pubsub with validation
30 | - [ ] how to shard data
31 | - [ ] how to process new txs to get new state
32 |
33 | - what telemetry is useful to gather
34 | - bandwidth
35 | - location
36 | - memory available?
37 | - connects/disconnects/discoveries
38 | - pubsub spanning tree
39 | - errors: sentry
40 | - @vyzo is working on pubsub
41 | - @JGAntunes is working on pubsub
42 | - https://github.com/ipfs/notes/issues/266
43 | - platform deploy a "job" (eg peering strategy)
44 | - how to stand up `ws-star.cloud.ipfs.team`?
45 | - https://github.com/libp2p/js-libp2p-websocket-star-rendezvous
46 | - how do we limit peers to avoid crashing?
47 | - @pgte is working on this
48 | - application level prioritization of peers
49 | - https://github.com/ipfs/dynamic-data-and-capabilities/issues/3#issuecomment-361919002
50 | - watch repo https://github.com/libp2p/js-libp2p-connection-manager
51 | - how to configure the ipfs node for this experiment?
52 | - just boot libp2p?
53 |
54 | ### eth light client
55 | - GraphSync + selectors
56 | - implement custom protcols
57 | - [dialProtocol](https://github.com/libp2p/js-libp2p#libp2pdialprotocolpeer-protocol-callback)
58 | - [handleProtocol](https://github.com/libp2p/js-libp2p#libp2phandleprotocol-handlerfunc--matchfunc)
59 | - [example protocol - check dialer and listener inside src](https://github.com/libp2p/js-libp2p-identify/)
60 |
61 |
62 |
63 |
64 | bridge (golang)
65 | + peer index
66 | + block syncer
67 | + state bridge
68 | + go-ipfs
69 | ipld selector alpha (handling) <--- daviddias
70 | - state prefetch
71 | - state transition (full)
72 |
73 | metamask (js) <--- daviddias
74 | + block tracking via ipns published head
75 | + ipld selector alpha (requesting)
76 | + broadcast new tx
77 | - log querying ????????
78 | - peering: bridge
79 | - block syncing
80 |
81 | light client perf hacks
82 | - eth_call proofs (eth_call as query)
83 | - pub/sub storage/logs on full node
84 | - extra-consensus geth bloom filter trie
85 |
86 |
87 | Better eth rpc
88 | - stream data (e.g. logs), with cancel (+ backpressure?)
89 | - selectors
90 | - query trace (anything sent message/eth to me)
91 |
92 |
93 |
94 |
95 | perf: ipld-resolver selector alpha
96 | peering: 1) bridge 2) metamask mesh
97 | new block + tx publishing
98 | coselector indexing
99 | tx -> block
100 | log querying, log -> tx
101 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### MetaMask light client testing container
2 |
3 | MetaMask currently relies on centralized infrastructure.
4 | We've begun a long journey to turn MetaMask into a light client and bridge this network with the Ethereum mainnet.
5 |
6 | In order to achieve this goal we've created a testing container for deploying p2p experiments in real world environments. These experiments will help us tweak critical p2p components such as peering strategy, state shard distribution, and global pub-sub.
7 |
8 | If you would have any questions or would like to propose an experiment, please open an issue.
9 |
10 | ### Development
11 |
12 | in separate terminal tabs:
13 | ```
14 | npm start
15 | npm run server
16 | npm run swarm
17 | ```
18 |
19 | - see `secret` printed out by `npm run server`'s `telemetry` process.
20 | - open `http://localhost:9966/?admin=SECRET` with your secret
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | telemetry:
2 | build: ./
3 | restart: always
4 | ports:
5 | - "9000"
6 | - "9221:9221"
7 | expose:
8 | - "9221"
9 | environment:
10 | VIRTUAL_PORT: "9000"
11 | VIRTUAL_HOST: "telemetry.lab.metamask.io"
12 | LETSENCRYPT_HOST: "telemetry.lab.metamask.io"
13 | LETSENCRYPT_EMAIL: "admin@metamask.io"
14 |
--------------------------------------------------------------------------------
/graph.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
21 |
22 |
23 |
180 |
--------------------------------------------------------------------------------
/graph.js:
--------------------------------------------------------------------------------
1 | // const graph = buildGraph(data)
2 | // drawGraph(graph)
3 | const h = require('virtual-dom/h')
4 | const svg = require('virtual-dom/virtual-hyperscript/svg')
5 | const diff = require('virtual-dom/diff')
6 | const patch = require('virtual-dom/patch')
7 | const createElement = require('virtual-dom/create-element')
8 |
9 | const d3 = require('d3')
10 |
11 | module.exports = {
12 | setupDom,
13 | buildGraph,
14 | drawGraph,
15 | }
16 |
17 | function setupDom({ container, action }) {
18 |
19 | let tree = render(h('loading...'))
20 | let rootNode = createElement(tree)
21 | container.appendChild(rootNode)
22 |
23 | function rerender(newTree) {
24 | // const newTree = render(state)
25 | const patches = diff(tree, newTree)
26 | rootNode = patch(rootNode, patches)
27 | tree = newTree
28 | }
29 |
30 | return rerender
31 |
32 | // svg styles
33 | const style = document.createElement('style')
34 | style.textContent = (
35 | `
36 | .links line {
37 | stroke: #999;
38 | stroke-opacity: 0.6;
39 | }
40 |
41 | .nodes circle {
42 | stroke: #fff;
43 | stroke-width: 1.5px;
44 | }
45 |
46 | button.refresh {
47 | width: 120px;
48 | height: 30px;
49 | background-color: #4CAF50;
50 | color: white;
51 | border-radius: 3px;
52 | outline: none;
53 | border: 0;
54 | cursor: pointer;
55 | }
56 |
57 | button.refresh:hover {
58 | background-color: green;
59 | }
60 |
61 | .legend {
62 | font-family: "Arial", sans-serif;
63 | font-size: 11px;
64 | }
65 | `
66 | )
67 | document.head.appendChild(style)
68 |
69 | // // svg canvas
70 | // const svg = document.createElement('svg')
71 | // container.appendChild(svg)
72 | // svg.setAttribute('width', 960)
73 | // svg.setAttribute('height', 600)
74 |
75 | // action button
76 | const button = document.createElement('button')
77 | button.innerText = 'Refresh Graph'
78 | button.setAttribute("class", "refresh")
79 | button.addEventListener('click', action)
80 | container.appendChild(button)
81 | }
82 |
83 | function buildGraph(data) {
84 | const GOOD = '#1f77b4'
85 | const BAD = '#aec7e8'
86 | const MISSING = '#ff7f0e'
87 |
88 | const graph = { nodes: [], links: [] }
89 |
90 | // first add kitsunet nodes
91 | Object.keys(data).forEach((clientId) => {
92 | const peerData = data[clientId].peers
93 | const badResponse = (typeof peerData !== 'object')
94 | graph.nodes.push({ id: clientId, color: badResponse ? BAD : GOOD })
95 | })
96 |
97 | // then links
98 | Object.keys(data).forEach((clientId) => {
99 | const peerData = data[clientId].peers
100 | if (typeof peerData !== 'object') return
101 | Object.keys(peerData).forEach((peerId) => {
102 | // if connected to a missing node, create missing node
103 | const alreadyExists = !!graph.nodes.find(item => item.id === peerId)
104 | if (!alreadyExists) {
105 | graph.nodes.push({ id: peerId, color: MISSING })
106 | }
107 | // if peer rtt is timeout, dont draw link
108 | const rtt = peerData[peerId]
109 | // if (typeof rtt === 'string') return
110 | const timeout = rtt === 'timeout'
111 | // const linkValue = Math.pow((10 - Math.log(rtt)), 2)
112 | const linkValue = timeout ? 0.1 : 2
113 | graph.links.push({ source: clientId, target: peerId, value: linkValue })
114 | })
115 | })
116 |
117 | return graph
118 | }
119 |
120 | function drawGraph(graph) {
121 |
122 | //