logic: allow manipulating multiple nodes in single logic

Change-Id: Iaa2a2b08e891d41c9aa71c13ffc531bef406b6d8
diff --git a/src/logic.cpp b/src/logic.cpp
index 4c3fc55..08ed60e 100644
--- a/src/logic.cpp
+++ b/src/logic.cpp
@@ -50,6 +50,7 @@
 #endif
 
 const ndn::Name Logic::DEFAULT_NAME;
+const ndn::Name Logic::EMPTY_NAME;
 const ndn::shared_ptr<ndn::Validator> Logic::DEFAULT_VALIDATOR;
 const time::steady_clock::Duration Logic::DEFAULT_RESET_TIMER = time::seconds(0);
 const time::steady_clock::Duration Logic::DEFAULT_CANCEL_RESET_TIMER = time::milliseconds(500);
@@ -62,9 +63,9 @@
 
 Logic::Logic(ndn::Face& face,
              const Name& syncPrefix,
-             const Name& userPrefix,
+             const Name& defaultUserPrefix,
              const UpdateCallback& onUpdate,
-             const Name& signingId,
+             const Name& defaultSigningId,
              ndn::shared_ptr<ndn::Validator> validator,
              const time::steady_clock::Duration& resetTimer,
              const time::steady_clock::Duration& cancelResetTimer,
@@ -73,7 +74,7 @@
              const time::milliseconds& syncReplyFreshness)
   : m_face(face)
   , m_syncPrefix(syncPrefix)
-  , m_userPrefix(userPrefix)
+  , m_defaultUserPrefix(defaultUserPrefix)
   , m_interestTable(m_face.getIoService())
   , m_outstandingInterestId(0)
   , m_isInReset(false)
@@ -88,7 +89,7 @@
   , m_resetInterestLifetime(resetInterestLifetime)
   , m_syncInterestLifetime(syncInterestLifetime)
   , m_syncReplyFreshness(syncReplyFreshness)
-  , m_signingId(signingId)
+  , m_defaultSigningId(defaultSigningId)
   , m_validator(validator)
 {
 #ifdef _DEBUG
@@ -97,6 +98,9 @@
 
   _LOG_DEBUG_ID(">> Logic::Logic");
 
+  addUserNode(m_defaultUserPrefix, m_defaultSigningId);
+
+
   m_syncReset = m_syncPrefix;
   m_syncReset.append("reset");
 
@@ -106,7 +110,6 @@
                              bind(&Logic::onSyncInterest, this, _1, _2),
                              bind(&Logic::onSyncRegisterFailed, this, _1, _2));
 
-  setUserPrefix(m_userPrefix);
 
   _LOG_DEBUG_ID("<< Logic::Logic");
 }
@@ -144,56 +147,132 @@
 }
 
 void
-Logic::setUserPrefix(const Name& userPrefix)
+Logic::setDefaultUserPrefix(const Name& defaultUserPrefix)
 {
-  m_userPrefix = userPrefix;
-
-  m_sessionName = m_userPrefix;
-  m_sessionName.appendNumber(ndn::time::toUnixTimestamp(ndn::time::system_clock::now()).count());
-
-  m_seqNo = 0;
-
-  reset();
+  if (defaultUserPrefix != EMPTY_NAME) {
+    if (m_nodeList.find(defaultUserPrefix) != m_nodeList.end()) {
+      m_defaultUserPrefix = defaultUserPrefix;
+      m_defaultSigningId = m_nodeList[defaultUserPrefix].signingId;
+    }
+  }
 }
 
 void
