communication: add SVS support

Change-Id: I74c5a091c8b1c05dd186a0c10849aa5bb4f39e50
Signed-off-by: Varun Patil <varunpatil@ucla.edu>
diff --git a/.jenkins.d/04-ndn-svs.sh b/.jenkins.d/04-ndn-svs.sh
new file mode 100755
index 0000000..5105197
--- /dev/null
+++ b/.jenkins.d/04-ndn-svs.sh
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+set -exo pipefail
+
+PROJ=ndn-svs
+
+pushd "$CACHE_DIR" >/dev/null
+
+INSTALLED_VERSION=
+NDN_CXX=$(ndnsec version)
+OLD_NDN_CXX=$(cat "$PROJ-ndn-cxx.txt" || :)
+if [[ $OLD_NDN_CXX != $NDN_CXX ]]; then
+    echo "$NDN_CXX" > "$PROJ-ndn-cxx.txt"
+    INSTALLED_VERSION=NONE
+fi
+
+if [[ -z $INSTALLED_VERSION ]]; then
+    INSTALLED_VERSION=$(git -C "$PROJ" rev-parse HEAD 2>/dev/null || echo NONE)
+fi
+
+sudo rm -rf "$PROJ-latest"
+git clone --depth 1 "https://github.com/named-data/$PROJ.git" "$PROJ-latest"
+LATEST_VERSION=$(git -C "$PROJ-latest" rev-parse HEAD 2>/dev/null || echo UNKNOWN)
+
+if [[ $INSTALLED_VERSION != $LATEST_VERSION ]]; then
+    sudo rm -rf "$PROJ"
+    mv "$PROJ-latest" "$PROJ"
+else
+    sudo rm -rf "$PROJ-latest"
+fi
+
+sudo rm -fr /usr/local/include/"$PROJ"
+sudo rm -f /usr/local/lib{,64}/lib"$PROJ"*
+sudo rm -f /usr/local/lib{,64}/pkgconfig/"$PROJ".pc
+
+pushd "$PROJ" >/dev/null
+
+./waf --color=yes configure
+./waf --color=yes build
+sudo ./waf --color=yes install
+
+popd >/dev/null
+popd >/dev/null
+
+if [[ $ID_LIKE == *fedora* ]]; then
+    sudo tee /etc/ld.so.conf.d/ndn.conf >/dev/null <<< /usr/local/lib64
+fi
+if [[ $ID_LIKE == *linux* ]]; then
+    sudo ldconfig
+fi
diff --git a/.jenkins.d/10-build.sh b/.jenkins.d/10-build.sh
index 55f50f4..ee8f4ba 100755
--- a/.jenkins.d/10-build.sh
+++ b/.jenkins.d/10-build.sh
@@ -27,7 +27,7 @@
 fi
 
 # Build in debug mode with tests
-./waf --color=yes configure --debug --with-chronosync --with-tests $ASAN $COVERAGE
+./waf --color=yes configure --debug --with-psync --with-chronosync --with-svs --with-tests $ASAN $COVERAGE
 ./waf --color=yes build
 
 # (tests will be run against the debug version)
