Skip to content

Commit a952ae7

Browse files
fix: make port 0 bind to any available port (trufflesuite#4070)
Co-authored-by: Micaiah Reid <micaiahreid@gmail.com>
1 parent 7ee7d4b commit a952ae7

File tree

17 files changed

+137
-94
lines changed

17 files changed

+137
-94
lines changed

.github/workflows/pr.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343

4444
- name: Check bundle size
4545
# this should match the os and version used in the release.yml
46-
if: startsWith(matrix.os, 'ubuntu-22.04') && startsWith(matrix.node, '14.')
46+
if: startsWith(matrix.os, 'ubuntu-22.04') && startsWith(matrix.node, '14.') && matrix.node != '14.0.0'
4747
# 1. build ganache
4848
# 2. pack it into a tarball
4949
# 3. measure the _unpacked_ tarball's size
@@ -65,7 +65,7 @@ jobs:
6565
INFURA_KEY: "badc0de0deadc0debadc0de0deadc0de"
6666
- name: Upload artifact
6767
# this should match the os and version used in the release.yml
68-
if: startsWith(matrix.os, 'ubuntu-22.04') && startsWith(matrix.node, '14.')
68+
if: startsWith(matrix.os, 'ubuntu-22.04') && startsWith(matrix.node, '14.') && matrix.node != '14.0.0'
6969
uses: actions/upload-artifact@v3
7070
with:
7171
name: Candidate

src/chains/ethereum/ethereum/package-lock.json

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/chains/ethereum/ethereum/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
"ws": "8.2.3"
8888
},
8989
"devDependencies": {
90-
"@trufflesuite/uws-js-unofficial": "20.10.0-unofficial.1",
90+
"@trufflesuite/uws-js-unofficial": "20.10.0-unofficial.2",
9191
"@types/encoding-down": "5.0.0",
9292
"@types/fs-extra": "9.0.2",
9393
"@types/keccak": "3.0.1",

src/chains/filecoin/filecoin/package-lock.json

+3-21
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/chains/filecoin/filecoin/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
"@filecoin-shipyard/lotus-client-schema": "2.0.0",
6060
"@ganache/filecoin-options": "0.7.0",
6161
"@ganache/utils": "0.7.0",
62-
"@trufflesuite/uws-js-unofficial": "20.10.0-unofficial.1",
62+
"@trufflesuite/uws-js-unofficial": "20.10.0-unofficial.2",
6363
"@types/abstract-leveldown": "7.2.0",
6464
"@types/bn.js": "5.1.0",
6565
"@types/deep-equal": "1.0.1",

src/chains/tezos/tezos/package-lock.json

+6-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/chains/tezos/tezos/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"emittery": "0.10.0"
4848
},
4949
"devDependencies": {
50-
"@trufflesuite/uws-js-unofficial": "20.10.0-unofficial.1",
50+
"@trufflesuite/uws-js-unofficial": "20.10.0-unofficial.2",
5151
"@types/mocha": "9.0.0",
5252
"cheerio": "1.0.0-rc.3",
5353
"cross-env": "7.0.3",

src/packages/cli/src/args.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ export default function (
218218
})
219219
.check(argv => {
220220
const { "server.port": port, "server.host": host } = argv;
221-
if (port < 1 || port > 65535) {
221+
if (port < 0 || port > 65535) {
222222
throw new Error(`Invalid port number '${port}'`);
223223
}
224224

src/packages/cli/src/cli.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -138,17 +138,18 @@ if (argv.action === "start") {
138138
return;
139139
}
140140
started = true;
141+
const { address: host, port } = server.address();
141142
switch (flavor) {
142143
case FilecoinFlavorName: {
143-
await initializeFilecoin(
144-
server.provider as FilecoinProvider,
145-
cliSettings
146-
);
144+
await initializeFilecoin(server.provider as FilecoinProvider, {
145+
host,
146+
port
147+
});
147148
break;
148149
}
149150
case EthereumFlavorName:
150151
default: {
151-
initializeEthereum(server.provider as EthereumProvider, cliSettings);
152+
initializeEthereum(server.provider as EthereumProvider, { host, port });
152153
break;
153154
}
154155
}
@@ -157,7 +158,7 @@ if (argv.action === "start") {
157158
// instance), so we need to notify that we are ready.
158159
const isDetachedInstance = process.send !== undefined;
159160
if (isDetachedInstance) {
160-
notifyDetachedInstanceReady();
161+
notifyDetachedInstanceReady(port);
161162
}
162163
});
163164
} else if (argv.action === "stop") {

src/packages/cli/src/detach.ts

+11-10
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ export type DetachedInstance = {
2020
};
2121

2222
const FILE_ENCODING = "utf8";
23-
const READY_MESSAGE = "ready";
2423
const START_ERROR =
2524
"An error occurred spawning a detached instance of Ganache:";
2625
const dataPath = envPaths(`Ganache/instances`, { suffix: "" }).data;
@@ -32,10 +31,10 @@ function getInstanceFilePath(instanceName: string): string {
3231
/**
3332
* Notify that the detached instance has started and is ready to receive requests.
3433
*/
35-
export function notifyDetachedInstanceReady() {
36-
// in "detach" mode, the parent will wait until the "ready" message is
34+
export function notifyDetachedInstanceReady(port: number) {
35+
// in "detach" mode, the parent will wait until the port is
3736
// received before disconnecting from the child process.
38-
process.send(READY_MESSAGE);
37+
process.send(port);
3938
}
4039

4140
/**
@@ -113,11 +112,13 @@ export async function startDetachedInstance(
113112
// event is emitted) will be streamed to stderr on the parent.
114113
child.stderr.pipe(process.stderr);
115114

116-
await new Promise<void>((resolve, reject) => {
117-
child.on("message", message => {
118-
if (message === READY_MESSAGE) {
119-
resolve();
120-
}
115+
// Wait for the child process to send its port, which indicates that the
116+
// Ganache server has started and is ready to receive RPC requests. It signals
117+
// by sending the port number to which it was bound back to us; this is needed
118+
// because Ganache may bind to a random port if the user specified port 0.
119+
const port = await new Promise<number>((resolve, reject) => {
120+
child.on("message", port => {
121+
resolve(port as number);
121122
});
122123

123124
child.on("error", err => {
@@ -146,7 +147,7 @@ export async function startDetachedInstance(
146147
child.disconnect();
147148

148149
const flavor = instanceInfo.flavor;
149-
const { host, port } = instanceInfo.server;
150+
const { host } = instanceInfo.server;
150151
const cmd =
151152
process.platform === "win32"
152153
? path.basename(process.execPath)

src/packages/core/package-lock.json

+6-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/packages/core/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
"@ganache/options": "0.7.0",
5454
"@ganache/tezos": "0.7.0",
5555
"@ganache/utils": "0.7.0",
56-
"@trufflesuite/uws-js-unofficial": "20.10.0-unofficial.1",
56+
"@trufflesuite/uws-js-unofficial": "20.10.0-unofficial.2",
5757
"aggregate-error": "3.1.0",
5858
"emittery": "0.10.0"
5959
},

src/packages/core/src/server.ts

+31-9
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type {
1313
import {
1414
App,
1515
us_listen_socket_close,
16+
us_socket_local_port,
1617
_cfg as setUwsGlobalConfig
1718
} from "@trufflesuite/uws-js-unofficial";
1819

@@ -33,6 +34,13 @@ import WebsocketServer, { WebSocketCapableFlavor } from "./servers/ws-server";
3334
import HttpServer from "./servers/http-server";
3435
import Emittery from "emittery";
3536

37+
// not using the "net" node package in order to avoid having to polyfill this
38+
// for the browser build.
39+
// isIPv4 taken from https://github.com/nodejs/node/blob/01323d50c4b24cf730a651d06ba20633905ecbed/lib/internal/net.js#L31
40+
const v4Seg = "(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])";
41+
const IPv4Reg = new RegExp(`^(${v4Seg}[.]){3}${v4Seg}$`);
42+
const isIPv4 = (s: string) => IPv4Reg.test(s);
43+
3644
export type Provider = Connector["provider"];
3745

3846
const DEFAULT_HOST = "127.0.0.1";
@@ -43,13 +51,13 @@ export type Callback = (err: Error | null) => void;
4351
* Server ready state constants.
4452
*
4553
* These are bit flags. This means that you can check if the status is:
46-
* * ready: `status === Status.ready` or `status & Status.ready !== 0`
47-
* * opening: `status === Status.opening` or `status & Status.opening !== 0`
48-
* * open: `status === Status.open` or `status & Status.open !== 0`
49-
* * opening || open: `status & Status.openingOrOpen !== 0` or `status & (Status.opening | Status.open) !== 0`
50-
* * closing: `status === Status.closing` or `status & Status.closing !== 0`
51-
* * closed: `status === Status.closed` or `status & Status.closed !== 0`
52-
* * closing || closed: `status & Status.closingOrClosed !== 0` or `status & (Status.closing | Status.closed) !== 0`
54+
* * ready: `status === ServerStatus.ready` or `status & ServerStatus.ready !== 0`
55+
* * opening: `status === ServerStatus.opening` or `status & ServerStatus.opening !== 0`
56+
* * open: `status === ServerStatus.open` or `status & ServerStatus.open !== 0`
57+
* * opening || open: `status & ServerStatus.openingOrOpen !== 0` or `status & (ServerStatus.opening | ServerStatus.open) !== 0`
58+
* * closing: `status === ServerStatus.closing` or `status & ServerStatus.closing !== 0`
59+
* * closed: `status === ServerStatus.closed` or `status & ServerStatus.closed !== 0`
60+
* * closing || closed: `status & ServerStatus.closingOrClosed !== 0` or `status & (ServerStatus.closing | ServerStatus.closed) !== 0`
5361
*/
5462
export enum ServerStatus {
5563
/**
@@ -106,6 +114,7 @@ export class Server<
106114
#app: TemplatedApp | null = null;
107115
#httpServer: HttpServer | null = null;
108116
#listenSocket: us_listen_socket | null = null;
117+
#host: string | null = null;
109118
#connector: ConnectorsByName[Flavor];
110119
#websocketServer: WebsocketServer | null = null;
111120

@@ -183,8 +192,7 @@ export class Server<
183192
(typeof port !== "number" && typeof port !== "string") ||
184193
(typeof port === "string" && (<string>port).trim().length === 0) ||
185194
+port !== +port >>> 0 ||
186-
port > 0xffff ||
187-
port === 0
195+
port > 0xffff
188196
) {
189197
const err = new Error(
190198
`Port should be >= 0 and < 65536. Received ${port}.`
@@ -239,6 +247,7 @@ export class Server<
239247
if (listenSocket) {
240248
this.#status = ServerStatus.open;
241249
this.#listenSocket = listenSocket;
250+
this.#host = (host as string) || DEFAULT_HOST;
242251
} else {
243252
this.#status = ServerStatus.closed;
244253
const err = new Error(
@@ -285,6 +294,19 @@ export class Server<
285294
}
286295
}
287296

297+
public address() {
298+
if (this.#listenSocket) {
299+
const address = this.#host;
300+
return {
301+
address,
302+
family: isIPv4(address) ? "IPv4" : "IPv6",
303+
port: us_socket_local_port(this.#listenSocket)
304+
};
305+
} else {
306+
return null;
307+
}
308+
}
309+
288310
public async close() {
289311
if (this.#status === ServerStatus.opening) {
290312
// if opening

0 commit comments

Comments
 (0)