ucoind 28 KB
Newer Older
1
#!/usr/bin/env node
Cédric Moreau's avatar
Cédric Moreau committed
2
"use strict";
3

4
5
var logger = require('../app/lib/logger')('ucoind');

6
process.on('uncaughtException', function (err) {
Cédric Moreau's avatar
Cédric Moreau committed
7
  // Dunno why this specific exception is not caught
8
  if (err.code !== "EADDRNOTAVAIL" && err.code !== "EINVAL") {
Cédric Moreau's avatar
Cédric Moreau committed
9
10
11
    logger.error(err);
    process.exit(1);
  }
12
13
});

Cédric Moreau's avatar
Cédric Moreau committed
14
var async       = require('async');
15
var co          = require('co');
16
var Q           = require('q');
Cédric Moreau's avatar
Cédric Moreau committed
17
18
var _           = require('underscore');
var program     = require('commander');
Cédric Moreau's avatar
Cédric Moreau committed
19
var vucoin      = require('vucoin');
20
var directory   = require('../app/lib/directory');
Cédric Moreau's avatar
Cédric Moreau committed
21
22
var wizard      = require('../app/lib/wizard');
var multicaster = require('../app/lib/streams/multicaster');
23
var signature   = require('../app/lib/signature');
24
25
var crypto      = require('../app/lib/crypto');
var base58      = require('../app/lib/base58');
Cédric Moreau's avatar
Cédric Moreau committed
26
var Synchroniser = require('../app/lib/sync');
Cédric Moreau's avatar
Cédric Moreau committed
27
var pjson       = require('../package.json');
Cédric Moreau's avatar
Cédric Moreau committed
28
var ucoin       = require('./../index');
29
var Peer        = require('../app/lib/entity/peer');
Cédric Moreau's avatar
Cédric Moreau committed
30
var Block       = require('../app/lib/entity/block');
31

32
33
const ERASE_IF_ALREADY_RECORDED = true;
const NO_LOGS = true;
34

35
program
Cédric Moreau's avatar
Cédric Moreau committed
36
  .version(pjson.version)
37
  .usage('<command> [options]')
38

39
  .option('--home <path>',         'Path to uCoin HOME (defaults to "$HOME/.config/duniter").')
Moul's avatar
Moul committed
40
  .option('-d, --mdb <name>',          'Database name (defaults to "ucoin_default").')
41
42

  .option('--autoconf',                'With `init` command, will guess the best network and key options witout aksing for confirmation')
Moul's avatar
Moul committed
43
44
  .option('--ipv4 <address>',          'IPv4 interface to listen for requests')
  .option('--ipv6 <address>',          'IPv6 interface to listen for requests')
45
46
47
  .option('--remoteh <host>',          'Remote interface others may use to contact this node')
  .option('--remote4 <host>',          'Remote interface for IPv4 access')
  .option('--remote6 <host>',          'Remote interface for IPv6 access')
48
  .option('-p, --port <port>',         'Port to listen for requests', parseInt)
49
  .option('--remotep <port>',          'Remote port others may use to contact this node')
50
51
52
  .option('--upnp',                    'Use UPnP to open remote port')
  .option('--noupnp',                  'Do not use UPnP to open remote port')

53
54
55
56
  // Webmin options
  .option('--webmhost <host>',         'Local network interface to connect to (IP)')
  .option('--webmport <port>',         'Local network port to connect', parseInt)

57
58
59
60
61
62
  .option('--salt <salt>',             'Key salt to generate this key\'s secret key')
  .option('--passwd <password>',       'Password to generate this key\'s secret key')
  .option('--participate <Y|N>',       'Participate to writing the blockchain')
  .option('--cpu <percent>',           'Percent of CPU usage for proof-of-work computation', parsePercent)

  .option('-c, --currency <name>',     'Name of the currency managed by this node.')
63
  .option('--sigPeriod <timestamp>',   'Minimum delay between 2 certifications of a same issuer, in seconds.')
64
  .option('--sigStock <count>',        'Maximum quantity of valid certifications per member.')
65
  .option('--sigWindow <duration>',    'Maximum age of a non-written certification.')
66
  .option('--idtyWindow <duration>',    'Maximum age of a non-written certification.')
67
  .option('--sigValidity <timestamp>', 'Validity duration of a certification, in seconds.')
68
  .option('--msValidity <timestamp>',  'Validity duration of a memberships, in seconds.')
69
  .option('--sigQty <number>',         'Minimum number of required certifications to be a member/stay as a member')
70
71
  .option('--medtblocks <number>',     'medianTimeBlocks parameter of UCP')
  .option('--avgGenTime <number>',     'avgGenTime parameter of UCP')
72
  .option('--dtdiffeval <number>',     'dtDiffEval parameter of UCP')
