Skip to content

Commit

Permalink
Add validations of block heights
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulBernier committed Oct 12, 2018
1 parent 76ed575 commit 8058c2a
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 33 deletions.
6 changes: 3 additions & 3 deletions dist/factom-vote-struct.js

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions dist/factom-vote.js

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "factom-vote",
"version": "0.0.10",
"version": "0.0.11",
"description": "JS implementation of the Factom voting specification",
"main": "src/factom-vote.js",
"scripts": {
Expand Down Expand Up @@ -44,7 +44,7 @@
"@babel/runtime": "^7.1.2",
"babel-loader": "^8.0.4",
"chai": "^4.2.0",
"dotenv": "^6.0.0",
"dotenv": "^6.1.0",
"eslint": "^5.6.1",
"mocha": "^5.2.0",
"nyc": "^13.0.1",
Expand Down
12 changes: 6 additions & 6 deletions src/factom-vote-manager.js
Expand Up @@ -13,27 +13,27 @@ class FactomVoteManager {
}
}

async createVoteRegistrationChain(ecAddress, nonce) {
createVoteRegistrationChain(ecAddress, nonce) {
return createVoteRegistrationChain(this.cli, nonce, ecAddress);
}

async createVote(voteData, ecAddress) {
createVote(voteData, ecAddress) {
return createVote(this.cli, voteData, ecAddress);
}

async appendEligibleVoters(appendEligibleVotersData, ecAddress) {
appendEligibleVoters(appendEligibleVotersData, ecAddress) {
return appendEligibleVoters(this.cli, appendEligibleVotersData, ecAddress);
}

async getVote(chainId) {
getVote(chainId) {
return getVote(this.cli, chainId);
}

async commitVote(voteChainId, vote, voter, ecAddress) {
commitVote(voteChainId, vote, voter, ecAddress) {
return commitVote(this.cli, voteChainId, vote, voter, ecAddress);
}

async revealVote(voteChainId, vote, voterId, ecAddress) {
revealVote(voteChainId, vote, voterId, ecAddress) {
return revealVote(this.cli, voteChainId, vote, voterId, ecAddress);
}

Expand Down
16 changes: 14 additions & 2 deletions src/read-vote/read-vote.js
@@ -1,5 +1,5 @@
const { processParsedVote } = require('./process-vote'),
{ parseVote } = require('./parse-vote'),
{ parseVote, parseVoteChainEntry } = require('./parse-vote'),
{ computeResult } = require('./compute-vote-result');

async function getVote(cli, chainId) {
Expand All @@ -12,6 +12,18 @@ async function getVote(cli, chainId) {
};
}

async function getVoteDefinition(cli, chainId) {
const firstEntry = await cli.getFirstEntry(chainId);
const parsed = await parseVoteChainEntry(cli, firstEntry);

if (parsed.type === 'definition') {
return parsed;
} else {
throw new Error(`Invalid vote first entry in chain [${chainId}].`);
}
}

module.exports = {
getVote
getVote,
getVoteDefinition
};
2 changes: 1 addition & 1 deletion src/write-vote/create-vote.js
Expand Up @@ -17,7 +17,7 @@ async function createVote(cli, voteData, ecAddress) {
const chainsToCreate = [];

if (!defCopy.vote.eligibleVotersChainId) {
const height = cli.getHeights().then(heights => heights.leaderHeight);
const height = await cli.getHeights().then(heights => heights.leaderHeight);

if (defCopy.vote.phasesBlockHeights.commitStart <= height) {
throw new Error(`Current height (${height}) is higher than commitStart height (${defCopy.vote.phasesBlockHeights.commitStart}) making your new eligible voters list unusable.`);
Expand Down
17 changes: 17 additions & 0 deletions src/write-vote/participate-vote.js
@@ -1,11 +1,16 @@
const { getPublicAddress } = require('factom'),
{ getVoteIdentity } = require('../factom-identity'),
{ getVoteDefinition } = require('../read-vote/read-vote'),
{ generateVoteCommitEntry, generateVoteRevealEntry } = require('./participate-vote-struct');

async function commitVote(cli, voteChainId, vote, identity, ecAddress) {
// TODO: possible online validation (commitVoteSafe?):
// config (possible options, min,max...)
// voter is an eliglbe voter
const definition = await getVoteDefinition(cli, voteChainId);
const { commitStart, commitEnd } = definition.data.vote.phasesBlockHeights;
await validateBlockHeightInterval(cli, 'commit', commitStart, commitEnd);

const voter = await getVoteIdentity(cli, identity);
const entry = generateVoteCommitEntry(voteChainId, vote, voter);
validateFunds(cli, entry.ecCost(), ecAddress, 'Cannot commit vote');
Expand All @@ -17,6 +22,10 @@ async function revealVote(cli, voteChainId, vote, voterId, ecAddress) {
// TODO: possible online validation (revealVoteSafe?):
// reveal match the commit

const definition = await getVoteDefinition(cli, voteChainId);
const { revealStart, revealEnd } = definition.data.vote.phasesBlockHeights;
await validateBlockHeightInterval(cli, 'commit', revealStart, revealEnd);

const entry = generateVoteRevealEntry(voteChainId, vote, voterId);
validateFunds(cli, entry.ecCost(), ecAddress, 'Cannot reveal vote');

Expand All @@ -31,6 +40,14 @@ async function validateFunds(cli, ecCost, ecAddress, message) {
}
}

async function validateBlockHeightInterval(cli, action, start, end) {
const currentHeight = await cli.getHeights().then(h => h.leaderHeight);

if (currentHeight < start || currentHeight > end) {
throw new Error(`Current block height ${currentHeight} is not within the block height range of the vote ${action}: [${start}, ${end}]`);
}
}


module.exports = {
generateVoteCommitEntry,
Expand Down
19 changes: 17 additions & 2 deletions test/create-vote.spec.js
Expand Up @@ -10,9 +10,9 @@ describe('Create vote', function () {

it('should create a vote', async function () {
this.timeout(20000);
const definition = JSON.parse(JSON.stringify(require('./data/vote-definition')));
delete definition.vote.eligibleVotersChainId;

const registrationChainId = 'a968e880ee3a7002f25ade15ae36a77c15f4dbc9d8c11fdd5fe86ba6af73a475';
const definition = await getVoteDefinition();
const eligibleVoters = require('./data/eligible-voters');
const identity = { chainId: '2d98021e3cf71580102224b2fcb4c5c60595e8fdf6fd1b97c6ef63e9fb3ed635', key: 'idsec2Vn3VT8FdE1YpcDms8zSvXR4DGzQeMMdeLRP2RbMCSWCFoQDbS' };
const voteData = {
Expand All @@ -26,6 +26,21 @@ describe('Create vote', function () {
assert.isObject(result.registration);
});

async function getVoteDefinition() {
const definition = JSON.parse(JSON.stringify(require('./data/vote-definition')));
delete definition.vote.eligibleVotersChainId;
const currentHeight = await cli.getHeights().then(h => h.leaderHeight);

definition.vote.phasesBlockHeights = {
commitStart: currentHeight + 10,
commitEnd: currentHeight + 20,
revealStart: currentHeight + 30,
revealEnd: currentHeight + 40
};

return definition;
}

xit('should append eligible voters', async function () {
this.timeout(10000);
const identityKey = 'idsec2Vn3VT8FdE1YpcDms8zSvXR4DGzQeMMdeLRP2RbMCSWCFoQDbS';
Expand Down
87 changes: 78 additions & 9 deletions test/participate-vote.spec.js
@@ -1,16 +1,17 @@
const { FactomCli } = require('factom'),
const { FactomCli, Entry } = require('factom'),
assert = require('chai').assert,
sinon = require('sinon'),
crypto = require('crypto'),
participateVote = require('../src/write-vote/participate-vote');
{ keyToSecretIdentityKey, getPublicIdentityKey } = require('../src/factom-identity'),
participateVote = require('../src/write-vote/participate-vote'),
{ generateVoteChain } = require('../src/write-vote/create-vote-struct');

require('dotenv').config();

const cli = new FactomCli({ host: process.env.FACTOM_HOST });

describe('Participate vote', function () {

it('should commit vote', async function () {
this.timeout(10000);
const voteChainId = 'c973b2db5a4959c64606f7df7903918737f226a0ffaf93911f192582878b29eb';
const vote = {
vote: ['yes', 'maybe'],
Expand All @@ -19,12 +20,49 @@ describe('Participate vote', function () {
};
const voter = { chainId: '2d98021e3cf71580102224b2fcb4c5c60595e8fdf6fd1b97c6ef63e9fb3ed635', key: 'idsec1wnZ9FLheMDXZNnnDHXdqZcMiDrgg2hTNzdseNLwFnEot362c4' };

const result = await participateVote.commitVote(cli, voteChainId, vote, voter, process.env.EC_PRIVATE_KEY);
assert.isObject(result);
const cli = new FactomCli();
const mock = sinon.mock(cli);
const definition = require('./data/vote-definition');
const initiator = { id: crypto.randomBytes(32), secretKey: crypto.randomBytes(32) };
const initiatorPublicIdentityKey = getPublicIdentityKey(keyToSecretIdentityKey(initiator.secretKey));

const firstEntry = Entry.builder(generateVoteChain(definition, initiator).firstEntry)
.blockContext({ directoryBlockHeight: 700 })
.build();
mock.expects('getFirstEntry')
.withArgs(voteChainId)
.returns(Promise.resolve(firstEntry));
mock.expects('getHeights')
.twice()
.returns(Promise.resolve({ leaderHeight: 1050 }));
mock.expects('getBalance')
.once()
.withArgs(process.env.EC_PRIVATE_KEY)
.returns(Promise.resolve(1));
mock.expects('walletdApi')
.once()
.withArgs('identity-keys-at-height', {
chainid: initiator.id.toString('hex'),
height: 700
})
.returns(Promise.resolve({ keys: [initiatorPublicIdentityKey] }));
mock.expects('walletdApi')
.once()
.withArgs('identity-keys-at-height', {
chainid: voter.chainId,
height: 1049
})
.returns(Promise.resolve({ keys: ['idpub3Doj5fqXye8PkX8w83hzPh3PXbiLhrxTZjT6sXmtFQdDyzwymz'] }));
mock.expects('add')
.once();

await participateVote.commitVote(cli, voteChainId, vote, voter, process.env.EC_PRIVATE_KEY);

mock.verify();
});


it('should reveal vote', async function () {
this.timeout(10000);
const voteChainId = 'c973b2db5a4959c64606f7df7903918737f226a0ffaf93911f192582878b29eb';

const vote = {
Expand All @@ -34,8 +72,39 @@ describe('Participate vote', function () {
};
const voterId = crypto.randomBytes(32);

const result = await participateVote.revealVote(cli, voteChainId, vote, voterId, process.env.EC_PRIVATE_KEY);
assert.isObject(result);
const cli = new FactomCli();
const mock = sinon.mock(cli);
const definition = require('./data/vote-definition');
const initiator = { id: crypto.randomBytes(32), secretKey: crypto.randomBytes(32) };
const initiatorPublicIdentityKey = getPublicIdentityKey(keyToSecretIdentityKey(initiator.secretKey));

const firstEntry = Entry.builder(generateVoteChain(definition, initiator).firstEntry)
.blockContext({ directoryBlockHeight: 700 })
.build();
mock.expects('getFirstEntry')
.withArgs(voteChainId)
.returns(Promise.resolve(firstEntry));
mock.expects('getHeights')
.once()
.returns(Promise.resolve({ leaderHeight: 2500 }));
mock.expects('getBalance')
.once()
.withArgs(process.env.EC_PRIVATE_KEY)
.returns(Promise.resolve(1));
mock.expects('walletdApi')
.once()
.withArgs('identity-keys-at-height', {
chainid: initiator.id.toString('hex'),
height: 700
})
.returns(Promise.resolve({ keys: [initiatorPublicIdentityKey] }));

mock.expects('add')
.once();

await participateVote.revealVote(cli, voteChainId, vote, voterId, process.env.EC_PRIVATE_KEY);

mock.verify();
});

});
Expand Down

0 comments on commit 8058c2a

Please sign in to comment.