From 0d3c8120f9185a9b3f86943c6d521b2165c5b64b Mon Sep 17 00:00:00 2001 From: imagobea Date: Thu, 12 Jan 2023 18:14:28 +0100 Subject: [PATCH 1/4] refactor: changes towards rm challenger --- cli/lib/nf3.mjs | 164 ++++++++---------- .../src/event-handlers/challenge-commit.mjs | 13 +- .../src/event-handlers/index.mjs | 13 +- .../src/event-handlers/rollback.mjs | 10 +- .../src/event-handlers/subscribe.mjs | 12 -- nightfall-optimist/src/index.mjs | 3 - nightfall-optimist/src/routes/challenger.mjs | 5 +- .../src/services/challenges.mjs | 62 +------ 8 files changed, 85 insertions(+), 197 deletions(-) diff --git a/cli/lib/nf3.mjs b/cli/lib/nf3.mjs index fc2b26874..ee0241998 100644 --- a/cli/lib/nf3.mjs +++ b/cli/lib/nf3.mjs @@ -31,11 +31,7 @@ function createQueue(options) { return queue; } -// TODO when SDK is refactored such that these functions are split by user, proposer and challenger, -// then there will only be one queue here. The constructor does not need to initialise clientBaseUrl -// for proposer/liquidityProvider/challenger and optimistBaseUrl, optimistWsUrl for a user etc const userQueue = createQueue({ autostart: true, concurrency: 1 }); -const challengerQueue = createQueue({ autostart: true, concurrency: 1 }); const liquidityProviderQueue = createQueue({ autostart: true, concurrency: 1 }); /** @@ -43,9 +39,9 @@ const liquidityProviderQueue = createQueue({ autostart: true, concurrency: 1 }); Creates a new Nightfall_3 library instance. @param {string} clientBaseUrl - The base url for nightfall-client @param {string} optimistBaseUrl - The base url for nightfall-optimist -@param {string} optimistWsUrl - The webscocket url for nightfall-optimist +@param {string} optimistWsUrl - The websocket url for nightfall-optimist @param {string} web3WsUrl - The websocket url for the web3js client -@param {string} ethereumSigningKey - the Ethereum siging key to be used for transactions (hex string). +@param {string} ethereumSigningKey - the Ethereum signing key to be used for transactions (hex string). @param {object} zkpKeys - An object containing the zkp keys to use. These will be auto-generated if left undefined. */ class Nf3 { @@ -415,7 +411,7 @@ class Nf3 { @method @async @param {string} contractName - the name of the smart contract in question. Possible - values are 'Shield', 'State', 'Proposers', 'Challengers'. + values are 'Shield', 'State', 'Proposers', 'Challenges'. @returns {Promise} Resolves into the Ethereum address of the contract */ async getContractAbi(contractName) { @@ -428,7 +424,7 @@ class Nf3 { @method @async @param {string} contractName - the name of the smart contract in question. Possible - values are 'Shield', 'State', 'Proposers', 'Challengers'. + values are 'Shield', 'State', 'Proposers', 'Challenges'. @returns {Promise} Resolves into the Ethereum ABI of the contract */ async getContractAbiOptimist(contractName) { @@ -441,7 +437,7 @@ class Nf3 { @method @async @param {string} contractName - the name of the smart contract in question. Possible - values are 'Shield', 'State', 'Proposers', 'Challengers'. + values are 'Shield', 'State', 'Proposers', 'Challenges'. @returns {Promise} Resolves into the Ethereum address of the contract */ async getContractAddress(contractName) { @@ -454,7 +450,7 @@ class Nf3 { @method @async @param {string} contractName - the name of the smart contract in question. Possible - values are 'Shield', 'State', 'Proposers', 'Challengers'. + values are 'Shield', 'State', 'Proposers', 'Challenges'. @returns {Promise} Resolves into the Ethereum address of the contract */ async getContractAddressOptimist(contractName) { @@ -1050,98 +1046,74 @@ class Nf3 { */ async startChallenger() { const challengeEmitter = this.createEmitter(); - const connection = new ReconnectingWebSocket(this.optimistWsUrl, [], { WebSocket }); - - this.websockets.push(connection); // save so we can close it properly later - - /* - we can't setup up a ping until the connection is made because the ping function - only exists in the underlying 'ws' object (_ws) and that is undefined until the - websocket is opened, it seems. Hence, we put all this code inside the onopen. - */ - connection.onopen = () => { - // setup a ping every 15s - this.intervalIDs.push( - setInterval(() => { - connection._ws.ping(); - }, WEBSOCKET_PING_TIME), - ); - // and a listener for the pong - logger.debug('Challenge websocket connection opened'); - - connection.send('challenge'); - }; - - connection.onmessage = async message => { - const msg = JSON.parse(message.data); - const { type, txDataToSign, sender } = msg; - - logger.debug(`Challenger received websocket message of type ${type}`); - - // if we're about to challenge, check it's actually our challenge, so as not to waste gas - if (type === 'challenge' && sender !== this.ethereumAddress) return null; - if (type === 'commit' || type === 'challenge') { - // Get the function selector from the encoded ABI, which corresponds to the first 4 bytes. - // In hex, it will correspond to the first 8 characters + 2 extra characters (0x), hence we - // do slice(0,10) - const txSelector = txDataToSign.slice(0, 10); - challengerQueue.push(async () => { - try { - const receipt = await this.submitTransaction( - txDataToSign, - this.challengesContractAddress, - 0, - ); - challengeEmitter.emit('receipt', receipt, type, txSelector); - } catch (err) { - logger.error({ - msg: 'Error while trying to challenge a block', - type, - err, - }); - challengeEmitter.emit('error', err, type, txSelector); - } - }); - logger.debug(`queued ${type} ${txDataToSign}`); - } - if (type === 'rollback') { - challengeEmitter.emit('rollback', 'rollback complete'); - } - return null; - }; - connection.onerror = () => logger.error('websocket connection error'); - connection.onclosed = () => logger.warn('websocket connection closed'); + // const connection = new ReconnectingWebSocket(this.optimistWsUrl, [], { WebSocket }); + + // this.websockets.push(connection); // save so we can close it properly later + + // /* + // we can't setup up a ping until the connection is made because the ping function + // only exists in the underlying 'ws' object (_ws) and that is undefined until the + // websocket is opened, it seems. Hence, we put all this code inside the onopen. + // */ + // connection.onopen = () => { + // // setup a ping every 15s + // this.intervalIDs.push( + // setInterval(() => { + // connection._ws.ping(); + // }, WEBSOCKET_PING_TIME), + // ); + // // and a listener for the pong + // logger.debug('Challenge websocket connection opened'); + + // connection.send('challenge'); + // }; + + // connection.onmessage = async message => { + // const msg = JSON.parse(message.data); + // const { type, txDataToSign, sender } = msg; + + // logger.debug(`Challenger received websocket message of type ${type}`); + + // // if we're about to challenge, check it's actually our challenge, so as not to waste gas + // if (type === 'challenge' && sender !== this.ethereumAddress) return null; + // if (type === 'commit' || type === 'challenge') { + // // Get the function selector from the encoded ABI, which corresponds to the first 4 bytes. + // // In hex, it will correspond to the first 8 characters + 2 extra characters (0x), hence we + // // do slice(0,10) + // const txSelector = txDataToSign.slice(0, 10); + // challengerQueue.push(async () => { + // try { + // const receipt = await this.submitTransaction( + // txDataToSign, + // this.challengesContractAddress, + // 0, + // ); + // challengeEmitter.emit('receipt', receipt, type, txSelector); + // } catch (err) { + // logger.error({ + // msg: 'Error while trying to challenge a block', + // type, + // err, + // }); + // challengeEmitter.emit('error', err, type, txSelector); + // } + // }); + // logger.debug(`queued ${type} ${txDataToSign}`); + // } + // if (type === 'rollback') { + // challengeEmitter.emit('rollback', 'rollback complete'); + // } + // return null; + // }; + // connection.onerror = () => logger.error('websocket connection error'); + // connection.onclosed = () => logger.warn('websocket connection closed'); return challengeEmitter; } - // method to turn challenges off and on. Note, this does not affect the queue - challengeEnable(enable) { + async challengeEnable(enable) { return axios.post(`${this.optimistBaseUrl}/challenger/enable`, { enable }); } - // eslint-disable-next-line class-methods-use-this - pauseQueueChallenger() { - return new Promise(resolve => { - if (challengerQueue.autostart) { - // put an event at the head of the queue which will cleanly pause it. - challengerQueue.unshift(async () => { - challengerQueue.autostart = false; - challengerQueue.stop(); - logger.info(`queue challengerQueue has been paused`); - resolve(); - }); - } else { - resolve(); - } - }); - } - - // eslint-disable-next-line class-methods-use-this - unpauseQueueChallenger() { - challengerQueue.autostart = true; - challengerQueue.unshift(async () => logger.info(`queue challengerQueue has been unpaused`)); - } - /** Returns the balance of tokens held in layer 2 @method diff --git a/nightfall-optimist/src/event-handlers/challenge-commit.mjs b/nightfall-optimist/src/event-handlers/challenge-commit.mjs index 6b831d127..86dd4430c 100644 --- a/nightfall-optimist/src/event-handlers/challenge-commit.mjs +++ b/nightfall-optimist/src/event-handlers/challenge-commit.mjs @@ -6,21 +6,22 @@ async function committedToChallengeEventHandler(data) { const { commitHash, sender } = data.returnValues; logger.debug({ - msg: 'Received commmitted to challenge event', + msg: 'Received CommittedToChallenge event', commitHash, sender, }); - logger.info('A challenge commitment has been mined'); + const commitData = await getCommit(commitHash); - // We may not find the commitment. In this case, it's probably not ours so we take no action + // We may not find the commitment (probably not ours), so we take no action if (commitData === null) { logger.debug('Commit hash not found in database'); return; } - // if retrieved is true, then we've looked up this commit before and we assume - // we have already revealed it - thus we don't reveal it again because that would - // just waste gas. This could happen if a chain reorg were to re-emit the + + // If `retrieved` is true, then we've looked up this commit before and we assume + // we have already revealed it so we take no action. + // This could happen if a chain reorg were to re-emit the // CommittedToChallenge event. const { txDataToSign, retrieved } = commitData; if (!retrieved) revealChallenge(txDataToSign, sender); diff --git a/nightfall-optimist/src/event-handlers/index.mjs b/nightfall-optimist/src/event-handlers/index.mjs index d531ceaf8..d25edc857 100644 --- a/nightfall-optimist/src/event-handlers/index.mjs +++ b/nightfall-optimist/src/event-handlers/index.mjs @@ -1,8 +1,4 @@ -import { - startEventQueue, - subscribeToChallengeWebSocketConnection, - subscribeToInstantWithDrawalWebSocketConnection, -} from './subscribe.mjs'; +import { startEventQueue, subscribeToInstantWithDrawalWebSocketConnection } from './subscribe.mjs'; import blockProposedEventHandler from './block-proposed.mjs'; import newCurrentProposerEventHandler from './new-current-proposer.mjs'; import transactionSubmittedEventHandler from './transaction-submitted.mjs'; @@ -39,9 +35,4 @@ const eventHandlers = { }, }; -export { - startEventQueue, - subscribeToChallengeWebSocketConnection, - subscribeToInstantWithDrawalWebSocketConnection, - eventHandlers, -}; +export { startEventQueue, subscribeToInstantWithDrawalWebSocketConnection, eventHandlers }; diff --git a/nightfall-optimist/src/event-handlers/rollback.mjs b/nightfall-optimist/src/event-handlers/rollback.mjs index b981589b6..1c3c41910 100644 --- a/nightfall-optimist/src/event-handlers/rollback.mjs +++ b/nightfall-optimist/src/event-handlers/rollback.mjs @@ -6,7 +6,7 @@ */ import logger from '@polygon-nightfall/common-files/utils/logger.mjs'; import constants from '@polygon-nightfall/common-files/constants/index.mjs'; -import { dequeueEvent, enqueueEvent } from '@polygon-nightfall/common-files/utils/event-queue.mjs'; +import { dequeueEvent } from '@polygon-nightfall/common-files/utils/event-queue.mjs'; import { addTransactionsToMemPool, deleteBlock, @@ -17,10 +17,6 @@ import { } from '../services/database.mjs'; import Block from '../classes/block.mjs'; import { checkTransaction } from '../services/transaction-checker.mjs'; -import { - signalRollbackCompleted as signalRollbackCompletedToChallenger, - isMakeChallengesEnable, -} from '../services/challenges.mjs'; const { ZERO } = constants; @@ -132,10 +128,6 @@ async function rollbackEventHandler(data) { await dequeueEvent(2); // Remove an event from the stopQueue. // A Rollback triggers a NewCurrentProposer event which should trigger queue[0].end() // But to be safe we enqueue a helper event to guarantee queue[0].end() runs. - - // assumption is if optimist has makeChallenges ON there is challenger - // websocket client waiting for signal rollback - if (isMakeChallengesEnable()) await enqueueEvent(() => signalRollbackCompletedToChallenger(), 0); } export default rollbackEventHandler; diff --git a/nightfall-optimist/src/event-handlers/subscribe.mjs b/nightfall-optimist/src/event-handlers/subscribe.mjs index 98dd6d556..b8436c078 100644 --- a/nightfall-optimist/src/event-handlers/subscribe.mjs +++ b/nightfall-optimist/src/event-handlers/subscribe.mjs @@ -76,18 +76,6 @@ export async function startEventQueue(callback, ...arg) { return emitters; } -export async function subscribeToChallengeWebSocketConnection(callback, ...args) { - wss.on('connection', ws => { - ws.on('message', message => { - if (message === 'challenge') { - setupWebsocketEvents(ws, 'challenge'); - callback(ws, args); - } - }); - }); - logger.debug('Subscribed to Challenge WebSocket connection'); -} - export async function subscribeToInstantWithDrawalWebSocketConnection(callback, ...args) { wss.on('connection', ws => { ws.on('message', message => { diff --git a/nightfall-optimist/src/index.mjs b/nightfall-optimist/src/index.mjs index b5218bc1a..a0391445c 100644 --- a/nightfall-optimist/src/index.mjs +++ b/nightfall-optimist/src/index.mjs @@ -7,13 +7,11 @@ import { import app from './app.mjs'; import { startEventQueue, - subscribeToChallengeWebSocketConnection, subscribeToInstantWithDrawalWebSocketConnection, eventHandlers, } from './event-handlers/index.mjs'; import Proposer from './classes/proposer.mjs'; import { conditionalMakeBlock } from './services/block-assembler.mjs'; -import { setChallengeWebSocketConnection } from './services/challenges.mjs'; import initialBlockSync from './services/state-sync.mjs'; import { setInstantWithdrawalWebSocketConnection } from './services/instant-withdrawal.mjs'; import { setProposer } from './routes/proposer.mjs'; @@ -26,7 +24,6 @@ const main = async () => { const proposerEthAddress = app.get('proposerEthAddress'); autoChangeCurrentProposer(proposerEthAddress); // starts the auto change current proposer service // subscribe to WebSocket events first - await subscribeToChallengeWebSocketConnection(setChallengeWebSocketConnection); await subscribeToInstantWithDrawalWebSocketConnection(setInstantWithdrawalWebSocketConnection); await startEventQueue(queueManager, eventHandlers, proposer); diff --git a/nightfall-optimist/src/routes/challenger.mjs b/nightfall-optimist/src/routes/challenger.mjs index 988c7a46a..d251c1a5d 100644 --- a/nightfall-optimist/src/routes/challenger.mjs +++ b/nightfall-optimist/src/routes/challenger.mjs @@ -16,7 +16,7 @@ const router = express.Router(); * security: * - ApiKeyAuth: [] * tags: - * - Challanger + * - Challenger * summary: Enable a challenger. * description: TBC * parameters: @@ -41,11 +41,12 @@ router.post('/enable', auth, async (req, res, next) => { const { enable } = req.body; const result = enable === true ? startMakingChallenges() : stopMakingChallenges(); res.json(result); + if (queues[2].length === 0) { logger.info('After enabling challenges back, no challenges remain unresolved'); } else { logger.info( - `After enabling challenges back, there were ${queues[2].length} unresolved challenges. Running them now.`, + `After enabling challenges back, there were ${queues[2].length} unresolved challenges. Running them now.`, ); // start queue[2] and await all the unresolved challenges being run diff --git a/nightfall-optimist/src/services/challenges.mjs b/nightfall-optimist/src/services/challenges.mjs index efe4cfaed..25e69fb96 100644 --- a/nightfall-optimist/src/services/challenges.mjs +++ b/nightfall-optimist/src/services/challenges.mjs @@ -1,4 +1,3 @@ -import WebSocket from 'ws'; import config from 'config'; import logger from '@polygon-nightfall/common-files/utils/logger.mjs'; import Web3 from '@polygon-nightfall/common-files/utils/web3.mjs'; @@ -18,34 +17,10 @@ const { TIMBER_HEIGHT } = config; const { CHALLENGES_CONTRACT_NAME, ZERO } = constants; let makeChallenges = process.env.IS_CHALLENGER === 'true'; -let ws; - export function isMakeChallengesEnable() { return makeChallenges; } -export function setChallengeWebSocketConnection(_ws) { - ws = _ws; -} - -/** -Function to indicate to a listening challenger that a rollback has been completed. -*/ -export async function signalRollbackCompleted(data) { - // check that the websocket exists (it should) and its readyState is OPEN - // before sending. If not wait until the challenger reconnects - let tryCount = 0; - while (!ws || ws.readyState !== WebSocket.OPEN) { - await new Promise(resolve => setTimeout(resolve, 3000)); // eslint-disable-line no-await-in-loop - logger.warn( - `Websocket to challenger is closed for rollback complete. Waiting for challenger to reconnect`, - ); - if (tryCount++ > 100) throw new Error(`Websocket to challenger has failed`); - } - logger.debug('Rollback completed'); - ws.send(JSON.stringify({ type: 'rollback', data })); -} - export function startMakingChallenges() { if (process.env.IS_CHALLENGER !== 'true') { throw new Error('Cannot start challenger as this optimist never intend to be a challenger'); @@ -73,44 +48,15 @@ export async function commitToChallenge(txDataToSign) { await saveCommit(commitHash, txDataToSign); - // check that the websocket exists (it should) and its readyState is OPEN - // before sending commit. If not wait until the challenger reconnects - let tryCount = 0; - while (!ws || ws.readyState !== WebSocket.OPEN) { - await new Promise(resolve => setTimeout(resolve, 3000)); // eslint-disable-line no-await-in-loop - - logger.warn( - 'Websocket to challenger is closed for commit. Waiting for challenger to reconnect', - ); - - if (tryCount++ > 100) throw new Error(`Websocket to challenger has failed`); - } - - ws.send(JSON.stringify({ type: 'commit', txDataToSign: commitToSign })); - - logger.debug({ - msg: 'Raw transaction for committing to challenge has been sent to be signed and submitted', - rawTransaction: commitToSign, - }); + // ws.send(JSON.stringify({ type: 'commit', txDataToSign: commitToSign })); + // TODO sign and send commitToSign } export async function revealChallenge(txDataToSign, sender) { logger.debug('Revealing challenge'); - // check that the websocket exists (it should) and its readyState is OPEN - // before sending commit. If not wait until the challenger reconnects - let tryCount = 0; - while (!ws || ws.readyState !== WebSocket.OPEN) { - await new Promise(resolve => setTimeout(resolve, 3000)); // eslint-disable-line no-await-in-loop - logger.warn( - 'Websocket to challenger is closed for reveal. Waiting for challenger to reconnect', - ); - - if (tryCount++ > 100) { - throw new Error(`Websocket to $challenger has failed`); - } - } - ws.send(JSON.stringify({ type: 'challenge', txDataToSign, sender })); + // ws.send(JSON.stringify({ type: 'challenge', txDataToSign, sender })); + // TODO sign and send txDataToSign } export async function createChallenge(block, transactions, err) { From 9799a8e43dd96dbe642eea1f4e3118002a4fde4b Mon Sep 17 00:00:00 2001 From: imagobea Date: Thu, 12 Jan 2023 18:16:09 +0100 Subject: [PATCH 2/4] chore: silence eslint --- nightfall-optimist/src/services/challenges.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/nightfall-optimist/src/services/challenges.mjs b/nightfall-optimist/src/services/challenges.mjs index 25e69fb96..57d63c805 100644 --- a/nightfall-optimist/src/services/challenges.mjs +++ b/nightfall-optimist/src/services/challenges.mjs @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-vars */ import config from 'config'; import logger from '@polygon-nightfall/common-files/utils/logger.mjs'; import Web3 from '@polygon-nightfall/common-files/utils/web3.mjs'; From 5746114e824a2780a41f654cbd22592394b6927b Mon Sep 17 00:00:00 2001 From: imagobea Date: Thu, 12 Jan 2023 18:17:56 +0100 Subject: [PATCH 3/4] chore: silence eslint --- nightfall-optimist/src/services/challenges.mjs | 3 --- 1 file changed, 3 deletions(-) diff --git a/nightfall-optimist/src/services/challenges.mjs b/nightfall-optimist/src/services/challenges.mjs index 57d63c805..7e2463719 100644 --- a/nightfall-optimist/src/services/challenges.mjs +++ b/nightfall-optimist/src/services/challenges.mjs @@ -18,9 +18,6 @@ const { TIMBER_HEIGHT } = config; const { CHALLENGES_CONTRACT_NAME, ZERO } = constants; let makeChallenges = process.env.IS_CHALLENGER === 'true'; -export function isMakeChallengesEnable() { - return makeChallenges; -} export function startMakingChallenges() { if (process.env.IS_CHALLENGER !== 'true') { From f999f79eafe7a14d11c750468f606161bd03b916 Mon Sep 17 00:00:00 2001 From: imagobea Date: Mon, 16 Jan 2023 06:50:54 +0100 Subject: [PATCH 4/4] refactor: challenger can sign and send txs - changes completed --- nightfall-optimist/src/routes/proposer.mjs | 16 ++--- .../src/services/block-assembler.mjs | 4 +- .../src/services/challenges.mjs | 65 ++++++++++++++++--- .../src/utils/transactions-queue.mjs | 5 +- 4 files changed, 69 insertions(+), 21 deletions(-) diff --git a/nightfall-optimist/src/routes/proposer.mjs b/nightfall-optimist/src/routes/proposer.mjs index b228f24dc..c18f12e64 100644 --- a/nightfall-optimist/src/routes/proposer.mjs +++ b/nightfall-optimist/src/routes/proposer.mjs @@ -26,7 +26,7 @@ import { sendSignedTransaction, } from '../services/transaction-sign-send.mjs'; import auth from '../utils/auth.mjs'; -import txsQueue from '../utils/transactions-queue.mjs'; +import { proposerTxsQueue } from '../utils/transactions-queue.mjs'; const router = express.Router(); const { TIMBER_HEIGHT, HASH_TYPE } = config; @@ -119,7 +119,7 @@ router.post('/register', auth, async (req, res, next) => { ); // Submit tx - txsQueue.push(async () => { + proposerTxsQueue.push(async () => { try { const receipt = await sendSignedTransaction(signedTx); logger.debug({ msg: 'Proposer registered', receipt }); @@ -230,7 +230,7 @@ router.post('/update', auth, async (req, res, next) => { ); // Submit tx and update db if tx is successful - txsQueue.push(async () => { + proposerTxsQueue.push(async () => { try { const receipt = await sendSignedTransaction(signedTx); logger.debug({ msg: 'Proposer updated', receipt }); @@ -354,7 +354,7 @@ router.post('/de-register', auth, async (req, res, next) => { ); // Submit tx and update db if tx is successful - txsQueue.push(async () => { + proposerTxsQueue.push(async () => { try { const receipt = await sendSignedTransaction(signedTx); logger.debug({ msg: 'Proposer removed', receipt }); @@ -423,7 +423,7 @@ router.post('/withdrawStake', auth, async (req, res, next) => { ); // Submit tx - txsQueue.push(async () => { + proposerTxsQueue.push(async () => { try { const receipt = await sendSignedTransaction(signedTx); logger.debug({ msg: 'Proposer stake withdrawn', receipt }); @@ -593,7 +593,7 @@ router.get('/withdraw', auth, async (req, res, next) => { ); // Submit tx - txsQueue.push(async () => { + proposerTxsQueue.push(async () => { try { const receipt = await sendSignedTransaction(signedTx); logger.debug({ msg: 'Proposer profits withdrawn', receipt }); @@ -673,7 +673,7 @@ router.post('/payment', auth, async (req, res, next) => { ); // Submit tx - txsQueue.push(async () => { + proposerTxsQueue.push(async () => { try { const receipt = await sendSignedTransaction(signedTx); logger.debug({ msg: 'Proposer payment completed', receipt }); @@ -738,7 +738,7 @@ router.get('/change', auth, async (req, res, next) => { ); // Submit tx - txsQueue.push(async () => { + proposerTxsQueue.push(async () => { try { const receipt = await sendSignedTransaction(signedTx); logger.debug({ msg: 'Proposer was rotated', receipt }); diff --git a/nightfall-optimist/src/services/block-assembler.mjs b/nightfall-optimist/src/services/block-assembler.mjs index 9687268e9..c93291f81 100644 --- a/nightfall-optimist/src/services/block-assembler.mjs +++ b/nightfall-optimist/src/services/block-assembler.mjs @@ -13,7 +13,7 @@ import { removeTransactionsFromMemPool, getMempoolTxsSortedByFee } from './datab import Block from '../classes/block.mjs'; import { Transaction } from '../classes/index.mjs'; import { createSignedTransaction, sendSignedTransaction } from './transaction-sign-send.mjs'; -import txsQueue from '../utils/transactions-queue.mjs'; +import { proposerTxsQueue } from '../utils/transactions-queue.mjs'; const { MAX_BLOCK_SIZE, MINIMUM_TRANSACTION_SLOTS, PROPOSER_MAX_BLOCK_PERIOD_MILIS } = config; const { STATE_CONTRACT_NAME } = constants; @@ -169,7 +169,7 @@ export async function conditionalMakeBlock(args) { ); // Submit tx and update db if tx is successful - txsQueue.push(async () => { + proposerTxsQueue.push(async () => { try { const receipt = await sendSignedTransaction(signedTx); logger.debug({ msg: 'Block proposed', receipt }); diff --git a/nightfall-optimist/src/services/challenges.mjs b/nightfall-optimist/src/services/challenges.mjs index 7e2463719..0210d7beb 100644 --- a/nightfall-optimist/src/services/challenges.mjs +++ b/nightfall-optimist/src/services/challenges.mjs @@ -1,8 +1,8 @@ /* eslint-disable no-unused-vars */ + import config from 'config'; import logger from '@polygon-nightfall/common-files/utils/logger.mjs'; -import Web3 from '@polygon-nightfall/common-files/utils/web3.mjs'; -import { getContractInstance } from '@polygon-nightfall/common-files/utils/contract.mjs'; +import { getContractInstance, web3 } from '@polygon-nightfall/common-files/utils/contract.mjs'; import constants from '@polygon-nightfall/common-files/constants/index.mjs'; import { rand } from '@polygon-nightfall/common-files/utils/crypto/crypto-random.mjs'; import { @@ -11,12 +11,20 @@ import { getTreeByBlockNumberL2, getTransactionHashSiblingInfo, } from './database.mjs'; +import { createSignedTransaction, sendSignedTransaction } from './transaction-sign-send.mjs'; import Block from '../classes/block.mjs'; import { Transaction } from '../classes/index.mjs'; +import { challengerTxsQueue } from '../utils/transactions-queue.mjs'; const { TIMBER_HEIGHT } = config; +const environment = config.ENVIRONMENTS[process.env.ENVIRONMENT] || config.ENVIRONMENTS.localhost; + const { CHALLENGES_CONTRACT_NAME, ZERO } = constants; +const challengerEthPrivateKey = environment.PROPOSER_KEY; +const { address: challengerEthAddress } = + web3.eth.accounts.privateKeyToAccount(challengerEthPrivateKey); + let makeChallenges = process.env.IS_CHALLENGER === 'true'; export function startMakingChallenges() { @@ -37,24 +45,65 @@ export async function commitToChallenge(txDataToSign) { logger.debug('makeChallenges is off, no challenge commitment was sent'); return; } - const web3 = Web3.connection(); const commitHash = web3.utils.soliditySha3({ t: 'bytes', v: txDataToSign }); + const challengeContractInstance = await getContractInstance(CHALLENGES_CONTRACT_NAME); + const challengesContractAddress = challengeContractInstance.options.address; + const commitToSign = await challengeContractInstance.methods .commitToChallenge(commitHash) .encodeABI(); - await saveCommit(commitHash, txDataToSign); + const signedCommit = await createSignedTransaction( + challengerEthPrivateKey, + challengerEthAddress, + challengesContractAddress, + commitToSign, + ); + + challengerTxsQueue.push(async () => { + try { + const receipt = await sendSignedTransaction(signedCommit); + logger.debug({ msg: 'Commit to challenge completed', receipt }); - // ws.send(JSON.stringify({ type: 'commit', txDataToSign: commitToSign })); - // TODO sign and send commitToSign + await saveCommit(commitHash, txDataToSign); + logger.debug('Commit hash to challenge in db'); + } catch (err) { + logger.error({ + msg: 'Something went wrong', + err, + }); + } + }); } export async function revealChallenge(txDataToSign, sender) { logger.debug('Revealing challenge'); - // ws.send(JSON.stringify({ type: 'challenge', txDataToSign, sender })); - // TODO sign and send txDataToSign + // Challenge coming from different address, ignore + if (sender !== challengerEthAddress) return; + + const challengeContractInstance = await getContractInstance(CHALLENGES_CONTRACT_NAME); + const challengesContractAddress = challengeContractInstance.options.address; + + const signedTx = await createSignedTransaction( + challengerEthPrivateKey, + challengerEthAddress, + challengesContractAddress, + txDataToSign, + ); + + challengerTxsQueue.push(async () => { + try { + const receipt = await sendSignedTransaction(signedTx); + logger.debug({ msg: 'Reveal challenge completed', receipt }); + } catch (err) { + logger.error({ + msg: 'Something went wrong', + err, + }); + } + }); } export async function createChallenge(block, transactions, err) { diff --git a/nightfall-optimist/src/utils/transactions-queue.mjs b/nightfall-optimist/src/utils/transactions-queue.mjs index 8596fe62e..d2a50ae4c 100644 --- a/nightfall-optimist/src/utils/transactions-queue.mjs +++ b/nightfall-optimist/src/utils/transactions-queue.mjs @@ -8,6 +8,5 @@ function createQueue(options) { return queue; } -const txsQueue = createQueue({ autostart: true }); - -export default txsQueue; +export const proposerTxsQueue = createQueue({ autostart: true }); +export const challengerTxsQueue = createQueue({ autostart: true });