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

fix: optimist resync over a bad block, tidy docker-compose yml #870

Merged
merged 33 commits into from
Sep 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
fb35fa5
feat: recover from bad block on resync
Westlad Aug 2, 2022
ff24a77
feat: adds a bad block
Westlad Aug 4, 2022
109dbb0
fix: recover block using sdk
Westlad Aug 5, 2022
e2b8b0c
feat: docker compose control
Westlad Aug 9, 2022
bf0d4d8
fix: merge issues
Westlad Aug 9, 2022
21bff47
fix: rebase issues
Westlad Aug 9, 2022
94db03b
feat: resync works over bad block
Westlad Aug 11, 2022
03394b1
feat: remove unnecessary containers
Westlad Aug 11, 2022
cf209df
fix: remove unused exports
Westlad Aug 11, 2022
fac28ab
fix: remove unused exports
Westlad Aug 11, 2022
e009d33
fix: merge issues
Westlad Aug 12, 2022
6f640f8
fix: rebase issues
Westlad Aug 12, 2022
4dbec83
fix: ci test
Westlad Aug 12, 2022
3eb4bca
fix: remove references to redundant containers
Westlad Aug 12, 2022
5e559b0
fix: remove challenger registration from adversary test
Westlad Aug 12, 2022
0c804e7
fix: adversary test
Westlad Aug 12, 2022
e5e0033
fix: adversary test
Westlad Aug 12, 2022
82b07f2
fix: gas test
Westlad Aug 15, 2022
e769346
fix: partial adversary fix
Westlad Aug 15, 2022
f580aca
fix: package lock
Westlad Aug 15, 2022
680dba9
fix: dependencies
Westlad Aug 15, 2022
7f1f4bb
fix: re-add docker-compose dependency - fixes got issue
Westlad Aug 16, 2022
5850207
fix: re-add optimist sync test
Westlad Aug 16, 2022
7afc359
fix: start challenger after optimist sync is done in all cases
Westlad Aug 16, 2022
9edbd83
fix: merge conflicts
Westlad Sep 5, 2022
8576ffe
fix: docker-compose does not build in parallel well
Westlad Sep 5, 2022
daa4ef7
fix: package lock
Westlad Sep 5, 2022
01500f3
fix: merge
Westlad Sep 6, 2022
67e01fa
fix: adverary test
Westlad Sep 6, 2022
5e219bb
fix: update insecure package
Westlad Sep 6, 2022
7c33a49
fix: package issues
Westlad Sep 6, 2022
bec3d11
Merge branch 'master' into westlad/challenge-sync
Westlad Sep 6, 2022
e1fa280
fix: circuits tests
Westlad Sep 6, 2022
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
50 changes: 48 additions & 2 deletions .github/workflows/check-PRs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,52 @@ jobs:
name: ganache-test-logs
path: ./ganache-test.log

optimist-sync-test:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@master
- uses: actions/setup-node@v1
with:
node-version: '14.17.0'

- name: Start Containers
run: |
./setup-nightfall
./start-nightfall -g -d &> optimist-sync-test.log &disown

- name: Wait for images to be ready
uses: Wandalen/wretry.action@v1.0.11
with:
command: |
docker wait nightfall_3_deployer_1
attempt_limit: 100
attempt_delay: 20000

- name: Debug logs - after image builds
if: always()
run: cat optimist-sync-test.log

- name: Run optimist sync test
run: |
npm ci
docker wait nightfall_3_deployer_1
npm run test-optimist-sync

- name: Debug logs - after optimist sync test run
if: always()
run: cat optimist-sync-test.log

- name: If optimist sync test failed, shutdown the Containers
if: failure()
run: docker-compose -f docker-compose.yml -f docker-compose.ganache.yml down -v

- name: If optimist sync test failed, upload logs files as artifacts
if: failure()
uses: actions/upload-artifact@master
with:
name: optimist-sync-test-logs
path: ./optimist-sync-test.log

adversary-test:
runs-on: ubuntu-20.04
env:
Expand All @@ -149,7 +195,7 @@ jobs:
run: |
./setup-nightfall
./geth-standalone -s
sleep 300
sleep 300
./start-nightfall -l -d -a &> adversary-test.log &disown

