add logger

Change-Id: If41d6d94086ec9fffb954a52e1529ccfad7f42fe
diff --git a/core/logger.cpp b/core/logger.cpp
new file mode 100644
index 0000000..6a1e67c
--- /dev/null
+++ b/core/logger.cpp
@@ -0,0 +1,306 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014,  Regents of the University of California
+ *
+ * This file is part of NSL (NDN Signature Logger).
+ * See AUTHORS.md for complete list of NSL authors and contributors.
+ *
+ * NSL is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NSL 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NSL, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of nsl authors and contributors.
+ */
+
+#include "logger.hpp"
+#include "tlv.hpp"
+#include "conf/config-file.hpp"
+
+namespace nsl {
+
+const int Logger::N_DATA_FETCHING_RETRIAL = 2;
+
+Logger::Logger(ndn::Face& face, const std::string& configFile)
+  : m_face(face)
+  , m_merkleTree(m_db)
+  , m_validator(m_face)
+{
+  conf::ConfigFile conf(configFile);
+  conf.parse();
+
+  m_loggerName = conf.getLoggerName();
+
+  m_treePrefix = m_loggerName;
+  m_treePrefix.append("tree");
+  m_leafPrefix = m_loggerName;
+  m_leafPrefix.append("leaf");
+  m_logPrefix = m_loggerName;
+  m_logPrefix.append("log");
+
+  m_merkleTree.setLoggerName(m_treePrefix);
+  m_merkleTree.loadPendingSubTrees();
+
+  m_db.open(conf.getDbDir());
+
+  // initialize security environment: keychain
+  initializeKeys();
+
+  // load policy checker
+  m_policyChecker.loadPolicy(conf.getPolicy());
+
+  // load validator rules
+  m_validator.load(conf.getValidatorRule(), conf.getConfFileName());
+
+  // register subtree prefix
+  m_face.setInterestFilter(m_treePrefix,
+                           bind(&Logger::onSubTreeInterest, this, _1, _2),
+                           [] (const Name&) {},
+                           [] (const Name&, const std::string&) {});
+
+  // register leaf prefix
+  m_face.setInterestFilter(m_leafPrefix,
+                           bind(&Logger::onLeafInterest, this, _1, _2),
+                           [] (const Name&) {},
+                           [] (const Name&, const std::string&) {});
+
+  // register log prefix
+  m_face.setInterestFilter(m_logPrefix,
+                           bind(&Logger::onLogRequestInterest, this, _1, _2),
+                           [] (const Name&) {},
+                           [] (const Name&, const std::string&) {});
+}
+
+NonNegativeInteger
+Logger::addSelfSignedCert(ndn::IdentityCertificate& cert, const Timestamp& timestamp)
+{
+  if (!ndn::Validator::verifySignature(cert, cert.getPublicKeyInfo()))
+    throw Error("Not self-signed cert");
+
+  NonNegativeInteger dataSeqNo = m_merkleTree.getNextLeafSeqNo();
+  Leaf leaf(cert.getFullName(), timestamp, dataSeqNo, dataSeqNo, m_leafPrefix);
+
+  if (m_merkleTree.addLeaf(dataSeqNo, leaf.getHash())) {
+    bool result = m_db.insertLeafData(leaf, cert);
+    BOOST_ASSERT(result);
+    m_db.getLeaf(dataSeqNo);
+  }
+  else
+    throw Error("Cannot add cert");
+
+  return dataSeqNo;
+}
+
+void
+Logger::initializeKeys()
+{
+  Name certName = m_keyChain.createIdentity(m_loggerName);
+
+  Name dskKeyName = m_keyChain.generateEcdsaKeyPair(m_loggerName);
+  std::vector<ndn::CertificateSubjectDescription> subjectDescription;
+  auto dskCert =
+    m_keyChain.prepareUnsignedIdentityCertificate(dskKeyName, m_loggerName,
+                                                  time::system_clock::now(),
+                                                  time::system_clock::now() + time::days(1),
+                                                  subjectDescription);
+  m_keyChain.sign(*dskCert, certName);
+  m_keyChain.addCertificate(*dskCert);
+  m_dskCert = dskCert;
+}
+
+void
+Logger::onSubTreeInterest(const ndn::InterestFilter& interestFilter, const Interest& interest)
+{
+  Name interestName = interest.getName();
+
+  size_t levelOffset = m_treePrefix.size();
+  size_t seqNoOffset = m_treePrefix.size() + 1;
+
+  if (interestName.size() < seqNoOffset + 1)
+    return; // interest is too short to answer
+
+  NonNegativeInteger level;
+  NonNegativeInteger seqNo;
+
+  try {
+    seqNo = interestName.get(seqNoOffset).toNumber();
+    level = interestName.get(levelOffset).toNumber();
+  }
+  catch (tlv::Error&) {
+    return;
+  }
+
+  Node::Index peakIndex = SubTreeBinary::toSubTreePeakIndex(Node::Index(seqNo, level));
+  shared_ptr<Data> data;
+
+  data = m_merkleTree.getPendingSubTreeData(peakIndex.level);
+
+  if (data != nullptr && interestName.isPrefixOf(data->getName())) {
+    m_face.put(*data);
+    return;
+  }
+
+  data = m_db.getSubTreeData(peakIndex.level, peakIndex.seqNo);
+
+  if (data != nullptr && interestName.isPrefixOf(data->getName())) {
+    m_face.put(*data);
+    return;
+  }
+}
+
+void
+Logger::onLeafInterest(const ndn::InterestFilter& interestFilter, const Interest& interest)
+{
+  Name interestName = interest.getName();
+
+  size_t seqNoOffset = m_leafPrefix.size();
+  size_t hashOffset = m_leafPrefix.size() + 1;
+
+  if (interestName.size() < seqNoOffset + 1)
+    return; // interest is too short to answer
+
+  NonNegativeInteger seqNo;
+
+  try {
+    seqNo = interestName.get(seqNoOffset).toNumber();
+  }
+  catch (tlv::Error&) {
+    return;
+  }
+  auto result = m_db.getLeaf(seqNo);
+
+  if (result.first != nullptr) {
+    if (interestName.size() >= hashOffset + 1) {
+      ndn::ConstBufferPtr leafHash;
+      try {
+        leafHash = make_shared<ndn::Buffer>(interestName.get(hashOffset).value(),
+                                            interestName.get(hashOffset).value_size());
+        ndn::ConstBufferPtr hash = result.first->getHash();
+        if (*hash != *leafHash)
+          return;
+      }
+      catch (tlv::Error&) {
+        return;
+      }
+    }
+    result.first->setLoggerName(m_leafPrefix);
+    m_face.put(*result.first->encode());
+  }
+}
+
+void
+Logger::onLogRequestInterest(const ndn::InterestFilter& interestFilter, const Interest& interest)
+{
+  m_validator.validate(interest,
+                       bind(&Logger::requestValidatedCallback, this, _1),
+                       [] (const shared_ptr<const Interest>&, const std::string&) {});
+}
+
+void
+Logger::requestValidatedCallback(const shared_ptr<const Interest>& interest)
+{
+  BOOST_ASSERT(interest->getName().size() == (m_logPrefix.size() + 6));
+
+  Name request = interest->getName().getPrefix(-4); // TODO: remove sig-related components
+
+  size_t dataOffset = m_logPrefix.size();
+  size_t signerOffset = m_logPrefix.size() + 1;
+
+  if (request.size() < signerOffset + 1)
+    return; // request is too short to answer
+
+  Name dataName;
+  NonNegativeInteger signerSeqNo;
+  try {
+    dataName.wireDecode(request.get(dataOffset).blockFromValue());
+    signerSeqNo = request.get(signerOffset).toNumber();
+  }
+  catch (tlv::Error&) {
+    return;
+  }
+
+  auto result = m_db.getLeaf(signerSeqNo);
+  if (result.first == nullptr || result.second == nullptr)
+    return;
+
+  Interest dataInterest(dataName);
+  m_face.expressInterest(dataInterest,
+                         bind(&Logger::dataReceivedCallback, this, _1, _2,
+                              signerSeqNo, *interest),
+                         bind(&Logger::dataTimeoutCallback, this, _1,
+                              N_DATA_FETCHING_RETRIAL, signerSeqNo, *interest));
+}
+
+void
+Logger::dataReceivedCallback(const Interest& interest, Data& data,
+                             const NonNegativeInteger& signerSeqNo,
+                             const Interest& reqInterest)
+{
+  auto result = m_db.getLeaf(signerSeqNo);
+  BOOST_ASSERT(result.first != nullptr);
+  BOOST_ASSERT(result.second != nullptr);
+
+  Timestamp dataTimestamp = time::toUnixTimestamp(time::system_clock::now()).count() / 1000;
+
+  try {
+    ndn::IdentityCertificate cert(*result.second);
+
+    if (m_policyChecker.check(dataTimestamp, data, result.first->getTimestamp(), cert)) {
+      NonNegativeInteger dataSeqNo = m_merkleTree.getNextLeafSeqNo();
+      Leaf leaf(data.getFullName(), dataTimestamp, dataSeqNo, signerSeqNo, m_leafPrefix);
+
+      if (m_merkleTree.addLeaf(dataSeqNo, leaf.getHash())) {
+        if (data.getContentType() == ndn::tlv::ContentType_Key)
+          m_db.insertLeafData(leaf, data);
+        else
+          m_db.insertLeafData(leaf);
+
+        makeLogResponse(reqInterest, LoggerResponse(dataSeqNo));
+      }
+      else
+        makeLogResponse(reqInterest,
+                        LoggerResponse(tlv::LogResponse_Error_Tree, "cannot add leaf"));
+    }
+    else
+      makeLogResponse(reqInterest,
+                      LoggerResponse(tlv::LogResponse_Error_Policy, "cannot pass policy checking"));
+  }
+  catch (tlv::Error&) {
+    makeLogResponse(reqInterest,
+                    LoggerResponse(tlv::LogResponse_Error_Signer, "signer is wrong"));
+  }
+}
+
+void
+Logger::dataTimeoutCallback(const Interest& interest, int nRetrials,
+                            const NonNegativeInteger& signerSeqNo,
+                            const Interest& reqInterest)
+{
+  if (nRetrials > 0) {
+    m_face.expressInterest(interest,
+                           bind(&Logger::dataReceivedCallback, this, _1, _2,
+                                signerSeqNo, reqInterest),
+                           bind(&Logger::dataTimeoutCallback, this, _1,
+                                nRetrials - 1, signerSeqNo, reqInterest));
+  }
+}
+
+void
+Logger::makeLogResponse(const Interest& reqInterest, const LoggerResponse& response)
+{
+  auto data = make_shared<Data>(reqInterest.getName());
+  data->setContent(response.wireEncode());
+
+  BOOST_ASSERT(m_dskCert != nullptr);
+  m_keyChain.sign(*data, m_dskCert->getName());
+  m_face.put(*data);
+}
+
+
+} // namespace nsl
diff --git a/core/logger.hpp b/core/logger.hpp
new file mode 100644
index 0000000..a53b0aa
--- /dev/null
+++ b/core/logger.hpp
@@ -0,0 +1,140 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014,  Regents of the University of California
+ *
+ * This file is part of NSL (NDN Signature Logger).
+ * See AUTHORS.md for complete list of NSL authors and contributors.
+ *
+ * NSL is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NSL 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NSL, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of nsl authors and contributors.
+ */
+
+#ifndef NSL_CORE_LOGGER_HPP
+#define NSL_CORE_LOGGER_HPP
+
+#include "common.hpp"
+#include "logger-response.hpp"
+#include "db.hpp"
+#include "policy-checker.hpp"
+#include "merkle-tree.hpp"
+#include "util/non-negative-integer.hpp"
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/security/validator-config.hpp>
+
+namespace nsl {
+
+class Logger
+{
+public:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+public:
+  Logger(ndn::Face& face, const std::string& configFile);
+
+  NonNegativeInteger
+  addSelfSignedCert(ndn::IdentityCertificate& cert, const Timestamp& timestamp);
+
+NSL_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  void
+  initializeKeys();
+
+  void
+  loadConfiguration(const std::string& filename);
+
+  void
+  onSubTreeInterest(const ndn::InterestFilter& interestFilter, const Interest& interest);
+
+  void
+  onLeafInterest(const ndn::InterestFilter& interestFilter, const Interest& interest);
+
+  void
+  onLogRequestInterest(const ndn::InterestFilter& interestFilter, const Interest& interest);
+
+  void
+  requestValidatedCallback(const shared_ptr<const Interest>& interest);
+
+  void
+  dataReceivedCallback(const Interest& interest, Data& data,
+                       const NonNegativeInteger& signerSeqNo,
+                       const Interest& reqInterest);
+
+  void
+  dataTimeoutCallback(const Interest& interest, int nRetrials,
+                      const NonNegativeInteger& signerSeqNo,
+                      const Interest& reqInterest);
+
+  void
+  makeLogResponse(const Interest& reqInterest, const LoggerResponse& response);
+
+  const Name&
+  getLoggerName() const
+  {
+    return m_loggerName;
+  }
+
+  const Name&
+  getTreePrefix() const
+  {
+    return m_treePrefix;
+  }
+
+  const Name&
+  getLeafPrefix() const
+  {
+    return m_leafPrefix;
+  }
+
+  const Name&
+  getLogPrefix() const
+  {
+    return m_logPrefix;
+  }
+
+  Db&
+  getDb()
+  {
+    return m_db;
+  }
+
+private:
+  static const int N_DATA_FETCHING_RETRIAL;
+
+private:
+  ndn::Face& m_face;
+  Name m_loggerName;
+  Name m_treePrefix;
+  Name m_leafPrefix;
+  Name m_logPrefix;
+
+  Db m_db;
+  MerkleTree  m_merkleTree;
+
+  ndn::KeyChain m_keyChain;
+  shared_ptr<ndn::IdentityCertificate> m_dskCert;
+
+  ndn::ValidatorConfig m_validator;
+  PolicyChecker m_policyChecker;
+};
+
+} // namespace nsl
+
+#endif // NSL_CORE_LOGGER_HPP
diff --git a/daemon/main.cpp b/daemon/main.cpp
index d172f7d..d0b6bd0 100644
--- a/daemon/main.cpp
+++ b/daemon/main.cpp
@@ -19,8 +19,71 @@
  * \author Yingdi Yu <yingdi@cs.ucla.edu>
  */
 
