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

Use libp2p-mdns with Electron #880

Closed
raphael10-collab opened this issue Jan 29, 2021 · 11 comments
Closed

Use libp2p-mdns with Electron #880

raphael10-collab opened this issue Jan 29, 2021 · 11 comments

Comments

@raphael10-collab
Copy link

raphael10-collab commented Jan 29, 2021

In an Electron-Typescript-React app when I add this line:

streamMuxer: [Mplex],

I get this error:

TS2322: Type '{ transport: any[]; connEncryption: Noise[]; streamMuxer: any[]; peerDiscovery: any[]; }' is not assignable to type  
'Libp2pModules'.
  Object literal may only specify known properties, and 'peerDiscovery' does not exist in type 'Libp2pModules'.
    30 |       connEncryption: [NOISE],
    31 |       streamMuxer: [Mplex],
  > 32 |       peerDiscovery: [MulticastDNS]
       |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    33 |     },
    34 |     config: {
    35 |       peerDiscovery: {

image

This is the code in src/app/components/App.tsx :

import Libp2p from 'libp2p';
import Websockets from 'libp2p-websockets';
import WebRTCStar from 'libp2p-webrtc-star';
import { NOISE } from 'libp2p-noise';
import Mplex from 'libp2p-mplex';
import Bootstrap from 'libp2p-bootstrap';
import KadDHT from 'libp2p-kad-dht';
import MulticastDNS from 'libp2p-mdns';

document.addEventListener('DOMContentLoaded', async () => {
  // Create our libp2p node
  const libp2p = await Libp2p.create({
    addresses: {
      // Add the signaling server address, along with our PeerId to our multiaddrs list
      // libp2p will automatically attempt to dial to the signaling server so that it can
      // receive inbound connections from other peers
      listen: [
        '/dns4/wrtc-star1.par.dwebops.pub/tcp/443/wss/p2p-webrtc-star',
        '/dns4/wrtc-star2.sjc.dwebops.pub/tcp/443/wss/p2p-webrtc-star'
      ]
    },
    modules: {
      transport: [Websockets, WebRTCStar],
      connEncryption: [NOISE],
      streamMuxer: [Mplex],
      peerDiscovery: [MulticastDNS]
    },
    config: {
      peerDiscovery: {
        // The `tag` property will be searched when creating the instance of your Peer Discovery service.
       // The associated object, will be passed to the service when it is instantiated.
        [Bootstrap.tag]: {
          enabled: true,
          list: [
            '/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN',
            '/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb',
            '/dnsaddr/bootstrap.libp2p.io/p2p/QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp',
            '/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa',
            '/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt'
          ]
        },
       [MulticastDNS.tag]: {
         interval: 20e3,
         enabled: true
      }
    }
  })

/tools/webpack/webpack.renderer.js :

const rules = require('./webpack.rules');
const plugins = require('./webpack.plugins');
const aliases = require('./webpack.aliases');

module.exports = {
  // https://github.com/electron/electron/issues/9920
  //target: 'electron-renderer',
  target: 'web',
  module: {
    rules,
  },
  plugins: plugins,
  resolve: {
    extensions: ['.js', '.ts', '.jsx', '.tsx', '.css'],
    alias: {
      // React Hot Loader Patch
      'react-dom': '@hot-loader/react-dom',
      // Custom Aliases
      ...aliases,
    },
  },
};

/tools/webpack/webpack.rules.js :

const inDev = process.env.NODE_ENV === 'development';

module.exports = [
  {
    // Add support for native node modules
    test: /\.node$/,
    use: 'node-loader',
  },
  {
    // Webpack asset relocator loader
    test: /\.(m?js|node)$/,
    parser: { amd: false },
    use: {
      loader: '@marshallofsound/webpack-asset-relocator-loader',
      options: {
        outputAssetBase: 'native_modules',
      },
    },
  },
  {
    // Typescript loader
    test: /\.tsx?$/,
    exclude: /(node_modules|\.webpack)/,
    use: {
      loader: 'ts-loader',
      options: {
        transpileOnly: true,
      },
    },
  },
  {
    // CSS Loader
    test: /\.css$/,
    use: [{ loader: 'style-loader' }, { loader: 'css-loader' }],
  },
  {
    // Less loader
    test: /\.less$/,
    use: [
      { loader: 'style-loader' },
      { loader: 'css-loader' },
      { loader: 'less-loader' },
    ],
  },
  {
    // Images Loader
    test: /\.(gif|jpe?g|tiff|png|webp|bmp)$/,
    use: [
      {
        loader: 'file-loader',
        options: {
          publicPath: 'images',
          outputPath: inDev ? 'images' : './main_window/images',
        },
      },
    ],
  },
];

/tools/webpack/webpack.main.js :

module.exports = {
  /**
   * This is the main entry point for your application, it's the first file
   * that runs in the main process.
   */
  entry: ['./src/main.ts'],
  // Put your normal webpack config below here
  module: {
    rules: require('./webpack.rules'),
  },
  resolve: {
    extensions: ['.js', '.ts', '.jsx', '.tsx', '.css', '.json'],
    alias: require('./webpack.aliases'),
  },
  // https://github.com/electron/electron/issues/9920
  target: 'electron-renderer'
};

/tools/webpack/webpack.plugins.js :

// eslint-disable-next-line @typescript-eslint/no-var-requires
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');

// https://stackoverflow.com/questions/44008674/how-to-import-the-electron-ipcrenderer-in-a-react-webpack-2-setup
const webpack = require('webpack');

module.exports = [
  new ForkTsCheckerWebpackPlugin(),
  new webpack.ExternalsPlugin('commonjs', [
    'electron'
  ])
];

devDependencies :

"@electron-forge/cli": "6.0.0-beta.53",
"@electron-forge/maker-deb": "6.0.0-beta.53",
"@electron-forge/maker-rpm": "6.0.0-beta.53",
"@electron-forge/maker-squirrel": "6.0.0-beta.53",
"@electron-forge/maker-zip": "6.0.0-beta.53",
"@electron-forge/plugin-webpack": "6.0.0-beta.53",
"electron": "^11.2.1",
"typescript": "^4.0.2",
"webpack": "4"

dependencies:

"libp2p": "^0.30.6",
"libp2p-bootstrap": "^0.12.1",
"libp2p-kad-dht": "^0.20.6",
"libp2p-mdns": "^0.15.0",
"libp2p-mplex": "^0.10.2",
"libp2p-noise": "^2.0.4",
"libp2p-tcp": "^0.15.2",
"libp2p-webrtc-star": "^0.21.0",
"libp2p-websockets": "^0.15.0",
  • node version: v14.5.0
  • O.S. : Ubuntu 18.04.4 Desktop

If you need to reproduce the error,
I've put the simple repo here: https://github.com/raphael10-collab/Libp2pPlaying

Just git clone it ----> yarn ---> yarn start

@raphael10-collab raphael10-collab changed the title Type '{ transport: any[]; connEncryption: Noise[]; streamMuxer: any[]; peerDiscovery: any[]; }' is not assignable to type 'Libp2pModules' Adding streamMuxer: [Mplex], causes: Type '{ transport: any[]; connEncryption: Noise[]; streamMuxer: any[]; peerDiscovery: any[]; }' is not assignable to type 'Libp2pModules' Jan 29, 2021
@vasco-santos
Copy link
Member

Can you add "suppressExcessPropertyErrors": true to your tsconfig and check if that solves the issue?

We do not have the type definitions complete in libp2p for some functionalities as you can see per #830

@raphael10-collab
Copy link
Author

raphael10-collab commented Jan 29, 2021

@vasco-santos Adding "suppressExcessPropertyErrors": true in tsconfig.json makes the error about the type disappearing.

But still got this error:

image

@vasco-santos
Copy link
Member

@raphael10-collab dgram is a Node.js module for UDP sockets used by https://github.com/mafintosh/multicast-dns/blob/master/index.js#L2 which is a dependency of libp2p-mdns. It seems to not be available here because the browser bundle is created and MDNS is not supported in the browser. However, this should be possible in Electron.

Any thoughts @hugomrdias ?

@vasco-santos vasco-santos changed the title Adding streamMuxer: [Mplex], causes: Type '{ transport: any[]; connEncryption: Noise[]; streamMuxer: any[]; peerDiscovery: any[]; }' is not assignable to type 'Libp2pModules' Use libp2p-mdns with Electron Feb 1, 2021
@hugomrdias
Copy link
Member

Again either nodeIntegration is enabled for the renderer or you need to run libp2p in the main process.

Tip: don't use renderer target in your main webpack config.

@raphael10-collab
Copy link
Author

raphael10-collab commented Feb 1, 2021

@hugomrdias
If in the renderer /src/app/components/App.tsx I comment the MDNS part:

import Libp2p from 'libp2p';
import Websockets from 'libp2p-websockets';
import WebRTCStar from 'libp2p-webrtc-star';
import { NOISE } from 'libp2p-noise';
import Mplex from 'libp2p-mplex';
import Bootstrap from 'libp2p-bootstrap';
import KadDHT from 'libp2p-kad-dht';
//import MulticastDNS from 'libp2p-mdns';

document.addEventListener('DOMContentLoaded', async () => {
  // Create our libp2p node
  const libp2p = await Libp2p.create({
    addresses: {
      // Add the signaling server address, along with our PeerId to our multiaddrs list
      // libp2p will automatically attempt to dial to the signaling server so that it can
      // receive inbound connections from other peers
      listen: [
        '/dns4/wrtc-star1.par.dwebops.pub/tcp/443/wss/p2p-webrtc-star',
        '/dns4/wrtc-star2.sjc.dwebops.pub/tcp/443/wss/p2p-webrtc-star'
      ]
    },
    modules: {
      transport: [Websockets, WebRTCStar],
      connEncryption: [NOISE],
      streamMuxer: [Mplex],
      //peerDiscovery: [MulticastDNS]
      peerDiscovery: [Bootstrap]
    },
    config: {
      peerDiscovery: {
        // The `tag` property will be searched when creating the instance of your Peer Discovery service.
        // The associated object, will be passed to the service when it is instantiated.
        [Bootstrap.tag]: {
          enabled: true,
          list: [
            '/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN',
            '/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb',
            '/dnsaddr/bootstrap.libp2p.io/p2p/QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp',
            '/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa',
            '/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt'
          ]
        },
        //[MulticastDNS.tag]: {
          //interval: 20e3,
          //enabled: true
        //}
      },
    }
  })

  // https://github.com/libp2p/js-libp2p/blob/master/examples/libp2p-in-the-browser/index.js

  // UI elements
  const status = document.getElementById('status')
  const output = document.getElementById('output')

  output.textContent = ''

  function log (txt) {
    console.info(txt)
    output.textContent += `${txt.trim()}\n`
  }

  // Listen for new peers
  libp2p.on('peer:discovery', (peerId) => {
    log(`Found peer ${peerId.toB58String()}`)
  })

  // Listen for new connections to peers
  libp2p.connectionManager.on('peer:connect', (connection) => {
    log(`Connected to ${connection.remotePeer.toB58String()}`)
  })

  // Listen for peers disconnecting
  libp2p.connectionManager.on('peer:disconnect', (connection) => {
    log(`Disconnected from ${connection.remotePeer.toB58String()}`)
  })

  await libp2p.start();
  status.innerText = 'libp2p started!';
  log(`libp2p id is ${libp2p.peerId.toB58String()}`);

  // Export libp2p to the window so you can play with the API
  // @ts-ignore
  window.libp2p = libp2p;
})

This is the ouput:

image

where, I kept, for security reasons, nodeIntegration disabled, and following the indications of @vasco-santos , I set

the target as 'web', while running libp2p in the renderer process, :

tools/webpack/webpack.renderer.js

/* eslint-disable @typescript-eslint/no-var-requires */
const rules = require('./webpack.rules');
const plugins = require('./webpack.plugins');
const aliases = require('./webpack.aliases');

module.exports = {
    // https://github.com/electron/electron/issues/9920
    //target: 'electron-renderer',
    target: 'web',
    module: {
      rules,
    },
    plugins: plugins,
    resolve: {
      extensions: ['.js', '.ts', '.jsx', '.tsx', '.css'],
      alias: {
        // React Hot Loader Patch
        'react-dom': '@hot-loader/react-dom',
        // Custom Aliases
        ...aliases,
      },
    },
  };

and added, temporarily, in tsconfig.json : "suppressExcessPropertyErrors": true

{
  "compilerOptions": {
    "jsx": "react",
    "allowJs": true,
    "target": "ES6",
    "module": "ESNext",
    "skipLibCheck": true,
    "esModuleInterop": true,
    "noImplicitAny": false,
    "sourceMap": true,
    "baseUrl": ".",
    "outDir": "dist",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    // https://www.typescriptlang.org/tsconfig#suppressExcessPropertyErrors
    "suppressExcessPropertyErrors": true,
    "paths": {
      "*": ["node_modules/*"],
      "@app/*": ["./src/app/*"],
      "@static/*": ["./src/static/*"],
      "@src/*": ["./src/*"]
   }
 },
 "include": ["src/**/*"]
}

But, once I add MulticastDNS part I get "cannot find module dgram" error

image

@raphael10-collab
Copy link
Author

I found that a previous issue of the same kind was resolved by upgrading node and npm: #91

In my case:

node: v14.5.0
npm: 6.14.11

What do I have to do to solve the problem and use the discovery mechanism?

@vasco-santos
Copy link
Member

@raphael10-collab

did you try this?

Again either nodeIntegration is enabled for the renderer or you need to run libp2p in the main process.
Tip: don't use renderer target in your main webpack config.

@vasco-santos
Copy link
Member

@raphael10-collab could you get this running?

@raphael10-collab
Copy link
Author

@vasco-santos Sorry Vasco. I didn't try yet. Since I realized that the available boilerplates or templates for electron-react-typescript-webpack or rely on Electron-Forge, which is full of bugs, or have their own problems, I'm now building, step-by-step, my own boilerplate for electron-react-typescript-webpack. Which is easier to say that to do.
Once I will be confident enough that the framework is "fine" and stable, I will be back (hopefully soon)

@BigLep
Copy link
Contributor

BigLep commented Mar 22, 2021

@raphael10-collab: we're closing this for now, but please reoppen if the issue remains. Thanks!

@BigLep BigLep closed this as completed Mar 22, 2021
@calware
Copy link

calware commented May 28, 2024

Hello friends from years in the past. Apologies for bumping this, but I had a related issue while trying to integrate the multicast-dns package into an application of mine; wherein the dgram module could not be resolved.

@hugomrdias gave a great hint about how to resolve this issue above (sorry for the ping, bom dia my friend), but it didn't encapsulate a full solution--especially not with newer electron/electron-forge builds--so I wanted to quickly explain how I solved this below for anyone like me who may have found themselves here.

This can be fixed by enabling nodeIntegration in your corresponding BrowserWindow configuration, and disabling contextIsolation in that same configuration. For my use case these security measures didn't have a lot of merit, but in other production apps (where you may be displaying potentially unsafe content) it could be worth exploring usage of the electron utility process API for hosting the code.

Next, you might run into issues with webpack resolving the dgram module, which requires you to assign a different target to the webpack output for the renderer script file (possible target entries can be found here). For me, my webpack config file used the target electron-renderer. Really, you just want to be building these modules so as to not have a browser target, and enable node integration without context isolation wherever your script is actually executed. I believe this criteria is met by default within electron utility processes, but don't quote me on that.

Hope this helps someone :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants