Skip to content

Commit

Permalink
refactor(brave): dedicated libp2p bundle
Browse files Browse the repository at this point in the history
  • Loading branch information
lidel committed Sep 11, 2019
1 parent 0d28d66 commit 2896eda
Show file tree
Hide file tree
Showing 6 changed files with 513 additions and 192 deletions.
107 changes: 107 additions & 0 deletions add-on/src/lib/ipfs-client/chrome-sockets/libp2p-bundle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
'use strict'

const get = require('dlv')
const mergeOptions = require('merge-options')
const errCode = require('err-code')

const ipns = require('ipns')
const multiaddr = require('multiaddr')
const DelegatedPeerRouter = require('libp2p-delegated-peer-routing')
const DelegatedContentRouter = require('libp2p-delegated-content-routing')
const PubsubRouters = { gossipsub: require('libp2p-gossipsub') }
const Libp2pChromeSockets = require('./libp2p')

// libp2p bundle customized for chrome.sockets
// Loosely follows https://github.com/ipfs/js-ipfs/blob/master/src/core/components/libp2p.js
module.exports = function chromeSocketsBundle ({ datastore, peerInfo, peerBook, options, config }) {
// TODO: Set up Delegate Routing based on the presence of Delegates in the config?
let contentRouting
let peerRouting

const delegateHosts = get(options, 'config.Addresses.Delegates',
get(config, 'Addresses.Delegates', [])
)
if (delegateHosts.length > 0) {
// Pick a random delegate host
const delegateString = delegateHosts[Math.floor(Math.random() * delegateHosts.length)]
const delegateAddr = multiaddr(delegateString).toOptions()
const delegatedApiOptions = {
host: delegateAddr.host,
// port is a string atm, so we need to convert for the check
protocol: parseInt(delegateAddr.port) === 443 ? 'https' : 'http',
port: delegateAddr.port
}
contentRouting = [new DelegatedContentRouter(peerInfo.id, delegatedApiOptions)]
peerRouting = [new DelegatedPeerRouter(delegatedApiOptions)]
}

const getPubsubRouter = () => {
const router = get(config, 'Pubsub.Router', 'gossipsub')

if (!PubsubRouters[router]) {
throw errCode(new Error(`Router unavailable. Configure libp2p.modules.pubsub to use the ${router} router.`), 'ERR_NOT_SUPPORTED')
}

return PubsubRouters[router]
}

const libp2pDefaults = {
datastore,
peerInfo,
peerBook,
modules: {
contentRouting,
peerRouting,
pubsub: getPubsubRouter()
},
config: {
peerDiscovery: {
bootstrap: {
list: get(options, 'config.Bootstrap',
get(config, 'Bootstrap', []))
}
},
relay: {
enabled: get(options, 'relay.enabled',
get(config, 'relay.enabled', true)),
hop: {
enabled: get(options, 'relay.hop.enabled',
get(config, 'relay.hop.enabled', false)),
active: get(options, 'relay.hop.active',
get(config, 'relay.hop.active', false))
}
},
dht: {
kBucketSize: get(options, 'dht.kBucketSize', 20),
// enabled: !get(options, 'offline', false), // disable if offline, on by default
enabled: false,
randomWalk: {
enabled: false // disabled waiting for https://github.com/libp2p/js-libp2p-kad-dht/issues/86
},
validators: {
ipns: {
func: (key, record, cb) => ipns.validator.validate(record, key, cb)
}
},
selectors: {
ipns: (k, records) => ipns.validator.select(records[0], records[1])
}
},
pubsub: {
enabled: get(config, 'Pubsub.Enabled', true)
}
},
connectionManager: get(options, 'connectionManager',
{
maxPeers: get(config, 'Swarm.ConnMgr.HighWater'),
minPeers: get(config, 'Swarm.ConnMgr.LowWater')
})
}
let libp2pOptions
if (typeof options.libp2p !== 'function') {
libp2pOptions = mergeOptions(libp2pDefaults, get(options, 'libp2p', {}))
} else {
libp2pOptions = libp2pDefaults
}
return new Libp2pChromeSockets(libp2pOptions)
}
85 changes: 85 additions & 0 deletions add-on/src/lib/ipfs-client/chrome-sockets/libp2p.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
const TCP = require('libp2p-tcp')
const MulticastDNS = require('libp2p-mdns')
const WS = require('libp2p-websockets')
const WebSocketStarMulti = require('libp2p-websocket-star-multi')
const Bootstrap = require('libp2p-bootstrap')
const KadDHT = require('libp2p-kad-dht')
const GossipSub = require('libp2p-gossipsub')
const Multiplex = require('pull-mplex')
const SECIO = require('libp2p-secio')
const libp2p = require('libp2p')
const mergeOptions = require('merge-options')
const multiaddr = require('multiaddr')

