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