73
74
  .option('--powZeroMin <number>',     'Minimum number of leading zeros for a proof-of-work')
  .option('--powPeriod <number>',      'Number of blocks to wait to decrease proof-of-work difficulty by one')
75
  .option('--powDelay <number>',       'Number of seconds to wait before starting the computation of next block')
Cédric Moreau's avatar
Cédric Moreau committed
76
  .option('--growth <number>',         'Universal Dividend %growth. Aka. \'c\' parameter in RTM', parsePercent)
77
78
  .option('--ud0 <number>',            'Universal Dividend initial value')
  .option('--dt <number>',             'Number of seconds between two UD')
79
  .option('--rootoffset <number>',     'Allow to give a time offset for first block (offset in the past)')
80
  .option('--show',                    'With gen-next or gen-root commands, displays the generated block')
81

Cédric Moreau's avatar
Cédric Moreau committed
82
  .option('--nointeractive',           'Disable interactive sync UI')
Cédric Moreau's avatar
Cédric Moreau committed
83
  .option('--nocautious',              'Do not check blocks validity during sync')
84
  .option('--cautious',                'Check blocks validity during sync (overrides --nocautious option)')
Cédric Moreau's avatar
Cédric Moreau committed
85
  .option('--nopeers',                 'Do not retrieve peers during sync')
86
87
88
89

  .option('--timeout <milliseconds>',  'Timeout to use when contacting peers', parseInt)
  .option('--httplogs',                'Enable HTTP logs')
  .option('--nohttplogs',              'Disable HTTP logs')
90
  .option('--isolate',                 'Avoid the node to send peering or status informations to the network')
91
  .option('--check',                   'With gen-next: just check validity of generated block')
92
  .option('--forksize <size>',         'Maximum size of fork window', parseInt)
93
  .option('--memory',                  'Memory mode')
94
  ;
Cédric Moreau's avatar
Cédric Moreau committed
95

Cédric Moreau's avatar
Cédric Moreau committed
96
97
98
program
  .command('start')
  .description('Start uCoin server.')
99
  .action(service(serverStart));
Cédric Moreau's avatar
Cédric Moreau committed
100

101
102
103
program
  .command('webstart')
  .description('Start uCoin server + web admin.')
104
  .action(webStart);
105

106
107
108
program
  .command('wizard [step]')
  .description('Launch the configuration Wizard')
109
110
  .action(function (step) {
    // Only show message "Saved"
Cédric Moreau's avatar
Cédric Moreau committed
111
112
113
114
    connect(function(step, server, conf){
      async.series([
        function(next) {
          startWizard(step, server, conf, next);
115
        }
Cédric Moreau's avatar
Cédric Moreau committed
116
      ], logIfErrorAndExit(server));
117
118
    })(step, null);
  });
119

120
program
121
  .command('sync [host] [port] [to]')
Cédric Moreau's avatar
Cédric Moreau committed
122
  .description('Synchronize blockchain from a remote uCoin node')
123
  .action(service(function (host, port, to, server, conf) {
124
125
126
127
128
129
    if (!host) {
      throw 'Host is required.';
    }
    if (!port) {
      throw 'Port is required.';
    }
Cédric Moreau's avatar
Cédric Moreau committed
130
131
132
    return co(function *() {
      try {
        yield sync(server, host, port, conf, to);
Cédric Moreau's avatar
Cédric Moreau committed
133
        logger.info('Sync finished.');
Cédric Moreau's avatar
Cédric Moreau committed
134
135
136
137
138
139
      } catch (err) {
        logger.error(err.stack || err.message);
      }
      yield ((server && server.disconnect()) || Q()).catch(() => null);
      process.exit();
    });
140
141
  }));

142
function sync(server, host, port, conf, to) {
Cédric Moreau's avatar
Cédric Moreau committed
143
144
  // Synchronize
  var remote = new Synchroniser(server, host, port, conf, !program.nointeractive);
145
146
147
148
149
150
151
152
  let cautious;
  if (program.nocautious) {
    cautious = false;
  }
  if (program.cautious) {
    cautious = true;
  }
  return remote.sync(parseInt(to), cautious, program.nopeers);
153
154
}

155
156
157
program
  .command('peer [host] [port]')
  .description('Exchange peerings with another node')