diff --git a/COPYING.md b/COPYING.md
index dbb7b31..661a055 100644
--- a/COPYING.md
+++ b/COPYING.md
@@ -15,6 +15,9 @@
 - PSync is licensed under the terms of the
   [GNU Lesser General Public License version 3](https://github.com/named-data/PSync/blob/master/COPYING.md)
 
+- ndn-svs is licensed under the terms of the
+  [GNU Lesser General Public License v2.1](https://github.com/named-data/ndn-svs/blob/master/COPYING.md)
+
 - The waf build system is licensed under the terms of the
   [BSD license](https://github.com/named-data/NLSR/blob/master/waf)
 
diff --git a/docs/INSTALL.rst b/docs/INSTALL.rst
index 71bc478..519841b 100644
--- a/docs/INSTALL.rst
+++ b/docs/INSTALL.rst
@@ -23,6 +23,12 @@
   the ChronoSync library and build it according to the instructions available at
   https://github.com/named-data/ChronoSync#build
 
+- [Optional] SVS library
+
+  NLSR can be used with State Vector Sync as the underlying Sync protocol. Download
+  the ndn-svs library and build it according to the instructions available at
+  https://github.com/named-data/ndn-svs#installation
+
 Build
 -----
 
diff --git a/docs/beginners-guide.rst b/docs/beginners-guide.rst
index a41c6d8..b12f009 100644
--- a/docs/beginners-guide.rst
+++ b/docs/beginners-guide.rst
@@ -18,10 +18,11 @@
 The following instructions are based on the information provided at the
 Named Data Networking project web page [NDNmain]_.
 Before installing NLSR it is necessary to install different libraries and
-programs: ndn-cxx, NFD, ChronoSync [optional], and PSync. This document describes
-the necessary steps to correctly install these programs (§ `2 <#ndncxx>`__,
-`3 <#nfd>`__ and `4 <#nlsr>`__) and a brief guide on how to configure
-and test NLSR using a simple two-node network (§ `5 <#test>`__).
+programs: ndn-cxx, NFD, ChronoSync [optional], ndn-svs [optional] and PSync.
+This document describes the necessary steps to correctly install these
+programs (§ `2 <#ndncxx>`__, `3 <#nfd>`__ and `4 <#nlsr>`__) and a brief
+guide on how to configure and test NLSR using a simple two-node
+network (§ `5 <#test>`__).
 
 Many of the commands in this guide need to be executed as *root*.
 Starting from Fedora 28, there is no need to set a root user
@@ -383,7 +384,36 @@
 
          libChronoSync.so.0.5.0 -> libChronoSync.so.0.5.0
 
-4.3 Downloading and installing NLSR
+4.3 [Optional] Installing SVS
+-----------------------------
+
+NLSR can also use State Vector Sync as the underlying Sync protocol,
+using the ndn-svs library.
+
+This library may be installed by running the following commands as a
+regular user and at the directory defined at § `1 <#intro>`__:
+
+::
+
+      $ git clone --depth 1 https://github.com/named-data/ndn-svs.git
+      $ cd ndn-svs
+      $ ./waf configure
+      $ ./waf
+      $ sudo ./waf install
+
+The following command needs to be used again to configure the libraries:
+
+::
+
+      $ sudo ldconfig -v | grep ndn-svs
+
+This command should display a line similar to the following:
+
+::
+
+         libndn-svs.so.0.0.1 -> libndn-svs.so.0.0.1
+
+4.4 Downloading and installing NLSR
 -----------------------------------
 
 NLSR is downloaded and installed in a folder called *NLSR* which should
@@ -404,7 +434,7 @@
 
       $ ./waf configure --with-chronosync
 
-4.4 Configuring NLSR
+4.5 Configuring NLSR
 --------------------
 
 Create and configure the following directory by running the following
diff --git a/nlsr.conf b/nlsr.conf
index bfed00b..7c59dee 100644
--- a/nlsr.conf
+++ b/nlsr.conf
@@ -19,7 +19,7 @@
   ; InterestLifetime (in seconds) for LSA fetching
   lsa-interest-lifetime 4    ; default value 4. Valid values 1-60
 
-  ; select sync protocol: chronosync or psync
+  ; select sync protocol: chronosync / psync / svs
   sync-protocol psync
 
   ; sync interest lifetime of ChronoSync/PSync in milliseconds
diff --git a/src/communication/sync-protocol-adapter.cpp b/src/communication/sync-protocol-adapter.cpp
index 2e09d2b..7e2dc92 100644
--- a/src/communication/sync-protocol-adapter.cpp
+++ b/src/communication/sync-protocol-adapter.cpp
@@ -58,7 +58,8 @@
                           chronosync::Logic::DEFAULT_RECOVERY_INTEREST_LIFETIME,
                           FIXED_SESSION);
     break;
-#endif
+#endif // HAVE_CHRONOSYNC
+#ifdef HAVE_PSYNC
   case SyncProtocol::PSYNC:
     NDN_LOG_DEBUG("Using PSync");
     m_psyncLogic = std::make_shared<psync::FullProducer>(face,
@@ -69,6 +70,15 @@
                      [this] (auto&&... args) { onPSyncUpdate(std::forward<decltype(args)>(args)...); },
                      syncInterestLifetime);
     break;
+#endif // HAVE_PSYNC
+#ifdef HAVE_SVS
+  case SyncProtocol::SVS:
+    NDN_LOG_DEBUG("Using SVS");
+    m_svsCore = std::make_shared<ndn::svs::SVSyncCore>(face,
+                    syncPrefix,
+                    [this] (auto&&... args) { onSvsUpdate(std::forward<decltype(args)>(args)...); });
+    break;
+#endif // HAVE_SVS
   default:
     NDN_CXX_UNREACHABLE;
   }
@@ -82,10 +92,16 @@
   case SyncProtocol::CHRONOSYNC:
     m_chronoSyncLogic->addUserNode(userPrefix, chronosync::Logic::DEFAULT_NAME, FIXED_SESSION);
     break;
-#endif
+#endif // HAVE_CHRONOSYNC
+#ifdef HAVE_PSYNC
   case SyncProtocol::PSYNC:
     m_psyncLogic->addUserNode(userPrefix);
     break;
+#endif // HAVE_PSYNC
+#ifdef HAVE_SVS
+  case SyncProtocol::SVS:
+    break;
+#endif // HAVE_SVS
   default:
     NDN_CXX_UNREACHABLE;
   }
