Skip to content

Commit 04d5cdb

Browse files
authored
adds default multisig broker server (#5520)
* adds default multisig broker server changes '--server' to boolean flag in 'wallet:multisig:sign' and 'wallet:multisig:dkg:create' adds '--hostname' and '--port' flags in place of previous '--server' flag to supply server hostname and port defaults 'hostname' to 'multisig.ironfish.network' and 'port' to 9035 adds support for connection strings with '--connection' flag adds util function to parse all connection options from flags prints connection string to console after starting session * allows sessionId and passphrase flags without server flag if a users sets the sessionId or passphrase flag in 'multisig:sign' or 'dkg:create', the commands will now interpret these flags as intent to use a broker server
1 parent 2176e9c commit 04d5cdb

File tree

6 files changed

+144
-61
lines changed

6 files changed

+144
-61
lines changed

ironfish-cli/src/commands/wallet/multisig/dkg/create.ts

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,26 @@ export class DkgCreateCommand extends IronfishCommand {
4444
description:
4545
"Block sequence to begin scanning from for the created account. Uses node's chain head by default",
4646
}),
47-
server: Flags.string({
48-
description: "multisig server to connect to. formatted as '<host>:<port>'",
47+
server: Flags.boolean({
48+
description: 'connect to a multisig broker server',
49+
}),
50+
connection: Flags.string({
51+
char: 'c',
52+
description: 'connection string for a multisig server session',
53+
}),
54+
hostname: Flags.string({
55+
description: 'hostname of the multisig broker server to connect to',
56+
default: 'multisig.ironfish.network',
57+
}),
58+
port: Flags.integer({
59+
description: 'port to connect to on the multisig broker server',
60+
default: 9035,
4961
}),
5062
sessionId: Flags.string({
5163
description: 'Unique ID for a multisig server session to join',
52-
dependsOn: ['server'],
5364
}),
5465
passphrase: Flags.string({
5566
description: 'Passphrase to join the multisig server session',
56-
dependsOn: ['server'],
5767
}),
5868
tls: Flags.boolean({
5969
description: 'connect to the multisig server over TLS',
@@ -90,21 +100,18 @@ export class DkgCreateCommand extends IronfishCommand {
90100
}
91101

92102
let multisigClient: MultisigClient | null = null
93-
if (flags.server) {
94-
let sessionId = flags.sessionId
95-
if (!sessionId && !flags.totalParticipants && !flags.minSigners) {
96-
sessionId = await ui.inputPrompt(
97-
'Enter the ID of a multisig session to join, or press enter to start a new session',
98-
false,
99-
)
100-
}
101-
102-
let passphrase = flags.passphrase
103-
if (!passphrase) {
104-
passphrase = await ui.inputPrompt('Enter the passphrase for the multisig session', true)
105-
}
106-
107-
multisigClient = await MultisigBrokerUtils.createClient(flags.server, {
103+
if (flags.server || flags.connection || flags.sessionId || flags.passphrase) {
104+
const { hostname, port, sessionId, passphrase } =
105+
await MultisigBrokerUtils.parseConnectionOptions({
106+
connection: flags.connection,
107+
hostname: flags.hostname,
108+
port: flags.port,
109+
sessionId: flags.sessionId,
110+
passphrase: flags.passphrase,
111+
logger: this.logger,
112+
})
113+
114+
multisigClient = MultisigBrokerUtils.createClient(hostname, port, {
108115
passphrase,
109116
tls: flags.tls ?? true,
110117
logger: this.logger,
@@ -380,6 +387,8 @@ export class DkgCreateCommand extends IronfishCommand {
380387
multisigClient.startDkgSession(totalParticipants, minSigners)
381388
this.log('\nStarted new DKG session:')
382389
this.log(`${multisigClient.sessionId}`)
390+
this.log('\nDKG session connection string:')
391+
this.log(`${multisigClient.connectionString}`)
383392
}
384393

385394
return { totalParticipants, minSigners }

ironfish-cli/src/commands/wallet/multisig/sign.ts

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,26 @@ export class SignMultisigTransactionCommand extends IronfishCommand {
4646
default: false,
4747
description: 'Perform operation with a ledger device',
4848
}),
49-
server: Flags.string({
50-
description: "multisig server to connect to. formatted as '<host>:<port>'",
49+
server: Flags.boolean({
50+
description: 'connect to a multisig broker server',
51+
}),
52+
connection: Flags.string({
53+
char: 'c',
54+
description: 'connection string for a multisig server session',
55+
}),
56+
hostname: Flags.string({
57+
description: 'hostname of the multisig broker server to connect to',
58+
default: 'multisig.ironfish.network',
59+
}),
60+
port: Flags.integer({
61+
description: 'port to connect to on the multisig broker server',
62+
default: 9035,
5163
}),
5264
sessionId: Flags.string({
5365
description: 'Unique ID for a multisig server session to join',
54-
dependsOn: ['server'],
5566
}),
5667
passphrase: Flags.string({
5768
description: 'Passphrase to join the multisig server session',
58-
dependsOn: ['server'],
5969
}),
6070
tls: Flags.boolean({
6171
description: 'connect to the multisig server over TLS',
@@ -114,21 +124,18 @@ export class SignMultisigTransactionCommand extends IronfishCommand {
114124
}
115125

116126
let multisigClient: MultisigClient | null = null
117-
if (flags.server) {
118-
let sessionId = flags.sessionId
119-
if (!sessionId) {
120-
sessionId = await ui.inputPrompt(
121-
'Enter the ID of a multisig session to join, or press enter to start a new session',
122-
false,
123-
)
124-
}
125-
126-
let passphrase = flags.passphrase
127-
if (!passphrase) {
128-
passphrase = await ui.inputPrompt('Enter the passphrase for the multisig session', true)
129-
}
127+
if (flags.server || flags.connection || flags.sessionId || flags.passphrase) {
128+
const { hostname, port, sessionId, passphrase } =
129+
await MultisigBrokerUtils.parseConnectionOptions({
130+
connection: flags.connection,
131+
hostname: flags.hostname,
132+
port: flags.port,
133+
sessionId: flags.sessionId,
134+
passphrase: flags.passphrase,
135+
logger: this.logger,
136+
})
130137

131-
multisigClient = await MultisigBrokerUtils.createClient(flags.server, {
138+
multisigClient = MultisigBrokerUtils.createClient(hostname, port, {
132139
passphrase,
133140
tls: flags.tls ?? true,
134141
logger: this.logger,
@@ -267,6 +274,8 @@ export class SignMultisigTransactionCommand extends IronfishCommand {
267274
multisigClient.startSigningSession(totalParticipants, unsignedTransactionInput)
268275
this.log('\nStarted new signing session:')
269276
this.log(`${multisigClient.sessionId}`)
277+
this.log('\nSigning session connection string:')
278+
this.log(`${multisigClient.connectionString}`)
270279
}
271280

272281
return { unsignedTransaction, totalParticipants }

ironfish-cli/src/multisigBroker/clients/client.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ const RETRY_INTERVAL = 5000
4242
export abstract class MultisigClient {
4343
readonly logger: Logger
4444
readonly version: number
45+
readonly hostname: string
46+
readonly port: number
4547

4648
private started: boolean
4749
private isClosing = false
@@ -65,9 +67,11 @@ export abstract class MultisigClient {
6567

6668
retries: Map<number, NodeJS.Timer> = new Map()
6769

68-
constructor(options: { passphrase: string; logger: Logger }) {
70+
constructor(options: { hostname: string; port: number; passphrase: string; logger: Logger }) {
6971
this.logger = options.logger
7072
this.version = 3
73+
this.hostname = options.hostname
74+
this.port = options.port
7175

7276
this.started = false
7377
this.nextMessageId = 0
@@ -78,6 +82,10 @@ export abstract class MultisigClient {
7882
this.passphrase = options.passphrase
7983
}
8084

85+
get connectionString(): string {
86+
return `tcp://${this.sessionId}:${this.passphrase}@${this.hostname}:${this.port}`
87+
}
88+
8189
get key(): xchacha20poly1305.XChaCha20Poly1305Key {
8290
if (!this.sessionId) {
8391
throw new Error('Client must join a session before encrypting/decrypting messages')

ironfish-cli/src/multisigBroker/clients/tcpClient.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ import net from 'net'
66
import { MultisigClient } from './client'
77

88
export class MultisigTcpClient extends MultisigClient {
9-
readonly host: string
10-
readonly port: number
11-
129
client: net.Socket | null = null
1310

14-
constructor(options: { host: string; port: number; passphrase: string; logger: Logger }) {
15-
super({ passphrase: options.passphrase, logger: options.logger })
16-
this.host = options.host
17-
this.port = options.port
11+
constructor(options: { hostname: string; port: number; passphrase: string; logger: Logger }) {
12+
super({
13+
hostname: options.hostname,
14+
port: options.port,
15+
passphrase: options.passphrase,
16+
logger: options.logger,
17+
})
1818
}
1919

2020
protected onSocketDisconnect = (): void => {
@@ -50,7 +50,7 @@ export class MultisigTcpClient extends MultisigClient {
5050
client.on('error', onError)
5151
client.on('connect', onConnect)
5252
client.on('data', this.onSocketData)
53-
client.connect({ host: this.host, port: this.port })
53+
client.connect({ host: this.hostname, port: this.port })
5454
this.client = client
5555
})
5656
}

ironfish-cli/src/multisigBroker/clients/tlsClient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export class MultisigTlsClient extends MultisigTcpClient {
2424
}
2525

2626
const client = tls.connect({
27-
host: this.host,
27+
host: this.hostname,
2828
port: this.port,
2929
rejectUnauthorized: false,
3030
})
Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,89 @@
11
/* This Source Code Form is subject to the terms of the Mozilla Public
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4-
import { Assert, Logger, parseUrl } from '@ironfish/sdk'
5-
import dns from 'dns'
4+
import { ErrorUtils, Logger } from '@ironfish/sdk'
5+
import * as ui from '../ui'
66
import { MultisigClient, MultisigTcpClient, MultisigTlsClient } from './clients'
77

8-
async function createClient(
9-
serverAddress: string,
10-
options: { passphrase: string; tls: boolean; logger: Logger },
11-
): Promise<MultisigClient> {
12-
const parsed = parseUrl(serverAddress)
8+
async function parseConnectionOptions(options: {
9+
connection?: string
10+
hostname: string
11+
port: number
12+
sessionId?: string
13+
passphrase?: string
14+
logger: Logger
15+
}): Promise<{
16+
hostname: string
17+
port: number
18+
sessionId: string
19+
passphrase: string
20+
}> {
21+
let hostname
22+
let port
23+
let sessionId
24+
let passphrase
25+
if (options.connection) {
26+
try {
27+
const url = new URL(options.connection)
28+
if (url.host) {
29+
hostname = url.hostname
30+
}
31+
if (url.port) {
32+
port = Number(url.port)
33+
}
34+
if (url.username) {
35+
sessionId = url.username
36+
}
37+
if (url.password) {
38+
passphrase = url.password
39+
}
40+
} catch (e) {
41+
if (e instanceof TypeError && e.message.includes('Invalid URL')) {
42+
options.logger.error(ErrorUtils.renderError(e))
43+
}
44+
throw e
45+
}
46+
}
1347

14-
Assert.isNotNull(parsed.hostname)
15-
Assert.isNotNull(parsed.port)
48+
hostname = hostname ?? options.hostname
49+
port = port ?? options.port
1650

17-
const resolved = await dns.promises.lookup(parsed.hostname)
18-
const host = resolved.address
19-
const port = parsed.port
51+
sessionId = sessionId ?? options.sessionId
52+
if (!sessionId) {
53+
sessionId = await ui.inputPrompt(
54+
'Enter the ID of a multisig session to join, or press enter to start a new session',
55+
false,
56+
)
57+
}
58+
59+
passphrase = passphrase ?? options.passphrase
60+
if (!passphrase) {
61+
passphrase = await ui.inputPrompt('Enter the passphrase for the multisig session', true)
62+
}
2063

64+
return {
65+
hostname,
66+
port,
67+
sessionId,
68+
passphrase,
69+
}
70+
}
71+
72+
function createClient(
73+
hostname: string,
74+
port: number,
75+
options: { passphrase: string; tls: boolean; logger: Logger },
76+
): MultisigClient {
2177
if (options.tls) {
2278
return new MultisigTlsClient({
23-
host,
79+
hostname,
2480
port,
2581
passphrase: options.passphrase,
2682
logger: options.logger,
2783
})
2884
} else {
2985
return new MultisigTcpClient({
30-
host,
86+
hostname,
3187
port,
3288
passphrase: options.passphrase,
3389
logger: options.logger,
@@ -36,5 +92,6 @@ async function createClient(
3692
}
3793

3894
export const MultisigBrokerUtils = {
95+
parseConnectionOptions,
3996
createClient,
4097
}

0 commit comments

Comments
 (0)