Mise à jour effectuée, merci de nous signaler tout dysfonctionnement ! | Upgrade done, please let us know about any dysfunction!

Commit 52825fda authored by Cédric Moreau's avatar Cédric Moreau

Vote is now called on CFlows reception

parent 154746b3
......@@ -51,7 +51,7 @@ module.exports = function (pgp, currency, conf) {
ParametersService.getAmendmentNumber(req, next);
},
function (amNumber, next){
Amendment.getTheOneToBeVoted(amNumber, next);
Amendment.getTheOneToBeVoted(amNumber, conf.sync.Algorithm, next);
},
], function (err, am) {
http.answer(res, 404, err, function () {
......@@ -235,17 +235,40 @@ module.exports = function (pgp, currency, conf) {
});
}
this.askFlow = function (req, res) {
var that = this;
async.waterfall([
// Parameters
function(next){
ParametersService.getAmendmentNumberAndAlgo(req, next);
},
function (amNumber, algo, next) {
SyncService.getFlow(parseInt(amNumber), algo, next)
},
], function (err, cf, am) {
http.answer(res, 404, err, function () {
res.end(JSON.stringify({
"communityflow": cf.json()
}, null, " "));
});
});
};
this.askVote = function (req, res) {
var that = this;
async.waterfall([
// Parameters
function(next){
ParametersService.getAmendmentNumber(req, next);
ParametersService.getAmendmentNumberAndAlgo(req, next);
},
function (amNumber, next) {
SyncService.getVote(amNumber, next)
function (amNumber, algo, next) {
SyncService.getVote(amNumber, algo, next);
},
function (vote, next){
......@@ -266,38 +289,5 @@ module.exports = function (pgp, currency, conf) {
});
};
function amendmentMerkle (req, res, merkleSource, merkleMap) {
ParametersService.getAmendmentNumber(req, function (err, number) {
if(err){
res.send(400, err);
return;
}
async.waterfall([
function (next){
Amendment.getTheOneToBeVoted(number, next);
},
], function (err, am) {
if(err){
res.send(404, err);
return;
}
async.waterfall([
function (next){
merkleSource.call(merkleSource, am.number, next);
},
function (merkle, next){
MerkleService.processForURL(req, merkle, merkleMap, next);
}
], function (err, json) {
if(err){
res.send(400, err);
return;
}
MerkleService.merkleDone(req, res, json);
});
});
});
}
return this;
}
......@@ -95,64 +95,84 @@ function Daemon () {
if (current && now > current.generated + AMFreq) {
var amNext = new Amendment({ generated: current.generated + AMFreq });
async.auto({
triggerSelfVote: function(callback){
// Case 1: just triggers self-vote
if (daemon.judges.timeForVote(amNext) && lastTried != amNext.generated) {
lastTried = amNext.generated;
// Must be a voter to vote!
Key.wasVoter(selfFingerprint, current.number, function (err, wasVoter) {
if (!err && wasVoter) {
logger.debug("Asking vote for SELF peer");
askVote(current, PeeringService.peer(), function (err, json) {
// Do nothing with result: it has been done by SyncService (self-submitted the vote)
callback(err);
});
return;
}
callback(err);
});
return;
}
callback();
},
triggerPeersVote: ['triggerSelfVote', function(callback){
if (daemon.judges.timeForAskingVotes(amNext)) {
// Case 2: triggers other peers' self-vote
async.forEach(PeeringService.upPeers(), function(peer, callback){
if (peer.fingerprint == selfFingerprint) {
callback();
return;
}
logger.debug("Asking vote for peer 0x%s", peer.fingerprint.substring(32));
// Must be a voter to vote!
Key.wasVoter(peer.fingerprint, current.number, function (err, wasVoter) {
if (!err && wasVoter) {
async.waterfall([
function (next){
askVote(current, peer, next);
},
function (json, next){
vucoin(peer.getIPv4(), peer.getPort(), true, function (err, node) {
next(null, json, node);
});
},
function (json, node, next){
var raw = json.amendment.raw;
var sig = json.signature;
node.hdc.amendments.votes.post(raw + sig, next);
},
], function (err) {
callback(err);
});
return;
}
callback(err);
});
}, next);
return;
}
callback();
}],
// triggerSelfCFlow: function(callback){
// // Case 1: just triggers self-vote
// if (daemon.judges.timeForVote(amNext) && lastTried != amNext.generated) {
// lastTried = amNext.generated;
// // Must be a voter to vote!
// Key.wasVoter(selfFingerprint, current.number, function (err, wasVoter) {
// if (!err && wasVoter) {
// logger.debug("Asking CF for SELF peer");
// askCF(current, PeeringService.peer(), function (err, json) {
// // Do nothing with result: it has been done by SyncService (self-submitted the vote)
// callback(err);
// });
// return;
// }
// callback(err);
// });
// return;
// }
// callback();
// },
// triggerSelfVote: function(callback){
// // Case 1: just triggers self-vote
// if (daemon.judges.timeForVote(amNext) && lastTried != amNext.generated) {
// lastTried = amNext.generated;
// // Must be a voter to vote!
// Key.wasVoter(selfFingerprint, current.number, function (err, wasVoter) {
// if (!err && wasVoter) {
// logger.debug("Asking vote for SELF peer");
// askVote(current, PeeringService.peer(), function (err, json) {
// // Do nothing with result: it has been done by SyncService (self-submitted the vote)
// callback(err);
// });
// return;
// }
// callback(err);
// });
// return;
// }
// callback();
// },
// triggerPeersVote: ['triggerSelfVote', function(callback){
// if (daemon.judges.timeForAskingVotes(amNext)) {
// // Case 2: triggers other peers' self-vote
// async.forEach(PeeringService.upPeers(), function(peer, callback){
// if (peer.fingerprint == selfFingerprint) {
// callback();
// return;
// }
// logger.debug("Asking vote for peer 0x%s", peer.fingerprint.substring(32));
// // Must be a voter to vote!
// Key.wasVoter(peer.fingerprint, current.number, function (err, wasVoter) {
// if (!err && wasVoter) {
// async.waterfall([
// function (next){
// askVote(current, peer, next);
// },
// function (json, next){
// vucoin(peer.getIPv4(), peer.getPort(), true, function (err, node) {
// next(null, json, node);
// });
// },
// function (json, node, next){
// var raw = json.amendment.raw;
// var sig = json.signature;
// node.hdc.amendments.votes.post(raw + sig, next);
// },
// ], function (err) {
// callback(err);
// });
// return;
// }
// callback(err);
// });
// }, next);
// return;
// }
// callback();
// }],
}, next);
}
else {
......@@ -186,6 +206,21 @@ function Daemon () {
});
}
function askCF (current, peer, done) {
async.auto({
connect: function (cb){
vucoin(peer.getIPv4(), peer.getPort(), true, cb);
},
vote: ['connect', function (cb, results) {
var node = results.connect;
// Ask for peer's vote
node.registry.amendment.vote(current.number + 1, cb);
}]
}, function (err, results) {
done(err, results.vote);
});
}
this.stop = function () {
clearTimeout(timeoutID);
};
......
......@@ -17,7 +17,7 @@ var logger = require('./logger')('http');
var pgplogger = require('./logger')('PGP');
var log4js = require('log4js');
var models = ['Amendment', 'Coin', 'Configuration', 'Forward', 'Key', 'Merkle', 'Peer', 'PublicKey', 'Wallet', 'Transaction', 'Vote', 'TxMemory', 'Membership', 'Voting', 'CommunityFlow'];
var models = ['Amendment', 'Coin', 'Configuration', 'Forward', 'Key', 'CKey', 'Merkle', 'Peer', 'PublicKey', 'Wallet', 'Transaction', 'Vote', 'TxMemory', 'Membership', 'Voting', 'CommunityFlow'];
function initModels() {
models.forEach(function (entity) {
......@@ -118,6 +118,7 @@ module.exports.database = {
'coins',
'forwards',
'keys',
'ckeys',
'merkles',
'peers',
'publickeys',
......@@ -341,12 +342,13 @@ module.exports.express = {
app.get( '/registry/community/voters/:fpr/current', reg.votingCurrent);
app.get( '/registry/community/voters/:fpr/history', reg.votingHistory);
app.get( '/registry/amendment', reg.amendmentCurrent);
app.get( '/registry/amendment/:amendment_number', reg.amendmentNext);
app.get( '/registry/amendment/:amendment_number/members/in', reg.membersIn);
app.get( '/registry/amendment/:amendment_number/members/out',reg.membersOut);
app.get( '/registry/amendment/:amendment_number/voters/in', reg.votersIn);
app.get( '/registry/amendment/:amendment_number/voters/out', reg.votersOut);
app.get( '/registry/amendment/:amendment_number/vote', reg.askVote);
app.get( '/registry/amendment/:am_number', reg.amendmentNext);
app.get( '/registry/amendment/:am_number/:algo/members/in', reg.membersIn);
app.get( '/registry/amendment/:am_number/:algo/members/out',reg.membersOut);
app.get( '/registry/amendment/:am_number/:algo/voters/in', reg.votersIn);
app.get( '/registry/amendment/:am_number/:algo/voters/out', reg.votersOut);
app.get( '/registry/amendment/:am_number/:algo/flow', reg.askFlow);
app.get( '/registry/amendment/:am_number/:algo/vote', reg.askVote);
// If the node's peering entry does not exist or is outdated,
// a new one is generated.
......@@ -467,7 +469,7 @@ module.exports.express = {
function (currentAM, next) {
var nextAMNumber = currentAM && currentAM.number + 1 || 0;
// Create NEXT AM proposal if not existing
mongoose.model('Amendment').getTheOneToBeVoted(nextAMNumber, function (err, am) {
mongoose.model('Amendment').getTheOneToBeVoted(nextAMNumber, conf.sync.Algorithm, function (err, am) {
if (err || !am) {
logger.info('Creating next AM (#%d) proposal...', nextAMNumber);
SyncService.createNext(currentAM, next);
......
......@@ -12,6 +12,7 @@ var AmendmentSchema = new Schema({
currency: String,
number: {"type": Number, "default": 0},
generated: {"type": Number, "default": 0},
algo: String,
dividend: Number,
coinAlgo: String,
coinBase: Number,
......@@ -52,6 +53,7 @@ AmendmentSchema.methods = {
"currency",
"number",
"generated",
"algo",
"dividend",
"coinAlgo",
"coinBase",
......@@ -299,9 +301,9 @@ AmendmentSchema.statics.getPreviouslyPromotedWithDividend = function (done) {
});
};
AmendmentSchema.statics.getTheOneToBeVoted = function (number, done) {
AmendmentSchema.statics.getTheOneToBeVoted = function (number, algo, done) {
this.find({ number: number, selfGenerated: true }, function (err, amends) {
this.find({ number: number, algo: algo, selfGenerated: true }, function (err, amends) {
if(amends && amends.length == 1){
done(err, amends[0]);
return;
......
var mongoose = require('mongoose');
var async = require('async');
var _ = require('underscore');
var Schema = mongoose.Schema;
var logger = require('../../app/lib/logger')('key model');
var CKeySchema = new Schema({
fingerprint: { type: String },
operation: { type: String },
algorithm: { type: String },
member: { type: Boolean, default: false },
count: { type: Number, default: 0 },
created: { type: Date, default: Date.now },
updated: { type: Date, default: Date.now }
});
CKeySchema.pre('save', function (next) {
this.updated = Date.now();
next();
});
CKeySchema.statics.increment = function(leaf, op, algo, isMember, done) {
CKey.update({ fingerprint: leaf, operation: op, algorithm: algo, member: isMember }, { $inc: { count: 1 }}, done);
}
CKeySchema.statics.findThose = function(op, algo, isMember, done) {
CKey.find({ operation: op, algorithm: algo, member: isMember }, done);
}
var CKey = mongoose.model('CKey', CKeySchema);
......@@ -8,10 +8,11 @@ var Schema = mongoose.Schema;
var CommunityFlowSchema = new Schema({
version: String,
currency: String,
issuer: { type: String, unique: true },
issuer: { type: String },
date: { type: Date },
amendmentNumber: Number,
amendmentHash: String,
algorithm: String,
membersJoiningCount: Number,
membersJoiningRoot: String,
membersLeavingCount: Number,
......@@ -22,6 +23,7 @@ var CommunityFlowSchema = new Schema({
votersLeavingRoot: String,
signature: String,
propagated: { type: Boolean, default: false },
selfGenerated: { type: Boolean, default: false },
hash: String,
sigDate: { type: Date, default: function(){ return new Date(0); } },
created: { type: Date, default: Date.now },
......@@ -39,6 +41,35 @@ CommunityFlowSchema.methods = {
return this.issuer && this.issuer.length > 24 ? "0x" + this.issuer.substring(24) : "0x?";
},
json: function() {
var that = this;
var json = { raw: this.getRaw() };
[
"version",
"amendmentNumber",
"membersJoiningCount",
"membersLeavingCount",
"votersJoiningCount",
"votersLeavingCount",
].forEach(function(field){
json[field] = parseInt(that[field], 10);
});
[
"currency",
"amendmentHash",
"algorithm",
"membersJoiningRoot",
"membersLeavingRoot",
"votersJoiningRoot",
"votersLeavingRoot",
"issuer",
].forEach(function(field){
json[field] = that[field] || "";
});
json.date = this.date && this.date.timestamp();
return json;
},
parse: function(rawEntryReq, callback) {
var rawEntry = rawEntryReq;
var sigIndex = rawEntryReq.lastIndexOf("-----BEGIN");
......@@ -61,6 +92,7 @@ CommunityFlowSchema.methods = {
{prop: "currency", regexp: /Currency: (.*)/},
{prop: "amendmentNumber", regexp: /Amendment: (.*)/, parser: parseAmendmentNumber},
{prop: "amendmentHash", regexp: /Amendment: (.*)/, parser: parseAmendmentHash},
{prop: "algorithm", regexp: /Algorithm: (.*)/},
{prop: "membersJoiningCount", regexp: /MembersJoining: (.*)/, parser: parseMerkleNumber},
{prop: "membersJoiningRoot", regexp: /MembersJoining: (.*)/, parser: parseMerkleRoot},
{prop: "membersLeavingCount", regexp: /MembersLeaving: (.*)/, parser: parseMerkleNumber},
......@@ -117,10 +149,15 @@ CommunityFlowSchema.methods = {
raw += "Amendment: " + [this.amendmentNumber, this.amendmentHash].join('-') + "\n";
raw += "Issuer: " + this.issuer + "\n";
raw += "Date: " + this.date.timestamp() + "\n";
raw += "MembersJoining: " + [this.membersJoiningCount, this.membersJoiningRoot].join('-') + "\n";
raw += "MembersLeaving: " + [this.membersLeavingCount, this.membersLeavingRoot].join('-') + "\n";
raw += "VotersJoining: " + [this.votersJoiningCount, this.votersJoiningRoot].join('-') + "\n";
raw += "VotersLeaving: " + [this.votersLeavingCount, this.votersLeavingRoot].join('-') + "\n";
raw += "Algorithm: " + this.algorithm + "\n";
if (this.membersJoiningRoot)
raw += "MembersJoining: " + [this.membersJoiningCount, this.membersJoiningRoot].join('-') + "\n";
if (this.membersLeavingRoot)
raw += "MembersLeaving: " + [this.membersLeavingCount, this.membersLeavingRoot].join('-') + "\n";
if (this.votersJoiningRoot)
raw += "VotersJoining: " + [this.votersJoiningCount, this.votersJoiningRoot].join('-') + "\n";
if (this.votersLeavingRoot)
raw += "VotersLeaving: " + [this.votersLeavingCount, this.votersLeavingRoot].join('-') + "\n";
return raw.unix2dos();
},
......@@ -211,8 +248,24 @@ function simpleLineExtraction(pr, rawEntry, cap, parser) {
return;
}
CommunityFlowSchema.statics.getTheOne = function (amNumber, amHash, done) {
this.find({ amNumber: amNumber, amHash: amHash }, function (err, entries) {
CommunityFlowSchema.statics.getTheOne = function (amNumber, issuer, algo, done) {
this.find({ amendmentNumber: amNumber, issuer: issuer, algorithm: algo }, function (err, entries) {
if(entries && entries.length == 1){
done(err, entries[0]);
return;
}
if(!entries || entries.length == 0){
done('No CommunityFlow entry found');
return;
}
if(entries || entries.length > 1){
done('More than one CommunityFlow entry found');
}
});
}
CommunityFlowSchema.statics.getSelf = function (amNumber, algo, done) {
this.find({ amendmentNumber: amNumber, algorithm: algo, selfGenerated: true }, function (err, entries) {
if(entries && entries.length == 1){
done(err, entries[0]);
return;
......@@ -227,4 +280,8 @@ CommunityFlowSchema.statics.getTheOne = function (amNumber, amHash, done) {
});
}
CommunityFlowSchema.statics.getForAmendmentAndAlgo = function (amNumber, algo, done) {
this.find({ amendmentNumber: amNumber, algorithm: algo }, done);
}
var CommunityFlow = mongoose.model('CommunityFlow', CommunityFlowSchema);
......@@ -26,7 +26,8 @@ var ConfigurationSchema = new Schema({
UDPercent: 0.007376575, // 0.73%
Consensus: 2/3,
MSExpires: 3600*24*30.4375*6, // 6 months
VTExpires: 3600*24*30.4375*1 // 1 months
VTExpires: 3600*24*30.4375*1, // 1 months
Algorithm: 'AnyKey'
}}
});
......
......@@ -99,6 +99,10 @@ MerkleSchema.methods = {
leaves: function () {
return this.levels[this.depth];
},
count: function () {
return this.leaves().length;
}
};
......@@ -153,20 +157,20 @@ MerkleSchema.statics.proposedVoters = function (done) {
retrieve({ type: 'proposedVoters', criteria: '{}' }, done);
};
MerkleSchema.statics.membersIn = function (number, done) {
retrieve({ type: 'membersIn', criteria: '{"number":'+number+'}' }, done);
MerkleSchema.statics.membersIn = function (number, algo, done) {
retrieve({ type: 'membersIn', criteria: '{"number":'+number+',algo:'+algo+'}' }, done);
};
MerkleSchema.statics.membersOut = function (number, done) {
retrieve({ type: 'membersOut', criteria: '{"number":'+number+'}' }, done);
MerkleSchema.statics.membersOut = function (number, algo, done) {
retrieve({ type: 'membersOut', criteria: '{"number":'+number+',algo:'+algo+'}' }, done);
};
MerkleSchema.statics.votersIn = function (number, done) {
retrieve({ type: 'votersIn', criteria: '{"number":'+number+'}' }, done);
MerkleSchema.statics.votersIn = function (number, algo, done) {
retrieve({ type: 'votersIn', criteria: '{"number":'+number+',algo:'+algo+'}' }, done);
};
MerkleSchema.statics.votersOut = function (number, done) {
retrieve({ type: 'votersOut', criteria: '{"number":'+number+'}' }, done);
MerkleSchema.statics.votersOut = function (number, algo, done) {
retrieve({ type: 'votersOut', criteria: '{"number":'+number+',algo:'+algo+'}' }, done);
};
MerkleSchema.statics.updatePeers = function (peer, previousHash, done) {
......
......@@ -254,6 +254,17 @@ VoteSchema.statics.getSelf = function (amNumber, done) {
});
};
VoteSchema.statics.getSelfForAlgo = function (amNumber, algo, done) {
this
.find({ selfGenerated: true, algo: algo, basis: amNumber })
.sort({ 'sigDate': -1 })
.limit(1)
.exec(function (err, votes) {
done(null, votes.length == 1 ? votes[0] : null);
});
};
VoteSchema.statics.getByIssuerHashAndBasis = function (issuer, hash, amNumber, done) {
this
......
......@@ -34,7 +34,7 @@ module.exports.get = function (currency, conf) {
});
},
function (next){
Amendment.getTheOneToBeVoted(current ? current.number + 1 : 0, function (err, am) {
Amendment.getTheOneToBeVoted(current ? current.number + 1 : 0, conf.sync.Algorithm, function (err, am) {
proposed = am;
next();
});
......
......@@ -175,11 +175,11 @@ function ParameterNamespace (currency) {
};
this.getAmendmentNumber = function (req, callback) {
if(!req.params || !req.params.amendment_number){
if(!req.params || !req.params.am_number){
callback("Amendment number is required");
return;
}
var matches = req.params.amendment_number.match(/^(\d+)$/);
var matches = req.params.am_number.match(/^(\d+)$/);
if(!matches){
callback("Amendment number format is incorrect, must be an integer value");
return;
......@@ -187,6 +187,28 @@ function ParameterNamespace (currency) {
callback(null, matches[1]);
};
this.getAmendmentNumberAndAlgo = function (req, callback) {
if(!req.params || !req.params.am_number){
callback("Amendment number is required");
return;
}
if(!req.params || !req.params.algo){
callback("Algorithm is required");
return;
}
var matchAMNumber = req.params.am_number.match(/^(\d+)$/);
if(!matchAMNumber){
callback("Amendment number format is incorrect, must be an integer value");
return;
}
var matchAlgo = req.params.algo.match(/^(AnyKey|1Sig)$/);
if(!matchAlgo){
callback("Algorithm is incorrect, must be either AnyKey or 1Sig");
return;
}
callback(null, matchAMNumber[1], matchAlgo[1]);
};
this.getCoinID = function (req, callback) {
if(!req.params || !req.params.coin_id){
callback("Coin ID is required");
......
......@@ -9,6 +9,7 @@ var Voting = mongoose.model('Voting');
var PublicKey = mongoose.model('PublicKey');
var Merkle = mongoose.model('Merkle');
var Vote = mongoose.model('Vote');
var CKey = mongoose.model('CKey');
var Coin = mongoose.model('Coin');
var Key = mongoose.model('Key');
var logger = require('../lib/logger')('amendment');
......@@ -57,6 +58,7 @@ module.exports.get = function (pgp, currency, conf) {
addVoters: async.apply(async.forEach, am.getNewVoters(), Key.addVoter),
removeMembers: async.apply(async.forEach, am.getLeavingMembers(), Key.removeMember),
removeVoters: async.apply(async.forEach, am.getLeavingVoters(), Key.removeVoter),
removeCKeys: async.apply(CKey.remove.bind(CKey), {}),
// Leaving voters have no more current voting document
currentVoting: async.apply(async.forEach, am.getLeavingVoters(), async.apply(Voting</