Add chatroom discovery logic

Change-Id: I1b65b7bbcf321c51cb00e8d1c05a42291601a284
diff --git a/src/chat-dialog.cpp b/src/chat-dialog.cpp
index 442e866..93d7dd0 100644
--- a/src/chat-dialog.cpp
+++ b/src/chat-dialog.cpp
@@ -78,6 +78,8 @@
   , m_sock(NULL)
   , m_session(static_cast<uint64_t>(time::toUnixTimestamp(time::system_clock::now()).count()))
   , m_inviteListDialog(new InviteListDialog)
+  //ymj
+  //, m_trustModel(withSecurity)
 {
   qRegisterMetaType<std::vector<Sync::MissingDataInfo> >("std::vector<Sync::MissingDataInfo>");
   qRegisterMetaType<ndn::shared_ptr<const ndn::Data> >("ndn.DataPtr");
@@ -155,7 +157,11 @@
             m_inviteListDialog, SLOT(onContactAliasListReady(const QStringList&)));
     connect(m_contactManager, SIGNAL(contactIdListReady(const QStringList&)),
             m_inviteListDialog, SLOT(onContactIdListReady(const QStringList&)));
+
+    m_trustModel = ChatroomInfo::TRUST_MODEL_WEBOFTRUST;
   }
+  else
+    m_trustModel = ChatroomInfo::TRUST_MODEL_NONE;
 
   initializeSync();
 }
@@ -180,7 +186,7 @@
 void
 ChatDialog::addSyncAnchor(const Invitation& invitation)
 {
-  // _LOG_DEBUG("Add sync anchor from invation");
+  // _LOG_DEBUG("Add sync anchor from invitation");
   // Add inviter certificate as trust anchor.
   m_sock->addParticipant(invitation.getInviterCertificate());
   plotTrustTree();
@@ -228,7 +234,7 @@
                               "system tray. To close the chatroom, "
                               "choose <b>Close chatroom</b> in the "
                               "context memu of the system tray entry."));
-  hide();
+  hide();//ymj
   e->ignore();
 }
 
@@ -282,6 +288,7 @@
 
   if (m_certListPrefixId)
     m_face->unsetInterestFilter(m_certListPrefixId);
+
   m_certListPrefixId = m_face->setInterestFilter(m_certListPrefix,
                                                  bind(&ChatDialog::onCertListInterest,
                                                       this, _1, _2),
@@ -522,7 +529,7 @@
 }
 
 void
-ChatDialog::onCertListInterest(const Name& prefix, const Interest& interest)
+ChatDialog::onCertListInterest(const ndn::Name& prefix, const ndn::Interest& interest)
 {
   vector<Name> certNameList;
   m_sock->getIntroCertNames(certNameList);
@@ -545,7 +552,7 @@
 }
 
 void
-ChatDialog::onCertListRegisterFailed(const Name& prefix, const string& msg)
+ChatDialog::onCertListRegisterFailed(const ndn::Name& prefix, const std::string& msg)
 {
   // _LOG_DEBUG("ChatDialog::onCertListRegisterFailed failed: " + msg);
 }
@@ -567,7 +574,7 @@
 }
 
 void
-ChatDialog::onCertSingleRegisterFailed(const Name& prefix, const string& msg)
+ChatDialog::onCertSingleRegisterFailed(const Name& prefix, const std::string& msg)
 {
   // _LOG_DEBUG("ChatDialog::onCertListRegisterFailed failed: " + msg);
 }
@@ -719,7 +726,7 @@
   msg.set_to(m_chatroomName);
   msg.set_data(text.toStdString());
   int32_t seconds =
-    static_cast<int32_t>(time::toUnixTimestamp(time::system_clock::now()).count()/1000000000);
+    static_cast<int32_t>(time::toUnixTimestamp(time::system_clock::now()).count()/1000);
   msg.set_timestamp(seconds);
   msg.set_type(SyncDemo::ChatMessage::CHAT);
 }
@@ -731,7 +738,7 @@
   msg.set_from(m_nick);
   msg.set_to(m_chatroomName);
   int32_t seconds =
-    static_cast<int32_t>(time::toUnixTimestamp(time::system_clock::now()).count()/1000000000);
+    static_cast<int32_t>(time::toUnixTimestamp(time::system_clock::now()).count()/1000);
   msg.set_timestamp(seconds);
   msg.set_type(type);
 }
@@ -924,6 +931,26 @@
   }
 }
 
+shared_ptr<ChatroomInfo>
+ChatDialog::getChatroomInfo() const
+{
+  shared_ptr<ChatroomInfo> chatroom = make_shared<ChatroomInfo>();
+  chatroom->setName(Name::Component(m_chatroomName));
+
+  Roster roster = m_scene->getRosterFull();
+  Roster_iterator it = roster.begin();
+
+  for(it = roster.begin(); it != roster.end(); ++it )
+  {
+    Name participant = Name(it.key().toStdString()).getPrefix(-3);
+    chatroom->addParticipant(participant);
+  }
+
+  chatroom->setTrustModel(m_trustModel);
+  return chatroom;
+}
+
+
 // public slots:
 void
 ChatDialog::onLocalPrefixUpdated(const QString& localPrefix)
@@ -1052,7 +1079,7 @@
 }
 
 void
-ChatDialog::onProcessData(const shared_ptr<const Data>& data, bool show, bool isHistory)
+ChatDialog::onProcessData(const ndn::shared_ptr<const ndn::Data>& data, bool show, bool isHistory)
 {
   SyncDemo::ChatMessage msg;
   bool corrupted = false;
@@ -1065,6 +1092,9 @@
     corrupted = true;
   }
 
+  //std::cout << "onProcessData: " << show << std::endl;
+  //std::cout << "onProcessData: " << corrupted << std::endl;
+
   // display msg received from network
   // we have to do so; this function is called by ccnd thread
   // so if we call appendMsg directly
@@ -1153,6 +1183,8 @@
     appendMessage(msg);
   }
   plotTrustTree();
+
+  emit rosterChanged(*getChatroomInfo());
 }
 
 void
@@ -1180,7 +1212,7 @@
 ChatDialog::sendHello()
 {
   int64_t now = time::toUnixTimestamp(time::system_clock::now()).count();
-  int elapsed = (now - m_lastMsgTime) / 1000000000;
+  int elapsed = (now - m_lastMsgTime) / 1000;
   if (elapsed >= m_randomizedInterval / 1000) {
     SyncDemo::ChatMessage msg;
     formControlMessage(msg, SyncDemo::ChatMessage::HELLO);
@@ -1262,7 +1294,7 @@
 }
 
 void
-ChatDialog::onReplyTimeout(const Interest& interest,
+ChatDialog::onReplyTimeout(const ndn::Interest& interest,
                            size_t routablePrefixOffset)
 {
   Name interestName;
@@ -1279,7 +1311,7 @@
 }
 
 void
-ChatDialog::onIntroCert(const Interest& interest, const shared_ptr<const Data>& data)
+ChatDialog::onIntroCert(const ndn::Interest& interest, const ndn::shared_ptr<const ndn::Data>& data)
 {
   Data innerData;
   innerData.wireDecode(data->getContent().blockFromValue());
@@ -1289,13 +1321,14 @@
 }
 
 void
-ChatDialog::onIntroCertTimeout(const Interest& interest, int retry, const QString& msg)
+ChatDialog::onIntroCertTimeout(const ndn::Interest& interest, int retry, const QString& msg)
 {
   // _LOG_DEBUG("onIntroCertTimeout: " << msg.toStdString());
 }
 
 } // namespace chronos
 
+
 #if WAF
 #include "chat-dialog.moc"
 // #include "chat-dialog.cpp.moc"
diff --git a/src/chat-dialog.hpp b/src/chat-dialog.hpp
index 12051f1..9c4008e 100644
--- a/src/chat-dialog.hpp
+++ b/src/chat-dialog.hpp
@@ -9,8 +9,8 @@
  *         Yingdi Yu <yingdi@cs.ucla.edu>
  */
 
-#ifndef CHRONOS_CHAT_DIALOG_HPP
-#define CHRONOS_CHAT_DIALOG_HPP
+#ifndef CHRONOCHAT_CHAT_DIALOG_HPP
+#define CHRONOCHAT_CHAT_DIALOG_HPP
 
 #include <QDialog>
 #include <QTextTable>
@@ -36,6 +36,7 @@
 #include <boost/thread/locks.hpp>
 #include <boost/thread/recursive_mutex.hpp>
 #include <boost/thread/thread.hpp>
+#include "chatroom-info.hpp"
 #endif
 
 #include "invite-list-dialog.hpp"
@@ -81,7 +82,10 @@
   void
   processRemoveWrapper(const std::string& prefix);
 
-protected:
+  //ymj
+  shared_ptr<ChatroomInfo>
+  getChatroomInfo() const;
+
   void
   closeEvent(QCloseEvent* e);
 
@@ -116,12 +120,12 @@
                       size_t routablePrefixOffset);
 
   void
