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

Commit 40f25a37 authored by Cédric Moreau's avatar Cédric Moreau

Renaming 'CommunityFlow' to 'Statement' + multicasting statements over the network

parent 64d03b3d
......@@ -199,11 +199,11 @@ function RegistryBinding (registryServer, conf) {
processMerkle(Merkle.votersOut.bind(Merkle), Merkle.mapForVotings.bind(Merkle), req, res);
};
this.communityFlowPost = function (req, res) {
this.statementPost = function (req, res) {
var onError = http400(res);
http2raw.communityFlow(req, onError)
.pipe(unix2dos())
.pipe(parsers.parseCommunityFlow(onError))
.pipe(parsers.parseStatement(onError))
.pipe(versionFilter(onError))
.pipe(currencyFilter(conf.currency, onError))
.pipe(extractSignature(onError))
......@@ -254,7 +254,7 @@ function RegistryBinding (registryServer, conf) {
});
};
this.askFlow = function (req, res) {
this.askStatement = function (req, res) {
var that = this;
async.waterfall([
......@@ -264,14 +264,14 @@ function RegistryBinding (registryServer, conf) {
},
function (amNumber, algo, next) {
SyncService.getFlow(parseInt(amNumber), algo, next)
SyncService.getStatement(parseInt(amNumber), algo, next)
},
], function (err, cf, am) {
http.answer(res, 404, err, function () {
res.end(JSON.stringify({
"communityflow": cf.json()
"statement": cf.json()
}, null, " "));
});
});
......
......@@ -11,14 +11,18 @@ var sha1 = require('sha1');
var vucoin = require('vucoin');
var logger = require('./logger')('daemon');
module.exports = function (conn, PeeringService, ContractService, SyncService) {
return new Daemon(conn, PeeringService, ContractService, SyncService);
module.exports = function (regServer) {
return new Daemon(regServer);
};
function Daemon (conn, PeeringService, ContractService, SyncService) {
function Daemon (regServer) {
var Amendment = conn.model('Amendment');
var Key = conn.model('Key');
var conn = regServer.conn;
var PeeringService = regServer.PeeringService;
var ContractService = regServer.ContractService;
var SyncService = regServer.SyncService;
var Amendment = conn.model('Amendment');
var Key = conn.model('Key');
// self reference, private scope
var daemon = this;
......@@ -100,11 +104,15 @@ function Daemon (conn, PeeringService, ContractService, SyncService) {
// Must be a voter to vote!
Key.wasVoter(selfFingerprint, current.number, function (err, wasVoter) {
if (!err && wasVoter) {
logger.debug("Asking CF for SELF peer");
SyncService.getFlow(current.number + 1, Algorithm, function (err, json) {
// Do nothing with result: it has been done by SyncService (self-submitted the vote)
callback(err);
});
logger.debug("Asking Statement for SELF peer");
async.waterfall([
function (next){
SyncService.getStatement(current.number + 1, Algorithm, next);
},
function (statement, next){
regServer.submit(statement, false, next);
},
], callback);
return;
}
callback(err);
......@@ -141,7 +149,7 @@ function Daemon (conn, PeeringService, ContractService, SyncService) {
vote: ['connect', function (cb, results) {
var node = results.connect;
// Ask for peer's vote
node.registry.amendment.flow(current.number + 1, Algorithm, cb);
node.registry.amendment.statement(current.number + 1, Algorithm, cb);
}]
}, function (err, results) {
done(err, results.vote);
......
......@@ -174,7 +174,7 @@ module.exports = new function() {
return unix2dos(signed(that.getVotingWithoutSignature(json), json));
};
this.getCommunityFlowWithoutSignature = function (json) {
this.getStatementWithoutSignature = function (json) {
var raw = "";
raw += "Version: " + json.version + "\n";
raw += "Currency: " + json.currency + "\n";
......@@ -193,8 +193,8 @@ module.exports = new function() {
return unix2dos(raw);
};
this.getCommunityFlow = function (json) {
return unix2dos(signed(that.getCommunityFlowWithoutSignature(json), json));
this.getStatement = function (json) {
return unix2dos(signed(that.getStatementWithoutSignature(json), json));
};
function signed (raw, json) {
......
......@@ -126,6 +126,19 @@ function Multicaster () {
});
});
that.on('statement', function(statement, peers) {
logger.debug('--> new Statement to be sent to %s peer(s)', peers.length);
peers.forEach(function(peer){
fifo.push(function (sent) {
// Do propagating
logger.debug('sending %s\'s statement to peer %s', statement.keyID(), peer.keyID());
sendStatement(peer, statement, success(function (err) {
sent();
}));
});
});
});
that.on('forward', function(forward, peers, done) {
logger.debug('--> new Forward to be sent to %s peer(s)', peers.length);
fifo.push(function (sent) {
......@@ -150,12 +163,15 @@ function Multicaster () {
});
});
});
this.sendPubkey = sendPubkey;
}
util.inherits(Multicaster, stream.Transform);
function sendPubkey(peer, pubkey, done) {
logger.info('POST pubkey to %s', peer.keyID());
var keyID = peer.keyID();
logger.info('POST pubkey to %s', keyID.match(/\?/) ? peer.getURL() : keyID);
post(peer, '/pks/add', {
"keytext": pubkey.getRaw(),
"keysign": pubkey.signature
......@@ -218,6 +234,14 @@ function sendVoting(peer, voting, done) {
}, done);
}
function sendStatement(peer, statement, done) {
logger.info('POST statement to %s', peer.keyID());
post(peer, '/registry/amendment/statement', {
"statement": statement.getRaw(),
"signature": statement.signature
}, done);
}
function success (done) {
return function (err, res, body) {
if (err) {
......
......@@ -9,7 +9,7 @@ module.exports = {
parseWallet: instanciate.bind(instanciate, require('./wallet')),
parseMembership: instanciate.bind(instanciate, require('./membership')),
parseVoting: instanciate.bind(instanciate, require('./voting')),
parseCommunityFlow: instanciate.bind(instanciate, require('./communityflow')),
parseStatement: instanciate.bind(instanciate, require('./statement')),
};
function instanciate (constructorFunc, onError) {
......
......@@ -6,9 +6,9 @@ var split = require('../../../split');
var unix2dos = require('../../../unix2dos');
var _ = require('underscore');
module.exports = CommunityFlowParser;
module.exports = StatementParser;
function CommunityFlowParser (onError) {
function StatementParser (onError) {
var captures = [
{prop: "version", regexp: /Version: (.*)/},
......@@ -28,7 +28,7 @@ function CommunityFlowParser (onError) {
{prop: "date", regexp: /Date: (.*)/, parser: parseDateFromTimestamp}
];
var multilineFields = [];
GenericParser.call(this, captures, multilineFields, rawer.getCommunityFlow, onError);
GenericParser.call(this, captures, multilineFields, rawer.getStatement, onError);
this._clean = function (obj) {
};
......@@ -66,7 +66,7 @@ function CommunityFlowParser (onError) {
err = {code: codes['BAD_FIELD'], message: "Incorrect amendment field: must be contain an amendment"};
}
['membersJoiningRoot', 'membersLeavingRoot', 'votersJoiningRoot', 'votersLeavingRoot'].forEach(function(field){
if(!err && !obj[field].match(/^[A-Z\d]+$/))
if(!err && obj[field] && !obj[field].match(/^[A-Z\d]+$/))
err = {code: codes['BAD_MERKLE_SUMMARY'], message: "Incorrect " + field + " field: must be a SHA-1 uppercased hash"};
});
return err && err.message;
......@@ -99,4 +99,4 @@ function parseAmendmentHash (value) {
var parseMerkleNumber = parseAmendmentNumber;
var parseMerkleRoot = parseAmendmentHash;
util.inherits(CommunityFlowParser, GenericParser);
util.inherits(StatementParser, GenericParser);
......@@ -11,7 +11,7 @@ module.exports = {
wallet: instanciate.bind(null, Http2RawWallet),
membership: instanciate.bind(null, Http2RawMembership),
voting: instanciate.bind(null, Http2RawVoting),
communityFlow: instanciate.bind(null, Http2RawCommunityFlow),
communityFlow: instanciate.bind(null, Http2RawStatement),
};
function instanciate (constructorFunc, req, onError) {
......@@ -156,16 +156,16 @@ function Http2RawVoting (req, onError) {
}
}
function Http2RawCommunityFlow (req, onError) {
function Http2RawStatement (req, onError) {
stream.Readable.call(this);
this._read = function () {
if(!(req.body && req.body.flow && req.body.signature)){
onError('Requires a flow + signature');
if(!(req.body && req.body.statement && req.body.signature)){
onError('Requires a statement + signature');
}
else {
this.push(req.body.flow + req.body.signature);
this.push(req.body.statement + req.body.signature);
}
this.push(null);
}
......@@ -180,4 +180,4 @@ util.inherits(Http2RawStatus, stream.Readable);
util.inherits(Http2RawWallet, stream.Readable);
util.inherits(Http2RawMembership, stream.Readable);
util.inherits(Http2RawVoting, stream.Readable);
util.inherits(Http2RawCommunityFlow, stream.Readable);
util.inherits(Http2RawStatement, stream.Readable);
......@@ -33,7 +33,7 @@ function Router (serverFPR, conn) {
else if (obj.requiredTrusts ? true : false) { route('wallet', obj, getRandomInUPPeers, done); }
else if (obj.type && obj.type == "MEMBERSHIP" ? true : false) { route('membership', obj, getRandomInUPPeers, done); }
else if (obj.type && obj.type == "VOTING" ? true : false) { route('voting', obj, getRandomInUPPeers, done); }
else if (obj.algorithm ? true : false) { route('flow', obj, getRandomInUPPeers, done); }
else if (obj.algorithm ? true : false) { route('statement', obj, getRandomInUPPeers, done); }
else {
done();
}
......
......@@ -6,7 +6,7 @@ var _ = require('underscore');
var rawer = require('../lib/rawer');
var Schema = mongoose.Schema;
var CommunityFlowSchema = new Schema({
var StatementSchema = new Schema({
version: String,
currency: String,
issuer: { type: String },
......@@ -31,20 +31,20 @@ var CommunityFlowSchema = new Schema({
updated: { type: Date, default: Date.now }
});
CommunityFlowSchema.pre('save', function (next) {
StatementSchema.pre('save', function (next) {
this.updated = Date.now();
next();
});
CommunityFlowSchema.virtual('pubkey').get(function () {
StatementSchema.virtual('pubkey').get(function () {
return this._pubkey;
});
CommunityFlowSchema.virtual('pubkey').set(function (am) {
StatementSchema.virtual('pubkey').set(function (am) {
this._pubkey = am;
});
CommunityFlowSchema.methods = {
StatementSchema.methods = {
keyID: function () {
return this.issuer && this.issuer.length > 24 ? "0x" + this.issuer.substring(24) : "0x?";
......@@ -88,52 +88,52 @@ CommunityFlowSchema.methods = {
},
getRaw: function() {
return rawer.getCommunityFlowWithoutSignature(this);
return rawer.getStatementWithoutSignature(this);
},
getRawSigned: function() {
return rawer.getCommunityFlow(this);
return rawer.getStatement(this);
}
}
CommunityFlowSchema.statics.getTheOne = function (amNumber, issuer, algo, done) {
StatementSchema.statics.getTheOne = function (amNumber, issuer, algo, done) {
this.find({ amendmentNumber: amNumber, issuer: issuer, algorithm: algo }, function (err, entries) {
if(entries && entries.length == 1){
done(err, entries[0]);
return;
}
if(!entries || entries.length == 0){
done('No CommunityFlow entry found');
done('No Statement entry found');
return;
}
if(entries || entries.length > 1){
done('More than one CommunityFlow entry found');
done('More than one Statement entry found');
}
});
}
CommunityFlowSchema.statics.getSelf = function (amNumber, algo, done) {
StatementSchema.statics.getSelf = function (amNumber, algo, done) {
this.find({ amendmentNumber: amNumber, algorithm: algo, selfGenerated: true }, function (err, entries) {
if(entries && entries.length == 1){
done(err, entries[0]);
return;
}
if(!entries || entries.length == 0){
done('No CommunityFlow entry found');
done('No Statement entry found');
return;
}
if(entries || entries.length > 1){
done('More than one CommunityFlow entry found');
done('More than one Statement entry found');
}
});
}
CommunityFlowSchema.statics.getForAmendmentAndAlgo = function (amNumber, algo, done) {
StatementSchema.statics.getForAmendmentAndAlgo = function (amNumber, algo, done) {
this.find({ amendmentNumber: amNumber, algorithm: algo }, done);
}
CommunityFlowSchema.statics.getByIssuerAlgoAmendmentHashAndNumber = function (issuer, algo, amHash, amNumber, done) {
StatementSchema.statics.getByIssuerAlgoAmendmentHashAndNumber = function (issuer, algo, amHash, amNumber, done) {
this.find({ amendmentNumber: amNumber, amendmentHash: amHash, issuer: issuer, algorithm: algo }, done);
}
module.exports = CommunityFlowSchema;
module.exports = StatementSchema;
This diff is collapsed.
......@@ -54,6 +54,47 @@ program
.option('--algorithm <AnyKey|1Sig>', 'Algorithm to use for membership')
;
program
.command('send-pubkey [host] [port]')
.description('Send self pubkey to given node')
.action(function (host, port) {
// Only show message "Saved"
require('log4js').configure({
"appenders": [{
category: "ucoind",
type: "console"
}]
});
connect(DO_NOT_LISTEN_HTTP, ucoin.createPeerServer, function (server, conf) {
// Only show message "Saved"
require('log4js').configure({
"appenders": [{
category: "multicaster",
type: "console"
}]
});
async.waterfall([
function (next){
server.on('peerInited', next);
},
function (next){
server.PublicKeyService.getTheOne(server.PeeringService.cert.fingerprint, next);
},
function (pubkey, next){
var Peer = server.conn.model('Peer');
var peer = new Peer({
endpoints: [['BASIC_MERKLED_API', host, port].join(' ')]
});
multicaster().sendPubkey(peer, pubkey, next);
},
], function (err, result) {
if (err) logger.error(err);
server.disconnect();
process.exit();
});
})(null);
});
program
.command('wizard [step]')
.description('Launch the configuration Wizard')
......
......@@ -43,19 +43,19 @@
* [coins/view/[COIN_ID]/history](#coinsviewcoin_idhistory)
* [registry/](#registry)
* [parameters](#parameters)
* [flow](#flow)
* [community/members (POST)](#communitymembers-post)
* [community/members/[PGP_FINGERPRINT]/current](#communitymemberspgp_fingerprintcurrent)
* [community/members/[PGP_FINGERPRINT]/history](#communitymemberspgp_fingerprinthistory)
* [community/voters (POST)](#communityvoters-post)
* [community/voters/[PGP_FINGERPRINT]/current](#communityvoterspgp_fingerprintcurrent)
* [community/voters/[PGP_FINGERPRINT]/history](#communityvoterspgp_fingerprinthistory)
* [amendment/statement](#amendmentstatement)
* [amendment/[AM_NUMBER]/[ALGO]/members/in](#amendmentam_numberalgomembersin)
* [amendment/[AM_NUMBER]/[ALGO]/members/out](#amendmentam_numberalgomembersout)
* [amendment/[AM_NUMBER]/[ALGO]/voters/in](#amendmentam_numberalgovotersin)
* [amendment/[AM_NUMBER]/[ALGO]/voters/out](#amendmentam_numberalgovotersout)
* [amendment/[AM_NUMBER]/[ALGO]/self](#amendmentam_numberalgoself)
* [amendment/[AM_NUMBER]/[ALGO]/flow](#amendmentam_numberalgoflow)
* [amendment/[AM_NUMBER]/[ALGO]/statement](#amendmentam_numberalgostatement)
* [amendment/[AM_NUMBER]/[ALGO]/vote](#amendmentam_numberalgovote)
## Overview
......@@ -114,7 +114,6 @@ Data is made accessible through an HTTP API mainly inspired from [OpenUDC_exchan
| `-- owner
`-- registry/
|-- parameters
|-- flow
|-- community/
| |-- members/
| | `-- [PGP_FINGERPRINT]/
......@@ -125,6 +124,7 @@ Data is made accessible through an HTTP API mainly inspired from [OpenUDC_exchan
| |-- history
| `-- current
`-- amendment/
|-- statement
`-- [AM_NUMBER]/
`-- [ALGO]
|-- members
......@@ -134,7 +134,7 @@ Data is made accessible through an HTTP API mainly inspired from [OpenUDC_exchan
| |-- in
| `-- out
|-- self
|-- flow
|-- statement
`-- vote
## Merkle URLs
......@@ -1441,45 +1441,6 @@ UDPercent | Universal Dividend % of monetary mass growth
CoinAlgo | Algorithm used for generating coins (this also gives interpretation of coins' value in each amendment)
Consensus | Percent of voters required to valid an Amendment
#### `flow`
**Goal**
POST a [Community flow](https://github.com/c-geek/ucoin/blob/master/doc/Registry.md#community_flow) document, in order to notify of a node's computed community changes.
**Parameters**
Name | Value | Method
---- | ----- | ------
`document` | The raw community flow document. | POST
`signature` | The signature of the `document` parameter. | POST
**Returns**
Posted document, json format.
```json
{
"communityflow": {
"raw": "Version: 1\r\nCurrency: beta_brousouf\r\nAmendment: 1-58C3E52B78D1D545B4BA41AEB048BB2B7E3CE0C7\r\nIssuer: C73882B64B7E72237A2F460CE9CAB76D19A8651E\r\nDate: 1401894350\r\nAlgorithm: AnyKey\r\n",
"version": 1,
"amendmentNumber": 1,
"membersJoiningCount": 0,
"membersLeavingCount": 0,
"votersJoiningCount": 0,
"votersLeavingCount": 0,
"currency": "beta_brousouf",
"amendmentHash": "58C3E52B78D1D545B4BA41AEB048BB2B7E3CE0C7",
"algorithm": "AnyKey",
"membersJoiningRoot": "",
"membersLeavingRoot": "",
"votersJoiningRoot": "",
"votersLeavingRoot": "",
"issuer": "C73882B64B7E72237A2F460CE9CAB76D19A8651E",
"date": 1401894350
}
}
```
#### `community/members (POST)`
**Goal**
......@@ -1698,6 +1659,45 @@ A list of posted voting requests + posted signatures.
}
```
#### `amendment/statement`
**Goal**
POST a [Statement](https://github.com/c-geek/ucoin/blob/master/doc/Registry.md#statement) document, in order to notify of a node's computed community changes.
**Parameters**
Name | Value | Method
---- | ----- | ------
`document` | The raw statement document. | POST
`signature` | The signature of the `document` parameter. | POST
**Returns**
Posted document, json format.
```json
{
"statement": {
"raw": "Version: 1\r\nCurrency: beta_brousouf\r\nAmendment: 1-58C3E52B78D1D545B4BA41AEB048BB2B7E3CE0C7\r\nIssuer: C73882B64B7E72237A2F460CE9CAB76D19A8651E\r\nDate: 1401894350\r\nAlgorithm: AnyKey\r\n",
"version": 1,
"amendmentNumber": 1,
"membersJoiningCount": 0,
"membersLeavingCount": 0,
"votersJoiningCount": 0,
"votersLeavingCount": 0,
"currency": "beta_brousouf",
"amendmentHash": "58C3E52B78D1D545B4BA41AEB048BB2B7E3CE0C7",
"algorithm": "AnyKey",
"membersJoiningRoot": "",
"membersLeavingRoot": "",
"votersJoiningRoot": "",
"votersLeavingRoot": "",
"issuer": "C73882B64B7E72237A2F460CE9CAB76D19A8651E",
"date": 1401894350
}
}
```
#### `amendment/[AM_NUMBER]/[ALGO]/members/in`
**Goal**
......@@ -1870,11 +1870,11 @@ Merkle URL leaf: voting or empty string.
```
> Note: as a member may leave community for other reasons than having asked to, "value" may not contain an object but just an empty string.
#### `amendment/[AM_NUMBER]/[ALGO]/flow`
#### `amendment/[AM_NUMBER]/[ALGO]/statement`
**Goal**
GET the [Community Flow](./Registry.md#community-flow) document associated to an amendment for a given membership algorithm.
GET the [Statement](./Registry.md#statement) document associated to an amendment for a given membership algorithm.
**Parameters**
......@@ -1885,10 +1885,10 @@ Name | Value
**Returns**
Current node's community flow, or HTTP 404 if not available yet.
Current node's statement, or HTTP 404 if not available yet.
```json
{
"communityflow": {
"statement": {
"raw": "Version: 1\r\nCurrency: beta_brousouf\r\nAmendment: 1-58C3E52B78D1D545B4BA41AEB048BB2B7E3CE0C7\r\nIssuer: C73882B64B7E72237A2F460CE9CAB76D19A8651E\r\nDate: 1401894350\r\nAlgorithm: AnyKey\r\n",
"version": 1,
"amendmentNumber": 1,
......
......@@ -6,12 +6,12 @@
* [Messages](#messages)
* [Membership](#membership)
* [Voting](#voting)
* [Community flow](#community_flow)
* [Statement](#statement)
* [Algorithms](#algorithms)
* [AnyKey](#anyey)
* [1Sig](#1sig)
* [Common rules](#common_rules)
* [Computing community flows](#computing_community_flows)
* [Computing statements](#computing_statements)
## Introduction
......@@ -94,11 +94,11 @@ A [Voting](#voting) is to be considered valid if:
* `Issuer` matches signature's key ID
* `Registry` matches `VOTING` value
### Community flow
### Statement
Message whose role is to sum up, for a given node, the variations of members & voters to impact for next amendment. Such variations are resulting from [Membership](#membership), [Voting](#voting) and [Public Key](./HDC.md#public-key) documents received for next amendment.
Here is an example of Community Flow:
Here is an example of Statement:
```bash
Version: 1
......@@ -174,7 +174,7 @@ Both algorithms **only accepts** keys with an [OpenUDC udid2](https://github.com
For both algorithms, **only members may be voters**.
### Computing community flows
### Computing statements
Here are 2 tables on how to compute analytically members & voters changes:
......@@ -239,7 +239,7 @@ Using this formula, it can be known at any moment how to apply variation of the
#### Impacts on members & voters changes
The above formulas are utilities allowing us, for each key, the changes to apply to [Merkle summary](#merkle-summary) fields of [CommunityFlow](#community-flow) document.
The above formulas are utilities allowing us, for each key, the changes to apply to [Merkle summary](#merkle-summary) fields of [Statement](#statement) document.
Below are the rules on how to interprete `Mvar` and `Vvar` for such fields:
......@@ -249,13 +249,13 @@ Value | Changes
-1 | Key is leaving
1 | Key is joining
For example, if `Mvar = 1` for key `2E69197FAB029D8669EF85E82457A1587CA0ED9C` is joining, this will lead this key to be present under `MembersJoining` of the community flow. If `Mvar = -1`, the key will be present under `MembersLeaving`. If `Mvar = 0` however, nothing happens.
For example, if `Mvar = 1` for key `2E69197FAB029D8669EF85E82457A1587CA0ED9C` is joining, this will lead this key to be present under `MembersJoining` of the statement. If `Mvar = -1`, the key will be present under `MembersLeaving`. If `Mvar = 0` however, nothing happens.
### Community flow impacts & voting
### Statement impacts & voting
Community flows may be seen as a "pre-vote" process for nodes to agree on changes about members & voters, which are data that may vary a lot depending on how the data was correctly submitted (or not) to all the nodes.
Statements may be seen as a "pre-vote" process for nodes to agree on changes about members & voters, which are data that may vary a lot depending on how the data was correctly submitted (or not) to all the nodes.
Here is how a node should process while receiving community flows:
Here is how a node should process while receiving statements:
###### 1. Compare Merkle summary fields
......@@ -271,7 +271,7 @@ For each leave behind each Merkle summary, increment by `1` the value of how man