Skip to content

Commit

Permalink
Merge pull request #786 from EYBlockchain/david/adversary-test2
Browse files Browse the repository at this point in the history
David/adversary test2
  • Loading branch information
ChaitanyaKonda authored Jul 11, 2022
2 parents d594424 + 0957e1d commit 45a9a7f
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 71 deletions.
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);
// 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

0 comments on commit 45a9a7f

Please sign in to comment.