From 2729dbaf06431358bec2919835f8b96aaa7ca93e Mon Sep 17 00:00:00 2001
From: cgeek <cem.moreau@gmail.com>
Date: Wed, 25 Nov 2015 19:17:47 +0100
Subject: [PATCH] Fix #229 added "revert" and "revert-to" commands

---
 app/lib/blockchainContext.js     |  6 ++---
 app/lib/dal/fileDAL.js           |  4 ----
 app/lib/dal/fileDALs/BlockDAL.js | 16 ++++++-------
 app/lib/sync.js                  |  7 +++---
 bin/ucoind                       | 41 ++++++++++++++++++++++++++++++++
 server.js                        | 11 +++++++++
 6 files changed, 66 insertions(+), 19 deletions(-)

diff --git a/app/lib/blockchainContext.js b/app/lib/blockchainContext.js
index e842ce59a..e5eb57ea4 100644
--- a/app/lib/blockchainContext.js
+++ b/app/lib/blockchainContext.js
@@ -106,7 +106,7 @@ function BlockchainContext(conf, dal) {
   this.revertBlock = (block) => co(function *() {
     let previousBlock = yield dal.getBlockByNumberAndHashOrNull(block.number - 1, block.previousHash || '');
     // Set the block as SIDE block (equivalent to removal from main branch)
-    dal.blockDAL.setSideBlock(block, previousBlock);
+    yield dal.blockDAL.setSideBlock(block, previousBlock);
     yield undoCertifications(block);
     yield undoLinks(block);
     if (previousBlock) {
@@ -308,13 +308,13 @@ function BlockchainContext(conf, dal) {
       }
       // Undo renew (only remove last membership IN document)
       for (let i = 0, len = block.actives.length; i < len; i++) {
-        let msRaw = block.joiners[i];
+        let msRaw = block.actives[i];
         let ms = Membership.statics.fromInline(msRaw, 'IN', conf.currency);
         yield dal.unRenewIdentity(ms.issuer);
       }
       // Undo leavers (forget about their last membership OUT document)
       for (let i = 0, len = block.leavers.length; i < len; i++) {
-        let msRaw = block.joiners[i];
+        let msRaw = block.leavers[i];
         let ms = Membership.statics.fromInline(msRaw, 'OUT', conf.currency);
         yield dal.unLeaveIdentity(ms.issuer);
       }
diff --git a/app/lib/dal/fileDAL.js b/app/lib/dal/fileDAL.js
index c5959b881..d5a3aa7ea 100644
--- a/app/lib/dal/fileDAL.js
+++ b/app/lib/dal/fileDAL.js
@@ -295,10 +295,6 @@ function FileDAL(profile, home, localDir, myFS, parentFileDAL, dalName, loki) {
 
   this.getBlocksBetween = (start, end) => Q(this.blockDAL.getBlocks(Math.max(0, start), end));
 
-  this.getLastSavedBlockFileNumber = function() {
-    return that.blockDAL.getLastSavedBlockFileNumber();
-  };
-
   this.getBlockCurrent = function(done) {
     return that.blockDAL.getCurrent()
       .then(function(current) {
diff --git a/app/lib/dal/fileDALs/BlockDAL.js b/app/lib/dal/fileDALs/BlockDAL.js
index d2e1e0c8a..39dfbc761 100644
--- a/app/lib/dal/fileDALs/BlockDAL.js
+++ b/app/lib/dal/fileDALs/BlockDAL.js
@@ -66,12 +66,6 @@ function BlockDAL(loki, rootFS, getLowerWindowBlock) {
   this.blocksDB = blocksDB;
   this.collection = collection;
 
-  this.getLastSavedBlockFileNumber = () => {
-    let last = collection.chain().simplesort('number', true).limit(1).data()[0];
-    if (last) return Q(last.number);
-    return Q(-1);
-  };
-
   this.getBlocks = (start, end) => {
     let lowerInLoki = collection.chain().simplesort('number').limit(1).data()[0];
     let lokiBlocks = blocksDB.branchResultset().find({
@@ -168,7 +162,7 @@ function BlockDAL(loki, rootFS, getLowerWindowBlock) {
     return Q(block);
   };
 
-  this.setSideBlock = (block, previousBlock) => {
+  this.setSideBlock = (block, previousBlock) => co(function *() {
     let existing = collection.find({
       $and: [{
         number: block.number
@@ -181,7 +175,13 @@ function BlockDAL(loki, rootFS, getLowerWindowBlock) {
       collection.update(found);
     });
     current = previousBlock;
-  };
+    let lowerInLoki = collection.chain().simplesort('number').limit(1).data()[0];
+    if (lowerInLoki.number > 0) {
+      let newLower = yield that.getBlock(lowerInLoki.number - 1);
+      yield rootFS.remove(pathOfBlock(newLower.number) + blockFileName(newLower.number) + '.json');
+      collection.insert(newLower);
+    }
+  });
 
   this.migrateOldBlocks = () => co(function *() {
     let number = yield getLowerWindowBlock();
diff --git a/app/lib/sync.js b/app/lib/sync.js
index 9cb2661d0..643c4a3b1 100644
--- a/app/lib/sync.js
+++ b/app/lib/sync.js
@@ -50,7 +50,6 @@ module.exports = function Synchroniser (server, host, port, conf, interactive) {
       var node = yield getVucoin(host, port, vucoinOptions);
       logger.info('Sync started.');
 
-      var lastSavedNumber = yield server.dal.getLastSavedBlockFileNumber();
       var lCurrent = yield dal.getCurrentBlockOrNull();
 
       //============
@@ -74,14 +73,14 @@ module.exports = function Synchroniser (server, host, port, conf, interactive) {
 
       // Prepare chunks of blocks to be downloaded
       var chunks = [];
-      for (let i = lastSavedNumber + 1; i <= remoteNumber; i = i + CONST_BLOCKS_CHUNK) {
+      for (let i = localNumber + 1; i <= remoteNumber; i = i + CONST_BLOCKS_CHUNK) {
         chunks.push([i, Math.min(i + CONST_BLOCKS_CHUNK - 1, remoteNumber)]);
       }
 
       // Prepare the array of download promises. The first is the promise of already downloaded blocks
       // which has not been applied yet.
       var toApply = [Q.defer()].concat(chunks.map(() => Q.defer()));
-      toApply[0].resolve([localNumber + 1, lastSavedNumber]);
+      toApply[0].resolve([localNumber + 1, localNumber]);
 
       // Chain download promises, and start download right now
       chunks.map((chunk, index) =>
@@ -132,7 +131,7 @@ module.exports = function Synchroniser (server, host, port, conf, interactive) {
       }
 
       // Specific treatment for nocautious
-      if (!cautious) {
+      if (!cautious && toApply.length > 1) {
         let lastChunk = yield toApplyNoCautious[toApplyNoCautious.length - 1].promise;
         let lastBlocks = lastChunk[2];
         let lastBlock = lastBlocks[lastBlocks.length - 1];
diff --git a/bin/ucoind b/bin/ucoind
index dffece861..cb11396b5 100755
--- a/bin/ucoind
+++ b/bin/ucoind
@@ -6,6 +6,7 @@ if (!~process.execArgv.indexOf('--harmony')) {
 }
 
 var async       = require('async');
+var co          = require('co');
 var Q           = require('q');
 var _           = require('underscore');
 var program     = require('commander');
@@ -232,6 +233,46 @@ program
     });
   }));
 
+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
+      server.disconnect()
+        .catch(() => null)
+        .then(function(){
+          process.exit();
+        });
+    });
+  }));
+
 program
   .command('gen-next [host] [port] [diff]')
   .description('Tries to generate the next block of the blockchain')
diff --git a/server.js b/server.js
index 22fd7dec8..d7964400e 100644
--- a/server.js
+++ b/server.js
@@ -352,6 +352,17 @@ function Server (dbConf, overrideConf) {
 
   this.revert = () => this.BlockchainService.revertCurrentBlock();
 
+  this.revertTo = (number) => co(function *() {
+    let current = yield that.BlockchainService.current();
+    for (let i = 0, count = current.number - number; i < count; i++) {
+      yield that.BlockchainService.revertCurrentBlock();
+    }
+    if (current.number <= number) {
+      logger.warn('Already reached');
+    }
+    that.BlockchainService.revertCurrentBlock();
+  });
+
   this.singleWriteStream = function (onError, onSuccess) {
     return new TempStream(that, onError, onSuccess);
   };
-- 
GitLab