@@ -99,10 +115,17 @@
   case SyncProtocol::CHRONOSYNC:
     m_chronoSyncLogic->updateSeqNo(seq, userPrefix);
     break;
-#endif
+#endif // HAVE_CHRONOSYNC
+#ifdef HAVE_PSYNC
   case SyncProtocol::PSYNC:
     m_psyncLogic->publishName(userPrefix, seq);
     break;
+#endif // HAVE_PSYNC
+#ifdef HAVE_SVS
+  case SyncProtocol::SVS:
+    m_svsCore->updateSeqNo(seq, userPrefix);
+    break;
+#endif // HAVE_SVS
   default:
     NDN_CXX_UNREACHABLE;
   }
@@ -119,8 +142,9 @@
     m_syncUpdateCallback(update.session.getPrefix(-1), update.high, 0);
   }
 }
-#endif
+#endif // HAVE_CHRONOSYNC
 
+#ifdef HAVE_PSYNC
 void
 SyncProtocolAdapter::onPSyncUpdate(const std::vector<psync::MissingDataInfo>& updates)
 {
@@ -130,5 +154,18 @@
     m_syncUpdateCallback(update.prefix, update.highSeq, update.incomingFace);
   }
 }
+#endif // HAVE_PSYNC
+
+#ifdef HAVE_SVS
+void
+SyncProtocolAdapter::onSvsUpdate(const std::vector<ndn::svs::MissingDataInfo>& updates)
+{
+  NLSR_LOG_TRACE("Received SVS update event");
+
+  for (const auto& update : updates) {
+    m_syncUpdateCallback(update.nodeId, update.high, 0);
+  }
+}
+#endif // HAVE_SVS
 
 } // namespace nlsr
diff --git a/src/communication/sync-protocol-adapter.hpp b/src/communication/sync-protocol-adapter.hpp
index fb5cdbe..e98fa53 100644
--- a/src/communication/sync-protocol-adapter.hpp
+++ b/src/communication/sync-protocol-adapter.hpp
@@ -30,7 +30,12 @@
 #ifdef HAVE_CHRONOSYNC
 #include <ChronoSync/logic.hpp>
 #endif
+#ifdef HAVE_PSYNC
 #include <PSync/full-producer.hpp>
+#endif
+#ifdef HAVE_SVS
+#include <ndn-svs/core.hpp>
+#endif
 
 namespace nlsr {
 
@@ -48,14 +53,14 @@
                       ndn::time::milliseconds syncInterestLifetime,
                       SyncUpdateCallback syncUpdateCallback);
 
-  /*! \brief Add user node to ChronoSync or PSync
+  /*! \brief Add user node to Sync
    *
    * \param userPrefix the Name under which the application will publishData
    */
   void
   addUserNode(const ndn::Name& userPrefix);
 
-  /*! \brief Publish update to ChronoSync or PSync
+  /*! \brief Publish update to Sync
    *
    * NLSR forces sequences number on the sync protocol
    * as it manages is its own sequence number by storing it in a file.
@@ -72,24 +77,36 @@
    *
    * This function packages the sync information into discrete updates
    * and passes those off to another function, m_syncUpdateCallback.
-   * \sa m_syncUpdateCallback
    *
-   * \param v A container with the new information sync has received
+   * \param updates A container with the new information sync has received
    */
   void
   onChronoSyncUpdate(const std::vector<chronosync::MissingDataInfo>& updates);
-#endif
+#endif // HAVE_CHRONOSYNC
 
+#ifdef HAVE_PSYNC
    /*! \brief Hook function to call whenever PSync detects new data.
    *
    * This function packages the sync information into discrete updates
    * and passes those off to another function, m_syncUpdateCallback.
-   * \sa m_syncUpdateCallback
    *
-   * \param v A container with the new information sync has received
+   * \param updates A container with the new information sync has received
    */
   void
   onPSyncUpdate(const std::vector<psync::MissingDataInfo>& updates);
+#endif // HAVE_PSYNC
+
+#ifdef HAVE_SVS
+  /*! \brief Hook function to call whenever SVS detects new data.
+   *
+   * This function packages the sync information into discrete updates
+   * and passes those off to another function, m_syncUpdateCallback.
+   *
+   * \param updates A container with the new information sync has received
+   */
+  void
+  onSvsUpdate(const std::vector<ndn::svs::MissingDataInfo>& updates);
+#endif // HAVE_SVS
 
 private:
   SyncProtocol m_syncProtocol;
@@ -98,7 +115,12 @@
 #ifdef HAVE_CHRONOSYNC
   std::shared_ptr<chronosync::Logic> m_chronoSyncLogic;
 #endif
+#ifdef HAVE_PSYNC
   std::shared_ptr<psync::FullProducer> m_psyncLogic;
+#endif
+#ifdef HAVE_SVS
+  std::shared_ptr<ndn::svs::SVSyncCore> m_svsCore;
+#endif
 };
 
 } // namespace nlsr