-  onReplyValidated(const shared_ptr<const Data>& data,
+  onReplyValidated(const ndn::shared_ptr<const ndn::Data>& data,
                    size_t inviteeRoutablePrefixOffset,
                    bool isIntroduce);
 
   void
-  onReplyValidationFailed(const shared_ptr<const Data>& data,
+  onReplyValidationFailed(const ndn::shared_ptr<const ndn::Data>& data,
                           const std::string& failureInfo);
 
   void
@@ -147,10 +151,10 @@
   introCertTimeoutWrapper(const Interest& interest, int retry, const QString& msg);
 
   void
-  onCertListInterest(const Name& prefix, const Interest& interest);
+  onCertListInterest(const ndn::Name& prefix, const ndn::Interest& interest);
 
   void
-  onCertListRegisterFailed(const Name& prefix, const std::string& msg);
+  onCertListRegisterFailed(const ndn::Name& prefix, const std::string& msg);
 
   void
   onCertSingleInterest(const Name& prefix, const Interest& interest);
@@ -206,7 +210,8 @@
 
 signals:
   void
-  processData(const shared_ptr<const Data>& data, bool show, bool isHistory);
+  processData(const ndn::shared_ptr<const ndn::Data>& data,
+              bool show, bool isHistory);
 
   void
   processTreeUpdate(const std::vector<Sync::MissingDataInfo>);
@@ -224,21 +229,24 @@
   resetIcon();
 
   void
-  reply(const Interest& interest, const shared_ptr<const Data>& data,
+  reply(const ndn::Interest& interest, const ndn::shared_ptr<const ndn::Data>& data,
         size_t routablePrefixOffset, bool isIntroducer);
 
   void
-  replyTimeout(const Interest& interest, size_t routablePrefixOffset);
+  replyTimeout(const ndn::Interest& interest, size_t routablePrefixOffset);
 
   void
-  introCert(const Interest& interest, const shared_ptr<const Data>& data);
+  introCert(const ndn::Interest& interest, const ndn::shared_ptr<const ndn::Data>& data);
 
   void
-  introCertTimeout(const Interest& interest, int retry, const QString& msg);
+  introCertTimeout(const ndn::Interest& interest, int retry, const QString& msg);
 
   void
   waitForContactList();
 
+  void
+  rosterChanged(const chronos::ChatroomInfo& info);
+
 public slots:
   void
   onLocalPrefixUpdated(const QString& localPrefix);
@@ -257,7 +265,7 @@
   onTrustTreeButtonPressed();
 
   void
-  onProcessData(const shared_ptr<const Data>& data, bool show, bool isHistory);
+  onProcessData(const ndn::shared_ptr<const ndn::Data>& data, bool show, bool isHistory);
 
   void
   onProcessTreeUpdate(const std::vector<Sync::MissingDataInfo>&);
@@ -290,17 +298,17 @@
   onSendInvitation(QString invitee);
 
   void
-  onReply(const Interest& interest, const shared_ptr<const Data>& data,
+  onReply(const ndn::Interest& interest, const ndn::shared_ptr<const ndn::Data>& data,
           size_t routablePrefixOffset, bool isIntroducer);
 
   void
-  onReplyTimeout(const Interest& interest, size_t routablePrefixOffset);
+  onReplyTimeout(const ndn::Interest& interest, size_t routablePrefixOffset);
 
   void
-  onIntroCert(const Interest& interest, const shared_ptr<const Data>& data);
+  onIntroCert(const ndn::Interest& interest, const ndn::shared_ptr<const ndn::Data>& data);
 
   void
-  onIntroCertTimeout(const Interest& interest, int retry, const QString& msg);
+  onIntroCertTimeout(const ndn::Interest& interest, int retry, const QString& msg);
 
 private:
   Ui::ChatDialog *ui;
@@ -345,8 +353,11 @@
   boost::recursive_mutex m_sceneMutex;
   QList<QString> m_zombieList;
   int m_zombieIndex;
+
+  //ymj
+  ChatroomInfo::TrustModel m_trustModel;
 };
 
 } // namespace chronos
 
-#endif // CHRONOS_CHAT_DIALOG_HPP
+#endif // CHRONOCHAT_CHAT_DIALOG_HPP
diff --git a/src/chatroom-discovery-logic.cpp b/src/chatroom-discovery-logic.cpp
new file mode 100644
index 0000000..048d121
--- /dev/null
+++ b/src/chatroom-discovery-logic.cpp
@@ -0,0 +1,221 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *                     Yingdi Yu
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Mengjin Yan <jane.yan0129@gmail.com>
+ * Author: Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#include "chatroom-discovery-logic.hpp"
+
+
+namespace chronos {
+
+const size_t ChatroomDiscoveryLogic::OFFSET_CHATROOM_NAME = 4;
+const size_t ChatroomDiscoveryLogic::DISCOVERY_INTEREST_NAME_SIZE = 4;
+const size_t ChatroomDiscoveryLogic::REFRESHING_INTEREST_NAME_SIZE = 5;
+const time::milliseconds ChatroomDiscoveryLogic::DEFAULT_REFRESHING_TIMER(10000);
+const Name ChatroomDiscoveryLogic::DISCOVERY_PREFIX("/ndn/broadcast/chronochat/chatroom-list");
+const time::seconds ChatroomDiscoveryLogic::m_discoveryInterval(600);
+
+ChatroomDiscoveryLogic::ChatroomDiscoveryLogic(shared_ptr<ndn::Face> face,
+                                               const UpdateCallback& updateCallback)
+  : m_face(face)
+  , m_scheduler(face->getIoService())
+  , m_onUpdate(updateCallback)
+{
+
+  m_face->setInterestFilter(DISCOVERY_PREFIX,
+                            bind(&ChatroomDiscoveryLogic::onDiscoveryInterest,
+                                 this, _1, _2),
+                            bind(&ChatroomDiscoveryLogic::onPrefixRegistrationFailed,
+                                 this, _1));
+}
+
+void
+ChatroomDiscoveryLogic::addLocalChatroom(const ChatroomInfo& chatroom)
+{
+  m_localChatrooms[chatroom.getName()] = chatroom;
+  m_chatrooms[chatroom.getName()] = chatroom; // no need to discover local chatroom
+}
+
+void
+ChatroomDiscoveryLogic::removeLocalChatroom(const Name::Component& chatroomName)
+{
+  m_localChatrooms.erase(chatroomName);
+  m_chatrooms.erase(chatroomName);
+}
+
+void
+ChatroomDiscoveryLogic::sendDiscoveryInterest()
+{
+  Interest discovery(DISCOVERY_PREFIX);
+  discovery.setMustBeFresh(true);
+  discovery.setInterestLifetime(time::milliseconds(10000));
+
+  Exclude exclude;
+  Chatrooms::iterator it;
+  for (it = m_chatrooms.begin(); it != m_chatrooms.end(); ++it) {
+    exclude.excludeOne(it->first);
+  }
+  discovery.setExclude(exclude);
+
+  m_face->expressInterest(discovery,
+                          bind(&ChatroomDiscoveryLogic::onReceiveData, this,
+                               _1, _2, false),
+                          bind(&ChatroomDiscoveryLogic::onDiscoveryInterestTimeout, this,
+                               _1));
+
+}
+
+void
+ChatroomDiscoveryLogic::onDiscoveryInterest(const Name& name, const Interest& interest)
+{
+  if (m_localChatrooms.empty())
+    return;
+
+  if (interest.getName() == DISCOVERY_PREFIX) {
+    // discovery
+    for (Chatrooms::const_iterator it = m_localChatrooms.begin();
+         it != m_localChatrooms.end(); ++it) {
+
+      if (!interest.getExclude().empty() &&
+          interest.getExclude().isExcluded(it->first))
+        continue;
+
+
+      Name dataName(interest.getName());
+      dataName.append(it->first);
+
+      shared_ptr<Data> chatroomInfo = make_shared<Data>(dataName);
+      chatroomInfo->setFreshnessPeriod(time::seconds(10));
+      chatroomInfo->setContent(it->second.wireEncode());
+
+      m_keyChain.sign(*chatroomInfo);
+      m_face->put(*chatroomInfo);
+      break;
+    }
+    return;
+  }
+
+  if (DISCOVERY_PREFIX.isPrefixOf(interest.getName()) &&
+      interest.getName().size() == REFRESHING_INTEREST_NAME_SIZE) {
+    // refreshing
+    Chatrooms::const_iterator it =
+      m_localChatrooms.find(interest.getName().get(OFFSET_CHATROOM_NAME));
+
+    if (it == m_localChatrooms.end())
+      return;
+
+    shared_ptr<Data> chatroomInfo = make_shared<Data>(interest.getName());
+    chatroomInfo->setFreshnessPeriod(time::seconds(10));
+    chatroomInfo->setContent(it->second.wireEncode());
+
+    m_keyChain.sign(*chatroomInfo);
+    m_face->put(*chatroomInfo);
+
+    return;
+  }
+}
+
+void
+ChatroomDiscoveryLogic::onPrefixRegistrationFailed(const Name& name)
+{
+}
+
+void
+ChatroomDiscoveryLogic::refreshChatroom(const Name::Component& chatroomName)
+{
+  Name name = DISCOVERY_PREFIX;
+  name.append(chatroomName);
+
+  BOOST_ASSERT(name.size() == REFRESHING_INTEREST_NAME_SIZE);
+
+  Interest discovery(name);
+  discovery.setMustBeFresh(true);
+  discovery.setInterestLifetime(time::milliseconds(10000));
+
+  m_face->expressInterest(discovery,
+                          bind(&ChatroomDiscoveryLogic::onReceiveData, this,
+                               _1, _2, true),
+                          bind(&ChatroomDiscoveryLogic::onRefreshingInterestTimeout, this,
+                               _1));
+
+}
+
+void
+ChatroomDiscoveryLogic::onReceiveData(const ndn::Interest& interest,
+                                      const ndn::Data& data,
+                                      const bool isRefreshing)
+{
+  Name::Component chatroomName = data.getName().get(OFFSET_CHATROOM_NAME);
+
+  ChatroomInfo chatroom;
+  chatroom.wireDecode(data.getContent().blockFromValue());
+  chatroom.setName(chatroomName);
+
+  // Tmp Disabled
+  // if (chatroom.getTrustModel() == ChatroomInfo::TRUST_MODEL_WEBOFTRUST)
+  //   addContacts(chatroom);
+
+
+  m_chatrooms[chatroomName] = chatroom;
+  m_onUpdate(chatroom, true); //add
+
+  time::milliseconds refreshingTime;
+  if (data.getFreshnessPeriod() > DEFAULT_REFRESHING_TIMER)
+    refreshingTime = data.getFreshnessPeriod();
+  else
+    refreshingTime = DEFAULT_REFRESHING_TIMER;
+
+  m_scheduler.scheduleEvent(refreshingTime,
+                            bind(&ChatroomDiscoveryLogic::refreshChatroom, this, chatroomName));
+
+  if (!isRefreshing)
+    sendDiscoveryInterest();
+}
+
+void
+ChatroomDiscoveryLogic::onRefreshingInterestTimeout(const ndn::Interest& interest)
+{
+  Name::Component chatroomName = interest.getName().get(OFFSET_CHATROOM_NAME);
+
+  Chatrooms::iterator it = m_chatrooms.find(chatroomName);
+  if (it != m_chatrooms.end()) {
+    m_onUpdate(it->second, false); //delete
+    m_chatrooms.erase(it);
+  }
+}
+
+
+void
+ChatroomDiscoveryLogic::onDiscoveryInterestTimeout(const Interest& interest)
+{
+  m_scheduler.scheduleEvent(m_discoveryInterval,
+                            bind(&ChatroomDiscoveryLogic::sendDiscoveryInterest, this));
+}
+
+void
+ChatroomDiscoveryLogic::addContacts(ChatroomInfo& chatroom)
+{
+  // Tmp Disabled
+
+  // std::vector<Name>::const_iterator nameIt;
+  // std::vector<shared_ptr<Contact> >::iterator contactIt;
+  // ContactList contactList;
+  // m_contactManager->getContactList(contactList);
+
+  // for (contactIt = contactList.begin();
+  //      contactIt != contactList.end(); ++contactIt) {
+  //   nameIt = std::find(chatroom.getParticipants().begin(),
+  //                 chatroom.getParticipants().end(), (*contactIt)->getNameSpace());
+  //   if (nameIt != chatroom.getParticipants().end())
+  //     chatroom.addContact(*nameIt);
+  // }
+}
+
+
+} //namespace chronos
diff --git a/src/chatroom-discovery-logic.hpp b/src/chatroom-discovery-logic.hpp
new file mode 100644
index 0000000..32b9c89
--- /dev/null
+++ b/src/chatroom-discovery-logic.hpp
@@ -0,0 +1,118 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *                     Yingdi Yu
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Mengjin Yan <jane.yan0129@gmail.com>
+ * Author: Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#ifndef CHRONOCHAT_CHATROOM_DISCOVERY_LOGIC_HPP
+#define CHRONOCHAT_CHATROOM_DISCOVERY_LOGIC_HPP
+
+#include "chatroom-info.hpp"
+#include <ndn-cxx/util/scheduler.hpp>
+
+namespace chronos {
+
+class ChatroomDiscoveryLogic : noncopyable
+{
+public:
+  typedef function<void(const ChatroomInfo& chatroomName, bool isAdd)> UpdateCallback;
+
+  typedef std::map<Name::Component, chronos::ChatroomInfo> Chatrooms;
+  static const size_t OFFSET_CHATROOM_NAME;
+  static const size_t DISCOVERY_INTEREST_NAME_SIZE;
+  static const size_t REFRESHING_INTEREST_NAME_SIZE;
+  static const time::milliseconds DEFAULT_REFRESHING_TIMER;
+  static const Name DISCOVERY_PREFIX;
+
+public:
+  explicit
+  ChatroomDiscoveryLogic(shared_ptr<ndn::Face> face,
+                         const UpdateCallback& updateCallback);
+
+
+  /** \brief obtain the ongoing chatroom list
+   */
+  const Chatrooms&
+  getChatrooms() const;
+
+  /** \brief add a local chatroom in the ongoing chatroom list
+   */
+  void
+  addLocalChatroom(const ChatroomInfo& chatroom);
+
+  void
+  removeLocalChatroom(const Name::Component& chatroomName);
+
+  /** \brief send discovery interest
+   */
+  void
+  sendDiscoveryInterest();
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  /**
+   */
+  void
+  onDiscoveryInterest(const Name& name, const Interest& interest);
+
+  /**
+   */
+  void
+  onPrefixRegistrationFailed(const Name& name);
+
+  /** \brief schedule another discovery
+   */
+  void
+  onDiscoveryInterestTimeout(const Interest& interest);
+
+  /** \brief send interest to find if the certain chatroom exists
+   */
+  void
+  refreshChatroom(const Name::Component& chatroomName);
+
+  /** \brief copy the contact from the participants of the chatroom to contacts of the chatroom
+   */
+  void
+  addContacts(ChatroomInfo& chatroom);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+
+  /** \brief erase the chatroom from the ongoing chatroom list
+   */
+  void
+  onRefreshingInterestTimeout(const Interest& interest);
+
+  /** \brief operate the chatroom data and schedule discovery and refreshing process
+   */
+  void
+  onReceiveData(const ndn::Interest& interest, const ndn::Data& data, const bool isRefreshing);
+
+private:
+  static const time::seconds m_discoveryInterval;
+
+  shared_ptr<ndn::Face> m_face;
+
+  ndn::KeyChain m_keyChain;
+
+  Chatrooms m_chatrooms;
+  Chatrooms m_localChatrooms;
+
+  ndn::Scheduler m_scheduler;
+
+  UpdateCallback m_onUpdate;
+
+};
+
+inline const ChatroomDiscoveryLogic::Chatrooms&
+ChatroomDiscoveryLogic::getChatrooms() const
+{
+  return m_chatrooms;
+}
+
+} // namespace chronos
+
+#endif // CHRONOCHAT_CHATROOM_DISCOVERY_LOGIC_HPP
diff --git a/src/chatroom-info.cpp b/src/chatroom-info.cpp
new file mode 100644
index 0000000..2b23719
--- /dev/null
+++ b/src/chatroom-info.cpp
@@ -0,0 +1,136 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *                     Yingdi Yu
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Mengjin Yan <jane.yan0129@gmail.com>
+ * Author: Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+#include "chatroom-info.hpp"
+
+namespace chronos {
+
+ChatroomInfo::ChatroomInfo()
+{
+}
+
+ChatroomInfo::ChatroomInfo(const Block& chatroomWire)
+{
+  this->wireDecode(chatroomWire);
+}
+
+template<bool T>
+size_t
+ChatroomInfo::wireEncode(ndn::EncodingImpl<T>& block) const
+{
+  size_t totalLength = 0;
+
+  //Chatroom := CHATROOM-TYPE TLV-LENGTH
+  //              TrustModel
+  //              Participant+
+
+  //Participants
+  for (std::vector<Name>::const_reverse_iterator it = m_participants.rbegin();
+       it != m_participants.rend(); ++it) {
+    size_t entryLength = 0;
+
+    entryLength += it->wireEncode(block);
+    entryLength += block.prependVarNumber(entryLength);
+    entryLength += block.prependVarNumber(tlv::PARTICIPANT);
+    totalLength += entryLength;
+  }
+
+  //TrustModel
+  totalLength += prependNonNegativeIntegerBlock(block, tlv::TRUSTMODEL, m_trustModel);
+
+  //type = TYPE_CHATROOM;
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(tlv::CHATROOM);
+
+  return totalLength;
+}
+
+const Block&
+ChatroomInfo::wireEncode() const
+{
+  ndn::EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  ndn::EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  m_wire.parse();
+
+  return m_wire;
+}
+
+void
+ChatroomInfo::wireDecode(const Block& chatroomWire)
+{
+  m_wire = chatroomWire;
+  m_wire.parse();
+
+  m_participants.clear();
+
+  //Chatroom := CHATROOM-TYPE TLV-LENGTH
+  //              TrustModel
+  //              Participant+
+
+  if (m_wire.type() != tlv::CHATROOM)
+    throw Error("Unexpected TLV number when decoding chatroom packet");
+
+  Block::element_const_iterator i = m_wire.elements_begin();
+
+  //TrustModel
+  if (i == m_wire.elements_end() || i->type() != tlv::TRUSTMODEL)
+    throw Error("Missing TrustModel");
+  m_trustModel =
+      static_cast<TrustModel>(readNonNegativeInteger(*i));
+
+  ++i;
+
+  //Participants
+  for (; i != m_wire.elements_end() && i->type() == tlv::PARTICIPANT; ++i) {
+    Name name;
+    name.wireDecode(i->blockFromValue());
+    m_participants.push_back(name);
+  }
+  if (m_participants.empty())
+    throw Error("Missing Participant");
+  if (i != m_wire.elements_end()) {
+    throw Error("Unexpected element");
+  }
+}
+
+void
+ChatroomInfo::addParticipant(const Name& participant)
+{
+  m_wire.reset();
+  m_participants.push_back(participant);
+}
+
+void
+ChatroomInfo::addContact(const Name& contact)
+{
+  m_wire.reset();
+  m_contacts.push_back(contact);
+}
+
+void
+ChatroomInfo::setName(const Name::Component& name)
+{
+  m_wire.reset();
+  m_name = name;
+}
+
+void
+ChatroomInfo::setTrustModel(const TrustModel trustModel)
+{
+  m_wire.reset();
+  m_trustModel = trustModel;
+}
+
+} //namespace chronos
diff --git a/src/chatroom-info.hpp b/src/chatroom-info.hpp
new file mode 100644
index 0000000..e182713
--- /dev/null
+++ b/src/chatroom-info.hpp
@@ -0,0 +1,135 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *                     Yingdi Yu
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Mengjin Yan <jane.yan0129@gmail.com>
+ * Author: Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+#ifndef CHRONOCHAT_CHATROOM_INFO_HPP
+#define CHRONOCHAT_CHATROOM_INFO_HPP
+
+#include "common.hpp"
+#include "chatroom-tlv.hpp"
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/name-component.hpp>
+#include <ndn-cxx/util/time.hpp>
+#include <ndn-cxx/util/concepts.hpp>
+#include <ndn-cxx/encoding/block.hpp>
+#include <ndn-cxx/encoding/encoding-buffer.hpp>
+#include <ndn-cxx/exclude.hpp>
+#include <boost/concept_check.hpp>
+
+namespace chronos {
+
+/** \brief store a chatroom's information with encode and decode method.
+    \sa docs/design.rst
+ */
+
+class ChatroomInfo
+{
+
+public:
+
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+
+  enum TrustModel {
+    TRUST_MODEL_HIERARCHICAL = 2,
+    TRUST_MODEL_WEBOFTRUST = 1,
+    TRUST_MODEL_NONE = 0
+  };
+
+public:
+
+  ChatroomInfo();
+
+  explicit
+  ChatroomInfo(const Block& chatroomWire);
+
+  const Block&
+  wireEncode() const;
+
+  void
+  wireDecode(const Block& chatroomWire);
+
+  const Name::Component&
+  getName() const;
+
+  void
+  setName(const Name::Component& name);
+
+  const std::vector<Name>&
+  getParticipants() const;
+
+  void
+  addParticipant(const Name& participant);
+
+  const std::vector<Name>&
+  getContacts() const;
+
+  void
+  addContact(const Name& contact);
+
+  const TrustModel
+  getTrustModel() const;
+
+  void
+  setTrustModel(const TrustModel trustModel);
+
+private:
+  template<bool T>
+  size_t
+  wireEncode(ndn::EncodingImpl<T>& block) const;
+
+private:
+  mutable Block m_wire;
+  Name::Component m_name;
+  std::vector<Name> m_participants;
+  TrustModel m_trustModel;
+  std::vector<Name> m_contacts;
+
+};
+
+inline const Name::Component&
+ChatroomInfo::getName() const
+{
+  return m_name;
+}
+
+inline const std::vector<Name>&
+ChatroomInfo::getParticipants() const
+{
+  return m_participants;
+}
+
+inline const std::vector<Name>&
+ChatroomInfo::getContacts() const
+{
+  return m_contacts;
+}
+
+inline const ChatroomInfo::TrustModel
+ChatroomInfo::getTrustModel() const
+{
+  return m_trustModel;
+}
+
+BOOST_CONCEPT_ASSERT((ndn::WireEncodable<ChatroomInfo>));
+BOOST_CONCEPT_ASSERT((ndn::WireDecodable<ChatroomInfo>));
+
+
+} // namespace chronos
+
+#endif //CHRONOCHAT_CHATROOM_INFO_HPP
diff --git a/src/chatroom-tlv.hpp b/src/chatroom-tlv.hpp
new file mode 100644
index 0000000..954fbb5
--- /dev/null
+++ b/src/chatroom-tlv.hpp
@@ -0,0 +1,18 @@
+#ifndef CHRONOCHAT_CHATROOM_TLV_HPP
+#define CHRONOCHAT_CHATROOM_TLV_HPP
+
+namespace chronos {
+
+namespace tlv {
+
+enum {
+  PARTICIPANT = 128,
+  CHATROOM = 129,
+  TRUSTMODEL = 130
+};
+
+} //namespace tlv
+
+} //namespace chronos
+
+#endif //CHRONOCHAT_CHATROOM_TLV_HPP
diff --git a/src/controller.hpp b/src/controller.hpp
index d445171..6641b20 100644
--- a/src/controller.hpp
+++ b/src/controller.hpp
@@ -79,7 +79,7 @@
   onLocalPrefixTimeout(const Interest& interest);
 
   void
-  onInvitationInterestWrapper(const Name& prefix, const Interest& interest,
+  onInvitationInterestWrapper(const ndn::Name& prefix, const ndn::Interest& interest,
                               size_t routingPrefixOffset);
 
   void
@@ -182,7 +182,8 @@
   onError(const QString& msg);
 
   void
-  onInvitationInterest(const Name& prefix, const Interest& interest, size_t routingPrefixOffset);
+  onInvitationInterest(const ndn::Name& prefix, const ndn::Interest& interest,
+                       size_t routingPrefixOffset);
 
 private: // private member
   typedef std::map<std::string, QAction*> ChatActionList;
diff --git a/src/digest-tree-scene.cpp b/src/digest-tree-scene.cpp
index a253384..07e5dea 100644
--- a/src/digest-tree-scene.cpp
+++ b/src/digest-tree-scene.cpp
@@ -47,7 +47,7 @@
       // std::cout << "processUpdate v[" << i << "]: " << prefix.toStdString() << std::endl;
       rePlot = true;
       DisplayUserPtr p(new DisplayUser());
-      time_t tempTime = ::time(NULL) - FRESHNESS + 1;
+      time_t tempTime = ::time(0) + 1;
       p->setReceived(tempTime);
       p->setPrefix(prefix);
       p->setSeq(v[i].high);
@@ -115,7 +115,7 @@
     // std::cout << "Updating for prefix = " << prefix.toStdString() <<
     // " nick = " << nick.toStdString() << std::endl;
     DisplayUserPtr p = it.value();
-    p->setReceived(::time(NULL));
+    p->setReceived(::time(0) + 1);
     if (nick != p->getNick()) {
       // std::cout << "old nick = " << p->getNick().toStdString() << std::endl;
       p->setNick(nick);
diff --git a/src/digest-tree-scene.hpp b/src/digest-tree-scene.hpp
index 434ee07..138f7f4 100644
--- a/src/digest-tree-scene.hpp
+++ b/src/digest-tree-scene.hpp
@@ -34,16 +34,17 @@
 class User;
 class DisplayUser;
 typedef boost::shared_ptr<DisplayUser> DisplayUserPtr;
+typedef QMap<QString, DisplayUserPtr> Roster;
+typedef QMap<QString, DisplayUserPtr>::iterator Roster_iterator;
+typedef QMapIterator<QString, DisplayUserPtr> RosterIterator;
+
 static DisplayUserPtr DisplayUserNullPtr;
 
+
 class DigestTreeScene : public QGraphicsScene
 {
   Q_OBJECT
 
-typedef QMap<QString, DisplayUserPtr> Roster;
-typedef QMap<QString, DisplayUserPtr>::iterator Roster_iterator;
-typedef QMapIterator<QString, DisplayUserPtr> RosterIterator;
-
 public:
   DigestTreeScene(QWidget *parent = 0);
 
diff --git a/test/dummy-client-face.hpp b/test/dummy-client-face.hpp
new file mode 100644
index 0000000..52e871e
--- /dev/null
+++ b/test/dummy-client-face.hpp
@@ -0,0 +1,141 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2014 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_TESTS_UNIT_TESTS_DUMMY_CLIENT_FACE_HPP
+#define NDN_TESTS_UNIT_TESTS_DUMMY_CLIENT_FACE_HPP
+
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/transport/transport.hpp>
+
+namespace chronos {
+namespace test {
+
+class DummyClientTransport : public ndn::Transport
+{
+public:
+  void
+  receive(const Block& block)
+  {
+    if (static_cast<bool>(m_receiveCallback))
+      m_receiveCallback(block);
+  }
+
+  virtual void
+  close()
+  {
+  }
+
+  virtual void
+  pause()
+  {
+  }
+
+  virtual void
+  resume()
+  {
+  }
+
+  virtual void
+  send(const Block& wire)
+  {
+    if (wire.type() == tlv::Interest) {
+      m_sentInterests->push_back(Interest(wire));
+    }
+    else if (wire.type() == tlv::Data) {
+      m_sentDatas->push_back(Data(wire));
+    }
+  }
+
+  virtual void
+  send(const Block& header, const Block& payload)
+  {
+    this->send(payload);
+  }
+
+public:
+  std::vector<Interest>* m_sentInterests;
+  std::vector<Data>*     m_sentDatas;
+};
+
+
+/** \brief a client-side face for unit testing
+ */
+class DummyClientFace : public ndn::Face
+{
+public:
+  explicit
+  DummyClientFace(shared_ptr<DummyClientTransport> transport)
+    : Face(transport)
+    , m_transport(transport)
+  {
+    m_transport->m_sentInterests = &m_sentInterests;
+    m_transport->m_sentDatas     = &m_sentDatas;
+  }
+
+  DummyClientFace(shared_ptr<DummyClientTransport> transport, boost::asio::io_service& ioService)
+    : Face(transport, ioService)
+    , m_transport(transport)
+  {
+    m_transport->m_sentInterests = &m_sentInterests;
+    m_transport->m_sentDatas     = &m_sentDatas;
+  }
+
+  /** \brief cause the Face to receive a packet
+   */
+  template<typename Packet>
+  void
+  receive(const Packet& packet)
+  {
+    m_transport->receive(packet.wireEncode());
+  }
+
+public:
+  /** \brief sent Interests
+   *  \note After .expressInterest, .processEvents must be called before
+   *        the Interest would show up here.
+   */
+  std::vector<Interest> m_sentInterests;
+  /** \brief sent Datas
+   *  \note After .put, .processEvents must be called before
+   *        the Interest would show up here.
+   */
+  std::vector<Data>     m_sentDatas;
+
+private:
+  shared_ptr<DummyClientTransport> m_transport;
+};
+
+inline shared_ptr<DummyClientFace>
+makeDummyClientFace()
+{
+  return make_shared<DummyClientFace>(make_shared<DummyClientTransport>());
+}
+
+inline shared_ptr<DummyClientFace>
+makeDummyClientFace(boost::asio::io_service& ioService)
+{
+  return make_shared<DummyClientFace>(make_shared<DummyClientTransport>(), ref(ioService));
+}
+
+} // namespace tests
+} // namespace ndn
+
+#endif // NDN_TESTS_UNIT_TESTS_DUMMY_CLIENT_FACE_HPP
diff --git a/test/test-chatroom-discovery-logic.cpp b/test/test-chatroom-discovery-logic.cpp
new file mode 100644
index 0000000..1714e8a
--- /dev/null
+++ b/test/test-chatroom-discovery-logic.cpp
@@ -0,0 +1,328 @@
+#include "chatroom-discovery-logic.hpp"
+#include <boost/test/unit_test.hpp>
+#include "dummy-client-face.hpp"
+
+namespace chronos {
+
+namespace test {
+
+using std::string;
+
+BOOST_AUTO_TEST_SUITE(TestChatroomDiscoveryLogic)
+
+class Fixture
+{
+public:
+  Fixture()
+    : face(makeDummyClientFace())
+  {
+  }
+
+  void
+  update(const ChatroomInfo& chatroomName, bool isAdd)
+  {
+  }
+
+public:
+  shared_ptr<DummyClientFace> face;
+};
+
+BOOST_FIXTURE_TEST_CASE(AddLocalChatroom, Fixture)
+{
+  ChatroomDiscoveryLogic chatroomDiscoveryLogic(face, bind(&Fixture::update, this, _1, _2));
+
+  ChatroomInfo chatroom;
+  chatroom.setName(ndn::Name::Component("lunch-talk"));
+  chatroom.addParticipant(Name("/ndn/ucla/ymj"));
+  chatroom.addParticipant(Name("/ndn/ucla/alice"));
+  chatroom.setTrustModel(ChatroomInfo::TRUST_MODEL_WEBOFTRUST);
+  chatroomDiscoveryLogic.addLocalChatroom(chatroom);
+
+  ndn::Name::Component chatroomName("lunch-talk");
+
+  BOOST_CHECK_EQUAL(chatroomDiscoveryLogic.getChatrooms().size(), 1);
+  BOOST_CHECK_EQUAL(chatroom.getName(),
+                    chatroomDiscoveryLogic.getChatrooms().find(chatroomName)
+                    ->second.getName());
+  BOOST_CHECK_EQUAL(chatroom.getParticipants().size(),
+                    chatroomDiscoveryLogic
+                    .getChatrooms().find(chatroomName)->second.getParticipants().size());
+  BOOST_CHECK_EQUAL(chatroom.getParticipants()[0].toUri(),
+                    chatroomDiscoveryLogic
+                    .getChatrooms().find(chatroomName)->second
+                    .getParticipants()[0].toUri());
+  BOOST_CHECK_EQUAL(chatroom.getParticipants()[1].toUri(),
+                    chatroomDiscoveryLogic
+                    .getChatrooms().find(chatroomName)->second
+                    .getParticipants()[1].toUri());
+  BOOST_CHECK_EQUAL(chatroom.getTrustModel(),
+                    chatroomDiscoveryLogic
+                    .getChatrooms().find(chatroomName)->second.getTrustModel());
+}
+
+BOOST_FIXTURE_TEST_CASE(RemoveLocalChatroom, Fixture)
+{
+  ChatroomDiscoveryLogic chatroomDiscoveryLogic(face, bind(&Fixture::update, this, _1, _2));
+
+  ChatroomInfo chatroom;
+  chatroom.setName(ndn::Name::Component("lunch-talk"));
+  chatroom.addParticipant(Name("/ndn/ucla/ymj"));
+  chatroom.addParticipant(Name("/ndn/ucla/alice"));
+  chatroom.setTrustModel(ChatroomInfo::TRUST_MODEL_WEBOFTRUST);
+  chatroomDiscoveryLogic.addLocalChatroom(chatroom);
+
+  ndn::Name::Component chatroomName1("lunch-talk");
+  ndn::Name::Component chatroomName2("supper-talk");
+
+  chatroomDiscoveryLogic.removeLocalChatroom(chatroomName2);
+
+  BOOST_CHECK_EQUAL(chatroomDiscoveryLogic.getChatrooms().size(), 1);
+  BOOST_CHECK_EQUAL(chatroom.getName(),
+                    chatroomDiscoveryLogic.getChatrooms().find(chatroomName1)
+                    ->second.getName());
+  BOOST_CHECK_EQUAL(chatroom.getParticipants().size(),
+                    chatroomDiscoveryLogic
+                    .getChatrooms().find(chatroomName1)->second.getParticipants().size());
+  BOOST_CHECK_EQUAL(chatroom.getParticipants()[0].toUri(),
+                    chatroomDiscoveryLogic
+                    .getChatrooms().find(chatroomName1)->second
+                    .getParticipants()[0].toUri());
+  BOOST_CHECK_EQUAL(chatroom.getParticipants()[1].toUri(),
+                    chatroomDiscoveryLogic
+                    .getChatrooms().find(chatroomName1)->second
+                    .getParticipants()[1].toUri());
+  BOOST_CHECK_EQUAL(chatroom.getTrustModel(),
+                    chatroomDiscoveryLogic
+                    .getChatrooms().find(chatroomName1)->second.getTrustModel());
+
+  chatroomDiscoveryLogic.removeLocalChatroom(chatroomName1);
+  BOOST_CHECK_EQUAL(chatroomDiscoveryLogic.getChatrooms().size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(sendChatroomInterestFunction, Fixture)
+{
+  ChatroomDiscoveryLogic chatroomDiscoveryLogic(face, bind(&Fixture::update, this, _1, _2));
+  chatroomDiscoveryLogic.sendDiscoveryInterest();
+
+  face->processEvents(time::milliseconds(100));
+
+  //with no exclude filter
+  BOOST_CHECK_EQUAL(face->m_sentInterests.size(), 2);//first is nfd register interest
+  BOOST_CHECK_EQUAL(face->m_sentDatas.size(), 0);
+  BOOST_CHECK_EQUAL(face->m_sentInterests[1].getName().toUri(),
+                    ChatroomDiscoveryLogic::DISCOVERY_PREFIX.toUri());
+  BOOST_CHECK_EQUAL(face->m_sentInterests[1].getExclude().size(), 0);
+
+  face->m_sentInterests.clear();
+
+  //with exclude filter
+  ChatroomInfo chatroom;
+  chatroom.setName(ndn::Name::Component("lunch-talk"));
+  chatroom.addParticipant(Name("/ndn/ucla/ymj"));
+  chatroom.addParticipant(Name("/ndn/ucla/alice"));
+  chatroom.setTrustModel(ChatroomInfo::TRUST_MODEL_WEBOFTRUST);
+  chatroomDiscoveryLogic.addLocalChatroom(chatroom);
+
+  chatroomDiscoveryLogic.sendDiscoveryInterest();
+  face->processEvents(time::milliseconds(100));
+
+  BOOST_CHECK_EQUAL(face->m_sentInterests.size(), 1);
+  BOOST_CHECK_EQUAL(face->m_sentDatas.size(), 0);
+  BOOST_CHECK_EQUAL(face->m_sentInterests[0].getName().toUri(),
+                    ChatroomDiscoveryLogic::DISCOVERY_PREFIX.toUri());
+  BOOST_CHECK_EQUAL(face->m_sentInterests[0].getExclude().size(), 1);
+  BOOST_CHECK_EQUAL(face->m_sentInterests[0].getExclude().toUri(), "lunch-talk");
+}
+
+BOOST_FIXTURE_TEST_CASE(onDiscoveryInterest, Fixture)
+{
+  face->m_sentInterests.clear();
+  face->m_sentDatas.clear();
+
+  Interest discovery(ChatroomDiscoveryLogic::DISCOVERY_PREFIX);
+  discovery.setMustBeFresh(true);
+  discovery.setInterestLifetime(time::milliseconds(10000));
+
+  Exclude exclude;
+  exclude.excludeOne(ndn::Name::Component("lunch-talk"));
+  discovery.setExclude(exclude);
+
+  ChatroomDiscoveryLogic chatroomDiscoveryLogic(face, bind(&Fixture::update, this, _1, _2));
+
+  ChatroomInfo chatroom1;
+  chatroom1.setName(ndn::Name::Component("lunch-talk"));
+  chatroom1.addParticipant(Name("/ndn/ucla/ymj"));
+  chatroom1.addParticipant(Name("/ndn/ucla/alice"));
+  chatroom1.setTrustModel(ChatroomInfo::TRUST_MODEL_WEBOFTRUST);
+  chatroomDiscoveryLogic.addLocalChatroom(chatroom1);
+
+  ChatroomInfo chatroom2;
+  chatroom2.setName(ndn::Name::Component("supper-talk"));
+  chatroom2.addParticipant(Name("/ndn/ucla/bob"));
+  chatroom2.addParticipant(Name("/ndn/ucla/peter"));
+  chatroom2.setTrustModel(ChatroomInfo::TRUST_MODEL_HIERARCHICAL);
+  chatroomDiscoveryLogic.addLocalChatroom(chatroom2);
+
+  //discovery
+  chatroomDiscoveryLogic.onDiscoveryInterest(ChatroomDiscoveryLogic::DISCOVERY_PREFIX, discovery);
+
+  face->processEvents(time::milliseconds(100));
+
+  BOOST_CHECK_EQUAL(face->m_sentInterests.size(), 1);//the interest is for nfd register
+  BOOST_CHECK_EQUAL(face->m_sentDatas.size(), 1);
+
+  ChatroomInfo chatroom;
+  chatroom.wireDecode(face->m_sentDatas[0].getContent().blockFromValue());
+  chatroom.setName(face->m_sentDatas[0].getName()
+                                      .get(ChatroomDiscoveryLogic::OFFSET_CHATROOM_NAME));
+
+  BOOST_CHECK_EQUAL(chatroom.getName(), chatroom2.getName());
+  BOOST_CHECK_EQUAL(chatroom.getParticipants().size(),
+                    chatroom2.getParticipants().size());
+  BOOST_CHECK_EQUAL(chatroom.getParticipants()[0].toUri(),
+                    chatroom2.getParticipants()[0].toUri());
+  BOOST_CHECK_EQUAL(chatroom.getParticipants()[1].toUri(),
+                    chatroom2.getParticipants()[1].toUri());
+  BOOST_CHECK_EQUAL(chatroom.getTrustModel(),
+                    chatroom2.getTrustModel());
+
+  //refreshing
+  face->m_sentInterests.clear();
+  face->m_sentDatas.clear();
+
+  Name name = ChatroomDiscoveryLogic::DISCOVERY_PREFIX;
+  name.append(Name("supper-talk"));
+
+  Interest refreshing(name);
+  refreshing.setMustBeFresh(true);
+  refreshing.setInterestLifetime(time::milliseconds(10000));
+
+  chatroomDiscoveryLogic.onDiscoveryInterest(name, refreshing);
+  face->processEvents(time::milliseconds(100));
+
+  BOOST_CHECK_EQUAL(face->m_sentInterests.size(), 0);
+  BOOST_CHECK_EQUAL(face->m_sentDatas.size(), 1);
+
+  chatroom.wireDecode(face->m_sentDatas[0].getContent().blockFromValue());
+  chatroom.setName(face->m_sentDatas[0].getName()
+                                      .get(ChatroomDiscoveryLogic::OFFSET_CHATROOM_NAME));
+
+  BOOST_CHECK_EQUAL(chatroom.getName(), chatroom2.getName());
+  BOOST_CHECK_EQUAL(chatroom.getParticipants().size(),
+                    chatroom2.getParticipants().size());
+  BOOST_CHECK_EQUAL(chatroom.getParticipants()[0].toUri(),
+                    chatroom2.getParticipants()[0].toUri());
+  BOOST_CHECK_EQUAL(chatroom.getParticipants()[1].toUri(),
+                    chatroom2.getParticipants()[1].toUri());
+  BOOST_CHECK_EQUAL(chatroom.getTrustModel(),
+                    chatroom2.getTrustModel());
+}
+
+BOOST_FIXTURE_TEST_CASE(refreshChatroomFunction, Fixture)
+{
+  ChatroomDiscoveryLogic chatroomDiscoveryLogic(face, bind(&Fixture::update, this, _1, _2));
+
+  ChatroomInfo chatroom1;
+  chatroom1.setName(ndn::Name::Component("lunch-talk"));
+  chatroom1.addParticipant(Name("/ndn/ucla/ymj"));
+  chatroom1.addParticipant(Name("/ndn/ucla/alice"));
+  chatroom1.setTrustModel(ChatroomInfo::TRUST_MODEL_WEBOFTRUST);
+  chatroomDiscoveryLogic.addLocalChatroom(chatroom1);
+
+  chatroomDiscoveryLogic.refreshChatroom(ndn::Name::Component("lunch-talk"));
+  face->processEvents(time::milliseconds(100));
+
+  BOOST_CHECK_EQUAL(face->m_sentInterests.size(), 2);
+  BOOST_CHECK_EQUAL(face->m_sentDatas.size(), 0);
+
+  Name name = ChatroomDiscoveryLogic::DISCOVERY_PREFIX;
+  name.append(Name("lunch-talk"));
+
+  BOOST_CHECK_EQUAL(face->m_sentInterests[1].getName().toUri(), name.toUri());
+  BOOST_CHECK_EQUAL(face->m_sentInterests[1].getExclude().size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(onReceiveData, Fixture)
+{
+  ChatroomDiscoveryLogic chatroomDiscoveryLogic(face, bind(&Fixture::update, this, _1, _2));
+
+  //discovery
+  Interest discovery(ChatroomDiscoveryLogic::DISCOVERY_PREFIX);
+  discovery.setMustBeFresh(true);
+  discovery.setInterestLifetime(time::milliseconds(10000));
+
+  Name dataName(ChatroomDiscoveryLogic::DISCOVERY_PREFIX);
+  dataName.append(ndn::Name::Component("lunch-talk"));
+
+  ChatroomInfo chatroom1;
+  chatroom1.setName(ndn::Name::Component("lunch-talk"));
+  chatroom1.addParticipant(Name("/ndn/ucla/ymj"));
+  chatroom1.addParticipant(Name("/ndn/ucla/alice"));
+  chatroom1.setTrustModel(ChatroomInfo::TRUST_MODEL_WEBOFTRUST);
+
+  shared_ptr<Data> chatroomInfo = make_shared<Data>(dataName);
+  chatroomInfo->setFreshnessPeriod(time::seconds(10));
+  chatroomInfo->setContent(chatroom1.wireEncode());
+
+  chatroomDiscoveryLogic.onReceiveData(discovery, *chatroomInfo, false);
+
+  face->processEvents(time::milliseconds(1000));
+
+  ndn::Name::Component chatroomName("lunch-talk");
+
+  BOOST_CHECK_EQUAL(chatroomDiscoveryLogic.getChatrooms().size(), 1);
+  BOOST_CHECK_EQUAL(chatroom1.getName(),
+                    chatroomDiscoveryLogic.getChatrooms().find(chatroomName)
+                    ->second.getName());
+  BOOST_CHECK_EQUAL(chatroom1.getParticipants().size(),
+                    chatroomDiscoveryLogic
+                    .getChatrooms().find(chatroomName)->second.getParticipants().size());
+  BOOST_CHECK_EQUAL(chatroom1.getParticipants()[0].toUri(),
+                    chatroomDiscoveryLogic
+                    .getChatrooms().find(chatroomName)->second
+                    .getParticipants()[0].toUri());
+  BOOST_CHECK_EQUAL(chatroom1.getParticipants()[1].toUri(),
+                    chatroomDiscoveryLogic
+                    .getChatrooms().find(chatroomName)->second
+                    .getParticipants()[1].toUri());
+  BOOST_CHECK_EQUAL(chatroom1.getTrustModel(),
+                    chatroomDiscoveryLogic
+                    .getChatrooms().find(chatroomName)->second.getTrustModel());
+
+  //refreshing
+  Name name = ChatroomDiscoveryLogic::DISCOVERY_PREFIX;
+  name.append(Name("lunch-talk"));
+
+  Interest refreshing(name);
+  refreshing.setMustBeFresh(true);
+  refreshing.setInterestLifetime(time::milliseconds(10000));
+
+  chatroomDiscoveryLogic.onReceiveData(discovery, *chatroomInfo, false);
+
+  face->processEvents(time::milliseconds(1000));
+
+  BOOST_CHECK_EQUAL(chatroomDiscoveryLogic.getChatrooms().size(), 1);
+  BOOST_CHECK_EQUAL(chatroom1.getName(),
+                    chatroomDiscoveryLogic.getChatrooms().find(chatroomName)
+                    ->second.getName());
+  BOOST_CHECK_EQUAL(chatroom1.getParticipants().size(),
+                    chatroomDiscoveryLogic
+                    .getChatrooms().find(chatroomName)->second.getParticipants().size());
+  BOOST_CHECK_EQUAL(chatroom1.getParticipants()[0].toUri(),
+                    chatroomDiscoveryLogic
+                    .getChatrooms().find(chatroomName)->second
+                    .getParticipants()[0].toUri());
+  BOOST_CHECK_EQUAL(chatroom1.getParticipants()[1].toUri(),
+                    chatroomDiscoveryLogic
+                    .getChatrooms().find(chatroomName)->second
+                    .getParticipants()[1].toUri());
+  BOOST_CHECK_EQUAL(chatroom1.getTrustModel(),
+                    chatroomDiscoveryLogic
+                    .getChatrooms().find(chatroomName)->second.getTrustModel());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace test
+
+} // namespace chronos
diff --git a/test/test-chatroom-info.cpp b/test/test-chatroom-info.cpp
new file mode 100644
index 0000000..a1b444e
--- /dev/null
+++ b/test/test-chatroom-info.cpp
@@ -0,0 +1,175 @@
+#include "chatroom-info.hpp"
+#include <boost/test/unit_test.hpp>
+#include <ndn-cxx/encoding/block.hpp>
+
+namespace chronos {
+
+namespace test {
+
+using std::string;
+
+BOOST_AUTO_TEST_SUITE(TestChatroomInfo)
+
+const uint8_t chatroomInfo[] = {
+  0x81, 0x2d, // ChatroomInfo
+    0x82, 0x01, // TrustModel
+      0x01,
+    0x80, 0x14,// Participant1
+      0x07, 0x12,
+        0x08, 0x03,
+          0x6e, 0x64, 0x6e,
+        0x08, 0x04,
+          0x75, 0x63, 0x6c, 0x61,
+        0x08, 0x05,
+          0x61, 0x6c, 0x69, 0x63, 0x65,
+    0x80, 0x12, // Participant2
+      0x07, 0x10,
+        0x08, 0x03,
+          0x6e, 0x64, 0x6e,
+        0x08, 0x04,
+          0x75, 0x63, 0x6c, 0x61,
+        0x08, 0x03,
+          0x79, 0x6d, 0x6a
+};
+
+BOOST_AUTO_TEST_CASE(EncodeChatroom)
+{
+  //Chatroom := CHATROOM-TYPE TLV-LENGTH
+  //              TrustModel
+  //              Participant+
+
+  ChatroomInfo chatroom;
+  chatroom.setName(ndn::Name::Component("lunch-talk"));
+  chatroom.addParticipant(Name("/ndn/ucla/alice"));
+  chatroom.addParticipant(Name("/ndn/ucla/ymj"));
+  chatroom.setTrustModel(ChatroomInfo::TRUST_MODEL_WEBOFTRUST);
+
+  const Block& encoded = chatroom.wireEncode();
+  Block chatroomInfoBlock(chatroomInfo, sizeof(chatroomInfo));
+
+  BOOST_CHECK_EQUAL_COLLECTIONS(chatroomInfoBlock.wire(),
+                                chatroomInfoBlock.wire() + chatroomInfoBlock.size(),
+                                encoded.wire(),
+                                encoded.wire() + encoded.size());
+}
+
+BOOST_AUTO_TEST_CASE(DecodeChatroomCorrect)
+{
+  ChatroomInfo chatroom;
+  chatroom.setName(ndn::Name::Component("lunch-talk"));
+  chatroom.addParticipant(Name("/ndn/ucla/alice"));
+  chatroom.addParticipant(Name("/ndn/ucla/ymj"));
+  chatroom.setTrustModel(ChatroomInfo::TRUST_MODEL_WEBOFTRUST);
+
+  Block chatroomInfoBlock(chatroomInfo, sizeof(chatroomInfo));
+  ChatroomInfo dechatroom;
+  dechatroom.wireDecode(chatroomInfoBlock);
+  dechatroom.setName(ndn::Name::Component("lunch-talk"));
+
+  BOOST_CHECK_EQUAL(chatroom.getName(), dechatroom.getName());
+  BOOST_CHECK_EQUAL(chatroom.getParticipants().size(), dechatroom.getParticipants().size());
+  BOOST_CHECK_EQUAL(chatroom.getParticipants()[0].toUri(), dechatroom.getParticipants()[0].toUri());
+  BOOST_CHECK_EQUAL(chatroom.getParticipants()[1].toUri(), dechatroom.getParticipants()[1].toUri());
+  BOOST_CHECK_EQUAL(chatroom.getTrustModel(), dechatroom.getTrustModel());
+}
+
+BOOST_AUTO_TEST_CASE(DecodeChatroomError)
+{
+  const uint8_t error1[] = {
+    0x80, 0x2d, // Wrong ChatroomInfo Type (0x81, 0x2d)
+      0x82, 0x01, // TrustModel
+        0x01,
+      0x80, 0x14,// Participant1
+        0x07, 0x12,
+          0x08, 0x03,
+            0x6e, 0x64, 0x6e,
+          0x08, 0x04,
+            0x75, 0x63, 0x6c, 0x61,
+          0x08, 0x05,
+            0x61, 0x6c, 0x69, 0x63, 0x65,
+      0x80, 0x12, // Participant2
+        0x07, 0x10,
+          0x08, 0x03,
+            0x6e, 0x64, 0x6e,
+          0x08, 0x04,
+            0x75, 0x63, 0x6c, 0x61,
+          0x08, 0x03,
+            0x79, 0x6d, 0x6a
+  };
+
+  Block errorBlock1(error1, sizeof(error1));
+  BOOST_CHECK_THROW(ChatroomInfo chatroom(errorBlock1), ChatroomInfo::Error);
+
+  const uint8_t error2[] = {
+    0x81, 0x2d, // ChatroomInfo
+      0x81, 0x01, // Wrong TrustModel Type (0x82, 0x01)
+        0x01,
+      0x80, 0x14,// Participant1
+        0x07, 0x12,
+          0x08, 0x03,
+            0x6e, 0x64, 0x6e,
+          0x08, 0x04,
+            0x75, 0x63, 0x6c, 0x61,
+          0x08, 0x05,
+            0x61, 0x6c, 0x69, 0x63, 0x65,
+      0x80, 0x12, // Participant2
+        0x07, 0x10,
+          0x08, 0x03,
+            0x6e, 0x64, 0x6e,
+          0x08, 0x04,
+            0x75, 0x63, 0x6c, 0x61,
+          0x08, 0x03,
+            0x79, 0x6d, 0x6a
+  };
+
+  Block errorBlock2(error2, sizeof(error2));
+  BOOST_CHECK_THROW(ChatroomInfo chatroom(errorBlock2), ChatroomInfo::Error);
+
+  const uint8_t error3[] = {
+    0x81, 0x2d, // ChatroomInfo
+      0x82, 0x01, // TrustModel
+        0x01,
+      0x80, 0x14,// Participant1
+        0x07, 0x12,
+          0x08, 0x03,
+            0x6e, 0x64, 0x6e,
+          0x08, 0x04,
+            0x75, 0x63, 0x6c, 0x61,
+          0x08, 0x05,
+            0x61, 0x6c, 0x69, 0x63, 0x65,
+      0x81, 0x12, // Wrong Participant Type (0x80, 0x12)
+        0x07, 0x10,
+          0x08, 0x03,
+            0x6e, 0x64, 0x6e,
+          0x08, 0x04,
+            0x75, 0x63, 0x6c, 0x61,
+          0x08, 0x03,
+            0x79, 0x6d, 0x6a
+  };
+
+  Block errorBlock3(error3, sizeof(error3));
+  BOOST_CHECK_THROW(ChatroomInfo chatroom(errorBlock3), ChatroomInfo::Error);
+
+  const uint8_t error4[] = {
+    0x81, 0x00 // Empty ChatroomInfo
+  };
+
+  Block errorBlock4(error4, sizeof(error4));
+  BOOST_CHECK_THROW(ChatroomInfo chatroom(errorBlock4), ChatroomInfo::Error);
+
+  const uint8_t error5[] = {
+    0x81, 0x03, // ChatroomInfo
+      0x82, 0x01, // TrustModel
+        0x01
+    //zero Participant
+  };
+
+  Block errorBlock5(error5, sizeof(error5));
+  BOOST_CHECK_THROW(ChatroomInfo chatroom(errorBlock5), ChatroomInfo::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} //namespace test
+
+} //namespace chronos