158
  .action(service(function(host, port, server) {
Cédric Moreau's avatar
Cédric Moreau committed
159
160
161
162
163
164
165
166
167
168
169
170
171
172
    return co(function *() {
      let node = yield Q.nfcall(vucoin, host, port);
      logger.info('Fetching peering record at %s:%s...', host, port);
      let peering = yield Q.nfcall(node.network.peering.get);
      logger.info('Apply peering ...');
      yield server.PeeringService.submitP(peering, ERASE_IF_ALREADY_RECORDED, !program.nocautious);
      logger.info('Applied');
      let selfPeer = yield server.dal.getPeer(server.PeeringService.pubkey);
      if (!selfPeer) {
        yield Q.nfcall(server.PeeringService.generateSelfPeer, server.conf, 0);
        selfPeer = yield server.dal.getPeer(server.PeeringService.pubkey);
      }
      logger.info('Send self peering ...');
      var caster = multicaster();
173
      yield caster.sendPeering(Peer.statics.peerize(peering), Peer.statics.peerize(selfPeer));
Cédric Moreau's avatar
Cédric Moreau committed
174
175
176
177
      logger.info('Sent.');
      yield server.disconnect();
      process.exit();
    })
178
      .catch(function(err){
179
180
181
182
183
        logger.error(err.code || err.message || err);
        process.exit();
      });
  }));

Cédric Moreau's avatar
Cédric Moreau committed
184
program
185
186
  .command('init [host] [port]')
  .description('Setup a node configuration and sync data with given node')
187
  .action(connect(bootstrapServer, true));
Cédric Moreau's avatar
Cédric Moreau committed
188

Cédric Moreau's avatar
Cédric Moreau committed
189
program
190
  .command('forward [host] [port] [to]')
Cédric Moreau's avatar
Cédric Moreau committed
191
  .description('Forward local blockchain to a remote uCoin node')
192
  .action(service(function (host, port, to, server) {
Cédric Moreau's avatar
Cédric Moreau committed
193

194
    var remoteCurrent;
Cédric Moreau's avatar
Cédric Moreau committed
195
    async.waterfall([
196
      function(next) {
197
        vucoin(host, port, next, { timeout: server.conf.timeout });
198
199
200
201
202
      },
      function (node, next) {
        node.blockchain.current(next);
      },
      function(current, next) {
203
        remoteCurrent = current;
Cédric Moreau's avatar
Cédric Moreau committed
204
        logger.info(remoteCurrent.number);
205
        server.dal.getBlockFrom(remoteCurrent.number - 10).then(_.partial(next, null)).catch(next);
Cédric Moreau's avatar
Cédric Moreau committed
206
207
      },
      function (blocks, next){
208
        blocks = _.sortBy(blocks, 'number');
Cédric Moreau's avatar
Cédric Moreau committed
209
210
211
212
213
214
        // Forward
        var peer = new Peer({
          endpoints: [['BASIC_MERKLED_API', host, port].join(' ')]
        });
        async.forEachSeries(blocks, function (block, callback) {
          logger.info("Forwarding block#" + block.number);
Cédric Moreau's avatar
Cédric Moreau committed
215
216
          server.dal.getBlock(block.number)
            .then(function(fullBlock){
217
              multicaster(server.conf).sendBlock(peer, new Block(fullBlock)).then(() => callback()).catch(callback);
218
            });
Cédric Moreau's avatar
Cédric Moreau committed
219
        }, next);
220
      }
Cédric Moreau's avatar
Cédric Moreau committed
221
222
223
224
225
    ], function (err) {
      if(err){
        logger.error('Error during forwarding:', err);
      }
      server.disconnect();
226
      process.exit();
Cédric Moreau's avatar
Cédric Moreau committed
227
228
229
    });
  }));

230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
program
  .command('revert [count]')
  .description('Revert (undo + remove) the top [count] blocks from the blockchain. EXPERIMENTAL')
  .action(service(function (count, server) {
    co(function *() {
      try {
        for (let i = 0; i < count; i++) {
          yield server.revert();
        }
      } catch (err) {
        logger.error('Error during revert:', err);
      }
      // Save DB
      server.disconnect()
        .catch(() => null)
        .then(function(){
          process.exit();
        });
    });
  }));

program
  .command('revert-to [number]')
  .description('Revert (undo + remove) top blockchain blocks until block #[number] is reached. EXPERIMENTAL')
  .action(service(function (number, server) {
    co(function *() {
      try {
        yield server.revertTo(number);
      } catch (err) {
        logger.error('Error during revert:', err);
      }
      // Save DB
262
      ((server && server.disconnect()) || Q())
263
264
265
266
267
268
269
        .catch(() => null)
        .then(function(){
          process.exit();
        });
    });
  }));

270
program
271
  .command('gen-next [host] [port] [diff]')
Cédric Moreau's avatar
Cédric Moreau committed
272
  .description('Tries to generate the next block of the blockchain')
273
  .action(service(generateAndSend("generateNext")));
274

275
program
Cédric Moreau's avatar
Cédric Moreau committed
276
277
  .command('gen-root [host] [port] [diff]')
  .description('Tries to generate root block, with choice of root members')
