From 8333b8d6f852c777264a39b03f8b49e94c7a8a6d Mon Sep 17 00:00:00 2001 From: Guo Liu Date: Wed, 9 Oct 2019 18:15:18 -0700 Subject: [PATCH 1/7] js-libp2p init setup --- .babelrc | 13 ++-- package.json | 5 ++ src/common/store.js | 1 - src/ui/components/Page/Peers/chatRoom.js | 71 +++++++++++++++++++ .../Page/{peers.css => Peers/index.css} | 14 ++-- .../Page/{peers.js => Peers/index.js} | 11 +-- src/ui/components/Page/Peers/p2p/index.js | 22 ++++++ src/ui/components/Page/index.js | 2 +- 8 files changed, 122 insertions(+), 17 deletions(-) create mode 100644 src/ui/components/Page/Peers/chatRoom.js rename src/ui/components/Page/{peers.css => Peers/index.css} (90%) rename src/ui/components/Page/{peers.js => Peers/index.js} (89%) create mode 100644 src/ui/components/Page/Peers/p2p/index.js diff --git a/.babelrc b/.babelrc index 6674048..aedcd18 100644 --- a/.babelrc +++ b/.babelrc @@ -1,9 +1,14 @@ { "presets": [ - "@babel/preset-env", + [ + "@babel/preset-env", + { + "targets": { + "electron": "5.0.6" + } + } + ], "@babel/preset-react" ], - "plugins": [ - "@babel/plugin-transform-runtime" - ] + "plugins": ["@babel/plugin-transform-runtime"] } diff --git a/package.json b/package.json index 770383b..2157934 100644 --- a/package.json +++ b/package.json @@ -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", @@ -86,6 +87,10 @@ "is-ipfs": "^0.6.1", "isomorphic-fetch": "^2.2.1", "iterall": "^1.2.2", + "libp2p": "^0.26.2", + "libp2p-mplex": "^0.8.5", + "libp2p-secio": "^0.11.1", + "libp2p-tcp": "^0.13.2", "lodash": "^4.17.15", "node-fetch": "^2.6.0", "react": "^16.8.6", diff --git a/src/common/store.js b/src/common/store.js index bfbe6b1..72ddfda 100644 --- a/src/common/store.js +++ b/src/common/store.js @@ -1,4 +1,3 @@ -import electron from 'electron' import Store from 'electron-store' const config = { diff --git a/src/ui/components/Page/Peers/chatRoom.js b/src/ui/components/Page/Peers/chatRoom.js new file mode 100644 index 0000000..7b18ad1 --- /dev/null +++ b/src/ui/components/Page/Peers/chatRoom.js @@ -0,0 +1,71 @@ +import React, { useEffect } from 'react' +import multiaddr from 'multiaddr' +import PeerInfo from 'peer-info' + +import store from '../../../../common/store' +import { P2PNode } from './p2p' + +function handleStart(peer) { + // get the list of addresses for our peer now that it's started. + // there should be one address of the form + // `/ip4/127.0.0.1/tcp/${assignedPort}/ipfs/${generatedPeerId}`, + // where `assignedPort` is randomly chosen by the operating system + // and `generatedPeerId` is generated in the `createPeer` function above. + const addresses = peer.peerInfo.multiaddrs.toArray() + console.log('peer started. listening on addresses:') + addresses.forEach(addr => console.log(addr.toString())) +} + +const createPeer = async () => { + 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) + + const peer = new P2PNode({ peerInfo }) + + // 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 + } + handleStart(peer) + }) + return peer +} + +export const ChatRoom = () => { + useEffect(() => { + createPeer() + }) + + return +} diff --git a/src/ui/components/Page/peers.css b/src/ui/components/Page/Peers/index.css similarity index 90% rename from src/ui/components/Page/peers.css rename to src/ui/components/Page/Peers/index.css index 298db76..cfeb1e2 100644 --- a/src/ui/components/Page/peers.css +++ b/src/ui/components/Page/Peers/index.css @@ -9,7 +9,7 @@ flex-direction: column; border-bottom: 1px solid rgba(239, 232, 227, 0.6); - color: #725B54; + color: #725b54; font-size: 0.9rem; width: 100%; padding: 2rem 0 1rem 2rem; @@ -21,12 +21,12 @@ } .idIcon { - color: #725B54; + color: #725b54; margin: 0 0.5rem 0 0; } .idText { - color: #D86143; + color: #d86143; margin: 0 0 0 0.3rem; } @@ -37,12 +37,12 @@ } .peersIcon { - color: #725B54; + color: #725b54; margin: 0 0.5rem 0 0; } .peersText { - color: #D86143; + color: #d86143; margin: 0 0 0 0.3rem; } @@ -54,12 +54,12 @@ display: flex; align-items: flex-start; flex-direction: column; - + height: 30%; padding: 2rem 1rem 1rem 2rem; } .head { - color: #725B54; + color: #725b54; font-size: 0.9rem; font-weight: bold; width: 100%; diff --git a/src/ui/components/Page/peers.js b/src/ui/components/Page/Peers/index.js similarity index 89% rename from src/ui/components/Page/peers.js rename to src/ui/components/Page/Peers/index.js index cb0f597..f253533 100644 --- a/src/ui/components/Page/peers.js +++ b/src/ui/components/Page/Peers/index.js @@ -1,11 +1,12 @@ import React, { useEffect, useState } from 'react' import { Radio, User } from 'react-feather' -import { ipfs } from '../../../common/ipfs' -import useInterval from '../Hooks/useInterval' -import { Spinner } from '../Spinner' +import { ipfs } from '../../../../common/ipfs' +import useInterval from '../../Hooks/useInterval' +import { Spinner } from '../../Spinner' -import css from './peers.css' +import css from './index.css' +import { ChatRoom } from './chatRoom' const PeersTable = ({ peers }) => { const rows = peers.map((peer, index) => ( @@ -84,6 +85,8 @@ export const Peers = () => { /> )} {!loading && } + + ) } diff --git a/src/ui/components/Page/Peers/p2p/index.js b/src/ui/components/Page/Peers/p2p/index.js new file mode 100644 index 0000000..f80f133 --- /dev/null +++ b/src/ui/components/Page/Peers/p2p/index.js @@ -0,0 +1,22 @@ +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' + +const DEFAULT_OPTS = { + modules: { + transport: [TCP], + connEncryption: [SECIO], + streamMuxer: [Multiplex] + } +} + +export class P2PNode extends Libp2p { + constructor(opts) { + super(defaultsDeep(opts, DEFAULT_OPTS)) + } +} + +// export default { P2PNode } diff --git a/src/ui/components/Page/index.js b/src/ui/components/Page/index.js index ce6e2bf..587615e 100644 --- a/src/ui/components/Page/index.js +++ b/src/ui/components/Page/index.js @@ -4,7 +4,7 @@ import { PageContext } from '../Context/page' import { Articles } from './articles' import { Bootstrap } from './bootstrap' import { Explore } from './explore' -import { Peers } from './peers' +import { Peers } from './Peers' export const Page = () => { const { page } = useContext(PageContext) From 9ad949abca88cfd4eae9cd3430c6d04524f3ab26 Mon Sep 17 00:00:00 2001 From: Guo Liu Date: Wed, 9 Oct 2019 23:36:42 -0700 Subject: [PATCH 2/7] add ping --- src/ui/components/Page/Peers/chatRoom.js | 36 ++++++++++++++++++++--- src/ui/components/Page/Peers/p2p/index.js | 14 +++++++-- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/ui/components/Page/Peers/chatRoom.js b/src/ui/components/Page/Peers/chatRoom.js index 7b18ad1..1855d62 100644 --- a/src/ui/components/Page/Peers/chatRoom.js +++ b/src/ui/components/Page/Peers/chatRoom.js @@ -1,10 +1,31 @@ -import React, { useEffect } from 'react' +import React, { useEffect, useState } from 'react' import multiaddr from 'multiaddr' import PeerInfo from 'peer-info' - +import PeerId from 'peer-id' +import process from 'process' import store from '../../../../common/store' import { P2PNode } from './p2p' +function pingRemotePeer(localPeer, remoteAddress) { + if (!remoteAddress) { + return console.log('no remote peer address given, skipping ping') + } + 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()) + localPeer.ping(remotePeerInfo, (err, time) => { + if (err) { + return console.error('error pinging: ', err) + } + console.log(`pinged ${remoteAddr.toString()} in ${time}ms`) + }) +} + function handleStart(peer) { // get the list of addresses for our peer now that it's started. // there should be one address of the form @@ -12,6 +33,7 @@ function handleStart(peer) { // where `assignedPort` is randomly chosen by the operating system // and `generatedPeerId` is generated in the `createPeer` function above. const addresses = peer.peerInfo.multiaddrs.toArray() + // pingRemotePeer(peer) console.log('peer started. listening on addresses:') addresses.forEach(addr => console.log(addr.toString())) } @@ -63,9 +85,15 @@ const createPeer = async () => { } export const ChatRoom = () => { + const [peerAddr, setPeerAddr] = useState('') + useEffect(() => { - createPeer() + createPeer().then(peer => pingRemotePeer(peer, peerAddr)) }) - return + return ( +
+ setPeerAddr(e.target.value)} /> +
+ ) } diff --git a/src/ui/components/Page/Peers/p2p/index.js b/src/ui/components/Page/Peers/p2p/index.js index f80f133..cd9ba2e 100644 --- a/src/ui/components/Page/Peers/p2p/index.js +++ b/src/ui/components/Page/Peers/p2p/index.js @@ -2,8 +2,8 @@ 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 Ping from 'libp2p/src/ping' const DEFAULT_OPTS = { modules: { @@ -17,6 +17,14 @@ export class P2PNode extends Libp2p { constructor(opts) { super(defaultsDeep(opts, DEFAULT_OPTS)) } -} -// export default { P2PNode } + 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() + } +} From 7fbb04c72fdcd21676c6b19d4b9cd0d0c71ab4f8 Mon Sep 17 00:00:00 2001 From: Guo Liu Date: Fri, 11 Oct 2019 12:15:20 -0700 Subject: [PATCH 3/7] move peer to p2p file --- package.json | 2 + src/common/p2p.js | 121 ++++++++++++++++++++++ src/ui/components/Page/Peers/chatRoom.js | 121 ++++++---------------- src/ui/components/Page/Peers/p2p/index.js | 30 ------ 4 files changed, 157 insertions(+), 117 deletions(-) create mode 100644 src/common/p2p.js delete mode 100644 src/ui/components/Page/Peers/p2p/index.js diff --git a/package.json b/package.json index 2157934..13d6a5d 100644 --- a/package.json +++ b/package.json @@ -88,9 +88,11 @@ "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", "react": "^16.8.6", diff --git a/src/common/p2p.js b/src/common/p2p.js new file mode 100644 index 0000000..684bc85 --- /dev/null +++ b/src/common/p2p.js @@ -0,0 +1,121 @@ +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 () => { + 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) + + const peer = new P2PNode({ peerInfo }) + + // 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 +} diff --git a/src/ui/components/Page/Peers/chatRoom.js b/src/ui/components/Page/Peers/chatRoom.js index 1855d62..b872a5d 100644 --- a/src/ui/components/Page/Peers/chatRoom.js +++ b/src/ui/components/Page/Peers/chatRoom.js @@ -1,99 +1,46 @@ import React, { useEffect, useState } from 'react' -import multiaddr from 'multiaddr' -import PeerInfo from 'peer-info' -import PeerId from 'peer-id' -import process from 'process' -import store from '../../../../common/store' -import { P2PNode } from './p2p' +import { createPeer } from '../../../../common/p2p' -function pingRemotePeer(localPeer, remoteAddress) { - if (!remoteAddress) { - return console.log('no remote peer address given, skipping ping') - } - 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()) - localPeer.ping(remotePeerInfo, (err, time) => { - if (err) { - return console.error('error pinging: ', err) - } - console.log(`pinged ${remoteAddr.toString()} in ${time}ms`) - }) -} - -function handleStart(peer) { - // get the list of addresses for our peer now that it's started. - // there should be one address of the form - // `/ip4/127.0.0.1/tcp/${assignedPort}/ipfs/${generatedPeerId}`, - // where `assignedPort` is randomly chosen by the operating system - // and `generatedPeerId` is generated in the `createPeer` function above. - const addresses = peer.peerInfo.multiaddrs.toArray() - // pingRemotePeer(peer) - console.log('peer started. listening on addresses:') - addresses.forEach(addr => console.log(addr.toString())) -} - -const createPeer = async () => { - 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) - - const peer = new P2PNode({ peerInfo }) +export const ChatRoom = () => { + const [remotePeerId, setRemotePeerId] = useState('') + const [remotePeerAddr, setRemotePeerAddr] = useState('') + const [peer, setPeer] = useState(null) - // 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 - }) + // initialize + useEffect(() => { + createPeer().then(peer => setPeer(peer)) + }, []) - peer.start(err => { - if (err) { - throw err + // get address from id + useEffect(() => { + if (peer && remotePeerId) { + peer + .getAddrFromId(remotePeerId) + .then(address => setRemotePeerAddr(address)) } - handleStart(peer) - }) - return peer -} - -export const ChatRoom = () => { - const [peerAddr, setPeerAddr] = useState('') + }, [remotePeerId]) + // trigger pings useEffect(() => { - createPeer().then(peer => pingRemotePeer(peer, peerAddr)) - }) + if (peer && remotePeerAddr) { + peer.pingRemotePeer(remotePeerAddr) + } + }, [remotePeerAddr]) return ( -
- setPeerAddr(e.target.value)} /> + + { + setRemotePeerId(e.target.value) + }} + /> + { + setRemotePeerAddr(e.target.value) + }} + />
) } diff --git a/src/ui/components/Page/Peers/p2p/index.js b/src/ui/components/Page/Peers/p2p/index.js deleted file mode 100644 index cd9ba2e..0000000 --- a/src/ui/components/Page/Peers/p2p/index.js +++ /dev/null @@ -1,30 +0,0 @@ -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 Ping from 'libp2p/src/ping' - -const DEFAULT_OPTS = { - modules: { - transport: [TCP], - connEncryption: [SECIO], - streamMuxer: [Multiplex] - } -} - -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() - } -} From 2189e52205ebec1c76532b4f67b54b95bb274bc8 Mon Sep 17 00:00:00 2001 From: Guo Liu Date: Wed, 16 Oct 2019 17:09:58 -0700 Subject: [PATCH 4/7] restructure folder --- package.json | 1 + src/common/p2p.js | 55 ++++++++++++++++++- src/ui/{components/App/style.css => app.css} | 0 src/ui/{components/App/index.js => app.js} | 12 ++-- src/ui/components/Bar/{hash.js => index.js} | 4 +- src/ui/components/Bar/{hash.css => style.css} | 0 src/ui/components/Button/{pin.js => index.js} | 2 +- .../components/Button/{pin.css => style.css} | 0 src/ui/components/Context/index.js | 4 ++ src/ui/components/Hooks/index.js | 3 + .../{Spinner/index.js => Loaders/Spinner.js} | 0 src/ui/components/Loaders/index.js | 3 + .../components/{Spinner => Loaders}/style.css | 0 src/ui/components/Menu/{side.js => index.js} | 2 +- .../components/Menu/{side.css => style.css} | 0 src/ui/components/index.js | 7 +++ src/ui/index.js | 2 +- .../Page => pages}/Peers/chatRoom.js | 17 ++++-- .../Page => pages}/Peers/index.css | 0 .../{components/Page => pages}/Peers/index.js | 6 +- .../{components/Page => pages}/articles.css | 0 src/ui/{components/Page => pages}/articles.js | 4 +- .../{components/Page => pages}/bootstrap.css | 0 .../{components/Page => pages}/bootstrap.js | 4 +- src/ui/{components/Page => pages}/explore.css | 0 src/ui/{components/Page => pages}/explore.js | 7 +-- src/ui/{components/Page => pages}/index.js | 3 +- 27 files changed, 104 insertions(+), 32 deletions(-) rename src/ui/{components/App/style.css => app.css} (100%) rename src/ui/{components/App/index.js => app.js} (56%) rename src/ui/components/Bar/{hash.js => index.js} (95%) rename src/ui/components/Bar/{hash.css => style.css} (100%) rename src/ui/components/Button/{pin.js => index.js} (98%) rename src/ui/components/Button/{pin.css => style.css} (100%) create mode 100644 src/ui/components/Context/index.js create mode 100644 src/ui/components/Hooks/index.js rename src/ui/components/{Spinner/index.js => Loaders/Spinner.js} (100%) create mode 100644 src/ui/components/Loaders/index.js rename src/ui/components/{Spinner => Loaders}/style.css (100%) rename src/ui/components/Menu/{side.js => index.js} (97%) rename src/ui/components/Menu/{side.css => style.css} (100%) create mode 100644 src/ui/components/index.js rename src/ui/{components/Page => pages}/Peers/chatRoom.js (64%) rename src/ui/{components/Page => pages}/Peers/index.css (100%) rename src/ui/{components/Page => pages}/Peers/index.js (92%) rename src/ui/{components/Page => pages}/articles.css (100%) rename src/ui/{components/Page => pages}/articles.js (94%) rename src/ui/{components/Page => pages}/bootstrap.css (100%) rename src/ui/{components/Page => pages}/bootstrap.js (96%) rename src/ui/{components/Page => pages}/explore.css (100%) rename src/ui/{components/Page => pages}/explore.js (84%) rename src/ui/{components/Page => pages}/index.js (86%) diff --git a/package.json b/package.json index 13d6a5d..b31155e 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "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", diff --git a/src/common/p2p.js b/src/common/p2p.js index 684bc85..a84d771 100644 --- a/src/common/p2p.js +++ b/src/common/p2p.js @@ -1,3 +1,4 @@ +import IPFS from 'ipfs' import Libp2p from 'libp2p' import TCP from 'libp2p-tcp' import Multiplex from 'libp2p-mplex' @@ -71,7 +72,7 @@ export class P2PNode extends Libp2p { } } -export const createPeer = async () => { +export const createPeer = async (opts = {}) => { const createPeerInfo = () => { return new Promise((resolve, reject) => { let PeerId = store.get('PeerId') @@ -93,12 +94,14 @@ export const createPeer = async () => { } }) } + 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) - const peer = new P2PNode({ peerInfo }) + 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 @@ -119,3 +122,51 @@ export const createPeer = async () => { }) 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 +} diff --git a/src/ui/components/App/style.css b/src/ui/app.css similarity index 100% rename from src/ui/components/App/style.css rename to src/ui/app.css diff --git a/src/ui/components/App/index.js b/src/ui/app.js similarity index 56% rename from src/ui/components/App/index.js rename to src/ui/app.js index 66c8089..58d88b1 100644 --- a/src/ui/components/App/index.js +++ b/src/ui/app.js @@ -1,13 +1,13 @@ import React from 'react' import { ApolloProvider } from '@apollo/react-hooks' -import { client } from '../../common/apollo' -import { HashProvider } from '../Context/hash' -import { PageProvider } from '../Context/page' -import { Page } from '../Page' -import { SideMenu } from '../Menu/side' +import { client } from './common/apollo' +import { HashProvider } from './components/Context/hash' +import { PageProvider } from './components/Context/page' +import { Page } from './pages' +import { SideMenu } from './components/Menu' -import css from './style.css' +import css from './app.css' export const App = () => ( diff --git a/src/ui/components/Bar/hash.js b/src/ui/components/Bar/index.js similarity index 95% rename from src/ui/components/Bar/hash.js rename to src/ui/components/Bar/index.js index 00cc4a8..746712f 100644 --- a/src/ui/components/Bar/hash.js +++ b/src/ui/components/Bar/index.js @@ -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) diff --git a/src/ui/components/Bar/hash.css b/src/ui/components/Bar/style.css similarity index 100% rename from src/ui/components/Bar/hash.css rename to src/ui/components/Bar/style.css diff --git a/src/ui/components/Button/pin.js b/src/ui/components/Button/index.js similarity index 98% rename from src/ui/components/Button/pin.js rename to src/ui/components/Button/index.js index 1f52c79..786076b 100644 --- a/src/ui/components/Button/pin.js +++ b/src/ui/components/Button/index.js @@ -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) diff --git a/src/ui/components/Button/pin.css b/src/ui/components/Button/style.css similarity index 100% rename from src/ui/components/Button/pin.css rename to src/ui/components/Button/style.css diff --git a/src/ui/components/Context/index.js b/src/ui/components/Context/index.js new file mode 100644 index 0000000..a597adf --- /dev/null +++ b/src/ui/components/Context/index.js @@ -0,0 +1,4 @@ +import { HashProvider, HashContext } from './hash' +import { PageProvider, PageContext } from './page' + +export { HashProvider, HashContext, PageProvider, PageContext } diff --git a/src/ui/components/Hooks/index.js b/src/ui/components/Hooks/index.js new file mode 100644 index 0000000..a39762d --- /dev/null +++ b/src/ui/components/Hooks/index.js @@ -0,0 +1,3 @@ +import useInterval from './useInterval' + +export default { useInterval } diff --git a/src/ui/components/Spinner/index.js b/src/ui/components/Loaders/Spinner.js similarity index 100% rename from src/ui/components/Spinner/index.js rename to src/ui/components/Loaders/Spinner.js diff --git a/src/ui/components/Loaders/index.js b/src/ui/components/Loaders/index.js new file mode 100644 index 0000000..372e35a --- /dev/null +++ b/src/ui/components/Loaders/index.js @@ -0,0 +1,3 @@ +import { Spinner } from './Spinner' + +export default { Spinner } diff --git a/src/ui/components/Spinner/style.css b/src/ui/components/Loaders/style.css similarity index 100% rename from src/ui/components/Spinner/style.css rename to src/ui/components/Loaders/style.css diff --git a/src/ui/components/Menu/side.js b/src/ui/components/Menu/index.js similarity index 97% rename from src/ui/components/Menu/side.js rename to src/ui/components/Menu/index.js index 7cd86fc..0d837ed 100644 --- a/src/ui/components/Menu/side.js +++ b/src/ui/components/Menu/index.js @@ -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) diff --git a/src/ui/components/Menu/side.css b/src/ui/components/Menu/style.css similarity index 100% rename from src/ui/components/Menu/side.css rename to src/ui/components/Menu/style.css diff --git a/src/ui/components/index.js b/src/ui/components/index.js new file mode 100644 index 0000000..34978e5 --- /dev/null +++ b/src/ui/components/index.js @@ -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' diff --git a/src/ui/index.js b/src/ui/index.js index 2493c3c..4c34156 100644 --- a/src/ui/index.js +++ b/src/ui/index.js @@ -1,6 +1,6 @@ import React from 'react' import { render } from 'react-dom' -import { App } from './components/App' +import { App } from './app' render(, document.getElementById('root')) diff --git a/src/ui/components/Page/Peers/chatRoom.js b/src/ui/pages/Peers/chatRoom.js similarity index 64% rename from src/ui/components/Page/Peers/chatRoom.js rename to src/ui/pages/Peers/chatRoom.js index b872a5d..844ea97 100644 --- a/src/ui/components/Page/Peers/chatRoom.js +++ b/src/ui/pages/Peers/chatRoom.js @@ -1,22 +1,29 @@ import React, { useEffect, useState } from 'react' -import { createPeer } from '../../../../common/p2p' +import PeerId from 'peer-id' +import { createPeer, createIPFS } from '../../../common/p2p' export const ChatRoom = () => { const [remotePeerId, setRemotePeerId] = useState('') const [remotePeerAddr, setRemotePeerAddr] = useState('') const [peer, setPeer] = useState(null) + const [node, setNode] = useState(null) // initialize useEffect(() => { createPeer().then(peer => setPeer(peer)) + createIPFS().then(node => setNode(node)) }, []) // get address from id useEffect(() => { - if (peer && remotePeerId) { - peer - .getAddrFromId(remotePeerId) - .then(address => setRemotePeerAddr(address)) + if (node && remotePeerId) { + // peer + // .getAddrFromId(remotePeerId) + // .then(address => setRemotePeerAddr(address)) + const peerId = PeerId.createFromB58String(remotePeerId) + node.libp2p.peerRouting + .findPeer(peerId) + .then((result, error) => console.log({ result, error })) } }, [remotePeerId]) diff --git a/src/ui/components/Page/Peers/index.css b/src/ui/pages/Peers/index.css similarity index 100% rename from src/ui/components/Page/Peers/index.css rename to src/ui/pages/Peers/index.css diff --git a/src/ui/components/Page/Peers/index.js b/src/ui/pages/Peers/index.js similarity index 92% rename from src/ui/components/Page/Peers/index.js rename to src/ui/pages/Peers/index.js index f253533..1cde69e 100644 --- a/src/ui/components/Page/Peers/index.js +++ b/src/ui/pages/Peers/index.js @@ -1,9 +1,9 @@ import React, { useEffect, useState } from 'react' import { Radio, User } from 'react-feather' -import { ipfs } from '../../../../common/ipfs' -import useInterval from '../../Hooks/useInterval' -import { Spinner } from '../../Spinner' +import { ipfs } from '../../../common/ipfs' +import useInterval from '../../components/Hooks/useInterval' +import { Spinner } from '../../components/Loaders/Spinner' import css from './index.css' import { ChatRoom } from './chatRoom' diff --git a/src/ui/components/Page/articles.css b/src/ui/pages/articles.css similarity index 100% rename from src/ui/components/Page/articles.css rename to src/ui/pages/articles.css diff --git a/src/ui/components/Page/articles.js b/src/ui/pages/articles.js similarity index 94% rename from src/ui/components/Page/articles.js rename to src/ui/pages/articles.js index 096015e..3f9150c 100644 --- a/src/ui/components/Page/articles.js +++ b/src/ui/pages/articles.js @@ -2,10 +2,8 @@ import React, { useContext } from 'react' import { useQuery } from '@apollo/react-hooks' import gql from 'graphql-tag' -import { Spinner } from '../Spinner' +import { Spinner, HashContext, PageContext } from '../components' import css from './articles.css' -import { HashContext } from '../Context/hash' -import { PageContext } from '../Context/page' export const Articles = () => { const SUBSCRIPTIONS = gql` diff --git a/src/ui/components/Page/bootstrap.css b/src/ui/pages/bootstrap.css similarity index 100% rename from src/ui/components/Page/bootstrap.css rename to src/ui/pages/bootstrap.css diff --git a/src/ui/components/Page/bootstrap.js b/src/ui/pages/bootstrap.js similarity index 96% rename from src/ui/components/Page/bootstrap.js rename to src/ui/pages/bootstrap.js index 7c348ad..058d455 100644 --- a/src/ui/components/Page/bootstrap.js +++ b/src/ui/pages/bootstrap.js @@ -2,8 +2,8 @@ import trim from 'lodash/trim' import React, { useEffect, useState } from 'react' import { GitCommit, Trash2, User } from 'react-feather' -import { ipfs } from '../../../common/ipfs' -import { Spinner } from '../Spinner' +import { ipfs } from '../../common/ipfs' +import { Spinner } from '../components/Loaders/Spinner' import css from './bootstrap.css' diff --git a/src/ui/components/Page/explore.css b/src/ui/pages/explore.css similarity index 100% rename from src/ui/components/Page/explore.css rename to src/ui/pages/explore.css diff --git a/src/ui/components/Page/explore.js b/src/ui/pages/explore.js similarity index 84% rename from src/ui/components/Page/explore.js rename to src/ui/pages/explore.js index be41dba..684722e 100644 --- a/src/ui/components/Page/explore.js +++ b/src/ui/pages/explore.js @@ -1,10 +1,7 @@ import React, { useContext, useEffect, useState, useRef } from 'react' -import { getLocalHttp } from '../../../common/ipfs' -import { HashContext } from '../Context/hash' -import { HashBar } from '../Bar/hash' -import { Welcome } from '../Welcome' -import { Spinner } from '../Spinner' +import { getLocalHttp } from '../../common/ipfs' +import { Welcome, Spinner, HashBar, HashContext } from '../components' import css from './explore.css' diff --git a/src/ui/components/Page/index.js b/src/ui/pages/index.js similarity index 86% rename from src/ui/components/Page/index.js rename to src/ui/pages/index.js index 587615e..170eaea 100644 --- a/src/ui/components/Page/index.js +++ b/src/ui/pages/index.js @@ -1,12 +1,13 @@ import React, { useContext } from 'react' -import { PageContext } from '../Context/page' +import { PageContext } from '../components' import { Articles } from './articles' import { Bootstrap } from './bootstrap' import { Explore } from './explore' import { Peers } from './Peers' export const Page = () => { + console.log({ PageContext }) const { page } = useContext(PageContext) switch (page) { From f5e2281362dea9c6268ea80f825ac20c50eb3781 Mon Sep 17 00:00:00 2001 From: Guo Liu Date: Thu, 17 Oct 2019 15:59:41 -0700 Subject: [PATCH 5/7] add message protocal --- .babelrc | 5 +- package.json | 1 + src/ui/app.js | 27 +++--- src/ui/components/Context/index.js | 7 +- src/ui/components/Context/ipfs.js | 33 +++++++ src/ui/components/Hooks/index.js | 4 +- src/ui/components/Loaders/index.js | 4 +- src/ui/pages/Peers/chatRoom.js | 144 ++++++++++++++++++++++------- src/ui/pages/index.js | 1 - 9 files changed, 169 insertions(+), 57 deletions(-) create mode 100644 src/ui/components/Context/ipfs.js diff --git a/.babelrc b/.babelrc index aedcd18..0bcad69 100644 --- a/.babelrc +++ b/.babelrc @@ -10,5 +10,8 @@ ], "@babel/preset-react" ], - "plugins": ["@babel/plugin-transform-runtime"] + "plugins": [ + "@babel/plugin-transform-runtime", + "@babel/plugin-transform-modules-commonjs" + ] } diff --git a/package.json b/package.json index b31155e..4c0ea6c 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,7 @@ "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" diff --git a/src/ui/app.js b/src/ui/app.js index 58d88b1..d598319 100644 --- a/src/ui/app.js +++ b/src/ui/app.js @@ -2,22 +2,27 @@ import React from 'react' import { ApolloProvider } from '@apollo/react-hooks' import { client } from './common/apollo' -import { HashProvider } from './components/Context/hash' -import { PageProvider } from './components/Context/page' +import { + HashProvider, + PageProvider, + IpfsProvider, + SideMenu +} from './components' import { Page } from './pages' -import { SideMenu } from './components/Menu' import css from './app.css' export const App = () => ( - - -
- - -
-
-
+ + + +
+ + +
+
+
+
) diff --git a/src/ui/components/Context/index.js b/src/ui/components/Context/index.js index a597adf..db7e4c3 100644 --- a/src/ui/components/Context/index.js +++ b/src/ui/components/Context/index.js @@ -1,4 +1,3 @@ -import { HashProvider, HashContext } from './hash' -import { PageProvider, PageContext } from './page' - -export { HashProvider, HashContext, PageProvider, PageContext } +export { HashProvider, HashContext } from './hash' +export { PageProvider, PageContext } from './page' +export { IpfsProvider, IpfsContext } from './ipfs' diff --git a/src/ui/components/Context/ipfs.js b/src/ui/components/Context/ipfs.js new file mode 100644 index 0000000..496e1da --- /dev/null +++ b/src/ui/components/Context/ipfs.js @@ -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 ( + {children} + ) +} diff --git a/src/ui/components/Hooks/index.js b/src/ui/components/Hooks/index.js index a39762d..829c83f 100644 --- a/src/ui/components/Hooks/index.js +++ b/src/ui/components/Hooks/index.js @@ -1,3 +1 @@ -import useInterval from './useInterval' - -export default { useInterval } +export { default as useInterval } from './useInterval' diff --git a/src/ui/components/Loaders/index.js b/src/ui/components/Loaders/index.js index 372e35a..fbf16c1 100644 --- a/src/ui/components/Loaders/index.js +++ b/src/ui/components/Loaders/index.js @@ -1,3 +1 @@ -import { Spinner } from './Spinner' - -export default { Spinner } +export { Spinner } from './Spinner' diff --git a/src/ui/pages/Peers/chatRoom.js b/src/ui/pages/Peers/chatRoom.js index 844ea97..e0619df 100644 --- a/src/ui/pages/Peers/chatRoom.js +++ b/src/ui/pages/Peers/chatRoom.js @@ -1,53 +1,129 @@ -import React, { useEffect, useState } from 'react' +import React, { useEffect, useState, useContext } from 'react' import PeerId from 'peer-id' -import { createPeer, createIPFS } from '../../../common/p2p' +import pull from 'pull-stream' + +import { IpfsContext } from '../../components' + +const protocalName = '/chat/1.0.0' export const ChatRoom = () => { + const { ipfsNode } = useContext(IpfsContext) + + const [viewerId, setViewerId] = useState('') const [remotePeerId, setRemotePeerId] = useState('') - const [remotePeerAddr, setRemotePeerAddr] = useState('') - const [peer, setPeer] = useState(null) - const [node, setNode] = useState(null) + const [remotePeerInfo, setRemotePeerInfo] = useState('') - // initialize + const [messageDraft, setMessageDraft] = useState('') + const [dialog, setDialog] = useState([]) + + // get viewer id, assign protocal useEffect(() => { - createPeer().then(peer => setPeer(peer)) - createIPFS().then(node => setNode(node)) - }, []) + if (ipfsNode) { + ipfsNode.id().then(({ id }, err) => { + if (err) { + throw err + } + setViewerId(id) + }) + + ipfsNode.libp2p.handle(protocalName, (protocol, connection) => { + console.log({ protocol, connection }) + pull( + connection, + pull.collect((err, data) => { + if (err) { + throw err + } + setDialog(dialog => dialog.concat([JSON.parse(data.toString())])) + }) + ) + }) + } + }) // get address from id useEffect(() => { - if (node && remotePeerId) { - // peer - // .getAddrFromId(remotePeerId) - // .then(address => setRemotePeerAddr(address)) + if (ipfsNode && remotePeerId) { const peerId = PeerId.createFromB58String(remotePeerId) - node.libp2p.peerRouting - .findPeer(peerId) - .then((result, error) => console.log({ result, error })) + ipfsNode.libp2p.peerRouting.findPeer(peerId).then((peerInfo, err) => { + if (err) { + throw err + } + setRemotePeerInfo(peerInfo) + }) } }, [remotePeerId]) - // trigger pings - useEffect(() => { - if (peer && remotePeerAddr) { - peer.pingRemotePeer(remotePeerAddr) + const handleSubmit = event => { + if (ipfsNode && remotePeerInfo) { + const message = { content: messageDraft, from: viewerId } + ipfsNode.libp2p.dialProtocol( + remotePeerInfo, + protocalName, + (err, connection) => { + pull(pull.values([message]), connection) + } + ) + setDialog(dialog => dialog.concat([message])) + } else { + console.error('connection not established', messageDraft) } - }, [remotePeerAddr]) + event.preventDefault() + } + + console.log(dialog) return ( -
- { - setRemotePeerId(e.target.value) - }} - /> - { - setRemotePeerAddr(e.target.value) +
+ My id: {viewerId} + - + > + { + setRemotePeerId(e.target.value) + }} + style={{ margin: 10 }} + /> +
+ +
+ {dialog.map(({ content, from }, index) => ( + + {content} + + ))} +
+
+ { + setMessageDraft(e.target.value) + }} + style={{ margin: 10, width: '80%' }} + /> +
+
) } diff --git a/src/ui/pages/index.js b/src/ui/pages/index.js index 170eaea..fd1bb7f 100644 --- a/src/ui/pages/index.js +++ b/src/ui/pages/index.js @@ -7,7 +7,6 @@ import { Explore } from './explore' import { Peers } from './Peers' export const Page = () => { - console.log({ PageContext }) const { page } = useContext(PageContext) switch (page) { From f6708eec25c92827c1a45266e2759d93ffaf3bd7 Mon Sep 17 00:00:00 2001 From: Guo Liu Date: Thu, 17 Oct 2019 16:53:12 -0700 Subject: [PATCH 6/7] fix message render --- src/ui/pages/Peers/chatRoom.js | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/ui/pages/Peers/chatRoom.js b/src/ui/pages/Peers/chatRoom.js index e0619df..029ba86 100644 --- a/src/ui/pages/Peers/chatRoom.js +++ b/src/ui/pages/Peers/chatRoom.js @@ -27,14 +27,18 @@ export const ChatRoom = () => { }) ipfsNode.libp2p.handle(protocalName, (protocol, connection) => { - console.log({ protocol, connection }) pull( connection, pull.collect((err, data) => { if (err) { throw err } - setDialog(dialog => dialog.concat([JSON.parse(data.toString())])) + + const message = JSON.parse(data.toString()) + setDialog(dialog => dialog.concat([message])) + + // temperally set repose target for now + setRemotePeerId(message.from) }) ) }) @@ -61,7 +65,7 @@ export const ChatRoom = () => { remotePeerInfo, protocalName, (err, connection) => { - pull(pull.values([message]), connection) + pull(pull.values([JSON.stringify(message)]), connection) } ) setDialog(dialog => dialog.concat([message])) @@ -71,8 +75,6 @@ export const ChatRoom = () => { event.preventDefault() } - console.log(dialog) - return (
My id: {viewerId} @@ -85,6 +87,7 @@ export const ChatRoom = () => { > { setRemotePeerId(e.target.value) }} @@ -104,7 +107,14 @@ export const ChatRoom = () => { }} /> -
+
{dialog.map(({ content, from }, index) => ( { onChange={e => { setMessageDraft(e.target.value) }} - style={{ margin: 10, width: '80%' }} + style={{ margin: 10, width: '60%' }} />
From b8e753af419a4303cb8ad6d70d7b33b43ce94b2f Mon Sep 17 00:00:00 2001 From: Guo Liu Date: Thu, 17 Oct 2019 16:59:11 -0700 Subject: [PATCH 7/7] renaming p2p file --- src/common/{p2p.js => peer.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/common/{p2p.js => peer.js} (100%) diff --git a/src/common/p2p.js b/src/common/peer.js similarity index 100% rename from src/common/p2p.js rename to src/common/peer.js