diff --git a/.eslintignore b/.eslintignore index 6d3b75fc7..edf47ed29 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,6 +3,8 @@ node_modules/* **/migrations/* **/doc/* mumbai/ +polygonpos/ +proving_files/ cli/build/* wallet/cli/* mumbai/ diff --git a/.gitignore b/.gitignore index f6620bf5f..1fe154dde 100644 --- a/.gitignore +++ b/.gitignore @@ -7,8 +7,10 @@ mumbai/ .*swp .vscode/* .openzeppelin/ +proving_files/ *.log mumbai/ +polygonpos/ # wallet wallet/build wallet/cli diff --git a/apps/proposer/src/proposer.mjs b/apps/proposer/src/proposer.mjs index a9666c44e..3c76118be 100644 --- a/apps/proposer/src/proposer.mjs +++ b/apps/proposer/src/proposer.mjs @@ -76,7 +76,9 @@ async function checkAndRegisterProposer(nf3, proposerBaseUrl) { try { await nf3.registerProposer(proposerBaseUrl, minimumStake); } catch (err) { - logger.info(err); + logger.info( + `Error registering proposer ${proposerBaseUrl} with error message ${err.message}`, + ); } } diff --git a/apps/proposer/src/routes/proposer.mjs b/apps/proposer/src/routes/proposer.mjs index e2e5a0bfa..6d944253b 100644 --- a/apps/proposer/src/routes/proposer.mjs +++ b/apps/proposer/src/routes/proposer.mjs @@ -14,8 +14,7 @@ const router = express.Router(); router.post('/offchain-transaction', async (req, res, next) => { const nf3 = req.app.get('nf3'); - const { transaction } = req.body; - + const { transaction, signature } = req.body; if (!transaction) { res.sendStatus(404); return; @@ -35,8 +34,8 @@ router.post('/offchain-transaction', async (req, res, next) => { } try { - await nf3.sendOffchainTransaction(transaction); - res.sendStatus(200); + const res2 = await nf3.forwardOffchainTransaction(transaction, signature); + res.sendStatus(res2.status); } catch (error) { next(error); } diff --git a/bin/polygonpos-deployment.env b/bin/polygonpos-deployment.env index 3c855981c..dd38dd564 100755 --- a/bin/polygonpos-deployment.env +++ b/bin/polygonpos-deployment.env @@ -7,9 +7,9 @@ MULTISIG_APPROVERS='0x0000000000000000000000000000000000000001,0x000000000000000 WHITELISTING=enable NF_SERVICES_TO_START='deployer,worker' DEPLOY_MOCK_TOKENS=false -ENVIRONMENT=mumbai +ENVIRONMENT=polygonPos FEE_L2_TOKEN_ID=WMATIC -DEPLOY_MOCKED_SANCTIONS_CONTRACT=true -GAS_PRICE=500000000000 +DEPLOY_MOCKED_SANCTIONS_CONTRACT=false +GAS_PRICE=400000000000 RESTRICT_TOKENS=disable set +o allexport \ No newline at end of file diff --git a/cli/lib/nf3.mjs b/cli/lib/nf3.mjs index a7debd70b..37138f223 100644 --- a/cli/lib/nf3.mjs +++ b/cli/lib/nf3.mjs @@ -108,6 +108,8 @@ class Nf3 { clientAuthenticationKey; + NEXT_N_PROPOSERS = 3; + // min fee or reward one should hold for withdaw // in State contract. minL1Balance = DEFAULT_MIN_L1_WITHDRAW; @@ -534,7 +536,7 @@ class Nf3 { if (res.data.error) { throw new Error(res.data.error); } - return res.status; + return this.sendOffchainTransaction(res.data.transaction); } /** @@ -568,7 +570,7 @@ class Nf3 { if (res.data.error) { throw new Error(res.data.error); } - return res.status; + return this.sendOffchainTransaction(res.data.transaction); } /** @@ -604,7 +606,7 @@ class Nf3 { if (res.data.error && res.data.error === 'No suitable commitments') { throw new Error('No suitable commitments'); } - return res.status; + return this.sendOffchainTransaction(res.data.transaction); } /** @@ -731,23 +733,21 @@ class Nf3 { if (res.data.error) { throw new Error(res.data.error); } - if (!offchain) { - return new Promise((resolve, reject) => { - userQueue.push(async () => { - try { - const receipt = await this.submitTransaction( - res.data.txDataToSign, - this.shieldContractAddress, - 0, - ); - resolve(receipt); - } catch (err) { - reject(err); - } - }); + if (offchain) return this.sendOffchainTransaction(res.data.transaction); + return new Promise((resolve, reject) => { + userQueue.push(async () => { + try { + const receipt = await this.submitTransaction( + res.data.txDataToSign, + this.shieldContractAddress, + 0, + ); + resolve(receipt); + } catch (err) { + reject(err); + } }); - } - return res.status; + }); } /** @@ -797,23 +797,21 @@ class Nf3 { throw new Error(res.data.error); } this.latestWithdrawHash = res.data.transaction.transactionHash; - if (!offchain) { - return new Promise((resolve, reject) => { - userQueue.push(async () => { - try { - const receipt = await this.submitTransaction( - res.data.txDataToSign, - this.shieldContractAddress, - 0, - ); - resolve(receipt); - } catch (err) { - reject(err); - } - }); + if (offchain) return this.sendOffchainTransaction(res.data.transaction); + return new Promise((resolve, reject) => { + userQueue.push(async () => { + try { + const receipt = await this.submitTransaction( + res.data.txDataToSign, + this.shieldContractAddress, + 0, + ); + resolve(receipt); + } catch (err) { + reject(err); + } }); - } - return res.status; + }); } /** @@ -1308,19 +1306,69 @@ class Nf3 { } /** - Send offchain transaction to Optimist + Intended to be called by a Proposer acting as a Proxy for offchain transactions. This + function allows the Proposer to forward offchain transactions to its Optimist instance. @method @async - @param {string} transaction - @returns {array} A promise that resolves to the API call status - */ - async sendOffchainTransaction(transaction) { - const res = axios.post( + @param {object} transaction + @returns {object} A promise that resolves to the API call status + */ + async forwardOffchainTransaction(transaction, signature) { + const res = await axios.post( `${this.optimistBaseUrl}/proposer/offchain-transaction`, - { transaction }, + { transaction, signature }, { timeout: 3600000 }, ); - return res.status; + return res; + } + + /** + Send offchain transaction to all Proposers that the chain knows about + This is intended to be called by a User to send the transaction to a Proposer + who will act as a proxy and forward the transaction to an Optimist (this allows an + Optimist to be firewalled off and only the Proposer exposed to Users). + @method + @async + @param {object} transaction + @returns {object} A promise that resolves to the API call status + */ + async sendOffchainTransaction(transaction) { + // we're proxying offchain transactions through the proposers. We send the transaction to every proposer + // dig up connection peers + const currentProposer = await this.stateContract.methods.currentProposer().call(); + const peerList = { [currentProposer.thisAddress]: currentProposer.url }; + let nextProposer = await this.stateContract.methods + .proposers(currentProposer.nextAddress) + .call(); + let proposerIdx = 0; + while ( + currentProposer.thisAddress !== nextProposer.thisAddress && + proposerIdx <= this.NEXT_N_PROPOSERS + ) { + peerList[nextProposer.thisAddress] = nextProposer.url; + // eslint-disable-next-line no-await-in-loop + nextProposer = await this.stateContract.methods.proposers(nextProposer.nextAddress).call(); + proposerIdx += 1; + } + + logger.debug({ msg: 'Peer List', peerList }); + return Promise.any( + Object.keys(peerList).map(async address => { + logger.debug( + `offchain transaction - calling ${peerList[address]}/proposer/offchain-transaction`, + ); + // sign the transaction if we're required to identify ourself + const signature = process.env.ANONYMOUS_USER + ? null + : this.web3.eth.accounts.sign(JSON.stringify(transaction), this.ethereumSigningKey) + .signature; + return axios.post( + `${peerList[address]}/proposer/offchain-transaction`, + { transaction, signature }, + { timeout: 3600000 }, + ); + }), + ); } /** diff --git a/doc/whitelist.md b/doc/whitelist.md index ca694e1f0..611e0297e 100644 --- a/doc/whitelist.md +++ b/doc/whitelist.md @@ -1,6 +1,6 @@ # Nightfall Whitelisting adaptions -Nightfall now incorporates the ability to manage a whitelist of accounts. It is an abstract contract, intended to be subclassed by a form of Whitelist Manager contract (currentyl `X509.sol` performs this role). When whitelisting is enabled, only accounts that are added to the whitelist are able to move funds from Layer 1 to Layer 2 and to withdraw Layer 1 funds from the Shield contract. +Nightfall now incorporates the ability to manage a whitelist of accounts. It is an abstract contract, intended to be subclassed by a form of Whitelist Manager contract (currentyl `X509.sol` performs this role). When whitelisting is enabled, only accounts that are added to the whitelist are able to move funds from Layer 1 to Layer 2 and to withdraw Layer 1 funds from the Shield contract. Further, only whitelisted accounts may act as Proposers or Challengers. ## Enabling Whitelisting @@ -9,3 +9,7 @@ To enable whitelisting, the deployer container should have its `WHITELISTING` en ## Operating Whitelisting All whitelisting functionality is managed by the contract `Whitelist.sol`, the functions therein are self-explanatory. + +## Identifying users to the Proposer + +The Proposer may require that all users for whom it accepts transactions must be whitelisted. This is in fact the default unless the environment variable ANONYMOUS_USER is set in the Proposer container. Unless this variable is set, the Proposer will expect the user to sign the submitted off-chain transaction wirh their Ethereum signing key. The Proposer will do an ecrrecover on the signature and require that the recovered address is whitelisted before processing the transaction. The user's nf3 class will automatically sign offchain transactions unless the ANNONYMOUS_USER environment variable is set there. diff --git a/docker/docker-compose.multiproposer-test.yml b/docker/docker-compose.multiproposer-test.yml index f42e3d133..5df14ffa4 100644 --- a/docker/docker-compose.multiproposer-test.yml +++ b/docker/docker-compose.multiproposer-test.yml @@ -308,7 +308,7 @@ services: OPTIMIST_HOST: proposer_optimist_1 OPTIMIST_PORT: 80 OPTIMIST_WS_PORT: 8080 - PROPOSER_HOST: proposer_1 + PROPOSER_HOST: localhost PROPOSER_PORT: 8093 PROPOSER_KEY: ${PROPOSER_KEY:-0xcbbf1d0686738a444cf9f66fdc96289035c384c4e8d26768f94fa81f3ab6596a} DEPLOYER_HOST: ${DEPLOYER_HOST:-deployer} @@ -319,6 +319,9 @@ services: - type: bind source: ../apps/proposer/src target: /app/src + - type: bind + source: ../cli + target: /app/cli proposer_2: build: @@ -337,7 +340,7 @@ services: OPTIMIST_HOST: proposer_optimist_2 OPTIMIST_PORT: 80 OPTIMIST_WS_PORT: 8080 - PROPOSER_HOST: proposer_2 + PROPOSER_HOST: localhost PROPOSER_PORT: 8094 PROPOSER_KEY: ${PROPOSER2_KEY:-0xabf4ed9f30bd1e4a290310d726c7bbdf39cd75a25eebd9a3a4874e10b4a0c4ce} DEPLOYER_HOST: ${DEPLOYER_HOST:-deployer} @@ -348,6 +351,9 @@ services: - type: bind source: ../apps/proposer/src target: /app/src + - type: bind + source: ../cli + target: /app/cli challenger_1: build: diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index b19e22b6d..19b0b2d48 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -153,6 +153,7 @@ services: PROPOSER_MAX_BLOCK_PERIOD_MILIS: ${PROPOSER_MAX_BLOCK_PERIOD_MILIS:-0} USE_EXTERNAL_NODE: ${USE_EXTERNAL_NODE} WEBSOCKET_PORT: ${WEBSOCKET_PORT:-8080} + ANONYMOUS_USER: ${ANONYMOUS_USER} command: ['npm', 'run', 'dev'] rabbitmq: diff --git a/docker/proposer.Dockerfile b/docker/proposer.Dockerfile index 5d6bc2b62..1637cdef8 100644 --- a/docker/proposer.Dockerfile +++ b/docker/proposer.Dockerfile @@ -4,7 +4,7 @@ FROM node:16.17-bullseye-slim # entrypoint script requires 'netcat' RUN apt-get update \ && apt-get install -y --no-install-recommends \ - python3 make g++ netcat-openbsd \ + python3 make g++ netcat-openbsd iputils-ping\ && rm -rf /var/lib/apt/lists/* # websocket port 8080 diff --git a/nightfall-client/src/services/burn.mjs b/nightfall-client/src/services/burn.mjs index 466073b1e..9a256bd47 100644 --- a/nightfall-client/src/services/burn.mjs +++ b/nightfall-client/src/services/burn.mjs @@ -9,10 +9,9 @@ import { import gen from 'general-number'; import Transaction from '@polygon-nightfall/common-files/classes/transaction.mjs'; import { compressProof } from '@polygon-nightfall/common-files/utils/curve-maths/curves.mjs'; -import { clearPending } from './commitment-storage.mjs'; +import { clearPending, saveExtendedTransaction } from './commitment-storage.mjs'; import { getCommitmentInfo } from '../utils/getCommitmentInfo.mjs'; import { computeCircuitInputs } from '../utils/computeCircuitInputs.mjs'; -import { submitTransaction } from '../utils/submitTransaction.mjs'; import { ZkpKeys } from './keys.mjs'; const { VK_IDS } = config; @@ -124,7 +123,7 @@ async function burn(burnParams) { const rawTransaction = await shieldContractInstance.methods .submitTransaction(Transaction.buildSolidityStruct(transaction)) .encodeABI(); - await submitTransaction( + await saveExtendedTransaction( transaction, commitmentsInfo, compressedZkpPublicKey, diff --git a/nightfall-client/src/services/commitment-storage.mjs b/nightfall-client/src/services/commitment-storage.mjs index e6a2c991b..99ecab352 100644 --- a/nightfall-client/src/services/commitment-storage.mjs +++ b/nightfall-client/src/services/commitment-storage.mjs @@ -129,7 +129,7 @@ export async function markPending(commitment) { } // function to mark a commitment as nullified for a mongo db -export async function markNullified(commitment, transaction) { +async function markNullified(commitment, transaction) { const connection = await mongo.connection(MONGO_URL); const query = { _id: commitment.hash.hex(32) }; const update = { @@ -1072,3 +1072,23 @@ export async function getCommitmentsDepositedRollbacked(compressedZkpPublicKey) return db.collection(COMMITMENTS_COLLECTION).find(query).toArray(); } + +// saves a transaction with extended information about nullifiers and commitments +export const saveExtendedTransaction = async ( + transaction, + commitmentsInfo, + compressedZkpPublicKey, + nullifierKey, +) => { + // Store new commitments that are ours. + logger.debug({ msg: 'storing commitments', commitments: commitmentsInfo.newCommitments }); + const storeNewCommitments = commitmentsInfo.newCommitments + .filter(c => c.compressedZkpPublicKey.hex(32) === compressedZkpPublicKey.hex(32)) + .map(c => storeCommitment(c, nullifierKey)); + + logger.debug({ msg: 'nullifying commitments', commitments: commitmentsInfo.oldCommitments }); + const nullifyOldCommitments = commitmentsInfo.oldCommitments.map(c => + markNullified(c, transaction), + ); + await Promise.all([...storeNewCommitments, ...nullifyOldCommitments]); +}; diff --git a/nightfall-client/src/services/deposit.mjs b/nightfall-client/src/services/deposit.mjs index 5ab46c8bc..30e1fb9ed 100644 --- a/nightfall-client/src/services/deposit.mjs +++ b/nightfall-client/src/services/deposit.mjs @@ -21,8 +21,7 @@ import { Commitment, Transaction } from '../classes/index.mjs'; import { ZkpKeys } from './keys.mjs'; import { computeCircuitInputs } from '../utils/computeCircuitInputs.mjs'; import { getCommitmentInfo } from '../utils/getCommitmentInfo.mjs'; -import { submitTransaction } from '../utils/submitTransaction.mjs'; -import { getCommitmentByHash } from './commitment-storage.mjs'; +import { getCommitmentByHash, saveExtendedTransaction } from './commitment-storage.mjs'; const { VK_IDS } = config; const { SHIELD_CONTRACT_NAME, BN128_GROUP_ORDER, DEPOSIT, DEPOSIT_FEE } = constants; @@ -186,7 +185,7 @@ async function deposit(depositParams) { const rawTransaction = await shieldContractInstance.methods .submitTransaction(Transaction.buildSolidityStruct(transaction)) .encodeABI(); - await submitTransaction( + await saveExtendedTransaction( transaction, commitmentsInfo, compressedZkpPublicKey, diff --git a/nightfall-client/src/services/tokenise.mjs b/nightfall-client/src/services/tokenise.mjs index 41347b2a8..ca679c09d 100644 --- a/nightfall-client/src/services/tokenise.mjs +++ b/nightfall-client/src/services/tokenise.mjs @@ -12,9 +12,8 @@ import gen from 'general-number'; import { ZkpKeys } from './keys.mjs'; import { Commitment, Transaction } from '../classes/index.mjs'; import { computeCircuitInputs } from '../utils/computeCircuitInputs.mjs'; -import { clearPending } from './commitment-storage.mjs'; +import { clearPending, saveExtendedTransaction } from './commitment-storage.mjs'; import { getCommitmentInfo } from '../utils/getCommitmentInfo.mjs'; -import { submitTransaction } from '../utils/submitTransaction.mjs'; const { VK_IDS } = config; const { SHIELD_CONTRACT_NAME, BN128_GROUP_ORDER, TOKENISE } = constants; @@ -129,7 +128,7 @@ async function tokenise(items) { const rawTransaction = await shieldContractInstance.methods .submitTransaction(Transaction.buildSolidityStruct(transaction)) .encodeABI(); - await submitTransaction( + await saveExtendedTransaction( transaction, commitmentsInfo, compressedZkpPublicKey, diff --git a/nightfall-client/src/services/transfer.mjs b/nightfall-client/src/services/transfer.mjs index 811ff258a..b50f17ac9 100644 --- a/nightfall-client/src/services/transfer.mjs +++ b/nightfall-client/src/services/transfer.mjs @@ -22,9 +22,8 @@ import { Transaction } from '../classes/index.mjs'; import { ZkpKeys } from './keys.mjs'; import { computeCircuitInputs } from '../utils/computeCircuitInputs.mjs'; import { encrypt, genEphemeralKeys, packSecrets } from './kem-dem.mjs'; -import { clearPending } from './commitment-storage.mjs'; +import { clearPending, saveExtendedTransaction } from './commitment-storage.mjs'; import { getCommitmentInfo } from '../utils/getCommitmentInfo.mjs'; -import { submitTransaction } from '../utils/submitTransaction.mjs'; const { VK_IDS } = config; const { SHIELD_CONTRACT_NAME, TRANSFER } = constants; @@ -161,7 +160,7 @@ async function transfer(transferParams) { const rawTransaction = await shieldContractInstance.methods .submitTransaction(Transaction.buildSolidityStruct(transaction)) .encodeABI(); - await submitTransaction( + await saveExtendedTransaction( transaction, commitmentsInfo, compressedZkpPublicKey, diff --git a/nightfall-client/src/services/transform.mjs b/nightfall-client/src/services/transform.mjs index 7961f2a84..bc75c4bde 100644 --- a/nightfall-client/src/services/transform.mjs +++ b/nightfall-client/src/services/transform.mjs @@ -12,10 +12,9 @@ import { import { compressProof } from '@polygon-nightfall/common-files/utils/curve-maths/curves.mjs'; import gen from 'general-number'; import { Commitment, Transaction } from '../classes/index.mjs'; -import { clearPending } from './commitment-storage.mjs'; +import { clearPending, saveExtendedTransaction } from './commitment-storage.mjs'; import { getCommitmentInfo } from '../utils/getCommitmentInfo.mjs'; import { computeCircuitInputs } from '../utils/computeCircuitInputs.mjs'; -import { submitTransaction } from '../utils/submitTransaction.mjs'; import { ZkpKeys } from './keys.mjs'; const { VK_IDS } = config; @@ -176,7 +175,7 @@ async function transform(transformParams) { .submitTransaction(Transaction.buildSolidityStruct(transaction)) .encodeABI(); - await submitTransaction( + await saveExtendedTransaction( transaction, commitmentInfo, compressedZkpPublicKey, diff --git a/nightfall-client/src/services/withdraw.mjs b/nightfall-client/src/services/withdraw.mjs index fa60090ad..ddfc68133 100644 --- a/nightfall-client/src/services/withdraw.mjs +++ b/nightfall-client/src/services/withdraw.mjs @@ -17,9 +17,8 @@ import { } from '@polygon-nightfall/common-files/utils/worker-calls.mjs'; import { Transaction } from '../classes/index.mjs'; import { computeCircuitInputs } from '../utils/computeCircuitInputs.mjs'; -import { clearPending } from './commitment-storage.mjs'; +import { clearPending, saveExtendedTransaction } from './commitment-storage.mjs'; import { getCommitmentInfo } from '../utils/getCommitmentInfo.mjs'; -import { submitTransaction } from '../utils/submitTransaction.mjs'; import { ZkpKeys } from './keys.mjs'; const { VK_IDS } = config; @@ -137,7 +136,7 @@ async function withdraw(withdrawParams) { const rawTransaction = await shieldContractInstance.methods .submitTransaction(Transaction.buildSolidityStruct(transaction)) .encodeABI(); - await submitTransaction( + await saveExtendedTransaction( transaction, commitmentsInfo, compressedZkpPublicKey, diff --git a/nightfall-client/src/utils/submitTransaction.mjs b/nightfall-client/src/utils/submitTransaction.mjs deleted file mode 100644 index a472a66e4..000000000 --- a/nightfall-client/src/utils/submitTransaction.mjs +++ /dev/null @@ -1,65 +0,0 @@ -import axios from 'axios'; -import logger from '@polygon-nightfall/common-files/utils/logger.mjs'; -import constants from '@polygon-nightfall/common-files/constants/index.mjs'; -import { getContractInstance } from '@polygon-nightfall/common-files/utils/contract.mjs'; -import { markNullified, storeCommitment } from '../services/commitment-storage.mjs'; - -const { STATE_CONTRACT_NAME } = constants; - -const NEXT_N_PROPOSERS = 3; - -// eslint-disable-next-line import/prefer-default-export -export const submitTransaction = async ( - transaction, - commitmentsInfo, - compressedZkpPublicKey, - nullifierKey, - offchain, -) => { - // Store new commitments that are ours. - logger.debug({ msg: 'storing commitments', commitments: commitmentsInfo.newCommitments }); - const storeNewCommitments = commitmentsInfo.newCommitments - .filter(c => c.compressedZkpPublicKey.hex(32) === compressedZkpPublicKey.hex(32)) - .map(c => storeCommitment(c, nullifierKey)); - - logger.debug({ msg: 'nullifying commitments', commitments: commitmentsInfo.oldCommitments }); - const nullifyOldCommitments = commitmentsInfo.oldCommitments.map(c => - markNullified(c, transaction), - ); - - await Promise.all([...storeNewCommitments, ...nullifyOldCommitments]); - - if (offchain) { - // dig up connection peers - const stateContractInstance = await getContractInstance(STATE_CONTRACT_NAME); - const currentProposer = await stateContractInstance.methods.currentProposer().call(); - const peerList = { [currentProposer.thisAddress]: currentProposer.url }; - let nextProposer = await stateContractInstance.methods - .proposers(currentProposer.nextAddress) - .call(); - let proposerIdx = 0; - while ( - currentProposer.thisAddress !== nextProposer.thisAddress && - proposerIdx <= NEXT_N_PROPOSERS - ) { - peerList[nextProposer.thisAddress] = nextProposer.url; - // eslint-disable-next-line no-await-in-loop - nextProposer = await stateContractInstance.methods.proposers(nextProposer.nextAddress).call(); - proposerIdx += 1; - } - - logger.debug({ msg: 'Peer List', peerList }); - await Promise.any( - Object.keys(peerList).map(async address => { - logger.debug( - `offchain transaction - calling ${peerList[address]}/proposer/offchain-transaction`, - ); - return axios.post( - `${peerList[address]}/proposer/offchain-transaction`, - { transaction }, - { timeout: 3600000 }, - ); - }), - ); - } -}; diff --git a/nightfall-deployer/migrations/3_test_tokens_migration.js b/nightfall-deployer/migrations/3_test_tokens_migration.js index 36cdd9e52..de54e892c 100644 --- a/nightfall-deployer/migrations/3_test_tokens_migration.js +++ b/nightfall-deployer/migrations/3_test_tokens_migration.js @@ -16,12 +16,11 @@ const ERC1155Mock = artifacts.require('ERC1155Mock.sol'); const nERC721 = 35; module.exports = function (deployer, _, accounts) { + if (DEPLOY_MOCK_TOKENS === 'false') return; deployer.then(async () => { const shield = await Shield.deployed(); const state = await State.deployed(); - if (DEPLOY_MOCK_TOKENS === 'false') return; - await deployer.deploy(ERC20Mock, 1001010000000000); // initialSupply const ERC20deployed = await ERC20Mock.deployed(); diff --git a/nightfall-optimist/src/routes/proposer.mjs b/nightfall-optimist/src/routes/proposer.mjs index 540dfefb4..5ad5c3f16 100644 --- a/nightfall-optimist/src/routes/proposer.mjs +++ b/nightfall-optimist/src/routes/proposer.mjs @@ -5,6 +5,7 @@ */ import express from 'express'; import config from 'config'; +import Web3 from 'web3'; import Timber from '@polygon-nightfall/common-files/classes/timber.mjs'; import logger from '@polygon-nightfall/common-files/utils/logger.mjs'; import { @@ -32,6 +33,7 @@ import transactionSubmittedEventHandler from '../event-handlers/transaction-subm const router = express.Router(); const { TIMBER_HEIGHT, HASH_TYPE } = config; const { STATE_CONTRACT_NAME, PROPOSERS_CONTRACT_NAME, SHIELD_CONTRACT_NAME, ZERO } = constants; +const web3 = new Web3(); let proposer; export function setProposer(p) { @@ -405,7 +407,20 @@ router.post('/encode', async (req, res, next) => { }); router.post('/offchain-transaction', async (req, res) => { - const { transaction } = req.body; + const { transaction, signature } = req.body; + // the first thing we'll do is check that the sender is whitelisted (if required by our config) + if (!process.env.ANONYMOUS_USER) { + const address = web3.eth.accounts.recover(JSON.stringify(transaction), signature); + logger.debug(`Recovered address ${address}`); + const x509Instance = await getContractInstance('X509'); + const isWhitelisted = await x509Instance.methods.x509Check(address).call(); + if (!isWhitelisted) { + logger.warn('Attempted transaction by a user who is not whitelisted'); + res.sendStatus(401); + return; + } + } + /* When a transaction is built by client, they are generalised into hex(32) interfacing with web3 The response from on-chain events converts them to saner string values (e.g. uint64 etc). diff --git a/test/adversary.test.mjs b/test/adversary.test.mjs index 737489c60..642de93e5 100644 --- a/test/adversary.test.mjs +++ b/test/adversary.test.mjs @@ -156,7 +156,7 @@ describe('Testing with an adversary', () => { // Proposer registration await nf3AdversarialProposer.registerProposer( - 'http://optimist', + 'http://localhost:8081', await nf3AdversarialProposer.getMinimumStake(), ); diff --git a/test/circuits.test.mjs b/test/circuits.test.mjs index b6c0d2a43..60d780e87 100644 --- a/test/circuits.test.mjs +++ b/test/circuits.test.mjs @@ -39,7 +39,10 @@ describe('General Circuit Test', () => { before(async () => { await nf3Proposer.init(mnemonics.proposer); // we must set the URL from the point of view of the client container - await nf3Proposer.registerProposer('http://optimist', await nf3Proposer.getMinimumStake()); + await nf3Proposer.registerProposer( + 'http://localhost:8081', + await nf3Proposer.getMinimumStake(), + ); await nf3Proposer.startProposer(); diff --git a/test/e2e/tokens/erc1155.test.mjs b/test/e2e/tokens/erc1155.test.mjs index 7824d15fd..16660014f 100644 --- a/test/e2e/tokens/erc1155.test.mjs +++ b/test/e2e/tokens/erc1155.test.mjs @@ -67,7 +67,10 @@ describe('ERC1155 tests', () => { await nf3User2.init(mnemonics.user2); await nf3Proposer.init(mnemonics.proposer); - await nf3Proposer.registerProposer('http://optimist', await nf3Proposer.getMinimumStake()); + await nf3Proposer.registerProposer( + 'http://localhost:8081', + await nf3Proposer.getMinimumStake(), + ); // Proposer listening for incoming events const newGasBlockEmitter = await nf3Proposer.startProposer(); diff --git a/test/e2e/tokens/erc20.test.mjs b/test/e2e/tokens/erc20.test.mjs index 71b29f270..43f31ecbf 100644 --- a/test/e2e/tokens/erc20.test.mjs +++ b/test/e2e/tokens/erc20.test.mjs @@ -75,7 +75,10 @@ describe('ERC20 tests', () => { if (DEPLOY_MOCKED_SANCTIONS_CONTRACT) await nf3UserSanctioned.init(mnemonics.sanctionedUser); await nf3Proposer.init(mnemonics.proposer); - await nf3Proposer.registerProposer('http://optimist', await nf3Proposer.getMinimumStake()); + await nf3Proposer.registerProposer( + 'http://localhost:8081', + await nf3Proposer.getMinimumStake(), + ); // Proposer listening for incoming events const newGasBlockEmitter = await nf3Proposer.startProposer(); newGasBlockEmitter.on('rollback', () => { diff --git a/test/e2e/tokens/erc721.test.mjs b/test/e2e/tokens/erc721.test.mjs index 6b909b136..cfc9fe5f8 100644 --- a/test/e2e/tokens/erc721.test.mjs +++ b/test/e2e/tokens/erc721.test.mjs @@ -59,7 +59,10 @@ describe('ERC721 tests', () => { await nf3User2.init(mnemonics.user2); await nf3Proposer.init(mnemonics.proposer); - await nf3Proposer.registerProposer('http://optimist', await nf3Proposer.getMinimumStake()); + await nf3Proposer.registerProposer( + 'http://localhost:8081', + await nf3Proposer.getMinimumStake(), + ); // Proposer listening for incoming events const newGasBlockEmitter = await nf3Proposer.startProposer(); diff --git a/test/e2e/tokens/l2tokenisation.test.mjs b/test/e2e/tokens/l2tokenisation.test.mjs index cefd4fb52..756eb0f93 100644 --- a/test/e2e/tokens/l2tokenisation.test.mjs +++ b/test/e2e/tokens/l2tokenisation.test.mjs @@ -53,7 +53,10 @@ let rollbackCount = 0; describe('L2 Tokenisation tests', () => { before(async () => { await nf3Proposer1.init(mnemonics.proposer); - await nf3Proposer1.registerProposer('http://optimist', await nf3Proposer1.getMinimumStake()); + await nf3Proposer1.registerProposer( + 'http://localhost:8081', + await nf3Proposer1.getMinimumStake(), + ); // Proposer listening for incoming events const newGasBlockEmitter = await nf3Proposer1.startProposer(); diff --git a/test/fee-and-reward-periodic-payment.test.mjs b/test/fee-and-reward-periodic-payment.test.mjs index ff56be709..d29e425ab 100644 --- a/test/fee-and-reward-periodic-payment.test.mjs +++ b/test/fee-and-reward-periodic-payment.test.mjs @@ -58,7 +58,10 @@ describe('Periodic Payment', () => { `-- proposer account balance before registration --- ${await web3.eth.getBalance(nf3Proposer.ethereumAddress)}`, ); - await nf3Proposer.registerProposer('http://optimist', await nf3Proposer.getMinimumStake()); + await nf3Proposer.registerProposer( + 'http://http://localhost:8081', + await nf3Proposer.getMinimumStake(), + ); await nf3Proposer.startProposer(); erc20Address = await nf3User.getContractAddress('ERC20Mock'); diff --git a/test/gas.test.mjs b/test/gas.test.mjs index 57bab4b73..d65465c67 100644 --- a/test/gas.test.mjs +++ b/test/gas.test.mjs @@ -48,7 +48,10 @@ describe('Gas test', () => { let txPerBlock; before(async () => { await nf3Proposer.init(mnemonics.proposer); - await nf3Proposer.registerProposer('http://optimist', await nf3Proposer.getMinimumStake()); + await nf3Proposer.registerProposer( + 'http://localhost:8081optimist', + await nf3Proposer.getMinimumStake(), + ); // Proposer listening for incoming events const newGasBlockEmitter = await nf3Proposer.startProposer(); diff --git a/test/multisig/administrator.test.mjs b/test/multisig/administrator.test.mjs index 2126626ca..d3e71c194 100644 --- a/test/multisig/administrator.test.mjs +++ b/test/multisig/administrator.test.mjs @@ -218,7 +218,7 @@ describe(`Testing Administrator`, () => { it('Allowing register first proposer', async () => { if (config.ENVIRONMENT !== 'aws') { - const res = await proposers[0].registerProposer('http://optimist', minimumStakeDef); + const res = await proposers[0].registerProposer('http://localhost:8081', minimumStakeDef); expectTransaction(res); } }); @@ -226,7 +226,7 @@ describe(`Testing Administrator`, () => { it('Not allowing register second proposer', async () => { let error = null; try { - const res = await proposers[1].registerProposer('http://optimist', minimumStakeDef); + const res = await proposers[1].registerProposer('http://localhost:8081', minimumStakeDef); expectTransaction(res); } catch (err) { error = err; @@ -258,7 +258,7 @@ describe(`Testing Administrator`, () => { }); it('Allowing register second proposer', async () => { - const res = await proposers[1].registerProposer('http://optimist', minimumStakeDef); + const res = await proposers[1].registerProposer('http://localhost:8081', minimumStakeDef); expectTransaction(res); }); diff --git a/test/optimist-resync.test.mjs b/test/optimist-resync.test.mjs index 9ede9a179..e3be29db1 100644 --- a/test/optimist-resync.test.mjs +++ b/test/optimist-resync.test.mjs @@ -54,7 +54,7 @@ describe('Optimist synchronisation tests', () => { await nf3Challenger.init(mnemonics.challenger); minimumStake = await nf3Proposer1.getMinimumStake(); // we must set the URL from the point of view of the client container - await nf3Proposer1.registerProposer('http://optimist', minimumStake); + await nf3Proposer1.registerProposer('http://localhost:8081', minimumStake); // Proposer listening for incoming events blockProposeEmitter = await nf3Proposer1.startProposer(); @@ -107,7 +107,7 @@ describe('Optimist synchronisation tests', () => { await restartOptimist(nf3Proposer1, false); // we need to remind optimist which proposer it's connected to - await nf3Proposer1.registerProposer('http://optimist', minimumStake); + await nf3Proposer1.registerProposer('http://localhost:8081', minimumStake); await waitForTimeout(5000); // TODO - get optimist to do this automatically. // Now we'll add another block and check that it's blocknumber is correct, indicating @@ -183,7 +183,7 @@ describe('Optimist synchronisation tests', () => { await restartOptimist(nf3Proposer1, true); // we need to remind optimist which proposer it's connected to - await nf3Proposer1.registerProposer('http://optimist', minimumStake); + await nf3Proposer1.registerProposer('http://localhost:8081', minimumStake); // TODO - get optimist to do this automatically. // Now we'll add another block and check that it's blocknumber is correct, indicating // that a resync correctly occured @@ -255,7 +255,7 @@ describe('Optimist synchronisation tests', () => { logger.debug('rollback complete event received'); // the rollback will have removed us as proposer. We need to re-register because we // were the only proposer in town! - await nf3Proposer1.registerProposer('http://optimist', minimumStake); + await nf3Proposer1.registerProposer('http://localhost:8081', minimumStake); // Now we'll add another block and check that it's blocknumber is correct, indicating // that a rollback correctly occured logger.debug(` Sending a deposit...`); diff --git a/test/ping-pong/index.mjs b/test/ping-pong/index.mjs index 11c25fb48..c145576d5 100644 --- a/test/ping-pong/index.mjs +++ b/test/ping-pong/index.mjs @@ -70,7 +70,7 @@ export async function simpleUserTest( const startBalance = await retrieveL2Balance(nf3, ercAddress); console.log(`start balance ${nf3.zkpKeys.compressedZkpPublicKey}`, startBalance); - let offchainTx = true; + const offchainTx = true; const { txTypes } = nf3; @@ -130,7 +130,7 @@ export async function simpleUserTest( to: userAdressTo, value: valueToTransfer, fee, - transactionHashL1: res.transactionHash, + transactionHashL1: res?.transactionHash, blockHash: res.blockHash, onchain: !offchainTx, type: 'transfer', @@ -161,7 +161,7 @@ export async function simpleUserTest( to: userAdressTo, value: valueToTransfer, fee, - transactionHashL1: res.transactionHash, + transactionHashL1: res?.transactionHash, blockHash: res.blockHash, onchain: !offchainTx, type: 'transfer', @@ -171,7 +171,6 @@ export async function simpleUserTest( console.warn('Error transfer', err); } } - offchainTx = !offchainTx; try { const res = await nf3.deposit( @@ -206,7 +205,7 @@ export async function simpleUserTest( try { const res = await nf3.withdraw( txTypes[i * 3 + 2], - offchainTx, + !offchainTx, ercAddress, tokenType, valueToTransfer, diff --git a/test/utils.mjs b/test/utils.mjs index 62b6156d6..d602209b5 100644 --- a/test/utils.mjs +++ b/test/utils.mjs @@ -453,7 +453,7 @@ export const retrieveL2Balance = async (client, ercAddress) => { */ export const registerProposerOnNoProposer = async proposer => { if ((await proposer.getCurrentProposer()) === '0x0000000000000000000000000000000000000000') { - await proposer.registerProposer('http://optimist', await proposer.getMinimumStake()); + await proposer.registerProposer('http://localhost:8081', await proposer.getMinimumStake()); } }; diff --git a/test/x509.test.mjs b/test/x509.test.mjs index 70d8c95fb..751abafcc 100644 --- a/test/x509.test.mjs +++ b/test/x509.test.mjs @@ -59,7 +59,10 @@ describe('x509 tests', () => { nf3Proposer.ethereumAddress, ); // we must set the URL from the point of view of the client container - await nf3Proposer.registerProposer('http://optimist', await nf3Proposer.getMinimumStake()); + await nf3Proposer.registerProposer( + 'http://localhost:8081', + await nf3Proposer.getMinimumStake(), + ); await nf3Proposer.startProposer(); await nf3Users[0].init(mnemonics.user1); erc20Address =