278
  .action(service(generateAndSend("generateManualRoot")));
279
280
281

function generateAndSend (generationMethod) {
  return function (host, port, difficulty, server, conf) {
282
283
    async.waterfall([
      function (next){
284
        var method = eval('server.BlockchainService.' + generationMethod);
Cédric Moreau's avatar
Cédric Moreau committed
285
        method().then(_.partial(next, null)).catch(next);
286
287
      },
      function (block, next){
288
289
290
        if (program.check) {
          block.time = block.medianTime;
          program.show && console.log(block.getRawSigned());
291
          server.doCheckBlock(block)
292
293
            .then(function(){
              logger.info('Acceptable block');
294
              next();
295
            })
296
            .catch(next);
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
        }
        else {
          logger.debug('Block to be sent: %s', block.quickDescription());
          var wiz = wizard(server);
          var doSign = null;
          async.waterfall([
            function (next){
              if (!conf.salt && !conf.passwd)
                wiz.configKey(conf, next);
              else
                next();
            },
            function (next){
              // Extract key pair
              crypto.getKeyPair(conf.passwd, conf.salt, next);
            },
            function (pair, next){
              signature.sync(pair, function(err, sigFunc) {
                doSign = sigFunc;
                next(err, pair);
              });
            },
            function (pair, next){
              proveAndSend(server, block, doSign, base58.encode(pair.publicKey), difficulty, host, port, next);
            }
          ], next);
        }
324
      }
325
    ], logIfErrorAndExit(server));
326
327
  };
}
328

329
330
function proveAndSend (server, block, sigFunc, issuer, difficulty, host, port, done) {
  var BlockchainService = server.BlockchainService;
331
332
  async.waterfall([
    function (next){
333
      block.issuer = issuer;
334
      program.show && console.log(block.getRawSigned());
335
      BlockchainService.prove(block, sigFunc, difficulty).then((proven) => next(null, proven)).catch(next);
336
337
338
339
340
    },
    function (block, next){
      var peer = new Peer({
        endpoints: [['BASIC_MERKLED_API', host, port].join(' ')]
      });
Cédric Moreau's avatar
Cédric Moreau committed
341
      program.show && console.log(block.getRawSigned());
342
      logger.info('Posted block ' + block.quickDescription());
343
      multicaster(server.conf).sendBlock(peer, block).then(() => next()).catch(next);
344
    }
345
346
347
  ], done);
}

Cédric Moreau's avatar
Cédric Moreau committed
348
349
350
program
  .command('export-bc [upto]')
  .description('Exports the whole blockchain as JSON array, up to [upto] block number (excluded).')
351
  .action(service(function (upto, server) {
352
353
354
355
356
357
358
359
360
361
362
363
    return co(function *() {
      let CHUNK_SIZE = 500;
      let jsoned = [];
      let current = yield server.dal.getCurrentBlockOrNull();
      let lastNumber = current ? current.number + 1 : -1;
      if (upto !== undefined && upto.match(/\d+/)) {
        lastNumber = Math.min(parseInt(upto), lastNumber);
      }
      let chunksCount = Math.floor(lastNumber / CHUNK_SIZE);
      let chunks = [];
      // Max-size chunks
      for (let i = 0, len = chunksCount; i < len; i++) {
364
        chunks.push({ start: i * CHUNK_SIZE, to: i * CHUNK_SIZE + CHUNK_SIZE - 1 });
365
366
      }
      // A last chunk
367
      if (lastNumber > chunksCount * CHUNK_SIZE) {
368
369
370
371
372
        chunks.push({ start: chunksCount * CHUNK_SIZE, to: lastNumber });
      }
      for (let j = 0, len = chunks.length; j < len; j++) {
        let chunk = chunks[j];
        let blocks = yield server.dal.getBlocksBetween(chunk.start, chunk.to);
Cédric Moreau's avatar
Cédric Moreau committed
373
        blocks.forEach(function (block) {
374
          jsoned.push(_(new Block(block).json()).omit('raw'));
Cédric Moreau's avatar
Cédric Moreau committed
375
376
        });
      }
377
378
      console.log(JSON.stringify(jsoned, null, "  "));
      yield server.disconnect();
379
      process.exit();
380
381
382
383
384
385
386
387
388
389
    })
      .catch(function(err) {
        logger.warn(err.message || err);
        server.disconnect()
          .catch(() => null)
          .then(function(){
            process.exit();
          });
      });
  }, NO_LOGS));
Cédric Moreau's avatar
Cédric Moreau committed
390

391
392
393
program
  .command('check-config')
  .description('Checks the node\'s configuration')
