Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Westlad/identify user #1413

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ node_modules/*
**/migrations/*
**/doc/*
mumbai/
polygonpos/
proving_files/
cli/build/*
wallet/cli/*
mumbai/
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ mumbai/
.*swp
.vscode/*
.openzeppelin/
proving_files/
*.log
mumbai/
polygonpos/
# wallet
wallet/build
wallet/cli
Expand Down
4 changes: 3 additions & 1 deletion apps/proposer/src/proposer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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}`,
);
}
}

Expand Down
7 changes: 3 additions & 4 deletions apps/proposer/src/routes/proposer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}
Expand Down
6 changes: 3 additions & 3 deletions bin/polygonpos-deployment.env
Original file line number Diff line number Diff line change
Expand Up @@ -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
134 changes: 91 additions & 43 deletions cli/lib/nf3.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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;
});
}

/**
Expand Down Expand Up @@ -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;
});
}

/**
Expand Down Expand Up @@ -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 },
);
}),
);
}

/**
Expand Down
6 changes: 5 additions & 1 deletion doc/whitelist.md
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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.
10 changes: 8 additions & 2 deletions docker/docker-compose.multiproposer-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand All @@ -319,6 +319,9 @@ services:
- type: bind
source: ../apps/proposer/src
target: /app/src
- type: bind
source: ../cli
target: /app/cli

proposer_2:
build:
Expand All @@ -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}
Expand All @@ -348,6 +351,9 @@ services:
- type: bind
source: ../apps/proposer/src
target: /app/src
- type: bind
source: ../cli
target: /app/cli

challenger_1:
build:
Expand Down
1 change: 1 addition & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion docker/proposer.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 2 additions & 3 deletions nightfall-client/src/services/burn.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
22 changes: 21 additions & 1 deletion nightfall-client/src/services/commitment-storage.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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]);
};
5 changes: 2 additions & 3 deletions nightfall-client/src/services/deposit.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
Loading