2021-06-08 12:01:48 +00:00
|
|
|
import Libp2p, { Libp2pModules, Libp2pOptions } from 'libp2p';
|
2021-03-19 03:40:16 +00:00
|
|
|
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-04-15 01:19:26 +00:00
|
|
|
import Websockets from 'libp2p-websockets';
|
2021-04-22 00:39:05 +00:00
|
|
|
import filters from 'libp2p-websockets/src/filters';
|
2021-05-13 01:47:03 +00:00
|
|
|
import { Multiaddr, multiaddr } from 'multiaddr';
|
2021-03-23 00:14:51 +00:00
|
|
|
import PeerId from 'peer-id';
|
2021-03-19 03:40:16 +00:00
|
|
|
|
2021-05-19 01:00:43 +00:00
|
|
|
import { WakuLightPush } from './waku_light_push';
|
2021-04-16 01:25:08 +00:00
|
|
|
import { RelayCodec, WakuRelay } 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-06-08 12:01:48 +00:00
|
|
|
const websocketsTransportKey = Websockets.prototype[Symbol.toStringTag];
|
2021-04-22 00:39:05 +00:00
|
|
|
|
2021-06-08 12:01:48 +00:00
|
|
|
export interface CreateOptions {
|
|
|
|
/**
|
|
|
|
* You can pass options to the `Libp2p` instance used by {@link Waku} using the {@link CreateOptions.libp2p} property.
|
|
|
|
* This property is the same type than the one passed to [`Libp2p.create`](https://github.com/libp2p/js-libp2p/blob/master/doc/API.md#create)
|
|
|
|
* apart that we made the `modules` property optional and partial,
|
|
|
|
* allowing its omission and letting Waku set good defaults.
|
|
|
|
* Notes that some values are overridden by {@link Waku} to ensure it implements the Waku protocol.
|
|
|
|
*/
|
|
|
|
libp2p?: Omit<Libp2pOptions & import('libp2p').CreateOptions, 'modules'> & {
|
|
|
|
modules?: Partial<Libp2pModules>;
|
|
|
|
};
|
|
|
|
/**
|
|
|
|
* Byte array used as key for the noise protocol used for connection encryption
|
|
|
|
* by [`Libp2p.create`](https://github.com/libp2p/js-libp2p/blob/master/doc/API.md#create)
|
|
|
|
* This is only used for test purposes to not run out of entropy during CI runs.
|
|
|
|
*/
|
|
|
|
staticNoiseKey?: bytes;
|
|
|
|
}
|
2021-03-29 02:56:17 +00:00
|
|
|
|
2021-05-10 05:26:14 +00:00
|
|
|
export class Waku {
|
2021-04-16 01:25:08 +00:00
|
|
|
public libp2p: Libp2p;
|
|
|
|
public relay: WakuRelay;
|
|
|
|
public store: WakuStore;
|
2021-05-19 01:00:43 +00:00
|
|
|
public lightPush: WakuLightPush;
|
2021-04-16 01:25:08 +00:00
|
|
|
|
2021-05-19 01:00:43 +00:00
|
|
|
private constructor(
|
|
|
|
libp2p: Libp2p,
|
|
|
|
store: WakuStore,
|
|
|
|
lightPush: WakuLightPush
|
|
|
|
) {
|
2021-04-16 01:25:08 +00:00
|
|
|
this.libp2p = libp2p;
|
|
|
|
this.relay = (libp2p.pubsub as unknown) as WakuRelay;
|
|
|
|
this.store = store;
|
2021-05-19 01:00:43 +00:00
|
|
|
this.lightPush = lightPush;
|
2021-04-16 01:25:08 +00:00
|
|
|
}
|
2021-03-19 03:40:16 +00:00
|
|
|
|
2021-03-22 11:16:12 +00:00
|
|
|
/**
|
|
|
|
* Create new waku node
|
2021-05-13 01:49:20 +00:00
|
|
|
*
|
|
|
|
* @param options Takes the same options than `Libp2p`.
|
2021-03-22 11:16:12 +00:00
|
|
|
*/
|
2021-06-08 12:01:48 +00:00
|
|
|
static async create(options?: CreateOptions): Promise<Waku> {
|
|
|
|
// Get an object in case options or libp2p are undefined
|
|
|
|
const libp2pOpts = Object.assign({}, options?.libp2p);
|
2021-03-29 02:56:17 +00:00
|
|
|
|
2021-06-08 12:01:48 +00:00
|
|
|
// Default for Websocket filter is `all`:
|
|
|
|
// Returns all TCP and DNS based addresses, both with ws or wss.
|
|
|
|
libp2pOpts.config = Object.assign(
|
2021-04-22 04:47:43 +00:00
|
|
|
{
|
|
|
|
transport: {
|
2021-06-08 12:01:48 +00:00
|
|
|
[websocketsTransportKey]: {
|
2021-04-22 04:47:43 +00:00
|
|
|
filter: filters.all,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2021-06-08 12:01:48 +00:00
|
|
|
options?.libp2p?.config
|
2021-04-22 04:47:43 +00:00
|
|
|
);
|
|
|
|
|
2021-06-08 12:01:48 +00:00
|
|
|
libp2pOpts.modules = Object.assign({}, options?.libp2p?.modules);
|
2021-04-22 04:47:43 +00:00
|
|
|
|
2021-06-08 12:01:48 +00:00
|
|
|
// Default transport for libp2p is Websockets
|
|
|
|
libp2pOpts.modules = Object.assign(
|
|
|
|
{
|
|
|
|
transport: [Websockets],
|
2021-03-19 03:40:16 +00:00
|
|
|
},
|
2021-06-08 12:01:48 +00:00
|
|
|
options?.libp2p?.modules
|
|
|
|
);
|
|
|
|
|
|
|
|
// streamMuxer, connection encryption and pubsub are overridden
|
|
|
|
// as those are the only ones currently supported by Waku nodes.
|
|
|
|
libp2pOpts.modules = Object.assign(libp2pOpts.modules, {
|
|
|
|
streamMuxer: [Mplex],
|
|
|
|
connEncryption: [new Noise(options?.staticNoiseKey)],
|
|
|
|
pubsub: WakuRelay,
|
2021-03-19 03:40:16 +00:00
|
|
|
});
|
|
|
|
|
2021-06-08 12:01:48 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
// @ts-ignore: modules property is correctly set thanks to voodoo
|
|
|
|
const libp2p = await Libp2p.create(libp2pOpts);
|
|
|
|
|
2021-04-07 01:04:30 +00:00
|
|
|
const wakuStore = new WakuStore(libp2p);
|
2021-05-19 01:00:43 +00:00
|
|
|
const wakuLightPush = new WakuLightPush(libp2p);
|
2021-04-07 01:04:30 +00:00
|
|
|
|
2021-03-19 03:40:16 +00:00
|
|
|
await libp2p.start();
|
|
|
|
|
2021-05-19 01:00:43 +00:00
|
|
|
return new Waku(libp2p, wakuStore, wakuLightPush);
|
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-05-13 01:47:03 +00:00
|
|
|
*
|
2021-03-23 00:14:51 +00:00
|
|
|
* @param peer The peer to dial
|
|
|
|
*/
|
2021-05-03 05:52:38 +00:00
|
|
|
async dial(
|
|
|
|
peer: PeerId | Multiaddr | string
|
|
|
|
): Promise<{
|
|
|
|
stream: import('libp2p-interfaces/src/stream-muxer/types').MuxedStream;
|
|
|
|
protocol: string;
|
|
|
|
}> {
|
|
|
|
return this.libp2p.dialProtocol(peer, [RelayCodec, StoreCodec]);
|
2021-03-23 00:14:51 +00:00
|
|
|
}
|
|
|
|
|
2021-05-13 01:47:03 +00:00
|
|
|
/**
|
|
|
|
* Add peer to address book, it will be auto-dialed in the background.
|
|
|
|
*/
|
|
|
|
addPeerToAddressBook(
|
|
|
|
peerId: PeerId | string,
|
|
|
|
multiaddrs: Multiaddr[] | string[]
|
|
|
|
): void {
|
|
|
|
let peer;
|
|
|
|
if (typeof peerId === 'string') {
|
|
|
|
peer = PeerId.createFromB58String(peerId);
|
|
|
|
} else {
|
|
|
|
peer = peerId;
|
|
|
|
}
|
|
|
|
const addresses = multiaddrs.map((addr: Multiaddr | string) => {
|
|
|
|
if (typeof addr === 'string') {
|
|
|
|
return multiaddr(addr);
|
|
|
|
} else {
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
this.libp2p.peerStore.addressBook.set(peer, addresses);
|
2021-03-23 00:14:51 +00:00
|
|
|
}
|
|
|
|
|
2021-05-03 05:52:38 +00:00
|
|
|
async stop(): Promise<void> {
|
|
|
|
return this.libp2p.stop();
|
2021-03-19 05:07:56 +00:00
|
|
|
}
|
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';
|
|
|
|
}
|
2021-05-13 01:49:36 +00:00
|
|
|
return localMultiaddr + '/p2p/' + this.libp2p.peerId.toB58String();
|
2021-04-06 01:06:10 +00:00
|
|
|
}
|
2021-03-19 03:40:16 +00:00
|
|
|
}
|