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

David/adversary test2 #786

Merged
merged 9 commits into from
Jul 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion docker-compose.adversary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ version: '3.5'
# Use this script for making an adversary service
services:
adversary1:
extra_hosts:
- 'host.docker.internal:host-gateway'
build:
dockerfile: optimist.Dockerfile
context: .
Expand Down Expand Up @@ -32,7 +34,12 @@ services:
BLOCKCHAIN_PORT: 8546
HASH_TYPE: mimc
LOG_LEVEL: debug
IS_CHALLENGER: 'false'
IS_CHALLENGER: 'true'
NONSTOP_QUEUE_AFTER_INVALID_BLOCK: 'true'
TRANSACTIONS_PER_BLOCK: ${TRANSACTIONS_PER_BLOCK:-2}
AUTOSTART_RETRIES: 100
BAD_BLOCK_SEQUENCE: ${BAD_BLOCK_SEQUENCE}
BAD_TX_SEQUENCE: ${BAD_TX_SEQUENCE}
command: ['npm', 'run', 'dev']


7 changes: 0 additions & 7 deletions docker-compose.host.docker.internal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,6 @@ services:
BLOCKCHAIN_WS_HOST: host.docker.internal
AUTOSTART_RETRIES: 100

adversary1:
extra_hosts:
- 'host.docker.internal:host-gateway'
environment:
BLOCKCHAIN_WS_HOST: host.docker.internal
AUTOSTART_RETRIES: 100

administrator:
extra_hosts:
- 'host.docker.internal:host-gateway'
Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ services:
BLOCKCHAIN_PORT: 8546
HASH_TYPE: mimc
LOG_LEVEL: debug
IS_CHALLENGER: 'true'
IS_CHALLENGER: 'false'
TRANSACTIONS_PER_BLOCK: ${TRANSACTIONS_PER_BLOCK:-2}
command: ['npm', 'run', 'dev']

Expand All @@ -163,7 +163,7 @@ services:
BLOCKCHAIN_PORT: 8546
HASH_TYPE: mimc
LOG_LEVEL: error
IS_CHALLENGER: 'true'
IS_CHALLENGER: 'false'
TRANSACTIONS_PER_BLOCK: ${TRANSACTIONS_PER_BLOCK:-2}
command: ['npm', 'run', 'dev']

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"prepare": "husky install",
"doc:build:sdk": "jsdoc -c jsdoc.json cli/lib/nf3.mjs",
"build-adversary": "node test/adversary/transpile-adversary.mjs",
"adversary-test": "mocha --timeout 0 --bail --exit test/adversary.test.mjs"
"adversary-test": "CHALLENGE_TYPE=${CHALLENGE_TYPE} mocha --timeout 0 --bail --exit test/adversary.test.mjs",
"adversary-test-all": "for CHALLENGE_TYPE in IncorrectTreeRoot IncorrectLeafCount DuplicateTransaction DuplicateNullifier HistoricRootError IncorrectProof; do CHALLENGE_TYPE=${CHALLENGE_TYPE} mocha --timeout 0 --bail --exit test/adversary.test.mjs; sleep 5; done"
},
"repository": {
"type": "git",
Expand Down
46 changes: 42 additions & 4 deletions test/adversary.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
waitForSufficientBalance,
registerProposerOnNoProposer,
retrieveL2Balance,
// eslint-disable-next-line no-unused-vars
waitForNoPendingCommitments,
} from './utils.mjs';
import logger from '../common-files/utils/logger.mjs';
Expand Down Expand Up @@ -77,7 +78,11 @@ describe('Testing with an adversary', () => {
optimistWsUrl: adversarialOptimistWsUrl,
});

nf3Challenger = new Nf3(ethereumSigningKeyChallenger, environment);
nf3Challenger = new Nf3(ethereumSigningKeyChallenger, {
...others,
optimistApiUrl: adversarialOptimistApiUrl,
optimistWsUrl: adversarialOptimistWsUrl,
});

// Generate a random mnemonic (uses crypto.randomBytes under the hood), defaults to 128-bits of entropy
await nf3User.init(mnemonicUser);
Expand Down Expand Up @@ -119,17 +124,45 @@ describe('Testing with an adversary', () => {
await nf3Challenger.registerChallenger();
// Chalenger listening for incoming events
nf3Challenger.startChallenger();

const challengerEmitter = await nf3Challenger.getChallengeEmitter();
challengerEmitter.on('data', txDataToSign => {
logger.debug(`Challenger emitter with data ${txDataToSign}`);
});

// Configure adversary bad block sequence
if (process.env.CHALLENGE_TYPE !== '') {
logger.debug(`Configuring Challenge Type ${process.env.CHALLENGE_TYPE}`);
await chai
.request(adversarialOptimistApiUrl)
.post('/block/gen-block')
.send({ blockType: ['ValidBlock', 'ValidBlock', process.env.CHALLENGE_TYPE] });
} else {
logger.debug(`Configuring Default Challenge Type`);
}

console.log('Pausing challenger queue...');
// we pause the challenger queue and don't process challenger until unpauseQueueChallenger
nf3Challenger.pauseQueueChallenger();
});

