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

[WIP] Integrating libp2p into Lodestar #154

Merged
merged 32 commits into from
May 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ed4e1c4
Updated package.json and package-lock.json
Mikerah Apr 5, 2019
1aa5429
Completed basic lodestar node libp2p bundle
Mikerah Apr 5, 2019
f48cd22
Decided to add floodsub to libp2p node object
Mikerah Apr 6, 2019
02ee1af
Basic stubs and structure for p2p server
Mikerah Apr 8, 2019
01bded1
Started basic integration of libp2p into lodestar
Mikerah Apr 9, 2019
2f39098
Resolved package.json conflict
Mikerah Apr 9, 2019
3ea6824
Added Cayman's suggestions and refactored to include browser support
Mikerah Apr 11, 2019
3805a33
Added some of Cayman's suggestions
Mikerah Apr 11, 2019
0c2847c
Added browser and KadDHT
Mikerah Apr 13, 2019
db8ae92
Simplified libp2p-bundle.ts
Mikerah Apr 18, 2019
54ebbfa
fix types
mpetrunic Apr 19, 2019
3537c94
changed wrong import
mpetrunic Apr 19, 2019
2f0b9fa
Removed my original files regarding libp2p in favor of Marin's changes
Mikerah Apr 22, 2019
4537a07
Switched several dependencies to devDependencies
Mikerah Apr 22, 2019
d705fae
Kept polkadot/ts in devDependencies and moved the others back to depe…
Mikerah Apr 22, 2019
8a00147
Removed old dependency
Mikerah Apr 29, 2019
468f0ce
Added logger to p2p network
Mikerah Apr 29, 2019
d76ad40
Added Cayman's suggestions from review
Mikerah Apr 29, 2019
9a11f8f
Made libp2p node into a singleton and changed index.ts accordingly
Mikerah Apr 29, 2019
2995532
Filled stub for createPeerInfo
Mikerah Apr 29, 2019
c32aba2
Merge branch 'master' into mikerah/libp2p_integration
Mikerah Apr 29, 2019
e7ec0c1
fixed build errors
Mikerah Apr 29, 2019
7fbe951
Merge branch 'mikerah/libp2p_integration' of github.com:ChainSafe/lod…
Mikerah Apr 29, 2019
cf82a75
fixed build errors
Mikerah May 1, 2019
2c3a5e9
Fixed more build errors
Mikerah May 1, 2019
fc240b7
Fixed logger error statement
Mikerah May 1, 2019
6461d4d
Removed async from listeners as per Greg's review
Mikerah May 1, 2019
b8f97d6
Promisified createNode
Mikerah May 1, 2019
29e6bf6
Updated package.json
Mikerah May 1, 2019
74596d9
Forgot to add imports for promisify
Mikerah May 1, 2019
ba86442
Added Greg's and Cayman's review
Mikerah May 2, 2019
fca6924
fixed lint errors
Mikerah May 2, 2019
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
14 changes: 13 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
],
"dependencies": {
"@chainsafe/ssz": "^0.2.0",
"@nodeutils/defaults-deep": "^1.1.0",
"@polkadot/util-crypto": "^0.33.27",
"@types/bn.js": "^4.11.4",
"@types/levelup": "^3.1.0",
Expand All @@ -45,13 +46,23 @@
"commander": "^2.19.0",
"deepmerge": "^3.2.0",
"ethers": "^4.0.27",
"ganache-core": "^2.5.3",
"level": "^4.0.0",
"libp2p": "^0.24.4",
"libp2p-bootstrap": "^0.9.7",
"libp2p-floodsub": "^0.15.8",
"libp2p-kad-dht": "^0.14.12",
"libp2p-mplex": "^0.8.5",
"libp2p-tcp": "^0.13.0",
"libp2p-webrtc-star": "^0.15.8",
"noice-json-rpc": "^1.2.0",
"peer-book": "^0.9.1",
"peer-id": "^0.12.2",
"peer-info": "^0.15.1",
"pouchdb-adapter-memory": "^7.0.0",
"pouchdb-core": "^7.0.0",
"promisify-es6": "^1.0.3",
"winston": "^3.2.1",
"ganache-core": "^2.5.3",
"ws": "^6.2.1"
},
"bin": {
Expand All @@ -74,6 +85,7 @@
"@types/sinon": "^7.0.11",
"@typescript-eslint/eslint-plugin": "^1.3.0",
"@typescript-eslint/parser": "^1.3.0",
"@polkadot/ts": "^0.1.56",
Mikerah marked this conversation as resolved.
Show resolved Hide resolved
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"codecov": "^3.1.0",
Expand Down
2 changes: 2 additions & 0 deletions src/node/defaults.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import eth1Defaults from "../eth1/defaults";
import p2pDefaults from "../p2p/defaults";

export default {
chain: {
Expand All @@ -11,4 +12,5 @@ export default {
port: 9545
},
eth1: eth1Defaults,
p2p: p2pDefaults
};
6 changes: 4 additions & 2 deletions src/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,19 @@ import {JSONRPC} from "../rpc/protocol";
import {WSServer} from "../rpc/transport";
import {BeaconAPI} from "../rpc/api";

interface Service {
export interface Service {
start(): Promise<void>;

stop(): Promise<void>;
}

// Temporarily have properties be optional until others portions of lodestar are ready
interface BeaconNodeCtx {
chain?: object;
db?: object;
// Temporarily set to any. Will be changed to object later.
eth1?: any;
network?: object;
network?: any;
rpc?: object;
sync?: object;
opPool?: object;
Expand Down
8 changes: 8 additions & 0 deletions src/p2p/defaults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {PeerBook} from "peer-book";

export default {
maxPeers: 25,
refreshInterval: 15000,
peerBook: new PeerBook(),
bootnodes: []
};
146 changes: 141 additions & 5 deletions src/p2p/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,149 @@
import {EventEmitter} from "events";
import {Service} from "../node";
import {LodestarNode} from "./node";
import logger, {AbstractLogger} from "../logger";
import {PeerInfo} from "peer-info";
import LibP2p from "libp2p";
import {PeerBook} from "peer-book";
import {PeerId} from "peer-id";
import {promisify} from "promisify-es6";

export interface P2pOptions {
maxPeers: number;
refreshInterval: number;
peerBook: PeerBook;
privateKey: Buffer;
bootnodes: string[];
}

/**
* The P2PNetwork service manages p2p connection/subscription objects
*/
export class P2PNetwork extends EventEmitter {
public constructor(opts) {
export class P2PNetwork extends EventEmitter implements Service {

private options: P2pOptions;

private maxPeers: number;

private refreshInterval: number;

private peerBook: PeerBook;

private privateKey: Buffer;

private bootnodes: string[];

private started: boolean;

private node: LibP2p;

private discoveredPeers: Set<PeerInfo>;

private log: AbstractLogger;

public constructor(opts: P2pOptions) {
super();
// build gossipsub object
this.options = opts;
this.maxPeers = this.options.maxPeers;
this.refreshInterval = this.options.refreshInterval;
this.peerBook = this.options.peerBook;
this.privateKey = this.options.privateKey;
this.bootnodes = this.options.bootnodes;


this.started = false;
this.node = null;
this.discoveredPeers = new Set();
this.log = logger;
}

public get isRunning(): boolean {
return this.started;
}

public async start(): Promise<void> {
if (this.started) {
throw new Error("P2P Network already started");
}

if (!this.node) {
this.node = LodestarNode.createNode({
peerInfo: await this.createPeerInfo(),
bootnodes: this.options.bootnodes
});

this.node.on('peer:discovery', (peerInfo) => {
try {
const peerId = peerInfo.id.toB58String();
// Check if peer has already been discovered
if (this.options.peerBook.has(peerId) || this.discoveredPeers.has(peerId)) {
return;
}
this.peerBook.put(peerInfo);
this.node.dial(peerInfo, () => {});
this.log.info(`Peer discovered: ${peerInfo}`);
this.emit('connected', peerInfo);
} catch (err) {
this.log.error(err);
}

});

this.node.on('peer:connect', (peerInfo) => {
try {
this.log.info(`Peer connected: ${peerInfo}`);
this.peerBook.put(peerInfo);
this.discoveredPeers.add(peerInfo);
} catch (err) {
this.log.error(err);
}
});

this.node.on('peer:disconnect', (peerInfo) => {
try {
this.peerBook.remove(peerInfo);
this.discoveredPeers.delete(peerInfo);
} catch (err) {
this.log.error(err);
}
});
}
await promisify(this.node.start.bind(this.node))();

this.started = true;
}

public async stop(): Promise<void> {
Mikerah marked this conversation as resolved.
Show resolved Hide resolved
if (!this.started) {
return;
}
this.node.removeAllListeners();
await promisify(this.node.stop.bind(this.node))();
}

private async createPeerInfo(): PeerInfo {
return new Promise((resolve, reject) => {
const handler = (err, peerInfo) => {
if (err) {
return reject(err);
}
this.peerBook.getAll().forEach((peer) => {
peer.multiaddrs.forEach((multiaddr) => {
peerInfo.multiaddrs.add(multiaddr);
resolve(peerInfo);
});
});
};
if (this.privateKey) {
PeerId.createFromPrivKey(this.privateKey, (err, id) => {
if (err) {
return reject(err);
}
PeerInfo.create(id, handler);
});
} else {
PeerInfo.create(handler);
}
});
}
public async start() {}
public async stop() {}
}
60 changes: 60 additions & 0 deletions src/p2p/node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import LibP2p from "libp2p";
import {TCP} from "libp2p-tcp";
import {Mplex} from "libp2p-mplex";
import {Bootstrap} from "libp2p-bootstrap";
import {promisify} from "es6-promisify";
import {PeerInfo} from "peer-info";
import {PeerId} from "peer-id";
import {defaultsDeep} from "@nodeutils/defaults-deep";
import * as FloodSub from "libp2p-floodsub";

export interface LodestarNodeOpts {
bootstrap?: string[];
peerInfo: PeerInfo;
bootnodes?: string[];
}

export class LodestarNode extends LibP2p {

private pubsub: FloodSub;

private constructor(_options: LodestarNodeOpts) {
const defaults = {
modules: {
transport: [TCP],
streamMuxer: [Mplex],
peerDiscovery: [Bootstrap]
},
config: {
peerDiscovery: {
bootstrap: {
interval: 2000,
enabled: true,
list: _options.bootstrap || []
}
}
}
};

super(defaultsDeep(_options, defaults));
}

public static createNode(callback): LodestarNode {
const id = promisify(PeerId.create)({bits: 1024});
const peerInfo = promisify(PeerInfo.create)(id);
peerInfo.multiaddrs.add('ip4/0.0.0.0/tcp/9000');
const node = new LodestarNode({
peerInfo
});

node.pubsub = new FloodSub(node);

return node;
}

public async start(): Promise<void> {
await promisify(super.start.bind(this))();
await promisify(this.pubsub.start.bind(this.pubsub))();
}
}