diff --git a/app/lib/server.js b/app/lib/server.js index 95b8f19572771b443522f9bf939dc70fc0d89021..694319781e872720815f27939de4cb60cbc5cc99 100644 --- a/app/lib/server.js +++ b/app/lib/server.js @@ -147,6 +147,32 @@ module.exports.database = { } }; +module.exports.openpgp = { + + init: function (currency, conf, done) { + + // Import PGP key + openpgp.keyring.importPrivateKey(conf.pgpkey, conf.pgppasswd); + if (!module.exports.fingerprint()) { + pgplogger.error("Wrong PGP key password."); + process.exit(1); + return; + } + done(); + } +}; + +function initServices (currency, conf, done) { + // Init ALL services + service.init(openpgp, currency, conf); + // Load services contexts + service.load(done); +} + +module.exports.services = { + init: initServices +}; + module.exports.express = { app: function (currency, conf, onLoaded) { @@ -166,16 +192,19 @@ module.exports.express = { app.use(express.cookieParser('your secret here')); app.use(express.session()); - // Import PGP key - openpgp.keyring.importPrivateKey(conf.pgpkey, conf.pgppasswd); - if (!module.exports.fingerprint()) { - pgplogger.error("Wrong PGP key password."); - process.exit(1); - return; - } - - // HTTP Signatures - httpgp(app, conf, function (err) { + async.series([ + function (next) { + // OpenPGP functions + module.exports.openpgp.init(currency, conf, next); + }, + function (next){ + initServices(currency, conf, next); + }, + function (next){ + // HTTP Signatures + httpgp(app, conf, next); + }, + ], function(err) { // HTTPGP OK? if (err) { @@ -192,9 +221,6 @@ module.exports.express = { app.use(express.errorHandler()); } - // Init ALL services - service.init(openpgp, currency, conf); - // Init Daemon var daemon = require('./daemon'); daemon.init(conf, module.exports.fingerprint()); @@ -273,6 +299,25 @@ module.exports.express = { var SyncService = service.Sync; async.waterfall([ + function (next) { + if(conf.ipv4){ + logger.info('Connecting on interface %s...', conf.ipv4); + http.createServer(app).listen(conf.port, conf.ipv4, function(){ + logger.info('uCoin server listening on ' + conf.ipv4 + ' port ' + conf.port); + next(); + }); + } + else next(); + }, + function (next) { + if(conf.ipv6){ + logger.info('Connecting on interface %s...', conf.ipv6); + http.createServer(app).listen(conf.port, conf.ipv6, function(){ + logger.info('uCoin server listening on ' + conf.ipv6 + ' port ' + conf.port); + }); + } + else next(); + }, function (next) { mongoose.model('Peer').find({ fingerprint: module.exports.fingerprint() }, next); }, @@ -311,7 +356,7 @@ module.exports.express = { }, function (signature, next) { signature = signature.substring(signature.indexOf('-----BEGIN PGP SIGNATURE')); - PeeringService.persistPeering(raw2 + signature, module.exports.publicKey(), next); + PeeringService.submit(raw2 + signature, module.exports.fingerprint(), next); }, ], function (err) { next(err); @@ -321,11 +366,11 @@ module.exports.express = { } }, function (next){ - // Load services contexts - service.load(next); + mongoose.model('Peer').getTheOne(module.exports.fingerprint(), next); }, - function (next){ + function (peer, next){ // Set peer's statut to UP + PeeringService.peer(peer); PeeringService.peer().status = 'UP'; PeeringService.peer().save(function (err) { // Update it in memory @@ -333,25 +378,6 @@ module.exports.express = { next(err); }); }, - function (next) { - if(conf.ipv4){ - logger.debug('Connecting on interface %s...', conf.ipv4); - http.createServer(app).listen(conf.port, conf.ipv4, function(){ - logger.debug('uCoin server listening on ' + conf.ipv4 + ' port ' + conf.port); - next(); - }); - } - else next(); - }, - function (next) { - if(conf.ipv6){ - logger.debug('Connecting on interface %s...', conf.ipv6); - http.createServer(app).listen(conf.port, conf.ipv6, function(){ - logger.debug('uCoin server listening on ' + conf.ipv6 + ' port ' + conf.port); - }); - } - else next(); - }, function (next) { // Initialize managed keys PeeringService.initKeys(next); @@ -370,12 +396,18 @@ module.exports.express = { logger.info('Broadcasting UP/NEW signals...'); PeeringService.sendUpSignal(next); }, - function (next) { - // Create AM0 proposal if not existing - mongoose.model('Amendment').getTheOneToBeVoted(0, function (err, am) { + function (next){ + mongoose.model('Amendment').current(function (err, am) { + next(null, am); + }); + }, + 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) { if (err || !am) { - logger.info('Creating root AM proposal...'); - SyncService.createNext(null, next); + logger.info('Creating next AM (#%d) proposal...', nextAMNumber); + SyncService.createNext(currentAM, next); return; } next(); diff --git a/app/lib/sync.js b/app/lib/sync.js index 99a761672098a7c1076e753ad35673314b797cf7..fb744a3e3043c7c973517eb7dc587933aa1b7774 100644 --- a/app/lib/sync.js +++ b/app/lib/sync.js @@ -7,6 +7,8 @@ var Amendment = mongoose.model('Amendment'); var PublicKey = mongoose.model('PublicKey'); var Merkle = mongoose.model('Merkle'); var Key = mongoose.model('Key'); +var Membership = mongoose.model('Membership'); +var Voting = mongoose.model('Voting'); var Transaction = mongoose.model('Transaction'); var THTEntry = mongoose.model('THTEntry'); var Peer = mongoose.model('Peer'); @@ -24,8 +26,9 @@ var THTService = service.THT; var PeeringService = service.Peering; var StrategyService = service.Strategy; var ParametersService = service.Parameters; +var SyncService = service.Sync; -module.exports = function Synchroniser (host, port, authenticated, pgp, currency, conf) { +module.exports = function Synchroniser (host, port, authenticated, currency, conf) { var that = this; this.remoteFingerprint = null; @@ -53,7 +56,7 @@ module.exports = function Synchroniser (host, port, authenticated, pgp, currency // Peer //============ function (next){ - node.ucg.peering.peer(next); + node.ucg.peering.get(next); }, function (json, next){ remotePeer.copyValuesFrom(json); @@ -152,13 +155,12 @@ module.exports = function Synchroniser (host, port, authenticated, pgp, currency err.split('\n').forEach(function (msg) { logger.warn(msg); }); - remoteCurrentNumber = -1; - next(null, -2); + next(); return; } remoteCurrentNumber = parseInt(json.number); amendments[remoteCurrentNumber] = json.raw; - var toGetNumbers = _.range(number, remoteCurrentNumber); + var toGetNumbers = _.range(number, remoteCurrentNumber + 1); async.forEachSeries(toGetNumbers, function(amNumber, callback){ async.waterfall([ function (cb){ @@ -169,45 +171,25 @@ module.exports = function Synchroniser (host, port, authenticated, pgp, currency }, function (am, cb){ amendments[amNumber] = am.raw; - node.hdc.amendments.promoted(amNumber + 1, cb); + node.hdc.amendments.promoted(amNumber, cb); }, function (am, cb){ - amendments[amNumber + 1] = am.raw; + amendments[amNumber] = am.raw; cb(); }, function (cb) { - node.hdc.amendments.view.signatures(amNumber + 1, sha1(amendments[amNumber + 1]).toUpperCase(), { leaves: true }, cb); + node.hdc.amendments.view.signatures(amNumber, sha1(amendments[amNumber]).toUpperCase(), { leaves: true }, cb); }, function (json, cb){ applyVotes(amendments, amNumber, number, json, node, cb); - }, - function (nextNumber, cb) { - number = nextNumber; - cb(); } ], function (err, result) { callback(err); }); }, function(err, result){ - next(err, number); - }); - }); - }, - function (number, next) { - if(number == remoteCurrentNumber){ - // Synchronise remote's current - async.waterfall([ - function (callback){ - node.hdc.amendments.currentVotes({ leaves: true }, callback); - }, - function (json, callback) { - applyVotes(amendments, number, number, json, node, callback); - } - ], function (err) { next(err); }); - } - else next(); + }); }, //============== @@ -233,9 +215,9 @@ module.exports = function Synchroniser (host, port, authenticated, pgp, currency var rm = new NodesMerkle(json); if(rm.root() != merkle.root()){ var leavesToAdd = []; - node.ucg.tht.get({ extract: true }, function (err, json) { + node.ucg.tht.get({ leaves: true }, function (err, json) { _(json.leaves).forEach(function(leaf){ - if(merkle.leaves().indexOf(leaf.hash) == -1){ + if(merkle.leaves().indexOf(leaf) == -1){ leavesToAdd.push(leaf); } }); @@ -243,7 +225,7 @@ module.exports = function Synchroniser (host, port, authenticated, pgp, currency async.forEachSeries(leavesToAdd, function(leaf, callback){ async.waterfall([ function (cb){ - node.ucg.tht.get({ "leaf": leaf }, next); + node.ucg.tht.get({ "leaf": leaf }, cb); }, function (json, cb){ var jsonEntry = json.leaf.value.entry; @@ -252,8 +234,6 @@ module.exports = function Synchroniser (host, port, authenticated, pgp, currency ["version", "currency", "fingerprint", "hosters", "trusts"].forEach(function (key) { entry[key] = jsonEntry[key]; }); - }, - function (cb){ logger.info('THT entry %s', jsonEntry.fingerprint); THTService.submit(entry.getRaw() + sign, cb); } @@ -278,9 +258,9 @@ module.exports = function Synchroniser (host, port, authenticated, pgp, currency var rm = new NodesMerkle(json); if(rm.root() != merkle.root()){ var leavesToAdd = []; - node.ucg.peering.peers.get({ extract: true }, function (err, json) { + node.ucg.peering.peers.get({ leaves: true }, function (err, json) { _(json.leaves).forEach(function(leaf){ - if(merkle.leaves().indexOf(leaf.hash) == -1){ + if(merkle.leaves().indexOf(leaf) == -1){ leavesToAdd.push(leaf); } }); @@ -288,17 +268,16 @@ module.exports = function Synchroniser (host, port, authenticated, pgp, currency async.forEachSeries(leavesToAdd, function(leaf, callback){ async.waterfall([ function (cb) { - node.ucg.peering.peers.get({ "leaf": leaf }, next); + node.ucg.peering.peers.get({ "leaf": leaf }, cb); }, function (json, cb) { var jsonEntry = json.leaf.value; var sign = json.leaf.value.signature; var entry = new Peer({}); - ["version", "currency", "fingerprint", "dns", "ipv4", "ipv6", "port"].forEach(function (key) { + ["version", "currency", "fingerprint", "endpoints"].forEach(function (key) { entry[key] = jsonEntry[key]; }); - }, - function (cb) { + entry.signature = sign; ParametersService.getPeeringEntryFromRaw(entry.getRaw(), sign, cb); }, function (rawSigned, keyID, cb){ @@ -316,6 +295,91 @@ module.exports = function Synchroniser (host, port, authenticated, pgp, currency else next(); }); }, + + //=========== + // Registry + //=========== + function (next){ + Amendment.current(function (err, am) { + if (!am) { + next(); + return; + } + async.waterfall([ + function (next){ + Key.getMembers(next); + }, + function (keys, next) { + async.forEach(keys, function(member, callback){ + async.waterfall([ + function (next){ + node.ucs.community.members.current(member.fingerprint, next); + }, + function (membership, next){ + logger.info('Membership of %s', member.fingerprint); + var ms = new Membership({ + "version": membership.membership.version, + "currency": membership.membership.currency, + "issuer": membership.membership.issuer, + "membership": membership.membership.membership, + "type": "MEMBERSHIP" + }); + ms.signature = membership.signature; + ParametersService.getMembership({ + body: { + membership: ms.getRaw(), + signature: membership.signature + } + }, next); + }, + function (ms, next){ + ms.amNumber = am.number - 1; + ms.current = true; + ms.save(function (err) { + next(err); + }); + }, + ], callback); + }, next); + }, + function (next){ + Key.getVoters(next); + }, + function (keys, next) { + async.forEach(keys, function(member, callback){ + async.waterfall([ + function (next){ + node.ucs.community.voters.current(member.fingerprint, next); + }, + function (voting, next){ + logger.info('Voting of %s', member.fingerprint); + var vt = new Voting({ + "version": voting.voting.version, + "currency": voting.voting.currency, + "issuer": voting.voting.issuer, + "type": "VOTING" + }); + vt.signature = voting.signature; + ParametersService.getVoting({ + body: { + voting: vt.getRaw(), + signature: voting.signature + } + }, next); + }, + function (voting, next){ + voting.amNumber = am.number - 1; + voting.current = true; + voting.save(function (err) { + next(err); + }); + }, + ], callback); + }, next); + }, + ], next); + }) + }, ], function (err, result) { logger.info('Sync finished.'); done(err); @@ -372,10 +436,8 @@ module.exports = function Synchroniser (host, port, authenticated, pgp, currency return; } async.waterfall([ - function (next){ - remoteMerkleFunc.call(remoteMerkleFunc, keyFingerprint, { leaves: false }, next); - }, - function (json, onEveryTransactionProcessed){ + function (onEveryTransactionProcessed){ + var json = results.remote; var transactions = {}; var numbers = _.range(json.leavesCount); async.forEachSeries(numbers, function(number, onSentTransactionsProcessed){ @@ -451,25 +513,38 @@ module.exports = function Synchroniser (host, port, authenticated, pgp, currency } function applyVotes(amendments, amNumber, number, json, node, cb) { - // logger.info('Applying votes for amendment #%s', amNumber); - // logger.info("Signatures: %s", _(json.leaves).size()); + logger.info('Applying votes for amendment #%s', amNumber); + logger.info("Signatures: %s", _(json.leaves).size()); async.forEachSeries(json.leaves, function(leaf, callback){ async.waterfall([ function (next){ var hash = sha1(amendments[amNumber]).toUpperCase(); - node.hdc.amendments.votes.of(amNumber, hash, { "leaf": leaf }, next); + node.hdc.amendments.view.signatures(amNumber, hash, { "leaf": leaf }, next); }, function (json, next){ var vote = json.leaf; - VoteService.submit(amendments[amNumber] + vote.value.signature, function (err, am) { - // Promotion time - StrategyService.tryToPromote(am, function (err) { - if(!err) - number++; - next(err); - }); - }); + ParametersService.getVote({ + body: { + amendment: amendments[amNumber], + signature: vote.value.signature + } + }, next); }, + function (vote, next){ + VoteService.submit(vote, function (err, am) { + if(!err) + number++; + next(err); + }); + // VoteService.submit(amendments[amNumber] + vote.value.signature, function (err, am) { + // // Promotion time + // StrategyService.tryToPromote(am, function (err) { + // if(!err) + // number++; + // next(err); + // }); + // }); + } ], callback); }, function(err, result){ cb(err, number); diff --git a/app/models/amendment.js b/app/models/amendment.js index b1537314a82eb9c062613be4c991f92a62529858..3ac909422b0b773a2adfb708fe4f3d7550130bc0 100644 --- a/app/models/amendment.js +++ b/app/models/amendment.js @@ -209,23 +209,15 @@ AmendmentSchema.statics.nextNumber = function (done) { AmendmentSchema.statics.current = function (done) { - this.find({ promoted: true }, function (err, amends) { + this.find({ promoted: true }).sort({ number: -1 }).limit(1).exec(function (err, amends) { if(amends && amends.length == 1){ done(err, amends[0]); return; } - if(!amends || amends.length == 0){ + else { done('No current amendment'); return; } - if(amends || amends.length > 1){ - var current = undefined; - amends.forEach(function (am) { - if(!current || (current && current.number < am.number)) - current = am; - }); - done(err, current); - } }); }; diff --git a/app/service/PeeringService.js b/app/service/PeeringService.js index a8118a26c001a50fee56ddb3b2911b417a17b62c..8997551aa142162bd60b1250050303f9bb3f0bc0 100644 --- a/app/service/PeeringService.js +++ b/app/service/PeeringService.js @@ -64,10 +64,13 @@ function PeeringService(pgp, currency, conf) { dbPeers.forEach(function(peer){ that.addPeer(peer); }); - Peer.getTheOne(that.cert.fingerprint, next); + Peer.getTheOne(that.cert.fingerprint, function (err, selfPeer) { + if (selfPeer) + peer = selfPeer; + next(); + }); }, - function (selfPeer, next){ - peer = selfPeer; + function (next){ logger.debug('Loaded service: Peering'); next(); }, @@ -105,7 +108,7 @@ function PeeringService(pgp, currency, conf) { next(null, pubkey.raw); }, function (pubkey, next){ - that.persistPeering(signedPR, pubkey, next); + persistPeering(signedPR, pubkey, next); } ], callback); } @@ -149,14 +152,14 @@ function PeeringService(pgp, currency, conf) { wasStatus = peer.status; peer.setStatus(status.status, next); peer.statusSigDate = status.sigDate; + peers[peer.fingerprint].status = status; } ], function (err) { - peers[peer.fingerprint].status = status; callback(err, status, peer, wasStatus); }); } - this.persistPeering = function (signedPR, pubkey, done) { + function persistPeering (signedPR, pubkey, done) { var peer = new Peer(); async.waterfall([ function (next){ @@ -679,7 +682,7 @@ function PeeringService(pgp, currency, conf) { function getForwardPeers (done) { async.waterfall([ - async.apply(Forward.find.bind(Forward), { to: this.cert.fingerprint }), + async.apply(Forward.find.bind(Forward), { to: that.cert.fingerprint }), function (fwds, next) { var fingerprints = _(fwds).map(function(fwd){ return fwd.fingerprint; }); Peer.getList(fingerprints, next); diff --git a/app/service/StrategyService.js b/app/service/StrategyService.js index 24fd8cc9b0bda9cc6d43a216093f4282d557da56..5f7b2d7fd0ce47dd6b607e21f00de526d04351a8 100644 --- a/app/service/StrategyService.js +++ b/app/service/StrategyService.js @@ -101,7 +101,55 @@ module.exports.get = function (pgp, currency, conf) { }, next); }, function (next){ - SyncService.createNext(am, next); + Key.getMembers(next); + }, + function (members, next){ + // Update Merkle of proposed members + async.waterfall([ + function (next){ + Merkle.proposedMembers(next); + }, + function (merkle, next){ + var fingerprints = []; + members.forEach(function(m){ + fingerprints.push(m.fingerprint); + }); + fingerprints.sort(); + merkle.initialize(fingerprints); + merkle.save(function (err) { + next(err); + }); + }, + ], next); + }, + function (next){ + Key.getVoters(next); + }, + function (voters, next){ + // Update Merkle of proposed voters + async.waterfall([ + function (next){ + Merkle.proposedVoters(next); + }, + function (merkle, next){ + var fingerprints = []; + voters.forEach(function(v){ + fingerprints.push(v.fingerprint); + }); + fingerprints.sort(); + merkle.initialize(fingerprints); + merkle.save(function (err) { + next(err); + }); + }, + ], next); + }, + function (next){ + if (conf.createNext) { + SyncService.createNext(am, next); + } else { + next(); + } }, ], cb); }, done); diff --git a/app/service/SyncService.js b/app/service/SyncService.js index 641dbeef15f05d75d2d47f37b38e024f8066dc62..11ae03db020eef07412754e5ae495a4b07664b33 100644 --- a/app/service/SyncService.js +++ b/app/service/SyncService.js @@ -454,9 +454,9 @@ module.exports.get = function (pgp, currency, conf) { } function updateUniversalDividend (amNext, amCurrent, done) { - // Time for Universal Dividend + // Time for Universal Dividend (and we have members) var delayPassedSinceRootAM = (amNext.generated - conf.sync.AMStart); - if (delayPassedSinceRootAM > 0 && delayPassedSinceRootAM % conf.sync.UDFreq == 0) { + if (delayPassedSinceRootAM > 0 && delayPassedSinceRootAM % conf.sync.UDFreq == 0 && amNext.membersCount > 0) { async.waterfall([ function (next) { Amendment.getPreviouslyPromotedWithDividend(next); @@ -799,11 +799,11 @@ module.exports.get = function (pgp, currency, conf) { var stateFunc = async.apply(whatToDo.state[lastKeyState.toString()][statusTo.toString()] || doNothingWithState, key, statusTo); if ((lastKeyState == 0 && statusTo == 1) || (lastKeyState == -1 && statusTo == 0)) { // Make positive change - console.log('> +%s', key); + mathlog.info('VT +%s', key); actionForVoters(merkleFunc, keysFunc, stateFunc, amNext, next); } else if ((lastKeyState == 1 && statusTo == 0) || (lastKeyState == 0 && statusTo == -1)) { // Make negative change - console.log('> -%s', key); + mathlog.info('VT -%s', key); actionForVoters(merkleFunc, keysFunc, stateFunc, amNext, next); } else { // Do nothing! diff --git a/bin/ucoind b/bin/ucoind index 0ea2bdb67311aa12c7f4d4e7614301d7c41e9bf2..879711f83ad5948c4864aef4fc8ff0c4ce0b3522 100755 --- a/bin/ucoind +++ b/bin/ucoind @@ -46,23 +46,28 @@ program .description('Tries to synchronise data with remote uCoin node') .action(connect(function (host, port, conf) { - // Init server - server.express.app(program.currency, conf, function (err, app) { + // Disable daemon + conf.sync.AMDaemon = "OFF"; - // Synchronize - var Synchroniser = require('../app/lib/sync'); - var remote = new Synchroniser(host, port, false, server.pgp, program.currency, conf); - async.waterfall([ - function (next){ - remote.sync(next); - }, - ], function (err) { - if(err){ - logger.error('Error during sync:', err); - } - server.database.disconnect(); - process.exit(); - }); + async.series([ + function (next){ + server.openpgp.init(program.currency, conf, next); + }, + function (next){ + server.services.init(program.currency, conf, next); + }, + function (next){ + // Synchronize + var Synchroniser = require('../app/lib/sync'); + var remote = new Synchroniser(host, port, false, program.currency, conf); + remote.sync(next); + }, + ], function (err) { + if(err){ + logger.error('Error during sync:', err); + } + server.database.disconnect(); + process.exit(); }); })); @@ -301,6 +306,9 @@ function overrideConf(conf) { MSExpires: config.sync.MSExpires || conf.sync.MSExpires || 3600*24*30.4375*6, // 6 months UCSBlockFreq: config.sync.UCSBlockFreq || conf.sync.UCSBlockFreq || 3600 // hourly }; + + // Specific internal settings + conf.createNext = true; return conf; } diff --git a/package.json b/package.json index e8e0cda8d5b29ceb1ef9fa767a7521600b1f754d..d8525561144e1196318e3d4bf7d62c9175e13e4f 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "hdc": "~0.5.0", "connect-pgp": "~0.2.1", "underscore": "~1.5.2", - "vucoin": "~0.10.0", + "vucoin": "~0.12.0", "request": "~2.31.0", "log4js": "~0.6.9" }, diff --git a/test/contract.js b/test/contract.js index 1a15ce15cbbe0fc275eec0011e0fe728aa64cee4..7c8518228813f10887902a696330366e9773c65e 100644 --- a/test/contract.js +++ b/test/contract.js @@ -67,7 +67,8 @@ var conf = { UDPercent: 0.5, // So it can be tested under 4 UD - this ultra high value of UD growth Consensus: 2/3, MSExpires: 3600*24*30 // 30 days - } + }, + createNext: true }; var amendments = { diff --git a/test/members-voters.js b/test/members-voters.js index 85a39fcd7ab4c5e10a56f90c5871fceeef1655d6..aed01bb50bd721204eb9c1f61bbadfb4f5d82d43 100644 --- a/test/members-voters.js +++ b/test/members-voters.js @@ -76,7 +76,8 @@ var conf = { UDPercent: 0.5, // So it can be tested under 4 UD - this ultra high value of UD growth Consensus: 2/3, MSExpires: 3600*24*30 // 30 days - } + }, + createNext: true }; var amendments = {