diff --git a/src/conf-file-processor.cpp b/src/conf-file-processor.cpp
index bd7dceb..b9ab454 100644
--- a/src/conf-file-processor.cpp
+++ b/src/conf-file-processor.cpp
@@ -289,17 +289,29 @@
 #ifdef HAVE_CHRONOSYNC
     m_confParam.setSyncProtocol(SyncProtocol::CHRONOSYNC);
 #else
-    std::cerr << "NLSR was compiled without ChronoSync support!\n"
-              << "Only PSync support is currently available ('sync-protocol psync')\n";
+    std::cerr << "NLSR was compiled without ChronoSync support!\n";
     return false;
 #endif
   }
   else if (syncProtocol == "psync") {
+#ifdef HAVE_PSYNC
     m_confParam.setSyncProtocol(SyncProtocol::PSYNC);
+#else
+    std::cerr << "NLSR was compiled without PSync support!\n";
+    return false;
+#endif
+  }
+  else if (syncProtocol == "svs") {
+#ifdef HAVE_SVS
+    m_confParam.setSyncProtocol(SyncProtocol::SVS);
+#else
+    std::cerr << "NLSR was compiled without SVS support!\n";
+    return false;
+#endif
   }
   else {
     std::cerr << "Sync protocol '" << syncProtocol << "' is not supported!\n"
-              << "Use either 'chronosync' or 'psync'\n";
+              << "Use 'chronosync' or 'psync' or 'svs'\n";
     return false;
   }
 
diff --git a/src/conf-parameter.hpp b/src/conf-parameter.hpp
index 32b343c..1bcf64c 100644
--- a/src/conf-parameter.hpp
+++ b/src/conf-parameter.hpp
@@ -38,6 +38,7 @@
 enum class SyncProtocol {
   CHRONOSYNC,
   PSYNC,
+  SVS,
 };
 
 enum {
diff --git a/wscript b/wscript
index a574ada..65d5c59 100644
--- a/wscript
+++ b/wscript
@@ -36,8 +36,21 @@
     optgrp = opt.add_option_group('NLSR Options')
     optgrp.add_option('--with-tests', action='store_true', default=False,
                       help='Build unit tests')
-    optgrp.add_option('--with-chronosync', action='store_true', default=False,
+
+    optgrp.add_option('--with-chronosync', dest='with_chronosync', action='store_true', default=False,
                       help='Build with ChronoSync support')
+    optgrp.add_option('--without-chronosync', dest='with_chronosync', action='store_false', default=False,
+                      help='Build without ChronoSync support')
+
+    optgrp.add_option('--with-psync', dest='with_psync', action='store_true', default=True,
+                      help='Build with PSync support')
+    optgrp.add_option('--without-psync', dest='with_psync', action='store_false', default=True,
+                      help='Build without PSync support')
+
+    optgrp.add_option('--with-svs', dest='with_svs', action='store_true', default=False,
+                      help='Build with State Vector Sync support')
+    optgrp.add_option('--without-svs', dest='with_svs', action='store_false', default=False,
+                      help='Build without State Vector Sync support')
 
 def configure(conf):
     conf.load(['compiler_cxx', 'gnu_dirs',
@@ -71,8 +84,17 @@
         conf.check_cfg(package='ChronoSync', args=['ChronoSync >= 0.5.4', '--cflags', '--libs'],
                        uselib_store='CHRONOSYNC', pkg_config_path=pkg_config_path)
 
-    conf.check_cfg(package='PSync', args=['PSync >= 0.3.0', '--cflags', '--libs'],
-                   uselib_store='PSYNC', pkg_config_path=pkg_config_path)
+    if conf.options.with_psync:
+        conf.check_cfg(package='PSync', args=['PSync >= 0.3.0', '--cflags', '--libs'],
+                       uselib_store='PSYNC', pkg_config_path=pkg_config_path)
+
+    if conf.options.with_svs:
+        conf.check_cfg(package='libndn-svs', args=['libndn-svs >= 0.1.0', '--cflags', '--libs'],
+                       uselib_store='SVS', pkg_config_path=pkg_config_path)
+
+    if not any((conf.options.with_chronosync, conf.options.with_psync, conf.options.with_svs)):
+        conf.fatal('Cannot compile without any Sync protocol. '
+                   'Specify at least one of --with-psync or --with-svs or --with-chronosync')
 
     conf.check_compiler_flags()
 
@@ -108,7 +130,7 @@
         target='nlsr-objects',
         source=bld.path.ant_glob('src/**/*.cpp',
                                  excl=['src/main.cpp']),
-        use='NDN_CXX BOOST CHRONOSYNC PSYNC',
+        use='NDN_CXX BOOST CHRONOSYNC PSYNC SVS',
         includes='. src',
         export_includes='. src')