logic: Implement Recovery mechanism

This commit implements the recovery mechanism that is critical for
handling network partitions.  When a node receives a sync interest with
unrecognizable digest, it goes into recovery.

Change-Id: I205687b9791b286cf6eca4c0159b49f744b38bed
Refs: #3929
diff --git a/src/logic.cpp b/src/logic.cpp
index a110328..aa08acc 100644
--- a/src/logic.cpp
+++ b/src/logic.cpp
@@ -58,9 +58,11 @@
 const time::milliseconds Logic::DEFAULT_RESET_INTEREST_LIFETIME(1000);
 const time::milliseconds Logic::DEFAULT_SYNC_INTEREST_LIFETIME(1000);
 const time::milliseconds Logic::DEFAULT_SYNC_REPLY_FRESHNESS(1000);
+const time::milliseconds Logic::DEFAULT_RECOVERY_INTEREST_LIFETIME(1000);
 
 const ndn::ConstBufferPtr Logic::EMPTY_DIGEST(new ndn::Buffer(EMPTY_DIGEST_VALUE, 32));
 const ndn::name::Component Logic::RESET_COMPONENT("reset");
+const ndn::name::Component Logic::RECOVERY_COMPONENT("recovery");
 
 Logic::Logic(ndn::Face& face,
              const Name& syncPrefix,
@@ -72,7 +74,8 @@
              const time::steady_clock::Duration& cancelResetTimer,
              const time::milliseconds& resetInterestLifetime,
              const time::milliseconds& syncInterestLifetime,
-             const time::milliseconds& syncReplyFreshness)
+             const time::milliseconds& syncReplyFreshness,
+             const time::milliseconds& recoveryInterestLifetime)
   : m_face(face)
   , m_syncPrefix(syncPrefix)
   , m_defaultUserPrefix(defaultUserPrefix)
@@ -90,6 +93,7 @@
   , m_resetInterestLifetime(resetInterestLifetime)
   , m_syncInterestLifetime(syncInterestLifetime)
   , m_syncReplyFreshness(syncReplyFreshness)
+  , m_recoveryInterestLifetime(recoveryInterestLifetime)
   , m_defaultSigningId(defaultSigningId)
   , m_validator(validator)
 {
@@ -316,13 +320,15 @@
 
   _LOG_DEBUG_ID("InterestName: " << name);
 
-  if (RESET_COMPONENT != name.get(-1)) {
-    // normal sync interest
+  if (name.size() >= 1 && RESET_COMPONENT == name.get(-1)) {
+    processResetInterest(interest);
+  }
+  else if (name.size() >= 2 && RECOVERY_COMPONENT == name.get(-2)) {
+    processRecoveryInterest(interest);
+  }
+  else {
     processSyncInterest(interest.shared_from_this());
   }
-  else
-    // reset interest
-    processResetInterest(interest);
 
   _LOG_DEBUG_ID("<< Logic::onSyncInterest");
 }
@@ -451,9 +457,9 @@
   }
   else {
     // OK, nobody is helping us, just tell the truth.
-    _LOG_DEBUG_ID("OK, nobody is helping us, just tell the truth");
+    _LOG_DEBUG_ID("OK, nobody is helping us, let us try to recover");
     m_interestTable.erase(digest);
-    sendSyncData(m_defaultUserPrefix, name, m_state);
+    sendRecoveryInterest(digest);
   }
 
   _LOG_DEBUG_ID("<< Logic::processSyncInterest");
@@ -687,4 +693,61 @@
   _LOG_DEBUG_ID("Hash: " << hash);
 }
 
-} // namespace chronosync
+void
+Logic::sendRecoveryInterest(ndn::ConstBufferPtr digest)
+{
+  _LOG_DEBUG_ID(">> Logic::sendRecoveryInterest");
+
+  Name interestName;
+  interestName.append(m_syncPrefix)
+              .append(RECOVERY_COMPONENT)
+              .append(ndn::name::Component(*digest));
+
+  Interest interest(interestName);
+  interest.setMustBeFresh(true);
+  interest.setInterestLifetime(m_recoveryInterestLifetime);
+
+  m_face.expressInterest(interest, bind(&Logic::onRecoveryData, this, _1, _2),
+                                   bind(&Logic::onRecoveryTimeout, this, _1));
+
+  _LOG_DEBUG_ID("interest: " << interest.getName());
+  _LOG_DEBUG_ID("<< Logic::sendRecoveryInterest");
+}
+
+void
+Logic::processRecoveryInterest(const Interest& interest)
+{
+  _LOG_DEBUG_ID(">> Logic::processRecoveryInterest");
+
+  const Name& name = interest.getName();
+  ConstBufferPtr digest = make_shared<ndn::Buffer>(name.get(-1).value(), name.get(-1).value_size());
+
+  ConstBufferPtr rootDigest = m_state.getRootDigest();
+
+  DiffStateContainer::iterator stateIter = m_log.find(digest);
+
+  if (stateIter != m_log.end() || *digest == *EMPTY_DIGEST || *rootDigest == *digest) {
+    _LOG_DEBUG_ID("I can help you recover");
+    sendSyncData(m_defaultUserPrefix, name, m_state);
+    return;
+  }
+  _LOG_DEBUG_ID("<< Logic::processRecoveryInterest");
+}
+
+void
+Logic::onRecoveryData(const Interest& interest, Data& data)
+{
+  _LOG_DEBUG_ID(">> Logic::onRecoveryData");
+  onSyncDataValidated(data.shared_from_this());
+  _LOG_DEBUG_ID("<< Logic::onRecoveryData");
+}
+
+void
+Logic::onRecoveryTimeout(const Interest& interest)
+{
+  _LOG_DEBUG_ID(">> Logic::onRecoveryTimeout");
+  _LOG_DEBUG_ID("Interest: " << interest.getName());
+  _LOG_DEBUG_ID("<< Logic::onRecoveryTimeout");
+}
+
+} // namespace chronosync
\ No newline at end of file
diff --git a/src/logic.hpp b/src/logic.hpp
index d4a811f..c684fa2 100644
--- a/src/logic.hpp
+++ b/src/logic.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
 /*
- * Copyright (c) 2012-2014 University of California, Los Angeles
+ * Copyright (c) 2012-2017 University of California, Los Angeles
  *
  * This file is part of ChronoSync, synchronization library for distributed realtime
  * applications for NDN.
@@ -20,6 +20,7 @@
  * @author Chaoyi Bian <bcy@pku.edu.cn>
  * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
  * @author Yingdi Yu <yingdi@cs.ucla.edu>
+ * @author Sonu Mishra <https://www.linkedin.com/in/mishrasonu>
  */
 
 #ifndef CHRONOSYNC_LOGIC_HPP