394
  .action(service(function (server) {
395
396
    server.checkConfig()
      .then(function () {
397
        logger.warn('Configuration seems correct.');
Cédric Moreau's avatar
Cédric Moreau committed
398
399
        server.disconnect();
        process.exit();
400
      })
401
      .catch(function(err) {
402
403
404
405
        logger.warn(err.message || err);
        server.disconnect();
        process.exit();
      });
406
407
  }));

408
409
410
program
  .command('config')
  .description('Register configuration in database')
411
  .action(connect(function (server, conf) {
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
    return co(function *() {
      if (program.autoconf) {
        let wiz = wizard();
        conf.upnp = !program.noupnp;
        yield Q.nbind(wiz.networkReconfiguration, wiz)(conf, program.autoconf, program.noupnp);
        yield Q.nbind(wiz.keyReconfigure, wiz)(conf, program.autoconf);
      }
      return server.dal.saveConf(conf)
        .then(function() {
          logger.debug("Configuration saved.");
          server.disconnect();
          process.exit();
        })
        .catch(function(err) {
          logger.error("Configuration could not be saved: " + err);
          server.disconnect();
          process.exit();
        });
    });
431
  }));
432

433
program
Cédric Moreau's avatar
Cédric Moreau committed
434
435
  .command('reset [config|data|peers|tx|stats|all]')
  .description('Reset configuration, data, peers, transactions or everything in the database')
436
  .action((type) => {
Cédric Moreau's avatar
Cédric Moreau committed
437
438
    let init = type != 'peers' ? connect : service;
    init(function (server) {
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
      if(!~['config', 'data', 'peers', 'tx', 'stats', 'all'].indexOf(type)){
        logger.error('Bad command: usage `reset config`, `reset data`, `reset peers`, `reset tx`, `reset stats` or `reset all`');
        server.disconnect();
        return;
      }
      if(type == 'data'){
        server.resetData(resetDone(server, 'Data successfully reseted.'));
      }
      if(type == 'peers'){
        server.resetPeers(resetDone(server, 'Peers successfully reseted.'));
      }
      if(type == 'tx'){
        server.resetTxs(resetDone(server, 'Transactions successfully reseted.'));
      }
      if(type == 'stats'){
        server.resetStats(resetDone(server, 'Stats successfully reseted.'));
      }
      if(type == 'config'){
        server.resetConf(resetDone(server, 'Configuration successfully reseted.'));
      }
      if(type == 'all'){
        server.reset(resetDone(server, 'Data & Configuration successfully reseted.'));
      }
    }, type != 'peers')(type);
  });
464

465
466
467
468
469
470
471
472
473
474
475
function resetDone(server, msg) {
  return function(err) {
    if(err)
      logger.error(err);
    else
      logger.warn(msg);
    server.disconnect();
    process.exit();
  }
}

Cédric Moreau's avatar
Cédric Moreau committed
476
477
function serverStart(server, conf) {

Cédric Moreau's avatar
Cédric Moreau committed
478
479
480
  return co(function *() {
    yield ucoin.statics.startNode(server, conf);
  })
481
    .catch((err) => {
Cédric Moreau's avatar
Cédric Moreau committed
482
483
      logger.error(err);
      server.disconnect();
484
      process.exit();
485
    });
Cédric Moreau's avatar
Cédric Moreau committed
486
}
487

Cédric Moreau's avatar
Cédric Moreau committed
488
489
490
491
492
493
494
function startWizard(step, server, conf, done) {
  var wiz = wizard(server);
  var task = {
    'currency': wiz.configCurrency,
    'basic': wiz.configBasic,
    'pow': wiz.configPoW,
    'network': wiz.configNetwork,
495
    'network-reconfigure': wiz.configNetworkReconfigure,
Cédric Moreau's avatar
Cédric Moreau committed
496
497
498
499
500
501
502
503
504
    'key': wiz.configKey,
    'ucp': wiz.configUCP
  };
  var wizDo = task[step] || wiz.configAll;
  async.waterfall([
    function (next){
      wizDo(conf, next);
    },
    function (next){
505
506
507
508
509
      return server.dal.saveConf(conf)
        .then(function() {
          logger.debug("Configuration saved.");
          next();
        })
510
        .catch(next);
Cédric Moreau's avatar
Cédric Moreau committed
511
512
513
    },
    function (next) {
      // Check config
514
      service(function (key, server, conf) {
Cédric Moreau's avatar
Cédric Moreau committed
515
516
517
518
519
520
521
        next();
      })(null, null);
    }
  ], done);
}

function bootstrapServer(host, port, server, conf) {
522
523
524
525
526
527
528
529
530
531
532
  async.series(getBootstrapOperations(host, port, server, conf), function (err) {
    if (err) {
      logger.error(err);
    }
    server.disconnect();
    process.exit();
  });
}