// libp2p class with constructor tweaked for use with chrome.sockets
// loosely follows:
// - https://github.com/ipfs/js-ipfs/blob/master/src/core/runtime/libp2p-nodejs.js
// - https://github.com/ipfs/js-ipfs/blob/master/src/core/runtime/libp2p-browser.js
class Libp2pChromeSockets extends libp2p {
constructor (_options) {
// this can be replaced once optional listening is supported with the below code. ref: https://github.com/libp2p/interface-transport/issues/41
// const wsstar = new WebSocketStar({ id: _options.peerInfo.id })
const wsstarServers = _options.peerInfo.multiaddrs.toArray().map(String).filter(addr => addr.includes('p2p-websocket-star'))
_options.peerInfo.multiaddrs.replace(wsstarServers.map(multiaddr), '/p2p-websocket-star') // the ws-star-multi module will replace this with the chosen ws-star servers
const wsstar = new WebSocketStarMulti({ servers: wsstarServers, id: _options.peerInfo.id, ignore_no_online: !wsstarServers.length || _options.wsStarIgnoreErrors })

const defaults = {
switch: {
denyTTL: 2 * 60 * 1e3, // 2 minute base
denyAttempts: 5, // back off 5 times
maxParallelDials: 150,
maxColdCalls: 50,
dialTimeout: 10e3 // Be strict with dial time
},
modules: {
transport: [
TCP,
WS,
wsstar
],
streamMuxer: [
Multiplex
],
connEncryption: [
SECIO
],
peerDiscovery: [
MulticastDNS,
Bootstrap,
wsstar.discovery
],
dht: KadDHT,
pubsub: GossipSub
},
config: {
peerDiscovery: {
autoDial: true,
mdns: {
enabled: true
},
bootstrap: {
enabled: true
},
websocketStar: {
enabled: true
}
},
dht: {
kBucketSize: 20,
enabled: false,
randomWalk: {
enabled: false
}
},
pubsub: {
enabled: true,
emitSelf: true
}
}
}

super(mergeOptions(defaults, _options))
}
}

module.exports = Libp2pChromeSockets
54 changes: 7 additions & 47 deletions add-on/src/lib/ipfs-client/embedded-chromesockets.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ const maToUri = require('multiaddr-to-uri')
const getPort = require('get-port')

// libp2p
const WS = require('libp2p-websockets')
// const WS = require('libp2p-websockets')
// const WSM = require('libp2p-websocket-star-multi')
const TCP = require('libp2p-tcp')
const MulticastDNS = require('libp2p-mdns')
const Bootstrap = require('libp2p-bootstrap')
// const TCP = require('libp2p-tcp')
// const MulticastDNS = require('libp2p-mdns')
// const Bootstrap = require('libp2p-bootstrap')

const { optionDefaults } = require('../options')

const chromeSocketsBundle = require('./chrome-sockets/libp2p-bundle')

// js-ipfs with embedded hapi HTTP server
let node = null
let nodeHttpApi = null
Expand All @@ -40,49 +42,7 @@ async function buildConfig (opts) {
const defaultOpts = JSON.parse(optionDefaults.ipfsNodeConfig)
const userOpts = JSON.parse(opts.ipfsNodeConfig)

const ipfsNodeConfig = mergeOptions.call({ concatArrays: true }, defaultOpts, userOpts, { start: false })

// TODO: replace object with function that builds the bundle
// See defaultBundle in js-ipfs/src/core/components/libp2p.js
ipfsNodeConfig.libp2p = {
// node defaults instead of browser ones
switch: {
blacklistTTL: 2 * 60 * 1e3, // 2 minute base
blackListAttempts: 5, // back off 5 times
maxParallelDials: 150,
maxColdCalls: 50,
dialTimeout: 10e3 // Be strict with dial time
},
modules: {
transport: [new TCP(), new WS()],
peerDiscovery: [
MulticastDNS,
new Bootstrap({ list: ipfsNodeConfig.config.Bootstrap })
]
},
config: {
peerDiscovery: {
autoDial: true,
mdns: {
enabled: true
},
bootstrap: {
enabled: true
},
websocketStar: {
enabled: true
}
},
dht: {
// TODO: KadDHT seems to be CPU-bound in browser context, needs investigation
kBucketSize: 20,
enabled: false,
randomWalk: {
enabled: false
}
}
}
}
const ipfsNodeConfig = mergeOptions.call({ concatArrays: true }, defaultOpts, userOpts, { start: false, libp2p: chromeSocketsBundle })

// Detect when API or Gateway port is not available (taken by something else)
// We find the next free port and update configuration to use it instead
Expand Down
4 changes: 2 additions & 2 deletions add-on/src/lib/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ function buildDefaultIpfsNodeConfig () {
// Until we have MulticastDNS+DNS, peer discovery is done over ws-star
config.config.Addresses.Swarm = [
'/ip4/0.0.0.0/tcp/0',
'/dns4/ws-star1.par.dwebops.pub/tcp/443/wss/p2p-websocket-star',
'/dns4/ws-star.discovery.libp2p.io/tcp/443/wss/p2p-websocket-star'
'/dns4/ws-star1.par.dwebops.pub/tcp/443/wss/p2p-websocket-star'
// '/dns4/ws-star.discovery.libp2p.io/tcp/443/wss/p2p-websocket-star'
]
/*
// Until DHT and p2p transport are ready, delegate + preload
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@
"get-port": "5.0.0",
"http-dns": "3.0.1",
"http-node": "1.2.0",
"ipfs": "https://github.com/ipfs/js-ipfs/tarball/6fa8f88310a4f7f451f0f6846e435134376703e6/js-ipfs.tar.gz",
"ipfs": "https://github.com/ipfs/js-ipfs/tarball/af208c3fe33168bac9573fab06e5728cf09b124c/js-ipfs.tar.gz",
"ipfs-css": "0.13.1",
"ipfs-http-client": "33.1.1",
"ipfs-http-response": "0.3.1",
Expand Down
Loading

0 comments on commit 2896eda

Please sign in to comment.