describe('User creates deposit and transfer transactions', () => {
it('User should have the correct balance after without challenge', async () => {
it('User should have the correct balance after a series of rollbacks', async () => {
// Because rollbacks removes the only registered proposer,
// the proposer is registered again after each remova
intervalId = setInterval(() => {
registerProposerOnNoProposer(nf3AdversarialProposer);
}, 5000);
let nDeposits = 0;
let nTransfers = 0;

// we are creating a block of deposits with high values such that there is
// enough balance for a lot of transfers with low value.
console.log('Starting balance :', startBalance);
expectedBalance = startBalance;
for (let j = 0; j < TRANSACTIONS_PER_BLOCK; j++) {
await nf3User.deposit(ercAddress, tokenType, value2, tokenId, fee);
nDeposits++;
expectedBalance += value2;
}

Expand All @@ -144,6 +177,7 @@ describe('Testing with an adversary', () => {
tokenId,
nf3User.zkpKeys.compressedPkd,
);
nTransfers++;
// expectedBalance += value2;
} catch (err) {
if (err.message.includes('No suitable commitments')) {
Expand All @@ -163,26 +197,30 @@ describe('Testing with an adversary', () => {
tokenId,
nf3User.zkpKeys.compressedPkd,
);
nTransfers++;
// expectedBalance += value2; // transfer to self, so balance does not increase
}
}
for (let k = 0; k < TRANSACTIONS_PER_BLOCK - 1; k++) {
await nf3User.deposit(ercAddress, tokenType, value2, tokenId);
nDeposits++;
expectedBalance += value2;
}
await new Promise(resolve => setTimeout(resolve, TX_WAIT)); // this may need to be longer on a real blockchain
console.log(`Completed ${i + 1} pings`);
}

// TODO:_ how can i check that queue 2 is empty
await waitForSufficientBalance(nf3User, expectedBalance);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line seems to be a duplicate of line 217 ?

// waiting sometime to ensure that all the good transactions from bad
// blocks were proposed in other good blocks
await waitForSufficientBalance(nf3User, expectedBalance);
await new Promise(resolve => setTimeout(resolve, 20 * TX_WAIT));
await waitForSufficientBalance(nf3User, expectedBalance);
await waitForNoPendingCommitments(nf3User);
const endBalance = await retrieveL2Balance(nf3User);
console.log(`Completed startBalance`, startBalance);
console.log(`Completed endBalance`, endBalance);
logger.debug(`N deposits: ${nDeposits} - N Transfers: ${nTransfers}`);
expect(expectedBalance).to.be.equal(endBalance - startBalance);
});

Expand All @@ -193,10 +231,10 @@ describe('Testing with an adversary', () => {
// waiting sometime to ensure that all the good transactions from bad
// blocks were proposed in other good blocks
console.log('Waiting for rollbacks...');

await waitForSufficientBalance(nf3User, expectedBalance);
await new Promise(resolve => setTimeout(resolve, 30 * TX_WAIT));
await waitForSufficientBalance(nf3User, expectedBalance);
await waitForNoPendingCommitments(nf3User);
const endBalance = await retrieveL2Balance(nf3User);
console.log(`Completed startBalance`, startBalance);
console.log(`Completed endBalance`, endBalance);
Expand Down
62 changes: 36 additions & 26 deletions test/adversary/adversary-code/block.mjs
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
/* ignore unused exports */
import logger from 'common-files/utils/logger.mjs';

const error = [
'ValidBlock',
'ValidBlock',
'ValidBlock',
'IncorrectTreeRoot', // Needs two prior blocks
'ValidBlock',
'ValidBlock',
/* 'ValidBlock',
'ValidBlock',
'ValidBlock',
'ValidBlock',
'ValidBlock',
'ValidBlock',
'IncorrectLeafCount', // Needs one prior block
'ValidBlock',
'DuplicateTransaction', // needs atleast one transaction in a prior block
'ValidBlock',
'DuplicateNullifier', // needs atleast one non deposit transaction in a prior block
'ValidBlock',
'HistoricRootError',
'ValidBlock',
'IncorrectProof',
'ValidBlock', */
];
let error = process.env.BAD_BLOCK_SEQUENCE
? process.env.BAD_BLOCK_SEQUENCE.split(',')
: [
'ValidBlock',
'ValidBlock',
'ValidBlock',
'IncorrectTreeRoot', // Needs two prior blocks
'ValidBlock',
'IncorrectLeafCount', // Needs one prior block
'ValidBlock',
'DuplicateTransaction', // needs atleast one transaction in a prior block
'ValidBlock',
'DuplicateNullifier', // needs atleast one non deposit transaction in a prior block
'ValidBlock',
'HistoricRootError',
'ValidBlock',
'IncorrectProof',
'ValidBlock',
];

let resetErrorIdx = false;
let indexOffset = 0;

// eslint-disable-next-line no-unused-vars
const incorrectTransactionHashesRoot = block => {
Expand All @@ -51,15 +49,27 @@ const incorrectLeafCount = block => {
};
};

export const addBlock = blockType => {
error = blockType;
resetErrorIdx = true;
logger.debug(`Received new block types to generate ${error}`);
};

// eslint-disable-next-line import/prefer-default-export
export const createBadBlock = (block, errorIndex) => {
logger.debug('Creating a block of type', error[errorIndex]);
switch (error[errorIndex]) {
if (resetErrorIdx) {
resetErrorIdx = false;
indexOffset = errorIndex;
}
const badBlockType = error[errorIndex - indexOffset];
logger.debug(`Creating a block of type ${badBlockType}`);
switch (badBlockType) {
case 'IncorrectTreeRoot':
return incorrectTreeRoot(block);
case 'IncorrectLeafCount':
return incorrectLeafCount(block);
default:
logger.debug(`Creating a block of type ValidBlock`);
return block;
}
};
62 changes: 36 additions & 26 deletions test/adversary/adversary-code/database.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,28 @@ import mongo from 'common-files/utils/mongo.mjs';

const { MONGO_URL, OPTIMIST_DB, TRANSACTIONS_COLLECTION } = config;

const error = [
'ValidTransaction',
'ValidTransaction',
'ValidTransaction',
'IncorrectTreeRoot',
'ValidTransaction',
'ValidTransaction',
/* 'ValidTransaction',
'ValidTransaction',
'ValidTransaction',
'ValidTransaction',
'ValidTransaction',
'ValidTransaction',
'IncorrectLeafCount',
'ValidTransaction',
'DuplicateTransaction',
'ValidTransaction',
'DuplicateNullifier',
'ValidTransaction',
'HistoricRootError',
'ValidTransaction',
'IncorrectProof',
'ValidTransaction', */
];
let error = process.env.BAD_TX_SEQUENCE
? process.env.BAD_TX_SEQUENCE.split(',')
: [
'ValidTransaction',
'ValidTransaction',
'ValidTransaction',
'IncorrectTreeRoot',
'ValidTransaction',
'IncorrectLeafCount',
'ValidTransaction',
'DuplicateTransaction',
'ValidTransaction',
'DuplicateNullifier',
'ValidTransaction',
'HistoricRootError',
'ValidTransaction',
'IncorrectProof',
'ValidTransaction',
];

let resetErrorIdx = false;
let indexOffset = 0;

const duplicateNullifier = async number => {
logger.debug('Creating Block with Duplicate Nullifier');
Expand Down Expand Up @@ -143,14 +141,25 @@ const historicRootError = async number => {
return transactions;
};

export const addTx = txType => {
error = txType;
resetErrorIdx = true;
logger.debug(`Received new Tx types to generate ${error}`);
};

/**
Function to return 'number' transactions, ordered by the highest fee. If there
are fewer than 'number' transactions, all are returned.
*/
// eslint-disable-next-line import/prefer-default-export
export async function getMostProfitableTransactions(number, errorIndex) {
logger.debug('Creating a transaction of type', error[errorIndex]);
switch (error[errorIndex]) {
if (resetErrorIdx) {
resetErrorIdx = false;
indexOffset = errorIndex;
}
const badTxType = error[errorIndex - indexOffset];
logger.debug(`Creating a transaction of type ${badTxType}`);
switch (badTxType) {
case 'DuplicateTransaction':
return duplicateTransaction(number);
case 'DuplicateNullifier':
Expand All @@ -160,6 +169,7 @@ export async function getMostProfitableTransactions(number, errorIndex) {
case 'HistoricRootError':
return historicRootError(number);
default: {
logger.debug(`Creating a transaction of type ValidBlock`);
const connection = await mongo.connection(MONGO_URL);
const db = connection.db(OPTIMIST_DB);
return db
Expand Down
25 changes: 25 additions & 0 deletions test/adversary/adversary-code/route-block.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* eslint-disable no-undef */
/* ignore unused exports */
router.get('/reset-localblock', async (req, res, next) => {
logger.debug('block endpoint received get');
try {
await Block.rollback();
res.json({ resetstatus: true });
} catch (err) {
next(err);
}
});

router.post('/gen-block', async (req, res, next) => {
logger.debug('gen-block endpoint received POST');
try {
const { blockType } = req.body;
await addBlock(blockType);
await addTx(blockType);
res.json({ status: 'OK' });
} catch (err) {
next(err);
}
});

export default router;
Loading