+#include "../core/logger.hpp"
+
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/parsers.hpp>
+#include <boost/filesystem.hpp>
+
+
 int
 main(int argc, char** argv)
 {
+  namespace po = boost::program_options;
+  namespace fs = boost::filesystem;
+
+  std::string configFile;
+
+  po::options_description description("General Usage\n"
+                                      "  nsl [-h] [-c config]\n"
+                                      "General options");
+  description.add_options()
+    ("help,h", "produce help message")
+    ("config,c", po::value<std::string>(&configFile))
+    ;
+
+  po::variables_map vm;
+  try {
+    po::store(po::parse_command_line(argc, argv, description), vm);
+    po::notify(vm);
+  }
+  catch (const std::exception& e) {
+    std::cerr << "ERROR: " << e.what() << std::endl;
+    std::cerr << description << std::endl;
+    return 1;
+  }
+
+  if (vm.count("help") != 0) {
+    std::cerr << description << std::endl;
+    return 0;
+  }
+
+  if (vm.count("config") == 0) {
+
+    if (!getenv("HOME")) {
+      configFile = "/usr/local/etc/ndn/nsl.conf";
+    }
+    else {
+      configFile = getenv("HOME");
+      configFile += "/.ndn/nsl.conf";
+    }
+
+    if (!fs::exists(fs::path(configFile))) {
+      std::cerr << "ERROR: config file is not available: " << configFile << std::endl;
+      return 1;
+    }
+  }
+
+  try {
+    ndn::Face face;
+    nsl::Logger(face, configFile);
+    face.processEvents();
+  }
+  catch (std::runtime_error& e) {
+    std::cerr << e.what() << std::endl;
+    return 1;
+  }
+
   return 0;
 }
