From b8c539d617f9957da43dc145bbda39c46bb789f8 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 10 Mar 2022 12:40:18 +0100 Subject: [PATCH 1/7] fix: minor changes for staging/prod deployments --- cli/lib/environment.mjs | 8 +++ cli/lib/nf3.mjs | 37 +++++++------ cli/src/proposer.mjs | 4 +- .../src/event-handlers/subscribe.mjs | 54 ++++++++++++++----- .../src/services/block-assembler.mjs | 7 ++- 5 files changed, 75 insertions(+), 35 deletions(-) diff --git a/cli/lib/environment.mjs b/cli/lib/environment.mjs index be42c5dbb..00b91c247 100644 --- a/cli/lib/environment.mjs +++ b/cli/lib/environment.mjs @@ -16,6 +16,14 @@ const SUPPORTED_ENVIRONMENTS = { optimistWsUrl: 'wss://optimist1-ws.testnet.nightfall3.com', web3WsUrl: 'wss://ropsten1-ws.testnet.nightfall3.com', }, + staging: { + name: 'Staging', + chainId: 4378921, + clientApiUrl: 'https://client1.testnet.nightfall3.com', + optimistApiUrl: 'https://optimist-api.ropsten.nightfall3.com', + optimistWsUrl: 'wss://optimist-ws.ropsten.nightfall3.com', + web3WsUrl: 'wss://ganache-ws.ropsten.nightfall3.com', + }, rinkeby: { name: 'Rinkeby', chainId: 4, diff --git a/cli/lib/nf3.mjs b/cli/lib/nf3.mjs index ed4001726..2869e680d 100644 --- a/cli/lib/nf3.mjs +++ b/cli/lib/nf3.mjs @@ -8,7 +8,6 @@ import { approve } from './tokens.mjs'; import erc20 from './abis/ERC20.mjs'; import erc721 from './abis/ERC721.mjs'; import erc1155 from './abis/ERC1155.mjs'; - import { DEFAULT_BLOCK_STAKE, DEFAULT_PROPOSER_BOND, DEFAULT_FEE } from './constants.mjs'; /** @@ -221,17 +220,18 @@ class Nf3 { // then return the receipt. // TODO does this still work if there is a chain reorg or do we have to handle that? return new Promise((resolve, reject) => { - logger.debug(`Confirming transaction ${signed.transactionHash}`); + if (process.env.VERBOSE) console.log(`Confirming transaction ${signed.transactionHash}`); this.notConfirmed++; this.web3.eth .sendSignedTransaction(signed.rawTransaction) .on('confirmation', (number, receipt) => { if (number === 12) { this.notConfirmed--; - logger.debug( - `Transaction ${receipt.transactionHash} has been confirmed ${number} times.`, - `Number of unconfirmed transactions is ${this.notConfirmed}`, - ); + if (process.env.VERBOSE) + console.log( + `Transaction ${receipt.transactionHash} has been confirmed ${number} times.`, + `Number of unconfirmed transactions is ${this.notConfirmed}`, + ); resolve(receipt); } }) @@ -614,17 +614,6 @@ class Nf3 { return this.submitTransaction(res.data.txDataToSign, this.proposersContractAddress, 0); } - /** - Get current proposer - @method - @async - @returns {array} A promise that resolves to the Ethereum transaction receipt. - */ - async getCurrentProposer() { - const res = await axios.get(`${this.optimistBaseUrl}/proposer/current-proposer`); - return res.data.currentProposer; - } - /** Get all the list of existing proposers. @method @@ -666,9 +655,19 @@ class Nf3 { const newGasBlockEmitter = new EventEmitter(); const connection = new WebSocket(this.optimistWsUrl); this.websockets.push(connection); // save so we can close it properly later + + // Heartbeat function to keep WS open. Send beat every 15 seconds + const heartbeat = async () => { + if (!connection) return; + if (connection.readyState !== WebSocket.OPEN) return; + connection.send("heartbeat"); + setTimeout(heartbeat, 15000); + } + connection.onopen = () => { logger.debug('websocket connection opened'); connection.send('blocks'); + heartbeat() }; connection.onmessage = async message => { const msg = JSON.parse(message.data); @@ -755,8 +754,8 @@ class Nf3 { connection.onmessage = async message => { const msg = JSON.parse(message.data); const { type, txDataToSign } = msg; - if (type === 'commit' || type === 'challenge') { - await this.submitTransaction(txDataToSign, this.challengesContractAddress, 0); + if (type === 'challenge') { + await this.submitTransaction(txDataToSign, this.stateContractAddress, 0); } }; } diff --git a/cli/src/proposer.mjs b/cli/src/proposer.mjs index 0848ded26..8d4ae2084 100644 --- a/cli/src/proposer.mjs +++ b/cli/src/proposer.mjs @@ -31,9 +31,9 @@ async function startProposer(testEnvironment) { setEnvironment('Localhost'); } const nf3Env = getCurrentEnvironment().currentEnvironment; - const nf3 = new Nf3(nf3Env.web3WsUrl, ethereumSigningKey, nf3Env); + const nf3 = new Nf3(ethereumSigningKey, nf3Env); - await nf3.init(defaultMnemonic); + await nf3.init(undefined, 'optimist'); if (await nf3.healthcheck('optimist')) console.log('Healthcheck passed'); else throw new Error('Healthcheck failed'); await nf3.registerProposer(); diff --git a/nightfall-optimist/src/event-handlers/subscribe.mjs b/nightfall-optimist/src/event-handlers/subscribe.mjs index 631aba659..2550bae26 100644 --- a/nightfall-optimist/src/event-handlers/subscribe.mjs +++ b/nightfall-optimist/src/event-handlers/subscribe.mjs @@ -72,28 +72,58 @@ export async function startEventQueue(callback, ...arg) { } export async function subscribeToChallengeWebSocketConnection(callback, ...args) { - wss.on('connection', ws => - ws.on('message', message => { - if (message === 'challenge') callback(ws, args); - }), + wss.on('connection', ws => { + ws.on('message', message => { + if (message === 'challenge') callback(ws, args); + }); + ws.on('error', () => { + logger.debug('ERROR challenge WS'); + }); + ws.on('open', () => { + logger.debug('OPEN challenge WS'); + }); + ws.on('close', (err) => { + logger.debug(`CLOSE challenge WS: ${err}`); + }); + } ); logger.debug('Subscribed to Challenge WebSocket connection'); } export async function subscribeToBlockAssembledWebSocketConnection(callback, ...args) { - wss.on('connection', ws => - ws.on('message', message => { - if (message === 'blocks') callback(ws, args); - }), + wss.on('connection', ws => { + ws.on('message', message => { + if (message === 'blocks') callback(ws, args); + }); + ws.on('error', () => { + logger.debug('ERROR block-assembly WS'); + }); + ws.on('open', () => { + logger.debug('OPEN block-assembly WS'); + }); + ws.on('close', (msg) => { + logger.debug(`CLOSE block-assembly ${msg}`); + }); + } ); logger.debug('Subscribed to BlockAssembled WebSocket connection'); } export async function subscribeToInstantWithDrawalWebSocketConnection(callback, ...args) { - wss.on('connection', ws => - ws.on('message', message => { - if (message === 'instant') callback(ws, args); - }), + wss.on('connection', ws => { + ws.on('message', message => { + if (message === 'instant') callback(ws, args); + }); + ws.on('error', () => { + logger.debug('ERROR instant-withdraw'); + }); + ws.on('open', () => { + logger.debug('OPEN instant-withdraw'); + }); + ws.on('close', (err) => { + logger.debug(`CLOSE instant-withdraw ${err}`); + }); + } ); logger.debug('Subscribed to InstantWithDrawal WebSocket connection'); } diff --git a/nightfall-optimist/src/services/block-assembler.mjs b/nightfall-optimist/src/services/block-assembler.mjs index 327c82b1c..67dfa6fb5 100644 --- a/nightfall-optimist/src/services/block-assembler.mjs +++ b/nightfall-optimist/src/services/block-assembler.mjs @@ -58,7 +58,9 @@ export async function conditionalMakeBlock(proposer) { transactions.map(t => Transaction.buildSolidityStruct(t)), ) .encodeABI(); - if (ws) + // TODO - check ws readyState is OPEN => CLOSED .WebSocket.OPEN(1), CONNECTING(0), CLOSING(2), CLOSED(3) + // before sending Poposed block. If not Open, try to open it + if (ws){ await ws.send( JSON.stringify({ type: 'block', @@ -67,7 +69,8 @@ export async function conditionalMakeBlock(proposer) { transactions, }), ); - logger.debug('Send unsigned block-assembler transaction to ws client'); + logger.debug('Send unsigned block-assembler transaction to ws client'); + } // remove the transactiosn from the mempool so we don't keep making new // blocks with them await removeTransactionsFromMemPool(block.transactionHashes); From d47c6cb711390bcaa64c75b5972dc2d0eb517f5b Mon Sep 17 00:00:00 2001 From: David Date: Thu, 10 Mar 2022 12:51:06 +0100 Subject: [PATCH 2/7] fix: changes for staging/prod --- cli/lib/environment.mjs | 8 -- cli/lib/nf3.mjs | 6 +- cli/src/proposer.mjs | 4 +- .../src/event-handlers/subscribe.mjs | 81 +++++++++---------- .../src/services/block-assembler.mjs | 2 +- 5 files changed, 45 insertions(+), 56 deletions(-) diff --git a/cli/lib/environment.mjs b/cli/lib/environment.mjs index 00b91c247..be42c5dbb 100644 --- a/cli/lib/environment.mjs +++ b/cli/lib/environment.mjs @@ -16,14 +16,6 @@ const SUPPORTED_ENVIRONMENTS = { optimistWsUrl: 'wss://optimist1-ws.testnet.nightfall3.com', web3WsUrl: 'wss://ropsten1-ws.testnet.nightfall3.com', }, - staging: { - name: 'Staging', - chainId: 4378921, - clientApiUrl: 'https://client1.testnet.nightfall3.com', - optimistApiUrl: 'https://optimist-api.ropsten.nightfall3.com', - optimistWsUrl: 'wss://optimist-ws.ropsten.nightfall3.com', - web3WsUrl: 'wss://ganache-ws.ropsten.nightfall3.com', - }, rinkeby: { name: 'Rinkeby', chainId: 4, diff --git a/cli/lib/nf3.mjs b/cli/lib/nf3.mjs index 2869e680d..900529d07 100644 --- a/cli/lib/nf3.mjs +++ b/cli/lib/nf3.mjs @@ -660,14 +660,14 @@ class Nf3 { const heartbeat = async () => { if (!connection) return; if (connection.readyState !== WebSocket.OPEN) return; - connection.send("heartbeat"); + connection.send('heartbeat'); setTimeout(heartbeat, 15000); - } + }; connection.onopen = () => { logger.debug('websocket connection opened'); connection.send('blocks'); - heartbeat() + heartbeat(); }; connection.onmessage = async message => { const msg = JSON.parse(message.data); diff --git a/cli/src/proposer.mjs b/cli/src/proposer.mjs index 8d4ae2084..0848ded26 100644 --- a/cli/src/proposer.mjs +++ b/cli/src/proposer.mjs @@ -31,9 +31,9 @@ async function startProposer(testEnvironment) { setEnvironment('Localhost'); } const nf3Env = getCurrentEnvironment().currentEnvironment; - const nf3 = new Nf3(ethereumSigningKey, nf3Env); + const nf3 = new Nf3(nf3Env.web3WsUrl, ethereumSigningKey, nf3Env); - await nf3.init(undefined, 'optimist'); + await nf3.init(defaultMnemonic); if (await nf3.healthcheck('optimist')) console.log('Healthcheck passed'); else throw new Error('Healthcheck failed'); await nf3.registerProposer(); diff --git a/nightfall-optimist/src/event-handlers/subscribe.mjs b/nightfall-optimist/src/event-handlers/subscribe.mjs index 2550bae26..823a656ff 100644 --- a/nightfall-optimist/src/event-handlers/subscribe.mjs +++ b/nightfall-optimist/src/event-handlers/subscribe.mjs @@ -73,57 +73,54 @@ export async function startEventQueue(callback, ...arg) { export async function subscribeToChallengeWebSocketConnection(callback, ...args) { wss.on('connection', ws => { - ws.on('message', message => { - if (message === 'challenge') callback(ws, args); - }); - ws.on('error', () => { - logger.debug('ERROR challenge WS'); - }); - ws.on('open', () => { - logger.debug('OPEN challenge WS'); - }); - ws.on('close', (err) => { - logger.debug(`CLOSE challenge WS: ${err}`); - }); - } - ); + ws.on('message', message => { + if (message === 'challenge') callback(ws, args); + }); + ws.on('error', () => { + logger.debug('ERROR challenge WS'); + }); + ws.on('open', () => { + logger.debug('OPEN challenge WS'); + }); + ws.on('close', err => { + logger.debug(`CLOSE challenge WS: ${err}`); + }); + }); logger.debug('Subscribed to Challenge WebSocket connection'); } export async function subscribeToBlockAssembledWebSocketConnection(callback, ...args) { wss.on('connection', ws => { - ws.on('message', message => { - if (message === 'blocks') callback(ws, args); - }); - ws.on('error', () => { - logger.debug('ERROR block-assembly WS'); - }); - ws.on('open', () => { - logger.debug('OPEN block-assembly WS'); - }); - ws.on('close', (msg) => { - logger.debug(`CLOSE block-assembly ${msg}`); - }); - } - ); + ws.on('message', message => { + if (message === 'blocks') callback(ws, args); + }); + ws.on('error', () => { + logger.debug('ERROR block-assembly WS'); + }); + ws.on('open', () => { + logger.debug('OPEN block-assembly WS'); + }); + ws.on('close', msg => { + logger.debug(`CLOSE block-assembly ${msg}`); + }); + }); logger.debug('Subscribed to BlockAssembled WebSocket connection'); } export async function subscribeToInstantWithDrawalWebSocketConnection(callback, ...args) { wss.on('connection', ws => { - ws.on('message', message => { - if (message === 'instant') callback(ws, args); - }); - ws.on('error', () => { - logger.debug('ERROR instant-withdraw'); - }); - ws.on('open', () => { - logger.debug('OPEN instant-withdraw'); - }); - ws.on('close', (err) => { - logger.debug(`CLOSE instant-withdraw ${err}`); - }); - } - ); + ws.on('message', message => { + if (message === 'instant') callback(ws, args); + }); + ws.on('error', () => { + logger.debug('ERROR instant-withdraw'); + }); + ws.on('open', () => { + logger.debug('OPEN instant-withdraw'); + }); + ws.on('close', err => { + logger.debug(`CLOSE instant-withdraw ${err}`); + }); + }); logger.debug('Subscribed to InstantWithDrawal WebSocket connection'); } diff --git a/nightfall-optimist/src/services/block-assembler.mjs b/nightfall-optimist/src/services/block-assembler.mjs index 67dfa6fb5..4e1a6ba6c 100644 --- a/nightfall-optimist/src/services/block-assembler.mjs +++ b/nightfall-optimist/src/services/block-assembler.mjs @@ -60,7 +60,7 @@ export async function conditionalMakeBlock(proposer) { .encodeABI(); // TODO - check ws readyState is OPEN => CLOSED .WebSocket.OPEN(1), CONNECTING(0), CLOSING(2), CLOSED(3) // before sending Poposed block. If not Open, try to open it - if (ws){ + if (ws) { await ws.send( JSON.stringify({ type: 'block', From 8187296149eeb810207dd44c5b3b5064503106fd Mon Sep 17 00:00:00 2001 From: David Date: Thu, 10 Mar 2022 12:57:10 +0100 Subject: [PATCH 3/7] fix: udated nf3 --- cli/lib/nf3.mjs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/cli/lib/nf3.mjs b/cli/lib/nf3.mjs index 900529d07..5c3a320c0 100644 --- a/cli/lib/nf3.mjs +++ b/cli/lib/nf3.mjs @@ -8,6 +8,7 @@ import { approve } from './tokens.mjs'; import erc20 from './abis/ERC20.mjs'; import erc721 from './abis/ERC721.mjs'; import erc1155 from './abis/ERC1155.mjs'; + import { DEFAULT_BLOCK_STAKE, DEFAULT_PROPOSER_BOND, DEFAULT_FEE } from './constants.mjs'; /** @@ -220,18 +221,17 @@ class Nf3 { // then return the receipt. // TODO does this still work if there is a chain reorg or do we have to handle that? return new Promise((resolve, reject) => { - if (process.env.VERBOSE) console.log(`Confirming transaction ${signed.transactionHash}`); + logger.debug(`Confirming transaction ${signed.transactionHash}`); this.notConfirmed++; this.web3.eth .sendSignedTransaction(signed.rawTransaction) .on('confirmation', (number, receipt) => { if (number === 12) { this.notConfirmed--; - if (process.env.VERBOSE) - console.log( - `Transaction ${receipt.transactionHash} has been confirmed ${number} times.`, - `Number of unconfirmed transactions is ${this.notConfirmed}`, - ); + logger.debug( + `Transaction ${receipt.transactionHash} has been confirmed ${number} times.`, + `Number of unconfirmed transactions is ${this.notConfirmed}`, + ); resolve(receipt); } }) @@ -614,6 +614,17 @@ class Nf3 { return this.submitTransaction(res.data.txDataToSign, this.proposersContractAddress, 0); } + /** + Get current proposer + @method + @async + @returns {array} A promise that resolves to the Ethereum transaction receipt. + */ + async getCurrentProposer() { + const res = await axios.get(`${this.optimistBaseUrl}/proposer/current-proposer`); + return res.data.currentProposer; + } + /** Get all the list of existing proposers. @method @@ -655,7 +666,6 @@ class Nf3 { const newGasBlockEmitter = new EventEmitter(); const connection = new WebSocket(this.optimistWsUrl); this.websockets.push(connection); // save so we can close it properly later - // Heartbeat function to keep WS open. Send beat every 15 seconds const heartbeat = async () => { if (!connection) return; @@ -663,7 +673,6 @@ class Nf3 { connection.send('heartbeat'); setTimeout(heartbeat, 15000); }; - connection.onopen = () => { logger.debug('websocket connection opened'); connection.send('blocks'); @@ -754,8 +763,8 @@ class Nf3 { connection.onmessage = async message => { const msg = JSON.parse(message.data); const { type, txDataToSign } = msg; - if (type === 'challenge') { - await this.submitTransaction(txDataToSign, this.stateContractAddress, 0); + if (type === 'commit' || type === 'challenge') { + await this.submitTransaction(txDataToSign, this.challengesContractAddress, 0); } }; } From 198335bd125a323f31864cd71fa61e2da69d7339 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 11 Mar 2022 11:37:47 +0100 Subject: [PATCH 4/7] fix: substituted heartbeat by ws ping function --- cli/lib/nf3.mjs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cli/lib/nf3.mjs b/cli/lib/nf3.mjs index 5c3a320c0..656b950c6 100644 --- a/cli/lib/nf3.mjs +++ b/cli/lib/nf3.mjs @@ -666,17 +666,18 @@ class Nf3 { const newGasBlockEmitter = new EventEmitter(); const connection = new WebSocket(this.optimistWsUrl); this.websockets.push(connection); // save so we can close it properly later - // Heartbeat function to keep WS open. Send beat every 15 seconds - const heartbeat = async () => { + // Ping function to keep WS open. Send beat every 15 seconds + const ping = async () => { if (!connection) return; if (connection.readyState !== WebSocket.OPEN) return; - connection.send('heartbeat'); - setTimeout(heartbeat, 15000); + //connection.send('heartbeat'); + connection.ping(); + setTimeout(ping, 15000); }; connection.onopen = () => { logger.debug('websocket connection opened'); connection.send('blocks'); - heartbeat(); + ping(); }; connection.onmessage = async message => { const msg = JSON.parse(message.data); From d52be9e5c0ab31e1432e7c427ebac49cf372c4fa Mon Sep 17 00:00:00 2001 From: David Date: Fri, 11 Mar 2022 11:39:12 +0100 Subject: [PATCH 5/7] fix: eslint --- cli/lib/nf3.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/lib/nf3.mjs b/cli/lib/nf3.mjs index 656b950c6..671a76c7b 100644 --- a/cli/lib/nf3.mjs +++ b/cli/lib/nf3.mjs @@ -670,7 +670,7 @@ class Nf3 { const ping = async () => { if (!connection) return; if (connection.readyState !== WebSocket.OPEN) return; - //connection.send('heartbeat'); + // connection.send('heartbeat'); connection.ping(); setTimeout(ping, 15000); }; From 313db84c985c5f45d03001347e71610d04638a08 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 11 Mar 2022 16:36:01 +0100 Subject: [PATCH 6/7] fix: remove comment --- cli/lib/nf3.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/lib/nf3.mjs b/cli/lib/nf3.mjs index 671a76c7b..ce03063f4 100644 --- a/cli/lib/nf3.mjs +++ b/cli/lib/nf3.mjs @@ -670,7 +670,6 @@ class Nf3 { const ping = async () => { if (!connection) return; if (connection.readyState !== WebSocket.OPEN) return; - // connection.send('heartbeat'); connection.ping(); setTimeout(ping, 15000); }; From 37cc33a06132f8f8d28b9244ea6a7100c934b7e5 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 13 Mar 2022 09:33:12 +0100 Subject: [PATCH 7/7] fix: add ws ping to other ws --- cli/lib/nf3.mjs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/cli/lib/nf3.mjs b/cli/lib/nf3.mjs index ce03063f4..93279c9c9 100644 --- a/cli/lib/nf3.mjs +++ b/cli/lib/nf3.mjs @@ -512,8 +512,15 @@ class Nf3 { const emitter = new EventEmitter(); const connection = new WebSocket(this.optimistWsUrl); this.websockets.push(connection); // save so we can close it properly later + const ping = async () => { + if (!connection) return; + if (connection.readyState !== WebSocket.OPEN) return; + connection.ping(); + setTimeout(ping, 15000); + }; connection.onopen = () => { connection.send('instant'); + ping(); }; connection.onmessage = async message => { const msg = JSON.parse(message.data); @@ -711,8 +718,15 @@ class Nf3 { const newBlockEmitter = new EventEmitter(); const connection = new WebSocket(this.optimistWsUrl); this.websockets.push(connection); // save so we can close it properly later + const ping = async () => { + if (!connection) return; + if (connection.readyState !== WebSocket.OPEN) return; + connection.ping(); + setTimeout(ping, 15000); + }; connection.onopen = () => { connection.send('blocks'); + ping(); }; connection.onmessage = async message => { const msg = JSON.parse(message.data); @@ -757,8 +771,15 @@ class Nf3 { async startChallenger() { const connection = new WebSocket(this.optimistWsUrl); this.websockets.push(connection); // save so we can close it properly later + const ping = async () => { + if (!connection) return; + if (connection.readyState !== WebSocket.OPEN) return; + connection.ping(); + setTimeout(ping, 15000); + }; connection.onopen = () => { connection.send('challenge'); + ping(); }; connection.onmessage = async message => { const msg = JSON.parse(message.data); @@ -783,8 +804,15 @@ class Nf3 { const newChallengeEmitter = new EventEmitter(); const connection = new WebSocket(this.optimistWsUrl); this.websockets.push(connection); // save so we can close it properly later + const ping = async () => { + if (!connection) return; + if (connection.readyState !== WebSocket.OPEN) return; + connection.ping(); + setTimeout(ping, 15000); + }; connection.onopen = () => { connection.send('blocks'); + ping(); }; connection.onmessage = async message => { const msg = JSON.parse(message.data);