- name: Wait for images to be ready
Expand All @@ -175,7 +221,7 @@ jobs:
- name: If integration test failed, shutdown the Containers
if: failure()
run: |
docker-compose -f docker-compose.yml -f docker-compose.dev.yml -f docker-compose.ganache.yml -f docker-compose.adversary.yml down -v
docker-compose -f docker-compose.yml -f docker-compose.dev.yml -f docker-compose.ganache.yml -f docker-compose.adversary.yml down -v
./geth-standalone -d

- name: If integration test failed, upload logs files as artifacts
Expand Down
14 changes: 7 additions & 7 deletions cli/lib/environment.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ const SUPPORTED_ENVIRONMENTS = {
ropsten: {
name: 'Ropsten',
chainId: 3,
clientApiUrl: 'https://client1.testnet.nightfall3.com',
optimistApiUrl: 'https://optimist1.testnet.nightfall3.com',
optimistWsUrl: 'wss://optimist1-ws.testnet.nightfall3.com',
clientApiUrl: 'https://client.testnet.nightfall3.com',
optimistApiUrl: 'https://optimist.testnet.nightfall3.com',
optimistWsUrl: 'wss://optimist-ws.testnet.nightfall3.com',
web3WsUrl: 'wss://ropsten1-ws.testnet.nightfall3.com',
},
rinkeby: {
Expand All @@ -35,10 +35,10 @@ const SUPPORTED_ENVIRONMENTS = {
docker: {
name: 'Docker',
chainId: 1337,
clientApiUrl: 'http://client1',
optimistApiUrl: 'http://optimist1',
optimistWsUrl: 'ws://optimist1:8080',
web3WsUrl: 'ws://blockchain1:8546',
clientApiUrl: 'http://client',
optimistApiUrl: 'http://optimist',
optimistWsUrl: 'ws://optimist:8080',
web3WsUrl: 'ws://blockchain:8546',
},
};

Expand Down
145 changes: 28 additions & 117 deletions cli/lib/nf3.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ class Nf3 {
data: unsignedTransaction,
});
} catch (error) {
logger.warn(`estimateGas failed. Falling back to constant value`);
// logger.warn(`estimateGas failed. Falling back to constant value`);
gasLimit = GAS; // backup if estimateGas failed
}
return Math.ceil(Number(gasLimit) * GAS_MULTIPLIER); // 50% seems a more than reasonable buffer.
Expand All @@ -232,11 +232,11 @@ class Nf3 {
const res = (await axios.get(GAS_ESTIMATE_ENDPOINT)).data.result;
proposedGasPrice = Number(res?.ProposeGasPrice) * 10 ** 9;
} catch (error) {
logger.warn('Gas Estimation Failed, using previous block gasPrice');
// logger.warn('Gas Estimation Failed, using previous block gasPrice');
try {
proposedGasPrice = Number(await this.web3.eth.getGasPrice());
} catch (err) {
logger.warn('Failed to get previous block gasprice. Falling back to default');
// logger.warn('Failed to get previous block gasprice. Falling back to default');
proposedGasPrice = GAS_PRICE;
}
}
Expand All @@ -259,11 +259,11 @@ class Nf3 {
const gasPrice = await this.estimateGasPrice();
// Estimate the gasLimit
const gas = await this.estimateGas(contractAddress, unsignedTransaction);
logger.debug(
`Transaction gasPrice was set at ${Math.ceil(
gasPrice / 10 ** 9,
)} GWei, gas limit was set at ${gas}`,
);
// logger.debug(
// `Transaction gasPrice was set at ${Math.ceil(
// gasPrice / 10 ** 9,
// )} GWei, gas limit was set at ${gas}`,
// );
const tx = {
from: this.ethereumAddress,
to: contractAddress,
Expand Down Expand Up @@ -874,7 +874,7 @@ class Nf3 {
@async
*/
async startProposer() {
const blockProposeEmitter = new EventEmitter();
const proposeEmitter = new EventEmitter();
const connection = new ReconnectingWebSocket(this.optimistWsUrl, [], { WebSocket });
this.websockets.push(connection); // save so we can close it properly later
// we can't setup up a ping until the connection is made because the ping function
Expand All @@ -895,7 +895,7 @@ class Nf3 {
};
connection.onmessage = async message => {
const msg = JSON.parse(message.data);
const { type, txDataToSign, block, transactions } = msg;
const { type, txDataToSign, block, transactions, data } = msg;
logger.debug(`Proposer received websocket message of type ${type}`);
if (type === 'block') {
proposerQueue.push(async () => {
Expand All @@ -905,20 +905,21 @@ class Nf3 {
this.stateContractAddress,
this.BLOCK_STAKE,
);
blockProposeEmitter.emit('receipt', receipt, block, transactions);
proposeEmitter.emit('receipt', receipt, block, transactions);
} catch (err) {
// block proposed is reverted. Send transactions back to mempool
blockProposeEmitter.emit('error', err, block, transactions);
proposeEmitter.emit('error', err, block, transactions);
await axios.get(`${this.optimistBaseUrl}/block/reset-localblock`);
}
});
}
if (type === 'rollback') proposeEmitter.emit('rollback', data);
return null;
};
connection.onerror = () => logger.error('Proposer websocket connection error');
connection.onclosed = () => logger.warn('Proposer websocket connection closed');
// add this proposer to the list of peers that can accept direct transfers and withdraws
return blockProposeEmitter;
return proposeEmitter;
}

/**
Expand All @@ -937,67 +938,6 @@ class Nf3 {
return res.status;
}

/**
Returns an emitter, whose 'data' event fires whenever a block is
detected, passing out the transaction needed to propose the block. This
is a lower level method than `Nf3.startProposer` because it does not sign and
send the transaction to the blockchain. If required, `Nf3.submitTransaction`
can be used to do that.
@method
@async
@returns {Promise} A Promise that resolves into an event emitter.
*/
async getNewBlockEmitter() {
const newBlockEmitter = new EventEmitter();
const connection = new ReconnectingWebSocket(this.optimistWsUrl, [], { WebSocket });
this.websockets.push(connection); // save so we can close it properly later
connection.onopen = () => {
// setup a ping every 15s
this.intervalIDs.push(
setInterval(() => {
connection._ws.ping();
// logger.debug('sent websocket ping');
}, WEBSOCKET_PING_TIME),
);
// and a listener for the pong
// connection._ws.on('pong', () => logger.debug('websocket received pong'));
logger.debug('Proposer websocket connection opened');
connection.send('blocks');
};
connection.onmessage = async message => {
const msg = JSON.parse(message.data);
const { type, txDataToSign } = msg;
if (type === 'block') {
newBlockEmitter.emit('data', txDataToSign);
}
};
return newBlockEmitter;
}

/**
Registers our address as a challenger address with the optimist container.
This is so that the optimist container can tell when a challenge that we have
committed to has appeared on chain.
@method
@async
@return {Promise} A promise that resolves to an axios response.
*/
async registerChallenger() {
return axios.post(`${this.optimistBaseUrl}/challenger/add`, { address: this.ethereumAddress });
}

/**
De-registers our address as a challenger address with the optimist container.
@method
@async
@return {Promise} A promise that resolves to an axios response.
*/
async deregisterChallenger() {
return axios.post(`${this.optimistBaseUrl}/challenger/remove`, {
address: this.ethereumAddress,
});
}

/**
Starts a Challenger that listens for challengable blocks and submits challenge
transactions to the blockchain to challenge the block.
Expand All @@ -1013,18 +953,20 @@ class Nf3 {
this.intervalIDs.push(
setInterval(() => {
connection._ws.ping();
// logger.debug('sent websocket ping');
// logger.debug('sent challenge websocket ping');
}, WEBSOCKET_PING_TIME),
);
// and a listener for the pong
// connection._ws.on('pong', () => logger.debug('websocket received pong'));
logger.debug('Challenger websocket connection opened');
// connection._ws.on('pong', () => logger.debug('Challenge websocket received pong'));
logger.debug('Challenge websocket connection opened');
connection.send('challenge');
};
connection.onmessage = async message => {
const msg = JSON.parse(message.data);
const { type, txDataToSign } = msg;
const { type, txDataToSign, sender } = msg;
logger.debug(`Challenger received websocket message of type ${type}`);
// if we're about to challenge, check it's actually our challenge, so as not to waste gas
if (type === 'challenge' && sender !== this.ethereumAddress) return null;
if (type === 'commit' || type === 'challenge') {
challengerQueue.push(async () => {
try {
Expand All @@ -1038,14 +980,20 @@ class Nf3 {
challengeEmitter.emit('error', err, type);
}
});
logger.debug(`queued ${type} ${txDataToSign}`);
}
return null;
};
connection.onerror = () => logger.error('Challenger websocket connection error');
connection.onclosed = () => logger.warn('Challenger websocket connection closed');
connection.onerror = () => logger.error('websocket connection error');
connection.onclosed = () => logger.warn('websocket connection closed');
return challengeEmitter;
}

// method to turn challenges off and on. Note, this does not affect the queue
challengeEnable(enable) {
return axios.post(`${this.optimistBaseUrl}/challenger/enable`, { enable });
}

// eslint-disable-next-line class-methods-use-this
pauseQueueChallenger() {
return new Promise(resolve => {
Expand All @@ -1069,43 +1017,6 @@ class Nf3 {
challengerQueue.unshift(async () => logger.info(`queue challengerQueue has been unpaused`));
}

/**
Returns an emitter, whose 'data' event fires whenever a challengeable block is
detected, passing out the transaction needed to raise the challenge. This
is a lower level method than `Nf3.startChallenger` because it does not sign and
send the transaction to the blockchain. If required, `Nf3.submitTransaction`
can be used to do that.
@method
@async
@returns {Promise} A Promise that resolves into an event emitter.
*/
async getChallengeEmitter() {
const newChallengeEmitter = new EventEmitter();
const connection = new ReconnectingWebSocket(this.optimistWsUrl, [], { WebSocket });
this.websockets.push(connection); // save so we can close it properly later
connection.onopen = () => {
// setup a ping every 15s
this.intervalIDs.push(
setInterval(() => {
connection._ws.ping();
// logger.debug('sent websocket ping');
}, WEBSOCKET_PING_TIME),
);
// and a listener for the pong
// connection._ws.on('pong', () => logger.debug('websocket received pong'));
logger.debug('Challenger websocket connection opened');
connection.send('challenge');
};
connection.onmessage = async message => {
const msg = JSON.parse(message.data);
const { type, txDataToSign } = msg;
if (type === 'challenge') {
newChallengeEmitter.emit('data', txDataToSign);
}
};
return newChallengeEmitter;
}

/**
Returns the balance of tokens held in layer 2
@method
Expand Down
4 changes: 2 additions & 2 deletions common-files/classes/transaction.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
An optimistic Transaction class
*/
import gen from 'general-number';
import Web3 from '../utils/web3.mjs';
import Web3 from 'web3';
import { compressProof } from '../utils/curve-maths/curves.mjs';
import constants from '../constants/index.mjs';

Expand All @@ -23,7 +23,7 @@ const arrayEquality = (as, bs) => {

// function to compute the keccak hash of a transaction
function keccak(preimage) {
const web3 = Web3.connection();
const web3 = new Web3();
const {
value,
fee,
Expand Down
12 changes: 11 additions & 1 deletion common-files/utils/event-queue.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ async function enqueueEvent(callback, priority, args) {
});
}

/**
This function immediately and unceremoniously empties the queue. It should probably
be used with extreme care on a running queue because the exact state on emptying, and thus
the last job that ran, will be unclear. It will cause the end event to fire.
*/
function emptyQueue(priority) {
return queues[priority].end();
}

/**
These functions pause the queue once the current process at the head of the queue has
completed. It will then wait until we tell it to start again via unpause.
Expand Down Expand Up @@ -119,7 +128,7 @@ function waitForConfirmation(eventObject) {
}

async function dequeueEvent(priority) {
queues[priority].shift();
return queues[priority].shift();
}

async function queueManager(eventObject, eventArgs) {
Expand Down Expand Up @@ -169,4 +178,5 @@ export {
waitForConfirmation,
pauseQueue,
unpauseQueue,
emptyQueue,
};
Loading