diff --git a/cli/lib/nf3.mjs b/cli/lib/nf3.mjs index bd83889c7..99f002741 100644 --- a/cli/lib/nf3.mjs +++ b/cli/lib/nf3.mjs @@ -223,15 +223,12 @@ class Nf3 { // 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}`); - 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}`, ); resolve(receipt); } @@ -566,7 +563,7 @@ class Nf3 { the proposer. @method @async - @param {string} Proposer REST API URL with format https://xxxx.xxx.xx + @param {string} Proposer REST API URL with format https://xxxx.xxx.xx @returns {Promise} A promise that resolves to the Ethereum transaction receipt. */ async registerProposer(url) { @@ -574,7 +571,7 @@ class Nf3 { address: this.ethereumAddress, url, }); - logger.debug(`Proposer Registered with address ${this.ethereumAddress} and URL ${url}`); + if (res.data.txDataToSign === '') return false; // already registered return this.submitTransaction( res.data.txDataToSign, this.proposersContractAddress, @@ -582,6 +579,25 @@ class Nf3 { ); } + /** + Registers a proposer locally with the Optimist instance only. This will cause + Optimist to make blocks when this proposer is current but these will revert if + the proposer isn't registered on the blockchain too. This method is useful only + if the proposer is already registered on the blockchain (has paid their bond) and + for some reason the Optimist instance does not know about them, e.g. a new instance + has been created. The method 'registerProposer' will both register the proposer + with the blockchain and register locally with the optimist instance. So, if + that method has been used successfully, there is no need to also call this method + @method + @async + @returns {Promise} A promise that resolves to the Ethereum transaction receipt. + */ + async registerProposerLocally() { + return axios.post(`${this.optimistBaseUrl}/proposer/registerlocally`, { + address: this.ethereumAddress, + }); + } + /** De-registers an existing proposer. It will use the address of the Ethereum Signing key that is holds to de-register @@ -652,7 +668,7 @@ class Nf3 { Update Proposers URL @method @async - @param {string} Proposer REST API URL with format https://xxxx.xxx.xx + @param {string} Proposer REST API URL with format https://xxxx.xxx.xx @returns {array} A promise that resolves to the Ethereum transaction receipt. */ async updateProposer(url) { @@ -714,7 +730,7 @@ class Nf3 { Send offchain transaction to Optimist @method @async - @param {string} transaction + @param {string} transaction @returns {array} A promise that resolves to the API call status */ async sendOffchainTransaction(transaction) { diff --git a/common-files/utils/contract.mjs b/common-files/utils/contract.mjs index 0c10447ab..0ae90e207 100644 --- a/common-files/utils/contract.mjs +++ b/common-files/utils/contract.mjs @@ -104,7 +104,6 @@ export async function waitForContract(contractName) { try { error = undefined; const address = await getContractAddress(contractName); // eslint-disable-line no-await-in-loop - logger.debug(`${contractName} contract address is ${address}`); if (address === undefined) throw new Error(`${contractName} contract address was undefined`); instance = getContractInstance(contractName, address); return instance; diff --git a/config/default.js b/config/default.js index a6506ba7a..adba489b0 100644 --- a/config/default.js +++ b/config/default.js @@ -4,7 +4,8 @@ const { DOMAIN_NAME = '' } = process.env; module.exports = { COMMITMENTS_DB: 'nightfall_commitments', OPTIMIST_DB: 'optimist_data', - METADATA_COLLECTION: 'metadata', + PROPOSER_COLLECTION: 'proposers', + CHALLENGER_COLLECTION: 'challengers', TRANSACTIONS_COLLECTION: 'transactions', SUBMITTED_BLOCKS_COLLECTION: 'blocks', NULLIFIER_COLLECTION: 'nullifiers', diff --git a/doc/contract-update.md b/doc/contract-update.md deleted file mode 100644 index 589ffc5dc..000000000 --- a/doc/contract-update.md +++ /dev/null @@ -1,68 +0,0 @@ -# Upgrading and Migrating contracts - -We consider two approaches: *Upgrading* existing contracts to a new version but retaining -state data as-is, and; *migrating* users to a completely new set of contracts. - -We would only migrate in the event of a key loss, which prevents upgrading contracts. - -## Upgrade contracts approach - -Our contracts are already mostly divided into contracts that contain data and contracts that -contain logic. This is fortuitous for the pattern we are going to use. - -`State.sol` contains most of the state, with some in `Shield.sol`. -This division could be made complete by moving the following functions out of `State.sol` -- `proposeBlock` -- `removeProposer` -- `emitRollback` -- `isBlockReal` -- `rewardChallenger` -- `removeProposer` - -and moving the `Shield.sol` data into `State.sol`. `State.sol` will then only contain storage data -and getters/setters for the same. Note that it also holds nightfall's escrow pool and so needs to host `withdraw`. -It will also need modifying so that the addresses of the logic contracts, which it allows to -set state, can be updated by a registered contract containing an `upgrade` function (see later). - -After that is done, we will not normally upgrade the `State.sol` contract but will upgrade the -stateless logic contracts. - -We will create a set of contracts, which proxy calls to the contracts (one for each logic contract). This avoids users -having to repoint their applications to the new contracts. These contracts must be ERC1967 compliant. - -![contract interaction](./contract-upgrade.png) - -### upgrade function - -This function will, atomically, carry out the following actions: - -1. Repoint `State.sol`'s registered logic contracts to the new logic contracts; -1. Repoint `Proxy.sol` contracts to the new contracts; -1. Change the addresses of the contracts registered with `State.sol` to the new contract addresses; -1. Store the address of the old contracts and the block number at which the swap-over occurred in `State.sol`; this -will be used to help nightfall applications parse historic events and calldata. This will be in the form of -an array, added to each time the contracts are upgraded. - -We will ensure that a unique private key is needed to call `upgrade`. - -Note: The nightfall applications will need to be updated so that they can sync events and calldata on startup, -which may have changing contract addresses, reflecting historic contract upgrades. - -## Migrate contracts approach - -In rare circumstances an upgrade may not be possible; the only use-case is probably compromise of the upgrade -private key. If this happens we would deploy an entirely new set of contracts. Rather than attempt to -migrate storage data (which would be difficult because of the number of mapping types, and the need to -freeze updates for the old contract data, which would require a wait of one finalisation period, ~1 week and may not be -possible if the relevant key is compromised), we will simply advertise the new contract addresses and recommend -that people upgrade by removing their funds from the old contract. - -## other - -We will include a function to pause nightfall, in case we need to copy over finalised state at any time. - -## Implementation - -We will use the [Openzepplin](https://docs.openzeppelin.com/upgrades-plugins/1.x/) Upgrades Truffle plugin to -implement the above approach. This will reduce the possibility of error. We will use the Beacon pattern -because this will allow us to atomically upgrade the contracts. diff --git a/doc/contract-upgrade.png b/doc/contract-upgrade.png deleted file mode 100644 index 69bfc94dc..000000000 Binary files a/doc/contract-upgrade.png and /dev/null differ diff --git a/nightfall-client/src/event-handlers/subscribe.mjs b/nightfall-client/src/event-handlers/subscribe.mjs index 9a1d67b39..1e58721e9 100644 --- a/nightfall-client/src/event-handlers/subscribe.mjs +++ b/nightfall-client/src/event-handlers/subscribe.mjs @@ -15,7 +15,7 @@ const { STATE_CONTRACT_NAME, RETRIES } = config; * This is useful in case nightfall-client comes up before the contract * is fully deployed. */ -async function waitForContract(contractName) { +export async function waitForContract(contractName) { let errorCount = 0; let error; let instance; @@ -23,7 +23,6 @@ async function waitForContract(contractName) { try { error = undefined; const address = await getContractAddress(contractName); - logger.debug(`${contractName} contract address is ${address}`); if (address === undefined) throw new Error(`${contractName} contract address was undefined`); instance = getContractInstance(contractName, address); return instance; diff --git a/nightfall-client/src/services/database.mjs b/nightfall-client/src/services/database.mjs index e0dc24b1a..2e0e3233f 100644 --- a/nightfall-client/src/services/database.mjs +++ b/nightfall-client/src/services/database.mjs @@ -109,7 +109,8 @@ export async function saveBlock(_block) { if (!existing || !existing.blockNumber) { return db.collection(SUBMITTED_BLOCKS_COLLECTION).updateOne(query, update, { upsert: true }); } - throw new Error('Attempted to replay existing layer 2 block'); + logger.warn('Attempted to replay existing layer 2 block. This is expected if we are syncing'); + return true; } /** diff --git a/nightfall-client/src/services/state-sync.mjs b/nightfall-client/src/services/state-sync.mjs index a398aa4fb..a05d0c67e 100644 --- a/nightfall-client/src/services/state-sync.mjs +++ b/nightfall-client/src/services/state-sync.mjs @@ -5,21 +5,20 @@ their local commitments databsae. import config from 'config'; import logger from 'common-files/utils/logger.mjs'; -import { getContractInstance } from 'common-files/utils/contract.mjs'; import mongo from 'common-files/utils/mongo.mjs'; +import { waitForContract } from '../event-handlers/subscribe.mjs'; import blockProposedEventHandler from '../event-handlers/block-proposed.mjs'; import rollbackEventHandler from '../event-handlers/rollback.mjs'; const { MONGO_URL, COMMITMENTS_DB, COMMITMENTS_COLLECTION, STATE_CONTRACT_NAME } = config; const syncState = async (fromBlock = 'earliest', toBlock = 'latest', eventFilter = 'allEvents') => { - const stateContractInstance = await getContractInstance(STATE_CONTRACT_NAME); // Rollback, BlockProposed + const stateContractInstance = await waitForContract(STATE_CONTRACT_NAME); // Rollback, BlockProposed const pastStateEvents = await stateContractInstance.getPastEvents(eventFilter, { fromBlock, toBlock, }); - logger.info(`pastStateEvents: ${JSON.stringify(pastStateEvents)}`); for (let i = 0; i < pastStateEvents.length; i++) { switch (pastStateEvents[i].event) { @@ -46,8 +45,6 @@ const genGetCommitments = async (query = {}, proj = {}) => { // eslint-disable-next-line import/prefer-default-export export const initialClientSync = async () => { const allCommitments = await genGetCommitments(); - if (allCommitments.length === 0) return {}; - const commitmentBlockNumbers = allCommitments.map(a => a.blockNumber).filter(n => n >= 0); logger.info(`commitmentBlockNumbers: ${commitmentBlockNumbers}`); const firstSeenBlockNumber = Math.min(...commitmentBlockNumbers); diff --git a/nightfall-deployer/src/index.mjs b/nightfall-deployer/src/index.mjs index f3567324d..615789e80 100644 --- a/nightfall-deployer/src/index.mjs +++ b/nightfall-deployer/src/index.mjs @@ -1,4 +1,5 @@ import Web3 from 'common-files/utils/web3.mjs'; +import logger from 'common-files/utils/logger.mjs'; import circuits from './circuit-setup.mjs'; import setupContracts from './contract-setup.mjs'; @@ -6,7 +7,15 @@ import setupContracts from './contract-setup.mjs'; async function main() { await circuits.waitForZokrates(); await circuits.setupCircuits(); - await setupContracts(); + try { + await setupContracts(); + } catch (err) { + if (err.message.includes('Transaction has been reverted by the EVM')) + logger.warn( + 'Writing contract addresses to the State contract failed. This is probably because they are aready set. Did you already run deployer?', + ); + else throw new Error(err); + } Web3.disconnect(); } diff --git a/nightfall-optimist/src/event-handlers/subscribe.mjs b/nightfall-optimist/src/event-handlers/subscribe.mjs index a98abd127..d4257bb7b 100644 --- a/nightfall-optimist/src/event-handlers/subscribe.mjs +++ b/nightfall-optimist/src/event-handlers/subscribe.mjs @@ -32,7 +32,6 @@ export async function waitForContract(contractName) { try { error = undefined; const address = await getContractAddress(contractName); - logger.debug(`${contractName} contract address is ${address}`); if (address === undefined) throw new Error(`${contractName} contract address was undefined`); instance = getContractInstance(contractName, address); return instance; diff --git a/nightfall-optimist/src/index.mjs b/nightfall-optimist/src/index.mjs index fc4544c84..d143ce784 100644 --- a/nightfall-optimist/src/index.mjs +++ b/nightfall-optimist/src/index.mjs @@ -17,11 +17,13 @@ import { 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'; import { setBlockProposedWebSocketConnection } from './event-handlers/block-proposed.mjs'; const main = async () => { try { const proposer = new Proposer(); + setProposer(proposer); // passes the proposer instance int the proposer routes // subscribe to WebSocket events first await subscribeToBlockAssembledWebSocketConnection(setBlockAssembledWebSocketConnection); await subscribeToChallengeWebSocketConnection(setChallengeWebSocketConnection); diff --git a/nightfall-optimist/src/routes/proposer.mjs b/nightfall-optimist/src/routes/proposer.mjs index 3ccf0daec..e6d5fe058 100644 --- a/nightfall-optimist/src/routes/proposer.mjs +++ b/nightfall-optimist/src/routes/proposer.mjs @@ -8,10 +8,12 @@ import config from 'config'; import Timber from 'common-files/classes/timber.mjs'; import logger from 'common-files/utils/logger.mjs'; import { getContractInstance } from 'common-files/utils/contract.mjs'; +import { enqueueEvent } from 'common-files/utils/event-queue.mjs'; import Block from '../classes/block.mjs'; import { Transaction, TransactionError } from '../classes/index.mjs'; import { setRegisteredProposerAddress, + isRegisteredProposerAddressMine, deleteRegisteredProposerAddress, getMempoolTransactions, getLatestTree, @@ -19,10 +21,16 @@ import { } from '../services/database.mjs'; import { waitForContract } from '../event-handlers/subscribe.mjs'; import transactionSubmittedEventHandler from '../event-handlers/transaction-submitted.mjs'; +import getProposers from '../services/proposer.mjs'; const router = express.Router(); const { STATE_CONTRACT_NAME, PROPOSERS_CONTRACT_NAME, SHIELD_CONTRACT_NAME, ZERO } = config; +let proposer; +export function setProposer(p) { + proposer = p; +} + /** * Function to return a raw transaction that registers a proposer. This just * provides the tx data, the user will need to append the registration bond @@ -33,12 +41,39 @@ router.post('/register', async (req, res, next) => { logger.debug(`register proposer endpoint received POST ${JSON.stringify(req.body, null, 2)}`); try { const { address, url = '' } = req.body; - const proposersContractInstance = await getContractInstance(PROPOSERS_CONTRACT_NAME); - const txDataToSign = await proposersContractInstance.methods.registerProposer(url).encodeABI(); - logger.debug('returning raw transaction data'); - logger.silly(`raw transaction is ${JSON.stringify(txDataToSign, null, 2)}`); + const proposersContractInstance = await waitForContract(PROPOSERS_CONTRACT_NAME); + // the first thing to do is to check if the proposer is already registered on the blockchain + const proposers = (await getProposers()).map(p => p.thisAddress); + // if not, let's register it + let txDataToSign = ''; + if (!proposers.includes(address)) { + txDataToSign = await proposersContractInstance.methods.registerProposer(url).encodeABI(); + } else + logger.warn( + 'Proposer was already registered on the blockchain - registration attempt ignored', + ); + // when we get to here, either the proposer was already registered (txDataToSign === '') + // or we're just about to register them. We may or may not be registed locally + // with optimist though. Let's check and fix that if needed. + if (!(await isRegisteredProposerAddressMine(address))) { + logger.debug('Registering proposer locally'); + await setRegisteredProposerAddress(address, url); // save the registration address + // We've just registered with optimist but if we were already registered on the blockchain, + // we should check if we're the current proposer and, if so, set things up so we start + // making blocks immediately + if (txDataToSign === '') { + logger.warn( + 'Proposer was already registered on the blockchain but not with this Optimist instance - registering locally', + ); + const stateContractInstance = await waitForContract(STATE_CONTRACT_NAME); + const currentProposer = await stateContractInstance.methods.getCurrentProposer().call(); + if (address === currentProposer.thisAddress) { + proposer.isMe = true; + await enqueueEvent(() => logger.info('Start Queue'), 0); // kickstart the queue + } + } + } res.json({ txDataToSign }); - setRegisteredProposerAddress(address, url); // save the registration address and URL } catch (err) { logger.error(err); next(err); @@ -93,22 +128,8 @@ router.get('/current-proposer', async (req, res, next) => { router.get('/proposers', async (req, res, next) => { logger.debug(`list proposals endpoint received GET`); try { - const stateContractInstance = await getContractInstance(STATE_CONTRACT_NAME); - // proposers is an on-chain mapping so to get proposers we need to key to start iterating - // the safest to start with is the currentProposer - const currentProposer = await stateContractInstance.methods.currentProposer().call(); - const proposers = []; - let thisPtr = currentProposer.thisAddress; - // Loop through the circular list until we run back into the currentProposer. - do { - // eslint-disable-next-line no-await-in-loop - const proposer = await stateContractInstance.methods.proposers(thisPtr).call(); - proposers.push(proposer); - thisPtr = proposer.nextAddress; - } while (thisPtr !== currentProposer.thisAddress); - - logger.debug('returning raw transaction data'); - logger.silly(`raw transaction is ${JSON.stringify(proposers, null, 2)}`); + const proposers = await getProposers(); + logger.debug(`Returning proposer list of length ${proposers.length}`); res.json({ proposers }); } catch (err) { logger.error(err); @@ -203,14 +224,14 @@ router.post('/propose', async (req, res, next) => { logger.debug(`propose endpoint received POST`); logger.silly(`With content ${JSON.stringify(req.body, null, 2)}`); try { - const { transactions, proposer, currentLeafCount } = req.body; + const { transactions, proposer: prop, currentLeafCount } = req.body; const latestBlockInfo = await getLatestBlockInfo(); const latestTree = await getLatestTree(); // use the information we've been POSTED to assemble a block // we use a Builder pattern because an async constructor is bad form const { block } = await Block.build({ transactions, - proposer, + proposer: prop, currentLeafCount, latestBlockInfo: { blockNumberL2: latestBlockInfo.blockNumberL2, diff --git a/nightfall-optimist/src/services/database.mjs b/nightfall-optimist/src/services/database.mjs index 8abc2b98c..778586fec 100644 --- a/nightfall-optimist/src/services/database.mjs +++ b/nightfall-optimist/src/services/database.mjs @@ -12,7 +12,8 @@ const { MONGO_URL, OPTIMIST_DB, TRANSACTIONS_COLLECTION, - METADATA_COLLECTION, + PROPOSER_COLLECTION, + CHALLENGER_COLLECTION, SUBMITTED_BLOCKS_COLLECTION, NULLIFIER_COLLECTION, COMMIT_COLLECTION, @@ -54,7 +55,7 @@ export async function addChallengerAddress(address) { const db = connection.db(OPTIMIST_DB); logger.debug(`Saving challenger address ${address}`); const data = { challenger: address }; - return db.collection(METADATA_COLLECTION).insertOne(data); + return db.collection(CHALLENGER_COLLECTION).insertOne(data); } /** @@ -67,7 +68,7 @@ export async function removeChallengerAddress(address) { const db = connection.db(OPTIMIST_DB); logger.debug(`Removing challenger address ${address}`); const data = { challenger: address }; - return db.collection(METADATA_COLLECTION).deleteOne(data); + return db.collection(CHALLENGER_COLLECTION).deleteOne(data); } /** @@ -76,7 +77,7 @@ Function to tell us if an address used to commit to a challenge belongs to us export async function isChallengerAddressMine(address) { const connection = await mongo.connection(MONGO_URL); const db = connection.db(OPTIMIST_DB); - const metadata = await db.collection(METADATA_COLLECTION).findOne({ challenger: address }); + const metadata = await db.collection(CHALLENGER_COLLECTION).findOne({ challenger: address }); return metadata !== null; } @@ -217,15 +218,10 @@ event to determine who is the current proposer. export async function setRegisteredProposerAddress(address, url) { const connection = await mongo.connection(MONGO_URL); const db = connection.db(OPTIMIST_DB); - logger.debug(`Saving proposer address ${address} and url ${url}`); - const data = { - proposer: { - address, - url, - }, - }; - const update = { $set: { 'proposer.url': url } }; - return db.collection(METADATA_COLLECTION).updateOne(data, update, { upsert: true }); + logger.debug(`Saving proposer address ${address}`); + const data = { _id: address }; + const update = { $set: { url } }; + return db.collection(PROPOSER_COLLECTION).updateOne(data, update, { upsert: true }); } /** @@ -235,8 +231,7 @@ thus it should start assembling blocks of transactions. export async function isRegisteredProposerAddressMine(address) { const connection = await mongo.connection(MONGO_URL); const db = connection.db(OPTIMIST_DB); - const query = { 'proposer.address': address }; - const metadata = await db.collection(METADATA_COLLECTION).findOne(query); + const metadata = await db.collection(PROPOSER_COLLECTION).findOne({ _id: address }); logger.silly(`found registered proposer ${JSON.stringify(metadata, null, 2)}`); return metadata; } @@ -247,10 +242,10 @@ export async function isRegisteredProposerAddressMine(address) { export async function deleteRegisteredProposerAddress(address) { const connection = await mongo.connection(MONGO_URL); const db = connection.db(OPTIMIST_DB); - const query = { 'proposer.address': address }; - const foundProposer = !!(await db.collection(METADATA_COLLECTION).findOne(query)); + const query = { _id: address }; + const foundProposer = !!(await db.collection(PROPOSER_COLLECTION).findOne(query)); if (foundProposer) { - await db.collection(METADATA_COLLECTION).deleteOne(query); + await db.collection(PROPOSER_COLLECTION).deleteOne(query); } logger.silly(`deleted registered proposer`); } diff --git a/nightfall-optimist/src/services/proposer.mjs b/nightfall-optimist/src/services/proposer.mjs new file mode 100644 index 000000000..d91ceb709 --- /dev/null +++ b/nightfall-optimist/src/services/proposer.mjs @@ -0,0 +1,26 @@ +/** +Module containing functions relating to proposers and their registration +*/ +import config from 'config'; +import { waitForContract } from 'common-files/utils/contract.mjs'; + +const { STATE_CONTRACT_NAME } = config; + +async function getProposers() { + const stateContractInstance = await waitForContract(STATE_CONTRACT_NAME); + // proposers is an on-chain mapping so to get proposers we need to key to start iterating + // the safest to start with is the currentProposer + const currentProposer = await stateContractInstance.methods.currentProposer().call(); + const proposers = []; + let thisPtr = currentProposer.thisAddress; + // Loop through the circular list until we run back into the currentProposer. + do { + // eslint-disable-next-line no-await-in-loop + const prop = await stateContractInstance.methods.proposers(thisPtr).call(); + proposers.push(prop); + thisPtr = prop.nextAddress; + } while (thisPtr !== currentProposer.thisAddress); + return proposers; +} + +export default getProposers; diff --git a/package.json b/package.json index 3a2e99139..9dbb4a460 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "Optimistic Nightfall", "main": "./nightfall-client/src/index.mjs", "scripts": { + "test": "LOG_LEVEL=error mocha --timeout 0 --bail --exit test/e2e/protocol/*.test.mjs test/e2e/tokens/*.test.mjs", "neg-test": "mocha --timeout 0 --bail --exit test/neg-http.mjs", "neg-test-ropsten": "mocha --timeout 0 --bail --exit test/neg-http.mjs", "test-e2e-protocol": "LOG_LEVEL=debug mocha --timeout 0 --bail --exit test/e2e/protocol/*.test.mjs ", diff --git a/test/e2e/protocol/proposer.test.mjs b/test/e2e/protocol/proposer.test.mjs index 0706d24af..d9e18d6b0 100644 --- a/test/e2e/protocol/proposer.test.mjs +++ b/test/e2e/protocol/proposer.test.mjs @@ -116,14 +116,9 @@ describe('Basic Proposer tests', () => { }); it('should fail to register a proposer twice', async () => { - try { - const res = await bootProposer.registerProposer(testProposersUrl[2]); - expectTransaction(res); - - expect.fail('Submitting the same proposer registration should have caused an EVM revert'); - } catch (err) { - expect(err.message).to.include('Transaction has been reverted by the EVM'); - } + const res = await bootProposer.registerProposer(testProposersUrl[2]); + // eslint-disable-next-line @babel/no-unused-expressions + expect(res).to.be.false; }); it('should unregister the boot proposer', async () => { diff --git a/test/ping-pong/pong-down b/test/ping-pong/pong-down index f7983b637..1219ad509 100755 --- a/test/ping-pong/pong-down +++ b/test/ping-pong/pong-down @@ -13,12 +13,8 @@ usage() while [ -n "$1" ]; do case $1 in -v | --volumes ) if [[ $(echo $VOLUME_LIST | grep ping-pong_proving_files) ]]; then - echo -n 'Removing ' - docker volume rm ping-pong_proving_files - fi - if [[ $(echo $VOLUME_LIST | grep ping-pong_mongodb) ]]; then echo -n 'Removing ' - docker volume rm ping-pong_mongodb + docker volume rm ping-pong_proving_files fi if [[ $(echo $VOLUME_LIST | grep ping-pong_build) ]]; then echo -n 'Removing ' @@ -34,6 +30,11 @@ while [ -n "$1" ]; do shift done +if [[ $(echo $VOLUME_LIST | grep ping-pong_mongodb) ]]; then + echo -n 'Removing ' + docker volume rm ping-pong_mongodb +fi + DIR=./common-files/node_modules if [[ -d "$DIR" ]]; then rm -dr common-files/node_modules diff --git a/test/ping-pong/proposer/src/index.mjs b/test/ping-pong/proposer/src/index.mjs index 49ceeaf08..7f520fb02 100644 --- a/test/ping-pong/proposer/src/index.mjs +++ b/test/ping-pong/proposer/src/index.mjs @@ -22,17 +22,8 @@ async function startProposer() { if (await nf3.healthcheck('optimist')) logger.info('Healthcheck passed'); else throw new Error('Healthcheck failed'); logger.info('Attempting to register proposer'); - // let's see if the proposer has been registered before const proposerUrl = PROPOSER_PORT !== '' ? `${PROPOSER_URL}:${PROPOSER_PORT}` : ''; - const { proposers } = await nf3.getProposers(); - // if not, let's register them - if (proposers.length === 0) { - await nf3.registerProposer(proposerUrl); - logger.info('Proposer registration complete'); - } else if (!proposers.map(p => p.thisAddress).includes(nf3.ethereumAddress)) { - await nf3.registerProposer(proposerUrl); - logger.info('Proposer registration complete'); - } else logger.warn('Proposer appears to be registerd already'); + await nf3.registerProposer(proposerUrl); if (PROPOSER_PORT !== '') { logger.debug('Proposer healthcheck up'); app.listen(PROPOSER_PORT);