function getBootstrapOperations(host, port, server, conf) {
  var ops = [];
Cédric Moreau's avatar
Cédric Moreau committed
533
  var wiz = wizard(server);
534
  ops = ops.concat([
Cédric Moreau's avatar
Cédric Moreau committed
535
536
537
538
539
    function(next) {
      // Reset data
      server.reset(next);
    },
    function(next) {
540
541
      conf.upnp = !program.noupnp;
      wiz.networkReconfiguration(conf, program.autoconf, program.noupnp, next);
Cédric Moreau's avatar
Cédric Moreau committed
542
543
544
    },
    function(next) {
      // PublicKey
545
546
      var keyChosen = true;
      async.doWhilst(function(next){
Cédric Moreau's avatar
Cédric Moreau committed
547
548
        async.waterfall([
          function(next) {
549
            if (!conf.salt && !conf.passwd) {
550
              wiz.keyReconfigure(conf, program.autoconf, next);
Cédric Moreau's avatar
Cédric Moreau committed
551
552
553
554
555
            } else {
              next();
            }
          }
        ], next);
556
557
      }, function(){
        return !keyChosen;
Cédric Moreau's avatar
Cédric Moreau committed
558
559
      }, next);
    },
560
    function(next) {
561
      return server.dal.saveConf(conf).then(_.partial(next, null)).catch(next);
562
563
564
565
    }]);
  ops.push(function (next){
    logger.info('Configuration saved.');
    next();
Cédric Moreau's avatar
Cédric Moreau committed
566
  });
567
  return ops;
Cédric Moreau's avatar
Cédric Moreau committed
568
}
569

