From cec890739b26e00c57f977212d35e50d8899742f Mon Sep 17 00:00:00 2001
From: cgeek <cem.moreau@gmail.com>
Date: Tue, 26 Aug 2014 21:21:06 +0200
Subject: [PATCH] Now allow to sync with an existing keychain

---
 app/lib/keyhelper.js           |  11 ++
 app/lib/sync.js                | 264 +++++++++++++++++----------------
 app/models/keyblock.js         |   2 +-
 app/service/KeychainService.js |   7 +-
 bin/ucoind                     |  10 +-
 5 files changed, 164 insertions(+), 130 deletions(-)

diff --git a/app/lib/keyhelper.js b/app/lib/keyhelper.js
index 9b908e2ef..e418fe8ed 100644
--- a/app/lib/keyhelper.js
+++ b/app/lib/keyhelper.js
@@ -260,6 +260,17 @@ function KeyHelper (packetList) {
     return this.getHashedPackets(subkeys);
   };
 
+  this.hasSubkey = function (keyID){
+    var matched = false;
+    (key.subKeys || []).forEach(function(subkeyWrapper){
+      if (subkeyWrapper.isValidSigningKey(key.primaryKey) || subkeyWrapper.isValidEncryptionKey(key.primaryKey)) {
+        if (subkeyWrapper.subKey.getFingerprint().toUpperCase().match(new RegExp(keyID + '$')))
+          matched = true;
+      }
+    });
+    return matched;
+  };
+
   this.getHashedCertifPackets = function (){
     var certifs = this.getBase64primaryUserOtherCertifications(); // Array of 1 packet lists (signature)
     return this.getHashedPackets(certifs);
diff --git a/app/lib/sync.js b/app/lib/sync.js
index 5c9c8d754..41ef516b4 100644
--- a/app/lib/sync.js
+++ b/app/lib/sync.js
@@ -16,20 +16,20 @@ module.exports = function Synchroniser (server, host, port, authenticated, conf)
   var that = this;
 
   // Services
-            var PublicKeyService         = server.PublicKeyService;
-            var KeyService         = server.KeyService;
-            var TransactionService = server.TransactionsService;
-            var WalletService      = server.WalletService;
-            var PeeringService     = server.PeeringService;
-            var ParametersService  = server.ParametersService;
+  var PublicKeyService   = server.PublicKeyService;
+  var KeyService         = server.KeyService;
+  var TransactionService = server.TransactionsService;
+  var WalletService      = server.WalletService;
+  var PeeringService     = server.PeeringService;
+  var ParametersService  = server.ParametersService;
+  var KeychainService    = server.KeychainService;
 
   // Models
-  var Amendment     = server.conn.model('Amendment');
   var PublicKey     = server.conn.model('PublicKey');
+  var KeyBlock      = server.conn.model('KeyBlock');
   var Merkle        = server.conn.model('Merkle');
   var Key           = server.conn.model('Key');
   var Membership    = server.conn.model('Membership');
-  var Voting        = server.conn.model('Voting');
   var Transaction   = server.conn.model('Transaction');
   var Wallet        = server.conn.model('Wallet');
   var Peer          = server.conn.model('Peer');
@@ -122,136 +122,152 @@ module.exports = function Synchroniser (server, host, port, authenticated, conf)
         //============
         // Parameters
         //============
-        function (next){
-          node.registry.parameters(next);
-        },
-        function (params, next){
-          async.waterfall([
-            function (next){
-              Configuration.find({}, next);
-            },
-            function (confs, next){
-              var config = confs[0] || new Configuration();
-              var sync = _({}).extend(config.sync);
-              sync.AMStart   = params.AMStart;
-              sync.AMFreq    = params.AMFrequency;
-              sync.UDFreq    = params.UDFrequency;
-              sync.UD0       = params.UD0;
-              sync.UDPercent = params.UDPercent;
-              sync.Consensus = params.Consensus;
-              config.sync = sync;
-              config.save(function (err) {
-                next(err);
-              });
-            },
-          ], next);
-        },
+        // function (next){
+        //   node.registry.parameters(next);
+        // },
+        // function (params, next){
+        //   async.waterfall([
+        //     function (next){
+        //       Configuration.find({}, next);
+        //     },
+        //     function (confs, next){
+        //       var config = confs[0] || new Configuration();
+        //       var sync = _({}).extend(config.sync);
+        //       sync.AMStart   = params.AMStart;
+        //       sync.AMFreq    = params.AMFrequency;
+        //       sync.UDFreq    = params.UDFrequency;
+        //       sync.UD0       = params.UD0;
+        //       sync.UDPercent = params.UDPercent;
+        //       sync.Consensus = params.Consensus;
+        //       config.sync = sync;
+        //       config.save(function (err) {
+        //         next(err);
+        //       });
+        //     },
+        //   ], next);
+        // },
 
         //============
         // Public Keys
         //============
-        function (next){
-          Merkle.forPublicKeys(next);
-        },
-        function (merkle, next) {
-          node.pks.all({}, function (err, json) {
-            var rm = new NodesMerkle(json);
-            if(rm.root() != merkle.root()){
-              var leavesToAdd = [];
-              // Call with nice no to have PGP error 'gpg: input line longer than 19995 characters'
-              node.pks.all({ leaves: true, nice: true }, function (err, json) {
-                _(json.leaves).forEach(function(leaf){
-                  if(merkle.leaves().indexOf(leaf) == -1){
-                    leavesToAdd.push(leaf);
-                  }
-                });
-                var hashes = [];
-                async.forEachSeries(leavesToAdd, function(leaf, callback){
-                  logger.info('Importing public key %s...', leaf);
-                  async.waterfall([
-                    function (cb){
-                      node.pks.all({ "leaf": leaf}, cb);
-                    },
-                    function (json, cb){
-                      hashes.push(leaf);
-                      PublicKey.persistFromRaw(json.leaf.value.pubkey, function (err) {
-                        cb();
-                      });
-                    },
-                    function (next) {
-                      KeyService.handleKey(leaf, conf && conf.kmanagement == 'ALL', next);
-                    },
-                  ], callback);
-                }, function(err, result){
-                  next(err);
-                });
-              });
-            }
-            else next();
-          });
-        },
+        // function (next){
+        //   Merkle.forPublicKeys(next);
+        // },
+        // function (merkle, next) {
+        //   node.pks.all({}, function (err, json) {
+        //     var rm = new NodesMerkle(json);
+        //     if(rm.root() != merkle.root()){
+        //       var leavesToAdd = [];
+        //       // Call with nice no to have PGP error 'gpg: input line longer than 19995 characters'
+        //       node.pks.all({ leaves: true, nice: true }, function (err, json) {
+        //         _(json.leaves).forEach(function(leaf){
+        //           if(merkle.leaves().indexOf(leaf) == -1){
+        //             leavesToAdd.push(leaf);
+        //           }
+        //         });
+        //         var hashes = [];
+        //         async.forEachSeries(leavesToAdd, function(leaf, callback){
+        //           logger.info('Importing public key %s...', leaf);
+        //           async.waterfall([
+        //             function (cb){
+        //               node.pks.all({ "leaf": leaf}, cb);
+        //             },
+        //             function (json, cb){
+        //               hashes.push(leaf);
+        //               PublicKey.persistFromRaw(json.leaf.value.pubkey, function (err) {
+        //                 cb();
+        //               });
+        //             },
+        //             function (next) {
+        //               KeyService.handleKey(leaf, conf && conf.kmanagement == 'ALL', next);
+        //             },
+        //           ], callback);
+        //         }, function(err, result){
+        //           next(err);
+        //         });
+        //       });
+        //     }
+        //     else next();
+        //   });
+        // },
 
         //============
-        // Amendments
+        // Keychain
         //============
         function (next){
-          Amendment.nextNumber(next);
+          node.keychain.current(next);
         },
-        function (number, next) {
-          node.hdc.amendments.current(function (err, json) {
-            if(err){
-              logger.warn('Issue getting current:');
-              err.split('\n').forEach(function (msg) {
-                logger.warn(msg);
-              });
-              next();
-              return;
-            }
-            remoteCurrentNumber = parseInt(json.number);
-            amendments[remoteCurrentNumber] = json.raw;
-            var toGetNumbers = _.range(number, remoteCurrentNumber + 1);
-            async.forEachSeries(toGetNumbers, function(amNumber, callback){
-              async.waterfall([
-                function (cb){
-                  if(!amendments[amNumber])
-                    node.hdc.amendments.promoted(amNumber, cb);
-                  else
-                    cb(null, { raw: amendments[amNumber] });
-                },
-                function (am, cb){
-                  amendments[amNumber] = am.raw;
-                  node.hdc.amendments.promoted(amNumber, cb);
-                },
-                function (am, cb){
-                  amendments[amNumber] = am.raw;
-                  cb();
-                },
-                function (cb) {
-                  node.hdc.amendments.view.signatures(amNumber, sha1(amendments[amNumber]).toUpperCase(), { leaves: true }, cb);
-                },
-                function (json, cb){
-                  applyVotes(amendments, amNumber, number, json, node, cb);
-                }
-              ], function (err, result) {
-                callback(err);
-              });
-            }, function(err, result){
-              next(err);
-            });
-          });
+        function (current, next) {
+          KeychainService.checkWithLocalTimestamp = false;
+          var numbers = _.range(current.number + 1);
+          async.forEachSeries(numbers, function(number, callback){
+            async.waterfall([
+              function (next){
+                node.keychain.keyblock(number, next);
+              },
+              function (keyblock, next){
+                var block = new KeyBlock(keyblock);
+                console.log('keyblock#' + block.number, sha1(block.getRawSigned()));
+                var keyID = jpgp().signature(block.signature).issuer();
+                var newPubkeys = block.getNewPubkeys();
+                // Save pubkeys + block
+                async.waterfall([
+                  function (next){
+                    if (block.number == 0) {
+                      var signatory = null;
+                      newPubkeys.forEach(function(key){
+                        if (key.hasSubkey(keyID))
+                          signatory = key;
+                      });
+                      if (!signatory) {
+                        next('Root block signatory not found');
+                        return;
+                      }
+                      next(null, { fingerprint: signatory.getFingerprint(), raw: signatory.getArmored() });
+                    } else {
+                      PublicKey.getTheOne(keyID, next);
+                    }
+                  },
+                  function (pubkey, next){
+                    keyblock.pubkey = pubkey;
+                    jpgp()
+                      .publicKey(pubkey.raw)
+                      .data(block.getRaw())
+                      .signature(block.signature)
+                      .verify(next);
+                  },
+                  function (verified, next){
+                    async.forEach(newPubkeys, function(newPubkey, callback){
+                      async.waterfall([
+                        function (next){
+                          parsers.parsePubkey(callback).asyncWrite(unix2dos(newPubkey.getArmored()), next);
+                        },
+                        function (obj, next){
+                          PublicKeyService.submitPubkey(obj, next);
+                        },
+                      ], callback);
+                    }, next);
+                  },
+                  function (next){
+                    KeychainService.submitKeyBlock(keyblock, next);
+                  },
+                ], next);
+              },
+            ], callback);
+          }, next)
         },
 
         //==============
         // Transactions
         //==============
-        function (next){
-          Key.find({ managed: true }, next);
-        },
-        function (keys, next) {
-          async.forEachSeries(keys, function (key, onKeyDone) {
-            syncTransactionsOfKey(node, key.fingerprint, onKeyDone);
-          }, next);
-        },
+        // function (next){
+        //   Key.find({ managed: true }, next);
+        // },
+        // function (keys, next) {
+        //   async.forEachSeries(keys, function (key, onKeyDone) {
+        //     syncTransactionsOfKey(node, key.fingerprint, onKeyDone);
+        //   }, next);
+        // },
 
         //=========
         // Wallets
diff --git a/app/models/keyblock.js b/app/models/keyblock.js
index 3205be1db..a5ef62fda 100644
--- a/app/models/keyblock.js
+++ b/app/models/keyblock.js
@@ -64,7 +64,7 @@ KeyBlockSchema.methods = {
     [
       "membersChanges",
     ].forEach(function(field){
-      json[field] = json[field] || [];
+      json[field] = that[field] || [];
     });
     [
       "keysChanges",
diff --git a/app/service/KeychainService.js b/app/service/KeychainService.js
index 9408b71fe..77eddf039 100644
--- a/app/service/KeychainService.js
+++ b/app/service/KeychainService.js
@@ -37,6 +37,10 @@ function KeyService (conn, conf, PublicKeyService, PeeringService) {
   var Link       = conn.model('Link');
   var Key        = conn.model('Key');
 
+  // Flag to say wether timestamp of received keyblocks should be tested
+  // Useful for synchronisation of old blocks
+  this.checkWithLocalTimestamp = true;
+
   this.load = function (done) {
     done();
   };
@@ -141,7 +145,7 @@ function KeyService (conn, conf, PublicKeyService, PeeringService) {
           return;
         }
         // Test timestamp
-        if (Math.abs(block.timestamp - now.utcZero().timestamp()) > conf.tsInterval) {
+        if (KeychainService.checkWithLocalTimestamp && Math.abs(block.timestamp - now.utcZero().timestamp()) > conf.tsInterval) {
           next('Timestamp does not match this node\'s time');
           return;
         }
@@ -1472,6 +1476,7 @@ function KeyService (conn, conf, PublicKeyService, PeeringService) {
   };
 
   this.startGeneration = function (done) {
+    if (!conf.participate) return;
     if (!PeeringService) {
       done('Needed peering service activated.');
       return;
diff --git a/bin/ucoind b/bin/ucoind
index 84dc818b2..1aabb84ec 100755
--- a/bin/ucoind
+++ b/bin/ucoind
@@ -141,13 +141,15 @@ program
 program
   .command('sync [host] [port]')
   .description('Tries to synchronise data with remote uCoin node')
-  .action(service(DO_NOT_LISTEN_HTTP, ucoin.createRegistryServer, function (host, port, server, conf) {
+  .action(service(DO_NOT_LISTEN_HTTP, ucoin.createPeerServer, function (host, port, server, conf) {
 
-    // Disable daemon
-    conf.sync.AMDaemon = "OFF";
-    conf.createNext = false;
+    // Stop keyblock generation
+    conf.participate = false;
 
     async.series([
+      function (next) {
+        server.once('peerInited', next);
+      },
       function (next){
         // Synchronize
         var Synchroniser = require('../app/lib/sync');
-- 
GitLab