mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Install SHAMapInnerNodeV2
* Inner node optimization.
This commit is contained in:
@@ -172,9 +172,9 @@ public:
|
||||
Ledger::Ledger (create_genesis_t, Config const& config, Family& family)
|
||||
: mImmutable (false)
|
||||
, txMap_ (std::make_shared <SHAMap> (SHAMapType::TRANSACTION,
|
||||
family))
|
||||
family, SHAMap::version{1}))
|
||||
, stateMap_ (std::make_shared <SHAMap> (SHAMapType::STATE,
|
||||
family))
|
||||
family, SHAMap::version{1}))
|
||||
{
|
||||
info_.seq = 1;
|
||||
info_.drops = SYSTEM_CURRENCY_START;
|
||||
@@ -209,9 +209,9 @@ Ledger::Ledger (uint256 const& parentHash,
|
||||
beast::Journal j)
|
||||
: mImmutable (true)
|
||||
, txMap_ (std::make_shared <SHAMap> (
|
||||
SHAMapType::TRANSACTION, transHash, family))
|
||||
, stateMap_ (std::make_shared <SHAMap> (
|
||||
SHAMapType::STATE, accountHash, family))
|
||||
SHAMapType::TRANSACTION, transHash, family, SHAMap::version{1}))
|
||||
, stateMap_ (std::make_shared <SHAMap> (SHAMapType::STATE, accountHash,
|
||||
family, SHAMap::version{1}))
|
||||
{
|
||||
info_.seq = ledgerSeq;
|
||||
info_.parentCloseTime = parentCloseTime;
|
||||
@@ -257,7 +257,7 @@ Ledger::Ledger (Ledger const& prevLedger,
|
||||
NetClock::time_point closeTime)
|
||||
: mImmutable (false)
|
||||
, txMap_ (std::make_shared <SHAMap> (SHAMapType::TRANSACTION,
|
||||
prevLedger.stateMap_->family()))
|
||||
prevLedger.stateMap_->family(), prevLedger.stateMap_->get_version()))
|
||||
, stateMap_ (prevLedger.stateMap_->snapShot (true))
|
||||
, fees_(prevLedger.fees_)
|
||||
, rules_(prevLedger.rules_)
|
||||
@@ -288,9 +288,9 @@ Ledger::Ledger (void const* data,
|
||||
Config const& config, Family& family)
|
||||
: mImmutable (true)
|
||||
, txMap_ (std::make_shared <SHAMap> (
|
||||
SHAMapType::TRANSACTION, family))
|
||||
SHAMapType::TRANSACTION, family, SHAMap::version{1}))
|
||||
, stateMap_ (std::make_shared <SHAMap> (
|
||||
SHAMapType::STATE, family))
|
||||
SHAMapType::STATE, family, SHAMap::version{1}))
|
||||
{
|
||||
SerialIter sit (data, size);
|
||||
setRaw (sit, hasPrefix, family);
|
||||
@@ -302,9 +302,9 @@ Ledger::Ledger (std::uint32_t ledgerSeq,
|
||||
Family& family)
|
||||
: mImmutable (false)
|
||||
, txMap_ (std::make_shared <SHAMap> (
|
||||
SHAMapType::TRANSACTION, family))
|
||||
SHAMapType::TRANSACTION, family, SHAMap::version{1}))
|
||||
, stateMap_ (std::make_shared <SHAMap> (
|
||||
SHAMapType::STATE, family))
|
||||
SHAMapType::STATE, family, SHAMap::version{1}))
|
||||
{
|
||||
info_.seq = ledgerSeq;
|
||||
info_.closeTime = closeTime;
|
||||
@@ -862,9 +862,9 @@ void Ledger::setRaw (SerialIter& sit, bool hasPrefix, Family& family)
|
||||
info_.closeFlags = sit.get8 ();
|
||||
updateHash ();
|
||||
txMap_ = std::make_shared<SHAMap> (SHAMapType::TRANSACTION, info_.txHash,
|
||||
family);
|
||||
family, SHAMap::version{1});
|
||||
stateMap_ = std::make_shared<SHAMap> (SHAMapType::STATE, info_.accountHash,
|
||||
family);
|
||||
family, SHAMap::version{1});
|
||||
}
|
||||
|
||||
static bool saveValidatedLedger (
|
||||
@@ -1200,6 +1200,30 @@ Ledger::getNeededAccountStateHashes (
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
Ledger::make_v2()
|
||||
{
|
||||
assert (! mImmutable);
|
||||
stateMap_ = stateMap_->make_v2();
|
||||
txMap_ = txMap_->make_v2();
|
||||
info_.validated = false;
|
||||
updateHash();
|
||||
}
|
||||
|
||||
void
|
||||
Ledger::unshare() const
|
||||
{
|
||||
stateMap_->unshare();
|
||||
txMap_->unshare();
|
||||
}
|
||||
|
||||
void
|
||||
Ledger::invariants() const
|
||||
{
|
||||
stateMap_->invariants();
|
||||
txMap_->invariants();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
|
||||
@@ -312,6 +312,9 @@ public:
|
||||
|
||||
bool assertSane (beast::Journal ledgerJ);
|
||||
|
||||
void make_v2();
|
||||
void invariants() const;
|
||||
void unshare() const;
|
||||
private:
|
||||
class sles_iter_impl;
|
||||
class txs_iter_impl;
|
||||
|
||||
@@ -259,9 +259,11 @@ public:
|
||||
if (!node.has_nodeid () || !node.has_nodedata ())
|
||||
return;
|
||||
|
||||
auto id_string = node.nodeid();
|
||||
auto newNode = SHAMapAbstractNode::make(
|
||||
Blob (node.nodedata().begin(), node.nodedata().end()),
|
||||
0, snfWIRE, SHAMapHash{uZero}, false, app_.journal ("SHAMapNodeID"));
|
||||
0, snfWIRE, SHAMapHash{uZero}, false, app_.journal ("SHAMapNodeID"),
|
||||
SHAMapNodeID(id_string.data(), id_string.size()));
|
||||
|
||||
if (!newNode)
|
||||
return;
|
||||
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
{
|
||||
m_zeroSet.mSet = std::make_shared<SHAMap> (
|
||||
SHAMapType::TRANSACTION, uint256(),
|
||||
app_.family());
|
||||
app_.family(), SHAMap::version{1});
|
||||
m_zeroSet.mSet->setUnbacked();
|
||||
}
|
||||
|
||||
|
||||
@@ -981,6 +981,12 @@ void LedgerConsensusImp::accept (std::shared_ptr<SHAMap> set)
|
||||
auto buildLCL = std::make_shared<Ledger>(
|
||||
*mPreviousLedger,
|
||||
app_.timeKeeper().closeTime());
|
||||
auto constexpr v2_ledger_seq_switch = 40'000'000;
|
||||
if (buildLCL->info().seq > v2_ledger_seq_switch &&
|
||||
!buildLCL->stateMap().is_v2())
|
||||
{
|
||||
buildLCL->make_v2();
|
||||
}
|
||||
|
||||
// Set up to write SHAMap changes to our database,
|
||||
// perform updates, extract changes
|
||||
@@ -1016,6 +1022,14 @@ void LedgerConsensusImp::accept (std::shared_ptr<SHAMap> set)
|
||||
|
||||
buildLCL->updateSkipList ();
|
||||
|
||||
// unshare in case a nodestore load changed the
|
||||
// version back, otherwise the map is inconsistent
|
||||
if (buildLCL->info().seq > v2_ledger_seq_switch &&
|
||||
!buildLCL->stateMap().is_v2())
|
||||
{
|
||||
buildLCL->unshare();
|
||||
}
|
||||
|
||||
{
|
||||
int asf = buildLCL->stateMap().flushDirty (
|
||||
hotACCOUNT_NODE, buildLCL->info().seq);
|
||||
@@ -1407,7 +1421,7 @@ void LedgerConsensusImp::takeInitialPosition (
|
||||
std::shared_ptr<ReadView const> const& initialLedger)
|
||||
{
|
||||
std::shared_ptr<SHAMap> initialSet = std::make_shared <SHAMap> (
|
||||
SHAMapType::TRANSACTION, app_.family());
|
||||
SHAMapType::TRANSACTION, app_.family(), SHAMap::version{1});
|
||||
initialSet->setUnbacked ();
|
||||
|
||||
// Build SHAMap containing all transactions in our open ledger
|
||||
|
||||
@@ -48,7 +48,7 @@ TransactionAcquire::TransactionAcquire (Application& app, uint256 const& hash, c
|
||||
, j_(app.journal("TransactionAcquire"))
|
||||
{
|
||||
mMap = std::make_shared<SHAMap> (SHAMapType::TRANSACTION, hash,
|
||||
app_.family());
|
||||
app_.family(), SHAMap::version{1});
|
||||
mMap->setUnbacked ();
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ nodeobject_decompress (void const* in,
|
||||
p, in_size, bf);
|
||||
break;
|
||||
}
|
||||
case 2: // inner node
|
||||
case 2: // compressed v1 inner node
|
||||
{
|
||||
auto const hs =
|
||||
field<std::uint16_t>::size; // Mask
|
||||
@@ -218,7 +218,7 @@ nodeobject_decompress (void const* in,
|
||||
"nodeobject codec: long inner node");
|
||||
break;
|
||||
}
|
||||
case 3: // full inner node
|
||||
case 3: // full v1 inner node
|
||||
{
|
||||
if (in_size != 16 * 32) // hashes
|
||||
Throw<codec_error> (
|
||||
@@ -235,6 +235,80 @@ nodeobject_decompress (void const* in,
|
||||
write(os, is(512), 512);
|
||||
break;
|
||||
}
|
||||
case 5: // compressed v2 inner node
|
||||
{
|
||||
auto const hs =
|
||||
field<std::uint16_t>::size; // Mask size
|
||||
if (in_size < hs + 65)
|
||||
Throw<codec_error> (
|
||||
"nodeobject codec: short inner node");
|
||||
istream is(p, in_size);
|
||||
std::uint16_t mask;
|
||||
read<std::uint16_t>(is, mask); // Mask
|
||||
in_size -= hs;
|
||||
std::uint8_t depth;
|
||||
read<std::uint8_t>(is, depth);
|
||||
in_size -= 1;
|
||||
result.second = 525 + 1 + (depth+1)/2;
|
||||
void* const out = bf(result.second);
|
||||
result.first = out;
|
||||
ostream os(out, result.second);
|
||||
write<std::uint32_t>(os, 0);
|
||||
write<std::uint32_t>(os, 0);
|
||||
write<std::uint8_t> (os, hotUNKNOWN);
|
||||
write<std::uint32_t>(os, HashPrefix::innerNodeV2);
|
||||
if (mask == 0)
|
||||
Throw<codec_error> (
|
||||
"nodeobject codec: empty inner node");
|
||||
std::uint16_t bit = 0x8000;
|
||||
for (int i = 16; i--; bit >>= 1)
|
||||
{
|
||||
if (mask & bit)
|
||||
{
|
||||
if (in_size < 32)
|
||||
Throw<codec_error> (
|
||||
"nodeobject codec: short inner node");
|
||||
std::memcpy(os.data(32), is(32), 32);
|
||||
in_size -= 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memset(os.data(32), 0, 32);
|
||||
}
|
||||
}
|
||||
write<std::uint8_t>(os, depth);
|
||||
if (in_size < (depth+1)/2)
|
||||
Throw<codec_error> (
|
||||
"nodeobject codec: short inner node");
|
||||
std::memcpy(os.data((depth+1)/2), is((depth+1)/2), (depth+1)/2);
|
||||
in_size -= (depth+1)/2;
|
||||
if (in_size > 0)
|
||||
Throw<codec_error> (
|
||||
"nodeobject codec: long inner node");
|
||||
break;
|
||||
}
|
||||
case 6: // full v2 inner node
|
||||
{
|
||||
istream is(p, in_size);
|
||||
std::uint8_t depth;
|
||||
read<std::uint8_t>(is, depth);
|
||||
in_size -= 1;
|
||||
result.second = 525 + 1 + (depth+1)/2;
|
||||
if (in_size != 16 * 32 + (depth+1)/2) // hashes and common
|
||||
Throw<codec_error> (
|
||||
"nodeobject codec: short full inner node");
|
||||
void* const out = bf(result.second);
|
||||
result.first = out;
|
||||
ostream os(out, result.second);
|
||||
write<std::uint32_t>(os, 0);
|
||||
write<std::uint32_t>(os, 0);
|
||||
write<std::uint8_t> (os, hotUNKNOWN);
|
||||
write<std::uint32_t>(os, HashPrefix::innerNodeV2);
|
||||
write(os, is(512), 512);
|
||||
write<std::uint8_t>(os, depth);
|
||||
write(os, is((depth+1)/2), (depth+1)/2);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Throw<codec_error> (
|
||||
"nodeobject codec: bad type=" +
|
||||
@@ -266,7 +340,7 @@ nodeobject_compress (void const* in,
|
||||
using namespace beast::nudb::detail;
|
||||
|
||||
std::size_t type = 1;
|
||||
// Check for inner node
|
||||
// Check for inner node v1
|
||||
if (in_size == 525)
|
||||
{
|
||||
istream is(in, in_size);
|
||||
@@ -300,7 +374,7 @@ nodeobject_compress (void const* in,
|
||||
std::size_t> result;
|
||||
if (n < 16)
|
||||
{
|
||||
// 2 = inner node compressed
|
||||
// 2 = v1 inner node compressed
|
||||
auto const type = 2U;
|
||||
auto const vs = size_varint(type);
|
||||
result.second =
|
||||
@@ -316,7 +390,7 @@ nodeobject_compress (void const* in,
|
||||
write(os, vh.data(), n * 32);
|
||||
return result;
|
||||
}
|
||||
// 3 = full inner node
|
||||
// 3 = full v1 inner node
|
||||
auto const type = 3U;
|
||||
auto const vs = size_varint(type);
|
||||
result.second =
|
||||
@@ -332,6 +406,87 @@ nodeobject_compress (void const* in,
|
||||
}
|
||||
}
|
||||
|
||||
// Check for inner node v2
|
||||
if (526 <= in_size && in_size <= 556)
|
||||
{
|
||||
istream is(in, in_size);
|
||||
std::uint32_t index;
|
||||
std::uint32_t unused;
|
||||
std::uint8_t kind;
|
||||
std::uint32_t prefix;
|
||||
read<std::uint32_t>(is, index);
|
||||
read<std::uint32_t>(is, unused);
|
||||
read<std::uint8_t> (is, kind);
|
||||
read<std::uint32_t>(is, prefix);
|
||||
if (prefix == HashPrefix::innerNodeV2)
|
||||
{
|
||||
std::size_t n = 0;
|
||||
std::uint16_t mask = 0;
|
||||
std::array<
|
||||
std::uint8_t, 512> vh;
|
||||
for (unsigned bit = 0x8000;
|
||||
bit; bit >>= 1)
|
||||
{
|
||||
void const* const h = is(32);
|
||||
if (std::memcmp(
|
||||
h, zero32(), 32) == 0)
|
||||
continue;
|
||||
std::memcpy(
|
||||
vh.data() + 32 * n, h, 32);
|
||||
mask |= bit;
|
||||
++n;
|
||||
}
|
||||
std::uint8_t depth;
|
||||
read<std::uint8_t>(is, depth);
|
||||
std::array<std::uint8_t, 32> common{};
|
||||
for (unsigned d = 0; d < (depth+1)/2; ++d)
|
||||
read<std::uint8_t>(is, common[d]);
|
||||
std::pair<void const*,
|
||||
std::size_t> result;
|
||||
if (n < 16)
|
||||
{
|
||||
// 5 = v2 inner node compressed
|
||||
auto const type = 5U;
|
||||
auto const vs = size_varint(type);
|
||||
result.second =
|
||||
vs +
|
||||
field<std::uint16_t>::size + // mask
|
||||
n * 32 + // hashes
|
||||
1 + // depth
|
||||
(depth+1)/2; // common prefix
|
||||
std::uint8_t* out = reinterpret_cast<
|
||||
std::uint8_t*>(bf(result.second));
|
||||
result.first = out;
|
||||
ostream os(out, result.second);
|
||||
write<varint>(os, type);
|
||||
write<std::uint16_t>(os, mask);
|
||||
write<std::uint8_t>(os, depth);
|
||||
write(os, vh.data(), n * 32);
|
||||
for (unsigned d = 0; d < (depth+1)/2; ++d)
|
||||
write<std::uint8_t>(os, common[d]);
|
||||
return result;
|
||||
}
|
||||
// 6 = full v2 inner node
|
||||
auto const type = 6U;
|
||||
auto const vs = size_varint(type);
|
||||
result.second =
|
||||
vs +
|
||||
n * 32 + // hashes
|
||||
1 + // depth
|
||||
(depth+1)/2; // common prefix
|
||||
std::uint8_t* out = reinterpret_cast<
|
||||
std::uint8_t*>(bf(result.second));
|
||||
result.first = out;
|
||||
ostream os(out, result.second);
|
||||
write<varint>(os, type);
|
||||
write<std::uint8_t>(os, depth);
|
||||
write(os, vh.data(), n * 32);
|
||||
for (unsigned d = 0; d < (depth+1)/2; ++d)
|
||||
write<std::uint8_t>(os, common[d]);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
std::array<std::uint8_t, varint_traits<
|
||||
std::size_t>::max> vi;
|
||||
auto const vn = write_varint(
|
||||
|
||||
@@ -76,9 +76,12 @@ public:
|
||||
/** account state */
|
||||
static HashPrefix const leafNode;
|
||||
|
||||
/** inner node in tree */
|
||||
/** inner node in V1 tree */
|
||||
static HashPrefix const innerNode;
|
||||
|
||||
/** inner node in V2 tree */
|
||||
static HashPrefix const innerNodeV2;
|
||||
|
||||
/** ledger master data for signing */
|
||||
static HashPrefix const ledgerMaster;
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ HashPrefix const HashPrefix::transactionID ('T', 'X', 'N');
|
||||
HashPrefix const HashPrefix::txNode ('S', 'N', 'D');
|
||||
HashPrefix const HashPrefix::leafNode ('M', 'L', 'N');
|
||||
HashPrefix const HashPrefix::innerNode ('M', 'I', 'N');
|
||||
HashPrefix const HashPrefix::innerNodeV2 ('I', 'N', 'R');
|
||||
HashPrefix const HashPrefix::ledgerMaster ('L', 'W', 'R');
|
||||
HashPrefix const HashPrefix::txSign ('S', 'T', 'X');
|
||||
HashPrefix const HashPrefix::txMultiSign ('S', 'M', 'T');
|
||||
|
||||
@@ -89,6 +89,18 @@ private:
|
||||
bool backed_ = true; // Map is backed by the database
|
||||
|
||||
public:
|
||||
class version
|
||||
{
|
||||
int v_;
|
||||
public:
|
||||
explicit version(int v) : v_(v) {}
|
||||
|
||||
friend bool operator==(version const& x, version const& y)
|
||||
{return x.v_ == y.v_;}
|
||||
friend bool operator!=(version const& x, version const& y)
|
||||
{return !(x == y);}
|
||||
};
|
||||
|
||||
using DeltaItem = std::pair<std::shared_ptr<SHAMapItem const>,
|
||||
std::shared_ptr<SHAMapItem const>>;
|
||||
using Delta = std::map<uint256, DeltaItem>;
|
||||
@@ -101,13 +113,14 @@ public:
|
||||
SHAMap (
|
||||
SHAMapType t,
|
||||
Family& f,
|
||||
std::uint32_t seq = 1
|
||||
version v
|
||||
);
|
||||
|
||||
SHAMap (
|
||||
SHAMapType t,
|
||||
uint256 const& hash,
|
||||
Family& f);
|
||||
Family& f,
|
||||
version v);
|
||||
|
||||
Family&
|
||||
family()
|
||||
@@ -209,8 +222,14 @@ public:
|
||||
std::function<void (SHAMapHash const&, const Blob&)>) const;
|
||||
|
||||
void setUnbacked ();
|
||||
bool is_v2() const;
|
||||
version get_version() const;
|
||||
std::shared_ptr<SHAMap> make_v1() const;
|
||||
std::shared_ptr<SHAMap> make_v2() const;
|
||||
int unshare ();
|
||||
|
||||
void dump (bool withHashes = false) const;
|
||||
void invariants() const;
|
||||
|
||||
private:
|
||||
using SharedPtrNodeStack =
|
||||
@@ -219,7 +238,6 @@ private:
|
||||
std::shared_ptr<SHAMapItem const> const&>;
|
||||
|
||||
void visitDifferences(SHAMap const* have, std::function<bool(SHAMapAbstractNode&)>) const;
|
||||
int unshare ();
|
||||
|
||||
// tree node cache operations
|
||||
std::shared_ptr<SHAMapAbstractNode> getCache (SHAMapHash const& hash) const;
|
||||
@@ -263,7 +281,7 @@ private:
|
||||
std::shared_ptr<SHAMapAbstractNode> node) const;
|
||||
|
||||
SHAMapTreeNode* firstBelow (std::shared_ptr<SHAMapAbstractNode>,
|
||||
SharedPtrNodeStack& stack) const;
|
||||
SharedPtrNodeStack& stack, int branch = 0) const;
|
||||
|
||||
// Simple descent
|
||||
// Get a child of the specified node
|
||||
@@ -291,8 +309,8 @@ private:
|
||||
bool hasInnerNode (SHAMapNodeID const& nodeID, SHAMapHash const& hash) const;
|
||||
bool hasLeafNode (uint256 const& tag, SHAMapHash const& hash) const;
|
||||
|
||||
SHAMapItem const* peekFirstItem(SharedPtrNodeStack& stack) const;
|
||||
SHAMapItem const* peekNextItem(uint256 const& id, SharedPtrNodeStack& stack) const;
|
||||
SHAMapTreeNode const* peekFirstItem(SharedPtrNodeStack& stack) const;
|
||||
SHAMapTreeNode const* peekNextItem(uint256 const& id, SharedPtrNodeStack& stack) const;
|
||||
bool walkBranch (SHAMapAbstractNode* node,
|
||||
std::shared_ptr<SHAMapItem const> const& otherMapItem,
|
||||
bool isFirstMap, Delta & differences, int & maxCount) const;
|
||||
@@ -349,6 +367,13 @@ SHAMap::setUnbacked ()
|
||||
backed_ = false;
|
||||
}
|
||||
|
||||
inline
|
||||
bool
|
||||
SHAMap::is_v2() const
|
||||
{
|
||||
return std::dynamic_pointer_cast<SHAMapInnerNodeV2>(root_) != nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class SHAMap::const_iterator
|
||||
@@ -386,8 +411,11 @@ private:
|
||||
inline
|
||||
SHAMap::const_iterator::const_iterator(SHAMap const* map)
|
||||
: map_(map)
|
||||
, item_(map_->peekFirstItem(stack_))
|
||||
, item_(nullptr)
|
||||
{
|
||||
auto temp = map_->peekFirstItem(stack_);
|
||||
if (temp)
|
||||
item_ = temp->peekItem().get();
|
||||
}
|
||||
|
||||
inline
|
||||
@@ -424,7 +452,11 @@ inline
|
||||
SHAMap::const_iterator&
|
||||
SHAMap::const_iterator::operator++()
|
||||
{
|
||||
item_ = map_->peekNextItem(item_->key(), stack_);
|
||||
auto temp = map_->peekNextItem(item_->key(), stack_);
|
||||
if (temp)
|
||||
item_ = temp->peekItem().get();
|
||||
else
|
||||
item_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ private:
|
||||
|
||||
public:
|
||||
SHAMapNodeID ();
|
||||
SHAMapNodeID (int depth, uint256 const& hash);
|
||||
SHAMapNodeID (void const* ptr, int len);
|
||||
|
||||
bool isValid () const;
|
||||
@@ -64,10 +65,9 @@ public:
|
||||
SHAMapNodeID getChildNodeID (int m) const;
|
||||
int selectBranch (uint256 const& hash) const;
|
||||
int getDepth () const;
|
||||
bool has_common_prefix(SHAMapNodeID const& other) const;
|
||||
|
||||
private:
|
||||
SHAMapNodeID (int depth, uint256 const& hash);
|
||||
|
||||
static uint256 const& Masks (int depth);
|
||||
|
||||
friend std::ostream& operator<< (std::ostream& out, SHAMapNodeID const& node);
|
||||
|
||||
@@ -128,10 +128,13 @@ public:
|
||||
virtual void addRaw (Serializer&, SHANodeFormat format) const = 0;
|
||||
virtual std::string getString (SHAMapNodeID const&) const;
|
||||
virtual std::shared_ptr<SHAMapAbstractNode> clone(std::uint32_t seq) const = 0;
|
||||
virtual uint256 const& key() const = 0;
|
||||
virtual void invariants(bool is_v2, bool is_root = false) const = 0;
|
||||
|
||||
static std::shared_ptr<SHAMapAbstractNode>
|
||||
make(Blob const& rawNode, std::uint32_t seq, SHANodeFormat format,
|
||||
SHAMapHash const& hash, bool hashValid, beast::Journal j);
|
||||
SHAMapHash const& hash, bool hashValid, beast::Journal j,
|
||||
SHAMapNodeID const& id = SHAMapNodeID{});
|
||||
|
||||
// debugging
|
||||
#ifdef BEAST_DEBUG
|
||||
@@ -139,6 +142,8 @@ public:
|
||||
#endif
|
||||
};
|
||||
|
||||
class SHAMapInnerNodeV2;
|
||||
|
||||
class SHAMapInnerNode
|
||||
: public SHAMapAbstractNode
|
||||
{
|
||||
@@ -149,7 +154,7 @@ class SHAMapInnerNode
|
||||
|
||||
static std::mutex childLock;
|
||||
public:
|
||||
SHAMapInnerNode(std::uint32_t seq = 0);
|
||||
SHAMapInnerNode(std::uint32_t seq);
|
||||
std::shared_ptr<SHAMapAbstractNode> clone(std::uint32_t seq) const override;
|
||||
|
||||
bool isEmpty () const;
|
||||
@@ -172,11 +177,47 @@ public:
|
||||
void updateHashDeep();
|
||||
void addRaw (Serializer&, SHANodeFormat format) const override;
|
||||
std::string getString (SHAMapNodeID const&) const override;
|
||||
uint256 const& key() const override;
|
||||
void invariants(bool is_v2, bool is_root = false) const override;
|
||||
|
||||
friend std::shared_ptr<SHAMapAbstractNode>
|
||||
SHAMapAbstractNode::make(Blob const& rawNode, std::uint32_t seq,
|
||||
SHANodeFormat format, SHAMapHash const& hash, bool hashValid,
|
||||
beast::Journal j);
|
||||
beast::Journal j, SHAMapNodeID const& id);
|
||||
|
||||
friend class SHAMapInnerNodeV2;
|
||||
};
|
||||
|
||||
class SHAMapTreeNode;
|
||||
|
||||
// SHAMapInnerNodeV2 is a "version 2" inner node. It always has at least two children
|
||||
// unless it is the root node and there is only one leaf in the tree, in which case
|
||||
// that leaf is a direct child of the root.
|
||||
class SHAMapInnerNodeV2
|
||||
: public SHAMapInnerNode
|
||||
{
|
||||
uint256 common_ = {};
|
||||
int depth_ = 64;
|
||||
public:
|
||||
explicit SHAMapInnerNodeV2(std::uint32_t seq);
|
||||
SHAMapInnerNodeV2(std::uint32_t seq, int depth);
|
||||
std::shared_ptr<SHAMapAbstractNode> clone(std::uint32_t seq) const override;
|
||||
|
||||
uint256 const& common() const;
|
||||
int depth() const;
|
||||
bool has_common_prefix(uint256 const& key) const;
|
||||
int get_common_prefix(uint256 const& key) const;
|
||||
void set_common(int depth, uint256 const& key);
|
||||
void addRaw(Serializer& s, SHANodeFormat format) const override;
|
||||
uint256 const& key() const override;
|
||||
void setChildren(std::shared_ptr<SHAMapTreeNode> const& child1,
|
||||
std::shared_ptr<SHAMapTreeNode> const& child2);
|
||||
void invariants(bool is_v2, bool is_root = false) const override;
|
||||
|
||||
friend std::shared_ptr<SHAMapAbstractNode>
|
||||
SHAMapAbstractNode::make(Blob const& rawNode, std::uint32_t seq,
|
||||
SHANodeFormat format, SHAMapHash const& hash, bool hashValid,
|
||||
beast::Journal j, SHAMapNodeID const& id);
|
||||
};
|
||||
|
||||
// SHAMapTreeNode represents a leaf, and may eventually be renamed to reflect that.
|
||||
@@ -197,6 +238,8 @@ public:
|
||||
std::shared_ptr<SHAMapAbstractNode> clone(std::uint32_t seq) const override;
|
||||
|
||||
void addRaw (Serializer&, SHANodeFormat format) const override;
|
||||
uint256 const& key() const override;
|
||||
void invariants(bool is_v2, bool is_root = false) const override;
|
||||
|
||||
public: // public only to SHAMap
|
||||
|
||||
@@ -325,6 +368,35 @@ SHAMapInnerNode::setFullBelowGen (std::uint32_t gen)
|
||||
mFullBelowGen = gen;
|
||||
}
|
||||
|
||||
// SHAMapInnerNodeV2
|
||||
|
||||
inline
|
||||
SHAMapInnerNodeV2::SHAMapInnerNodeV2(std::uint32_t seq)
|
||||
: SHAMapInnerNode(seq)
|
||||
{
|
||||
}
|
||||
|
||||
inline
|
||||
SHAMapInnerNodeV2::SHAMapInnerNodeV2(std::uint32_t seq, int depth)
|
||||
: SHAMapInnerNode(seq)
|
||||
, depth_(depth)
|
||||
{
|
||||
}
|
||||
|
||||
inline
|
||||
uint256 const&
|
||||
SHAMapInnerNodeV2::common() const
|
||||
{
|
||||
return common_;
|
||||
}
|
||||
|
||||
inline
|
||||
int
|
||||
SHAMapInnerNodeV2::depth() const
|
||||
{
|
||||
return depth_;
|
||||
}
|
||||
|
||||
// SHAMapTreeNode
|
||||
|
||||
inline
|
||||
|
||||
@@ -27,29 +27,34 @@ namespace ripple {
|
||||
SHAMap::SHAMap (
|
||||
SHAMapType t,
|
||||
Family& f,
|
||||
std::uint32_t seq)
|
||||
version v)
|
||||
: f_ (f)
|
||||
, journal_(f.journal())
|
||||
, seq_ (seq)
|
||||
, seq_ (1)
|
||||
, state_ (SHAMapState::Modifying)
|
||||
, type_ (t)
|
||||
{
|
||||
assert (seq_ != 0);
|
||||
|
||||
root_ = std::make_shared<SHAMapInnerNode> (seq_);
|
||||
if (v == version{2})
|
||||
root_ = std::make_shared<SHAMapInnerNodeV2>(seq_, 0);
|
||||
else
|
||||
root_ = std::make_shared<SHAMapInnerNode>(seq_);
|
||||
}
|
||||
|
||||
SHAMap::SHAMap (
|
||||
SHAMapType t,
|
||||
uint256 const& hash,
|
||||
Family& f)
|
||||
Family& f,
|
||||
version v)
|
||||
: f_ (f)
|
||||
, journal_(f.journal())
|
||||
, seq_ (1)
|
||||
, state_ (SHAMapState::Synching)
|
||||
, type_ (t)
|
||||
{
|
||||
root_ = std::make_shared<SHAMapInnerNode> (seq_);
|
||||
if (v == version{2})
|
||||
root_ = std::make_shared<SHAMapInnerNodeV2>(seq_, 0);
|
||||
else
|
||||
root_ = std::make_shared<SHAMapInnerNode>(seq_);
|
||||
}
|
||||
|
||||
SHAMap::~SHAMap ()
|
||||
@@ -60,7 +65,7 @@ SHAMap::~SHAMap ()
|
||||
std::shared_ptr<SHAMap>
|
||||
SHAMap::snapShot (bool isMutable) const
|
||||
{
|
||||
auto ret = std::make_shared<SHAMap> (type_, f_);
|
||||
auto ret = std::make_shared<SHAMap> (type_, f_, get_version());
|
||||
SHAMap& newMap = *ret;
|
||||
|
||||
if (!isMutable)
|
||||
@@ -78,6 +83,72 @@ SHAMap::snapShot (bool isMutable) const
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::shared_ptr<SHAMap>
|
||||
SHAMap::make_v2() const
|
||||
{
|
||||
assert(!is_v2());
|
||||
auto ret = std::make_shared<SHAMap>(type_, f_, version{2});
|
||||
ret->seq_ = seq_ + 1;
|
||||
SharedPtrNodeStack stack;
|
||||
for (auto leaf = peekFirstItem(stack); leaf != nullptr;
|
||||
leaf = peekNextItem(leaf->peekItem()->key(), stack))
|
||||
{
|
||||
auto node_type = leaf->getType();
|
||||
ret->addGiveItem(leaf->peekItem(),
|
||||
node_type != SHAMapTreeNode::tnACCOUNT_STATE,
|
||||
node_type == SHAMapTreeNode::tnTRANSACTION_MD);
|
||||
}
|
||||
NodeObjectType t;
|
||||
switch (type_)
|
||||
{
|
||||
case SHAMapType::TRANSACTION:
|
||||
t = hotTRANSACTION_NODE;
|
||||
break;
|
||||
case SHAMapType::STATE:
|
||||
t = hotACCOUNT_NODE;
|
||||
break;
|
||||
default:
|
||||
t = hotUNKNOWN;
|
||||
break;
|
||||
}
|
||||
ret->unshare();
|
||||
ret->flushDirty(t, ret->seq_);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::shared_ptr<SHAMap>
|
||||
SHAMap::make_v1() const
|
||||
{
|
||||
assert(is_v2());
|
||||
auto ret = std::make_shared<SHAMap>(type_, f_, version{1});
|
||||
ret->seq_ = seq_ + 1;
|
||||
SharedPtrNodeStack stack;
|
||||
for (auto leaf = peekFirstItem(stack); leaf != nullptr;
|
||||
leaf = peekNextItem(leaf->peekItem()->key(), stack))
|
||||
{
|
||||
auto node_type = leaf->getType();
|
||||
ret->addGiveItem(leaf->peekItem(),
|
||||
node_type != SHAMapTreeNode::tnACCOUNT_STATE,
|
||||
node_type == SHAMapTreeNode::tnTRANSACTION_MD);
|
||||
}
|
||||
NodeObjectType t;
|
||||
switch (type_)
|
||||
{
|
||||
case SHAMapType::TRANSACTION:
|
||||
t = hotTRANSACTION_NODE;
|
||||
break;
|
||||
case SHAMapType::STATE:
|
||||
t = hotACCOUNT_NODE;
|
||||
break;
|
||||
default:
|
||||
t = hotUNKNOWN;
|
||||
break;
|
||||
}
|
||||
ret->unshare();
|
||||
ret->flushDirty(t, ret->seq_);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
SHAMap::dirtyUp (SharedPtrNodeStack& stack,
|
||||
uint256 const& target, std::shared_ptr<SHAMapAbstractNode> child)
|
||||
@@ -103,10 +174,6 @@ SHAMap::dirtyUp (SharedPtrNodeStack& stack,
|
||||
node = unshareNode(std::move(node), nodeID);
|
||||
node->setChild (branch, child);
|
||||
|
||||
#ifdef ST_DEBUG
|
||||
JLOG(journal_.trace()) <<
|
||||
"dirtyUp sets branch " << branch << " to " << prevHash;
|
||||
#endif
|
||||
child = std::move (node);
|
||||
}
|
||||
}
|
||||
@@ -119,16 +186,39 @@ SHAMap::walkTowardsKey(uint256 const& id, SharedPtrNodeStack* stack) const
|
||||
SHAMapNodeID nodeID;
|
||||
if (stack != nullptr)
|
||||
stack->push({inNode, nodeID});
|
||||
auto const isv2 = is_v2();
|
||||
|
||||
while (inNode->isInner())
|
||||
{
|
||||
auto const branch = nodeID.selectBranch (id);
|
||||
auto const inner = std::static_pointer_cast<SHAMapInnerNode>(std::move(inNode));
|
||||
if (isv2)
|
||||
{
|
||||
auto n = std::static_pointer_cast<SHAMapInnerNodeV2>(inNode);
|
||||
if (!n->has_common_prefix(id))
|
||||
return nullptr;
|
||||
}
|
||||
auto const branch = nodeID.selectBranch (id);
|
||||
if (inner->isEmptyBranch (branch))
|
||||
return nullptr;
|
||||
|
||||
inNode = descendThrow (inner, branch);
|
||||
nodeID = nodeID.getChildNodeID (branch);
|
||||
if (isv2)
|
||||
{
|
||||
if (inNode->isInner())
|
||||
{
|
||||
auto n = std::dynamic_pointer_cast<SHAMapInnerNodeV2>(inNode);
|
||||
assert(n);
|
||||
nodeID = SHAMapNodeID{n->depth(), n->common()};
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeID = SHAMapNodeID{64, inNode->key()};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeID = nodeID.getChildNodeID (branch);
|
||||
}
|
||||
if (stack != nullptr)
|
||||
stack->push({inNode, nodeID});
|
||||
}
|
||||
@@ -158,6 +248,26 @@ SHAMap::fetchNodeFromDB (SHAMapHash const& hash) const
|
||||
{
|
||||
node = SHAMapAbstractNode::make(obj->getData(),
|
||||
0, snfPREFIX, hash, true, f_.journal());
|
||||
if (node && node->isInner())
|
||||
{
|
||||
bool isv2 = std::dynamic_pointer_cast<SHAMapInnerNodeV2>(node) != nullptr;
|
||||
if (isv2 != is_v2())
|
||||
{
|
||||
auto root = std::dynamic_pointer_cast<SHAMapInnerNode>(root_);
|
||||
assert(root);
|
||||
assert(root->isEmpty());
|
||||
if (isv2)
|
||||
{
|
||||
auto temp = make_v2();
|
||||
swap(temp->root_, const_cast<std::shared_ptr<SHAMapAbstractNode>&>(root_));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto temp = make_v1();
|
||||
swap(temp->root_, const_cast<std::shared_ptr<SHAMapAbstractNode>&>(root_));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node)
|
||||
canonicalize (hash, node);
|
||||
}
|
||||
@@ -189,6 +299,26 @@ SHAMap::checkFilter(SHAMapHash const& hash,
|
||||
{
|
||||
node = SHAMapAbstractNode::make(
|
||||
nodeData, 0, snfPREFIX, hash, true, f_.journal ());
|
||||
if (node && node->isInner())
|
||||
{
|
||||
bool isv2 = std::dynamic_pointer_cast<SHAMapInnerNodeV2>(node) != nullptr;
|
||||
if (isv2 != is_v2())
|
||||
{
|
||||
auto root = std::dynamic_pointer_cast<SHAMapInnerNode>(root_);
|
||||
assert(root);
|
||||
assert(root->isEmpty());
|
||||
if (isv2)
|
||||
{
|
||||
auto temp = make_v2();
|
||||
swap(temp->root_, const_cast<std::shared_ptr<SHAMapAbstractNode>&>(root_));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto temp = make_v1();
|
||||
swap(temp->root_, const_cast<std::shared_ptr<SHAMapAbstractNode>&>(root_));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node)
|
||||
{
|
||||
filter->gotNode (true, hash,
|
||||
@@ -316,7 +446,6 @@ SHAMap::descend (SHAMapInnerNode * parent, SHAMapNodeID const& parentID,
|
||||
assert ((branch >= 0) && (branch < 16));
|
||||
assert (!parent->isEmptyBranch (branch));
|
||||
|
||||
SHAMapNodeID childID = parentID.getChildNodeID (branch);
|
||||
SHAMapAbstractNode* child = parent->getChildPointer (branch);
|
||||
auto const& childHash = parent->getChildHash (branch);
|
||||
|
||||
@@ -331,7 +460,17 @@ SHAMap::descend (SHAMapInnerNode * parent, SHAMapNodeID const& parentID,
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair (child, childID);
|
||||
if (child && is_v2())
|
||||
{
|
||||
if (child->isInner())
|
||||
{
|
||||
auto n = static_cast<SHAMapInnerNodeV2*>(child);
|
||||
return std::make_pair(child, SHAMapNodeID{n->depth(), n->key()});
|
||||
}
|
||||
return std::make_pair(child, SHAMapNodeID{64, child->key()});
|
||||
}
|
||||
|
||||
return std::make_pair (child, parentID.getChildNodeID (branch));
|
||||
}
|
||||
|
||||
SHAMapAbstractNode*
|
||||
@@ -363,9 +502,17 @@ SHAMap::descendAsync (SHAMapInnerNode* parent, int branch,
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
ptr = SHAMapAbstractNode::make(
|
||||
obj->getData(), 0, snfPREFIX, hash, true, f_.journal ());
|
||||
ptr = SHAMapAbstractNode::make(obj->getData(), 0, snfPREFIX,
|
||||
hash, true, f_.journal());
|
||||
|
||||
if (ptr && ptr->isInner())
|
||||
{
|
||||
bool isv2 = std::dynamic_pointer_cast<SHAMapInnerNodeV2>(ptr) != nullptr;
|
||||
if (isv2 != is_v2())
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
if (ptr && backed_)
|
||||
canonicalize (hash, ptr);
|
||||
}
|
||||
@@ -398,22 +545,52 @@ SHAMap::unshareNode (std::shared_ptr<Node> node, SHAMapNodeID const& nodeID)
|
||||
|
||||
SHAMapTreeNode*
|
||||
SHAMap::firstBelow(std::shared_ptr<SHAMapAbstractNode> node,
|
||||
SharedPtrNodeStack& stack) const
|
||||
SharedPtrNodeStack& stack, int branch) const
|
||||
{
|
||||
// Return the first item at or below this node
|
||||
if (node->isLeaf())
|
||||
return static_cast<SHAMapTreeNode*>(node.get());
|
||||
{
|
||||
auto n = std::static_pointer_cast<SHAMapTreeNode>(node);
|
||||
stack.push({node, {64, n->peekItem()->key()}});
|
||||
return n.get();
|
||||
}
|
||||
auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
|
||||
if (stack.empty())
|
||||
stack.push({inner, SHAMapNodeID{}});
|
||||
else
|
||||
{
|
||||
if (is_v2())
|
||||
{
|
||||
auto inner2 = std::static_pointer_cast<SHAMapInnerNodeV2>(inner);
|
||||
stack.push({inner2, {inner2->depth(), inner2->common()}});
|
||||
}
|
||||
else
|
||||
{
|
||||
stack.push({inner, stack.top().second.getChildNodeID(branch)});
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 16;)
|
||||
{
|
||||
if (!inner->isEmptyBranch(i))
|
||||
{
|
||||
node = descendThrow(inner, i);
|
||||
assert(!stack.empty());
|
||||
stack.push({node, stack.top().second.getChildNodeID(i)});
|
||||
if (node->isLeaf())
|
||||
return static_cast<SHAMapTreeNode*>(node.get());
|
||||
{
|
||||
auto n = std::static_pointer_cast<SHAMapTreeNode>(node);
|
||||
stack.push({n, {64, n->peekItem()->key()}});
|
||||
return n.get();
|
||||
}
|
||||
inner = std::static_pointer_cast<SHAMapInnerNode>(node);
|
||||
if (is_v2())
|
||||
{
|
||||
auto inner2 = std::static_pointer_cast<SHAMapInnerNodeV2>(inner);
|
||||
stack.push({inner2, {inner2->depth(), inner2->common()}});
|
||||
}
|
||||
else
|
||||
{
|
||||
stack.push({inner, stack.top().second.getChildNodeID(branch)});
|
||||
}
|
||||
i = 0; // scan all 16 branches of this new node
|
||||
}
|
||||
else
|
||||
@@ -464,11 +641,10 @@ SHAMap::onlyBelow (SHAMapAbstractNode* node) const
|
||||
static std::shared_ptr<
|
||||
SHAMapItem const> const nullConstSHAMapItem;
|
||||
|
||||
SHAMapItem const*
|
||||
SHAMapTreeNode const*
|
||||
SHAMap::peekFirstItem(SharedPtrNodeStack& stack) const
|
||||
{
|
||||
assert(stack.empty());
|
||||
stack.push({root_, SHAMapNodeID{}});
|
||||
SHAMapTreeNode* node = firstBelow(root_, stack);
|
||||
if (!node)
|
||||
{
|
||||
@@ -476,10 +652,10 @@ SHAMap::peekFirstItem(SharedPtrNodeStack& stack) const
|
||||
stack.pop();
|
||||
return nullptr;
|
||||
}
|
||||
return node->peekItem().get();
|
||||
return node;
|
||||
}
|
||||
|
||||
SHAMapItem const*
|
||||
SHAMapTreeNode const*
|
||||
SHAMap::peekNextItem(uint256 const& id, SharedPtrNodeStack& stack) const
|
||||
{
|
||||
assert(!stack.empty());
|
||||
@@ -496,13 +672,11 @@ SHAMap::peekNextItem(uint256 const& id, SharedPtrNodeStack& stack) const
|
||||
if (!inner->isEmptyBranch(i))
|
||||
{
|
||||
node = descendThrow(inner, i);
|
||||
nodeID = nodeID.getChildNodeID(i);
|
||||
stack.push({node, nodeID});
|
||||
auto leaf = firstBelow(node, stack);
|
||||
auto leaf = firstBelow(node, stack, i);
|
||||
if (!leaf)
|
||||
Throw<SHAMapMissingNode> (type_, id);
|
||||
assert(leaf->isLeaf());
|
||||
return leaf->peekItem().get();
|
||||
return leaf;
|
||||
}
|
||||
}
|
||||
stack.pop();
|
||||
@@ -555,6 +729,7 @@ SHAMap::upper_bound(uint256 const& id) const
|
||||
walkTowardsKey(id, &stack);
|
||||
std::shared_ptr<SHAMapAbstractNode> node;
|
||||
SHAMapNodeID nodeID;
|
||||
auto const isv2 = is_v2();
|
||||
while (!stack.empty())
|
||||
{
|
||||
std::tie(node, nodeID) = stack.top();
|
||||
@@ -567,14 +742,27 @@ SHAMap::upper_bound(uint256 const& id) const
|
||||
else
|
||||
{
|
||||
auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
|
||||
for (auto branch = nodeID.selectBranch(id) + 1; branch < 16; ++branch)
|
||||
int branch;
|
||||
if (isv2)
|
||||
{
|
||||
auto n = std::static_pointer_cast<SHAMapInnerNodeV2>(inner);
|
||||
if (n->has_common_prefix(id))
|
||||
branch = nodeID.selectBranch(id) + 1;
|
||||
else if (id < n->common())
|
||||
branch = 0;
|
||||
else
|
||||
branch = 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
branch = nodeID.selectBranch(id) + 1;
|
||||
}
|
||||
for (; branch < 16; ++branch)
|
||||
{
|
||||
if (!inner->isEmptyBranch(branch))
|
||||
{
|
||||
node = descendThrow(inner, branch);
|
||||
nodeID = nodeID.getChildNodeID(branch);
|
||||
stack.push({node, nodeID});
|
||||
auto leaf = firstBelow(node, stack);
|
||||
auto leaf = firstBelow(node, stack, branch);
|
||||
if (!leaf)
|
||||
Throw<SHAMapMissingNode> (type_, id);
|
||||
return const_iterator(this, leaf->peekItem().get(),
|
||||
@@ -615,61 +803,76 @@ bool SHAMap::delItem (uint256 const& id)
|
||||
|
||||
// What gets attached to the end of the chain
|
||||
// (For now, nothing, since we deleted the leaf)
|
||||
SHAMapHash prevHash;
|
||||
std::shared_ptr<SHAMapAbstractNode> prevNode;
|
||||
|
||||
while (!stack.empty ())
|
||||
{
|
||||
auto node = std::dynamic_pointer_cast<SHAMapInnerNode>(stack.top ().first);
|
||||
SHAMapNodeID nodeID = stack.top ().second;
|
||||
stack.pop ();
|
||||
|
||||
assert (node);
|
||||
auto node = std::static_pointer_cast<SHAMapInnerNode>(stack.top().first);
|
||||
SHAMapNodeID nodeID = stack.top().second;
|
||||
stack.pop();
|
||||
|
||||
node = unshareNode(std::move(node), nodeID);
|
||||
node->setChild (nodeID.selectBranch (id), prevNode);
|
||||
node->setChild(nodeID.selectBranch(id), prevNode);
|
||||
|
||||
if (!nodeID.isRoot ())
|
||||
{
|
||||
// we may have made this a node with 1 or 0 children
|
||||
// And, if so, we need to remove this branch
|
||||
int bc = node->getBranchCount ();
|
||||
|
||||
if (bc == 0)
|
||||
int bc = node->getBranchCount();
|
||||
if (is_v2())
|
||||
{
|
||||
// no children below this branch
|
||||
prevHash.zero();
|
||||
prevNode.reset ();
|
||||
}
|
||||
else if (bc == 1)
|
||||
{
|
||||
// If there's only one item, pull up on the thread
|
||||
std::shared_ptr<SHAMapItem const> item = onlyBelow (node.get ());
|
||||
|
||||
if (item)
|
||||
assert(bc != 0);
|
||||
if (bc == 1)
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
if (!node->isEmptyBranch (i))
|
||||
{
|
||||
node->setChild (i, nullptr);
|
||||
prevNode = descendThrow(node, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
prevNode = std::make_shared<SHAMapTreeNode>(item, type, node->getSeq());
|
||||
prevHash = prevNode->getNodeHash();
|
||||
}
|
||||
else
|
||||
else // bc >= 2
|
||||
{
|
||||
prevHash = node->getNodeHash ();
|
||||
prevNode = std::move (node);
|
||||
// This node is now the end of the branch
|
||||
prevNode = std::move(node);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This node is now the end of the branch
|
||||
prevHash = node->getNodeHash ();
|
||||
prevNode = std::move (node);
|
||||
if (bc == 0)
|
||||
{
|
||||
// no children below this branch
|
||||
prevNode.reset ();
|
||||
}
|
||||
else if (bc == 1)
|
||||
{
|
||||
// If there's only one item, pull up on the thread
|
||||
auto item = onlyBelow (node.get ());
|
||||
|
||||
if (item)
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
if (!node->isEmptyBranch (i))
|
||||
{
|
||||
node->setChild (i, nullptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
prevNode = std::make_shared<SHAMapTreeNode>(item, type, node->getSeq());
|
||||
}
|
||||
else
|
||||
{
|
||||
prevNode = std::move (node);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This node is now the end of the branch
|
||||
prevNode = std::move (node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -677,6 +880,20 @@ bool SHAMap::delItem (uint256 const& id)
|
||||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
uint256
|
||||
prefix(unsigned depth, uint256 const& key)
|
||||
{
|
||||
uint256 r{};
|
||||
auto x = r.begin();
|
||||
auto y = key.begin();
|
||||
for (auto i = 0; i < depth/2; ++i, ++x, ++y)
|
||||
*x = *y;
|
||||
if (depth & 1)
|
||||
*x = *y & 0xF0;
|
||||
return r;
|
||||
}
|
||||
|
||||
bool
|
||||
SHAMap::addGiveItem (std::shared_ptr<SHAMapItem const> const& item,
|
||||
bool isTransaction, bool hasMeta)
|
||||
@@ -705,48 +922,97 @@ SHAMap::addGiveItem (std::shared_ptr<SHAMapItem const> const& item,
|
||||
return false;
|
||||
}
|
||||
node = unshareNode(std::move(node), nodeID);
|
||||
if (node->isInner ())
|
||||
if (is_v2())
|
||||
{
|
||||
// easy case, we end on an inner node
|
||||
auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
|
||||
int branch = nodeID.selectBranch (tag);
|
||||
assert (inner->isEmptyBranch (branch));
|
||||
auto newNode = std::make_shared<SHAMapTreeNode> (item, type, seq_);
|
||||
inner->setChild (branch, newNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
// this is a leaf node that has to be made an inner node holding two items
|
||||
auto leaf = std::static_pointer_cast<SHAMapTreeNode>(node);
|
||||
std::shared_ptr<SHAMapItem const> otherItem = leaf->peekItem ();
|
||||
assert (otherItem && (tag != otherItem->key()));
|
||||
|
||||
node = std::make_shared<SHAMapInnerNode>(node->getSeq());
|
||||
|
||||
int b1, b2;
|
||||
|
||||
while ((b1 = nodeID.selectBranch (tag)) ==
|
||||
(b2 = nodeID.selectBranch (otherItem->key())))
|
||||
if (node->isInner())
|
||||
{
|
||||
stack.push ({node, nodeID});
|
||||
|
||||
// we need a new inner node, since both go on same branch at this level
|
||||
nodeID = nodeID.getChildNodeID (b1);
|
||||
node = std::make_shared<SHAMapInnerNode> (seq_);
|
||||
auto inner = std::static_pointer_cast<SHAMapInnerNodeV2>(node);
|
||||
if (inner->has_common_prefix(tag))
|
||||
{
|
||||
int branch = nodeID.selectBranch(tag);
|
||||
assert(inner->isEmptyBranch(branch));
|
||||
auto newNode = std::make_shared<SHAMapTreeNode>(item, type, seq_);
|
||||
inner->setChild(branch, newNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(!stack.empty());
|
||||
auto parent = unshareNode(
|
||||
std::static_pointer_cast<SHAMapInnerNodeV2>(stack.top().first),
|
||||
stack.top().second);
|
||||
stack.top().first = parent;
|
||||
auto parent_depth = parent->depth();
|
||||
auto depth = inner->get_common_prefix(tag);
|
||||
auto new_inner = std::make_shared<SHAMapInnerNodeV2>(seq_);
|
||||
auto nodeID = SHAMapNodeID{depth, prefix(depth, inner->common())};
|
||||
new_inner->setChild(nodeID.selectBranch(inner->common()), inner);
|
||||
nodeID = SHAMapNodeID{depth, prefix(depth, tag)};
|
||||
new_inner->setChild(nodeID.selectBranch(tag),
|
||||
std::make_shared<SHAMapTreeNode>(item, type, seq_));
|
||||
new_inner->set_common(depth, prefix(depth, tag));
|
||||
nodeID = SHAMapNodeID{parent_depth, prefix(parent_depth, tag)};
|
||||
parent->setChild(nodeID.selectBranch(tag), new_inner);
|
||||
node = new_inner;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto leaf = std::static_pointer_cast<SHAMapTreeNode>(node);
|
||||
auto inner = std::make_shared<SHAMapInnerNodeV2>(seq_);
|
||||
inner->setChildren(leaf, std::make_shared<SHAMapTreeNode>(item, type, seq_));
|
||||
assert(!stack.empty());
|
||||
auto parent = unshareNode(
|
||||
std::static_pointer_cast<SHAMapInnerNodeV2>(stack.top().first),
|
||||
stack.top().second);
|
||||
stack.top().first = parent;
|
||||
node = inner;
|
||||
}
|
||||
}
|
||||
else // !is_v2()
|
||||
{
|
||||
if (node->isInner ())
|
||||
{
|
||||
// easy case, we end on an inner node
|
||||
auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
|
||||
int branch = nodeID.selectBranch (tag);
|
||||
assert (inner->isEmptyBranch (branch));
|
||||
auto newNode = std::make_shared<SHAMapTreeNode> (item, type, seq_);
|
||||
inner->setChild (branch, newNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
// this is a leaf node that has to be made an inner node holding two items
|
||||
auto leaf = std::static_pointer_cast<SHAMapTreeNode>(node);
|
||||
std::shared_ptr<SHAMapItem const> otherItem = leaf->peekItem ();
|
||||
assert (otherItem && (tag != otherItem->key()));
|
||||
|
||||
// we can add the two leaf nodes here
|
||||
assert (node->isInner ());
|
||||
node = std::make_shared<SHAMapInnerNode>(node->getSeq());
|
||||
|
||||
std::shared_ptr<SHAMapTreeNode> newNode =
|
||||
std::make_shared<SHAMapTreeNode> (item, type, seq_);
|
||||
assert (newNode->isValid () && newNode->isLeaf ());
|
||||
auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
|
||||
inner->setChild (b1, newNode);
|
||||
int b1, b2;
|
||||
|
||||
newNode = std::make_shared<SHAMapTreeNode> (otherItem, type, seq_);
|
||||
assert (newNode->isValid () && newNode->isLeaf ());
|
||||
inner->setChild (b2, newNode);
|
||||
while ((b1 = nodeID.selectBranch (tag)) ==
|
||||
(b2 = nodeID.selectBranch (otherItem->key())))
|
||||
{
|
||||
stack.push ({node, nodeID});
|
||||
|
||||
// we need a new inner node, since both go on same branch at this level
|
||||
nodeID = nodeID.getChildNodeID (b1);
|
||||
node = std::make_shared<SHAMapInnerNode> (seq_);
|
||||
}
|
||||
|
||||
// we can add the two leaf nodes here
|
||||
assert (node->isInner ());
|
||||
|
||||
std::shared_ptr<SHAMapTreeNode> newNode =
|
||||
std::make_shared<SHAMapTreeNode> (item, type, seq_);
|
||||
assert (newNode->isValid () && newNode->isLeaf ());
|
||||
auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
|
||||
inner->setChild (b1, newNode);
|
||||
|
||||
newNode = std::make_shared<SHAMapTreeNode> (otherItem, type, seq_);
|
||||
assert (newNode->isValid () && newNode->isLeaf ());
|
||||
inner->setChild (b2, newNode);
|
||||
}
|
||||
}
|
||||
|
||||
dirtyUp (stack, tag, node);
|
||||
@@ -931,7 +1197,10 @@ SHAMap::walkSubTree (bool doWrite, NodeObjectType t, std::uint32_t seq)
|
||||
|
||||
if (node->isEmpty ())
|
||||
{ // replace empty root with a new empty root
|
||||
root_ = std::make_shared <SHAMapInnerNode> (0);
|
||||
if (is_v2())
|
||||
root_ = std::make_shared<SHAMapInnerNodeV2>(0, 0);
|
||||
else
|
||||
root_ = std::make_shared<SHAMapInnerNode>(0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1089,4 +1358,22 @@ SHAMap::canonicalize(SHAMapHash const& hash, std::shared_ptr<SHAMapAbstractNode>
|
||||
f_.treecache().canonicalize (hash.as_uint256(), node);
|
||||
}
|
||||
|
||||
SHAMap::version
|
||||
SHAMap::get_version() const
|
||||
{
|
||||
if (is_v2())
|
||||
return version{2};
|
||||
return version{1};
|
||||
}
|
||||
|
||||
void
|
||||
SHAMap::invariants() const
|
||||
{
|
||||
(void)getHash(); // update node hashes
|
||||
auto node = root_.get();
|
||||
assert(node != nullptr);
|
||||
assert(!node->isLeaf());
|
||||
node->invariants(is_v2(), true);
|
||||
}
|
||||
|
||||
} // ripple
|
||||
|
||||
@@ -102,7 +102,7 @@ std::string SHAMapNodeID::getRawString () const
|
||||
SHAMapNodeID SHAMapNodeID::getChildNodeID (int m) const
|
||||
{
|
||||
assert ((m >= 0) && (m < 16));
|
||||
assert (mDepth <= 64);
|
||||
assert (mDepth < 64);
|
||||
|
||||
uint256 child (mNodeID);
|
||||
child.begin ()[mDepth / 2] |= (mDepth & 1) ? m : (m << 4);
|
||||
@@ -143,6 +143,25 @@ int SHAMapNodeID::selectBranch (uint256 const& hash) const
|
||||
return branch;
|
||||
}
|
||||
|
||||
bool
|
||||
SHAMapNodeID::has_common_prefix(SHAMapNodeID const& other) const
|
||||
{
|
||||
assert(mDepth <= other.mDepth);
|
||||
auto x = mNodeID.begin();
|
||||
auto y = other.mNodeID.begin();
|
||||
for (unsigned i = 0; i < mDepth/2; ++i, ++x, ++y)
|
||||
{
|
||||
if (*x != *y)
|
||||
return false;
|
||||
}
|
||||
if (mDepth & 1)
|
||||
{
|
||||
auto i = mDepth/2;
|
||||
return (*(mNodeID.begin() + i) & 0xF0) == (*(other.mNodeID.begin() + i) & 0xF0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SHAMapNodeID::dump (beast::Journal journal) const
|
||||
{
|
||||
JLOG(journal.debug()) <<
|
||||
|
||||
@@ -25,27 +25,17 @@
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// VFALCO TODO tidy up this global
|
||||
|
||||
static const uint256 uZero;
|
||||
|
||||
static bool visitLeavesHelper (
|
||||
std::function <void (std::shared_ptr<SHAMapItem const> const&)> const& function,
|
||||
SHAMapAbstractNode& node)
|
||||
{
|
||||
// Adapt visitNodes to visitLeaves
|
||||
if (!node.isInner ())
|
||||
function (static_cast<SHAMapTreeNode&>(node).peekItem ());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
SHAMap::visitLeaves(
|
||||
std::function<void(std::shared_ptr<SHAMapItem const> const& item)> const& leafFunction) const
|
||||
{
|
||||
visitNodes (std::bind (visitLeavesHelper,
|
||||
std::cref (leafFunction), std::placeholders::_1));
|
||||
visitNodes(
|
||||
[&leafFunction](SHAMapAbstractNode& node)
|
||||
{
|
||||
if (!node.isInner())
|
||||
leafFunction(static_cast<SHAMapTreeNode&>(node).peekItem());
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void SHAMap::visitNodes(std::function<bool (SHAMapAbstractNode&)> const& function) const
|
||||
@@ -216,7 +206,10 @@ SHAMap::getMissingNodes(std::size_t max, SHAMapSyncFilter* filter)
|
||||
|
||||
// Switch to processing the child node
|
||||
node = static_cast<SHAMapInnerNode*>(d);
|
||||
nodeID = childID;
|
||||
if (auto v2Node = dynamic_cast<SHAMapInnerNodeV2*>(node))
|
||||
nodeID = SHAMapNodeID{v2Node->depth(), v2Node->key()};
|
||||
else
|
||||
nodeID = childID;
|
||||
firstChild = rand_int(255);
|
||||
currentChild = 0;
|
||||
fullBelow = true;
|
||||
@@ -340,13 +333,20 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted,
|
||||
return false;
|
||||
|
||||
node = descendThrow(inner, branch);
|
||||
nodeID = nodeID.getChildNodeID (branch);
|
||||
if (auto v2Node = dynamic_cast<SHAMapInnerNodeV2*>(node))
|
||||
nodeID = SHAMapNodeID{v2Node->depth(), v2Node->key()};
|
||||
else
|
||||
nodeID = nodeID.getChildNodeID (branch);
|
||||
}
|
||||
|
||||
if (!node || (nodeID != wanted))
|
||||
if (node == nullptr ||
|
||||
(dynamic_cast<SHAMapInnerNodeV2*>(node) != nullptr &&
|
||||
!wanted.has_common_prefix(nodeID)) ||
|
||||
(dynamic_cast<SHAMapInnerNodeV2*>(node) == nullptr && wanted != nodeID))
|
||||
{
|
||||
JLOG(journal_.warn()) <<
|
||||
"peer requested node that is not in the map: " << wanted;
|
||||
JLOG(journal_.warn())
|
||||
<< "peer requested node that is not in the map:\n"
|
||||
<< wanted << " but found\n" << nodeID;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -383,8 +383,12 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted,
|
||||
{
|
||||
if (! inner->isEmptyBranch (i))
|
||||
{
|
||||
auto childID = nodeID.getChildNodeID (i);
|
||||
auto childNode = descendThrow (inner, i);
|
||||
SHAMapNodeID childID;
|
||||
if (auto v2Node = dynamic_cast<SHAMapInnerNodeV2*>(childNode))
|
||||
childID = SHAMapNodeID{v2Node->depth(), v2Node->key()};
|
||||
else
|
||||
childID = nodeID.getChildNodeID (i);
|
||||
|
||||
if (childNode->isInner () &&
|
||||
((depth > 1) || (bc == 1)))
|
||||
@@ -430,7 +434,7 @@ SHAMapAddNode SHAMap::addRootNode (SHAMapHash const& hash, Blob const& rootNode,
|
||||
|
||||
assert (seq_ >= 1);
|
||||
auto node = SHAMapAbstractNode::make(
|
||||
rootNode, 0, format, SHAMapHash{uZero}, false, f_.journal ());
|
||||
rootNode, 0, format, SHAMapHash{}, false, f_.journal ());
|
||||
if (!node || !node->isValid() || node->getNodeHash () != hash)
|
||||
return SHAMapAddNode::invalid ();
|
||||
|
||||
@@ -467,6 +471,8 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
|
||||
}
|
||||
|
||||
std::uint32_t generation = f_.fullbelow().getGeneration();
|
||||
auto newNode = SHAMapAbstractNode::make(rawNode, 0, snfWIRE,
|
||||
SHAMapHash{}, false, f_.journal(), node);
|
||||
SHAMapNodeID iNodeID;
|
||||
auto iNode = root_.get();
|
||||
|
||||
@@ -490,9 +496,10 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
|
||||
auto prevNode = inner;
|
||||
std::tie(iNode, iNodeID) = descend(inner, iNodeID, branch, filter);
|
||||
|
||||
if (!iNode)
|
||||
if (iNode == nullptr)
|
||||
{
|
||||
if (iNodeID != node)
|
||||
if ((std::dynamic_pointer_cast<SHAMapInnerNodeV2>(newNode) && !iNodeID.has_common_prefix(node)) ||
|
||||
(!std::dynamic_pointer_cast<SHAMapInnerNodeV2>(newNode) && iNodeID != node))
|
||||
{
|
||||
// Either this node is broken or we didn't request it (yet)
|
||||
JLOG(journal_.warn()) << "unable to hook node " << node;
|
||||
@@ -503,9 +510,6 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
|
||||
return SHAMapAddNode::invalid ();
|
||||
}
|
||||
|
||||
auto newNode = SHAMapAbstractNode::make(
|
||||
rawNode, 0, snfWIRE, SHAMapHash{uZero}, false, f_.journal ());
|
||||
|
||||
if (!newNode || !newNode->isValid() || childHash != newNode->getNodeHash ())
|
||||
{
|
||||
JLOG(journal_.warn()) << "Corrupt node received";
|
||||
@@ -519,9 +523,15 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
|
||||
return SHAMapAddNode::useful ();
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (newNode && newNode->isInner())
|
||||
{
|
||||
bool isv2 = std::dynamic_pointer_cast<SHAMapInnerNodeV2>(newNode) != nullptr;
|
||||
assert(isv2 == is_v2());
|
||||
}
|
||||
#endif
|
||||
if (backed_)
|
||||
canonicalize (childHash, newNode);
|
||||
|
||||
newNode = prevNode->canonicalizeChild (branch, std::move(newNode));
|
||||
|
||||
if (filter)
|
||||
@@ -674,6 +684,11 @@ There's no point in including the leaves of transaction trees.
|
||||
void SHAMap::getFetchPack (SHAMap const* have, bool includeLeaves, int max,
|
||||
std::function<void (SHAMapHash const&, const Blob&)> func) const
|
||||
{
|
||||
if (have != nullptr && have->is_v2() != is_v2())
|
||||
{
|
||||
JLOG(journal_.info()) << "Can not get fetch pack when versions are different.";
|
||||
return;
|
||||
}
|
||||
visitDifferences (have,
|
||||
[includeLeaves, &max, &func] (SHAMapAbstractNode& smn) -> bool
|
||||
{
|
||||
|
||||
@@ -46,7 +46,31 @@ SHAMapInnerNode::clone(std::uint32_t seq) const
|
||||
std::memcpy(p->mHashes, mHashes, sizeof(mHashes));
|
||||
std::unique_lock <std::mutex> lock(childLock);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
p->mChildren[i] = mChildren[i];
|
||||
assert(std::dynamic_pointer_cast<SHAMapInnerNodeV2>(p->mChildren[i]) == nullptr);
|
||||
}
|
||||
return std::move(p);
|
||||
}
|
||||
|
||||
std::shared_ptr<SHAMapAbstractNode>
|
||||
SHAMapInnerNodeV2::clone(std::uint32_t seq) const
|
||||
{
|
||||
auto p = std::make_shared<SHAMapInnerNodeV2>(seq);
|
||||
p->mHash = mHash;
|
||||
p->mIsBranch = mIsBranch;
|
||||
p->mFullBelowGen = mFullBelowGen;
|
||||
std::memcpy(p->mHashes, mHashes, sizeof(mHashes));
|
||||
p->common_ = common_;
|
||||
p->depth_ = depth_;
|
||||
std::unique_lock <std::mutex> lock(childLock);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
p->mChildren[i] = mChildren[i];
|
||||
if (p->mChildren[i] != nullptr)
|
||||
assert(std::dynamic_pointer_cast<SHAMapInnerNodeV2>(p->mChildren[i]) != nullptr ||
|
||||
std::dynamic_pointer_cast<SHAMapTreeNode>(p->mChildren[i]) != nullptr);
|
||||
}
|
||||
return std::move(p);
|
||||
}
|
||||
|
||||
@@ -75,7 +99,8 @@ SHAMapTreeNode::SHAMapTreeNode (std::shared_ptr<SHAMapItem const> const& item,
|
||||
|
||||
std::shared_ptr<SHAMapAbstractNode>
|
||||
SHAMapAbstractNode::make(Blob const& rawNode, std::uint32_t seq, SHANodeFormat format,
|
||||
SHAMapHash const& hash, bool hashValid, beast::Journal j)
|
||||
SHAMapHash const& hash, bool hashValid, beast::Journal j,
|
||||
SHAMapNodeID const& id)
|
||||
{
|
||||
if (format == snfWIRE)
|
||||
{
|
||||
@@ -86,9 +111,8 @@ SHAMapAbstractNode::make(Blob const& rawNode, std::uint32_t seq, SHANodeFormat f
|
||||
int type = rawNode.back ();
|
||||
int len = s.getLength ();
|
||||
|
||||
if ((type < 0) || (type > 4))
|
||||
if ((type < 0) || (type > 6))
|
||||
return {};
|
||||
|
||||
if (type == 0)
|
||||
{
|
||||
// transaction
|
||||
@@ -176,6 +200,49 @@ SHAMapAbstractNode::make(Blob const& rawNode, std::uint32_t seq, SHANodeFormat f
|
||||
return std::make_shared<SHAMapTreeNode>(item, tnTRANSACTION_MD, seq, hash);
|
||||
return std::make_shared<SHAMapTreeNode>(item, tnTRANSACTION_MD, seq);
|
||||
}
|
||||
else if (type == 5)
|
||||
{
|
||||
// full inner
|
||||
if (len != 512)
|
||||
Throw<std::runtime_error> ("invalid FI node");
|
||||
|
||||
auto ret = std::make_shared<SHAMapInnerNodeV2>(seq);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
s.get256 (ret->mHashes[i].as_uint256(), i * 32);
|
||||
|
||||
if (ret->mHashes[i].isNonZero ())
|
||||
ret->mIsBranch |= (1 << i);
|
||||
}
|
||||
ret->set_common(id.getDepth(), id.getNodeID());
|
||||
if (hashValid)
|
||||
ret->mHash = hash;
|
||||
else
|
||||
ret->updateHash();
|
||||
return ret;
|
||||
}
|
||||
else if (type == 6)
|
||||
{
|
||||
auto ret = std::make_shared<SHAMapInnerNodeV2>(seq);
|
||||
// compressed inner
|
||||
for (int i = 0; i < (len / 33); ++i)
|
||||
{
|
||||
int pos;
|
||||
if (! s.get8 (pos, 32 + (i * 33)))
|
||||
Throw<std::runtime_error> ("short CI node");
|
||||
if ((pos < 0) || (pos >= 16))
|
||||
Throw<std::runtime_error> ("invalid CI node");
|
||||
s.get256 (ret->mHashes[pos].as_uint256(), i * 33);
|
||||
if (ret->mHashes[pos].isNonZero ())
|
||||
ret->mIsBranch |= (1 << pos);
|
||||
}
|
||||
ret->set_common(id.getDepth(), id.getNodeID());
|
||||
if (hashValid)
|
||||
ret->mHash = hash;
|
||||
else
|
||||
ret->updateHash();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
else if (format == snfPREFIX)
|
||||
@@ -224,11 +291,20 @@ SHAMapAbstractNode::make(Blob const& rawNode, std::uint32_t seq, SHANodeFormat f
|
||||
return std::make_shared<SHAMapTreeNode>(item, tnACCOUNT_STATE, seq, hash);
|
||||
return std::make_shared<SHAMapTreeNode>(item, tnACCOUNT_STATE, seq);
|
||||
}
|
||||
else if (prefix == HashPrefix::innerNode)
|
||||
else if ((prefix == HashPrefix::innerNode) || (prefix == HashPrefix::innerNodeV2))
|
||||
{
|
||||
if (s.getLength () != 512)
|
||||
auto len = s.getLength();
|
||||
bool isV2 = (prefix == HashPrefix::innerNodeV2);
|
||||
|
||||
if ((len < 512) || (!isV2 && (len != 512)))
|
||||
Throw<std::runtime_error> ("invalid PIN node");
|
||||
auto ret = std::make_shared<SHAMapInnerNode>(seq);
|
||||
|
||||
std::shared_ptr<SHAMapInnerNode> ret;
|
||||
if (isV2)
|
||||
ret = std::make_shared<SHAMapInnerNodeV2>(seq);
|
||||
else
|
||||
ret = std::make_shared<SHAMapInnerNode>(seq);
|
||||
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
s.get256 (ret->mHashes[i].as_uint256(), i * 32);
|
||||
@@ -236,6 +312,22 @@ SHAMapAbstractNode::make(Blob const& rawNode, std::uint32_t seq, SHANodeFormat f
|
||||
if (ret->mHashes[i].isNonZero ())
|
||||
ret->mIsBranch |= (1 << i);
|
||||
}
|
||||
|
||||
if (isV2)
|
||||
{
|
||||
auto temp = std::static_pointer_cast<SHAMapInnerNodeV2>(ret);
|
||||
s.get8(temp->depth_, 512);
|
||||
auto n = (temp->depth_ + 1) / 2;
|
||||
if (len != 512 + 1 + n)
|
||||
Throw<std::runtime_error> ("invalid PIN node");
|
||||
auto x = temp->common_.begin();
|
||||
for (auto i = 0; i < n; ++i, ++x)
|
||||
{
|
||||
int byte;
|
||||
s.get8(byte, 512+1+i);
|
||||
*x = byte;
|
||||
}
|
||||
}
|
||||
if (hashValid)
|
||||
ret->mHash = hash;
|
||||
else
|
||||
@@ -349,7 +441,7 @@ SHAMapInnerNode::addRaw(Serializer& s, SHANodeFormat format) const
|
||||
for (int i = 0; i < 16; ++i)
|
||||
s.add256 (mHashes[i].as_uint256());
|
||||
}
|
||||
else
|
||||
else // format == snfWIRE
|
||||
{
|
||||
if (getBranchCount () < 12)
|
||||
{
|
||||
@@ -376,6 +468,33 @@ SHAMapInnerNode::addRaw(Serializer& s, SHANodeFormat format) const
|
||||
assert (false);
|
||||
}
|
||||
|
||||
void
|
||||
SHAMapInnerNodeV2::addRaw(Serializer& s, SHANodeFormat format) const
|
||||
{
|
||||
if (format == snfPREFIX)
|
||||
{
|
||||
s.add32 (HashPrefix::innerNodeV2);
|
||||
|
||||
for (int i = 0 ; i < 16; ++i)
|
||||
s.add256 (mHashes[i].as_uint256());
|
||||
|
||||
s.add8(depth_);
|
||||
|
||||
auto x = common_.begin();
|
||||
for (auto i = 0; i < (depth_+1)/2; ++i, ++x)
|
||||
s.add8(*x);
|
||||
}
|
||||
else
|
||||
{
|
||||
SHAMapInnerNode::addRaw(s, format);
|
||||
if (format == snfWIRE)
|
||||
{
|
||||
auto& data = s.modData();
|
||||
data.back() += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SHAMapTreeNode::addRaw(Serializer& s, SHANodeFormat format) const
|
||||
{
|
||||
@@ -593,5 +712,187 @@ SHAMapInnerNode::canonicalizeChild(int branch, std::shared_ptr<SHAMapAbstractNod
|
||||
return node;
|
||||
}
|
||||
|
||||
bool
|
||||
SHAMapInnerNodeV2::has_common_prefix(uint256 const& key) const
|
||||
{
|
||||
auto x = common_.begin();
|
||||
auto y = key.begin();
|
||||
for (unsigned i = 0; i < depth_/2; ++i, ++x, ++y)
|
||||
{
|
||||
if (*x != *y)
|
||||
return false;
|
||||
}
|
||||
if (depth_ & 1)
|
||||
{
|
||||
auto i = depth_/2;
|
||||
return (*(common_.begin() + i) & 0xF0) == (*(key.begin() + i) & 0xF0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
SHAMapInnerNodeV2::get_common_prefix(uint256 const& key) const
|
||||
{
|
||||
auto x = common_.begin();
|
||||
auto y = key.begin();
|
||||
auto r = 0;
|
||||
for (unsigned i = 0; i < depth_/2; ++i, ++x, ++y, r += 2)
|
||||
{
|
||||
if (*x != *y)
|
||||
{
|
||||
if ((*x & 0xF0) == (*y & 0xF0))
|
||||
++r;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
if (depth_ & 1)
|
||||
{
|
||||
auto i = depth_/2;
|
||||
if ((*(common_.begin() + i) & 0xF0) == (*(key.begin() + i) & 0xF0))
|
||||
++r;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
SHAMapInnerNodeV2::setChildren(std::shared_ptr<SHAMapTreeNode> const& child1,
|
||||
std::shared_ptr<SHAMapTreeNode> const& child2)
|
||||
{
|
||||
assert(child1->peekItem()->key() != child2->peekItem()->key());
|
||||
auto k1 = child1->peekItem()->key().begin();
|
||||
auto k2 = child2->peekItem()->key().begin();
|
||||
auto k = common_.begin();
|
||||
for (depth_ = 0; *k1 == *k2; ++depth_, ++k1, ++k2, ++k)
|
||||
*k = *k1;
|
||||
unsigned b1;
|
||||
unsigned b2;
|
||||
if ((*k1 & 0xF0) == (*k2 & 0xF0))
|
||||
{
|
||||
*k = *k1 & 0xF0;
|
||||
b1 = *k1 & 0x0F;
|
||||
b2 = *k2 & 0x0F;
|
||||
depth_ = 2*depth_ + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
b1 = *k1 >> 4;
|
||||
b2 = *k2 >> 4;
|
||||
depth_ = 2*depth_;
|
||||
}
|
||||
mChildren[b1] = child1;
|
||||
mIsBranch |= 1 << b1;
|
||||
mChildren[b2] = child2;
|
||||
mIsBranch |= 1 << b2;
|
||||
}
|
||||
|
||||
void
|
||||
SHAMapInnerNodeV2::set_common(int depth, uint256 const& common)
|
||||
{
|
||||
depth_ = depth;
|
||||
common_ = common;
|
||||
}
|
||||
|
||||
uint256 const&
|
||||
SHAMapInnerNode::key() const
|
||||
{
|
||||
Throw<std::logic_error>("SHAMapInnerNode::key() should never be called");
|
||||
static uint256 x;
|
||||
return x;
|
||||
}
|
||||
|
||||
uint256 const&
|
||||
SHAMapInnerNodeV2::key() const
|
||||
{
|
||||
return common_;
|
||||
}
|
||||
|
||||
uint256 const&
|
||||
SHAMapTreeNode::key() const
|
||||
{
|
||||
return mItem->key();
|
||||
}
|
||||
|
||||
void
|
||||
SHAMapInnerNode::invariants(bool is_v2, bool is_root) const
|
||||
{
|
||||
assert(!is_v2);
|
||||
assert(mType == tnINNER);
|
||||
unsigned count = 0;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
if (mHashes[i].isNonZero())
|
||||
{
|
||||
assert((mIsBranch & (1 << i)) != 0);
|
||||
if (mChildren[i] != nullptr)
|
||||
mChildren[i]->invariants(is_v2);
|
||||
++count;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert((mIsBranch & (1 << i)) == 0);
|
||||
}
|
||||
}
|
||||
if (!is_root)
|
||||
{
|
||||
assert(mHash.isNonZero());
|
||||
assert(count >= 1);
|
||||
}
|
||||
assert((count == 0) ? mHash.isZero() : mHash.isNonZero());
|
||||
}
|
||||
|
||||
void
|
||||
SHAMapInnerNodeV2::invariants(bool is_v2, bool is_root) const
|
||||
{
|
||||
assert(is_v2);
|
||||
assert(mType == tnINNER);
|
||||
unsigned count = 0;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
if (mHashes[i].isNonZero())
|
||||
{
|
||||
assert((mIsBranch & (1 << i)) != 0);
|
||||
if (mChildren[i] != nullptr)
|
||||
{
|
||||
assert(mHashes[i] == mChildren[i]->getNodeHash());
|
||||
#ifndef NDEBUG
|
||||
auto const& childID = mChildren[i]->key();
|
||||
|
||||
// Make sure this child it attached to the correct branch
|
||||
SHAMapNodeID nodeID {depth(), common()};
|
||||
assert (i == nodeID.selectBranch(childID));
|
||||
#endif
|
||||
assert(has_common_prefix(childID));
|
||||
mChildren[i]->invariants(is_v2);
|
||||
}
|
||||
++count;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert((mIsBranch & (1 << i)) == 0);
|
||||
}
|
||||
}
|
||||
if (!is_root)
|
||||
{
|
||||
assert(mHash.isNonZero());
|
||||
assert(count >= 2);
|
||||
assert(depth_ > 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(depth_ == 0);
|
||||
}
|
||||
if (count == 0)
|
||||
assert(mHash.isZero());
|
||||
else
|
||||
assert(mHash.isNonZero());
|
||||
}
|
||||
|
||||
void
|
||||
SHAMapTreeNode::invariants(bool, bool) const
|
||||
{
|
||||
assert(mType >= tnTRANSACTION_NM);
|
||||
assert(mHash.isNonZero());
|
||||
assert(mItem != nullptr);
|
||||
}
|
||||
|
||||
} // ripple
|
||||
|
||||
@@ -121,7 +121,7 @@ public:
|
||||
beast::Journal const j; // debug journal
|
||||
TestFamily f(j);
|
||||
std::shared_ptr <Table> t1 (std::make_shared <Table> (
|
||||
SHAMapType::FREE, f));
|
||||
SHAMapType::FREE, f, SHAMap::version{2}));
|
||||
|
||||
pass ();
|
||||
|
||||
|
||||
@@ -28,6 +28,76 @@
|
||||
namespace ripple {
|
||||
namespace tests {
|
||||
|
||||
static_assert( std::is_nothrow_destructible <SHAMap>{}, "");
|
||||
static_assert(!std::is_default_constructible<SHAMap>{}, "");
|
||||
static_assert(!std::is_copy_constructible <SHAMap>{}, "");
|
||||
static_assert(!std::is_copy_assignable <SHAMap>{}, "");
|
||||
static_assert(!std::is_move_constructible <SHAMap>{}, "");
|
||||
static_assert(!std::is_move_assignable <SHAMap>{}, "");
|
||||
|
||||
static_assert( std::is_nothrow_destructible <SHAMap::version>{}, "");
|
||||
static_assert(!std::is_default_constructible <SHAMap::version>{}, "");
|
||||
static_assert( std::is_trivially_copy_constructible<SHAMap::version>{}, "");
|
||||
static_assert( std::is_trivially_copy_assignable <SHAMap::version>{}, "");
|
||||
static_assert( std::is_trivially_move_constructible<SHAMap::version>{}, "");
|
||||
static_assert( std::is_trivially_move_assignable <SHAMap::version>{}, "");
|
||||
|
||||
static_assert( std::is_nothrow_destructible <SHAMap::const_iterator>{}, "");
|
||||
static_assert( std::is_default_constructible<SHAMap::const_iterator>{}, "");
|
||||
static_assert( std::is_copy_constructible <SHAMap::const_iterator>{}, "");
|
||||
static_assert( std::is_copy_assignable <SHAMap::const_iterator>{}, "");
|
||||
static_assert( std::is_move_constructible <SHAMap::const_iterator>{}, "");
|
||||
static_assert( std::is_move_assignable <SHAMap::const_iterator>{}, "");
|
||||
|
||||
static_assert( std::is_nothrow_destructible <SHAMapItem>{}, "");
|
||||
static_assert(!std::is_default_constructible<SHAMapItem>{}, "");
|
||||
static_assert( std::is_copy_constructible <SHAMapItem>{}, "");
|
||||
static_assert( std::is_copy_assignable <SHAMapItem>{}, "");
|
||||
static_assert( std::is_move_constructible <SHAMapItem>{}, "");
|
||||
static_assert( std::is_move_assignable <SHAMapItem>{}, "");
|
||||
|
||||
static_assert( std::is_nothrow_destructible <SHAMapNodeID>{}, "");
|
||||
static_assert( std::is_default_constructible<SHAMapNodeID>{}, "");
|
||||
static_assert( std::is_copy_constructible <SHAMapNodeID>{}, "");
|
||||
static_assert( std::is_copy_assignable <SHAMapNodeID>{}, "");
|
||||
static_assert( std::is_move_constructible <SHAMapNodeID>{}, "");
|
||||
static_assert( std::is_move_assignable <SHAMapNodeID>{}, "");
|
||||
|
||||
static_assert( std::is_nothrow_destructible <SHAMapHash>{}, "");
|
||||
static_assert( std::is_default_constructible<SHAMapHash>{}, "");
|
||||
static_assert( std::is_copy_constructible <SHAMapHash>{}, "");
|
||||
static_assert( std::is_copy_assignable <SHAMapHash>{}, "");
|
||||
static_assert( std::is_move_constructible <SHAMapHash>{}, "");
|
||||
static_assert( std::is_move_assignable <SHAMapHash>{}, "");
|
||||
|
||||
static_assert(!std::is_nothrow_destructible <SHAMapAbstractNode>{}, "");
|
||||
static_assert(!std::is_default_constructible<SHAMapAbstractNode>{}, "");
|
||||
static_assert(!std::is_copy_constructible <SHAMapAbstractNode>{}, "");
|
||||
static_assert(!std::is_copy_assignable <SHAMapAbstractNode>{}, "");
|
||||
static_assert(!std::is_move_constructible <SHAMapAbstractNode>{}, "");
|
||||
static_assert(!std::is_move_assignable <SHAMapAbstractNode>{}, "");
|
||||
|
||||
static_assert( std::is_nothrow_destructible <SHAMapInnerNode>{}, "");
|
||||
static_assert(!std::is_default_constructible<SHAMapInnerNode>{}, "");
|
||||
static_assert(!std::is_copy_constructible <SHAMapInnerNode>{}, "");
|
||||
static_assert(!std::is_copy_assignable <SHAMapInnerNode>{}, "");
|
||||
static_assert(!std::is_move_constructible <SHAMapInnerNode>{}, "");
|
||||
static_assert(!std::is_move_assignable <SHAMapInnerNode>{}, "");
|
||||
|
||||
static_assert( std::is_nothrow_destructible <SHAMapInnerNodeV2>{}, "");
|
||||
static_assert(!std::is_default_constructible<SHAMapInnerNodeV2>{}, "");
|
||||
static_assert(!std::is_copy_constructible <SHAMapInnerNodeV2>{}, "");
|
||||
static_assert(!std::is_copy_assignable <SHAMapInnerNodeV2>{}, "");
|
||||
static_assert(!std::is_move_constructible <SHAMapInnerNodeV2>{}, "");
|
||||
static_assert(!std::is_move_assignable <SHAMapInnerNodeV2>{}, "");
|
||||
|
||||
static_assert( std::is_nothrow_destructible <SHAMapTreeNode>{}, "");
|
||||
static_assert(!std::is_default_constructible<SHAMapTreeNode>{}, "");
|
||||
static_assert(!std::is_copy_constructible <SHAMapTreeNode>{}, "");
|
||||
static_assert(!std::is_copy_assignable <SHAMapTreeNode>{}, "");
|
||||
static_assert(!std::is_move_constructible <SHAMapTreeNode>{}, "");
|
||||
static_assert(!std::is_move_assignable <SHAMapTreeNode>{}, "");
|
||||
|
||||
inline bool operator== (SHAMapItem const& a, SHAMapItem const& b) { return a.key() == b.key(); }
|
||||
inline bool operator!= (SHAMapItem const& a, SHAMapItem const& b) { return a.key() != b.key(); }
|
||||
inline bool operator== (SHAMapItem const& a, uint256 const& b) { return a.key() == b; }
|
||||
@@ -48,11 +118,13 @@ public:
|
||||
|
||||
void run ()
|
||||
{
|
||||
run (true);
|
||||
run (false);
|
||||
run (true, SHAMap::version{1});
|
||||
run (false, SHAMap::version{1});
|
||||
run (true, SHAMap::version{2});
|
||||
run (false, SHAMap::version{2});
|
||||
}
|
||||
|
||||
void run (bool backed)
|
||||
void run (bool backed, SHAMap::version v)
|
||||
{
|
||||
if (backed)
|
||||
testcase ("add/traverse backed");
|
||||
@@ -70,13 +142,16 @@ public:
|
||||
h4.SetHex ("b92891fe4ef6cee585fdc6fda2e09eb4d386363158ec3321b8123e5a772c6ca8");
|
||||
h5.SetHex ("a92891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
|
||||
|
||||
SHAMap sMap (SHAMapType::FREE, f);
|
||||
SHAMap sMap (SHAMapType::FREE, f, v);
|
||||
sMap.invariants();
|
||||
if (! backed)
|
||||
sMap.setUnbacked ();
|
||||
|
||||
SHAMapItem i1 (h1, IntToVUC (1)), i2 (h2, IntToVUC (2)), i3 (h3, IntToVUC (3)), i4 (h4, IntToVUC (4)), i5 (h5, IntToVUC (5));
|
||||
unexpected (!sMap.addItem (SHAMapItem{i2}, true, false), "no add");
|
||||
sMap.invariants();
|
||||
unexpected (!sMap.addItem (SHAMapItem{i1}, true, false), "no add");
|
||||
sMap.invariants();
|
||||
|
||||
auto i = sMap.begin();
|
||||
auto e = sMap.end();
|
||||
@@ -86,8 +161,11 @@ public:
|
||||
++i;
|
||||
unexpected (i != e, "bad traverse");
|
||||
sMap.addItem (SHAMapItem{i4}, true, false);
|
||||
sMap.invariants();
|
||||
sMap.delItem (i2.key());
|
||||
sMap.invariants();
|
||||
sMap.addItem (SHAMapItem{i3}, true, false);
|
||||
sMap.invariants();
|
||||
i = sMap.begin();
|
||||
e = sMap.end();
|
||||
unexpected (i == e || (*i != i1), "bad traverse");
|
||||
@@ -105,13 +183,29 @@ public:
|
||||
|
||||
SHAMapHash mapHash = sMap.getHash ();
|
||||
std::shared_ptr<SHAMap> map2 = sMap.snapShot (false);
|
||||
map2->invariants();
|
||||
unexpected (sMap.getHash () != mapHash, "bad snapshot");
|
||||
unexpected (map2->getHash () != mapHash, "bad snapshot");
|
||||
|
||||
SHAMap::Delta delta;
|
||||
expect(sMap.compare(*map2, delta, 100), "There should be no differences");
|
||||
expect(delta.empty(), "The delta should be empty");
|
||||
|
||||
unexpected (!sMap.delItem (sMap.begin()->key()), "bad mod");
|
||||
sMap.invariants();
|
||||
unexpected (sMap.getHash () == mapHash, "bad snapshot");
|
||||
unexpected (map2->getHash () != mapHash, "bad snapshot");
|
||||
|
||||
expect(sMap.compare(*map2, delta, 100), "There should be 1 difference");
|
||||
expect(delta.size() == 1, "The delta should be size 1");
|
||||
expect(delta.begin()->first == h1, "Should be the first key");
|
||||
expect(delta.begin()->second.first == nullptr, "Must be null");
|
||||
expect(delta.begin()->second.second->key() == h1, "The difference is the first key");
|
||||
|
||||
sMap.dump();
|
||||
|
||||
auto const is_v2 = sMap.is_v2();
|
||||
|
||||
if (backed)
|
||||
testcase ("build/tear backed");
|
||||
else
|
||||
@@ -128,16 +222,30 @@ public:
|
||||
keys[7].SetHex ("292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
|
||||
|
||||
std::vector<uint256> hashes(8);
|
||||
hashes[0].SetHex ("B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C64B6281F7F");
|
||||
hashes[1].SetHex ("FBC195A9592A54AB44010274163CB6BA95F497EC5BA0A8831845467FB2ECE266");
|
||||
hashes[2].SetHex ("4E7D2684B65DFD48937FFB775E20175C43AF0C94066F7D5679F51AE756795B75");
|
||||
hashes[3].SetHex ("7A2F312EB203695FFD164E038E281839EEF06A1B99BFC263F3CECC6C74F93E07");
|
||||
hashes[4].SetHex ("395A6691A372387A703FB0F2C6D2C405DAF307D0817F8F0E207596462B0E3A3E");
|
||||
hashes[5].SetHex ("D044C0A696DE3169CC70AE216A1564D69DE96582865796142CE7D98A84D9DDE4");
|
||||
hashes[6].SetHex ("76DCC77C4027309B5A91AD164083264D70B77B5E43E08AEDA5EBF94361143615");
|
||||
hashes[7].SetHex ("DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA02BBE7230E5");
|
||||
if (is_v2)
|
||||
{
|
||||
hashes[0].SetHex ("B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C64B6281F7F");
|
||||
hashes[1].SetHex ("6A70885D21024F9F4F50E688B365D0E017266F53AE3E77B52AAEF84E167FE942");
|
||||
hashes[2].SetHex ("BA635322BDF510CCFC0578C194C1F87DA2D97C1A55A469F6E7A463BE963663D7");
|
||||
hashes[3].SetHex ("5F751361AC7A161DED4D6EAAC4B587C7C01E50E1F9EC3DD61207BD6B196E7DB1");
|
||||
hashes[4].SetHex ("FC1C57DD4BF15E37961E0B3064C43E60A9DC26EA332D7A6178FE2284901DB49F");
|
||||
hashes[5].SetHex ("4FCDFE944E8E19E35FF60E7BDA7DB21B1CD99670BCF158FEF8F0F8B49BF5C9AD");
|
||||
hashes[6].SetHex ("31F6DE8152FBE77EAD59805631FCDDB71F1BC6A5E9BD8FA3948D82D1CE1F93D6");
|
||||
hashes[7].SetHex ("00895AD3B161D483C4EF7B5469B0D305685222B5102C2C3F614FD84AD50C4B14");
|
||||
}
|
||||
else
|
||||
{
|
||||
hashes[0].SetHex ("B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C64B6281F7F");
|
||||
hashes[1].SetHex ("FBC195A9592A54AB44010274163CB6BA95F497EC5BA0A8831845467FB2ECE266");
|
||||
hashes[2].SetHex ("4E7D2684B65DFD48937FFB775E20175C43AF0C94066F7D5679F51AE756795B75");
|
||||
hashes[3].SetHex ("7A2F312EB203695FFD164E038E281839EEF06A1B99BFC263F3CECC6C74F93E07");
|
||||
hashes[4].SetHex ("395A6691A372387A703FB0F2C6D2C405DAF307D0817F8F0E207596462B0E3A3E");
|
||||
hashes[5].SetHex ("D044C0A696DE3169CC70AE216A1564D69DE96582865796142CE7D98A84D9DDE4");
|
||||
hashes[6].SetHex ("76DCC77C4027309B5A91AD164083264D70B77B5E43E08AEDA5EBF94361143615");
|
||||
hashes[7].SetHex ("DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA02BBE7230E5");
|
||||
}
|
||||
|
||||
SHAMap map (SHAMapType::FREE, f);
|
||||
SHAMap map (SHAMapType::FREE, f, v);
|
||||
if (! backed)
|
||||
map.setUnbacked ();
|
||||
|
||||
@@ -147,11 +255,31 @@ public:
|
||||
SHAMapItem item (keys[i], IntToVUC (i));
|
||||
expect (map.addItem (std::move(item), true, false), "unable to add item");
|
||||
expect (map.getHash().as_uint256() == hashes[i], "bad buildup map hash");
|
||||
map.invariants();
|
||||
}
|
||||
if (v == SHAMap::version{1})
|
||||
{
|
||||
expect(!map.is_v2(), "map should be version 1");
|
||||
auto map_v2 = map.make_v2();
|
||||
expect(map_v2 != nullptr, "make_v2 should never return nullptr");
|
||||
expect(map_v2->is_v2(), "map should be version 2");
|
||||
map_v2->invariants();
|
||||
auto i1 = map.begin();
|
||||
auto e1 = map.end();
|
||||
auto i2 = map_v2->begin();
|
||||
auto e2 = map_v2->end();
|
||||
for (; i1 != e1; ++i1, ++i2)
|
||||
{
|
||||
expect(i2 != e2, "make_v2 size mismatch");
|
||||
expect(*i1 == *i2, "make_v2, item mismatch");
|
||||
}
|
||||
expect(i2 == e2, "make_v2 size mismatch");
|
||||
}
|
||||
for (int i = keys.size() - 1; i >= 0; --i)
|
||||
{
|
||||
expect (map.getHash().as_uint256() == hashes[i], "bad teardown hash");
|
||||
expect (map.delItem (keys[i]), "unable to remove item");
|
||||
map.invariants();
|
||||
}
|
||||
expect (map.getHash() == zero, "bad final empty map hash");
|
||||
}
|
||||
@@ -173,11 +301,14 @@ public:
|
||||
keys[7].SetHex ("292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
|
||||
|
||||
tests::TestFamily f{beast::Journal{}};
|
||||
SHAMap map{SHAMapType::FREE, f};
|
||||
SHAMap map{SHAMapType::FREE, f, v};
|
||||
if (! backed)
|
||||
map.setUnbacked ();
|
||||
for (auto const& k : keys)
|
||||
{
|
||||
map.addItem(SHAMapItem{k, IntToVUC(0)}, true, false);
|
||||
map.invariants();
|
||||
}
|
||||
|
||||
int i = 7;
|
||||
for (auto const& k : map)
|
||||
|
||||
@@ -80,21 +80,54 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
void run ()
|
||||
void run()
|
||||
{
|
||||
std::cerr << "Run, version 1" << std::endl;
|
||||
run(SHAMap::version{1});
|
||||
|
||||
std::cerr << "Run, version 2" << std::endl;
|
||||
run(SHAMap::version{2});
|
||||
}
|
||||
|
||||
void run(SHAMap::version v)
|
||||
{
|
||||
beast::Journal const j; // debug journal
|
||||
TestFamily f(j);
|
||||
SHAMap source (SHAMapType::FREE, f);
|
||||
SHAMap destination (SHAMapType::FREE, f);
|
||||
SHAMap source (SHAMapType::FREE, f, v);
|
||||
SHAMap destination (SHAMapType::FREE, f, v);
|
||||
|
||||
int items = 10000;
|
||||
for (int i = 0; i < items; ++i)
|
||||
{
|
||||
source.addItem (std::move(*makeRandomAS ()), false, false);
|
||||
if (i % 100 == 0)
|
||||
source.invariants();
|
||||
}
|
||||
|
||||
source.invariants();
|
||||
expect (confuseMap (source, 500), "ConfuseMap");
|
||||
source.invariants();
|
||||
|
||||
source.setImmutable ();
|
||||
|
||||
int count = 0;
|
||||
source.visitLeaves([&count](auto const& item)
|
||||
{
|
||||
++count;
|
||||
});
|
||||
expect(count == items, "These must be equal");
|
||||
|
||||
std::vector<SHAMapMissingNode> missingNodes;
|
||||
source.walkMap(missingNodes, 2048);
|
||||
expect(missingNodes.empty(), "should be empty");
|
||||
|
||||
std::vector<SHAMapNodeID> nodeIDs, gotNodeIDs;
|
||||
std::vector< Blob > gotNodes;
|
||||
std::vector<uint256> hashes;
|
||||
|
||||
std::vector<SHAMapNodeID>::iterator nodeIDIterator;
|
||||
std::vector< Blob >::iterator rawNodeIterator;
|
||||
|
||||
destination.setSynching ();
|
||||
|
||||
{
|
||||
@@ -158,6 +191,9 @@ public:
|
||||
destination.clearSynching ();
|
||||
|
||||
expect (source.deepCompare (destination), "Deep Compare");
|
||||
|
||||
std::cerr << "Checking destination invariants" << std::endl;
|
||||
destination.invariants();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user