From 29d24c0af8c3721a6a4e1081287defe0c3ea913f Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Tue, 7 Feb 2012 16:54:59 -0800 Subject: [PATCH] Bugfixes and new unit test. --- SHAMap.cpp | 95 ++++++++++++++++++++++++++++++++++++++++++++----- SHAMap.h | 2 ++ SHAMapNodes.cpp | 6 ++-- SHAMapSync.cpp | 77 ++++++++++++++++++++++++++++++--------- 4 files changed, 152 insertions(+), 28 deletions(-) diff --git a/SHAMap.cpp b/SHAMap.cpp index cc0dd1293f..fd2e0a766b 100644 --- a/SHAMap.cpp +++ b/SHAMap.cpp @@ -33,7 +33,7 @@ std::stack SHAMap::getStack(const uint256& id, bool inc uint256 hash=node->getChildHash(branch); if(!hash) return stack; - node=getNode(node->getChildNodeID(branch), node->getChildHash(branch), false); + node=getNode(node->getChildNodeID(branch), hash, false); if(!node) { if(mSynching) return stack; @@ -210,6 +210,60 @@ SHAMapItem::pointer SHAMap::lastBelow(SHAMapTreeNode::pointer node) } while(1); } +SHAMapItem::pointer SHAMap::onlyBelow(SHAMapTreeNode::pointer node) +{ + // If there is only one item below this node, return it + bool found; + while(!node->isLeaf()) + { + found=false; + SHAMapTreeNode::pointer nextNode; + + for(int i=0; i<15; i++) + if(!node->isEmptyBranch(i)) + { + if(found) return SHAMapItem::pointer(); // two leaves below + nextNode=getNode(node->getChildNodeID(i), node->getChildHash(i), false); + if(!nextNode) throw SHAMapException(MissingNode); + found=true; + } + + if(!found) + { + assert(false); + return SHAMapItem::pointer(); + } + node=nextNode; + } + assert(node->hasItem()); + return node->peekItem(); +} + +void SHAMap::eraseChildren(SHAMapTreeNode::pointer node) +{ // this node has only one item below it, erase its children + bool erase=false; + while(!node->isLeaf()) + { + for(int i=0; i<16; i++) + if(!node->isEmptyBranch(i)) + { + SHAMapTreeNode::pointer nextNode=getNode(node->getChildNodeID(i), node->getChildHash(i), false); + if(erase) + { + returnNode(node, true); + if(mTNByID.erase(*node)) + assert(false); + } + erase=true; + node=nextNode; + } + } + returnNode(node, true); + if(mTNByID.erase(*node)==0) + assert(false); + return; +} + SHAMapItem::pointer SHAMap::peekFirstItem() { boost::recursive_mutex::scoped_lock sl(mLock); @@ -302,16 +356,18 @@ bool SHAMap::delItem(const uint256& id) { // delete the item with this ID boost::recursive_mutex::scoped_lock sl(mLock); - std::stack stack=getStack(id, false); + std::stack stack=getStack(id, true); if(stack.empty()) throw SHAMapException(MissingNode); SHAMapTreeNode::pointer leaf=stack.top(); stack.pop(); if( !leaf || !leaf->hasItem() || (leaf->peekItem()->getTag()!=id) ) return false; + SHAMapTreeNode::TNType type=leaf->getType(); returnNode(leaf, true); - mTNByID.erase(*leaf); + if(mTNByID.erase(*leaf)==0) + assert(false); uint256 prevHash; while(!stack.empty()) @@ -319,25 +375,46 @@ bool SHAMap::delItem(const uint256& id) SHAMapTreeNode::pointer node=stack.top(); stack.pop(); returnNode(node, true); + assert(node->isInner()); + if(!node->setChildHash(node->selectBranch(id), prevHash)) + { + assert(false); return true; - if(!prevHash && !node->isRoot()) + } + if(!node->isRoot()) { // we may have made this a node with 1 or 0 children int bc=node->getBranchCount(); if(bc==0) { +#ifdef DEBUG + std::cerr << "delItem makes empty node" << std::endl; +#endif prevHash=uint256(); - mTNByID.erase(*node); + if(!mTNByID.erase(*node)) + assert(false); } else if(bc==1) { // pull up on the thread - SHAMapItem::pointer item=firstBelow(node); - assert(item); - node->setItem(item, type); + SHAMapItem::pointer item=onlyBelow(node); + if(item) + { + eraseChildren(node); +#ifdef ST_DEBUG + std::cerr << "Making item node " << node->getString() << std::endl; +#endif + node->setItem(item, type); + } prevHash=node->getNodeHash(); + assert(!!prevHash); + } + else + { + prevHash=node->getNodeHash(); + assert(!!prevHash); } } - else prevHash=node->getNodeHash(); + else assert(stack.empty()); } return true; } diff --git a/SHAMap.h b/SHAMap.h index 2e7b55d00c..0e35f6b4b9 100644 --- a/SHAMap.h +++ b/SHAMap.h @@ -231,6 +231,8 @@ protected: SHAMapItem::pointer firstBelow(SHAMapTreeNode::pointer); SHAMapItem::pointer lastBelow(SHAMapTreeNode::pointer); + SHAMapItem::pointer onlyBelow(SHAMapTreeNode::pointer); + void eraseChildren(SHAMapTreeNode::pointer); bool walkBranch(SHAMapTreeNode::pointer node, SHAMapItem::pointer otherMapItem, bool isFirstMap, SHAMapDiff& differences, int& maxCount); diff --git a/SHAMapNodes.cpp b/SHAMapNodes.cpp index 252bc0aa6e..a6b27bed39 100644 --- a/SHAMapNodes.cpp +++ b/SHAMapNodes.cpp @@ -279,14 +279,16 @@ bool SHAMapTreeNode::setItem(SHAMapItem::pointer& i, TNType type) SHAMapItem::pointer SHAMapTreeNode::getItem() const { + assert(isLeaf()); return boost::make_shared(*mItem); } int SHAMapTreeNode::getBranchCount() const { + assert(isInner()); int ret=0; - for(int i=0; i<16; i++) - if(!mHashes[i]) ret++; + for(int i=0; i<16; ++i) + if(!!mHashes[i]) ++ret; return ret; } diff --git a/SHAMapSync.cpp b/SHAMapSync.cpp index 8035ae2f4d..546fa30ce1 100644 --- a/SHAMapSync.cpp +++ b/SHAMapSync.cpp @@ -294,6 +294,55 @@ bool SHAMap::deepCompare(SHAMap& other) return true; } +#ifdef DEBUG +#define SMS_DEBUG +#endif + +static SHAMapItem::pointer makeRandomAS() +{ + Serializer s; + for(int d=0; d<8; d++) + s.add32(rand()); + return boost::make_shared(s.getRIPEMD160(), s.peekData()); +} + +static bool confuseMap(SHAMap &map, int count) +{ + // add a bunch of random states to a map, then remove them + // map should be the same + uint256 beforeHash=map.getHash(); + + std::list items; + + for(int i=0; igetTag()); + if(!map.addItem(*item, false)) + { + std::cerr << "Unable to add item to map" << std::endl; + return false; + } + } + + for(std::list::iterator it=items.begin(); it!=items.end(); ++it) + { + if(!map.delItem(*it)) + { + std::cerr << "Unable to remove item from map" << std::endl; + return false; + } + } + + if(beforeHash!=map.getHash()) + { + std::cerr << "Hashes do not match" << std::endl; + return false; + } + + return true; +} + bool SHAMap::syncTest() { unsigned int seed; @@ -302,20 +351,14 @@ bool SHAMap::syncTest() SHAMap source, destination; + // add random data to the source map - int items=rand()%20000; + int items=10000; for(int i=0; i