diff --git a/app/controllers/network.js b/app/controllers/network.js index 7e1970ae3f0cd5eb48e0c3519659864503bcab8c..6cc2d837c136ba9cdd0b82f2cd8083c7afcf3f73 100644 --- a/app/controllers/network.js +++ b/app/controllers/network.js @@ -13,6 +13,7 @@ var link2pubkey = require('../lib/streams/link2pubkey'); var extractSignature = require('../lib/streams/extractSignature'); var verifySignature = require('../lib/streams/verifySignature'); var logger = require('../lib/logger'); +var constants = require('../lib/constants'); var plogger = logger('peering'); var slogger = logger('status'); @@ -87,36 +88,45 @@ function NetworkBinding (peerServer, conf) { var onError = http400(res); async.waterfall([ function (next) { + function errorPeer (err) { + if (err == constants.ERROR.PEER.ALREADY_RECORDED) + next(); + else + next(err); + } // If peer is provided, parse it first if (req.body && req.body.peer) { - http2raw.peer(req, onError) + http2raw.peer(req, errorPeer) .pipe(dos2unix()) - .pipe(parsers.parsePeer(onError)) - .pipe(versionFilter(onError)) - .pipe(currencyFilter(conf.currency, onError)) - // .pipe(verifySignature(onError)) - .pipe(peerServer.singleWriteStream(onError)) - .pipe(es.mapSync(function () { + .pipe(parsers.parsePeer(errorPeer)) + .pipe(versionFilter(errorPeer)) + .pipe(currencyFilter(conf.currency, errorPeer)) + // .pipe(verifySignature(errorPeer)) + .pipe(peerServer.singleWriteStream(errorPeer)) + .pipe(es.mapSync(function (data) { next(); })) } else next(); }, function (next) { - http2raw.status(req, onError) + http2raw.status(req, next) .pipe(dos2unix()) - .pipe(parsers.parseStatus(onError)) - .pipe(versionFilter(onError)) - .pipe(currencyFilter(conf.currency, onError)) - // .pipe(extractSignature(onError)) - // .pipe(link2pubkey(peerServer.PublicKeyService, onError)) - // .pipe(verifySignature(onError)) - .pipe(peerServer.singleWriteStream(onError)) + .pipe(parsers.parseStatus(next)) + .pipe(versionFilter(next)) + .pipe(currencyFilter(conf.currency, next)) + // .pipe(extractSignature(next)) + // .pipe(link2pubkey(peerServer.PublicKeyService, next)) + // .pipe(verifySignature(next)) + .pipe(peerServer.singleWriteStream(next)) .pipe(jsoner()) .pipe(es.stringify()) .pipe(res); next(); } - ]); + ], function (err) { + if (err) + onError(err); + }); } } diff --git a/app/lib/constants.js b/app/lib/constants.js index 45e4354db78d203a265a5feeb20dbbc570bf64e5..71d4565adbdaa98e841470e49633d4cb2979446d 100644 --- a/app/lib/constants.js +++ b/app/lib/constants.js @@ -20,6 +20,9 @@ module.exports = { PUBKEY: { ALREADY_UPDATED: 1 + }, + PEER: { + ALREADY_RECORDED: 'Peer document is older than currently recorded' } }, @@ -69,6 +72,14 @@ module.exports = { TARGET: exact(PUBKEY + ":" + POSITIVE_INT), COMMENT: find("Comment: (" + COMMENT + ")"), }, + PEER: { + BLOCK: find("Block: (" + INTEGER + "-" + FINGERPRINT + ")"), + SPECIAL_BLOCK: '0-DA39A3EE5E6B4B0D3255BFEF95601890AFD80709' + }, + STATUS: { + BLOCK: find("Block: (" + INTEGER + "-" + FINGERPRINT + ")"), + SPECIAL_BLOCK: '0-DA39A3EE5E6B4B0D3255BFEF95601890AFD80709' + }, setUDID2Format: function () { module.exports.USER_ID = module.exports.UDID2_FORMAT; diff --git a/app/lib/rawer.js b/app/lib/rawer.js index 4b6e78fdb49708edfa8084a4cb9c5645a3e0d365..41679dfe26af1a0f5d6c9baa4f619a9fc91724e4 100644 --- a/app/lib/rawer.js +++ b/app/lib/rawer.js @@ -33,6 +33,7 @@ module.exports = new function() { raw += "Type: Peer\n"; raw += "Currency: " + json.currency + "\n"; raw += "PublicKey: " + json.pub + "\n"; + raw += "Block: " + json.block + "\n"; raw += "Endpoints:" + "\n"; json.endpoints.forEach(function(ep){ raw += ep + "\n"; @@ -50,6 +51,7 @@ module.exports = new function() { raw += "Type: Status\n"; raw += "Currency: " + json.currency + "\n"; raw += "Status: " + json.status + "\n"; + raw += "Block: " + json.block + "\n"; raw += "From: " + json.from + "\n"; raw += "To: " + json.to + "\n"; return dos2unix(raw); diff --git a/app/lib/streams/parsers/doc/peer.js b/app/lib/streams/parsers/doc/peer.js index f0d64e7fe89ac6ec2ec7575e61befe8c32c8ee3a..9d56a0c1c7838167457d6429d5bdabb86cf1815f 100644 --- a/app/lib/streams/parsers/doc/peer.js +++ b/app/lib/streams/parsers/doc/peer.js @@ -17,7 +17,7 @@ function PeerParser (onError) { {prop: "version", regexp: /Version: (.*)/}, {prop: "currency", regexp: /Currency: (.*)/}, {prop: "pub", regexp: /PublicKey: (.*)/}, - {prop: "time", regexp: /Time: (.*)/}, + {prop: "block", regexp: constants.PEER.BLOCK}, {prop: "endpoints", regexp: /Endpoints:\n([\s\S]*)/, parser: split("\n")}, ]; var multilineFields = []; @@ -59,6 +59,7 @@ function PeerParser (onError) { 'BAD_IPV6': 154, 'BAD_PORT': 155, 'BAD_FINGERPRINT': 156, + 'BAD_BLOCK': 157, 'NO_IP_GIVEN': 158 } if(!err){ @@ -71,6 +72,11 @@ function PeerParser (onError) { if(!obj.pub || !obj.pub.match(constants.BASE58)) err = {code: codes['BAD_FINGERPRINT'], message: "Incorrect PublicKey field"}; } + if(!err){ + // Block + if(!obj.block) + err = {code: codes['BAD_BLOCK'], message: "Incorrect Block field"}; + } // Basic Merkled API requirements var bma = obj.getBMA(); if(!err){ diff --git a/app/lib/streams/parsers/doc/status.js b/app/lib/streams/parsers/doc/status.js index c5f7f3b1dbad18466dff615a5fe4d2f0b79257c3..9c097aaf57d11833bc32c77e0d31886bd12caa5d 100644 --- a/app/lib/streams/parsers/doc/status.js +++ b/app/lib/streams/parsers/doc/status.js @@ -15,7 +15,7 @@ function StatusParser (onError) { {prop: "version", regexp: /Version: (.*)/}, {prop: "currency", regexp: /Currency: (.*)/}, {prop: "status", regexp: /Status: (.*)/}, - {prop: "time", regexp: /Time: (.*)/, parser: parseDateFromTimestamp}, + {prop: "block", regexp: constants.STATUS.BLOCK}, {prop: "from", regexp: /From: (.*)/}, {prop: "to", regexp: /To: (.*)/}, ]; @@ -59,8 +59,4 @@ function StatusParser (onError) { }; } -function parseDateFromTimestamp (value) { - return new Date(parseInt(value)*1000); -} - util.inherits(StatusParser, GenericParser); diff --git a/app/models/peer.js b/app/models/peer.js index 32c37dda00b8a56dce3755e62237a77012311b50..e9dcf04c7f7f2468916d0e49311332773f868f37 100644 --- a/app/models/peer.js +++ b/app/models/peer.js @@ -23,7 +23,10 @@ var PeerSchema = new Schema({ endpoints: [String], signature: String, hash: String, - status: { type: String, default: STATUS.NOTHING }, + hash: String, + block: { type: String }, + statusBlock: { type: String }, + status: { type: String }, statusSent: { type: String, default: STATUS.NOTHING }, statusSigDate: { type: Date, default: function(){ return new Date(0); } }, propagated: { type: Boolean, default: false }, @@ -64,14 +67,14 @@ PeerSchema.methods = { copyValues: function(to) { var obj = this; - ["version", "currency", "pub", "endpoints", "hash", "status", "signature"].forEach(function (key) { + ["version", "currency", "pub", "endpoints", "hash", "status", "block", "signature"].forEach(function (key) { to[key] = obj[key]; }); }, copyValuesFrom: function(from) { var obj = this; - ["version", "currency", "pub", "endpoints", "signature"].forEach(function (key) { + ["version", "currency", "pub", "endpoints", "block", "signature"].forEach(function (key) { obj[key] = from[key]; }); }, @@ -79,7 +82,7 @@ PeerSchema.methods = { json: function() { var obj = this; var json = {}; - ["version", "currency", "endpoints", "status", "signature"].forEach(function (key) { + ["version", "currency", "endpoints", "status", "block", "signature"].forEach(function (key) { json[key] = obj[key]; }); json.raw = this.getRaw(); diff --git a/app/models/statusMessage.js b/app/models/statusMessage.js index 9c11cc2570c12868b7d60305370e1d4812b5c069..b764531c5418806ef414fb57d46214c853381c98 100644 --- a/app/models/statusMessage.js +++ b/app/models/statusMessage.js @@ -6,13 +6,13 @@ var _ = require('underscore'); module.exports = function StatusMessage (values) { var that = this; - ['version', 'currency', 'status', 'from', 'to', 'hash', 'pubkey', 'time', 'signature'].forEach(function(field){ + ['version', 'currency', 'status', 'from', 'to', 'hash', 'pubkey', 'block', 'signature'].forEach(function(field){ that[field] = values && values[field] || ''; }); this.json = function () { var obj = {}; - ['version', 'currency', 'status', 'from', 'to', 'signature'].forEach(function(field){ + ['version', 'currency', 'status', 'from', 'to', 'block', 'signature'].forEach(function(field){ obj[field] = that[field] || ''; }); obj.raw = this.getRaw(); diff --git a/app/service/PeeringService.js b/app/service/PeeringService.js index 298940eafbe9cb3c05061d07ba66fb8774ab9011..0bf5447b82077f58d6ebbcc0f2480547b8aa7c81 100644 --- a/app/service/PeeringService.js +++ b/app/service/PeeringService.js @@ -7,12 +7,14 @@ var Status = require('../models/statusMessage'); var logger = require('../lib/logger')('peering'); var base58 = require('../lib/base58'); var moment = require('moment'); +var constants = require('../lib/constants'); var localValidator = require('../lib/localValidator'); function PeeringService(conn, conf, pair, signFunc, ParametersService) { var currency = conf.currency; + var Block = conn.model('Block'); var Transaction = conn.model('Transaction'); var Merkle = conn.model('Merkle'); var Peer = conn.model('Peer'); @@ -71,12 +73,20 @@ function PeeringService(conn, conf, pair, signFunc, ParametersService) { this.submit = function(peering, callback){ var peer = new Peer(peering); + var sp = peer.block.split('-'); + var number = sp[0], fpr = sp[1]; async.waterfall([ function (next) { localValidator(null).checkPeerSignature(peer, next); }, - function (next){ - that.addPeer(peer); + function (next) { + if (peer.block == constants.PEER.SPECIAL_BLOCK) + next(null, null); + else + // Check if document is based upon an existing block as time reference + Block.findByNumberAndHash(number, fpr, next); + }, + function (block, next){ Peer.find({ pub: peer.pub }, next); }, function (peers, next){ @@ -84,8 +94,10 @@ function PeeringService(conn, conf, pair, signFunc, ParametersService) { var previousHash = null; if(peers.length > 0){ // Already existing peer - if(peers[0].sigDate > peerEntity.sigDate){ - next('Cannot record a previous peering'); + var sp2 = peers[0].block.split('-'); + var number2 = sp2[0], fpr2 = sp2[1]; + if(number <= number2){ + next(constants.ERROR.PEER.ALREADY_RECORDED); return; } peerEntity = peers[0]; @@ -108,18 +120,31 @@ function PeeringService(conn, conf, pair, signFunc, ParametersService) { var status = new Status(obj); var peer; var wasStatus = null; + var sp = status.block.split('-'); + var number = sp[0], fpr = sp[1]; async.waterfall([ function (next) { localValidator(null).checkStatusSignature(status, next); }, - function (next){ + function (next) { + if (status.block == constants.STATUS.SPECIAL_BLOCK) + next(null, null); + else + // Check if document is based upon an existing block as time reference + Block.findByNumberAndHash(number, fpr, next); + }, + function (block, next){ Peer.getTheOne(status.from, next); }, function (theOne, next){ peer = theOne; - if (peer.statusSigDate > status.sigDate) { - next('Old status given'); - return; + if (peer.statusBlock) { + var sp2 = peer.statusBlock.split('-'); + var number2 = sp2[0], fpr2 = sp2[1]; + if(number <= number2){ + next('Old status given'); + return; + } } wasStatus = peer.status; peer.statusSigDate = status.sigDate; @@ -220,7 +245,14 @@ function PeeringService(conn, conf, pair, signFunc, ParametersService) { * @param pubs List of peers' pubs to which status is to be sent */ this.sendStatusTo = function (statusStr, pubs, done) { + var current = null; async.waterfall([ + function (next) { + Block.current(function (err, block) { + current = block; + next(); + }); + }, async.apply(Peer.getList.bind(Peer), pubs), function (peers, next) { async.forEach(peers, function(peer, callback){ @@ -229,6 +261,7 @@ function PeeringService(conn, conf, pair, signFunc, ParametersService) { currency: currency, time: new Date(moment.utc().unix()*1000), status: statusStr, + block: current ? [current.number, current.hash].join('-') : '0-DA39A3EE5E6B4B0D3255BFEF95601890AFD80709', from: selfPubkey, to: peer.pub }); diff --git a/peerserver.js b/peerserver.js index 14c6b3f713999736e8286c9888b8ceda46d990ec..a224f77ff86df707b2ac44abe51c79f47ed5ccc2 100644 --- a/peerserver.js +++ b/peerserver.js @@ -9,6 +9,7 @@ var WOTServer = require('./wotserver'); var signature = require('./app/lib/signature'); var parsers = require('./app/lib/streams/parsers/doc'); var multicaster = require('./app/lib/streams/multicaster'); +var constants = require('./app/lib/constants'); function PeerServer (dbConf, overrideConf, interceptors, onInit) { @@ -244,8 +245,13 @@ function PeerServer (dbConf, overrideConf, interceptors, onInit) { this.initPeeringEntry = function (conn, conf, done) { var Peer = conn.model('Peer'); var currency = conf.currency; + var current = null; async.waterfall([ function (next) { + that.BlockchainService.current(next); + }, + function (currentBlock, next) { + current = currentBlock; Peer.find({ pub: that.PeeringService.pubkey }, next); }, function (peers, next) { @@ -270,6 +276,7 @@ function PeerServer (dbConf, overrideConf, interceptors, onInit) { version: 1, currency: currency, pub: that.PeeringService.pubkey, + block: current ? [current.number, current.hash].join('-') : constants.PEER.SPECIAL_BLOCK, endpoints: [endpoint] }; var raw1 = p1.getRaw().dos2unix(); diff --git a/test/fast/peering.js b/test/fast/peering.js index 955dba384da34317de1a572751c9d32dad16e460..16c63b6f5e6c653e8d70ebf6a5b156aa0b689a87 100644 --- a/test/fast/peering.js +++ b/test/fast/peering.js @@ -12,6 +12,7 @@ var rawPeer = "" + "Type: Peer\n" + "Currency: beta_brousouf\n" + "PublicKey: 3Z7w5g4gC9oxwEbATnmK2UFgGWhLZPmZQb5dRxvNrXDu\n" + + "Block: 0-DA39A3EE5E6B4B0D3255BFEF95601890AFD80709\n" + "Endpoints:\n" + "BASIC_MERKLED_API ucoin.twiced.fr 88.163.127.43 9101\n" + "OTHER_PROTOCOL 88.163.127.43 9102\n" + diff --git a/test/fast/status.js b/test/fast/status.js index 42285201772b4a04511222583863e12f2f98cb65..d46d981c3f0fc850691acaf5233890110b8c5f0a 100644 --- a/test/fast/status.js +++ b/test/fast/status.js @@ -12,7 +12,7 @@ var rawStatus = "" + "Type: Status\n" + "Currency: beta_brousouf\n" + "Status: UP\n" + - "Time: 1414937786\n" + + "Block: 0-DA39A3EE5E6B4B0D3255BFEF95601890AFD80709\n" + "From: 3Z7w5g4gC9oxwEbATnmK2UFgGWhLZPmZQb5dRxvNrXDu\n" + "To: 6GCnm36t4DnvoJshCYS13i64PxsqyJnGxuNXkzt9Rkh7\n" + "bvuKzc6+cGWMGC8FIkZHN8kdQhaRL/MK60KYyw5vJqkKEgxXbygQHAzfoojeSY4gPKIu4FggBkR1HndSEm2FAQ==\n";