2021-03-19 03:40:16 +00:00
|
|
|
import Libp2p from 'libp2p';
|
|
|
|
import Mplex from 'libp2p-mplex';
|
2021-03-22 11:16:12 +00:00
|
|
|
import { bytes } from 'libp2p-noise/dist/src/@types/basic';
|
|
|
|
import { Noise } from 'libp2p-noise/dist/src/noise';
|
2021-03-19 03:40:16 +00:00
|
|
|
import TCP from 'libp2p-tcp';
|
2021-03-23 00:14:51 +00:00
|
|
|
import Multiaddr from 'multiaddr';
|
2021-04-13 12:36:37 +00:00
|
|
|
import pTimeout from 'p-timeout';
|
2021-03-23 00:14:51 +00:00
|
|
|
import PeerId from 'peer-id';
|
2021-03-19 03:40:16 +00:00
|
|
|
|
2021-04-13 12:36:37 +00:00
|
|
|
import { delay } from './delay';
|
2021-04-01 05:41:49 +00:00
|
|
|
import { RelayCodec, WakuRelay, WakuRelayPubsub } from './waku_relay';
|
2021-04-07 01:04:30 +00:00
|
|
|
import { StoreCodec, WakuStore } from './waku_store';
|
2021-03-19 03:40:16 +00:00
|
|
|
|
2021-04-13 12:36:37 +00:00
|
|
|
const WaitForIdentityFreqMs = 50;
|
|
|
|
const WaitForIdentityTimeoutMs = 2_000;
|
|
|
|
|
2021-03-29 02:56:17 +00:00
|
|
|
export interface CreateOptions {
|
|
|
|
listenAddresses: string[];
|
|
|
|
staticNoiseKey: bytes | undefined;
|
|
|
|
}
|
|
|
|
|
2021-03-19 03:40:16 +00:00
|
|
|
export default class Waku {
|
2021-04-07 01:04:30 +00:00
|
|
|
private constructor(
|
|
|
|
public libp2p: Libp2p,
|
|
|
|
public relay: WakuRelay,
|
|
|
|
public store: WakuStore
|
|
|
|
) {}
|
2021-03-19 03:40:16 +00:00
|
|
|
|
2021-03-22 11:16:12 +00:00
|
|
|
/**
|
|
|
|
* Create new waku node
|
2021-03-29 02:56:17 +00:00
|
|
|
* @param listenAddresses: Array of Multiaddrs on which the node should listen. If not present, defaults to ['/ip4/0.0.0.0/tcp/0'].
|
2021-03-22 11:16:12 +00:00
|
|
|
* @param staticNoiseKey: A static key to use for noise,
|
|
|
|
* mainly used for test to reduce entropy usage.
|
|
|
|
* @returns {Promise<Waku>}
|
|
|
|
*/
|
2021-03-29 02:56:17 +00:00
|
|
|
static async create(options: Partial<CreateOptions>): Promise<Waku> {
|
|
|
|
const opts = Object.assign(
|
|
|
|
{
|
|
|
|
listenAddresses: ['/ip4/0.0.0.0/tcp/0'],
|
|
|
|
staticNoiseKey: undefined,
|
|
|
|
},
|
|
|
|
options
|
|
|
|
);
|
|
|
|
|
2021-03-19 03:40:16 +00:00
|
|
|
const libp2p = await Libp2p.create({
|
|
|
|
addresses: {
|
2021-03-29 02:56:17 +00:00
|
|
|
listen: opts.listenAddresses,
|
2021-03-19 03:40:16 +00:00
|
|
|
},
|
|
|
|
modules: {
|
|
|
|
transport: [TCP],
|
|
|
|
streamMuxer: [Mplex],
|
2021-03-29 02:56:17 +00:00
|
|
|
connEncryption: [new Noise(opts.staticNoiseKey)],
|
2021-03-19 03:40:16 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
// @ts-ignore: Type needs update
|
|
|
|
pubsub: WakuRelayPubsub,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2021-04-07 01:04:30 +00:00
|
|
|
const wakuStore = new WakuStore(libp2p);
|
|
|
|
|
2021-03-19 03:40:16 +00:00
|
|
|
await libp2p.start();
|
|
|
|
|
2021-04-07 01:04:30 +00:00
|
|
|
return new Waku(libp2p, new WakuRelay(libp2p.pubsub), wakuStore);
|
2021-03-19 03:40:16 +00:00
|
|
|
}
|
2021-03-19 05:07:56 +00:00
|
|
|
|
2021-03-23 00:14:51 +00:00
|
|
|
/**
|
2021-04-07 01:04:30 +00:00
|
|
|
* Dials to the provided peer.
|
2021-03-23 00:14:51 +00:00
|
|
|
* @param peer The peer to dial
|
|
|
|
*/
|
|
|
|
async dial(peer: PeerId | Multiaddr | string) {
|
2021-04-13 12:36:37 +00:00
|
|
|
await this.libp2p.dialProtocol(peer, [RelayCodec, StoreCodec]);
|
|
|
|
const peerId = toPeerId(peer);
|
|
|
|
await this.waitForIdentify(
|
|
|
|
peerId,
|
|
|
|
WaitForIdentityFreqMs,
|
|
|
|
WaitForIdentityTimeoutMs
|
|
|
|
);
|
2021-03-23 00:14:51 +00:00
|
|
|
}
|
|
|
|
|
2021-03-23 00:14:51 +00:00
|
|
|
async dialWithMultiAddr(peerId: PeerId, multiaddr: Multiaddr[]) {
|
|
|
|
this.libp2p.peerStore.addressBook.set(peerId, multiaddr);
|
2021-04-01 05:41:49 +00:00
|
|
|
await this.libp2p.dialProtocol(peerId, RelayCodec);
|
2021-04-13 12:36:37 +00:00
|
|
|
await this.waitForIdentify(
|
|
|
|
peerId,
|
|
|
|
WaitForIdentityFreqMs,
|
|
|
|
WaitForIdentityTimeoutMs
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wait for the identify protocol to be finished. This helps ensure
|
|
|
|
* we know what protocols the peer implements
|
|
|
|
* @param peerId
|
|
|
|
* @param frequencyMilliseconds
|
|
|
|
* @param maxTimeoutMilliseconds
|
|
|
|
* @throws If there is no known connection with this peer.
|
|
|
|
*/
|
|
|
|
async waitForIdentify(
|
|
|
|
peerId: PeerId,
|
|
|
|
frequencyMilliseconds: number,
|
|
|
|
maxTimeoutMilliseconds: number
|
|
|
|
): Promise<void> {
|
|
|
|
const checkProtocols = this._waitForIdentify.bind(
|
|
|
|
this,
|
|
|
|
peerId,
|
|
|
|
frequencyMilliseconds
|
|
|
|
)();
|
|
|
|
|
|
|
|
await pTimeout(checkProtocols, maxTimeoutMilliseconds);
|
|
|
|
}
|
|
|
|
|
|
|
|
async _waitForIdentify(peerId: PeerId, frequencyMilliseconds: number) {
|
|
|
|
while (true) {
|
|
|
|
const peer = this.libp2p.peerStore.get(peerId);
|
|
|
|
if (!peer) throw 'No connection to peer';
|
|
|
|
if (peer.protocols.length > 0) {
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
await delay(frequencyMilliseconds);
|
|
|
|
}
|
|
|
|
}
|
2021-03-23 00:14:51 +00:00
|
|
|
}
|
|
|
|
|
2021-03-19 05:07:56 +00:00
|
|
|
async stop() {
|
|
|
|
await this.libp2p.stop();
|
|
|
|
}
|
2021-04-06 01:06:10 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the local multiaddr with peer id on which libp2p is listening.
|
|
|
|
* @throws if libp2p is not listening on localhost
|
|
|
|
*/
|
|
|
|
getLocalMultiaddrWithID(): string {
|
|
|
|
const localMultiaddr = this.libp2p.multiaddrs.find((addr) =>
|
|
|
|
addr.toString().match(/127\.0\.0\.1/)
|
|
|
|
);
|
|
|
|
if (!localMultiaddr || localMultiaddr.toString() === '') {
|
|
|
|
throw 'Not listening on localhost';
|
|
|
|
}
|
|
|
|
const multiAddrWithId =
|
|
|
|
localMultiaddr + '/p2p/' + this.libp2p.peerId.toB58String();
|
|
|
|
return multiAddrWithId;
|
|
|
|
}
|
2021-03-19 03:40:16 +00:00
|
|
|
}
|
2021-04-13 12:36:37 +00:00
|
|
|
|
|
|
|
function toPeerId(peer: PeerId | Multiaddr | string): PeerId {
|
|
|
|
if (typeof peer === 'string') {
|
|
|
|
peer = new Multiaddr(peer);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Multiaddr.isMultiaddr(peer)) {
|
|
|
|
try {
|
|
|
|
peer = PeerId.createFromB58String(peer.getPeerId());
|
|
|
|
} catch (err) {
|
|
|
|
throw `${peer} is not a valid peer type`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return peer;
|
|
|
|
}
|