@@ -48,7 +49,8 @@
  * Instances of this class is usually used as elements of some containers
  * such as std::vector, thus it is copyable.
  */
-class NodeInfo {
+class NodeInfo
+{
 public:
   Name userPrefix;
   Name signingId;
@@ -97,6 +99,7 @@
   static const time::milliseconds DEFAULT_RESET_INTEREST_LIFETIME;
   static const time::milliseconds DEFAULT_SYNC_INTEREST_LIFETIME;
   static const time::milliseconds DEFAULT_SYNC_REPLY_FRESHNESS;
+  static const time::milliseconds DEFAULT_RECOVERY_INTEREST_LIFETIME;
 
   /**
    * @brief Constructor
@@ -123,7 +126,8 @@
         const time::steady_clock::Duration& cancelResetTimer = DEFAULT_CANCEL_RESET_TIMER,
         const time::milliseconds& resetInterestLifetime = DEFAULT_RESET_INTEREST_LIFETIME,
         const time::milliseconds& syncInterestLifetime = DEFAULT_SYNC_INTEREST_LIFETIME,
-        const time::milliseconds& syncReplyFreshness = DEFAULT_SYNC_REPLY_FRESHNESS);
+        const time::milliseconds& syncReplyFreshness = DEFAULT_SYNC_REPLY_FRESHNESS,
+        const time::milliseconds& recoveryInterestLifetime = DEFAULT_RECOVERY_INTEREST_LIFETIME);
 
   ~Logic();
 
@@ -387,6 +391,47 @@
   void
   printDigest(ndn::ConstBufferPtr digest);
 
+  /**
+   * @brief Helper method to send Recovery Interest
+   *
+   * @param digest    The digest to be included in the recovery interest
+   */
+  void
+  sendRecoveryInterest(ndn::ConstBufferPtr digest);
+
+  /**
+   * @brief Process Recovery Interest
+   *
+   * This method extracts the digest from the incoming Recovery Interest.
+   * If it recognizes this incoming digest, then it sends its full state
+   * as reply.
+   *
+   * @param interest    The incoming interest
+   */
+  void
+  processRecoveryInterest(const Interest& interest);
+
+  /**
+   * @brief Callback to handle Recovery Reply
+   *
+   * This method calls Logic::onSyncDataValidated directly.
+   *
+   * @param interest The Recovery Interest
+   * @param data     The reply to the Recovery Interest
+   */
+  void
+  onRecoveryData(const Interest& interest, Data& data);
+
+  /**
+   * @brief Callback to handle Recovery Interest timeout.
+   *
+   * This method does nothing.
+   *
+   * @param interest The Recovery Interest
+   */
+  void
+  onRecoveryTimeout(const Interest& interest);
+
 public:
   static const ndn::Name DEFAULT_NAME;
   static const ndn::Name EMPTY_NAME;
@@ -397,6 +442,7 @@
 
   static const ndn::ConstBufferPtr EMPTY_DIGEST;
   static const ndn::name::Component RESET_COMPONENT;
+  static const ndn::name::Component RECOVERY_COMPONENT;
 
   // Communication
   ndn::Face& m_face;
@@ -438,6 +484,8 @@
   time::milliseconds m_syncInterestLifetime;
   /// @brief FreshnessPeriod of SyncReply
   time::milliseconds m_syncReplyFreshness;
+  /// @brief Lifetime of recovery interest
+  time::milliseconds m_recoveryInterestLifetime;
 
   // Security
   ndn::Name m_defaultSigningId;
@@ -454,4 +502,4 @@
 
 } // namespace chronosync
 
-#endif // CHRONOSYNC_LOGIC_HPP
+#endif // CHRONOSYNC_LOGIC_HPP
\ No newline at end of file