Files
xahaud/src/ripple/shamap/impl/SHAMapTreeNode.cpp
Vinnie Falco 8be4e7e65f Refactor Serializer, SerialIter, SHAMapItem, NodeObject:
* Make LessThan private
* Make NodeObject::isSame private
* Remove hotTRANSACTION
* Remove some Serializer members
* Remove unused SHAMapItem::getRaw
* Remove unused STLedgerEntry::getOwners
* Remove Serializer constructors
* Remove unused Serializer members
* Remove SerialIter ctor
2015-06-02 12:55:07 -07:00

589 lines
15 KiB
C++

//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <BeastConfig.h>
#include <ripple/shamap/SHAMapTreeNode.h>
#include <ripple/basics/Log.h>
#include <ripple/basics/SHA512Half.h>
#include <ripple/basics/Slice.h>
#include <ripple/basics/StringUtilities.h>
#include <ripple/protocol/HashPrefix.h>
#include <beast/module/core/text/LexicalCast.h>
#include <mutex>
#include <openssl/sha.h>
namespace ripple {
std::mutex SHAMapTreeNode::childLock;
SHAMapTreeNode::SHAMapTreeNode (std::uint32_t seq)
: mSeq (seq)
, mType (tnERROR)
, mIsBranch (0)
, mFullBelowGen (0)
{
}
SHAMapTreeNode::SHAMapTreeNode (const SHAMapTreeNode& node, std::uint32_t seq)
: mHash (node.mHash)
, mSeq (seq)
, mType (node.mType)
, mIsBranch (node.mIsBranch)
, mFullBelowGen (0)
{
if (node.mItem)
mItem = node.mItem;
else
{
memcpy (mHashes, node.mHashes, sizeof (mHashes));
std::unique_lock <std::mutex> lock (childLock);
for (int i = 0; i < 16; ++i)
mChildren[i] = node.mChildren[i];
}
}
SHAMapTreeNode::SHAMapTreeNode (std::shared_ptr<SHAMapItem> const& item,
TNType type, std::uint32_t seq)
: mItem (item)
, mSeq (seq)
, mType (type)
, mIsBranch (0)
, mFullBelowGen (0)
{
assert (item->peekData ().size () >= 12);
updateHash ();
}
SHAMapTreeNode::SHAMapTreeNode (Blob const& rawNode,
std::uint32_t seq, SHANodeFormat format,
uint256 const& hash, bool hashValid)
: mSeq (seq)
, mType (tnERROR)
, mIsBranch (0)
, mFullBelowGen (0)
{
if (format == snfWIRE)
{
if (rawNode.empty ())
{
#ifdef BEAST_DEBUG
deprecatedLogs().journal("SHAMapTreeNode").fatal <<
"Wire format node is empty";
assert (false);
#endif
throw std::runtime_error ("invalid node AW type");
}
Serializer s (rawNode.data(), rawNode.size() - 1);
int type = rawNode.back ();
int len = s.getLength ();
if ((type < 0) || (type > 4))
{
#ifdef BEAST_DEBUG
deprecatedLogs().journal("SHAMapTreeNode").fatal <<
"Invalid wire format node" << strHex (rawNode);
assert (false);
#endif
throw std::runtime_error ("invalid node AW type");
}
if (type == 0)
{
// transaction
mItem = std::make_shared<SHAMapItem>(
sha512Half(HashPrefix::transactionID,
Slice(s.data(), s.size())),
s.peekData());
mType = tnTRANSACTION_NM;
}
else if (type == 1)
{
// account state
if (len < (256 / 8))
throw std::runtime_error ("short AS node");
uint256 u;
s.get256 (u, len - (256 / 8));
s.chop (256 / 8);
if (u.isZero ()) throw std::runtime_error ("invalid AS node");
mItem = std::make_shared<SHAMapItem> (u, s.peekData ());
mType = tnACCOUNT_STATE;
}
else if (type == 2)
{
// full inner
if (len != 512)
throw std::runtime_error ("invalid FI node");
for (int i = 0; i < 16; ++i)
{
s.get256 (mHashes[i], i * 32);
if (mHashes[i].isNonZero ())
mIsBranch |= (1 << i);
}
mType = tnINNER;
}
else if (type == 3)
{
// compressed inner
for (int i = 0; i < (len / 33); ++i)
{
int pos;
s.get8 (pos, 32 + (i * 33));
if ((pos < 0) || (pos >= 16)) throw std::runtime_error ("invalid CI node");
s.get256 (mHashes[pos], i * 33);
if (mHashes[pos].isNonZero ())
mIsBranch |= (1 << pos);
}
mType = tnINNER;
}
else if (type == 4)
{
// transaction with metadata
if (len < (256 / 8))
throw std::runtime_error ("short TM node");
uint256 u;
s.get256 (u, len - (256 / 8));
s.chop (256 / 8);
if (u.isZero ())
throw std::runtime_error ("invalid TM node");
mItem = std::make_shared<SHAMapItem> (u, s.peekData ());
mType = tnTRANSACTION_MD;
}
}
else if (format == snfPREFIX)
{
if (rawNode.size () < 4)
{
WriteLog (lsINFO, SHAMapNodeID) << "size < 4";
throw std::runtime_error ("invalid P node");
}
std::uint32_t prefix = rawNode[0];
prefix <<= 8;
prefix |= rawNode[1];
prefix <<= 8;
prefix |= rawNode[2];
prefix <<= 8;
prefix |= rawNode[3];
Serializer s (rawNode.data() + 4, rawNode.size() - 4);
if (prefix == HashPrefix::transactionID)
{
mItem = std::make_shared<SHAMapItem>(
sha512Half(make_Slice(rawNode)),
s.peekData ());
mType = tnTRANSACTION_NM;
}
else if (prefix == HashPrefix::leafNode)
{
if (s.getLength () < 32)
throw std::runtime_error ("short PLN node");
uint256 u;
s.get256 (u, s.getLength () - 32);
s.chop (32);
if (u.isZero ())
{
WriteLog (lsINFO, SHAMapNodeID) << "invalid PLN node";
throw std::runtime_error ("invalid PLN node");
}
mItem = std::make_shared<SHAMapItem> (u, s.peekData ());
mType = tnACCOUNT_STATE;
}
else if (prefix == HashPrefix::innerNode)
{
if (s.getLength () != 512)
throw std::runtime_error ("invalid PIN node");
for (int i = 0; i < 16; ++i)
{
s.get256 (mHashes[i], i * 32);
if (mHashes[i].isNonZero ())
mIsBranch |= (1 << i);
}
mType = tnINNER;
}
else if (prefix == HashPrefix::txNode)
{
// transaction with metadata
if (s.getLength () < 32)
throw std::runtime_error ("short TXN node");
uint256 txID;
s.get256 (txID, s.getLength () - 32);
s.chop (32);
mItem = std::make_shared<SHAMapItem> (txID, s.peekData ());
mType = tnTRANSACTION_MD;
}
else
{
WriteLog (lsINFO, SHAMapNodeID) << "Unknown node prefix " << std::hex << prefix << std::dec;
throw std::runtime_error ("invalid node prefix");
}
}
else
{
assert (false);
throw std::runtime_error ("Unknown format");
}
if (hashValid)
{
mHash = hash;
#if RIPPLE_VERIFY_NODEOBJECT_KEYS
updateHash ();
assert (mHash == hash);
#endif
}
else
updateHash ();
}
bool SHAMapTreeNode::updateHash ()
{
uint256 nh;
if (mType == tnINNER)
{
if (mIsBranch != 0)
{
// VFALCO This code assumes the layout of a base_uint
nh = sha512Half(HashPrefix::innerNode,
Slice(reinterpret_cast<unsigned char const*>(mHashes),
sizeof (mHashes)));
#if RIPPLE_VERIFY_NODEOBJECT_KEYS
SHA512HalfHasher h;
using beast::hash_append;
hash_append(h, HashPrefix::innerNode, mHashes);
assert (nh == sha512Half(
static_cast<uint256>(h)));
#endif
}
else
nh.zero ();
}
else if (mType == tnTRANSACTION_NM)
{
nh = sha512Half(HashPrefix::transactionID,
make_Slice(mItem->peekData()));
}
else if (mType == tnACCOUNT_STATE)
{
nh = sha512Half(HashPrefix::leafNode,
make_Slice(mItem->peekData()),
mItem->getTag());
}
else if (mType == tnTRANSACTION_MD)
{
nh = sha512Half(HashPrefix::txNode,
make_Slice(mItem->peekData()),
mItem->getTag());
}
else
assert (false);
if (nh == mHash)
return false;
mHash = nh;
return true;
}
void
SHAMapTreeNode::updateHashDeep()
{
for (auto pos = 0; pos < 16; ++pos)
{
if (mChildren[pos] != nullptr)
mHashes[pos] = mChildren[pos]->mHash;
}
updateHash();
}
void SHAMapTreeNode::addRaw (Serializer& s, SHANodeFormat format)
{
assert ((format == snfPREFIX) || (format == snfWIRE) || (format == snfHASH));
if (mType == tnERROR)
throw std::runtime_error ("invalid I node type");
if (format == snfHASH)
{
s.add256 (getNodeHash ());
}
else if (mType == tnINNER)
{
assert (!isEmpty ());
if (format == snfPREFIX)
{
s.add32 (HashPrefix::innerNode);
for (int i = 0; i < 16; ++i)
s.add256 (mHashes[i]);
}
else
{
if (getBranchCount () < 12)
{
// compressed node
for (int i = 0; i < 16; ++i)
if (!isEmptyBranch (i))
{
s.add256 (mHashes[i]);
s.add8 (i);
}
s.add8 (3);
}
else
{
for (int i = 0; i < 16; ++i)
s.add256 (mHashes[i]);
s.add8 (2);
}
}
}
else if (mType == tnACCOUNT_STATE)
{
if (format == snfPREFIX)
{
s.add32 (HashPrefix::leafNode);
s.addRaw (mItem->peekData ());
s.add256 (mItem->getTag ());
}
else
{
s.addRaw (mItem->peekData ());
s.add256 (mItem->getTag ());
s.add8 (1);
}
}
else if (mType == tnTRANSACTION_NM)
{
if (format == snfPREFIX)
{
s.add32 (HashPrefix::transactionID);
s.addRaw (mItem->peekData ());
}
else
{
s.addRaw (mItem->peekData ());
s.add8 (0);
}
}
else if (mType == tnTRANSACTION_MD)
{
if (format == snfPREFIX)
{
s.add32 (HashPrefix::txNode);
s.addRaw (mItem->peekData ());
s.add256 (mItem->getTag ());
}
else
{
s.addRaw (mItem->peekData ());
s.add256 (mItem->getTag ());
s.add8 (4);
}
}
else
assert (false);
}
bool SHAMapTreeNode::setItem (std::shared_ptr<SHAMapItem> const& i, TNType type)
{
mType = type;
mItem = i;
assert (isLeaf ());
assert (mSeq != 0);
return updateHash ();
}
bool SHAMapTreeNode::isEmpty () const
{
return mIsBranch == 0;
}
int SHAMapTreeNode::getBranchCount () const
{
assert (isInner ());
int count = 0;
for (int i = 0; i < 16; ++i)
if (!isEmptyBranch (i))
++count;
return count;
}
void SHAMapTreeNode::makeInner ()
{
mItem.reset ();
mIsBranch = 0;
memset (mHashes, 0, sizeof (mHashes));
mType = tnINNER;
mHash.zero ();
}
#ifdef BEAST_DEBUG
void SHAMapTreeNode::dump (const SHAMapNodeID & id, beast::Journal journal)
{
if (journal.debug) journal.debug <<
"SHAMapTreeNode(" << id.getNodeID () << ")";
}
#endif // BEAST_DEBUG
std::string SHAMapTreeNode::getString (const SHAMapNodeID & id) const
{
std::string ret = "NodeID(";
ret += beast::lexicalCastThrow <std::string> (id.getDepth ());
ret += ",";
ret += to_string (id.getNodeID ());
ret += ")";
if (isInner ())
{
for (int i = 0; i < 16; ++i)
if (!isEmptyBranch (i))
{
ret += "\nb";
ret += beast::lexicalCastThrow <std::string> (i);
ret += " = ";
ret += to_string (mHashes[i]);
}
}
if (isLeaf ())
{
if (mType == tnTRANSACTION_NM)
ret += ",txn\n";
else if (mType == tnTRANSACTION_MD)
ret += ",txn+md\n";
else if (mType == tnACCOUNT_STATE)
ret += ",as\n";
else
ret += ",leaf\n";
ret += " Tag=";
ret += to_string (peekItem()->getTag ());
ret += "\n Hash=";
ret += to_string (mHash);
ret += "/";
ret += beast::lexicalCast <std::string> (mItem->size());
}
return ret;
}
// We are modifying an inner node
void
SHAMapTreeNode::setChild (int m, std::shared_ptr<SHAMapTreeNode> const& child)
{
assert ((m >= 0) && (m < 16));
assert (mType == tnINNER);
assert (mSeq != 0);
assert (child.get() != this);
mHashes[m].zero();
mHash.zero();
if (child)
mIsBranch |= (1 << m);
else
mIsBranch &= ~ (1 << m);
mChildren[m] = child;
}
// finished modifying, now make shareable
void SHAMapTreeNode::shareChild (int m, std::shared_ptr<SHAMapTreeNode> const& child)
{
assert ((m >= 0) && (m < 16));
assert (mType == tnINNER);
assert (mSeq != 0);
assert (child);
assert (child.get() != this);
mChildren[m] = child;
}
SHAMapTreeNode* SHAMapTreeNode::getChildPointer (int branch)
{
assert (branch >= 0 && branch < 16);
assert (isInnerNode ());
std::unique_lock <std::mutex> lock (childLock);
return mChildren[branch].get ();
}
std::shared_ptr<SHAMapTreeNode> SHAMapTreeNode::getChild (int branch)
{
assert (branch >= 0 && branch < 16);
assert (isInnerNode ());
std::unique_lock <std::mutex> lock (childLock);
return mChildren[branch];
}
void SHAMapTreeNode::canonicalizeChild (int branch, std::shared_ptr<SHAMapTreeNode>& node)
{
assert (branch >= 0 && branch < 16);
assert (isInnerNode ());
assert (node);
assert (node->getNodeHash() == mHashes[branch]);
std::unique_lock <std::mutex> lock (childLock);
if (mChildren[branch])
{
// There is already a node hooked up, return it
node = mChildren[branch];
}
else
{
// Hook this node up
mChildren[branch] = node;
}
}
} // ripple