Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

p2p messaging PoC #12

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
{
"presets": [
"@babel/preset-env",
[
"@babel/preset-env",
{
"targets": {
"electron": "5.0.6"
}
}
],
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-transform-runtime"
"@babel/plugin-transform-runtime",
"@babel/plugin-transform-modules-commonjs"
]
}
9 changes: 9 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"dependencies": {
"@apollo/react-hooks": "^3.1.0",
"@babel/runtime": "^7.5.4",
"@nodeutils/defaults-deep": "^1.1.0",
"apollo-cache-inmemory": "^1.6.3",
"apollo-client": "^2.6.4",
"apollo-link-http": "^1.5.16",
Expand All @@ -81,13 +82,21 @@
"graphql-tag": "^2.10.1",
"graphql-tools": "^4.0.5",
"graphql-transport-electron": "^1.0.1",
"ipfs": "^0.38.0",
"ipfs-http-client": "^33.1.0",
"ipfsd-ctl": "^0.44.1",
"is-ipfs": "^0.6.1",
"isomorphic-fetch": "^2.2.1",
"iterall": "^1.2.2",
"libp2p": "^0.26.2",
"libp2p-kad-dht": "^0.16.0",
"libp2p-mplex": "^0.8.5",
"libp2p-secio": "^0.11.1",
"libp2p-tcp": "^0.13.2",
"libp2p-webrtc-star": "^0.16.1",
"lodash": "^4.17.15",
"node-fetch": "^2.6.0",
"pull-stream": "^3.6.14",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-feather": "^2.0.3"
Expand Down
172 changes: 172 additions & 0 deletions src/common/peer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import IPFS from 'ipfs'
import Libp2p from 'libp2p'
import TCP from 'libp2p-tcp'
import Multiplex from 'libp2p-mplex'
import SECIO from 'libp2p-secio'
import defaultsDeep from '@nodeutils/defaults-deep'
import KadDHT from 'libp2p-kad-dht'
import Ping from 'libp2p/src/ping'
// import WebRTCStar from 'libp2p-webrtc-star'
import multiaddr from 'multiaddr'
import PeerInfo from 'peer-info'
import PeerId from 'peer-id'

import store from './store'

// const wstar = new WebRTCStar()

const DEFAULT_OPTS = {
modules: {
transport: [TCP],
// discovery: [wstar.discovery],
connEncryption: [SECIO],
streamMuxer: [Multiplex],
dht: KadDHT
},
config: {
dht: {
enabled: true,
kBucketSize: 20
}
}
}

export class P2PNode extends Libp2p {
constructor(opts) {
super(defaultsDeep(opts, DEFAULT_OPTS))
}

ping(remotePeerInfo, callback) {
const p = new Ping(this._switch, remotePeerInfo)
p.on('ping', time => {
p.stop() // stop sending pings
callback(null, time)
})
p.on('error', callback)
p.start()
}

pingRemotePeer(remoteAddress) {
const remoteAddr = multiaddr(remoteAddress)

// Convert the multiaddress into a PeerInfo object
const peerId = PeerId.createFromB58String(remoteAddr.getPeerId())
const remotePeerInfo = new PeerInfo(peerId)
remotePeerInfo.multiaddrs.add(remoteAddr)

console.log('pinging remote peer at ', remoteAddr.toString())
this.ping(remotePeerInfo, (err, time) => {
if (err) {
return console.error('error pinging: ', err)
}
console.log(`pinged ${remoteAddr.toString()} in ${time}ms`)
})
}

// query dht and get current address
async getAddrFromId(multihash) {
const peerId = PeerId.createFromB58String(multihash)
const result = await this.peerRouting.findPeer(peerId)
console.log({ result })
return result
}
}

export const createPeer = async (opts = {}) => {
const createPeerInfo = () => {
return new Promise((resolve, reject) => {
let PeerId = store.get('PeerId')
if (PeerId) {
PeerInfo.create(PeerId, (err, peerInfo) => {
if (err) {
reject(err)
}
resolve(peerInfo)
})
} else {
PeerInfo.create((err, peerInfo) => {
if (err) {
reject(err)
}
store.set('PeerId', peerInfo.id)
resolve(peerInfo)
})
}
})
}

const peerInfo = await createPeerInfo()
// add a listen address to accept TCP connections on a random port
const listenAddress = multiaddr(`/ip4/127.0.0.1/tcp/0`)
peerInfo.multiaddrs.add(listenAddress)

console.log({ opts })
const peer = new P2PNode({ peerInfo, ...opts })

// register an event handler for errors.
// here we're just going to print and re-throw the error
// to kill the program
peer.on('error', err => {
console.error('libp2p error: ', err)
throw err
})

peer.start(err => {
if (err) {
throw err
}
const addresses = peer.peerInfo.multiaddrs.toArray()
// pingRemotePeer(peer)
console.log('peer started. listening on addresses:')
addresses.forEach(addr => console.log(addr.toString()))
})
return peer
}

export const createIPFS = async () => {
const node = await IPFS.create({
libp2p: {
config: {
dht: {
enabled: true
}
}
}
})

// Lets log out the number of peers we have every 2 seconds
setInterval(async () => {
try {
const peers = await node.swarm.peers()
console.log(`The node now has ${peers.length} peers.`)
} catch (err) {
console.log('An error occurred trying to check our peers:', err)
}
}, 2000)

// Log out the bandwidth stats every 4 seconds so we can see how our configuration is doing
setInterval(async () => {
try {
const stats = await node.stats.bw()
console.log(`\nBandwidth Stats: ${JSON.stringify(stats, null, 2)}\n`)
} catch (err) {
console.log('An error occurred trying to check our stats:', err)
}
}, 4000)

let peers
peers = await node.swarm.peers() // empty peers (still connecting)

setTimeout(async () => {
peers = await node.swarm.peers() // several peers (connected now)

if (peers.length) {
const b58peerId = peers[0].peer._idB58String
const peerId = await node.dht.findPeer(b58peerId)

console.log('peerId', peerId)
}
}, 5000)

return node
}
1 change: 0 additions & 1 deletion src/common/store.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import electron from 'electron'
import Store from 'electron-store'

const config = {
Expand Down
File renamed without changes.
28 changes: 28 additions & 0 deletions src/ui/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react'
import { ApolloProvider } from '@apollo/react-hooks'

import { client } from './common/apollo'
import {
HashProvider,
PageProvider,
IpfsProvider,
SideMenu
} from './components'
import { Page } from './pages'

import css from './app.css'

export const App = () => (
<ApolloProvider client={client}>
<IpfsProvider>
<PageProvider>
<HashProvider>
<main className={css.main}>
<SideMenu />
<Page />
</main>
</HashProvider>
</PageProvider>
</IpfsProvider>
</ApolloProvider>
)
23 changes: 0 additions & 23 deletions src/ui/components/App/index.js

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { Box, Radio } from 'react-feather'
import { ipfs } from '../../../common/ipfs'
import { HashContext } from '../Context/hash'
import useInterval from '../Hooks/useInterval'
import { PinButton } from '../Button/pin'
import { PinButton } from '../Button'

import css from './hash.css'
import css from './style.css'

export const HashBar = () => {
const { hash, setHash } = useContext(HashContext)
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { CheckCircle, Download, Loader } from 'react-feather'
import { ipfs } from '../../../common/ipfs'
import { HashContext } from '../Context/hash'

import css from './pin.css'
import css from './style.css'

export const PinButton = () => {
const { hash, setHash } = useContext(HashContext)
Expand Down
File renamed without changes.
3 changes: 3 additions & 0 deletions src/ui/components/Context/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { HashProvider, HashContext } from './hash'
export { PageProvider, PageContext } from './page'
export { IpfsProvider, IpfsContext } from './ipfs'
33 changes: 33 additions & 0 deletions src/ui/components/Context/ipfs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import IPFS from 'ipfs'
import React, { createContext, useState, useEffect } from 'react'

export const IpfsContext = createContext({
ipfsNode: null
})

export const IpfsConsumer = IpfsContext.Consumer

export const IpfsProvider = ({ children }) => {
const [ipfsNode, setIpfsNode] = useState(null)

useEffect(() => {
IPFS.create({
libp2p: {
config: {
dht: {
enabled: true
}
}
}
}).then((node, error) => {
if (error) {
throw error
}
setIpfsNode(node)
})
}, [])

return (
<IpfsContext.Provider value={{ ipfsNode }}>{children}</IpfsContext.Provider>
)
}
1 change: 1 addition & 0 deletions src/ui/components/Hooks/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as useInterval } from './useInterval'
1 change: 1 addition & 0 deletions src/ui/components/Loaders/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Spinner } from './Spinner'
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FileText, GitCommit, Radio, Search } from 'react-feather'

import { PageContext } from '../Context/page'

import css from './side.css'
import css from './style.css'

export const SideMenu = () => {
const { page, setPage } = useContext(PageContext)
Expand Down
File renamed without changes.
7 changes: 7 additions & 0 deletions src/ui/components/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export * from './Bar'
export * from './Button'
export * from './Context'
export * from './Hooks'
export * from './Loaders'
export * from './Menu'
export * from './Welcome'
2 changes: 1 addition & 1 deletion src/ui/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react'
import { render } from 'react-dom'

import { App } from './components/App'
import { App } from './app'

render(<App />, document.getElementById('root'))
Loading