-Logic::updateSeqNo(const SeqNo& seqNo)
+Logic::addUserNode(const Name& userPrefix, const Name& signingId)
 {
-  _LOG_DEBUG_ID(">> Logic::updateSeqNo");
-  _LOG_DEBUG_ID("seqNo: " << seqNo << " m_seqNo: " << m_seqNo);
-  if (seqNo < m_seqNo || seqNo == 0)
+  if (userPrefix == EMPTY_NAME)
     return;
+  if (m_defaultUserPrefix == EMPTY_NAME) {
+    m_defaultUserPrefix = userPrefix;
+    m_defaultSigningId = signingId;
+  }
+  if (m_nodeList.find(userPrefix) == m_nodeList.end()) {
+    m_nodeList[userPrefix].userPrefix = userPrefix;
+    m_nodeList[userPrefix].signingId = signingId;
+    Name sessionName = userPrefix;
+    sessionName.appendNumber(ndn::time::toUnixTimestamp(ndn::time::system_clock::now()).count());
+    m_nodeList[userPrefix].sessionName = sessionName;
+    m_nodeList[userPrefix].seqNo = 0;
+    reset();
+  }
+}
 
-  m_seqNo = seqNo;
-
-  _LOG_DEBUG_ID("updateSeqNo: m_seqNo " << m_seqNo);
-
-  if (!m_isInReset) {
-    _LOG_DEBUG_ID("updateSeqNo: not in Reset ");
-    ndn::ConstBufferPtr previousRoot = m_state.getRootDigest();
-    {
-      using namespace CryptoPP;
-
-      std::string hash;
-      StringSource(previousRoot->buf(), previousRoot->size(), true,
-                   new HexEncoder(new StringSink(hash), false));
-      _LOG_DEBUG_ID("Hash: " << hash);
+void
+Logic::removeUserNode(const Name& userPrefix)
+{
+  auto userNode = m_nodeList.find(userPrefix);
+  if (userNode != m_nodeList.end()) {
+    m_nodeList.erase(userNode);
+    if (m_defaultUserPrefix == userPrefix) {
+      if (!m_nodeList.empty()) {
+        m_defaultUserPrefix = m_nodeList.begin()->second.userPrefix;
+        m_defaultSigningId = m_nodeList.begin()->second.signingId;
+      }
+      else {
+        m_defaultUserPrefix = EMPTY_NAME;
+        m_defaultSigningId = DEFAULT_NAME;
+      }
     }
+    reset();
+  }
+}
 
-    bool isInserted = false;
-    bool isUpdated = false;
-    SeqNo oldSeq;
-    boost::tie(isInserted, isUpdated, oldSeq) = m_state.update(m_sessionName, seqNo);
+const Name&
+Logic::getSessionName(Name prefix)
+{
+  if (prefix == EMPTY_NAME)
+    prefix = m_defaultUserPrefix;
+  auto node = m_nodeList.find(prefix);
+  if (node != m_nodeList.end())
+    return node->second.sessionName;
+  else
+    throw Error("Refer to non-existent node:" + prefix.toUri());
+}
 
-    _LOG_DEBUG_ID("Insert: " << std::boolalpha << isInserted);
-    _LOG_DEBUG_ID("Updated: " << std::boolalpha << isUpdated);
-    if (isInserted || isUpdated) {
-      DiffStatePtr commit = make_shared<DiffState>();
-      commit->update(m_sessionName, seqNo);
-      commit->setRootDigest(m_state.getRootDigest());
-      insertToDiffLog(commit, previousRoot);
+const SeqNo&
+Logic::getSeqNo(Name prefix)
+{
+  if (prefix == EMPTY_NAME)
+    prefix = m_defaultUserPrefix;
+  auto node = m_nodeList.find(prefix);
+  if (node != m_nodeList.end())
+    return node->second.seqNo;
+  else
+    throw Logic::Error("Refer to non-existent node:" + prefix.toUri());
 
-      satisfyPendingSyncInterests(commit);
+}
+
+void
+Logic::updateSeqNo(const SeqNo& seqNo, const Name &updatePrefix)
+{
+  Name prefix;
+  if (updatePrefix == EMPTY_NAME) {
+    if (m_defaultUserPrefix == EMPTY_NAME)
+      return;
+    prefix = m_defaultUserPrefix;
+  }
+  else
+    prefix = updatePrefix;
+
+  auto it = m_nodeList.find(prefix);
+  if (it != m_nodeList.end()) {
+    NodeInfo& node = it->second;
+    _LOG_DEBUG_ID(">> Logic::updateSeqNo");
+    _LOG_DEBUG_ID("seqNo: " << seqNo << " m_seqNo: " << node.seqNo);
+    if (seqNo < node.seqNo || seqNo == 0)
+      return;
+
+    node.seqNo = seqNo;
+    _LOG_DEBUG_ID("updateSeqNo: m_seqNo " << node.seqNo);
+
+    if (!m_isInReset) {
+      _LOG_DEBUG_ID("updateSeqNo: not in Reset ");
+      ndn::ConstBufferPtr previousRoot = m_state.getRootDigest();
+      {
+        using namespace CryptoPP;
+
+        std::string hash;
+        StringSource(previousRoot->buf(), previousRoot->size(), true,
+                     new HexEncoder(new StringSink(hash), false));
+        _LOG_DEBUG_ID("Hash: " << hash);
+      }
+
+      bool isInserted = false;
+      bool isUpdated = false;
+      SeqNo oldSeq;
+      boost::tie(isInserted, isUpdated, oldSeq) = m_state.update(node.sessionName,
+                                                                 node.seqNo);
+
+      _LOG_DEBUG_ID("Insert: " << std::boolalpha << isInserted);
+      _LOG_DEBUG_ID("Updated: " << std::boolalpha << isUpdated);
+      if (isInserted || isUpdated) {
+        DiffStatePtr commit = make_shared<DiffState>();
+        commit->update(node.sessionName, node.seqNo);
+        commit->setRootDigest(m_state.getRootDigest());
+        insertToDiffLog(commit, previousRoot);
+
+        satisfyPendingSyncInterests(prefix, commit);
+      }
     }
   }
 }
@@ -341,7 +420,7 @@
   // If the digest of incoming interest is an "empty" digest
   if (digest == EMPTY_DIGEST) {
     _LOG_DEBUG_ID("Poor guy, he knows nothing");
-    sendSyncData(name, m_state);
+    sendSyncData(m_defaultUserPrefix, name, m_state);
     return;
   }
 
@@ -349,7 +428,7 @@
   // If the digest of incoming interest can be found from the log
   if (stateIter != m_log.end()) {
     _LOG_DEBUG_ID("It is ok, you are so close");
-    sendSyncData(name, *(*stateIter)->diff());
+    sendSyncData(m_defaultUserPrefix, name, *(*stateIter)->diff());
     return;
   }
 
@@ -370,7 +449,7 @@
     // OK, nobody is helping us, just tell the truth.
     _LOG_DEBUG_ID("OK, nobody is helping us, just tell the truth");
     m_interestTable.erase(digest);
-    sendSyncData(name, m_state);
+    sendSyncData(m_defaultUserPrefix, name, m_state);
   }
 
   _LOG_DEBUG_ID("<< Logic::processSyncInterest");
@@ -389,7 +468,6 @@
                        const Block& syncReplyBlock)
 {
   _LOG_DEBUG_ID(">> Logic::processSyncData");
-
   DiffStatePtr commit = make_shared<DiffState>();
   ndn::ConstBufferPtr previousRoot = m_state.getRootDigest();
 
@@ -411,7 +489,6 @@
         bool isUpdated = false;
         SeqNo oldSeq;
         boost::tie(isInserted, isUpdated, oldSeq) = m_state.update(info, seq);
-
         if (isInserted || isUpdated) {
           commit->update(info, seq);
 
@@ -451,7 +528,7 @@
 }
 
 void
-Logic::satisfyPendingSyncInterests(ConstDiffStatePtr commit)
+Logic::satisfyPendingSyncInterests(const Name& updatedPrefix, ConstDiffStatePtr commit)
 {
   _LOG_DEBUG_ID(">> Logic::satisfyPendingSyncInterests");
   try {
@@ -459,11 +536,10 @@
     for (InterestTable::const_iterator it = m_interestTable.begin();
          it != m_interestTable.end(); it++) {
       ConstUnsatisfiedInterestPtr request = *it;
-
       if (request->isUnknown)
-        sendSyncData(request->interest->getName(), m_state);
+        sendSyncData(updatedPrefix, request->interest->getName(), m_state);
       else
-        sendSyncData(request->interest->getName(), *commit);
+        sendSyncData(updatedPrefix, request->interest->getName(), *commit);
     }
     m_interestTable.clear();
   }
@@ -548,17 +624,18 @@
 }
 
 void
-Logic::sendSyncData(const Name& name, const State& state)
+Logic::sendSyncData(const Name& nodePrefix, const Name& name, const State& state)
 {
   _LOG_DEBUG_ID(">> Logic::sendSyncData");
   shared_ptr<Data> syncReply = make_shared<Data>(name);
   syncReply->setContent(state.wireEncode());
   syncReply->setFreshnessPeriod(m_syncReplyFreshness);
-
-  if (m_signingId.empty())
+  if (m_nodeList.find(nodePrefix) == m_nodeList.end())
+    return;
+  if (m_nodeList[nodePrefix].signingId.empty())
     m_keyChain.sign(*syncReply);
   else
-    m_keyChain.signByIdentity(*syncReply, m_signingId);
+    m_keyChain.signByIdentity(*syncReply, m_nodeList[nodePrefix].signingId);
 
   m_face.put(*syncReply);
 
@@ -589,7 +666,9 @@
     return;
 
   m_isInReset = false;
-  updateSeqNo(m_seqNo);
+  for (const auto& node : m_nodeList) {
+    updateSeqNo(node.second.seqNo, node.first);
+  }
   _LOG_DEBUG_ID("<< Logic::cancelReset");
 }
 
diff --git a/src/logic.hpp b/src/logic.hpp
index acca168..e8ea4f6 100644
--- a/src/logic.hpp
+++ b/src/logic.hpp
@@ -27,7 +27,7 @@
 
 #include "boost-header.h"
 #include <memory>
-#include <map>
+#include <unordered_map>
 
 #include <ndn-cxx/face.hpp>
 #include <ndn-cxx/util/scheduler.hpp>
@@ -48,6 +48,14 @@
  * Instances of this class is usually used as elements of some containers
  * such as std::vector, thus it is copyable.
  */
+class NodeInfo {
+public:
+  Name userPrefix;
+  Name signingId;
+  Name sessionName;
+  SeqNo seqNo;
+};
+
 class MissingDataInfo
 {
 public:
@@ -73,6 +81,17 @@
 class Logic : noncopyable
 {
 public:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+public:
   static const time::steady_clock::Duration DEFAULT_RESET_TIMER;
   static const time::steady_clock::Duration DEFAULT_CANCEL_RESET_TIMER;
   static const time::milliseconds DEFAULT_RESET_INTEREST_LIFETIME;
@@ -83,8 +102,9 @@
    * @brief Constructor
    *
    * @param syncPrefix The prefix of the sync group
-   * @param userPrefix The prefix of the user who owns the session
+   * @param defaultUserPrefix The prefix of the first user added to this session
    * @param onUpdate The callback function to handle state updates
+   * @param defaultSigningId The signing Id of the default user
    * @param validator The validator for packet validation
    * @param resetTimer The timer to periodically send Reset Interest
    * @param syncReplyFreshness The FreshnessPeriod of sync reply
@@ -94,9 +114,9 @@
    */
   Logic(ndn::Face& face,
         const Name& syncPrefix,
-        const Name& userPrefix,
+        const Name& defaultUserPrefix,
         const UpdateCallback& onUpdate,
-        const Name& signingId = DEFAULT_NAME,
+        const Name& defaultSigningId = DEFAULT_NAME,
         ndn::shared_ptr<ndn::Validator> validator = DEFAULT_VALIDATOR,
         const time::steady_clock::Duration& resetTimer = DEFAULT_RESET_TIMER,
         const time::steady_clock::Duration& cancelResetTimer = DEFAULT_CANCEL_RESET_TIMER,
@@ -113,36 +133,67 @@
   /**
    * @brief Set user prefix
    *
-   * This method will also change the session name and trigger reset.
+   * This method will also change the default user and signing Id of that user.
    *
-   * @param userPrefix The prefix of user.
+   * @param defaultUserPrefix The prefix of user.
    */
   void
-  setUserPrefix(const Name& userPrefix);
+  setDefaultUserPrefix(const Name& defaultUserPrefix);
 
-  /// @brief Get the name of the local session.
+  /// @brief Get the name of default user.
   const Name&
-  getSessionName() const
+  getDefaultUserPrefix() const
   {
-    return m_sessionName;
+    return m_defaultUserPrefix;
   }
 
-  /// @brief Get current seqNo of the local session.
+  /**
+   * @brief Add user node into the local session.
+   *
+   * This method also reset after adding
+   *
+   * @param userPrefix prefix of the added node
+   * @param signingId signing Id of the added node
+   */
+  void
+  addUserNode(const Name& userPrefix, const Name& signingId = DEFAULT_NAME);
+
+  /// @brief remove the node from the local session
+  void
+  removeUserNode(const Name& userPrefix);
+
+  /**
+   * @brief Get the name of the local session.
+   *
+   * This method gets the session name according to prefix, if prefix is not specified,
+   * it returns the session name of default user.
+   *
+   * @param prefix prefix of the node
+   */
+  const Name&
+  getSessionName(Name prefix = EMPTY_NAME);
+
+  /**
+   * @brief Get current seqNo of the local session.
+   *
+   * This method gets the seqNo according to prefix, if prefix is not specified,
+   * it returns the seqNo of default user.
+   *
+   * @param prefix prefix of the node
+   */
   const SeqNo&
-  getSeqNo() const
-  {
-    return m_seqNo;
-  }
+  getSeqNo(Name prefix = EMPTY_NAME);
 
   /**
    * @brief Update the seqNo of the local session
    *
-   * The method updates the existing seqNo with the supplied seqNo.
+   * The method updates the existing seqNo with the supplied seqNo and prefix.
    *
    * @param seq The new seqNo.
+   * @param updatePrefix The prefix of node to update.
    */
   void
-  updateSeqNo(const SeqNo& seq);
+  updateSeqNo(const SeqNo& seq, const Name& updatePrefix = EMPTY_NAME);
 
   /// @brief Get root digest of current sync tree
   ndn::ConstBufferPtr
@@ -168,6 +219,7 @@
     return m_state;
   }
 
+
 private:
   /**
    * @brief Callback to handle Sync Interest
@@ -304,7 +356,7 @@
    * @param commit The diff.
    */
   void
-  satisfyPendingSyncInterests(ConstDiffStatePtr commit);
+  satisfyPendingSyncInterests(const Name& updatedPrefix, ConstDiffStatePtr commit);
 
   /// @brief Helper method to send normal Sync Interest
   void
@@ -316,7 +368,7 @@
 
   /// @brief Helper method to send Sync Reply
   void
-  sendSyncData(const Name& name, const State& state);
+  sendSyncData(const Name& nodePrefix, const Name& name, const State& state);
 
   /**
    * @brief Unset reset status
@@ -332,9 +384,11 @@
 
 public:
   static const ndn::Name DEFAULT_NAME;
+  static const ndn::Name EMPTY_NAME;
   static const ndn::shared_ptr<ndn::Validator> DEFAULT_VALIDATOR;
 
 private:
+  typedef std::unordered_map<ndn::Name, NodeInfo> NodeList;
 
   static const ndn::ConstBufferPtr EMPTY_DIGEST;
   static const ndn::name::Component RESET_COMPONENT;
@@ -344,11 +398,10 @@
   Name m_syncPrefix;
   const ndn::RegisteredPrefixId* m_syncRegisteredPrefixId;
   Name m_syncReset;
-  Name m_userPrefix;
+  Name m_defaultUserPrefix;
 
   // State
-  Name m_sessionName;
-  SeqNo m_seqNo;
+  NodeList m_nodeList;
   State m_state;
   DiffStateContainer m_log;
   InterestTable m_interestTable;
@@ -382,10 +435,11 @@
   time::milliseconds m_syncReplyFreshness;
 
   // Security
-  ndn::Name m_signingId;
+  ndn::Name m_defaultSigningId;
   ndn::KeyChain m_keyChain;
   ndn::shared_ptr<ndn::Validator> m_validator;
 
+
 #ifdef _DEBUG
   int m_instanceId;
   static int m_instanceCounter;