diff --git a/packages/client/README.md b/packages/client/README.md index 6ae2ac453da..d04d21a5b01 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -337,6 +337,61 @@ to help contributors better understand how the project is organized. ## Developer +### Debugging + +#### Networking + +##### Local Connection: EthereumJS <- EthereumJS + +For debugging on networking issues there are two custom npm start scripts with appropriate settings. + +Start a first client listening on the default port and using the default data directory with: + +```shell +DEBUG=devp2p:* npm run client:start:dev1 +DEBUG=devp2p:* npm run client:start:dev1 -- --datadir=datadir-dev1 +``` + +Then take the enode address from the started client instance (use `127.0.0.1` for the IP address) and start a second client with: + +```shell +DEBUG=devp2p:* npm run client:start:dev2 -- --bootnodes=enode://[DEV1_NODE_ID]@127.0.0.1:30303 +``` + +This second client is using './datadir-dev2' for its data directory. + +##### Local Connection: EthereumJS <- Geth + +To connect Geth to a running EthereumJS instance start a client with: + +```shell +DEBUG=devp2p:* npm run client:start:dev1 +``` + +Then connect with your Geth instance via: + +```shell +geth --maxpeers=1 --bootnodes=enode://[DEV1_NODE_ID]@127.0.0.1:30303 +``` + +Depending on your use case you might want to turn off your internet connection to not allow further incoming connections on your Geth instance in case the EthereumJS connection gets disconnected. + +##### Local Connection: Geth <- EthereumJS + +Start your Geth instance: + +```shell +geth [--syncmode=full] [--verbosity=5] +``` + +Note that you might want to turn off your internet connection to limit on the Geth discovery process (setting the `--nodiscover` flag won't work since this also disallows our local client from connecting). + +Then connect with your EthereumJS instance via: + +```shell +DEBUG=devp2p:* npm run client:start:dev2 -- --bootnodes=enode://[GETH_NODE_ID]@127.0.0.1:30303 +``` + ### Diagram Updates To update the structure diagram files in the root folder open the `client.drawio` file in [draw.io](https://draw.io/), make your changes, and open a PR with the updated files. Export `svg` and `png` with `border` `width=20` and `transparency=false`. For `png` go to "Advanced" and select `300 DPI`. diff --git a/packages/client/bin/cli.ts b/packages/client/bin/cli.ts index a9da2978d50..d9e59ea273f 100755 --- a/packages/client/bin/cli.ts +++ b/packages/client/bin/cli.ts @@ -50,6 +50,10 @@ const args = require('yargs') describe: 'Network bootnodes', array: true, }, + port: { + describe: 'RLPx listening port', + default: Config.PORT_DEFAULT, + }, multiaddrs: { describe: 'Network multiaddrs', array: true, @@ -176,6 +180,8 @@ async function run() { const common = new Common({ chain, hardfork: 'chainstart' }) const datadir = args.datadir ?? Config.DATADIR_DEFAULT + const configDirectory = `${datadir}/${common.chainName()}/config` + fs.ensureDirSync(configDirectory) const key = await Config.getClientKey(datadir, common) const config = new Config({ common, @@ -185,6 +191,7 @@ async function run() { key, transports: args.transports, bootnodes: args.bootnodes ? parseMultiaddrs(args.bootnodes) : undefined, + port: args.port, multiaddrs: args.multiaddrs ? parseMultiaddrs(args.multiaddrs) : undefined, rpc: args.rpc, rpcport: args.rpcport, diff --git a/packages/client/lib/config.ts b/packages/client/lib/config.ts index 8760480512e..234f6173d02 100644 --- a/packages/client/lib/config.ts +++ b/packages/client/lib/config.ts @@ -53,7 +53,7 @@ export interface ConfigOptions { /** * Network transports ('rlpx' and/or 'libp2p') * - * Default: `['rlpx:port=30303', 'libp2p']` + * Default: `['rlpx', 'libp2p']` */ transports?: string[] @@ -63,6 +63,13 @@ export interface ConfigOptions { */ bootnodes?: Multiaddr[] + /** + * RLPx listening port + * + * Default: `30303` + */ + port?: number + /** * Network multiaddrs for libp2p * (e.g. /ip4/127.0.0.1/tcp/50505/p2p/QmABC) @@ -167,7 +174,8 @@ export class Config { public static readonly SYNCMODE_DEFAULT = 'full' public static readonly LIGHTSERV_DEFAULT = false public static readonly DATADIR_DEFAULT = `./datadir` - public static readonly TRANSPORTS_DEFAULT = ['rlpx:port=30303', 'libp2p'] + public static readonly TRANSPORTS_DEFAULT = ['rlpx', 'libp2p'] + public static readonly PORT_DEFAULT = 30303 public static readonly RPC_DEFAULT = false public static readonly RPCPORT_DEFAULT = 8545 public static readonly RPCADDR_DEFAULT = 'localhost' @@ -185,6 +193,7 @@ export class Config { public readonly key: Buffer public readonly transports: string[] public readonly bootnodes?: Multiaddr[] + public readonly port?: number public readonly multiaddrs?: Multiaddr[] public readonly rpc: boolean public readonly rpcport: number @@ -208,6 +217,7 @@ export class Config { this.lightserv = options.lightserv ?? Config.LIGHTSERV_DEFAULT this.transports = options.transports ?? Config.TRANSPORTS_DEFAULT this.bootnodes = options.bootnodes + this.port = options.port ?? Config.PORT_DEFAULT this.multiaddrs = options.multiaddrs this.datadir = options.datadir ?? Config.DATADIR_DEFAULT this.key = options.key ?? genPrivateKey() diff --git a/packages/client/lib/net/peerpool.ts b/packages/client/lib/net/peerpool.ts index e01a69c137c..6536779f911 100644 --- a/packages/client/lib/net/peerpool.ts +++ b/packages/client/lib/net/peerpool.ts @@ -204,7 +204,7 @@ export class PeerPool extends EventEmitter { this.noPeerPeriods += 1 if (this.noPeerPeriods >= 3) { const promises = this.config.servers.map(async (server) => { - if (server instanceof RlpxServer) { + if (server instanceof RlpxServer && server.discovery) { this.config.logger.info('Restarting RLPx server: bootstrap') await server.stop() await server.start() @@ -215,11 +215,11 @@ export class PeerPool extends EventEmitter { } else { let tablesize: number | undefined = 0 this.config.servers.forEach((server) => { - if (server instanceof RlpxServer) { + if (server instanceof RlpxServer && server.discovery) { tablesize = server.dpt?.getPeers().length + this.config.logger.info(`Looking for suited peers: peertablesize=${tablesize}`) } }) - this.config.logger.info(`Looking for suited peers: peertablesize=${tablesize}`) } } else { this.noPeerPeriods = 0 diff --git a/packages/client/lib/net/server/rlpxserver.ts b/packages/client/lib/net/server/rlpxserver.ts index 5ee2adad7ea..c5bf5ac2f85 100644 --- a/packages/client/lib/net/server/rlpxserver.ts +++ b/packages/client/lib/net/server/rlpxserver.ts @@ -3,9 +3,6 @@ import { RlpxPeer } from '../peer/rlpxpeer' import { Server, ServerOptions } from './server' export interface RlpxServerOptions extends ServerOptions { - /* Local port to listen on (default: 30303) */ - port?: number - /* List of supported clients */ clientFilter?: string[] } @@ -49,7 +46,7 @@ const ignoredErrors = new RegExp( export class RlpxServer extends Server { private peers: Map = new Map() - public port: number + public discovery: boolean private clientFilter: string[] public rlpx: Devp2pRLPx | null = null @@ -65,7 +62,7 @@ export class RlpxServer extends Server { // TODO: get the external ip from the upnp service this.ip = '::' - this.port = options.port ?? 30303 + this.discovery = options.config.discV4 || options.config.discDns this.clientFilter = options.clientFilter ?? [ 'go1.5', 'go1.6', @@ -97,17 +94,17 @@ export class RlpxServer extends Server { enode: undefined, id: undefined, ip: this.ip, - listenAddr: `[${this.ip}]:${this.port}`, - ports: { discovery: this.port, listener: this.port }, + listenAddr: `[${this.ip}]:${this.config.port}`, + ports: { discovery: this.config.port, listener: this.config.port }, } } const id = this.rlpx._id.toString('hex') return { - enode: `enode://${id}@[${this.ip}]:${this.port}`, + enode: `enode://${id}@[${this.ip}]:${this.config.port}`, id: id, ip: this.ip, - listenAddr: `[${this.ip}]:${this.port}`, - ports: { discovery: this.port, listener: this.port }, + listenAddr: `[${this.ip}]:${this.config.port}`, + ports: { discovery: this.config.port, listener: this.config.port }, } } @@ -230,8 +227,8 @@ export class RlpxServer extends Server { this.dpt.on('error', (e: Error) => this.error(e)) - if (this.port) { - this.dpt.bind(this.port, '0.0.0.0') + if (this.config.port) { + this.dpt.bind(this.config.port, '0.0.0.0') } } @@ -245,7 +242,7 @@ export class RlpxServer extends Server { maxPeers: this.config.maxPeers, capabilities: RlpxPeer.capabilities(Array.from(this.protocols)), remoteClientIdFilter: this.clientFilter, - listenPort: this.port, + listenPort: this.config.port, common: this.config.chainCommon, }) @@ -301,8 +298,8 @@ export class RlpxServer extends Server { }) }) - if (this.port) { - this.rlpx.listen(this.port, '0.0.0.0') + if (this.config.port) { + this.rlpx.listen(this.config.port, '0.0.0.0') } } } diff --git a/packages/client/package.json b/packages/client/package.json index 5cfa3fdd659..2142790c5a8 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -19,6 +19,8 @@ "prepublishOnly": "npm run build && npm run test", "bundle": "webpack", "client:start": "ts-node bin/cli.ts", + "client:start:dev1": "npm run client:start -- --discDns=false --discV4=false --bootnodes", + "client:start:dev2": "npm run client:start -- --discDns=false --discV4=false --transports=rlpx --port=30304 --datadir=datadir-dev2", "coverage": "nyc npm run test && nyc report --reporter=lcov", "docs:build": "typedoc --tsconfig tsconfig.prod.json", "lint": "ethereumjs-config-lint", diff --git a/packages/client/test/net/server/rlpxserver.spec.ts b/packages/client/test/net/server/rlpxserver.spec.ts index 3d787790eb1..2d476ea7bae 100644 --- a/packages/client/test/net/server/rlpxserver.spec.ts +++ b/packages/client/test/net/server/rlpxserver.spec.ts @@ -179,7 +179,7 @@ tape('[RlpxServer]', async (t) => { const config = new Config({ loglevel: 'error', transports: [] }) const server = new RlpxServer({ config }) server.initDpt() - td.verify((server.dpt as any).bind(server.port, '0.0.0.0')) + td.verify((server.dpt as any).bind(server.config.port, '0.0.0.0')) server.on('error', (err: any) => t.equals(err, 'err0', 'got error')) ;(server.dpt as any).emit('error', 'err0') }) @@ -193,7 +193,7 @@ tape('[RlpxServer]', async (t) => { td.when(RlpxPeer.prototype.accept(rlpxPeer, td.matchers.isA(RlpxServer))).thenResolve() server.initRlpx() td.verify(RlpxPeer.capabilities(Array.from((server as any).protocols))) - td.verify(server.rlpx!.listen(server.port, '0.0.0.0')) + td.verify(server.rlpx!.listen(server.config.port, '0.0.0.0')) server.on('connected', (peer: any) => t.ok(peer instanceof RlpxPeer, 'connected')) server.on('disconnected', (peer: any) => t.equals(peer.id, '01', 'disconnected')) server.on('error', (err: Error) => t.equals(err.message, 'err0', 'got error'))