diff --git a/bin/start-apps b/bin/start-apps index aac56231e..ec4f075f6 100755 --- a/bin/start-apps +++ b/bin/start-apps @@ -1,2 +1,2 @@ #! /bin/bash -docker-compose -f docker/docker-compose.apps.yml -p 'nightfall_3' up proposer +docker-compose -f docker/docker-compose.apps.yml -p 'nightfall_3' up diff --git a/config/default.js b/config/default.js index 8052d7df3..50d59316c 100644 --- a/config/default.js +++ b/config/default.js @@ -816,4 +816,5 @@ module.exports = { WEBHOOK_PATH: process.env.WEBHOOK_PATH, // For posting optional layer 2 transaction finalization details WEBHOOK_SIGNING_KEY: process.env.WEBHOOK_SIGNING_KEY, }, + MEMPOOL_TXS_FETCH_LIMIT: Number(process.env.MEMPOOL_TXS_FETCH_LIMIT), }; diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index b1772926a..8b5798fb2 100755 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -37,10 +37,10 @@ services: ports: - 9239:9229 command: ['npm', 'run', 'dev'] - environment: - - CALL_WEBHOOK_ON_CONFIRMATION=true - - WEBHOOK_PATH=http://host.docker.internal:3099/zk-tx-response - - WEBHOOK_SIGNING_KEY=0x720117fdc01793b32217d32622916babc66350c4bb60905336ec8b29b1f16757 + # environment: + # - CALL_WEBHOOK_ON_CONFIRMATION=true + # - WEBHOOK_PATH=http://host.docker.internal:3099/zk-tx-response + # - WEBHOOK_SIGNING_KEY=0x720117fdc01793b32217d32622916babc66350c4bb60905336ec8b29b1f16757 deployer: build: @@ -63,11 +63,11 @@ services: source: ../config/default.js target: /app/config/default.js - type: bind - source: ../nightfall-deployer/migrations - target: /app/migrations + source: ../nightfall-deployer/scripts + target: /app/scripts - type: bind - source: ../nightfall-deployer/truffle-config.js - target: /app/truffle-config.js + source: ../nightfall-deployer/hardhat.config.js + target: /app/hardhat.config.js - type: bind source: ../nightfall-deployer/entrypoint.sh target: /app/entrypoint.sh @@ -91,6 +91,8 @@ services: - type: bind source: ../config/default.js target: /app/config/default.js + # environment: + # MEMPOOL_TXS_FETCH_LIMIT: 5 ports: - 9229:9229 command: ['npm', 'run', 'dev'] diff --git a/nightfall-client/src/app.mjs b/nightfall-client/src/app.mjs index ec8006d89..993b5ca4a 100644 --- a/nightfall-client/src/app.mjs +++ b/nightfall-client/src/app.mjs @@ -23,11 +23,12 @@ import { const app = express(); -// Add check for syncing state. If it is in syncing state, just respond 400 +// Add check for syncing state. If it is in syncing state, just respond 503 app.use((req, res, next) => { + if (req.path === '/healthcheck') return next(); + if (req.app.get('isSyncing')) { - res.sendStatus(400); - return res.status; + return res.status(503).send('Nightfall3 Client is syncing'); } return next(); }); diff --git a/nightfall-client/src/index.mjs b/nightfall-client/src/index.mjs index d1383119a..ada38d5ef 100644 --- a/nightfall-client/src/index.mjs +++ b/nightfall-client/src/index.mjs @@ -1,7 +1,7 @@ import config from 'config'; import logger from 'common-files/utils/logger.mjs'; import mongo from 'common-files/utils/mongo.mjs'; -import { queueManager, pauseQueue, unpauseQueue } from 'common-files/utils/event-queue.mjs'; +import { queueManager, pauseQueue } from 'common-files/utils/event-queue.mjs'; import { checkContractsABI } from 'common-files/utils/sync-files.mjs'; import app from './app.mjs'; import rabbitmq from './utils/rabbitmq.mjs'; @@ -27,10 +27,9 @@ const main = async () => { await checkContractsABI(); await startEventQueue(queueManager, eventHandlers); await pauseQueue(0); - initialClientSync().then(() => { - app.set('isSyncing', false); - unpauseQueue(0); - }); + await initialClientSync(); + app.set('isSyncing', false); + logger.info('Syncing complete, queues unpaused'); } catch (err) { logger.error(err); process.exit(1); diff --git a/nightfall-client/src/services/state-sync.mjs b/nightfall-client/src/services/state-sync.mjs index 48421beb3..8ee813f38 100644 --- a/nightfall-client/src/services/state-sync.mjs +++ b/nightfall-client/src/services/state-sync.mjs @@ -67,20 +67,18 @@ const genGetCommitments = async (query = {}, proj = {}) => { // eslint-disable-next-line import/prefer-default-export export const initialClientSync = async () => { - const allCommitments = await genGetCommitments(); - const commitmentBlockNumbers = allCommitments.map(a => a.blockNumber).filter(n => n >= 0); - - logger.info(`commitmentBlockNumbers: ${commitmentBlockNumbers}`); - - const firstSeenBlockNumber = Math.min(...commitmentBlockNumbers); - - logger.info(`firstSeenBlockNumber: ${firstSeenBlockNumber}`); - - // fistSeenBlockNumber can be infinity if the commitmentBlockNumbers array is empty - if (firstSeenBlockNumber === Infinity) { + const allCommitments = await genGetCommitments({ isOnChain: { $ne: '-1' } }); + if (allCommitments?.length < 1) { + logger.info(`No existing commitments to sync, starting from L1 block ${STATE_GENESIS_BLOCK}`); await syncState(STATE_GENESIS_BLOCK); } else { - await syncState(firstSeenBlockNumber); + const commitmentBlockNumbers = allCommitments.map(a => a.blockNumber).filter(n => n >= 0); + logger.info(`commitmentBlockNumbers: ${commitmentBlockNumbers}`); + + const lastProcessedBlockNumber = Math.max(...commitmentBlockNumbers); + logger.info(`lastProcessedBlockNumber: ${lastProcessedBlockNumber}`); + + await syncState(lastProcessedBlockNumber); } unpauseQueue(0); // the queues are paused to start with, so get them going once we are synced diff --git a/nightfall-deployer/contracts/Proposers.sol b/nightfall-deployer/contracts/Proposers.sol index 8b844afc1..b4ce2bd5f 100644 --- a/nightfall-deployer/contracts/Proposers.sol +++ b/nightfall-deployer/contracts/Proposers.sol @@ -9,7 +9,6 @@ import './Config.sol'; import './Utils.sol'; import './Stateful.sol'; import './Certified.sol'; -import "hardhat/console.sol"; contract Proposers is Stateful, Config, ReentrancyGuardUpgradeable, Certified { function initialize() public override(Stateful, Config, Certified) initializer { @@ -22,11 +21,12 @@ contract Proposers is Stateful, Config, ReentrancyGuardUpgradeable, Certified { /** @dev register proposer with stake */ - function registerProposer( - string calldata url, - uint256 fee - ) external payable nonReentrant onlyCertified { - console.log("registering proposer ", url); + function registerProposer(string calldata url, uint256 fee) + external + payable + nonReentrant + onlyCertified + { require( state.numProposers() < maxProposers, 'Proposers: Max number of registered proposers' @@ -43,7 +43,6 @@ contract Proposers is Stateful, Config, ReentrancyGuardUpgradeable, Certified { (bool success, ) = payable(address(state)).call{value: msg.value}(''); require(success, 'Proposers: Transfer failed.'); state.setStakeAccount(msg.sender, stake.amount, stake.challengeLocked); - console.log("Sent stake to state contract"); LinkedAddress memory currentProposer = state.getCurrentProposer(); // cope with this being the first proposer @@ -53,7 +52,6 @@ contract Proposers is Stateful, Config, ReentrancyGuardUpgradeable, Certified { state.setProposerStartBlock(block.number); emit NewCurrentProposer(currentProposer.thisAddress); } else { - console.log("This is a new proposer"); // only if it's not a proposer yet if (state.getProposer(msg.sender).thisAddress == address(0)) { // else, splice the new proposer into the circular linked list of proposers just behind the current proposer @@ -88,7 +86,6 @@ contract Proposers is Stateful, Config, ReentrancyGuardUpgradeable, Certified { state.setProposer(msg.sender, proposer); } } - console.log("Current proposer: ", currentProposer.thisAddress); state.setCurrentProposer(currentProposer.thisAddress); state.setNumProposers(state.numProposers() + 1); } @@ -123,10 +120,12 @@ contract Proposers is Stateful, Config, ReentrancyGuardUpgradeable, Certified { } // Proposers can change REST API URL or increment stake - function updateProposer( - string calldata url, - uint256 fee - ) external payable nonReentrant onlyCertified { + function updateProposer(string calldata url, uint256 fee) + external + payable + nonReentrant + onlyCertified + { require( state.getProposer(msg.sender).thisAddress != address(0), 'Proposers: This proposer is not registered or you are not that proposer' diff --git a/nightfall-deployer/truffle-config.js b/nightfall-deployer/truffle-config.js deleted file mode 100644 index e88b4986d..000000000 --- a/nightfall-deployer/truffle-config.js +++ /dev/null @@ -1,181 +0,0 @@ -/** - * Use this file to configure your truffle project. It's seeded with some - * common settings for different networks and features like migrations, - * compilation and testing. Uncomment the ones you need or modify - * them to suit your project as necessary. - * - * More information about configuration can be found at: - * - * truffleframework.com/docs/advanced/configuration - * - * To deploy via Infura you'll need a wallet provider (like @truffle/hdwallet-provider) - * to sign your transactions before they're sent to a remote public node. Infura accounts - * are available for free at: infura.io/register. - * - * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate - * public/private key pairs. If you're publishing your code to GitHub make sure you load this - * phrase from a file you've .gitignored so it doesn't accidentally become public. - * - */ - -// eslint-disable-next-line import/no-extraneous-dependencies -const HDWalletProvider = require('@truffle/hdwallet-provider'); -const config = require('config'); - -module.exports = { - /** - * Networks define how you connect to your ethereum client and let you set the - * defaults web3 uses to send transactions. If you don't specify one truffle - * will spin up a development blockchain for you on port 9545 when you - * run `develop` or `test`. You can ask a truffle command to use a specific - * network from the command line, e.g - * - * $ truffle test --network - */ - - networks: { - // Useful for testing. The `development` name is special - truffle uses it by default - // if it's defined here and no other network is specified at the command line. - // You should run a client (like ganache-cli, geth or parity) in a separate terminal - // tab if you use this network and you must also set the `host`, `port` and `network_id` - // options below to some value. - - blockchain: { - url: 'ws://blockchain:8546', - network_id: 1337, // Any network (default: none) - gas: 1000000000, - websockets: true, - }, - - blockchain2: { - url: 'ws://blockchain2:8546', - network_id: 1337, // Any network (default: none) - gas: 8000000, - websockets: true, - }, - - ganache: { - host: 'ganache', // Localhost (default: none) - port: 8545, // Standard Ethereum port (default: none) - network_id: '*', // Any network (default: none) - gas: 8000000, - }, - - development: { - url: 'ws://host.docker.internal:8546', - network_id: 1337, // Any network (default: none) - gas: 8000000, - websockets: true, - }, - - localhost: { - provider: () => new HDWalletProvider(config.ETH_PRIVATE_KEY, config.BLOCKCHAIN_URL), - network_id: 1337, // Any network (default: none) - networkCheckTimeout: 1000000000, - timeoutBlocks: 2000, - gas: 6721975, - websockets: true, - }, - - staging: { - url: config.BLOCKCHAIN_URL, - network_id: 1337, // Any network (default: none) - gas: 8000000, - websockets: true, - networkCheckTimeout: 1000000000, - }, - - staging_edge: { - provider: () => - new HDWalletProvider({ - privateKeys: [config.ETH_PRIVATE_KEY], - providerOrUrl: config.BLOCKCHAIN_URL, - }), - network_id: 100, // Any network (default: none) - gas: 8000000, - gasPrice: config.WEB3_OPTIONS.gasPrice, - websockets: true, - networkCheckTimeout: 1000000000, - timeoutBlocks: 2000, - }, - mumbai: { - provider: () => - new HDWalletProvider({ - privateKeys: [config.ETH_PRIVATE_KEY], - providerOrUrl: config.BLOCKCHAIN_URL, - chainId: 80001, - pollingInterval: 180000, - }), - network_id: 80001, - networkCheckTimeout: 1000000000, - timeoutBlocks: 2000, - skipDryRun: true, - websockets: true, - gasPrice: config.WEB3_OPTIONS.gasPrice, - gas: config.WEB3_OPTIONS.gas, - disableConfirmationListener: true, - pollingInterval: 180000, - }, - amoy: { - provider: () => - new HDWalletProvider({ - privateKeys: [config.ETH_PRIVATE_KEY], - providerOrUrl: config.BLOCKCHAIN_URL, - chainId: 80002, - pollingInterval: 180000, - }), - network_id: 80002, - networkCheckTimeout: 1000000000, - timeoutBlocks: 2000, - skipDryRun: true, - websockets: true, - gasPrice: config.WEB3_OPTIONS.gasPrice, - gas: config.WEB3_OPTIONS.gas, - disableConfirmationListener: true, - pollingInterval: 180000, - }, - polygonPos: { - provider: () => - new HDWalletProvider({ - privateKeys: [config.ETH_PRIVATE_KEY], - providerOrUrl: config.BLOCKCHAIN_URL, - chainId: 137, - }), - network_id: 137, - networkCheckTimeout: 1000000000, - timeoutBlocks: 2000, - skipDryRun: true, - websockets: true, - gasPrice: config.WEB3_OPTIONS.gasPrice, - gas: config.WEB3_OPTIONS.gas, - }, - mainnet: { - provider: () => new HDWalletProvider(config.ETH_PRIVATE_KEY, config.BLOCKCHAIN_URL), - network_id: 1, - networkCheckTimeout: 1000000000, - timeoutBlocks: 2000, - skipDryRun: true, - websockets: true, - gasPrice: config.WEB3_OPTIONS.gasPrice, - gas: config.WEB3_OPTIONS.gas, - }, - }, - - // Set default mocha options here, use special reporters etc. - mocha: {}, - - // Configure your compilers - compilers: { - solc: { - version: '0.8.17', // Fetch exact version from solc-bin (default: truffle's version) - settings: { - // See the solidity docs for advice about optimization and evmVersion - optimizer: { - enabled: true, - runs: 1, - }, - }, - }, - }, - plugins: ['truffle-contract-size'], -}; diff --git a/nightfall-optimist/src/services/block-assembler.mjs b/nightfall-optimist/src/services/block-assembler.mjs index 520b6b2a7..78c6690a3 100644 --- a/nightfall-optimist/src/services/block-assembler.mjs +++ b/nightfall-optimist/src/services/block-assembler.mjs @@ -19,7 +19,12 @@ import { increaseProposerBlockNotSent, } from './debug-counters.mjs'; -const { MAX_BLOCK_SIZE, MINIMUM_TRANSACTION_SLOTS, PROPOSER_MAX_BLOCK_PERIOD_MILIS } = config; +const { + MAX_BLOCK_SIZE, + MINIMUM_TRANSACTION_SLOTS, + PROPOSER_MAX_BLOCK_PERIOD_MILIS, + MEMPOOL_TXS_FETCH_LIMIT, +} = config; const { STATE_CONTRACT_NAME } = constants; let ws; @@ -117,8 +122,12 @@ export async function conditionalMakeBlock(proposer) { const currentTime = new Date().getTime(); if (enableHeartBeatLogging) { + let hbMsg = 'In the mempool there are the following number of transactions'; + if (MEMPOOL_TXS_FETCH_LIMIT) { + hbMsg = 'In this throttled mempool batch there are the following number of transactions'; + } logger.info({ - msg: 'In the mempool there are the following number of transactions', + msg: hbMsg, numberTransactions: mempoolTransactions.length, totalBytes, }); diff --git a/nightfall-optimist/src/services/database.mjs b/nightfall-optimist/src/services/database.mjs index 5b3ebaa1c..bfa2f4761 100644 --- a/nightfall-optimist/src/services/database.mjs +++ b/nightfall-optimist/src/services/database.mjs @@ -22,6 +22,7 @@ const { TIMBER_COLLECTION, TIMBER_HEIGHT, HASH_TYPE, + MEMPOOL_TXS_FETCH_LIMIT, } = config; /** @@ -370,11 +371,17 @@ export async function getMempoolTransactions() { export async function getMempoolTransactionsSortedByFee() { const connection = await mongo.connection(MONGO_URL); const db = connection.db(OPTIMIST_DB); - return db + let query = db .collection(TRANSACTIONS_COLLECTION) .find({ mempool: true }, { _id: 0 }) - .sort({ fee: -1 }) - .toArray(); + .sort({ fee: -1 }); + + if (MEMPOOL_TXS_FETCH_LIMIT) { + logger.info(`Throttling enabled for pending mempool transactions: ${MEMPOOL_TXS_FETCH_LIMIT}`); + query = query.limit(MEMPOOL_TXS_FETCH_LIMIT); + } + + return query.toArray(); } /**