diff --git a/tests/core/logger.t.cpp b/tests/core/logger.t.cpp
new file mode 100644
index 0000000..521be9b
--- /dev/null
+++ b/tests/core/logger.t.cpp
@@ -0,0 +1,335 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014,  Regents of the University of California
+ *
+ * This file is part of NSL (NDN Signature Logger).
+ * See AUTHORS.md for complete list of NSL authors and contributors.
+ *
+ * NSL is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NSL 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NSL, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of nsl authors and contributors.
+ */
+
+#include "logger.hpp"
+#include "identity-fixture.hpp"
+#include "db-fixture.hpp"
+#include <ndn-cxx/util/dummy-client-face.hpp>
+#include <ndn-cxx/util/io.hpp>
+
+#include "boost-test.hpp"
+
+namespace nsl {
+namespace tests {
+
+class LoggerFixture : public IdentityFixture
+                    , public DbFixture
+{
+public:
+  LoggerFixture()
+    : face1(ndn::util::makeDummyClientFace(io, {true, true}))
+    , face2(ndn::util::makeDummyClientFace(io, {true, true}))
+    , readInterestOffset1(0)
+    , readDataOffset1(0)
+    , readInterestOffset2(0)
+    , readDataOffset2(0)
+  {
+  }
+
+  ~LoggerFixture()
+  {
+  }
+
+  bool
+  passPacket()
+  {
+    bool hasPassed = false;
+
+    checkFace(face1->sentInterests, readInterestOffset1, *face2, hasPassed);
+    checkFace(face1->sentDatas, readDataOffset1, *face2, hasPassed);
+    checkFace(face2->sentInterests, readInterestOffset2, *face1, hasPassed);
+    checkFace(face2->sentDatas, readDataOffset2, *face1, hasPassed);
+
+    return hasPassed;
+  }
+
+  template<typename Packet>
+  void
+  checkFace(std::vector<Packet>& receivedPackets,
+            size_t& readPacketOffset,
+            ndn::util::DummyClientFace& receiver,
+            bool& hasPassed)
+  {
+    while (receivedPackets.size() > readPacketOffset) {
+      receiver.receive(receivedPackets[readPacketOffset]);
+      readPacketOffset++;
+      hasPassed = true;
+    }
+  }
+
+  void
+  clear()
+  {
+    face1->sentDatas.clear();
+    face1->sentInterests.clear();
+    face2->sentDatas.clear();
+    face2->sentInterests.clear();
+
+    readInterestOffset1 = 0;
+    readDataOffset1 = 0;
+    readInterestOffset2 = 0;
+    readDataOffset2 = 0;
+  }
+
+public:
+  shared_ptr<ndn::util::DummyClientFace> face1;
+  shared_ptr<ndn::util::DummyClientFace> face2;
+
+  size_t readInterestOffset1;
+  size_t readDataOffset1;
+  size_t readInterestOffset2;
+  size_t readDataOffset2;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestLogger, LoggerFixture)
+
+const std::string CONFIG =
+  "logger-name /test/logger                             \n"
+  "policy                                               \n"
+  "{                                                    \n"
+  "  rule                                               \n"
+  "  {                                                  \n"
+  "    id \"Simple Rule\"                               \n"
+  "    for data                                         \n"
+  "    checker                                          \n"
+  "    {                                                \n"
+  "      type customized                                \n"
+  "      sig-type rsa-sha256                            \n"
+  "      key-locator                                    \n"
+  "      {                                              \n"
+  "        type name                                    \n"
+  "        hyper-relation                               \n"
+  "        {                                            \n"
+  "          k-regex ^([^<KEY>]*)<KEY>(<>*)<><ID-CERT>$ \n"
+  "          k-expand \\\\1\\\\2                        \n"
+  "          h-relation is-strict-prefix-of             \n"
+  "          p-regex ^(<>*)$                            \n"
+  "          p-expand \\\\1                             \n"
+  "        }                                            \n"
+  "      }                                              \n"
+  "    }                                                \n"
+  "  }                                                  \n"
+  "}                                                    \n"
+  "validator                                            \n"
+  "{                                                    \n"
+  "  rule                                               \n"
+  "  {                                                  \n"
+  "    id \"Request Rule\"                              \n"
+  "    for interest                                     \n"
+  "    filter                                           \n"
+  "    {                                                \n"
+  "      type name                                      \n"
+  "      name /test/logger/log                          \n"
+  "      relation is-strict-prefix-of                   \n"
+  "    }                                                \n"
+  "    checker                                          \n"
+  "    {                                                \n"
+  "      type customized                                \n"
+  "      sig-type rsa-sha256                            \n"
+  "      key-locator                                    \n"
+  "      {                                              \n"
+  "        type name                                    \n"
+  "        regex ^[^<KEY>]*<KEY><>*<><ID-CERT>$         \n"
+  "      }                                              \n"
+  "    }                                                \n"
+  "  }                                                  \n"
+  "  rule                                               \n"
+  "  {                                                  \n"
+  "    id \"Simple Rule\"                               \n"
+  "    for data                                         \n"
+  "    checker                                          \n"
+  "    {                                                \n"
+  "      type customized                                \n"
+  "      sig-type rsa-sha256                            \n"
+  "      key-locator                                    \n"
+  "      {                                              \n"
+  "        type name                                    \n"
+  "        hyper-relation                               \n"
+  "        {                                            \n"
+  "          k-regex ^([^<KEY>]*)<KEY>(<>*)<><ID-CERT>$ \n"
+  "          k-expand \\\\1\\\\2                        \n"
+  "          h-relation is-strict-prefix-of             \n"
+  "          p-regex ^(<>*)$                            \n"
+  "          p-expand \\\\1                             \n"
+  "        }                                            \n"
+  "      }                                              \n"
+  "    }                                                \n"
+  "  }                                                  \n"
+  "  trust-anchor                                       \n"
+  "  {                                                  \n"
+  "    type file                                        \n"
+  "    file-name \"trust-anchor.cert\"                  \n"
+  "  }                                                  \n"
+  "}                                                    \n";
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  namespace fs = boost::filesystem;
+
+  fs::create_directory(fs::path(TEST_LOGGER_PATH));
+
+  fs::path configPath = fs::path(TEST_LOGGER_PATH) / "logger-test.conf";
+  std::ofstream os(configPath.c_str());
+  os << CONFIG;
+  os.close();
+
+  Name root("/ndn");
+  addIdentity(root);
+  auto rootCert = m_keyChain.getCertificate(m_keyChain.getDefaultCertificateNameForIdentity(root));
+  fs::path certPath = fs::path(TEST_LOGGER_PATH) / "trust-anchor.cert";
+  ndn::io::save(*rootCert, certPath.string());
+
+  Logger logger(*face1, configPath.string());
+
+  BOOST_CHECK_EQUAL(logger.getLoggerName(), Name("/test/logger"));
+  BOOST_CHECK_EQUAL(logger.getTreePrefix(), Name("/test/logger/tree"));
+  BOOST_CHECK_EQUAL(logger.getLeafPrefix(), Name("/test/logger/leaf"));
+  BOOST_CHECK_EQUAL(logger.getLogPrefix(), Name("/test/logger/log"));
+
+  advanceClocks(time::milliseconds(2), 100);
+
+  Timestamp rootTs = time::toUnixTimestamp(time::system_clock::now()).count() / 1000;
+  NonNegativeInteger rootSeqNo = logger.addSelfSignedCert(*rootCert, rootTs);
+  BOOST_CHECK_EQUAL(rootSeqNo, 0);
+
+  Name leafInterestName("/test/logger/leaf");
+  leafInterestName.appendNumber(0);
+  auto leafInterest = make_shared<Interest>(leafInterestName);
+
+  face1->receive(*leafInterest);
+  advanceClocks(time::milliseconds(2), 100);
+
+  BOOST_CHECK_EQUAL(face1->sentDatas.size(), 1);
+  BOOST_CHECK(leafInterestName.isPrefixOf(face1->sentDatas[0].getName()));
+
+  face1->sentDatas.clear();
+
+  Name treeInterestName("/test/logger/tree");
+  treeInterestName.appendNumber(0);
+  treeInterestName.appendNumber(0);
+  auto treeInterest = make_shared<Interest>(treeInterestName);
+
+  face1->receive(*treeInterest);
+  advanceClocks(time::milliseconds(2), 100);
+
+  BOOST_CHECK_EQUAL(face1->sentDatas.size(), 1);
+  BOOST_CHECK(treeInterestName.isPrefixOf(face1->sentDatas[0].getName()));
+
+  face1->sentDatas.clear();
+
+  Name tld("/ndn/tld");
+  Name tldKeyName = m_keyChain.generateRsaKeyPair(tld);
+  std::vector<ndn::CertificateSubjectDescription> subjectDescription;
+  auto tldCert =
+    m_keyChain.prepareUnsignedIdentityCertificate(tldKeyName, root,
+                                                  time::system_clock::now(),
+                                                  time::system_clock::now() + time::days(1),
+                                                  subjectDescription);
+  m_keyChain.signByIdentity(*tldCert, root);
+  m_keyChain.addCertificate(*tldCert);
+
+  face2->setInterestFilter(tldCert->getName().getPrefix(-1),
+    [&] (const ndn::InterestFilter&, const Interest&) { face2->put(*tldCert); },
+    ndn::RegisterPrefixSuccessCallback(),
+    [] (const Name&, const std::string&) {});
+  advanceClocks(time::milliseconds(2), 100);
+  clear();
+
+  Name logInterestName("/test/logger/log");
+  logInterestName.append(tldCert->getFullName().wireEncode());
+  logInterestName.appendNumber(0);
+  auto logInterest = make_shared<Interest>(logInterestName);
+  m_keyChain.sign(*logInterest, tldCert->getName());
+
+  face1->receive(*logInterest);
+  do {
+    advanceClocks(time::milliseconds(2), 100);
+  } while (passPacket());
+  clear();
+
+  BOOST_CHECK_EQUAL(logger.getDb().getMaxLeafSeq(), 2);
+  auto leafResult1 = logger.getDb().getLeaf(1);
+  BOOST_CHECK(leafResult1.first != nullptr);
+  BOOST_CHECK(leafResult1.second != nullptr);
+
+
+
+  Name leafInterestName2("/test/logger/leaf");
+  leafInterestName2.appendNumber(1);
+  auto leafInterest2 = make_shared<Interest>(leafInterestName2);
+
+  face1->receive(*leafInterest2);
+  advanceClocks(time::milliseconds(2), 100);
+
+  BOOST_CHECK_EQUAL(face1->sentDatas.size(), 1);
+  BOOST_CHECK(leafInterestName2.isPrefixOf(face1->sentDatas[0].getName()));
+  clear();
+
+
+
+  Name treeInterestName2("/test/logger/tree");
+  treeInterestName2.appendNumber(1);
+  treeInterestName2.appendNumber(0);
+  auto treeInterest2 = make_shared<Interest>(treeInterestName2);
+
+  face1->receive(*treeInterest2);
+  advanceClocks(time::milliseconds(2), 100);
+
+  BOOST_CHECK_EQUAL(face1->sentDatas.size(), 1);
+  BOOST_CHECK(treeInterestName2.isPrefixOf(face1->sentDatas[0].getName()));
+  clear();
+
+
+  auto data = make_shared<Data>(Name("/ndn/tld/data"));
+  m_keyChain.sign(*data, tldCert->getName());
+
+  face2->setInterestFilter(data->getName(),
+    [&] (const ndn::InterestFilter&, const Interest&) { face2->put(*data); },
+    ndn::RegisterPrefixSuccessCallback(),
+    [] (const Name&, const std::string&) {});
+  advanceClocks(time::milliseconds(2), 100);
+  clear();
+
+  Name logInterestName2("/test/logger/log");
+  logInterestName2.append(data->getFullName().wireEncode());
+  logInterestName2.appendNumber(1);
+  auto logInterest2 = make_shared<Interest>(logInterestName2);
+  m_keyChain.sign(*logInterest2, tldCert->getName());
+
+  face1->receive(*logInterest2);
+  do {
+    advanceClocks(time::milliseconds(2), 100);
+  } while (passPacket());
+  clear();
+
+  BOOST_CHECK_EQUAL(logger.getDb().getMaxLeafSeq(), 3);
+  auto leafResult2 = logger.getDb().getLeaf(2);
+  BOOST_CHECK(leafResult2.first != nullptr);
+  BOOST_CHECK(leafResult2.second == nullptr);
+
+
+  fs::remove_all(fs::path(TEST_LOGGER_PATH));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nsl