570
function commandLineConf(conf) {
571

572
  conf = conf || {};
573
574
  conf.sync = conf.sync || {};
  var cli = {
575
    currency: program.currency,
576
    cpu: program.cpu,
577
578
579
580
    server: {
      port: program.port,
      ipv4address: program.ipv4,
      ipv6address: program.ipv6,
581
582
      salt: program.salt,
      passwd: program.passwd,
583
584
      remote: {
        host: program.remoteh,
585
586
        ipv4: program.remote4,
        ipv6: program.remote6,
587
        port: program.remotep
588
      }
589
590
    },
    db: {
Cédric Moreau's avatar
Cédric Moreau committed
591
      mport: program.mport,
Cédric Moreau's avatar
Cédric Moreau committed
592
593
      mdb: program.mdb,
      home: program.home
Cédric Moreau's avatar
Cédric Moreau committed
594
595
596
597
    },
    net: {
      upnp:          program.upnp,
      noupnp:        program.noupnp
598
    },
Cédric Moreau's avatar
Cédric Moreau committed
599
600
601
602
    logs: {
      http:          program.httplogs,
      nohttp:        program.nohttplogs
    },
603
    ucp: {
604
      rootoffset:  program.rootoffset,
605
      sigPeriod:   program.sigPeriod,
606
      sigStock:    program.sigStock,
607
      sigWindow:   program.sigWindow,
608
      idtyWindow:   program.idtyWindow,
609
      msWindow:   program.msWindow,
610
611
      sigValidity: program.sigValidity,
      sigQty:      program.sigQty,
612
      msValidity:  program.msValidity,
613
614
      powZeroMin:  program.powZeroMin,
      powPeriod:   program.powPeriod,
615
      powDelay:    program.powDelay,
616
      participate: program.participate,
617
618
      ud0:         program.ud0,
      c:           program.growth,
619
      dt:          program.dt,
620
621
      incDateMin:  program.incDateMin,
      medtblocks:  program.medtblocks,
622
      dtdiffeval:  program.dtdiffeval,
623
      avgGenTime:  program.avgGenTime
624
    },
625
    isolate: program.isolate,
626
    forksize: program.forksize,
Cédric Moreau's avatar
Cédric Moreau committed
627
    nofork: program.nofork,
628
    timeout: program.timeout
629
  };
630

631
632
633
634
635
  // Update conf
  if (cli.currency)                         conf.currency       = cli.currency;
  if (cli.server.ipv4address)               conf.ipv4           = cli.server.ipv4address;
  if (cli.server.ipv6address)               conf.ipv6           = cli.server.ipv6address;
  if (cli.server.port)                      conf.port           = cli.server.port;
636
637
  if (cli.server.salt)                      conf.salt           = cli.server.salt;
  if (cli.server.passwd != undefined)       conf.passwd         = cli.server.passwd;
638
639
640
641
  if (cli.server.remote.host != undefined)  conf.remotehost     = cli.server.remote.host;
  if (cli.server.remote.ipv4 != undefined)  conf.remoteipv4     = cli.server.remote.ipv4;
  if (cli.server.remote.ipv6 != undefined)  conf.remoteipv6     = cli.server.remote.ipv6;
  if (cli.server.remote.port != undefined)  conf.remoteport     = cli.server.remote.port;
642
  if (cli.ucp.rootoffset)                   conf.rootoffset     = cli.ucp.rootoffset;
643
  if (cli.ucp.sigPeriod)                    conf.sigPeriod      = cli.ucp.sigPeriod;
644
  if (cli.ucp.sigStock)                     conf.sigStock       = cli.ucp.sigStock;
645
  if (cli.ucp.sigWindow)                    conf.sigWindow      = cli.ucp.sigWindow;
646
  if (cli.ucp.idtyWindow)                   conf.idtyWindow     = cli.ucp.idtyWindow;
647
  if (cli.ucp.msWindow)                     conf.msWindow       = cli.ucp.msWindow;
648
  if (cli.ucp.sigValidity)                  conf.sigValidity    = cli.ucp.sigValidity;
649
  if (cli.ucp.msValidity)                   conf.msValidity     = cli.ucp.msValidity;
650
  if (cli.ucp.sigQty)                       conf.sigQty         = cli.ucp.sigQty;
651
  if (cli.ucp.msValidity)                   conf.msValidity     = cli.ucp.msValidity;
652
653
  if (cli.ucp.powZeroMin)                   conf.powZeroMin     = cli.ucp.powZeroMin;
  if (cli.ucp.powPeriod)                    conf.powPeriod      = cli.ucp.powPeriod;
654
  if (cli.ucp.powDelay)                     conf.powDelay       = cli.ucp.powDelay;
655
  if (cli.ucp.participate)                  conf.participate    = cli.ucp.participate == 'Y';
656
657
658
  if (cli.ucp.dt)                           conf.dt             = cli.ucp.dt;
  if (cli.ucp.c)                            conf.c              = cli.ucp.c;
  if (cli.ucp.ud0)                          conf.ud0            = cli.ucp.ud0;
659
  if (cli.ucp.incDateMin)                   conf.incDateMin     = cli.ucp.incDateMin;
660
661
  if (cli.ucp.medtblocks)                   conf.medianTimeBlocks = cli.ucp.medtblocks;
  if (cli.ucp.avgGenTime)                   conf.avgGenTime     = cli.ucp.avgGenTime;
662
  if (cli.ucp.dtdiffeval)                   conf.dtDiffEval     = cli.ucp.dtdiffeval;
Cédric Moreau's avatar
Cédric Moreau committed
663
664
  if (cli.net.upnp)                         conf.upnp           = true;
  if (cli.net.noupnp)                       conf.upnp           = false;
665
  if (cli.cpu)                              conf.cpu            = Math.max(0.01, Math.min(1.0, cli.cpu));
Cédric Moreau's avatar
Cédric Moreau committed
666
667
  if (cli.logs.http)                        conf.httplogs       = true;
  if (cli.logs.nohttp)                      conf.httplogs       = false;
Cédric Moreau's avatar
Cédric Moreau committed
668
  if (cli.db.mport)                         conf.mport          = cli.db.mport;
Cédric Moreau's avatar
Cédric Moreau committed
669
  if (cli.db.home)                          conf.home           = cli.db.home;
Cédric Moreau's avatar
Cédric Moreau committed
670
  if (cli.db.mdb)                           conf.mdb            = cli.db.mdb;
671
  if (cli.isolate)                          conf.isolate        = cli.isolate;
672
  if (cli.timeout)                          conf.timeout        = cli.timeout;
673
  if (cli.forksize != null)                 conf.forksize       = cli.forksize;
674
675
676

  // Specific internal settings
  conf.createNext = true;
677
  return _(conf).extend({ routing: true });
678
}
679

680
function connect(callback, useDefaultConf) {
681
682
  return function () {
    var cbArgs = arguments;
683
    var dbName = program.mdb || "duniter_default";
Cédric Moreau's avatar
Cédric Moreau committed
684
    var dbHome = program.home;
685

Cédric Moreau's avatar
Cédric Moreau committed
686
    var server = ucoin({ home: dbHome, name: dbName }, commandLineConf());
687

Cédric Moreau's avatar
Cédric Moreau committed
688
    // Initialize server (db connection, ...)
689
690
    server.plugFileSystem(useDefaultConf)
      .then(() => server.loadConf())
691
692
693
694
695
696
      .then(function(){
        cbArgs.length--;
        cbArgs[cbArgs.length++] = server;
        cbArgs[cbArgs.length++] = server.conf;
        callback.apply(this, cbArgs);
      })
697
      .catch(function(err){
Cédric Moreau's avatar
Cédric Moreau committed
698
        logger.error(err);
699
        server.disconnect();
700
        process.exit();
701
      });
Cédric Moreau's avatar
Cédric Moreau committed
702
703

    // If ever the process gets interrupted
Cédric Moreau's avatar
Cédric Moreau committed
704
    let isSaving = false;
Cédric Moreau's avatar
Cédric Moreau committed
705
    process.on('SIGINT', function() {
Cédric Moreau's avatar
Cédric Moreau committed
706
707
708
709
710
711
712
713
714
      if (!isSaving) {
        isSaving = true;
        // Save DB
        server.disconnect()
          .catch(() => null)
          .then(function(){
            process.exit();
          });
      }
Cédric Moreau's avatar
Cédric Moreau committed
715
    });
716
717
718
  };
}

