Commit ec3d424b authored by Cédric Moreau's avatar Cédric Moreau

Autonomous node (generating PoW)

parent 0d22d4c8
...@@ -18,7 +18,7 @@ module.exports = function (armoredPrivateKey, password, withOpenPGPJS, done) { ...@@ -18,7 +18,7 @@ module.exports = function (armoredPrivateKey, password, withOpenPGPJS, done) {
} else { } else {
var asciiPrivateKey = armoredPrivateKey; var asciiPrivateKey = armoredPrivateKey;
var keyring = '~/.gnupg/ucoin_' + fingerprint; var keyring = '~/.gnupg/ucoin_' + fingerprint;
logger.debug("Keyring = %s", keyring); logger.trace("Keyring = %s", keyring);
var gnupg = new (require('./gnupg'))(asciiPrivateKey, password, fingerprint, keyring); var gnupg = new (require('./gnupg'))(asciiPrivateKey, password, fingerprint, keyring);
gnupg.init(function (err) { gnupg.init(function (err) {
next(err, function (message, done) { next(err, function (message, done) {
......
...@@ -8,15 +8,25 @@ var unix2dos = require('../lib/unix2dos'); ...@@ -8,15 +8,25 @@ var unix2dos = require('../lib/unix2dos');
var dos2unix = require('../lib/dos2unix'); var dos2unix = require('../lib/dos2unix');
var parsers = require('../lib/streams/parsers/doc'); var parsers = require('../lib/streams/parsers/doc');
var keyhelper = require('../lib/keyhelper'); var keyhelper = require('../lib/keyhelper');
var logger = require('../lib/logger')('membership'); var logger = require('../lib/logger')('keychain');
var signature = require('../lib/signature');
var moment = require('moment'); var moment = require('moment');
var inquirer = require('inquirer'); var inquirer = require('inquirer');
module.exports.get = function (conn, conf, PublicKeyService) { module.exports.get = function (conn, conf, PublicKeyService, PeeringService) {
return new KeyService(conn, conf, PublicKeyService); return new KeyService(conn, conf, PublicKeyService, PeeringService);
}; };
function KeyService (conn, conf, PublicKeyService) { // Callback used as a semaphore to sync keyblock reception & PoW computation
var newKeyblockCallback = null;
// Callback used to start again computation of next PoW
var computeNextCallback = null;
// Flag telling if computation has started
var computationActivated = false;
function KeyService (conn, conf, PublicKeyService, PeeringService) {
var KeychainService = this; var KeychainService = this;
...@@ -101,6 +111,7 @@ function KeyService (conn, conf, PublicKeyService) { ...@@ -101,6 +111,7 @@ function KeyService (conn, conf, PublicKeyService) {
var block = new KeyBlock(kb); var block = new KeyBlock(kb);
block.issuer = kb.pubkey.fingerprint; block.issuer = kb.pubkey.fingerprint;
var currentBlock = null; var currentBlock = null;
var newLinks;
async.waterfall([ async.waterfall([
function (next){ function (next){
KeyBlock.current(function (err, kb) { KeyBlock.current(function (err, kb) {
...@@ -135,7 +146,7 @@ function KeyService (conn, conf, PublicKeyService) { ...@@ -135,7 +146,7 @@ function KeyService (conn, conf, PublicKeyService) {
return; return;
} }
// Check the challenge depending on issuer // Check the challenge depending on issuer
checkProofOfWork(block, next); checkProofOfWork(current, block, next);
}, },
function (next) { function (next) {
// Check document's coherence // Check document's coherence
...@@ -145,9 +156,28 @@ function KeyService (conn, conf, PublicKeyService) { ...@@ -145,9 +156,28 @@ function KeyService (conn, conf, PublicKeyService) {
// Check document's coherence // Check document's coherence
checkCoherence(block, next); checkCoherence(block, next);
}, },
function (newLinks, next) { function (theNewLinks, next) {
newLinks = theNewLinks;
// If computation is started, stop it and wait for stop event
var isComputeProcessWaiting = computeNextCallback ? true : false;
if (computationActivated && !isComputeProcessWaiting) {
// Next will be triggered by computation of the PoW process
newKeyblockCallback = next;
} else {
next();
}
},
function (next) {
newKeyblockCallback = null;
// Save block data + compute links obsolescence // Save block data + compute links obsolescence
saveBlockData(block, newLinks, next); saveBlockData(block, newLinks, next);
},
function (block, next) {
// If PoW computation process is waiting, trigger it
if (computeNextCallback)
computeNextCallback();
computeNextCallback = null;
next();
} }
], function (err) { ], function (err) {
done(err, block); done(err, block);
...@@ -475,7 +505,7 @@ function KeyService (conn, conf, PublicKeyService) { ...@@ -475,7 +505,7 @@ function KeyService (conn, conf, PublicKeyService) {
], done); ], done);
} }
function checkProofOfWork (block, done) { function checkProofOfWork (current, block, done) {
var powRegexp = new RegExp('^0{' + conf.powZeroMin + '}'); var powRegexp = new RegExp('^0{' + conf.powZeroMin + '}');
if (!block.hash.match(powRegexp)) if (!block.hash.match(powRegexp))
done('Not a proof-of-work'); done('Not a proof-of-work');
...@@ -485,22 +515,9 @@ function KeyService (conn, conf, PublicKeyService) { ...@@ -485,22 +515,9 @@ function KeyService (conn, conf, PublicKeyService) {
var nbWaitedPeriods = 0; var nbWaitedPeriods = 0;
async.waterfall([ async.waterfall([
function (next){ function (next){
KeyBlock.lastOfIssuer(block.issuer, next); getTrialLevel(block.issuer, block.number, current ? current.membersCount : 0, next);
}, },
function (last, next){ function (nbZeros, next){
if (last) {
var leadingZeros = last.hash.match(/^0+/)[0];
lastBlockPenality = leadingZeros.length - conf.powZeroMin + 1;
var powPeriodIsContant = conf.powPeriodC;
var nbPeriodsToWait = (powPeriodIsContant ? conf.powPeriod : Math.floor(conf.powPeriod/100*block.membersCount));
nbWaitedPeriods = Math.floor((block.number - last.number) / nbPeriodsToWait);
next();
} else {
next();
}
},
function (next){
var nbZeros = Math.max(conf.powZeroMin, conf.powZeroMin + lastBlockPenality - nbWaitedPeriods);
var powRegexp = new RegExp('^0{' + nbZeros + ',}'); var powRegexp = new RegExp('^0{' + nbZeros + ',}');
if (!block.hash.match(powRegexp)) if (!block.hash.match(powRegexp))
next('Wrong proof-of-work level: given ' + block.hash.match(/^0+/)[0].length + ' zeros, required was ' + nbZeros + ' zeros'); next('Wrong proof-of-work level: given ' + block.hash.match(/^0+/)[0].length + ' zeros, required was ' + nbZeros + ' zeros');
...@@ -512,6 +529,28 @@ function KeyService (conn, conf, PublicKeyService) { ...@@ -512,6 +529,28 @@ function KeyService (conn, conf, PublicKeyService) {
} }
} }
function getTrialLevel (issuer, nextBlockNumber, currentWoTsize, done) {
// Compute exactly how much zeros are required for this block's issuer
var lastBlockPenality = 0;
var nbWaitedPeriods = 0;
async.waterfall([
function (next){
KeyBlock.lastOfIssuer(issuer, next);
},
function (last, next){
if (last) {
var leadingZeros = last.hash.match(/^0+/)[0];
lastBlockPenality = leadingZeros.length - conf.powZeroMin + 1;
var powPeriodIsContant = conf.powPeriodC;
var nbPeriodsToWait = (powPeriodIsContant ? conf.powPeriod : Math.floor(conf.powPeriod/100*currentWoTsize));
nbWaitedPeriods = Math.floor((nextBlockNumber - last.number) / nbPeriodsToWait);
}
var nbZeros = Math.max(conf.powZeroMin, conf.powZeroMin + lastBlockPenality - nbWaitedPeriods);
next(null, nbZeros);
},
], done);
}
function checkKicked (block, newLinks, done) { function checkKicked (block, newLinks, done) {
var membersChanges = block.membersChanges; var membersChanges = block.membersChanges;
async.waterfall([ async.waterfall([
...@@ -1384,10 +1423,22 @@ function KeyService (conn, conf, PublicKeyService) { ...@@ -1384,10 +1423,22 @@ function KeyService (conn, conf, PublicKeyService) {
var full = raw + sig; var full = raw + sig;
pow = full.hash(); pow = full.hash();
testsCount++; testsCount++;
if (testsCount % 100 == 0) process.stdout.write('.'); if (testsCount % 50 == 0) {
process.stdout.write('.');
if (newKeyblockCallback) {
computationActivated = false
next('New block received');
}
}
next(); next();
}); });
}, function (err) { }, function (err) {
if (err) {
logger.debug('Proof-of-work computation canceled: valid block received');
done(err);
newKeyblockCallback();
return;
}
block.signature = sig; block.signature = sig;
var end = new Date().timestamp(); var end = new Date().timestamp();
var duration = moment.duration((end - start)) + 's'; var duration = moment.duration((end - start)) + 's';
...@@ -1411,5 +1462,57 @@ function KeyService (conn, conf, PublicKeyService) { ...@@ -1411,5 +1462,57 @@ function KeyService (conn, conf, PublicKeyService) {
}, next); }, next);
}, },
], done); ], done);
} };
this.startGeneration = function (done) {
if (!PeeringService) {
done('Needed peering service activated.');
return;
}
computationActivated = true;
var sigFunc, block, difficulty;
async.waterfall([
function (next) {
KeyBlock.current(function (err, current) {
next(null, current);
});
},
function (current, next){
if (!current) {
next(null, null);
return;
} else {
async.parallel({
block: function(callback){
KeychainService.generateNext(callback);
},
signature: function(callback){
signature(conf.pgpkey, conf.pgppasswd, conf.openpgpjs, callback);
},
trial: function (callback) {
getTrialLevel(PeeringService.cert.fingerprint, current ? current.number + 1 : 0, current ? current.membersCount : 0, callback);
}
}, next);
}
},
function (res, next){
if (!res) {
next(null, null, 'Waiting for a root block before computing new blocks');
} else {
KeychainService.prove(res.block, res.signature, res.trial, function (err, proofBlock) {
next(null, proofBlock, err);
});
}
},
], function (err, proofBlock, powCanceled) {
if (powCanceled) {
logger.warn(powCanceled);
computeNextCallback = async.apply(done, null, null);
computationActivated = false
} else {
computationActivated = false
done(err, proofBlock);
}
});
};
} }
...@@ -405,7 +405,7 @@ program ...@@ -405,7 +405,7 @@ program
program program
.command('start') .command('start')
.description('Start uCoin server using given --currency') .description('Start uCoin server using given --currency')
.action(service(LISTEN_HTTP, ucoin.createWOTServer, function (server, conf) { .action(service(LISTEN_HTTP, ucoin.createPeerServer, function (server, conf) {
// server // server
// .pipe(router(server.PeeringService.cert.fingerprint, server.conn)) // .pipe(router(server.PeeringService.cert.fingerprint, server.conn))
......
var async = require('async'); var async = require('async');
var util = require('util'); var util = require('util');
var openpgp = require('openpgp'); var openpgp = require('openpgp');
var jpgp = require('./app/lib/jpgp'); var jpgp = require('./app/lib/jpgp');
var unix2dos = require('./app/lib/unix2dos'); var unix2dos = require('./app/lib/unix2dos');
var logger = require('./app/lib/logger')('peerserver'); var logger = require('./app/lib/logger')('peerserver');
var plogger = require('./app/lib/logger')('peer'); var plogger = require('./app/lib/logger')('peer');
var flogger = require('./app/lib/logger')('forward'); var flogger = require('./app/lib/logger')('forward');
var slogger = require('./app/lib/logger')('status'); var slogger = require('./app/lib/logger')('status');
var wlogger = require('./app/lib/logger')('wallet'); var wlogger = require('./app/lib/logger')('wallet');
var WOT = require('./wotserver'); var WOT = require('./wotserver');
var signature = require('./app/lib/signature'); var signature = require('./app/lib/signature');
var parsers = require('./app/lib/streams/parsers/doc'); var parsers = require('./app/lib/streams/parsers/doc');
var multicaster = require('./app/lib/streams/multicaster');
function PeerServer (dbConf, overrideConf, interceptors, onInit) { function PeerServer (dbConf, overrideConf, interceptors, onInit) {
...@@ -116,6 +117,7 @@ function PeerServer (dbConf, overrideConf, interceptors, onInit) { ...@@ -116,6 +117,7 @@ function PeerServer (dbConf, overrideConf, interceptors, onInit) {
that.PublicKeyService = require('./app/service/PublicKeyService').get(conn, that.conf, that.KeyService); that.PublicKeyService = require('./app/service/PublicKeyService').get(conn, that.conf, that.KeyService);
that.ContractService = require('./app/service/ContractService').get(conn, that.conf); that.ContractService = require('./app/service/ContractService').get(conn, that.conf);
that.PeeringService = require('./app/service/PeeringService').get(conn, that.conf, that.PublicKeyService, that.ParametersService); that.PeeringService = require('./app/service/PeeringService').get(conn, that.conf, that.PublicKeyService, that.ParametersService);
that.KeychainService = require('./app/service/KeychainService').get(conn, that.conf, that.PublicKeyService, that.PeeringService);
that.TransactionsService = require('./app/service/TransactionsService').get(conn, that.MerkleService, that.PeeringService); that.TransactionsService = require('./app/service/TransactionsService').get(conn, that.MerkleService, that.PeeringService);
that.WalletService = require('./app/service/WalletService').get(conn); that.WalletService = require('./app/service/WalletService').get(conn);
async.parallel({ async.parallel({
...@@ -219,6 +221,30 @@ function PeerServer (dbConf, overrideConf, interceptors, onInit) { ...@@ -219,6 +221,30 @@ function PeerServer (dbConf, overrideConf, interceptors, onInit) {
that.PeeringService.updateForwards(next); that.PeeringService.updateForwards(next);
}, },
function (next){ function (next){
async.forever(
function tryToGenerateNextBlock(next) {
async.waterfall([
function (next){
that.KeychainService.startGeneration(next);
},
function (block, next){
if (block) {
var Peer = that.conn.model('Peer');
var peer = new Peer({ endpoints: [['BASIC_MERKLED_API', conf.ipv4, conf.port].join(' ')] });
multicaster().sendKeyblock(peer, block, next);
} else {
next();
}
},
], function (err) {
next(err);
});
},
function onError (err) {
logger.error(err);
logger.error('Keyblock generation STOPPED.');
}
);
next(); next();
}, },
], done); ], done);
...@@ -304,7 +330,7 @@ function PeerServer (dbConf, overrideConf, interceptors, onInit) { ...@@ -304,7 +330,7 @@ function PeerServer (dbConf, overrideConf, interceptors, onInit) {
this._listenBMA = function (app) { this._listenBMA = function (app) {
this.listenPKS(app); this.listenPKS(app);
this.listenHDC(app); this.listenWOT(app);
this.listenNET(app); this.listenNET(app);
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment