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

CLI change: --currency no more required, may be stored in configuration

parent c6e979ef
......@@ -49,13 +49,11 @@ module.exports.sign = function (message, done) {
module.exports.database = {
databaseName: '',
init: function () {
initModels();
},
connect: function (currency, host, port, reset, done) {
connect: function (databaseName, host, port, reset, done) {
if (arguments.length == 4) {
done = reset;
reset = false;
......@@ -78,7 +76,6 @@ module.exports.database = {
port = undefined;
}
host = host ? host : 'localhost';
databaseName = currency.replace(/\r/g, '').replace(/\n/g, '').replace(/\s/g, '_');
mongoose.connect('mongodb://' + host + (port ? ':' + port : '') + '/' + databaseName);
var db = mongoose.connection;
db.on('error', function (err) {
......@@ -102,21 +99,7 @@ module.exports.database = {
next();
},
function (next){
// Returns found conf or default one
next(null, confs[0] || new Configuration({
port: 8081,
ipv4: "localhost",
ipv6: null,
remotehost: null,
remoteipv4: null,
remoteipv6: null,
remoteport: null,
pgpkey: null,
pgppasswd: null,
kmanagement: 'ALL',
kaccept: 'ALL',
sync: {}
}));
next(null, confs[0]);
},
], done);
},
......@@ -165,24 +148,9 @@ module.exports.database = {
}
};
module.exports.openpgp = {
init: function (currency, conf, done) {
// Import PGP key
privateKey = openpgp.key.readArmored(conf.pgpkey).keys[0];
if(!privateKey.decrypt(conf.pgppasswd)) {
throw new Error("Wrong private key password.");
process.exit(1);
return;
}
done();
}
};
function initServices (currency, conf, done) {
function initServices (currency, conf, withoutNetworker, done) {
// Init ALL services
service.init(openpgp, currency, conf);
service.init(openpgp, currency, conf, withoutNetworker);
// Load services contexts
service.load(done);
}
......@@ -193,10 +161,77 @@ module.exports.services = {
module.exports.express = {
app: function (currency, conf, onLoaded) {
app: function (conf, onLoaded) {
var app = express();
var port = process.env.PORT || conf.port;
var currency = conf.currency;
// Checking configuration
privateKey = openpgp.key.readArmored(conf.pgpkey).keys[0];
if (!privateKey) {
onLoaded('A private key for this node is mandatory. Relaunch with --pgpkey <pathToKey>.');
return;
}
try {
if(!privateKey.decrypt(conf.pgppasswd)) {
onLoaded('Wrong private key password. Relaunch with --pgppasswd <password>.');
return;
}
} catch(ex) {
onLoaded('Not a valid private key, message was: "' + ex.message + '"');
return;
}
if (!conf.currency) {
onLoaded('No currency name was given. Relaunch with --currency <currencyName> parameter.');
return;
}
if(!conf.ipv4 && !conf.ipv6){
onLoaded("No interface to listen to. Relaunch with either --ipv4 or --ipv6 parameters.");
return;
}
if(!conf.remoteipv4 && !conf.remoteipv6){
onLoaded('Either --remote4 or --remote6 must be given');
return;
}
if (!conf.remoteport) {
onLoaded('--remotep is mandatory');
return;
}
if (conf.sync.AMDaemon == "ON") {
if (!conf.sync.AMStart) {
onLoaded('--amstart is mandatory when --amdaemon is set to ON');
return;
}
if (!conf.sync.AMFreq) {
onLoaded('--amfreq is mandatory when --amdaemon is set to ON');
return;
}
if (!conf.sync.UDFreq) {
onLoaded('--udfreq is mandatory when --amdaemon is set to ON');
return;
}
if (!conf.sync.UD0) {
onLoaded('--ud0 is mandatory when --amdaemon is set to ON');
return;
}
if (!conf.sync.UDPercent) {
onLoaded('--udpercent is mandatory when --amdaemon is set to ON');
return;
}
if (!conf.sync.Consensus) {
onLoaded('--consensus is mandatory when --amdaemon is set to ON');
return;
}
if (!conf.sync.MSExpires) {
onLoaded('--msexpires is mandatory when --amdaemon is set to ON');
return;
}
if (!conf.sync.VTExpires) {
onLoaded('--vtexpires is mandatory when --amdaemon is set to ON');
return;
}
}
// all environments
app.set('conf', conf);
......@@ -211,12 +246,8 @@ module.exports.express = {
app.use(express.session());
async.series([
function (next) {
// OpenPGP functions
module.exports.openpgp.init(currency, conf, next);
},
function (next){
initServices(currency, conf, next);
initServices(currency, conf, false, next);
},
function (next){
// HTTP Signatures
......@@ -294,22 +325,6 @@ module.exports.express = {
app.get( '/registry/amendment/:amendment_number', reg.amendmentNext);
app.get( '/registry/amendment/:amendment_number/vote', reg.askVote);
if(!conf.ipv4 && !conf.ipv6){
onLoaded("No interface to listen to. Relaunch with either --ipv4 or --ipv6 parameters.");
return;
}
if (!conf.remoteport) {
onLoaded('--remotep is mandatory');
return;
}
if(!conf.remoteipv4 && !conf.remoteipv6){
onLoaded('Either --remote4 or --remote6 must be given');
return;
}
if (conf.sync.AMDaemon == "ON" && !conf.sync.AMStart) {
onLoaded('--amstart is mandatory when --amdaemon is set to ON');
return;
}
// If the node's peering entry does not exist or is outdated,
// a new one is generated.
var PeeringService = service.Peering;
......
......@@ -13,22 +13,23 @@ var Transaction = mongoose.model('Transaction');
var Wallet = mongoose.model('Wallet');
var Peer = mongoose.model('Peer');
var vucoin = require('vucoin');
var jpgp = require('./jpgp');
var logger = require('./logger')('sync');
var service = require('../service');
var CONST_FORCE_TX_PROCESSING = false;
// Services
var KeyService = service.Key;
var VoteService = service.Vote;
var TransactionService = service.Transactions;
var WalletService = service.Wallet;
var PeeringService = service.Peering;
var StrategyService = service.Strategy;
var ParametersService = service.Parameters;
var SyncService = service.Sync;
var KeyService = null;
var VoteService = null;
var TransactionService = null;
var WalletService = null;
var PeeringService = null;
var StrategyService = null;
var ParametersService = null;
var SyncService = null;
module.exports = function Synchroniser (host, port, authenticated, currency, conf) {
module.exports = function Synchroniser (server, host, port, authenticated, conf) {
var that = this;
this.remoteFingerprint = null;
......@@ -60,11 +61,43 @@ module.exports = function Synchroniser (host, port, authenticated, currency, con
},
function (json, next){
remotePeer.copyValuesFrom(json);
ParametersService.getPeeringEntryFromRaw(remotePeer.getRaw(), remotePeer.signature, next);
var entry = remotePeer.getRaw();
var signature = remotePeer.signature;
// Parameters
if(!(entry && signature)){
callback('Requires a peering entry + signature');
return;
}
// Check signature's key ID
var keyID = jpgp().signature(signature).issuer();
if(!(keyID && keyID.length == 16)){
callback('Cannot identify signature issuer`s keyID');
return;
}
next(null, entry + signature, keyID);
},
function (signedPR, pubkey, next) {
function (signedPR, keyID, next) {
var peer = new Peer();
async.waterfall([
function (next){
peer.parse(signedPR, next);
},
function (peer, next){
peer.verify(peer.currency, next);
},
function (verified, next) {
server.services.init(peer.currency, conf, true, next);
},
function (next){
KeyService = service.Key;
VoteService = service.Vote;
TransactionService = service.Transactions;
WalletService = service.Wallet;
PeeringService = service.Peering;
StrategyService = service.Strategy;
ParametersService = service.Parameters;
SyncService = service.Sync;
Peer.find({ fingerprint: remotePeer.fingerprint, hash: sha1(signedPR).toUpperCase() }, next);
},
function (peers, next){
......@@ -75,7 +108,7 @@ module.exports = function Synchroniser (host, port, authenticated, currency, con
next();
},
function (next){
PeeringService.submit(signedPR, pubkey, next);
PeeringService.submit(signedPR, keyID, next);
},
], function (err, peer) {
if(err && !peer){
......
......@@ -3,20 +3,41 @@ var Schema = mongoose.Schema;
var logger = require('../lib/logger')();
var ConfigurationSchema = new Schema({
port: {"type": Number, "default": 0},
ipv4: String,
ipv6: String,
remotehost: String,
remoteipv4: String,
remoteipv6: String,
remoteport: Number,
pgpkey: String,
pgppasswd: String,
kmanagement: String,
kaccept: String,
sync: Object
currency: {"type": String, "default": null},
port: {"type": Number, "default": 8033},
ipv4: {"type": String, "default": "127.0.0.1"},
ipv6: {"type": String, "default": null},
remotehost: {"type": String, "default": null},
remoteipv4: {"type": String, "default": null},
remoteipv6: {"type": String, "default": null},
remoteport: {"type": Number, "default": null},
pgpkey: {"type": String, "default": null},
pgppasswd: {"type": String, "default": null},
kmanagement: {"type": String, "default": "ALL"},
kaccept: {"type": String, "default": "ALL"},
sync: {"type": Object, "default": {
AMDaemon: "OFF", // No deamon by default
AMFreq: 3600*24, // every day
AMFreq: 3600*24, // every day
UDFreq: 3600*24*30.4375, // every month
UD0: 100,
UDPercent: 0.007376575, // 0.73%
Consensus: 2/3,
MSExpires: 3600*24*30.4375*6, // 6 months
VTExpires: 3600*24*30.4375*1 // 1 months
}}
});
// Automatic Monetary Contract default parameters:
// - Voting start: None (must be given)
// - Voting frequency: 1 day
// - UD frequency: 1 month (30.4375 days/month)
// - UD(0): 100 unities
// - UD % (aka 'c'): 9.22% a year <=> 0.7376575% a month
// - UD Minimal Coin: none
// - Voting % threshold: 2/3 of votes
// - Membership max. delay: 6 months
ConfigurationSchema.pre('save', function (next) {
if(!this.kmanagement || !this.kmanagement.match(/^(ALL|KEYS)$/)){
logger.error('Incorrect --kmanagement value, reset to default `KEYS` value');
......
......@@ -14,7 +14,7 @@ function Service () {
this.HTTP = services.HTTP = require("./HTTPService");
this.Merkle = services.Merkle = require("./MerkleService");
this.init = function (pgp, currency, conf) {
this.init = function (pgp, currency, conf, withoutNetworker) {
// Services requiring configuration
this.Parameters = services.Parameters = require("./ParametersService").get(currency);
this.PublicKey = services.PublicKey = require("./PublicKeyService").get(pgp, currency, conf);
......@@ -27,7 +27,7 @@ function Service () {
this.Vote = services.Vote = require("./VoteService").get(pgp, currency, conf);
// Binds Peering events to Networker
networker(this.Peering);
!withoutNetworker && networker(this.Peering);
};
/**
......
......@@ -16,12 +16,12 @@ function keys (val) {
program
.version('0.5.3')
.usage('--currency <name> [options] <command> [options]')
.usage('<command> [options]')
.option('-p, --port <port>', 'Port to listen for requests', parseInt)
.option('-c, --currency <name>', 'Name of the currency managed by this node.')
.option('--mhost <host>', 'MongoDB host.')
.option('--mport <port>', 'MongoDB port.')
.option('--mdb <name>', 'MongoDB database name (defaults to currency name).')
.option('-d, --mdb <name>', 'MongoDB database name (defaults to "ucoin_default").')
.option('--pgpkey <keyPath>', 'Path to the private key used for signing HTTP responses.')
.option('--pgppasswd <password>', 'Password for the key provided with --httpgp-key option.')
.option('--ipv4 <address>', 'IPV4 interface to listen for requests')
......@@ -40,6 +40,7 @@ program
.option('--udpercent <float>', 'Percent of monetary mass growth per UD', parseFloat)
.option('--consensus <float>', 'Percent of voters required to accept an amendment', parseFloat)
.option('--msvalidity <timestamp>', 'Duration of a valid membership, in seconds', parseInt)
.option('--vtvalidity <timestamp>', 'Duration of a valid voter, in seconds', parseInt)
;
program
......@@ -51,16 +52,10 @@ program
conf.sync.AMDaemon = "OFF";
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);
var remote = new Synchroniser(server, host, port, false, conf);
remote.sync(next);
},
], function (err) {
......@@ -187,10 +182,10 @@ program
.action(connect(function (conf) {
// Launching server
server.express.app(program.currency, conf, function (err, app) {
server.express.app(conf, function (err, app) {
if(err){
logger.error(err);
console.error(err);
server.database.disconnect();
process.exit();
return;
......@@ -204,10 +199,11 @@ var config = {};
function overrideConf(conf) {
// Ensure is not null and have good structure
conf = conf || {};
conf.sync = conf.sync || {};
var Configuration = mongoose.model('Configuration');
conf = conf || new Configuration();
config = {
cli = {
currency: program.currency,
server: {
port: program.port,
ipv4address: program.ipv4,
......@@ -241,43 +237,34 @@ function overrideConf(conf) {
UDPercent: program.udpercent,
Consensus: program.consensus,
MSExpires: program.msfreq,
UCSBlockFreq: program.ubfreq
VTExpires: program.vtfreq
}
};
// Update conf
if(config.server.pgp.key) config.server.pgp.key = fs.readFileSync(config.server.pgp.key, 'utf8');
conf.ipv4 = config.server.ipv4address || conf.ipv4;
conf.ipv6 = config.server.ipv6address || conf.ipv6;
conf.port = config.server.port || conf.port;
conf.pgpkey = config.server.pgp.key || conf.pgpkey;
conf.pgppasswd = config.server.pgp.password || conf.pgppasswd;
conf.remotehost = config.server.remote.host != undefined ? config.server.remote.host : conf.remotehost;
conf.remoteipv4 = config.server.remote.ipv4 != undefined ? config.server.remote.ipv4 : conf.remoteipv4;
conf.remoteipv6 = config.server.remote.ipv6 != undefined ? config.server.remote.ipv6 : conf.remoteipv6;
conf.remoteport = config.server.remote.port != undefined ? config.server.remote.port : conf.remoteport;
conf.kmanagement = config.policy.keys || conf.kmanagement;
conf.kaccept = config.policy.pubkeys || conf.kaccept;
// Monetary Contract sync default parameters:
// - Voting start: None (must be given)
// - Voting frequency: 1 day
// - UD frequency: 1 month (30.4375 days/month)
// - UD(0): 100 unities
// - UD % (aka 'c'): 9.22% a year <=> 0.7376575% a month
// - UD Minimal Coin: none
// - Voting % threshold: 2/3 of votes
// - Membership max. delay: 6 months
if(cli.server.pgp.key) cli.server.pgp.key = fs.readFileSync(cli.server.pgp.key, 'utf8');
conf.currency = cli.currency || conf.currency;
conf.ipv4 = cli.server.ipv4address || conf.ipv4;
conf.ipv6 = cli.server.ipv6address || conf.ipv6;
conf.port = cli.server.port || conf.port;
conf.pgpkey = cli.server.pgp.key || conf.pgpkey;
conf.pgppasswd = cli.server.pgp.password || conf.pgppasswd;
conf.remotehost = cli.server.remote.host != undefined ? cli.server.remote.host : (conf.remotehost || conf.host);
conf.remoteipv4 = cli.server.remote.ipv4 != undefined ? cli.server.remote.ipv4 : (conf.remoteipv4 || conf.ipv4);
conf.remoteipv6 = cli.server.remote.ipv6 != undefined ? cli.server.remote.ipv6 : (conf.remoteipv6 || conf.ipv6);
conf.remoteport = cli.server.remote.port != undefined ? cli.server.remote.port : (conf.remoteport || conf.port);
conf.kmanagement = cli.policy.keys || conf.kmanagement;
conf.kaccept = cli.policy.pubkeys || conf.kaccept;
conf.sync = {
AMDaemon: config.sync.AMDaemon || conf.sync.AMDaemon,
AMStart: config.sync.AMStart || conf.sync.AMStart,
AMFreq: config.sync.AMFreq || conf.sync.AMFreq || 3600*24, // every day
UDFreq: config.sync.UDFreq || conf.sync.UDFreq || 3600*24*30.4375, // every month
UD0: config.sync.UD0 || conf.sync.UD0 || 100,
UDPercent: config.sync.UDPercent || conf.sync.UDPercent || 0.007376575, // 0.73%
UDMinCoin: config.sync.UDMinCoin || conf.sync.UDMinCoin,
Consensus: config.sync.Consensus || conf.sync.Consensus || 2/3,
MSExpires: config.sync.MSExpires || conf.sync.MSExpires || 3600*24*30.4375*6, // 6 months
UCSBlockFreq: config.sync.UCSBlockFreq || conf.sync.UCSBlockFreq || 3600 // hourly
AMDaemon: cli.sync.AMDaemon || conf.sync.AMDaemon,
AMStart: cli.sync.AMStart || conf.sync.AMStart,
AMFreq: cli.sync.AMFreq || conf.sync.AMFreq,
UDFreq: cli.sync.UDFreq || conf.sync.UDFreq,
UD0: cli.sync.UD0 || conf.sync.UD0,
UDPercent: cli.sync.UDPercent || conf.sync.UDPercent,
Consensus: cli.sync.Consensus || conf.sync.Consensus,
MSExpires: cli.sync.MSExpires || conf.sync.MSExpires,
VTExpires: cli.sync.VTExpires || conf.sync.VTExpires
};
// Specific internal settings
......@@ -288,14 +275,10 @@ function overrideConf(conf) {
function connect(callback) {
return function () {
var cbArgs = arguments;
if(!program.currency){
logger.error('--currency parameter is mandatory.');
program.help();
return;
}
var dbName = program.mdb || "ucoin_default";
// Connecting to DB
server.database.connect(program.mdb || program.currency, program.mhost, program.mport, function (err, conf) {
server.database.connect(dbName, program.mhost, program.mport, function (err, conf) {
if(err){
logger.debug("Error connecting to DB: " + err);
......
......@@ -93,44 +93,33 @@ $ git clone git@github.com:ucoin-io/ucoin.git
$ sudo npm install ./ucoin -g
```
## Get uCoin run
Launch it using the following command:
```bash
$ ucoind --currency beta_brousouf start
uCoin server listening on localhost port 8081
```
Ok, this is cool, but several features may be turned on. Let's configure uCoin.
## Get uCoin configured
## Configure uCoin (required)
All uCoin configuration is stored in its database, i.e. MongoDB.
The database name is the currency name given to uCoin when started.
Thus, when using:
To add configuration parameters to uCoin, use `config` command:
```bash
$ ucoind --currency beta_brousouf start
$ ucoind config
```
... the targeted database is "beta_brousouf".
To configure "beta_brousouf" database, just run uCoin with the `config` command, instead of `start` parameter:
The default database name is "ucoin_default". Thus, when using above command, the targeted database is "ucoin_default".
To deal with another database, just add `--mdb` parameter:
```bash
$ ucoind --currency beta_brousouf config
$ ucoind --mdb mycurrency config
```
This will alter `mycurrency` database *only*.
All the parameters given after this will be stored in the database.
Any parameter given to this command will be stored in the database.
### Network parameters
By default, ucoin runs on port 8081. You may change it using the --port parameter:
By default, ucoin runs on port 8033. You may change it using the --port parameter:
```bash
$ ucoind --currency beta_brousouf config --port 80
$ ucoind config --port 80
```
(may require root access to launch on port 80)
......@@ -138,25 +127,25 @@ $ ucoind --currency beta_brousouf config --port 80
It is also possible to specify the IPv4 interface:
```bash
$ ucoind --currency beta_brousouf config -p 8888 --ipv4 127.0.0.1
$ ucoind config -p 8888 --ipv4 127.0.0.1
```
Or IPv6 interface:
```bash
$ ucoind --currency beta_brousouf config -p 8888 --ipv6 ::1
$ ucoind config -p 8888 --ipv6 ::1
```
Or both:
```bash
$ ucoind --currency beta_brousouf config -p 8888 --ipv4 127.0.0.1 --ipv6 ::1
$ ucoind config -p 8888 --ipv4 127.0.0.1 --ipv6 ::1
```
Launching uCoin will results:
Launching uCoin (when completely configured) will results:
```bash
$ ucoind --currency beta_brousouf start
$ ucoind start
uCoin server listening on 127.0.0.1 port 8888
uCoin server listening on ::1 port 8888
......@@ -168,7 +157,7 @@ Note too that listening to multiple interfaces doesn't imply mutiple program ins
#### Peering informations
uCoin protocol uses peering mecanisms, hence needs to any ucoin node to be reachable through the network.
uCoin protocol uses peering mecanisms, hence needs any ucoin node to be reachable through the network.
As the server may be behind a reverse proxy, or because hosts may change of address, remote informations are likely to be different from listening host and port parameters. ucoin software defines 4 remote parameters you need to precise for your ucoin instance to be working:
......@@ -180,29 +169,29 @@ As the server may be behind a reverse proxy, or because hosts may change of addr
You must define at least `--remote4` and `--remotep` not to have any error. Here is an example:
```bash
$ ucoind --currency beta_brousouf config --remoteh "some.remote.url" --remotep "8844" --remote4 "11.11.11.11" --remote6 "::1"
$ ucoind config --remoteh "some.remote.url" --remotep "8844" --remote4 "11.11.11.11" --remote6 "::1"
```
Note that this is not required and may be removed in the future, as uCoin protocol already include peering mecanisms giving network informations.
#### Authentication
uCoin protocol requires your responses to be signed in order to be interpreted. Such a feature is very important to authenticate nodes messages. To use this feature, just configure uCoin using `--pgpkey` parameter:
uCoin protocol requires your responses to be signed in order to be interpreted. Such a feature is very important to authenticate nodes' messages. To use this feature, just configure uCoin using `--pgpkey` parameter:
```bash
$ ucoind --currency beta_brousouf config --pgpkey /path/to/private/key
$ ucoind config --pgpkey /path/to/private/key
```
Eventually, you might need to give a password, otherwise uCoin will crash:
```bash
$ ucoind --currency beta_brousouf config --pgppasswd "ultr[A]!%HiGhly-s3cuR3-p4ssw0d"
$ ucoind config --pgppasswd "ultr[A]!%HiGhly-s3cuR3-p4ssw0d"
```
Resulting in:
```bash
$ ucoind --currency beta_brousouf start
$ ucoind start
Signed requests with PGP: enabled.
uCoin server listening on 127.0.0.1 port 8888
......@@ -230,13 +219,13 @@ In this cas, you need to synchronize with existing peers to fetch existing:
This is easily done with:
```bash
$ ucoind --currency beta_brousouf sync <host_name> <port>
$ ucoind sync <host_name> <port>
```
For example, to synchronise with [ucoin.twiced.fr:9101](http://ucoin.twiced.fr:9101/network/peering):