Skip to content

Commit

Permalink
feat: robust multivalidator period handling (#379)
Browse files Browse the repository at this point in the history
* chore: warn if period height data is missing, we gonna miss period data in the db in this case

chore: move periodVotes out of consensus

Make validators store collected periodVotes in a bridgeState object directly, not in a
bridgeState.currentState. The latter represents a consensus state, something the nodes should agree upon.
Collected period votes have nothing to do with a consensus and only needed for proposing validator to submit
a period.

refactored period submission logic

store stalePeriodProposal in db as well

* improve coverage and fix some tests
  • Loading branch information
troggy authored Dec 18, 2019
1 parent 49adeee commit 7bc1c13
Show file tree
Hide file tree
Showing 34 changed files with 1,352 additions and 977 deletions.
7 changes: 4 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ async function run() {
db,
privKey,
config,
eventsRelay.relayBuffer
eventsRelay.relayBuffer,
sender
);
global.blockTicker = new BlockTicker(bridgeState.web3, [
bridgeState.onNewBlock,
Expand All @@ -91,8 +92,8 @@ async function run() {
const nodeConfig = Object.assign({}, cliArgs, { network: config });

app.useTx(txHandler(bridgeState, nodeConfig));
app.useBlock(blockHandler(bridgeState, db, nodeConfig, sender));
app.usePeriod(periodHandler(bridgeState, sender));
app.useBlock(blockHandler(bridgeState, db, nodeConfig));
app.usePeriod(periodHandler(bridgeState));

const lastGoodState = await bridgeState.loadState();

Expand Down
10 changes: 10 additions & 0 deletions src/api/createDb.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ const createDb = levelDb => {
await levelDb.put('chainState', JSON.stringify(state));
};

const getNodeState = () => {
return getNullable('nodeState');
};

const storeNodeState = async state => {
await levelDb.put('nodeState', JSON.stringify(state));
};

const storePeriods = async submissions => {
const dbOpsBatch = levelDb.batch();
await Promise.all(
Expand Down Expand Up @@ -135,8 +143,10 @@ const createDb = levelDb => {
getTransaction,
getTransactionByPrevOut,
getChainState,
getNodeState,
storeChainState,
storePeriods,
storeNodeState,
getPeriodData,
};
};
Expand Down
22 changes: 22 additions & 0 deletions src/api/createDb.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -308,4 +308,26 @@ describe('db', () => {
);
});
});

describe('nodeState', () => {
test('store', async () => {
const state = { someState: { state: 1 }};
const db = createDb(levelMock);

await db.storeNodeState(state);

expect(levelMock.put).toHaveBeenCalledWith(
'nodeState',
JSON.stringify(state)
);
});

test('read', async () => {
const db = createDb(levelMock);

await db.getNodeState();

expect(levelMock.get).toHaveBeenCalledWith('nodeState');
});
})
});
24 changes: 14 additions & 10 deletions src/block/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
const { logNode } = require('../utils/debug');

const addBlock = require('./addBlock');
const updatePeriod = require('./updatePeriod');
const updateValidators = require('./updateValidators');
const updateEpoch = require('./updateEpoch');
const isReplay = require('../period/utils/isReplay');
const handleSlotActivation = require('../validator/handleSlotActivation');
const handlePeriod = require('../validator/handlePeriod');

module.exports = (bridgeState, db, nodeConfig = {}, sender) => async (
module.exports = (bridgeState, db, nodeConfig = {}) => async (
state,
chainInfo
) => {
bridgeState.checkCallsCount = 0;
const { height } = chainInfo;

if (chainInfo.height % 32 === 0 && !isReplay(bridgeState)) {
bridgeState.checkCallsCount = 0;

if (height % 32 === 0 && !bridgeState.isReplay()) {
// catch this, it is not fatal if it fails here
logNode('Saving state');
try {
Expand All @@ -22,22 +24,24 @@ module.exports = (bridgeState, db, nodeConfig = {}, sender) => async (
}
}

// delete collected votes for submitted period
delete (state.periodVotes || {})[bridgeState.lastBlocksRoot];
await handlePeriod(height, bridgeState);

await updatePeriod(state, chainInfo, bridgeState, sender);
await handleSlotActivation(height, bridgeState);

await addBlock(state, chainInfo, {
bridgeState,
db,
});

if (!nodeConfig.no_validators_updates && state.slots.length > 0) {
await updateValidators(state, chainInfo);
}

updateEpoch(state, chainInfo);

logNode(
'Height: %d, epoch: %d, epochLength: %d',
chainInfo.height,
height,
state.epoch.epoch,
state.epoch.epochLength
);
Expand All @@ -46,5 +50,5 @@ module.exports = (bridgeState, db, nodeConfig = {}, sender) => async (
bridgeState.currentState = state;
bridgeState.blockHeight = chainInfo.height;

await bridgeState.saveSubmissions();
await bridgeState.saveNodeState();
};
86 changes: 0 additions & 86 deletions src/block/updatePeriod.js

This file was deleted.

Loading

0 comments on commit 7bc1c13

Please sign in to comment.