blob: a579350c2951edb5a138a68d277879ef4e60ed98 [file] [log] [blame]
/* -*- 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 "merkle-tree.hpp"
namespace nsl {
MerkleTree::MerkleTree(Db& db)
: m_db(db)
, m_nextLeafSeqNo(0)
{
}
MerkleTree::MerkleTree(const Name& loggerName, Db& db)
: m_loggerName(loggerName)
, m_db(db)
, m_nextLeafSeqNo(0)
{
loadPendingSubTrees();
}
MerkleTree::~MerkleTree()
{
savePendingTree();
}
void
MerkleTree::setLoggerName(const Name& loggerName)
{
m_loggerName = loggerName;
}
bool
MerkleTree::addLeaf(const NonNegativeInteger& seqNo, ndn::ConstBufferPtr hash)
{
auto baseTree = m_pendingTrees[SubTreeBinary::SUB_TREE_DEPTH - 1];
BOOST_ASSERT(baseTree != nullptr);
NodePtr leaf = make_shared<Node>(seqNo, 0, seqNo + 1, hash);
return baseTree->addLeaf(leaf);
}
void
MerkleTree::savePendingTree()
{
size_t level = m_rootSubTree->getPeakIndex().level;
size_t step = SubTreeBinary::SUB_TREE_DEPTH - 1;
for (size_t i = level; i > 0; i -= step) {
auto pendingTree = m_pendingTrees[i];
BOOST_ASSERT(pendingTree != nullptr);
auto data = pendingTree->encode();
if (data != nullptr) {
m_db.insertSubTreeData(pendingTree->getPeakIndex().level, pendingTree->getPeakIndex().seqNo,
*data, false, pendingTree->getNextLeafSeqNo());
}
}
}
shared_ptr<Data>
MerkleTree::getPendingSubTreeData(size_t level)
{
auto it = m_pendingTrees.find(level);
if (it != m_pendingTrees.end())
return it->second->encode();
else
return nullptr;
}
// private:
void
MerkleTree::loadPendingSubTrees()
{
std::vector<shared_ptr<Data>> subtreeDatas = m_db.getPendingSubTrees();
shared_ptr<SubTreeBinary> subtree;
if (subtreeDatas.empty()) {
subtree = make_shared<SubTreeBinary>(m_loggerName,
Node::Index(0, SubTreeBinary::SUB_TREE_DEPTH - 1),
[this] (const Node::Index& idx) {
// std::cerr << "complete: " << idx.level << ", " << idx.seqNo << std::endl;
this->getNewRoot(idx);
},
[this] (const Node::Index& idx,
const NonNegativeInteger& seqNo,
ndn::ConstBufferPtr hash) {
// std::cerr << "update: " << idx.level << ", " << idx.seqNo << std::endl;
// std::cerr << "seqNo: " << seqNo << std::endl;
this->m_nextLeafSeqNo = seqNo;
this->m_hash = hash;
});
m_pendingTrees[SubTreeBinary::SUB_TREE_DEPTH - 1] = subtree;
m_rootSubTree = subtree;
return;
}
subtree = make_shared<SubTreeBinary>(m_loggerName,
[this] (const Node::Index& idx) { this->getNewRoot(idx); },
[this] (const Node::Index& idx,
const NonNegativeInteger& seqNo,
ndn::ConstBufferPtr hash) {
this->m_nextLeafSeqNo = seqNo;
this->m_hash = hash;
});
subtree->decode(*subtreeDatas[0]);
m_pendingTrees[subtree->getPeakIndex().level] = subtree;
m_rootSubTree = subtree;
shared_ptr<SubTreeBinary> parentTree = subtree;
for (size_t i = 1; i < subtreeDatas.size(); i++) {
subtree = make_shared<SubTreeBinary>(m_loggerName,
[this] (const Node::Index& idx) {
this->getNewSibling(idx);
},
[parentTree] (const Node::Index&,
const NonNegativeInteger& seqNo,
ndn::ConstBufferPtr hash) {
parentTree->updateLeaf(seqNo, hash);
});
subtree->decode(*subtreeDatas[i]);
if (parentTree->getPeakIndex().level + 1 - SubTreeBinary::SUB_TREE_DEPTH !=
subtree->getPeakIndex().level)
throw Error("loadPendingSubTrees: inconsistent pending subtree level");
if (parentTree->getNextLeafSeqNo() != subtree->getNextLeafSeqNo())
throw Error("loadPendingSubTrees: inconsistent pending subtree next leaf seqNo");
m_pendingTrees[subtree->getPeakIndex().level] = subtree;
parentTree = subtree;
}
}
void
MerkleTree::getNewRoot(const Node::Index& idx)
{
// save the old root tree into db
auto oldRoot = m_pendingTrees[idx.level];
BOOST_ASSERT(oldRoot != nullptr);
m_db.insertSubTreeData(idx.level, idx.seqNo, *oldRoot->encode());
// create a new root tree
Node::Index newRootIdx(0, idx.level + SubTreeBinary::SUB_TREE_DEPTH - 1);
auto newRoot = make_shared<SubTreeBinary>(m_loggerName, newRootIdx,
[this] (const Node::Index& idx) {
// std::cerr << "complete: " << idx.level << ", " << idx.seqNo << std::endl;
this->getNewRoot(idx);
},
[this] (const Node::Index& index,
const NonNegativeInteger& seqNo,
ndn::ConstBufferPtr hash) {
// std::cerr << "update: " << index.level << ", " << index.seqNo << std::endl;
// std::cerr << "seqNo: " << seqNo << std::endl;
this->m_nextLeafSeqNo = seqNo;
this->m_hash = hash;
});
m_pendingTrees[newRoot->getPeakIndex().level] = newRoot;
m_rootSubTree = newRoot;
bool result = newRoot->updateLeaf(idx.seqNo + idx.range, oldRoot->getRoot()->getHash());
BOOST_ASSERT(result);
// create a sibling
getNewSibling(idx);
}
void
MerkleTree::getNewSibling(const Node::Index& idx)
{
// save old sibling
auto oldSibling = m_pendingTrees[idx.level];
BOOST_ASSERT(oldSibling != nullptr);
m_db.insertSubTreeData(idx.level, idx.seqNo, *oldSibling->encode());
// get parent tree
Node::Index parentIdx(0, idx.level + SubTreeBinary::SUB_TREE_DEPTH - 1);
auto parent = m_pendingTrees[parentIdx.level];
BOOST_ASSERT(parent != nullptr);
// create a new sibling
Node::Index newSiblingIdx(idx.seqNo + idx.range, idx.level);
// std::cerr << "new Sibling: " << newSiblingIdx.level << ", " << newSiblingIdx.seqNo << std::endl;
auto newSibling = make_shared<SubTreeBinary>(m_loggerName, newSiblingIdx,
[this] (const Node::Index& idx) { this->getNewSibling(idx); },
[parent] (const Node::Index& index,
const NonNegativeInteger& seqNo,
ndn::ConstBufferPtr hash) {
// std::cerr << "update: " << index.level << ", " << index.seqNo << std::endl;
// std::cerr << "seqNo: " << seqNo << std::endl;
// std::cerr << "parent: " << parent->getRoot()->getIndex().level << ", " <<
// parent->getRoot()->getIndex().seqNo << std::endl;
bool result = parent->updateLeaf(seqNo, hash);
BOOST_ASSERT(result);
});
m_pendingTrees[newSibling->getPeakIndex().level] = newSibling;
}
}// namespace nsl