719
720
function service(callback, nologs) {

721
  return function () {
Cédric Moreau's avatar
Cédric Moreau committed
722
723
724

    if (nologs) {
      // Disable logs
725
      require('../app/lib/logger')().mute();
Cédric Moreau's avatar
Cédric Moreau committed
726
727
    }

728
    var cbArgs = arguments;
729
    var dbName = program.mdb;
Cédric Moreau's avatar
Cédric Moreau committed
730
    var dbHome = program.home;
731

732
733
734
    // Add log files for this instance
    logger.addHomeLogs(directory.getHome(dbName, dbHome));

Cédric Moreau's avatar
Cédric Moreau committed
735
    var server = ucoin({ home: dbHome, name: dbName, memory: program.memory }, commandLineConf());
736

Cédric Moreau's avatar
Cédric Moreau committed
737
    // Initialize server (db connection, ...)
738
    server.initWithDAL()
739
740
741
742
743
744
      .then(function(){
        cbArgs.length--;
        cbArgs[cbArgs.length++] = server;
        cbArgs[cbArgs.length++] = server.conf;
        callback.apply(this, cbArgs);
      })
745
      .catch(function(err){
Cédric Moreau's avatar
Cédric Moreau committed
746
        logger.error(err);
747
        server.disconnect();
748
        process.exit();
749
      });
Cédric Moreau's avatar
Cédric Moreau committed
750
751

    // If ever the process gets interrupted
Cédric Moreau's avatar
Cédric Moreau committed
752
    let isSaving = false;
Cédric Moreau's avatar
Cédric Moreau committed
753
    process.on('SIGINT', function() {
Cédric Moreau's avatar
Cédric Moreau committed
754
755
756
      if (!isSaving) {
        isSaving = true;
        // Save DB
757
        ((server && server.disconnect()) || Q())
Cédric Moreau's avatar
Cédric Moreau committed
758
759
760
761
762
          .catch(() => null)
          .then(function(){
            process.exit();
          });
      }
Cédric Moreau's avatar
Cédric Moreau committed
763
    });
764
765
766
  };
}

767
768
769
770
771
function exit (server) {
  server.disconnect();
  process.exit();
}

Cédric Moreau's avatar
Cédric Moreau committed
772
773
function logIfErrorAndExit (server, prefix) {
  return function (err) {
774
775
776
    if (err && err.uerr) {
      err = err.uerr.message;
    }
Cédric Moreau's avatar
Cédric Moreau committed
777
778
    err && logger.error((prefix ? prefix : "") + (err.message || err));
    server.disconnect();
779
    process.exit();
Cédric Moreau's avatar
Cédric Moreau committed
780
781
782
  };
}

783
784
785
786
787
function parsePercent(s) {
  var f = parseFloat(s);
  return isNaN(f) ? 0 : f;
}

788
789
790
791
792
793
program
  .on('*', function(cmd) {
    console.log("Unknown command '%s'. Try --help for a listing of commands & options.", cmd);
    process.exit();
  });

794
function webStart() {
795
796
797
798
799
800
  return co(function *() {
    var dbName = program.mdb;
    var dbHome = program.home;
    if (!program.memory) {
      let params = yield directory.getHomeFS(program.memory, dbHome);
      yield directory.createHomeIfNotExists(params.fs, params.home);
801

802
803
804
      // Add log files for this instance
      logger.addHomeLogs(params.home);
    }
805
    let webminapi = yield ucoin.statics.enableHttpAdmin({ home: dbHome, name: dbName, memory: program.memory }, commandLineConf(), false, program.webmhost, program.webmport);
806
807
    return webminapi.openConnections();

808
809
810
  });
}

811
program.parse(process.argv);
812
813

if (program.args.length == 0) {
814
815

  console.log('No command given, using default: ucoind webstart');
816
817
818
819
820
821
822
823
824
  return co(function *() {
    try {
      yield webStart();
    } catch (e) {
      logger.error(e);
      process.exit();
    }
  });
}