www.pudn.com > DCPlusPlus-src.zip > MerkleTree.h
/* * Copyright (C) 2001-2004 Jacek Sieka, j_s at telia com * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _MERKLE_TREE #define _MERKLE_TREE #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include "TigerHash.h" #include "Encoder.h" #include "HashValue.h" #include "File.h" #include/** * A class that represents a Merkle Tree hash. Storing * only the leaves of the tree, it is rather memory efficient, * but can still take a significant amount of memory during / after * hash generation. * The root hash produced can be used like any * other hash to verify the integrity of a whole file, while * the leaves provide checking of smaller parts of the file. */ template class MerkleTree { public: enum { HASH_SIZE = Hasher::HASH_SIZE }; enum { BASE_BLOCK_SIZE = baseBlockSize }; typedef HashValue MerkleValue; typedef vector MerkleList; typedef typename MerkleList::iterator MerkleIter; MerkleTree() : fileSize(0), timeStamp(0), blockSize(baseBlockSize) { } MerkleTree(size_t aBlockSize, u_int32_t aTimeStamp = 0) : fileSize(0), timeStamp(aTimeStamp), blockSize(aBlockSize) { } /** * Loads a set of leaf hashes, calculating the root * @param data Pointer to (aFileSize + aBlockSize - 1) / aBlockSize) hash values, * stored consecutively left to right */ MerkleTree(int64_t aFileSize, u_int32_t aTimeStamp, size_t aBlockSize, u_int8_t* aData) : fileSize(aFileSize), timeStamp(aTimeStamp), blockSize(aBlockSize) { size_t n = calcBlocks(aFileSize, aBlockSize); for(size_t i = 0; i < n; i++) leaves.push_back(MerkleValue(aData + i * Hasher::HASH_SIZE)); calcRoot(); } ~MerkleTree() { } static size_t calcBlockSize(int64_t aFileSize, int maxLevels) { size_t tmp = baseBlockSize; int64_t maxHashes = ((int64_t)1) << (maxLevels - 1); while((maxHashes * tmp) < aFileSize) tmp *= 2; return tmp; } static size_t calcBlocks(int64_t aFileSize, size_t aBlockSize) { return max((size_t)((aFileSize + aBlockSize - 1) / aBlockSize), (size_t)1); } /** * Update the merkle tree. * @param len Length of data, must be a multiple of baseBlockSize, unless it's * the last block. */ void update(const void* data, size_t len) { u_int8_t* buf = (u_int8_t*)data; u_int8_t zero = 0; size_t i = 0; // Skip empty data sets if we already added at least one of them... if(len == 0 && !(leaves.empty() && blocks.empty())) return; do { size_t n = min(baseBlockSize, len-i); Hasher h; h.update(&zero, 1); h.update(buf + i, n); if(baseBlockSize < blockSize) { blocks.push_back(make_pair(MerkleValue(h.finalize()), baseBlockSize)); reduceBlocks(); } else { leaves.push_back(MerkleValue(h.finalize())); } i += n; } while(i < len); fileSize += len; } u_int8_t* finalize() { while(blocks.size() > 1) { MerkleBlock& a = blocks[blocks.size()-2]; MerkleBlock& b = blocks[blocks.size()-1]; a.first = combine(a.first, b.first); blocks.pop_back(); } dcassert(blocks.size() == 0 || blocks.size() == 1); if(!blocks.empty()) { leaves.push_back(blocks[0].first); } calcRoot(); return root.data; } MerkleValue& getRoot() { return root; } const MerkleValue& getRoot() const { return root; } MerkleList& getLeaves() { return leaves; } const MerkleList& getLeaves() const { return leaves; } size_t getBlockSize() const { return blockSize; } void setBlockSize(size_t aSize) { blockSize = aSize; } int64_t getFileSize() const { return fileSize; } void setFileSize(int64_t aSize) { fileSize = aSize; } u_int32_t getTimeStamp() const { return timeStamp; } bool verifyRoot(const u_int8_t* aRoot) { return memcmp(aRoot, getRoot().data(), Hasher::HASH_SIZE) == 0; } void calcRoot() { root = getHash(0, fileSize); } private: typedef pair MerkleBlock; typedef vector MBList; MBList blocks; MerkleList leaves; MerkleValue root; /** Total size of hashed data */ int64_t fileSize; /** Last modification date of data */ u_int32_t timeStamp; /** Final block size */ size_t blockSize; MerkleValue getHash(int64_t start, int64_t length) { dcassert((start % blockSize) == 0); if(length <= blockSize) { dcassert((start / blockSize) < leaves.size()); return leaves[(u_int32_t)(start / blockSize)]; } else { int64_t l = blockSize; while(l * 2 < length) l *= 2; return combine(getHash(start, l), getHash(start+l, length - l)); } } MerkleValue combine(const MerkleValue& a, const MerkleValue& b) { u_int8_t one = 1; Hasher h; h.update(&one, 1); h.update(a.data, MerkleValue::SIZE); h.update(b.data, MerkleValue::SIZE); return MerkleValue(h.finalize()); } void reduceBlocks() { while(blocks.size() > 1) { MerkleBlock& a = blocks[blocks.size()-2]; MerkleBlock& b = blocks[blocks.size()-1]; if(a.second == b.second) { if(a.second*2 == blockSize) { leaves.push_back(combine(a.first, b.first)); blocks.pop_back(); blocks.pop_back(); } else { a.second *= 2; a.first = combine(a.first, b.first); blocks.pop_back(); } } else { break; } } } }; typedef MerkleTree TigerTree; typedef TigerTree::MerkleValue TTHValue; template class TreeInputStream : public InputStream { public: TreeInputStream(const MerkleTree & aTree) : leaves(aTree.getLeaves().size() * Value::SIZE, 0), pos(0) { u_int8_t* p = &leaves[0]; for(size_t i = 0; i < aTree.getLeaves().size(); ++i) { memcpy(p + i * Value::SIZE, &aTree.getLeaves()[i], Value::SIZE); } } virtual ~TreeInputStream() { } virtual size_t read(void* buf, size_t& len) throw(Exception) { len = min(len, leaves.size() - pos); memcpy(buf, &leaves[pos], len); pos += len; return len; } private: typedef typename MerkleTree ::MerkleValue Value; vector leaves; size_t pos; }; struct TTFilter { TTFilter(size_t aBlockSize, u_int32_t aTimeStamp = 0) : tt(aBlockSize, aTimeStamp) { }; void operator()(const void* data, size_t len) { tt.update(data, len); } TigerTree& getTree() { tt.finalize(); return tt; } private: TigerTree tt; }; #endif // _MERKLE_TREE /** * @file * $Id: MerkleTree.h,v 1.17 2004/09/21 08:19:55 arnetheduck Exp $ */