diff --git a/Builds/CMake/RippledCore.cmake b/Builds/CMake/RippledCore.cmake index 461de2f86..82b27ee18 100644 --- a/Builds/CMake/RippledCore.cmake +++ b/Builds/CMake/RippledCore.cmake @@ -635,7 +635,9 @@ target_sources (rippled PRIVATE src/ripple/shamap/impl/NodeFamily.cpp src/ripple/shamap/impl/SHAMap.cpp src/ripple/shamap/impl/SHAMapDelta.cpp + src/ripple/shamap/impl/SHAMapInnerNode.cpp src/ripple/shamap/impl/SHAMapItem.cpp + src/ripple/shamap/impl/SHAMapLeafNode.cpp src/ripple/shamap/impl/SHAMapNodeID.cpp src/ripple/shamap/impl/SHAMapSync.cpp src/ripple/shamap/impl/SHAMapTreeNode.cpp diff --git a/src/ripple/app/consensus/RCLConsensus.cpp b/src/ripple/app/consensus/RCLConsensus.cpp index fd61ae2bf..d0bb15fca 100644 --- a/src/ripple/app/consensus/RCLConsensus.cpp +++ b/src/ripple/app/consensus/RCLConsensus.cpp @@ -315,9 +315,8 @@ RCLConsensus::Adaptor::onClose( Serializer s(2048); tx.first->add(s); initialSet->addItem( - SHAMapItem(tx.first->getTransactionID(), std::move(s)), - true, - false); + SHAMapNodeType::tnTRANSACTION_NM, + SHAMapItem(tx.first->getTransactionID(), std::move(s))); } // Add pseudo-transactions to the set diff --git a/src/ripple/app/consensus/RCLCxTx.h b/src/ripple/app/consensus/RCLCxTx.h index 14c61a602..45ee55743 100644 --- a/src/ripple/app/consensus/RCLCxTx.h +++ b/src/ripple/app/consensus/RCLCxTx.h @@ -91,7 +91,8 @@ public: insert(Tx const& t) { return map_->addItem( - SHAMapItem{t.id(), t.tx_.peekData()}, true, false); + SHAMapNodeType::tnTRANSACTION_NM, + SHAMapItem{t.id(), t.tx_.peekData()}); } /** Remove a transaction from the set. diff --git a/src/ripple/app/ledger/AccountStateSF.cpp b/src/ripple/app/ledger/AccountStateSF.cpp index 5d3456a9f..1137fae13 100644 --- a/src/ripple/app/ledger/AccountStateSF.cpp +++ b/src/ripple/app/ledger/AccountStateSF.cpp @@ -27,7 +27,7 @@ AccountStateSF::gotNode( SHAMapHash const& nodeHash, std::uint32_t ledgerSeq, Blob&& nodeData, - SHAMapTreeNode::TNType) const + SHAMapNodeType) const { db_.store( hotACCOUNT_NODE, std::move(nodeData), nodeHash.as_uint256(), ledgerSeq); diff --git a/src/ripple/app/ledger/AccountStateSF.h b/src/ripple/app/ledger/AccountStateSF.h index aa1557f98..5829880d5 100644 --- a/src/ripple/app/ledger/AccountStateSF.h +++ b/src/ripple/app/ledger/AccountStateSF.h @@ -42,7 +42,7 @@ public: SHAMapHash const& nodeHash, std::uint32_t ledgerSeq, Blob&& nodeData, - SHAMapTreeNode::TNType type) const override; + SHAMapNodeType type) const override; boost::optional getNode(SHAMapHash const& nodeHash) const override; diff --git a/src/ripple/app/ledger/ConsensusTransSetSF.cpp b/src/ripple/app/ledger/ConsensusTransSetSF.cpp index 336875121..5ae864438 100644 --- a/src/ripple/app/ledger/ConsensusTransSetSF.cpp +++ b/src/ripple/app/ledger/ConsensusTransSetSF.cpp @@ -41,14 +41,14 @@ ConsensusTransSetSF::gotNode( SHAMapHash const& nodeHash, std::uint32_t, Blob&& nodeData, - SHAMapTreeNode::TNType type) const + SHAMapNodeType type) const { if (fromFilter) return; m_nodeCache.insert(nodeHash, nodeData); - if ((type == SHAMapTreeNode::tnTRANSACTION_NM) && (nodeData.size() > 16)) + if ((type == SHAMapNodeType::tnTRANSACTION_NM) && (nodeData.size() > 16)) { // this is a transaction, and we didn't have it JLOG(j_.debug()) diff --git a/src/ripple/app/ledger/ConsensusTransSetSF.h b/src/ripple/app/ledger/ConsensusTransSetSF.h index c413caffa..a010a1ba3 100644 --- a/src/ripple/app/ledger/ConsensusTransSetSF.h +++ b/src/ripple/app/ledger/ConsensusTransSetSF.h @@ -45,7 +45,7 @@ public: SHAMapHash const& nodeHash, std::uint32_t ledgerSeq, Blob&& nodeData, - SHAMapTreeNode::TNType type) const override; + SHAMapNodeType type) const override; boost::optional getNode(SHAMapHash const& nodeHash) const override; diff --git a/src/ripple/app/ledger/Ledger.cpp b/src/ripple/app/ledger/Ledger.cpp index e52aebeb4..9aa5e212c 100644 --- a/src/ripple/app/ledger/Ledger.cpp +++ b/src/ripple/app/ledger/Ledger.cpp @@ -202,7 +202,7 @@ Ledger::Ledger( rawInsert(sle); } - stateMap_->flushDirty(hotACCOUNT_NODE, info_.seq); + stateMap_->flushDirty(hotACCOUNT_NODE); setImmutable(config); } @@ -354,7 +354,7 @@ bool Ledger::addSLE(SLE const& sle) { SHAMapItem item(sle.key(), sle.getSerializer()); - return stateMap_->addItem(std::move(item), false, false); + return stateMap_->addItem(SHAMapNodeType::tnACCOUNT_STATE, std::move(item)); } //------------------------------------------------------------------------------ @@ -499,8 +499,9 @@ Ledger::rawInsert(std::shared_ptr const& sle) { Serializer ss; sle->add(ss); - auto item = std::make_shared(sle->key(), std::move(ss)); - if (!stateMap_->addGiveItem(std::move(item), false, false)) + if (!stateMap_->addGiveItem( + SHAMapNodeType::tnACCOUNT_STATE, + std::make_shared(sle->key(), std::move(ss)))) LogicError("Ledger::rawInsert: key already exists"); } @@ -509,9 +510,9 @@ Ledger::rawReplace(std::shared_ptr const& sle) { Serializer ss; sle->add(ss); - auto item = std::make_shared(sle->key(), std::move(ss)); - - if (!stateMap_->updateGiveItem(std::move(item), false, false)) + if (!stateMap_->updateGiveItem( + SHAMapNodeType::tnACCOUNT_STATE, + std::make_shared(sle->key(), std::move(ss)))) LogicError("Ledger::rawReplace: key not found"); } @@ -527,8 +528,9 @@ Ledger::rawTxInsert( Serializer s(txn->getDataLength() + metaData->getDataLength() + 16); s.addVL(txn->peekData()); s.addVL(metaData->peekData()); - auto item = std::make_shared(key, std::move(s)); - if (!txMap().addGiveItem(std::move(item), true, true)) + if (!txMap().addGiveItem( + SHAMapNodeType::tnTRANSACTION_MD, + std::make_shared(key, std::move(s)))) LogicError("duplicate_tx: " + to_string(key)); } diff --git a/src/ripple/app/ledger/TransactionMaster.h b/src/ripple/app/ledger/TransactionMaster.h index 6c3f13bff..a902fadf9 100644 --- a/src/ripple/app/ledger/TransactionMaster.h +++ b/src/ripple/app/ledger/TransactionMaster.h @@ -69,7 +69,7 @@ public: std::shared_ptr fetch( std::shared_ptr const& item, - SHAMapTreeNode::TNType type, + SHAMapNodeType type, std::uint32_t uCommitLedger); // return value: true = we had the transaction already diff --git a/src/ripple/app/ledger/TransactionStateSF.cpp b/src/ripple/app/ledger/TransactionStateSF.cpp index 5ac9b44fe..e717eef9b 100644 --- a/src/ripple/app/ledger/TransactionStateSF.cpp +++ b/src/ripple/app/ledger/TransactionStateSF.cpp @@ -27,10 +27,10 @@ TransactionStateSF::gotNode( SHAMapHash const& nodeHash, std::uint32_t ledgerSeq, Blob&& nodeData, - SHAMapTreeNode::TNType type) const + SHAMapNodeType type) const { - assert(type != SHAMapTreeNode::tnTRANSACTION_NM); + assert(type != SHAMapNodeType::tnTRANSACTION_NM); db_.store( hotTRANSACTION_NODE, std::move(nodeData), diff --git a/src/ripple/app/ledger/TransactionStateSF.h b/src/ripple/app/ledger/TransactionStateSF.h index bfe33dbe5..c9cae3dc8 100644 --- a/src/ripple/app/ledger/TransactionStateSF.h +++ b/src/ripple/app/ledger/TransactionStateSF.h @@ -42,7 +42,7 @@ public: SHAMapHash const& nodeHash, std::uint32_t ledgerSeq, Blob&& nodeData, - SHAMapTreeNode::TNType type) const override; + SHAMapNodeType type) const override; boost::optional getNode(SHAMapHash const& nodeHash) const override; diff --git a/src/ripple/app/ledger/impl/BuildLedger.cpp b/src/ripple/app/ledger/impl/BuildLedger.cpp index 97592220e..f70b754ab 100644 --- a/src/ripple/app/ledger/impl/BuildLedger.cpp +++ b/src/ripple/app/ledger/impl/BuildLedger.cpp @@ -67,10 +67,8 @@ buildLedgerImpl( // Write the final version of all modified SHAMap // nodes to the node store to preserve the new LCL - int const asf = - built->stateMap().flushDirty(hotACCOUNT_NODE, built->info().seq); - int const tmf = - built->txMap().flushDirty(hotTRANSACTION_NODE, built->info().seq); + int const asf = built->stateMap().flushDirty(hotACCOUNT_NODE); + int const tmf = built->txMap().flushDirty(hotTRANSACTION_NODE); JLOG(j.debug()) << "Flushed " << asf << " accounts and " << tmf << " transaction nodes"; } diff --git a/src/ripple/app/ledger/impl/InboundLedger.cpp b/src/ripple/app/ledger/impl/InboundLedger.cpp index 7513a2248..ffe9e284c 100644 --- a/src/ripple/app/ledger/impl/InboundLedger.cpp +++ b/src/ripple/app/ledger/impl/InboundLedger.cpp @@ -235,36 +235,42 @@ InboundLedger::~InboundLedger() } } -std::vector -InboundLedger::neededTxHashes(int max, SHAMapSyncFilter* filter) const +static std::vector +neededHashes( + uint256 const& root, + SHAMap& map, + int max, + SHAMapSyncFilter* filter) { std::vector ret; - if (mLedger->info().txHash.isNonZero()) + if (!root.isZero()) { - if (mLedger->txMap().getHash().isZero()) - ret.push_back(mLedger->info().txHash); + if (map.getHash().isZero()) + ret.push_back(root); else - ret = mLedger->txMap().getNeededHashes(max, filter); + { + auto mn = map.getMissingNodes(max, filter); + ret.reserve(mn.size()); + for (auto const& n : mn) + ret.push_back(n.second); + } } return ret; } +std::vector +InboundLedger::neededTxHashes(int max, SHAMapSyncFilter* filter) const +{ + return neededHashes(mLedger->info().txHash, mLedger->txMap(), max, filter); +} + std::vector InboundLedger::neededStateHashes(int max, SHAMapSyncFilter* filter) const { - std::vector ret; - - if (mLedger->info().accountHash.isNonZero()) - { - if (mLedger->stateMap().getHash().isZero()) - ret.push_back(mLedger->info().accountHash); - else - ret = mLedger->stateMap().getNeededHashes(max, filter); - } - - return ret; + return neededHashes( + mLedger->info().accountHash, mLedger->stateMap(), max, filter); } LedgerInfo diff --git a/src/ripple/app/ledger/impl/InboundLedgers.cpp b/src/ripple/app/ledger/impl/InboundLedgers.cpp index b7116aba2..eef248989 100644 --- a/src/ripple/app/ledger/impl/InboundLedgers.cpp +++ b/src/ripple/app/ledger/impl/InboundLedgers.cpp @@ -247,8 +247,8 @@ public: if (!node.has_nodeid() || !node.has_nodedata()) return; - auto newNode = SHAMapAbstractNode::makeFromWire( - makeSlice(node.nodedata())); + auto newNode = + SHAMapTreeNode::makeFromWire(makeSlice(node.nodedata())); if (!newNode) return; @@ -257,7 +257,7 @@ public: newNode->serializeWithPrefix(s); app_.getLedgerMaster().addFetchPack( - newNode->getNodeHash().as_uint256(), + newNode->getHash().as_uint256(), std::make_shared(s.begin(), s.end())); } } diff --git a/src/ripple/app/ledger/impl/LedgerMaster.cpp b/src/ripple/app/ledger/impl/LedgerMaster.cpp index 939ec978f..3a21c050d 100644 --- a/src/ripple/app/ledger/impl/LedgerMaster.cpp +++ b/src/ripple/app/ledger/impl/LedgerMaster.cpp @@ -2033,6 +2033,64 @@ LedgerMaster::gotFetchPack(bool progress, std::uint32_t seq) } } +/** Populate a fetch pack with data from the map the recipient wants. + + A recipient may or may not have the map that they are asking for. If + they do, we can optimize the transfer by not including parts of the + map that they are already have. + + @param have The map that the recipient already has (if any). + @param cnt The maximum number of nodes to return. + @param into The protocol object into which we add information. + @param seq The sequence number of the ledger the map is a part of. + @param withLeaves True if leaf nodes should be included. + + @note: The withLeaves parameter is configurable even though the + code, so far, only ever sets the parameter to true. + + The rationale is that for transaction trees, it may make + sense to not include the leaves if the fetch pack is being + constructed for someone attempting to get a recent ledger + for which they already have the transactions. + + However, for historical ledgers, which is the only use we + have for fetch packs right now, it makes sense to include + the transactions because the caller is unlikely to have + them. + */ +static void +populateFetchPack( + SHAMap const& want, + SHAMap const* have, + std::uint32_t cnt, + protocol::TMGetObjectByHash* into, + std::uint32_t seq, + bool withLeaves = true) +{ + assert(cnt != 0); + + Serializer s(1024); + + want.visitDifferences( + have, + [&s, withLeaves, &cnt, into, seq](SHAMapTreeNode const& n) -> bool { + if (!withLeaves && n.isLeaf()) + return true; + + s.erase(); + n.serializeWithPrefix(s); + + auto const& hash = n.getHash().as_uint256(); + + protocol::TMIndexedObject* obj = into->add_objects(); + obj->set_ledgerseq(seq); + obj->set_hash(hash.data(), hash.size()); + obj->set_data(s.getDataPtr(), s.getLength()); + + return --cnt != 0; + }); +} + void LedgerMaster::makeFetchPack( std::weak_ptr const& wPeer, @@ -2058,55 +2116,46 @@ LedgerMaster::makeFetchPack( if (!peer) return; - auto haveLedger = getLedgerByHash(haveLedgerHash); + auto have = getLedgerByHash(haveLedgerHash); - if (!haveLedger) + if (!have) { JLOG(m_journal.info()) - << "Peer requests fetch pack for ledger we don't have: " - << haveLedger; + << "Peer requests fetch pack for ledger we don't have: " << have; peer->charge(Resource::feeRequestNoReply); return; } - if (haveLedger->open()) + if (have->open()) { JLOG(m_journal.warn()) - << "Peer requests fetch pack from open ledger: " << haveLedger; + << "Peer requests fetch pack from open ledger: " << have; peer->charge(Resource::feeInvalidRequest); return; } - if (haveLedger->info().seq < getEarliestFetch()) + if (have->info().seq < getEarliestFetch()) { JLOG(m_journal.debug()) << "Peer requests fetch pack that is too early"; peer->charge(Resource::feeInvalidRequest); return; } - auto wantLedger = getLedgerByHash(haveLedger->info().parentHash); + auto want = getLedgerByHash(have->info().parentHash); - if (!wantLedger) + if (!want) { JLOG(m_journal.info()) << "Peer requests fetch pack for ledger whose predecessor we " - << "don't have: " << haveLedger; + << "don't have: " << have; peer->charge(Resource::feeRequestNoReply); return; } - auto fpAppender = [](protocol::TMGetObjectByHash* reply, - std::uint32_t ledgerSeq, - SHAMapHash const& hash, - const Blob& blob) { - protocol::TMIndexedObject& newObj = *(reply->add_objects()); - newObj.set_ledgerseq(ledgerSeq); - newObj.set_hash(hash.as_uint256().begin(), 256 / 8); - newObj.set_data(&blob[0], blob.size()); - }; - try { + Serializer hdr(128); + protocol::TMGetObjectByHash reply; reply.set_query(false); @@ -2121,56 +2170,49 @@ LedgerMaster::makeFetchPack( // 2. Add the nodes for the AccountStateMap of that ledger. // 3. If there are transactions, add the nodes for the // transactions of the ledger. - // 4. If the FetchPack now contains greater than or equal to - // 256 entries then stop. + // 4. If the FetchPack now contains at least 512 entries then stop. // 5. If not very much time has elapsed, then loop back and repeat // the same process adding the previous ledger to the FetchPack. do { - std::uint32_t lSeq = wantLedger->info().seq; + std::uint32_t lSeq = want->info().seq; - protocol::TMIndexedObject& newObj = *reply.add_objects(); - newObj.set_hash(wantLedger->info().hash.data(), 256 / 8); - Serializer s(256); - s.add32(HashPrefix::ledgerMaster); - addRaw(wantLedger->info(), s); - newObj.set_data(s.getDataPtr(), s.getLength()); - newObj.set_ledgerseq(lSeq); + { + // Serialize the ledger header: + hdr.erase(); - wantLedger->stateMap().getFetchPack( - &haveLedger->stateMap(), - true, - 16384, - std::bind( - fpAppender, - &reply, - lSeq, - std::placeholders::_1, - std::placeholders::_2)); + hdr.add32(HashPrefix::ledgerMaster); + addRaw(want->info(), hdr); - if (wantLedger->info().txHash.isNonZero()) - wantLedger->txMap().getFetchPack( - nullptr, - true, - 512, - std::bind( - fpAppender, - &reply, - lSeq, - std::placeholders::_1, - std::placeholders::_2)); + // Add the data + protocol::TMIndexedObject* obj = reply.add_objects(); + obj->set_hash( + want->info().hash.data(), want->info().hash.size()); + obj->set_data(hdr.getDataPtr(), hdr.getLength()); + obj->set_ledgerseq(lSeq); + } + + populateFetchPack( + want->stateMap(), &have->stateMap(), 16384, &reply, lSeq); + + // We use nullptr here because transaction maps are per ledger + // and so the requestor is unlikely to already have it. + if (want->info().txHash.isNonZero()) + populateFetchPack(want->txMap(), nullptr, 512, &reply, lSeq); if (reply.objects().size() >= 512) break; - // move may save a ref/unref - haveLedger = std::move(wantLedger); - wantLedger = getLedgerByHash(haveLedger->info().parentHash); - } while (wantLedger && UptimeClock::now() <= uptime + 1s); + have = std::move(want); + want = getLedgerByHash(have->info().parentHash); + } while (want && UptimeClock::now() <= uptime + 1s); + + auto msg = std::make_shared(reply, protocol::mtGET_OBJECTS); JLOG(m_journal.info()) - << "Built fetch pack with " << reply.objects().size() << " nodes"; - auto msg = std::make_shared(reply, protocol::mtGET_OBJECTS); + << "Built fetch pack with " << reply.objects().size() << " nodes (" + << msg->getBufferSize() << " bytes)"; + peer->send(msg); } catch (std::exception const&) diff --git a/src/ripple/app/ledger/impl/TransactionMaster.cpp b/src/ripple/app/ledger/impl/TransactionMaster.cpp index f83b1aa92..e01c0d7c3 100644 --- a/src/ripple/app/ledger/impl/TransactionMaster.cpp +++ b/src/ripple/app/ledger/impl/TransactionMaster.cpp @@ -108,7 +108,7 @@ TransactionMaster::fetch( std::shared_ptr TransactionMaster::fetch( std::shared_ptr const& item, - SHAMapTreeNode::TNType type, + SHAMapNodeType type, std::uint32_t uCommitLedger) { std::shared_ptr txn; @@ -116,12 +116,12 @@ TransactionMaster::fetch( if (!iTx) { - if (type == SHAMapTreeNode::tnTRANSACTION_NM) + if (type == SHAMapNodeType::tnTRANSACTION_NM) { SerialIter sit(item->slice()); txn = std::make_shared(std::ref(sit)); } - else if (type == SHAMapTreeNode::tnTRANSACTION_MD) + else if (type == SHAMapNodeType::tnTRANSACTION_MD) { auto blob = SerialIter{item->data(), item->size()}.getVL(); txn = std::make_shared( diff --git a/src/ripple/app/main/Application.cpp b/src/ripple/app/main/Application.cpp index 04828557c..5b4531af4 100644 --- a/src/ripple/app/main/Application.cpp +++ b/src/ripple/app/main/Application.cpp @@ -1906,8 +1906,7 @@ ApplicationImp::loadLedgerFromFile(std::string const& name) } } - loadLedger->stateMap().flushDirty( - hotACCOUNT_NODE, loadLedger->info().seq); + loadLedger->stateMap().flushDirty(hotACCOUNT_NODE); loadLedger->setAccepted( closeTime, closeTimeResolution, !closeTimeEstimated, *config_); diff --git a/src/ripple/app/misc/AmendmentTable.h b/src/ripple/app/misc/AmendmentTable.h index bcd21f763..e0884fecb 100644 --- a/src/ripple/app/misc/AmendmentTable.h +++ b/src/ripple/app/misc/AmendmentTable.h @@ -155,10 +155,9 @@ public: amendTx.add(s); initialPosition->addGiveItem( + SHAMapNodeType::tnTRANSACTION_NM, std::make_shared( - amendTx.getTransactionID(), s.peekData()), - true, - false); + amendTx.getTransactionID(), s.peekData())); } } }; diff --git a/src/ripple/app/misc/FeeVoteImpl.cpp b/src/ripple/app/misc/FeeVoteImpl.cpp index e2dc2e407..ef2ffecc5 100644 --- a/src/ripple/app/misc/FeeVoteImpl.cpp +++ b/src/ripple/app/misc/FeeVoteImpl.cpp @@ -244,9 +244,9 @@ FeeVoteImpl::doVoting( Serializer s; feeTx.add(s); - auto tItem = std::make_shared(txID, s.peekData()); - - if (!initialPosition->addGiveItem(std::move(tItem), true, false)) + if (!initialPosition->addGiveItem( + SHAMapNodeType::tnTRANSACTION_NM, + std::make_shared(txID, s.peekData()))) { JLOG(journal_.warn()) << "Ledger already had fee change"; } diff --git a/src/ripple/app/misc/NegativeUNLVote.cpp b/src/ripple/app/misc/NegativeUNLVote.cpp index 3e502fc81..6b02bc321 100644 --- a/src/ripple/app/misc/NegativeUNLVote.cpp +++ b/src/ripple/app/misc/NegativeUNLVote.cpp @@ -119,7 +119,8 @@ NegativeUNLVote::addTx( Serializer s; negUnlTx.add(s); if (!initialSet->addGiveItem( - std::make_shared(txID, s.peekData()), true, false)) + SHAMapNodeType::tnTRANSACTION_NM, + std::make_shared(txID, s.peekData()))) { JLOG(j_.warn()) << "N-UNL: ledger seq=" << seq << ", add ttUNL_MODIFY tx failed"; diff --git a/src/ripple/app/misc/SHAMapStoreImp.cpp b/src/ripple/app/misc/SHAMapStoreImp.cpp index 3e96bbc27..887bb580c 100644 --- a/src/ripple/app/misc/SHAMapStoreImp.cpp +++ b/src/ripple/app/misc/SHAMapStoreImp.cpp @@ -299,12 +299,10 @@ SHAMapStoreImp::fdRequired() const } bool -SHAMapStoreImp::copyNode( - std::uint64_t& nodeCount, - SHAMapAbstractNode const& node) +SHAMapStoreImp::copyNode(std::uint64_t& nodeCount, SHAMapTreeNode const& node) { // Copy a single record from node to dbRotating_ - dbRotating_->fetchNodeObject(node.getNodeHash().as_uint256()); + dbRotating_->fetchNodeObject(node.getHash().as_uint256()); if (!(++nodeCount % checkHealthInterval_)) { if (health()) diff --git a/src/ripple/app/misc/SHAMapStoreImp.h b/src/ripple/app/misc/SHAMapStoreImp.h index ea75eedb9..541d74a38 100644 --- a/src/ripple/app/misc/SHAMapStoreImp.h +++ b/src/ripple/app/misc/SHAMapStoreImp.h @@ -194,7 +194,7 @@ public: private: // callback for visitNodes bool - copyNode(std::uint64_t& nodeCount, SHAMapAbstractNode const& node); + copyNode(std::uint64_t& nodeCount, SHAMapTreeNode const& node); void run(); void diff --git a/src/ripple/nodestore/impl/Database.cpp b/src/ripple/nodestore/impl/Database.cpp index 178d4bb7f..3bec8dfff 100644 --- a/src/ripple/nodestore/impl/Database.cpp +++ b/src/ripple/nodestore/impl/Database.cpp @@ -244,11 +244,11 @@ Database::storeLedger( } bool error = false; - auto visit = [&](SHAMapAbstractNode& node) { + auto visit = [&](SHAMapTreeNode& node) { if (!isStopping()) { if (auto nodeObject = srcDB.fetchNodeObject( - node.getNodeHash().as_uint256(), srcLedger.info().seq)) + node.getHash().as_uint256(), srcLedger.info().seq)) { batch.emplace_back(std::move(nodeObject)); if (batch.size() < batchWritePreallocationSize || storeBatch()) diff --git a/src/ripple/nodestore/impl/Shard.cpp b/src/ripple/nodestore/impl/Shard.cpp index 6032bf3e8..7694362f1 100644 --- a/src/ripple/nodestore/impl/Shard.cpp +++ b/src/ripple/nodestore/impl/Shard.cpp @@ -405,11 +405,11 @@ Shard::storeLedger( } bool error = false; - auto visit = [&](SHAMapAbstractNode& node) { + auto visit = [&](SHAMapTreeNode const& node) { if (!stop_) { if (auto nodeObject = srcDB.fetchNodeObject( - node.getNodeHash().as_uint256(), srcLedger->info().seq)) + node.getHash().as_uint256(), srcLedger->info().seq)) { batch.emplace_back(std::move(nodeObject)); if (batch.size() < batchWritePreallocationSize || storeBatch()) @@ -1288,10 +1288,10 @@ Shard::verifyLedger( return fail("Invalid ledger account hash"); bool error{false}; - auto visit = [this, &error](SHAMapAbstractNode& node) { + auto visit = [this, &error](SHAMapTreeNode const& node) { if (stop_) return false; - if (!verifyFetch(node.getNodeHash().as_uint256())) + if (!verifyFetch(node.getHash().as_uint256())) error = true; return !error; }; diff --git a/src/ripple/overlay/Message.h b/src/ripple/overlay/Message.h index 724fad07e..5ce665858 100644 --- a/src/ripple/overlay/Message.h +++ b/src/ripple/overlay/Message.h @@ -64,6 +64,10 @@ public: int type, boost::optional const& validator = {}); + /** Retrieve the size of the packed but uncompressed message data. */ + std::size_t + getBufferSize(); + /** Retrieve the packed message data. If compressed message is requested but * the message is not compressible then the uncompressed buffer is returned. * @param compressed Request compressed (Compress::On) or diff --git a/src/ripple/overlay/impl/Message.cpp b/src/ripple/overlay/impl/Message.cpp index b5b24c3d3..d5aec1a2d 100644 --- a/src/ripple/overlay/impl/Message.cpp +++ b/src/ripple/overlay/impl/Message.cpp @@ -177,6 +177,12 @@ Message::setHeader( } } +std::size_t +Message::getBufferSize() +{ + return buffer_.size(); +} + std::vector const& Message::getBuffer(Compressed tryCompressed) { diff --git a/src/ripple/rpc/handlers/Tx.cpp b/src/ripple/rpc/handlers/Tx.cpp index 9db86b1e1..613128a47 100644 --- a/src/ripple/rpc/handlers/Tx.cpp +++ b/src/ripple/rpc/handlers/Tx.cpp @@ -49,24 +49,6 @@ isValidated(LedgerMaster& ledgerMaster, std::uint32_t seq, uint256 const& hash) return ledgerMaster.getHashBySeq(seq) == hash; } -bool -getMetaHex(Ledger const& ledger, uint256 const& transID, std::string& hex) -{ - SHAMapTreeNode::TNType type; - auto const item = ledger.txMap().peekItem(transID, type); - - if (!item) - return false; - - if (type != SHAMapTreeNode::tnTRANSACTION_MD) - return false; - - SerialIter it(item->slice()); - it.getVL(); // skip transaction - hex = strHex(makeSlice(it.getVL())); - return true; -} - struct TxResult { Transaction::pointer txn; diff --git a/src/ripple/shamap/README.md b/src/ripple/shamap/README.md index 76d514f47..ef2d22024 100644 --- a/src/ripple/shamap/README.md +++ b/src/ripple/shamap/README.md @@ -17,14 +17,14 @@ or account state can be used to navigate the trie. A `SHAMap` is a trie with two node types: 1. SHAMapInnerNode -2. SHAMapTreeNode +2. SHAMapLeafNode -Both of these nodes directly inherit from SHAMapAbstractNode which holds data +Both of these nodes directly inherit from SHAMapTreeNode which holds data common to both of the node types. All non-leaf nodes have type SHAMapInnerNode. -All leaf nodes have type SHAMapTreeNode. +All leaf nodes have type SHAMapLeafNode. The root node is always a SHAMapInnerNode. @@ -193,7 +193,7 @@ continues, unless the path indicates a child that does not exist. And in this case, `nullptr` is returned to indicate no leaf node along the given path exists. Otherwise a leaf node is found and a (non-owning) pointer to it is returned. At each step, if a stack is requested, a -`pair, SHAMapNodeID>` is pushed onto the stack. +`pair, SHAMapNodeID>` is pushed onto the stack. When a child node is found by `selectBranch`, the traversal to that node consists of two steps: @@ -220,11 +220,11 @@ is this case stands for 'No Throw'. The `fetchNodeNT()` method goes through three phases: - 1. By calling `getCache()` we attempt to locate the missing node in the + 1. By calling `cacheLookup()` we attempt to locate the missing node in the TreeNodeCache. The TreeNodeCache is a cache of immutable SHAMapTreeNodes that are shared across all `SHAMap`s. - Any SHAMapTreeNode that is immutable has a sequence number of zero + Any SHAMapLeafNode that is immutable has a sequence number of zero (sharable). When a mutable `SHAMap` is created then its SHAMapTreeNodes are given non-zero sequence numbers (unsharable). But all nodes in the TreeNodeCache are immutable, so if one is found here, its sequence number @@ -250,17 +250,17 @@ the `SHAMap`, node `TreeNodeCache` or database, then we don't create duplicates by favoring the copy already in the `TreeNodeCache`. By using `canonicalize()` we manage a thread race condition where two different -threads might both recognize the lack of a SHAMapTreeNode at the same time +threads might both recognize the lack of a SHAMapLeafNode at the same time (during a fetch). If they both attempt to insert the node into the `SHAMap`, then `canonicalize` makes sure that the first node in wins and the slower thread receives back a pointer to the node inserted by the faster thread. Recall that these two `SHAMap`s will share the same `TreeNodeCache`. -## TreeNodeCache ## +## `TreeNodeCache` ## The `TreeNodeCache` is a `std::unordered_map` keyed on the hash of the -`SHAMap` node. The stored type consists of `shared_ptr`, -`weak_ptr`, and a time point indicating the most recent +`SHAMap` node. The stored type consists of `shared_ptr`, +`weak_ptr`, and a time point indicating the most recent access of this node in the cache. The time point is based on `std::chrono::steady_clock`. @@ -271,7 +271,7 @@ and logging, and a target age for the contained nodes. When the target age for a node is exceeded, and there are no more references to the node, the node is removed from the `TreeNodeCache`. -## FullBelowCache ## +## `FullBelowCache` ## This cache remembers which trie keys have all of their children resident in a `SHAMap`. This optimizes the process of acquiring a complete trie. This is used @@ -284,38 +284,51 @@ nodes, and thus that subtree does not need to be walked. These nodes are stored in the FullBelowCache. Subsequent walks check the FullBelowCache first when encountering a node, and ignore that subtree if found. -## SHAMapAbstractNode ## +## `SHAMapTreeNode` ## -This is a base class for the two concrete node types. It holds the following -common data: +This is an abstract base class for the concrete node types. It holds the +following common data: -1. A node type, one of: - a. error - b. inner - c. transaction with no metadata - d. transaction with metadata - e. account state -2. A hash -3. A sequence number +1. A hash +2. An identifier used to perform copy-on-write operations -## SHAMapInnerNode ## +### `SHAMapInnerNode` ### -SHAMapInnerNode publicly inherits directly from SHAMapAbstractNode. It holds +`SHAMapInnerNode` publicly inherits directly from `SHAMapTreeNode`. It holds the following data: 1. Up to 16 child nodes, each held with a shared_ptr. 2. A hash for each child. -3. A 16-bit bitset with a 1 bit set for each child that exists. -4. Flag to aid online delete and consistency with data on disk. +3. A bitset to indicate which of the 16 children exist. +4. An identifier used to determine whether the map below this node is + fully populated -## SHAMapTreeNode ## +### `SHAMapLeafNode` ### -SHAMapTreeNode publicly inherits directly from SHAMapAbstractNode. It holds the +`SHAMapLeafNode` is an abstract class which publicly inherits directly from +`SHAMapTreeNode`. It isIt holds the following data: 1. A shared_ptr to a const SHAMapItem. +#### `SHAMapAccountStateLeafNode` #### + +`SHAMapAccountStateLeafNode` is a class which publicly inherits directly from +`SHAMapLeafNode`. It is used to represent entries (i.e. account objects, escrow +objects, trust lines, etc.) in a state map. + +#### `SHAMapTxLeafNode` #### + +`SHAMapTxLeafNode` is a class which publicly inherits directly from +`SHAMapLeafNode`. It is used to represent transactions in a state map. + +#### `SHAMapTxPlusMetaLeafNode` #### + +`SHAMapTxPlusMetaLeafNode` is a class which publicly inherits directly from +`SHAMapLeafNode`. It is used to represent transactions along with metadata +associated with this transaction in a state map. + ## SHAMapItem ## This holds the following data: @@ -323,27 +336,4 @@ This holds the following data: 1. uint256. The hash of the data. 2. vector. The data (transactions, account info). -## SHAMap Improvements ## - -Here's a simple one: the SHAMapTreeNode::mAccessSeq member is currently not used -and could be removed. - -Here's a more important change. The trie structure is currently embedded in the -SHAMapTreeNodes themselves. It doesn't have to be that way, and that should be -fixed. - -When we navigate the trie (say, like `SHAMap::walkTo()`) we currently ask each -node for information that we could determine locally. We know the depth because -we know how many nodes we have traversed. We know the ID that we need because -that's how we're steering. So we don't need to store the ID in the node. The -next refactor should remove all calls to `SHAMapTreeNode::GetID()`. - -Then we can remove the NodeID member from SHAMapTreeNode. - -Then we can change the `SHAMap::mTNBtID` member to be `mTNByHash`. - -An additional possible refactor would be to have a base type, SHAMapTreeNode, -and derive from that InnerNode and LeafNode types. That would remove some -storage (the array of 16 hashes) from the LeafNodes. That refactor would also -have the effect of simplifying methods like `isLeaf()` and `hasItem()`. diff --git a/src/ripple/shamap/SHAMap.h b/src/ripple/shamap/SHAMap.h index 27946b215..38466450e 100644 --- a/src/ripple/shamap/SHAMap.h +++ b/src/ripple/shamap/SHAMap.h @@ -27,7 +27,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -95,13 +97,18 @@ class SHAMap private: Family& f_; beast::Journal journal_; - std::uint32_t seq_; - std::uint32_t ledgerSeq_ = 0; // sequence number of ledger this is part of - std::shared_ptr root_; + + /** ID to distinguish this map for all others we're sharing nodes with. */ + std::uint32_t cowid_ = 1; + + /** The sequence of the ledger that this map references, if any. */ + std::uint32_t ledgerSeq_ = 0; + + std::shared_ptr root_; mutable SHAMapState state_; - SHAMapType type_; - bool backed_ = true; // Map is backed by the database - bool full_ = false; // Map is believed complete in database + SHAMapType const type_; + bool backed_ = true; // Map is backed by the database + mutable bool full_ = false; // Map is believed complete in database public: /** Each non-leaf node has 16 children (the 'radix tree' part of the map) */ @@ -115,7 +122,6 @@ public: std::shared_ptr>; using Delta = std::map; - ~SHAMap(); SHAMap(SHAMap const&) = delete; SHAMap& operator=(SHAMap const&) = delete; @@ -125,6 +131,8 @@ public: SHAMap(SHAMapType t, uint256 const& hash, Family& f); + ~SHAMap() = default; + Family const& family() const { @@ -171,26 +179,26 @@ public: fetchRoot(SHAMapHash const& hash, SHAMapSyncFilter* filter); // normal hash access functions + + /** Does the tree have an item with the given ID? */ bool hasItem(uint256 const& id) const; + bool delItem(uint256 const& id); + bool - addItem(SHAMapItem&& i, bool isTransaction, bool hasMeta); + addItem(SHAMapNodeType type, SHAMapItem&& i); + SHAMapHash getHash() const; // save a copy if you have a temporary anyway bool - updateGiveItem( - std::shared_ptr, - bool isTransaction, - bool hasMeta); + updateGiveItem(SHAMapNodeType type, std::shared_ptr); + bool - addGiveItem( - std::shared_ptr, - bool isTransaction, - bool hasMeta); + addGiveItem(SHAMapNodeType type, std::shared_ptr item); // Save a copy if you need to extend the life // of the SHAMapItem beyond this SHAMap @@ -198,8 +206,6 @@ public: peekItem(uint256 const& id) const; std::shared_ptr const& peekItem(uint256 const& id, SHAMapHash& hash) const; - std::shared_ptr const& - peekItem(uint256 const& id, SHAMapTreeNode::TNType& type) const; // traverse functions const_iterator @@ -211,7 +217,7 @@ public: If function returns false, visitNodes exits. */ void - visitNodes(std::function const& function) const; + visitNodes(std::function const& function) const; /** Visit every node in this SHAMap that is not present in the specified SHAMap @@ -222,7 +228,7 @@ public: void visitDifferences( SHAMap const* have, - std::function) const; + std::function) const; /** Visit every leaf node in this SHAMap @@ -250,9 +256,9 @@ public: bool getNodeFat( - SHAMapNodeID node, + SHAMapNodeID const& wanted, std::vector& nodeIDs, - std::vector& rawNode, + std::vector& rawNodes, bool fatLeaves, std::uint32_t depth) const; @@ -260,8 +266,6 @@ public: void serializeRoot(Serializer& s) const; - std::vector - getNeededHashes(int max, SHAMapSyncFilter* filter); SHAMapAddNode addRootNode( SHAMapHash const& hash, @@ -290,26 +294,21 @@ public: bool compare(SHAMap const& otherMap, Delta& differences, int maxCount) const; + /** Convert any modified nodes to shared. */ int - flushDirty(NodeObjectType t, std::uint32_t seq); + unshare(); + + /** Flush modified nodes to the nodestore and convert them to shared. */ + int + flushDirty(NodeObjectType t); + void walkMap(std::vector& missingNodes, int maxMissing) const; bool deepCompare(SHAMap& other) const; // Intended for debug/test only - using fetchPackEntry_t = std::pair; - - void - getFetchPack( - SHAMap const* have, - bool includeLeaves, - int max, - std::function) const; - void setUnbacked(); - int - unshare(); void dump(bool withHashes = false) const; @@ -317,29 +316,29 @@ public: invariants() const; private: - using SharedPtrNodeStack = std::stack< - std::pair, SHAMapNodeID>>; + using SharedPtrNodeStack = + std::stack, SHAMapNodeID>>; using DeltaRef = std::pair< std::shared_ptr const&, std::shared_ptr const&>; // tree node cache operations - std::shared_ptr - getCache(SHAMapHash const& hash) const; + std::shared_ptr + cacheLookup(SHAMapHash const& hash) const; void - canonicalize(SHAMapHash const& hash, std::shared_ptr&) + canonicalize(SHAMapHash const& hash, std::shared_ptr&) const; // database operations - std::shared_ptr + std::shared_ptr fetchNodeFromDB(SHAMapHash const& hash) const; - std::shared_ptr + std::shared_ptr fetchNodeNT(SHAMapHash const& hash) const; - std::shared_ptr + std::shared_ptr fetchNodeNT(SHAMapHash const& hash, SHAMapSyncFilter* filter) const; - std::shared_ptr + std::shared_ptr fetchNode(SHAMapHash const& hash) const; - std::shared_ptr + std::shared_ptr checkFilter(SHAMapHash const& hash, SHAMapSyncFilter* filter) const; /** Update hashes up to the root */ @@ -347,16 +346,16 @@ private: dirtyUp( SharedPtrNodeStack& stack, uint256 const& target, - std::shared_ptr terminal); + std::shared_ptr terminal); /** Walk towards the specified id, returning the node. Caller must check if the return is nullptr, and if not, if the node->peekItem()->key() == id */ - SHAMapTreeNode* + SHAMapLeafNode* walkTowardsKey(uint256 const& id, SharedPtrNodeStack* stack = nullptr) const; /** Return nullptr if key not found */ - SHAMapTreeNode* + SHAMapLeafNode* findKey(uint256 const& id) const; /** Unshare the node, allowing it to be modified */ @@ -370,38 +369,35 @@ private: preFlushNode(std::shared_ptr node) const; /** write and canonicalize modified node */ - std::shared_ptr - writeNode( - NodeObjectType t, - std::uint32_t seq, - std::shared_ptr node) const; + std::shared_ptr + writeNode(NodeObjectType t, std::shared_ptr node) const; - SHAMapTreeNode* + SHAMapLeafNode* firstBelow( - std::shared_ptr, + std::shared_ptr, SharedPtrNodeStack& stack, int branch = 0) const; // Simple descent // Get a child of the specified node - SHAMapAbstractNode* + SHAMapTreeNode* descend(SHAMapInnerNode*, int branch) const; - SHAMapAbstractNode* + SHAMapTreeNode* descendThrow(SHAMapInnerNode*, int branch) const; - std::shared_ptr + std::shared_ptr descend(std::shared_ptr const&, int branch) const; - std::shared_ptr + std::shared_ptr descendThrow(std::shared_ptr const&, int branch) const; // Descend with filter - SHAMapAbstractNode* + SHAMapTreeNode* descendAsync( SHAMapInnerNode* parent, int branch, SHAMapSyncFilter* filter, bool& pending) const; - std::pair + std::pair descend( SHAMapInnerNode* parent, SHAMapNodeID const& parentID, @@ -410,31 +406,31 @@ private: // Non-storing // Does not hook the returned node to its parent - std::shared_ptr + std::shared_ptr descendNoStore(std::shared_ptr const&, int branch) const; /** If there is only one leaf below this node, get its contents */ std::shared_ptr const& - onlyBelow(SHAMapAbstractNode*) const; + onlyBelow(SHAMapTreeNode*) const; bool hasInnerNode(SHAMapNodeID const& nodeID, SHAMapHash const& hash) const; bool hasLeafNode(uint256 const& tag, SHAMapHash const& hash) const; - SHAMapTreeNode const* + SHAMapLeafNode const* peekFirstItem(SharedPtrNodeStack& stack) const; - SHAMapTreeNode const* + SHAMapLeafNode const* peekNextItem(uint256 const& id, SharedPtrNodeStack& stack) const; bool walkBranch( - SHAMapAbstractNode* node, + SHAMapTreeNode* node, std::shared_ptr const& otherMapItem, bool isFirstMap, Delta& differences, int& maxCount) const; int - walkSubTree(bool doWrite, NodeObjectType t, std::uint32_t seq); + walkSubTree(bool doWrite, NodeObjectType t); // Structure to track information about call to // getMissingNodes while it's in progress diff --git a/src/ripple/shamap/SHAMapAccountStateLeafNode.h b/src/ripple/shamap/SHAMapAccountStateLeafNode.h new file mode 100644 index 000000000..1d1711d49 --- /dev/null +++ b/src/ripple/shamap/SHAMapAccountStateLeafNode.h @@ -0,0 +1,93 @@ +//------------------------------------------------------------------------------ +/* + 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. +*/ +//============================================================================== + +#ifndef RIPPLE_SHAMAP_SHAMAPACCOUNTSTATELEAFNODE_H_INCLUDED +#define RIPPLE_SHAMAP_SHAMAPACCOUNTSTATELEAFNODE_H_INCLUDED + +#include +#include +#include +#include +#include +#include + +namespace ripple { + +/** A leaf node for a state object. */ +class SHAMapAccountStateLeafNode final + : public SHAMapLeafNode, + public CountedObject +{ +public: + SHAMapAccountStateLeafNode( + std::shared_ptr item, + std::uint32_t cowid) + : SHAMapLeafNode(std::move(item), cowid) + { + updateHash(); + } + + SHAMapAccountStateLeafNode( + std::shared_ptr item, + std::uint32_t cowid, + SHAMapHash const& hash) + : SHAMapLeafNode(std::move(item), cowid, hash) + { + } + + std::shared_ptr + clone(std::uint32_t cowid) const final override + { + return std::make_shared( + item_, cowid, hash_); + } + + SHAMapNodeType + getType() const final override + { + return SHAMapNodeType::tnACCOUNT_STATE; + } + + void + updateHash() final override + { + hash_ = SHAMapHash{sha512Half( + HashPrefix::leafNode, makeSlice(item_->peekData()), item_->key())}; + } + + void + serializeForWire(Serializer& s) const final override + { + s.addRaw(item_->peekData()); + s.addBitString(item_->key()); + s.add8(wireTypeAccountState); + } + + void + serializeWithPrefix(Serializer& s) const final override + { + s.add32(HashPrefix::leafNode); + s.addRaw(item_->peekData()); + s.addBitString(item_->key()); + } +}; + +} // namespace ripple + +#endif diff --git a/src/ripple/shamap/SHAMapInnerNode.h b/src/ripple/shamap/SHAMapInnerNode.h new file mode 100644 index 000000000..d77884113 --- /dev/null +++ b/src/ripple/shamap/SHAMapInnerNode.h @@ -0,0 +1,155 @@ +//------------------------------------------------------------------------------ +/* + 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. +*/ +//============================================================================== + +#ifndef RIPPLE_SHAMAP_SHAMAPINNERNODE_H_INCLUDED +#define RIPPLE_SHAMAP_SHAMAPINNERNODE_H_INCLUDED + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace ripple { + +class SHAMapInnerNode final : public SHAMapTreeNode, + public CountedObject +{ + std::array mHashes; + std::shared_ptr mChildren[16]; + int mIsBranch = 0; + std::uint32_t mFullBelowGen = 0; + + static std::mutex childLock; + +public: + SHAMapInnerNode(std::uint32_t cowid); + + std::shared_ptr + clone(std::uint32_t cowid) const override; + + SHAMapNodeType + getType() const override + { + return SHAMapNodeType::tnINNER; + } + + bool + isLeaf() const override + { + return false; + } + + bool + isInner() const override + { + return true; + } + + bool + isEmpty() const; + bool + isEmptyBranch(int m) const; + int + getBranchCount() const; + SHAMapHash const& + getChildHash(int m) const; + + void + setChild(int m, std::shared_ptr const& child); + void + shareChild(int m, std::shared_ptr const& child); + SHAMapTreeNode* + getChildPointer(int branch); + std::shared_ptr + getChild(int branch); + virtual std::shared_ptr + canonicalizeChild(int branch, std::shared_ptr node); + + // sync functions + bool + isFullBelow(std::uint32_t generation) const; + void + setFullBelowGen(std::uint32_t gen); + + void + updateHash() override; + + /** Recalculate the hash of all children and this node. */ + void + updateHashDeep(); + + void + serializeForWire(Serializer&) const override; + + void + serializeWithPrefix(Serializer&) const override; + + std::string + getString(SHAMapNodeID const&) const override; + + void + invariants(bool is_root = false) const override; + + static std::shared_ptr + makeFullInner(Slice data, SHAMapHash const& hash, bool hashValid); + + static std::shared_ptr + makeCompressedInner(Slice data); +}; + +inline SHAMapInnerNode::SHAMapInnerNode(std::uint32_t cowid) + : SHAMapTreeNode(cowid) +{ +} + +inline bool +SHAMapInnerNode::isEmptyBranch(int m) const +{ + return (mIsBranch & (1 << m)) == 0; +} + +inline SHAMapHash const& +SHAMapInnerNode::getChildHash(int m) const +{ + assert(m >= 0 && m < 16); + return mHashes[m]; +} + +inline bool +SHAMapInnerNode::isFullBelow(std::uint32_t generation) const +{ + return mFullBelowGen == generation; +} + +inline void +SHAMapInnerNode::setFullBelowGen(std::uint32_t gen) +{ + mFullBelowGen = gen; +} + +} // namespace ripple +#endif diff --git a/src/ripple/shamap/SHAMapLeafNode.h b/src/ripple/shamap/SHAMapLeafNode.h new file mode 100644 index 000000000..776aca76d --- /dev/null +++ b/src/ripple/shamap/SHAMapLeafNode.h @@ -0,0 +1,82 @@ +//------------------------------------------------------------------------------ +/* + 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. +*/ +//============================================================================== + +#ifndef RIPPLE_SHAMAP_SHAMAPLEAFNODE_H_INCLUDED +#define RIPPLE_SHAMAP_SHAMAPLEAFNODE_H_INCLUDED + +#include +#include +#include + +#include +#include + +namespace ripple { + +class SHAMapLeafNode : public SHAMapTreeNode +{ +protected: + std::shared_ptr item_; + + SHAMapLeafNode(std::shared_ptr item, std::uint32_t cowid); + SHAMapLeafNode( + std::shared_ptr item, + std::uint32_t cowid, + SHAMapHash const& hash); + +public: + SHAMapLeafNode(const SHAMapLeafNode&) = delete; + SHAMapLeafNode& + operator=(const SHAMapLeafNode&) = delete; + + bool + isLeaf() const final override + { + return true; + } + + bool + isInner() const final override + { + return false; + } + + void + invariants(bool is_root = false) const final override; + +public: + std::shared_ptr const& + peekItem() const; + + /** Set the item that this node points to and update the node's hash. + + @param i the new item + @return false if the change was, effectively, a noop (that is, if the + hash was unchanged); true otherwise. + */ + bool + setItem(std::shared_ptr i); + + std::string + getString(SHAMapNodeID const&) const final override; +}; + +} // namespace ripple + +#endif diff --git a/src/ripple/shamap/SHAMapNodeID.h b/src/ripple/shamap/SHAMapNodeID.h index 38dcda9f8..e89f63ce7 100644 --- a/src/ripple/shamap/SHAMapNodeID.h +++ b/src/ripple/shamap/SHAMapNodeID.h @@ -41,7 +41,8 @@ public: SHAMapNodeID(SHAMapNodeID const& other) = default; SHAMapNodeID(unsigned int depth, uint256 const& hash); - SHAMapNodeID& operator=(SHAMapNodeID const& other) = default; + SHAMapNodeID& + operator=(SHAMapNodeID const& other) = default; bool isRoot() const diff --git a/src/ripple/shamap/SHAMapSyncFilter.h b/src/ripple/shamap/SHAMapSyncFilter.h index 86a6451b6..7ab649f8a 100644 --- a/src/ripple/shamap/SHAMapSyncFilter.h +++ b/src/ripple/shamap/SHAMapSyncFilter.h @@ -43,7 +43,7 @@ public: SHAMapHash const& nodeHash, std::uint32_t ledgerSeq, Blob&& nodeData, - SHAMapTreeNode::TNType type) const = 0; + SHAMapNodeType type) const = 0; virtual boost::optional getNode(SHAMapHash const& nodeHash) const = 0; diff --git a/src/ripple/shamap/SHAMapTreeNode.h b/src/ripple/shamap/SHAMapTreeNode.h index 030b00740..7423444a9 100644 --- a/src/ripple/shamap/SHAMapTreeNode.h +++ b/src/ripple/shamap/SHAMapTreeNode.h @@ -33,8 +33,17 @@ namespace ripple { +// These are wire-protocol identifiers used during serialization to encode the +// type of a node. They should not be arbitrarily be changed. +static constexpr unsigned char const wireTypeTransaction = 0; +static constexpr unsigned char const wireTypeAccountState = 1; +static constexpr unsigned char const wireTypeInner = 2; +static constexpr unsigned char const wireTypeCompressedInner = 3; +static constexpr unsigned char const wireTypeTransactionWithMeta = 4; + // A SHAMapHash is the hash of a node in a SHAMap, and also the // type of the hash of the entire SHAMap. + class SHAMapHash { uint256 hash_; @@ -114,49 +123,120 @@ operator!=(SHAMapHash const& x, SHAMapHash const& y) return !(x == y); } -class SHAMapAbstractNode +enum class SHAMapNodeType { + tnINNER = 1, + tnTRANSACTION_NM = 2, // transaction, no metadata + tnTRANSACTION_MD = 3, // transaction, with metadata + tnACCOUNT_STATE = 4 +}; + +class SHAMapTreeNode { -public: - enum TNType { - tnINNER = 1, - tnTRANSACTION_NM = 2, // transaction, no metadata - tnTRANSACTION_MD = 3, // transaction, with metadata - tnACCOUNT_STATE = 4 - }; +protected: + SHAMapHash hash_; + + /** Determines the owning SHAMap, if any. Used for copy-on-write semantics. + + If this value is 0, the node is not dirty and does not need to be + flushed. It is eligible for sharing and may be included multiple + SHAMap instances. + */ + std::uint32_t cowid_; protected: - TNType mType; - SHAMapHash mHash; - std::uint32_t mSeq; + SHAMapTreeNode(SHAMapTreeNode const&) = delete; + SHAMapTreeNode& + operator=(SHAMapTreeNode const&) = delete; -protected: - virtual ~SHAMapAbstractNode() = 0; - SHAMapAbstractNode(SHAMapAbstractNode const&) = delete; - SHAMapAbstractNode& - operator=(SHAMapAbstractNode const&) = delete; + /** Construct a node - SHAMapAbstractNode(TNType type, std::uint32_t seq); - SHAMapAbstractNode(TNType type, std::uint32_t seq, SHAMapHash const& hash); + @param cowid The identifier of a SHAMap. For more, see #cowid_ + @param hash The hash associated with this node, if any. + */ + /** @{ */ + explicit SHAMapTreeNode(std::uint32_t cowid) noexcept : cowid_(cowid) + { + } + + explicit SHAMapTreeNode( + std::uint32_t cowid, + SHAMapHash const& hash) noexcept + : hash_(hash), cowid_(cowid) + { + } + /** @} */ public: + virtual ~SHAMapTreeNode() noexcept = default; + + /** \defgroup SHAMap Copy-on-Write Support + + By nature, a node may appear in multiple SHAMap instances. Rather than + actually duplicating these nodes, SHAMap opts to be memory efficient + and uses copy-on-write semantics for nodes. + + Only nodes that are not modified and don't need to be flushed back can + be shared. Once a node needs to be changed, it must first be copied and + the copy must marked as not shareable. + + Note that just because a node may not be *owned* by a given SHAMap + instance does not mean that the node is NOT a part of any SHAMap. It + only means that the node is not owned exclusively by any one SHAMap. + + For more on copy-on-write, check out: + https://en.wikipedia.org/wiki/Copy-on-write + */ + /** @{ */ + /** Returns the SHAMap that owns this node. + + @return the ID of the SHAMap that owns this node, or 0 if the node + is not owned by any SHAMap and is a candidate for sharing. + */ std::uint32_t - getSeq() const; - void - setSeq(std::uint32_t s); - SHAMapHash const& - getNodeHash() const; - TNType - getType() const; - bool - isLeaf() const; - bool - isInner() const; - bool - isInBounds(SHAMapNodeID const& id) const; + cowid() const + { + return cowid_; + } - virtual bool + /** If this node is shared with another map, mark it as no longer shared. + + Only nodes that are not modified and do not need to be flushed back + should be marked as unshared. + */ + void + unshare() + { + cowid_ = 0; + } + + /** Make a copy of this node, setting the owner. */ + virtual std::shared_ptr + clone(std::uint32_t cowid) const = 0; + /** @} */ + + /** Recalculate the hash of this node. */ + virtual void updateHash() = 0; + /** Return the hash of this node. */ + SHAMapHash const& + getHash() const + { + return hash_; + } + + /** Determines the type of node. */ + virtual SHAMapNodeType + getType() const = 0; + + /** Determines if this is a leaf node. */ + virtual bool + isLeaf() const = 0; + + /** Determines if this is an inner node. */ + virtual bool + isInner() const = 0; + /** Serialize the node in a format appropriate for sending over the wire */ virtual void serializeForWire(Serializer&) const = 0; @@ -167,268 +247,27 @@ public: virtual std::string getString(SHAMapNodeID const&) const; - virtual std::shared_ptr - clone(std::uint32_t seq) const = 0; - virtual uint256 const& - key() const = 0; + virtual void invariants(bool is_root = false) const = 0; - static std::shared_ptr + static std::shared_ptr makeFromPrefix(Slice rawNode, SHAMapHash const& hash); - static std::shared_ptr + static std::shared_ptr makeFromWire(Slice rawNode); private: - static std::shared_ptr - makeTransaction( - Slice data, - std::uint32_t seq, - SHAMapHash const& hash, - bool hashValid); + static std::shared_ptr + makeTransaction(Slice data, SHAMapHash const& hash, bool hashValid); - static std::shared_ptr - makeAccountState( - Slice data, - std::uint32_t seq, - SHAMapHash const& hash, - bool hashValid); + static std::shared_ptr + makeAccountState(Slice data, SHAMapHash const& hash, bool hashValid); - static std::shared_ptr - makeTransactionWithMeta( - Slice data, - std::uint32_t seq, - SHAMapHash const& hash, - bool hashValid); + static std::shared_ptr + makeTransactionWithMeta(Slice data, SHAMapHash const& hash, bool hashValid); }; -class SHAMapInnerNode : public SHAMapAbstractNode, - public CountedObject -{ - std::array mHashes; - std::shared_ptr mChildren[16]; - int mIsBranch = 0; - std::uint32_t mFullBelowGen = 0; - - static std::mutex childLock; - -public: - SHAMapInnerNode(std::uint32_t seq); - std::shared_ptr - clone(std::uint32_t seq) const override; - - bool - isEmpty() const; - bool - isEmptyBranch(int m) const; - int - getBranchCount() const; - SHAMapHash const& - getChildHash(int m) const; - - void - setChild(int m, std::shared_ptr const& child); - void - shareChild(int m, std::shared_ptr const& child); - SHAMapAbstractNode* - getChildPointer(int branch); - std::shared_ptr - getChild(int branch); - virtual std::shared_ptr - canonicalizeChild(int branch, std::shared_ptr node); - - // sync functions - bool - isFullBelow(std::uint32_t generation) const; - void - setFullBelowGen(std::uint32_t gen); - - bool - updateHash() override; - void - updateHashDeep(); - - void - serializeForWire(Serializer&) const override; - - void - serializeWithPrefix(Serializer&) const override; - - std::string - getString(SHAMapNodeID const&) const override; - uint256 const& - key() const override; - void - invariants(bool is_root = false) const override; - - static std::shared_ptr - makeFullInner( - Slice data, - std::uint32_t seq, - SHAMapHash const& hash, - bool hashValid); - - static std::shared_ptr - makeCompressedInner(Slice data, std::uint32_t seq); -}; - -// SHAMapTreeNode represents a leaf, and may eventually be renamed to reflect -// that. -class SHAMapTreeNode : public SHAMapAbstractNode, - public CountedObject -{ -private: - std::shared_ptr mItem; - -public: - SHAMapTreeNode(const SHAMapTreeNode&) = delete; - SHAMapTreeNode& - operator=(const SHAMapTreeNode&) = delete; - - SHAMapTreeNode( - std::shared_ptr item, - TNType type, - std::uint32_t seq); - SHAMapTreeNode( - std::shared_ptr item, - TNType type, - std::uint32_t seq, - SHAMapHash const& hash); - std::shared_ptr - clone(std::uint32_t seq) const override; - - void - serializeForWire(Serializer&) const override; - - void - serializeWithPrefix(Serializer&) const override; - - uint256 const& - key() const override; - void - invariants(bool is_root = false) const override; - -public: // public only to SHAMap - // item node function - bool - hasItem() const; - std::shared_ptr const& - peekItem() const; - bool - setItem(std::shared_ptr i, TNType type); - - std::string - getString(SHAMapNodeID const&) const override; - bool - updateHash() override; -}; - -// SHAMapAbstractNode - -inline SHAMapAbstractNode::SHAMapAbstractNode(TNType type, std::uint32_t seq) - : mType(type), mSeq(seq) -{ -} - -inline SHAMapAbstractNode::SHAMapAbstractNode( - TNType type, - std::uint32_t seq, - SHAMapHash const& hash) - : mType(type), mHash(hash), mSeq(seq) -{ -} - -inline std::uint32_t -SHAMapAbstractNode::getSeq() const -{ - return mSeq; -} - -inline void -SHAMapAbstractNode::setSeq(std::uint32_t s) -{ - mSeq = s; -} - -inline SHAMapHash const& -SHAMapAbstractNode::getNodeHash() const -{ - return mHash; -} - -inline SHAMapAbstractNode::TNType -SHAMapAbstractNode::getType() const -{ - return mType; -} - -inline bool -SHAMapAbstractNode::isLeaf() const -{ - return (mType == tnTRANSACTION_NM) || (mType == tnTRANSACTION_MD) || - (mType == tnACCOUNT_STATE); -} - -inline bool -SHAMapAbstractNode::isInner() const -{ - return mType == tnINNER; -} - -inline bool -SHAMapAbstractNode::isInBounds(SHAMapNodeID const& id) const -{ - // Nodes at depth 64 must be leaves - return (!isInner() || (id.getDepth() < 64)); -} - -// SHAMapInnerNode - -inline SHAMapInnerNode::SHAMapInnerNode(std::uint32_t seq) - : SHAMapAbstractNode(tnINNER, seq) -{ -} - -inline bool -SHAMapInnerNode::isEmptyBranch(int m) const -{ - return (mIsBranch & (1 << m)) == 0; -} - -inline SHAMapHash const& -SHAMapInnerNode::getChildHash(int m) const -{ - assert((m >= 0) && (m < 16) && (getType() == tnINNER)); - return mHashes[m]; -} - -inline bool -SHAMapInnerNode::isFullBelow(std::uint32_t generation) const -{ - return mFullBelowGen == generation; -} - -inline void -SHAMapInnerNode::setFullBelowGen(std::uint32_t gen) -{ - mFullBelowGen = gen; -} - -// SHAMapTreeNode - -inline bool -SHAMapTreeNode::hasItem() const -{ - return bool(mItem); -} - -inline std::shared_ptr const& -SHAMapTreeNode::peekItem() const -{ - return mItem; -} - } // namespace ripple #endif diff --git a/src/ripple/shamap/SHAMapTxLeafNode.h b/src/ripple/shamap/SHAMapTxLeafNode.h new file mode 100644 index 000000000..480318212 --- /dev/null +++ b/src/ripple/shamap/SHAMapTxLeafNode.h @@ -0,0 +1,89 @@ +//------------------------------------------------------------------------------ +/* + 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. +*/ +//============================================================================== + +#ifndef RIPPLE_SHAMAP_SHAMAPTXLEAFNODE_H_INCLUDED +#define RIPPLE_SHAMAP_SHAMAPTXLEAFNODE_H_INCLUDED + +#include +#include +#include +#include +#include +#include + +namespace ripple { + +/** A leaf node for a transaction. No metadata is included. */ +class SHAMapTxLeafNode final : public SHAMapLeafNode, + public CountedObject +{ +public: + SHAMapTxLeafNode( + std::shared_ptr item, + std::uint32_t cowid) + : SHAMapLeafNode(std::move(item), cowid) + { + updateHash(); + } + + SHAMapTxLeafNode( + std::shared_ptr item, + std::uint32_t cowid, + SHAMapHash const& hash) + : SHAMapLeafNode(std::move(item), cowid, hash) + { + } + + std::shared_ptr + clone(std::uint32_t cowid) const final override + { + return std::make_shared(item_, cowid, hash_); + } + + SHAMapNodeType + getType() const final override + { + return SHAMapNodeType::tnTRANSACTION_NM; + } + + void + updateHash() final override + { + hash_ = SHAMapHash{sha512Half( + HashPrefix::transactionID, makeSlice(item_->peekData()))}; + } + + void + serializeForWire(Serializer& s) const final override + { + s.addRaw(item_->peekData()); + s.add8(wireTypeTransaction); + } + + void + serializeWithPrefix(Serializer& s) const final override + { + s.add32(HashPrefix::transactionID); + s.addRaw(item_->peekData()); + } +}; + +} // namespace ripple + +#endif diff --git a/src/ripple/shamap/SHAMapTxPlusMetaLeafNode.h b/src/ripple/shamap/SHAMapTxPlusMetaLeafNode.h new file mode 100644 index 000000000..ba919ce26 --- /dev/null +++ b/src/ripple/shamap/SHAMapTxPlusMetaLeafNode.h @@ -0,0 +1,92 @@ +//------------------------------------------------------------------------------ +/* + 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. +*/ +//============================================================================== + +#ifndef RIPPLE_SHAMAP_SHAMAPLEAFTXPLUSMETANODE_H_INCLUDED +#define RIPPLE_SHAMAP_SHAMAPLEAFTXPLUSMETANODE_H_INCLUDED + +#include +#include +#include +#include +#include +#include + +namespace ripple { + +/** A leaf node for a transaction and its associated metadata. */ +class SHAMapTxPlusMetaLeafNode final + : public SHAMapLeafNode, + public CountedObject +{ +public: + SHAMapTxPlusMetaLeafNode( + std::shared_ptr item, + std::uint32_t cowid) + : SHAMapLeafNode(std::move(item), cowid) + { + updateHash(); + } + + SHAMapTxPlusMetaLeafNode( + std::shared_ptr item, + std::uint32_t cowid, + SHAMapHash const& hash) + : SHAMapLeafNode(std::move(item), cowid, hash) + { + } + + std::shared_ptr + clone(std::uint32_t cowid) const override + { + return std::make_shared(item_, cowid, hash_); + } + + SHAMapNodeType + getType() const override + { + return SHAMapNodeType::tnTRANSACTION_MD; + } + + void + updateHash() final override + { + hash_ = SHAMapHash{sha512Half( + HashPrefix::txNode, makeSlice(item_->peekData()), item_->key())}; + } + + void + serializeForWire(Serializer& s) const final override + { + s.addRaw(item_->peekData()); + s.addBitString(item_->key()); + s.add8(wireTypeTransactionWithMeta); + } + + void + serializeWithPrefix(Serializer& s) const final override + { + s.add32(HashPrefix::txNode); + s.addRaw(item_->peekData()); + s.addBitString(item_->key()); + } +}; + +} // namespace ripple + +#endif diff --git a/src/ripple/shamap/TreeNodeCache.h b/src/ripple/shamap/TreeNodeCache.h index 9951db73d..f35c252f4 100644 --- a/src/ripple/shamap/TreeNodeCache.h +++ b/src/ripple/shamap/TreeNodeCache.h @@ -24,7 +24,7 @@ namespace ripple { -using TreeNodeCache = TaggedCache; +using TreeNodeCache = TaggedCache; } // namespace ripple diff --git a/src/ripple/shamap/impl/SHAMap.cpp b/src/ripple/shamap/impl/SHAMap.cpp index 92336965f..5e3b477b3 100644 --- a/src/ripple/shamap/impl/SHAMap.cpp +++ b/src/ripple/shamap/impl/SHAMap.cpp @@ -19,19 +19,41 @@ #include #include +#include #include #include +#include +#include namespace ripple { -SHAMap::SHAMap(SHAMapType t, Family& f) - : f_(f) - , journal_(f.journal()) - , seq_(1) - , state_(SHAMapState::Modifying) - , type_(t) +[[nodiscard]] std::shared_ptr +makeTypedLeaf( + SHAMapNodeType type, + std::shared_ptr item, + std::uint32_t owner) { - root_ = std::make_shared(seq_); + if (type == SHAMapNodeType::tnTRANSACTION_NM) + return std::make_shared(std::move(item), owner); + + if (type == SHAMapNodeType::tnTRANSACTION_MD) + return std::make_shared( + std::move(item), owner); + + if (type == SHAMapNodeType::tnACCOUNT_STATE) + return std::make_shared( + std::move(item), owner); + + LogicError( + "Attempt to create leaf node of unknown type " + + std::to_string( + static_cast>(type))); +} + +SHAMap::SHAMap(SHAMapType t, Family& f) + : f_(f), journal_(f.journal()), state_(SHAMapState::Modifying), type_(t) +{ + root_ = std::make_shared(cowid_); } // The `hash` parameter is unused. It is part of the interface so it's clear @@ -39,18 +61,9 @@ SHAMap::SHAMap(SHAMapType t, Family& f) // known. The fact that the parameter is unused is an implementation detail that // should not change the interface. SHAMap::SHAMap(SHAMapType t, uint256 const& hash, Family& f) - : f_(f) - , journal_(f.journal()) - , seq_(1) - , state_(SHAMapState::Synching) - , type_(t) + : f_(f), journal_(f.journal()), state_(SHAMapState::Synching), type_(t) { - root_ = std::make_shared(seq_); -} - -SHAMap::~SHAMap() -{ - state_ = SHAMapState::Invalid; + root_ = std::make_shared(cowid_); } std::shared_ptr @@ -62,7 +75,7 @@ SHAMap::snapShot(bool isMutable) const if (!isMutable) newMap.state_ = SHAMapState::Immutable; - newMap.seq_ = seq_ + 1; + newMap.cowid_ = cowid_ + 1; newMap.ledgerSeq_ = ledgerSeq_; newMap.root_ = root_; newMap.backed_ = backed_; @@ -81,7 +94,7 @@ void SHAMap::dirtyUp( SharedPtrNodeStack& stack, uint256 const& target, - std::shared_ptr child) + std::shared_ptr child) { // walk the tree up from through the inner nodes to the root_ // update hashes and links @@ -91,7 +104,7 @@ SHAMap::dirtyUp( assert( (state_ != SHAMapState::Synching) && (state_ != SHAMapState::Immutable)); - assert(child && (child->getSeq() == seq_)); + assert(child && (child->cowid() == cowid_)); while (!stack.empty()) { @@ -111,7 +124,7 @@ SHAMap::dirtyUp( } } -SHAMapTreeNode* +SHAMapLeafNode* SHAMap::walkTowardsKey(uint256 const& id, SharedPtrNodeStack* stack) const { assert(stack == nullptr || stack->empty()); @@ -134,22 +147,22 @@ SHAMap::walkTowardsKey(uint256 const& id, SharedPtrNodeStack* stack) const if (stack != nullptr) stack->push({inNode, nodeID}); - return static_cast(inNode.get()); + return static_cast(inNode.get()); } -SHAMapTreeNode* +SHAMapLeafNode* SHAMap::findKey(uint256 const& id) const { - SHAMapTreeNode* leaf = walkTowardsKey(id); + SHAMapLeafNode* leaf = walkTowardsKey(id); if (leaf && leaf->peekItem()->key() != id) leaf = nullptr; return leaf; } -std::shared_ptr +std::shared_ptr SHAMap::fetchNodeFromDB(SHAMapHash const& hash) const { - std::shared_ptr node; + std::shared_ptr node; if (backed_) { @@ -158,7 +171,7 @@ SHAMap::fetchNodeFromDB(SHAMapHash const& hash) const { try { - node = SHAMapAbstractNode::makeFromPrefix( + node = SHAMapTreeNode::makeFromPrefix( makeSlice(nodeObject->getData()), hash); if (node) canonicalize(hash, node); @@ -166,13 +179,13 @@ SHAMap::fetchNodeFromDB(SHAMapHash const& hash) const catch (std::exception const&) { JLOG(journal_.warn()) << "Invalid DB node " << hash; - return std::shared_ptr(); + return std::shared_ptr(); } } else if (full_) { + full_ = false; f_.missingNode(ledgerSeq_); - const_cast(full_) = false; } } @@ -180,7 +193,7 @@ SHAMap::fetchNodeFromDB(SHAMapHash const& hash) const } // See if a sync filter has a node -std::shared_ptr +std::shared_ptr SHAMap::checkFilter(SHAMapHash const& hash, SHAMapSyncFilter* filter) const { if (auto nodeData = filter->getNode(hash)) @@ -188,7 +201,7 @@ SHAMap::checkFilter(SHAMapHash const& hash, SHAMapSyncFilter* filter) const try { auto node = - SHAMapAbstractNode::makeFromPrefix(makeSlice(*nodeData), hash); + SHAMapTreeNode::makeFromPrefix(makeSlice(*nodeData), hash); if (node) { filter->gotNode( @@ -213,10 +226,10 @@ SHAMap::checkFilter(SHAMapHash const& hash, SHAMapSyncFilter* filter) const // Get a node without throwing // Used on maps where missing nodes are expected -std::shared_ptr +std::shared_ptr SHAMap::fetchNodeNT(SHAMapHash const& hash, SHAMapSyncFilter* filter) const { - std::shared_ptr node = getCache(hash); + auto node = cacheLookup(hash); if (node) return node; @@ -236,10 +249,10 @@ SHAMap::fetchNodeNT(SHAMapHash const& hash, SHAMapSyncFilter* filter) const return node; } -std::shared_ptr +std::shared_ptr SHAMap::fetchNodeNT(SHAMapHash const& hash) const { - auto node = getCache(hash); + auto node = cacheLookup(hash); if (!node && backed_) node = fetchNodeFromDB(hash); @@ -248,7 +261,7 @@ SHAMap::fetchNodeNT(SHAMapHash const& hash) const } // Throw if the node is missing -std::shared_ptr +std::shared_ptr SHAMap::fetchNode(SHAMapHash const& hash) const { auto node = fetchNodeNT(hash); @@ -259,10 +272,10 @@ SHAMap::fetchNode(SHAMapHash const& hash) const return node; } -SHAMapAbstractNode* +SHAMapTreeNode* SHAMap::descendThrow(SHAMapInnerNode* parent, int branch) const { - SHAMapAbstractNode* ret = descend(parent, branch); + SHAMapTreeNode* ret = descend(parent, branch); if (!ret && !parent->isEmptyBranch(branch)) Throw(type_, parent->getChildHash(branch)); @@ -270,11 +283,11 @@ SHAMap::descendThrow(SHAMapInnerNode* parent, int branch) const return ret; } -std::shared_ptr +std::shared_ptr SHAMap::descendThrow(std::shared_ptr const& parent, int branch) const { - std::shared_ptr ret = descend(parent, branch); + std::shared_ptr ret = descend(parent, branch); if (!ret && !parent->isEmptyBranch(branch)) Throw(type_, parent->getChildHash(branch)); @@ -282,14 +295,14 @@ SHAMap::descendThrow(std::shared_ptr const& parent, int branch) return ret; } -SHAMapAbstractNode* +SHAMapTreeNode* SHAMap::descend(SHAMapInnerNode* parent, int branch) const { - SHAMapAbstractNode* ret = parent->getChildPointer(branch); + SHAMapTreeNode* ret = parent->getChildPointer(branch); if (ret || !backed_) return ret; - std::shared_ptr node = + std::shared_ptr node = fetchNodeNT(parent->getChildHash(branch)); if (!node) return nullptr; @@ -298,11 +311,11 @@ SHAMap::descend(SHAMapInnerNode* parent, int branch) const return node.get(); } -std::shared_ptr +std::shared_ptr SHAMap::descend(std::shared_ptr const& parent, int branch) const { - std::shared_ptr node = parent->getChild(branch); + std::shared_ptr node = parent->getChild(branch); if (node || !backed_) return node; @@ -316,18 +329,18 @@ SHAMap::descend(std::shared_ptr const& parent, int branch) // Gets the node that would be hooked to this branch, // but doesn't hook it up. -std::shared_ptr +std::shared_ptr SHAMap::descendNoStore( std::shared_ptr const& parent, int branch) const { - std::shared_ptr ret = parent->getChild(branch); + std::shared_ptr ret = parent->getChild(branch); if (!ret && backed_) ret = fetchNode(parent->getChildHash(branch)); return ret; } -std::pair +std::pair SHAMap::descend( SHAMapInnerNode* parent, SHAMapNodeID const& parentID, @@ -338,12 +351,12 @@ SHAMap::descend( assert((branch >= 0) && (branch < branchFactor)); assert(!parent->isEmptyBranch(branch)); - SHAMapAbstractNode* child = parent->getChildPointer(branch); + SHAMapTreeNode* child = parent->getChildPointer(branch); if (!child) { auto const& childHash = parent->getChildHash(branch); - std::shared_ptr childNode = + std::shared_ptr childNode = fetchNodeNT(childHash, filter); if (childNode) @@ -356,7 +369,7 @@ SHAMap::descend( return std::make_pair(child, parentID.getChildNodeID(branch)); } -SHAMapAbstractNode* +SHAMapTreeNode* SHAMap::descendAsync( SHAMapInnerNode* parent, int branch, @@ -365,13 +378,13 @@ SHAMap::descendAsync( { pending = false; - SHAMapAbstractNode* ret = parent->getChildPointer(branch); + SHAMapTreeNode* ret = parent->getChildPointer(branch); if (ret) return ret; auto const& hash = parent->getChildHash(branch); - std::shared_ptr ptr = getCache(hash); + auto ptr = cacheLookup(hash); if (!ptr) { if (filter) @@ -388,8 +401,8 @@ SHAMap::descendAsync( if (!obj) return nullptr; - ptr = SHAMapAbstractNode::makeFromPrefix( - makeSlice(obj->getData()), hash); + ptr = + SHAMapTreeNode::makeFromPrefix(makeSlice(obj->getData()), hash); if (ptr && backed_) canonicalize(hash, ptr); } @@ -406,28 +419,28 @@ std::shared_ptr SHAMap::unshareNode(std::shared_ptr node, SHAMapNodeID const& nodeID) { // make sure the node is suitable for the intended operation (copy on write) - assert(node->getSeq() <= seq_); - if (node->getSeq() != seq_) + assert(node->cowid() <= cowid_); + if (node->cowid() != cowid_) { // have a CoW assert(state_ != SHAMapState::Immutable); - node = std::static_pointer_cast(node->clone(seq_)); + node = std::static_pointer_cast(node->clone(cowid_)); if (nodeID.isRoot()) root_ = node; } return node; } -SHAMapTreeNode* +SHAMapLeafNode* SHAMap::firstBelow( - std::shared_ptr node, + std::shared_ptr node, SharedPtrNodeStack& stack, int branch) const { // Return the first item at or below this node if (node->isLeaf()) { - auto n = std::static_pointer_cast(node); + auto n = std::static_pointer_cast(node); stack.push({node, {leafDepth, n->peekItem()->key()}}); return n.get(); } @@ -444,7 +457,7 @@ SHAMap::firstBelow( assert(!stack.empty()); if (node->isLeaf()) { - auto n = std::static_pointer_cast(node); + auto n = std::static_pointer_cast(node); stack.push({n, {leafDepth, n->peekItem()->key()}}); return n.get(); } @@ -461,13 +474,13 @@ SHAMap::firstBelow( static const std::shared_ptr no_item; std::shared_ptr const& -SHAMap::onlyBelow(SHAMapAbstractNode* node) const +SHAMap::onlyBelow(SHAMapTreeNode* node) const { // If there is only one item below this node, return it while (!node->isLeaf()) { - SHAMapAbstractNode* nextNode = nullptr; + SHAMapTreeNode* nextNode = nullptr; auto inner = static_cast(node); for (int i = 0; i < branchFactor; ++i) { @@ -491,17 +504,16 @@ SHAMap::onlyBelow(SHAMapAbstractNode* node) const // An inner node must have at least one leaf // below it, unless it's the root_ - auto leaf = static_cast(node); - assert(leaf->hasItem() || (leaf == root_.get())); - + auto const leaf = static_cast(node); + assert(leaf->peekItem() || (leaf == root_.get())); return leaf->peekItem(); } -SHAMapTreeNode const* +SHAMapLeafNode const* SHAMap::peekFirstItem(SharedPtrNodeStack& stack) const { assert(stack.empty()); - SHAMapTreeNode* node = firstBelow(root_, stack); + SHAMapLeafNode* node = firstBelow(root_, stack); if (!node) { while (!stack.empty()) @@ -511,7 +523,7 @@ SHAMap::peekFirstItem(SharedPtrNodeStack& stack) const return node; } -SHAMapTreeNode const* +SHAMapLeafNode const* SHAMap::peekNextItem(uint256 const& id, SharedPtrNodeStack& stack) const { assert(!stack.empty()); @@ -543,7 +555,7 @@ SHAMap::peekNextItem(uint256 const& id, SharedPtrNodeStack& stack) const std::shared_ptr const& SHAMap::peekItem(uint256 const& id) const { - SHAMapTreeNode* leaf = findKey(id); + SHAMapLeafNode* leaf = findKey(id); if (!leaf) return no_item; @@ -551,27 +563,15 @@ SHAMap::peekItem(uint256 const& id) const return leaf->peekItem(); } -std::shared_ptr const& -SHAMap::peekItem(uint256 const& id, SHAMapTreeNode::TNType& type) const -{ - SHAMapTreeNode* leaf = findKey(id); - - if (!leaf) - return no_item; - - type = leaf->getType(); - return leaf->peekItem(); -} - std::shared_ptr const& SHAMap::peekItem(uint256 const& id, SHAMapHash& hash) const { - SHAMapTreeNode* leaf = findKey(id); + SHAMapLeafNode* leaf = findKey(id); if (!leaf) return no_item; - hash = leaf->getNodeHash(); + hash = leaf->getHash(); return leaf->peekItem(); } @@ -587,7 +587,7 @@ SHAMap::upper_bound(uint256 const& id) const auto [node, nodeID] = stack.top(); if (node->isLeaf()) { - auto leaf = static_cast(node.get()); + auto leaf = static_cast(node.get()); if (leaf->peekItem()->key() > id) return const_iterator( this, leaf->peekItem().get(), std::move(stack)); @@ -618,9 +618,7 @@ SHAMap::upper_bound(uint256 const& id) const bool SHAMap::hasItem(uint256 const& id) const { - // does the tree have an item with this ID - SHAMapTreeNode* leaf = findKey(id); - return (leaf != nullptr); + return (findKey(id) != nullptr); } bool @@ -635,17 +633,17 @@ SHAMap::delItem(uint256 const& id) if (stack.empty()) Throw(type_, id); - auto leaf = std::dynamic_pointer_cast(stack.top().first); + auto leaf = std::dynamic_pointer_cast(stack.top().first); stack.pop(); if (!leaf || (leaf->peekItem()->key() != id)) return false; - SHAMapTreeNode::TNType type = leaf->getType(); + SHAMapNodeType type = leaf->getType(); // What gets attached to the end of the chain // (For now, nothing, since we deleted the leaf) - std::shared_ptr prevNode; + std::shared_ptr prevNode; while (!stack.empty()) { @@ -682,8 +680,8 @@ SHAMap::delItem(uint256 const& id) break; } } - prevNode = std::make_shared( - item, type, node->getSeq()); + + prevNode = makeTypedLeaf(type, item, node->cowid()); } else { @@ -702,19 +700,13 @@ SHAMap::delItem(uint256 const& id) } bool -SHAMap::addGiveItem( - std::shared_ptr item, - bool isTransaction, - bool hasMeta) +SHAMap::addGiveItem(SHAMapNodeType type, std::shared_ptr item) { + assert(state_ != SHAMapState::Immutable); + assert(type != SHAMapNodeType::tnINNER); + // add the specified item, does not update uint256 tag = item->key(); - SHAMapTreeNode::TNType type = !isTransaction - ? SHAMapTreeNode::tnACCOUNT_STATE - : (hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD - : SHAMapTreeNode::tnTRANSACTION_NM); - - assert(state_ != SHAMapState::Immutable); SharedPtrNodeStack stack; walkTowardsKey(tag, &stack); @@ -727,7 +719,7 @@ SHAMap::addGiveItem( if (node->isLeaf()) { - auto leaf = std::static_pointer_cast(node); + auto leaf = std::static_pointer_cast(node); if (leaf->peekItem()->key() == tag) return false; } @@ -738,21 +730,20 @@ SHAMap::addGiveItem( auto inner = std::static_pointer_cast(node); int branch = selectBranch(nodeID, tag); assert(inner->isEmptyBranch(branch)); - auto newNode = - std::make_shared(std::move(item), type, seq_); + auto newNode = makeTypedLeaf(type, std::move(item), cowid_); 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(node); + auto leaf = std::static_pointer_cast(node); std::shared_ptr otherItem = leaf->peekItem(); assert(otherItem && (tag != otherItem->key())); - node = std::make_shared(node->getSeq()); + node = std::make_shared(node->cowid()); - int b1, b2; + unsigned int b1, b2; while ((b1 = selectBranch(nodeID, tag)) == (b2 = selectBranch(nodeID, otherItem->key()))) @@ -762,22 +753,15 @@ SHAMap::addGiveItem( // we need a new inner node, since both go on same branch at this // level nodeID = nodeID.getChildNodeID(b1); - node = std::make_shared(seq_); + node = std::make_shared(cowid_); } // we can add the two leaf nodes here assert(node->isInner()); - std::shared_ptr newNode = - std::make_shared(std::move(item), type, seq_); - assert(newNode->isLeaf()); - auto inner = std::static_pointer_cast(node); - inner->setChild(b1, newNode); - - newNode = - std::make_shared(std::move(otherItem), type, seq_); - assert(newNode->isLeaf()); - inner->setChild(b2, newNode); + auto inner = static_cast(node.get()); + inner->setChild(b1, makeTypedLeaf(type, std::move(item), cowid_)); + inner->setChild(b2, makeTypedLeaf(type, std::move(otherItem), cowid_)); } dirtyUp(stack, tag, node); @@ -785,31 +769,27 @@ SHAMap::addGiveItem( } bool -SHAMap::addItem(SHAMapItem&& i, bool isTransaction, bool hasMetaData) +SHAMap::addItem(SHAMapNodeType type, SHAMapItem&& i) { - return addGiveItem( - std::make_shared(std::move(i)), - isTransaction, - hasMetaData); + return addGiveItem(type, std::make_shared(std::move(i))); } SHAMapHash SHAMap::getHash() const { - auto hash = root_->getNodeHash(); + auto hash = root_->getHash(); if (hash.isZero()) { const_cast(*this).unshare(); - hash = root_->getNodeHash(); + hash = root_->getHash(); } return hash; } bool SHAMap::updateGiveItem( - std::shared_ptr item, - bool isTransaction, - bool hasMeta) + SHAMapNodeType type, + std::shared_ptr item) { // can't change the tag but can change the hash uint256 tag = item->key(); @@ -822,7 +802,7 @@ SHAMap::updateGiveItem( if (stack.empty()) Throw(type_, tag); - auto node = std::dynamic_pointer_cast(stack.top().first); + auto node = std::dynamic_pointer_cast(stack.top().first); auto nodeID = stack.top().second; stack.pop(); @@ -832,26 +812,24 @@ SHAMap::updateGiveItem( return false; } - node = unshareNode(std::move(node), nodeID); - - if (!node->setItem( - std::move(item), - !isTransaction ? SHAMapTreeNode::tnACCOUNT_STATE - : (hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD - : SHAMapTreeNode::tnTRANSACTION_NM))) + if (node->getType() != type) { - JLOG(journal_.trace()) << "SHAMap setItem, no change"; - return true; + JLOG(journal_.fatal()) << "SHAMap::setItem: cross-type change!"; + return false; } - dirtyUp(stack, tag, node); + node = unshareNode(std::move(node), nodeID); + + if (node->setItem(std::move(item))) + dirtyUp(stack, tag, node); + return true; } bool SHAMap::fetchRoot(SHAMapHash const& hash, SHAMapSyncFilter* filter) { - if (hash == root_->getNodeHash()) + if (hash == root_->getHash()) return true; if (auto stream = journal_.trace()) @@ -875,42 +853,37 @@ SHAMap::fetchRoot(SHAMapHash const& hash, SHAMapSyncFilter* filter) if (newRoot) { root_ = newRoot; - assert(root_->getNodeHash() == hash); + assert(root_->getHash() == hash); return true; } return false; } -// Replace a node with a shareable node. -// -// This code handles two cases: -// -// 1) An unshared, unshareable node needs to be made shareable -// so immutable SHAMap's can have references to it. -// -// 2) An unshareable node is shared. This happens when you make -// a mutable snapshot of a mutable SHAMap. -std::shared_ptr -SHAMap::writeNode( - NodeObjectType t, - std::uint32_t seq, - std::shared_ptr node) const -{ - // Node is ours, so we can just make it shareable - assert(node->getSeq() == seq_); - assert(backed_); - node->setSeq(0); +/** Replace a node with a shareable node. - canonicalize(node->getNodeHash(), node); + This code handles two cases: + + 1) An unshared, unshareable node needs to be made shareable + so immutable SHAMap's can have references to it. + 2) An unshareable node is shared. This happens when you make + a mutable snapshot of a mutable SHAMap. + + @note The node must have already been unshared by having the caller + first call SHAMapTreeNode::unshare(). + */ +std::shared_ptr +SHAMap::writeNode(NodeObjectType t, std::shared_ptr node) const +{ + assert(node->cowid() == 0); + assert(backed_); + + canonicalize(node->getHash(), node); Serializer s; node->serializeWithPrefix(s); f_.db().store( - t, - std::move(s.modData()), - node->getNodeHash().as_uint256(), - ledgerSeq_); + t, std::move(s.modData()), node->getHash().as_uint256(), ledgerSeq_); return node; } @@ -923,13 +896,13 @@ SHAMap::preFlushNode(std::shared_ptr node) const { // A shared node should never need to be flushed // because that would imply someone modified it - assert(node->getSeq() != 0); + assert(node->cowid() != 0); - if (node->getSeq() != seq_) + if (node->cowid() != cowid_) { // Node is not uniquely ours, so unshare it before // possibly modifying it - node = std::static_pointer_cast(node->clone(seq_)); + node = std::static_pointer_cast(node->clone(cowid_)); } return node; } @@ -937,35 +910,36 @@ SHAMap::preFlushNode(std::shared_ptr node) const int SHAMap::unshare() { - // Don't share nodes wth parent map - return walkSubTree(false, hotUNKNOWN, 0); -} - -/** Convert all modified nodes to shared nodes */ -// If requested, write them to the node store -int -SHAMap::flushDirty(NodeObjectType t, std::uint32_t seq) -{ - return walkSubTree(true, t, seq); + // Don't share nodes with parent map + return walkSubTree(false, hotUNKNOWN); } int -SHAMap::walkSubTree(bool doWrite, NodeObjectType t, std::uint32_t seq) +SHAMap::flushDirty(NodeObjectType t) { + // We only write back if this map is backed. + return walkSubTree(backed_, t); +} + +int +SHAMap::walkSubTree(bool doWrite, NodeObjectType t) +{ + assert(!doWrite || backed_); + int flushed = 0; - Serializer s; - if (!root_ || (root_->getSeq() == 0)) + if (!root_ || (root_->cowid() == 0)) return flushed; if (root_->isLeaf()) { // special case -- root_ is leaf root_ = preFlushNode(std::move(root_)); root_->updateHash(); - if (doWrite && backed_) - root_ = writeNode(t, seq, std::move(root_)); - else - root_->setSeq(0); + root_->unshare(); + + if (doWrite) + root_ = writeNode(t, std::move(root_)); + return 1; } @@ -1002,7 +976,7 @@ SHAMap::walkSubTree(bool doWrite, NodeObjectType t, std::uint32_t seq) int branch = pos; auto child = node->getChild(pos++); - if (child && (child->getSeq() != 0)) + if (child && (child->cowid() != 0)) { // This is a node that needs to be flushed @@ -1025,13 +999,12 @@ SHAMap::walkSubTree(bool doWrite, NodeObjectType t, std::uint32_t seq) // flush this leaf ++flushed; - assert(node->getSeq() == seq_); + assert(node->cowid() == cowid_); child->updateHash(); + child->unshare(); - if (doWrite && backed_) - child = writeNode(t, seq, std::move(child)); - else - child->setSeq(0); + if (doWrite) + child = writeNode(t, std::move(child)); node->shareChild(branch, child); } @@ -1043,11 +1016,11 @@ SHAMap::walkSubTree(bool doWrite, NodeObjectType t, std::uint32_t seq) node->updateHashDeep(); // This inner node can now be shared - if (doWrite && backed_) + node->unshare(); + + if (doWrite) node = std::static_pointer_cast( - writeNode(t, seq, std::move(node))); - else - node->setSeq(0); + writeNode(t, std::move(node))); ++flushed; @@ -1059,7 +1032,7 @@ SHAMap::walkSubTree(bool doWrite, NodeObjectType t, std::uint32_t seq) stack.pop(); // Hook this inner node to its parent - assert(parent->getSeq() == seq_); + assert(parent->cowid() == cowid_); parent->shareChild(pos, node); // Continue with parent's next child, if any @@ -1079,7 +1052,7 @@ SHAMap::dump(bool hash) const int leafCount = 0; JLOG(journal_.info()) << " MAP Contains"; - std::stack> stack; + std::stack> stack; stack.push({root_.get(), SHAMapNodeID()}); do @@ -1090,7 +1063,7 @@ SHAMap::dump(bool hash) const JLOG(journal_.info()) << node->getString(nodeID); if (hash) { - JLOG(journal_.info()) << "Hash: " << node->getNodeHash(); + JLOG(journal_.info()) << "Hash: " << node->getHash(); } if (node->isInner()) @@ -1103,7 +1076,7 @@ SHAMap::dump(bool hash) const auto child = inner->getChildPointer(i); if (child) { - assert(child->getNodeHash() == inner->getChildHash(i)); + assert(child->getHash() == inner->getChildHash(i)); stack.push({child, nodeID.getChildNodeID(i)}); } } @@ -1116,22 +1089,22 @@ SHAMap::dump(bool hash) const JLOG(journal_.info()) << leafCount << " resident leaves"; } -std::shared_ptr -SHAMap::getCache(SHAMapHash const& hash) const +std::shared_ptr +SHAMap::cacheLookup(SHAMapHash const& hash) const { auto ret = f_.getTreeNodeCache(ledgerSeq_)->fetch(hash.as_uint256()); - assert(!ret || !ret->getSeq()); + assert(!ret || !ret->cowid()); return ret; } void SHAMap::canonicalize( SHAMapHash const& hash, - std::shared_ptr& node) const + std::shared_ptr& node) const { assert(backed_); - assert(node->getSeq() == 0); - assert(node->getNodeHash() == hash); + assert(node->cowid() == 0); + assert(node->getHash() == hash); f_.getTreeNodeCache(ledgerSeq_) ->canonicalize_replace_client(hash.as_uint256(), node); diff --git a/src/ripple/shamap/impl/SHAMapDelta.cpp b/src/ripple/shamap/impl/SHAMapDelta.cpp index 67a26dc5c..98efec2ce 100644 --- a/src/ripple/shamap/impl/SHAMapDelta.cpp +++ b/src/ripple/shamap/impl/SHAMapDelta.cpp @@ -32,7 +32,7 @@ namespace ripple { bool SHAMap::walkBranch( - SHAMapAbstractNode* node, + SHAMapTreeNode* node, std::shared_ptr const& otherMapItem, bool isFirstMap, Delta& differences, @@ -40,7 +40,7 @@ SHAMap::walkBranch( { // Walk a branch of a SHAMap that's matched by an empty branch or single // item in the other map - std::stack> nodeStack; + std::stack> nodeStack; nodeStack.push(node); bool emptyBranch = !otherMapItem; @@ -61,7 +61,7 @@ SHAMap::walkBranch( else { // This is a leaf node, process its item - auto item = static_cast(node)->peekItem(); + auto item = static_cast(node)->peekItem(); if (emptyBranch || (item->key() != otherMapItem->key())) { @@ -133,7 +133,7 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const if (getHash() == otherMap.getHash()) return true; - using StackEntry = std::pair; + using StackEntry = std::pair; std::stack> nodeStack; // track nodes we've pushed @@ -152,8 +152,8 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const if (ourNode->isLeaf() && otherNode->isLeaf()) { // two leaves - auto ours = static_cast(ourNode); - auto other = static_cast(otherNode); + auto ours = static_cast(ourNode); + auto other = static_cast(otherNode); if (ours->peekItem()->key() == other->peekItem()->key()) { if (ours->peekItem()->peekData() != @@ -188,14 +188,14 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const else if (ourNode->isInner() && otherNode->isLeaf()) { auto ours = static_cast(ourNode); - auto other = static_cast(otherNode); + auto other = static_cast(otherNode); if (!walkBranch( ours, other->peekItem(), true, differences, maxCount)) return false; } else if (ourNode->isLeaf() && otherNode->isInner()) { - auto ours = static_cast(ourNode); + auto ours = static_cast(ourNode); auto other = static_cast(otherNode); if (!otherMap.walkBranch( other, ours->peekItem(), false, differences, maxCount)) @@ -211,7 +211,7 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const if (other->isEmptyBranch(i)) { // We have a branch, the other tree does not - SHAMapAbstractNode* iNode = descendThrow(ours, i); + SHAMapTreeNode* iNode = descendThrow(ours, i); if (!walkBranch( iNode, std::shared_ptr(), @@ -223,8 +223,7 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const else if (ours->isEmptyBranch(i)) { // The other tree has a branch, we do not - SHAMapAbstractNode* iNode = - otherMap.descendThrow(other, i); + SHAMapTreeNode* iNode = otherMap.descendThrow(other, i); if (!otherMap.walkBranch( iNode, std::shared_ptr(), @@ -267,7 +266,7 @@ SHAMap::walkMap(std::vector& missingNodes, int maxMissing) { if (!node->isEmptyBranch(i)) { - std::shared_ptr nextNode = + std::shared_ptr nextNode = descendNoStore(node, i); if (nextNode) diff --git a/src/ripple/shamap/impl/SHAMapInnerNode.cpp b/src/ripple/shamap/impl/SHAMapInnerNode.cpp new file mode 100644 index 000000000..62258544a --- /dev/null +++ b/src/ripple/shamap/impl/SHAMapInnerNode.cpp @@ -0,0 +1,313 @@ +//------------------------------------------------------------------------------ +/* + 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace ripple { + +std::mutex SHAMapInnerNode::childLock; + +std::shared_ptr +SHAMapInnerNode::clone(std::uint32_t cowid) const +{ + auto p = std::make_shared(cowid); + p->hash_ = hash_; + p->mIsBranch = mIsBranch; + p->mFullBelowGen = mFullBelowGen; + p->mHashes = mHashes; + std::lock_guard lock(childLock); + for (int i = 0; i < 16; ++i) + p->mChildren[i] = mChildren[i]; + return p; +} + +std::shared_ptr +SHAMapInnerNode::makeFullInner( + Slice data, + SHAMapHash const& hash, + bool hashValid) +{ + if (data.size() != 512) + Throw("Invalid FI node"); + + auto ret = std::make_shared(0); + + Serializer s(data.data(), data.size()); + + for (int i = 0; i < 16; ++i) + { + s.getBitString(ret->mHashes[i].as_uint256(), i * 32); + + if (ret->mHashes[i].isNonZero()) + ret->mIsBranch |= (1 << i); + } + + if (hashValid) + ret->hash_ = hash; + else + ret->updateHash(); + return ret; +} + +std::shared_ptr +SHAMapInnerNode::makeCompressedInner(Slice data) +{ + Serializer s(data.data(), data.size()); + + int len = s.getLength(); + + auto ret = std::make_shared(0); + + for (int i = 0; i < (len / 33); ++i) + { + int pos; + + if (!s.get8(pos, 32 + (i * 33))) + Throw("short CI node"); + + if ((pos < 0) || (pos >= 16)) + Throw("invalid CI node"); + + s.getBitString(ret->mHashes[pos].as_uint256(), i * 33); + + if (ret->mHashes[pos].isNonZero()) + ret->mIsBranch |= (1 << pos); + } + + ret->updateHash(); + + return ret; +} + +void +SHAMapInnerNode::updateHash() +{ + uint256 nh; + if (mIsBranch != 0) + { + sha512_half_hasher h; + using beast::hash_append; + hash_append(h, HashPrefix::innerNode); + for (auto const& hh : mHashes) + hash_append(h, hh); + nh = static_cast(h); + } + hash_ = SHAMapHash{nh}; +} + +void +SHAMapInnerNode::updateHashDeep() +{ + for (auto pos = 0; pos < 16; ++pos) + { + if (mChildren[pos] != nullptr) + mHashes[pos] = mChildren[pos]->getHash(); + } + updateHash(); +} + +void +SHAMapInnerNode::serializeForWire(Serializer& s) const +{ + assert(!isEmpty()); + + // If the node is sparse, then only send non-empty branches: + if (getBranchCount() < 12) + { + // compressed node + for (int i = 0; i < mHashes.size(); ++i) + { + if (!isEmptyBranch(i)) + { + s.addBitString(mHashes[i].as_uint256()); + s.add8(i); + } + } + + s.add8(wireTypeCompressedInner); + } + else + { + for (auto const& hh : mHashes) + s.addBitString(hh.as_uint256()); + + s.add8(wireTypeInner); + } +} + +void +SHAMapInnerNode::serializeWithPrefix(Serializer& s) const +{ + assert(!isEmpty()); + + s.add32(HashPrefix::innerNode); + for (auto const& hh : mHashes) + s.addBitString(hh.as_uint256()); +} + +bool +SHAMapInnerNode::isEmpty() const +{ + return mIsBranch == 0; +} + +int +SHAMapInnerNode::getBranchCount() const +{ + int count = 0; + + for (int i = 0; i < 16; ++i) + if (!isEmptyBranch(i)) + ++count; + + return count; +} + +std::string +SHAMapInnerNode::getString(const SHAMapNodeID& id) const +{ + std::string ret = SHAMapTreeNode::getString(id); + for (int i = 0; i < mHashes.size(); ++i) + { + if (!isEmptyBranch(i)) + { + ret += "\n"; + ret += std::to_string(i); + ret += " = "; + ret += to_string(mHashes[i]); + } + } + return ret; +} + +// We are modifying an inner node +void +SHAMapInnerNode::setChild(int m, std::shared_ptr const& child) +{ + assert((m >= 0) && (m < 16)); + assert(cowid_ != 0); + assert(child.get() != this); + mHashes[m].zero(); + hash_.zero(); + if (child) + mIsBranch |= (1 << m); + else + mIsBranch &= ~(1 << m); + mChildren[m] = child; +} + +// finished modifying, now make shareable +void +SHAMapInnerNode::shareChild(int m, std::shared_ptr const& child) +{ + assert((m >= 0) && (m < 16)); + assert(cowid_ != 0); + assert(child); + assert(child.get() != this); + + mChildren[m] = child; +} + +SHAMapTreeNode* +SHAMapInnerNode::getChildPointer(int branch) +{ + assert(branch >= 0 && branch < 16); + + std::lock_guard lock(childLock); + return mChildren[branch].get(); +} + +std::shared_ptr +SHAMapInnerNode::getChild(int branch) +{ + assert(branch >= 0 && branch < 16); + + std::lock_guard lock(childLock); + return mChildren[branch]; +} + +std::shared_ptr +SHAMapInnerNode::canonicalizeChild( + int branch, + std::shared_ptr node) +{ + assert(branch >= 0 && branch < 16); + assert(node); + assert(node->getHash() == mHashes[branch]); + + std::lock_guard 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; + } + return node; +} + +void +SHAMapInnerNode::invariants(bool is_root) const +{ + 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(); + ++count; + } + else + { + assert((mIsBranch & (1 << i)) == 0); + } + } + if (!is_root) + { + assert(hash_.isNonZero()); + assert(count >= 1); + } + assert((count == 0) ? hash_.isZero() : hash_.isNonZero()); +} + +} // namespace ripple diff --git a/src/ripple/shamap/impl/SHAMapLeafNode.cpp b/src/ripple/shamap/impl/SHAMapLeafNode.cpp new file mode 100644 index 000000000..4c2d82f88 --- /dev/null +++ b/src/ripple/shamap/impl/SHAMapLeafNode.cpp @@ -0,0 +1,94 @@ +//------------------------------------------------------------------------------ +/* + 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 +#include +#include + +namespace ripple { + +SHAMapLeafNode::SHAMapLeafNode( + std::shared_ptr item, + std::uint32_t cowid) + : SHAMapTreeNode(cowid), item_(std::move(item)) +{ + assert(item_->peekData().size() >= 12); +} + +SHAMapLeafNode::SHAMapLeafNode( + std::shared_ptr item, + std::uint32_t cowid, + SHAMapHash const& hash) + : SHAMapTreeNode(cowid, hash), item_(std::move(item)) +{ + assert(item_->peekData().size() >= 12); +} + +std::shared_ptr const& +SHAMapLeafNode::peekItem() const +{ + return item_; +} + +bool +SHAMapLeafNode::setItem(std::shared_ptr i) +{ + assert(cowid_ != 0); + item_ = std::move(i); + + auto const oldHash = hash_; + + updateHash(); + + return (oldHash != hash_); +} + +std::string +SHAMapLeafNode::getString(const SHAMapNodeID& id) const +{ + std::string ret = SHAMapTreeNode::getString(id); + + auto const type = getType(); + + if (type == SHAMapNodeType::tnTRANSACTION_NM) + ret += ",txn\n"; + else if (type == SHAMapNodeType::tnTRANSACTION_MD) + ret += ",txn+md\n"; + else if (type == SHAMapNodeType::tnACCOUNT_STATE) + ret += ",as\n"; + else + ret += ",leaf\n"; + + ret += " Tag="; + ret += to_string(item_->key()); + ret += "\n Hash="; + ret += to_string(hash_); + ret += "/"; + ret += std::to_string(item_->size()); + return ret; +} + +void +SHAMapLeafNode::invariants(bool) const +{ + assert(hash_.isNonZero()); + assert(item_ != nullptr); +} + +} // namespace ripple diff --git a/src/ripple/shamap/impl/SHAMapSync.cpp b/src/ripple/shamap/impl/SHAMapSync.cpp index 85022621e..0b5864120 100644 --- a/src/ripple/shamap/impl/SHAMapSync.cpp +++ b/src/ripple/shamap/impl/SHAMapSync.cpp @@ -28,16 +28,15 @@ SHAMap::visitLeaves( std::function const& item)> const& leafFunction) const { - visitNodes([&leafFunction](SHAMapAbstractNode& node) { + visitNodes([&leafFunction](SHAMapTreeNode& node) { if (!node.isInner()) - leafFunction(static_cast(node).peekItem()); + leafFunction(static_cast(node).peekItem()); return true; }); } void -SHAMap::visitNodes( - std::function const& function) const +SHAMap::visitNodes(std::function const& function) const { if (!root_) return; @@ -60,7 +59,7 @@ SHAMap::visitNodes( uint256 childHash; if (!node->isEmptyBranch(pos)) { - std::shared_ptr child = + std::shared_ptr child = descendNoStore(node, pos); if (!function(*child)) return; @@ -101,24 +100,24 @@ SHAMap::visitNodes( void SHAMap::visitDifferences( SHAMap const* have, - std::function function) const + std::function function) const { // Visit every node in this SHAMap that is not present // in the specified SHAMap if (!root_) return; - if (root_->getNodeHash().isZero()) + if (root_->getHash().isZero()) return; - if (have && (root_->getNodeHash() == have->root_->getNodeHash())) + if (have && (root_->getHash() == have->root_->getHash())) return; if (root_->isLeaf()) { - auto leaf = std::static_pointer_cast(root_); + auto leaf = std::static_pointer_cast(root_); if (!have || - !have->hasLeafNode(leaf->peekItem()->key(), leaf->getNodeHash())) + !have->hasLeafNode(leaf->peekItem()->key(), leaf->getHash())) function(*root_); return; } @@ -155,7 +154,7 @@ SHAMap::visitDifferences( else if ( !have || !have->hasLeafNode( - static_cast(next)->peekItem()->key(), + static_cast(next)->peekItem()->key(), childHash)) { if (!function(*next)) @@ -242,7 +241,7 @@ SHAMap::gmn_ProcessNodes(MissingNodes& mn, MissingNodes::StackEntry& se) if (backed_) { f_.getFullBelowCache(ledgerSeq_) - ->insert(node->getNodeHash().as_uint256()); + ->insert(node->getHash().as_uint256()); } } @@ -315,7 +314,7 @@ SHAMap::gmn_ProcessDeferredReads(MissingNodes& mn) std::vector> SHAMap::getMissingNodes(int max, SHAMapSyncFilter* filter) { - assert(root_->getNodeHash().isNonZero()); + assert(root_->getHash().isNonZero()); assert(max > 0); MissingNodes mn( @@ -423,23 +422,9 @@ SHAMap::getMissingNodes(int max, SHAMapSyncFilter* filter) return std::move(mn.missingNodes_); } -std::vector -SHAMap::getNeededHashes(int max, SHAMapSyncFilter* filter) -{ - auto ret = getMissingNodes(max, filter); - - std::vector hashes; - hashes.reserve(ret.size()); - - for (auto const& n : ret) - hashes.push_back(n.second); - - return hashes; -} - bool SHAMap::getNodeFat( - SHAMapNodeID wanted, + SHAMapNodeID const& wanted, std::vector& nodeIDs, std::vector& rawNodes, bool fatLeaves, @@ -476,7 +461,7 @@ SHAMap::getNodeFat( return false; } - std::stack> stack; + std::stack> stack; stack.emplace(node, nodeID, depth); while (!stack.empty()) @@ -547,16 +532,16 @@ SHAMap::addRootNode( SHAMapSyncFilter* filter) { // we already have a root_ node - if (root_->getNodeHash().isNonZero()) + if (root_->getHash().isNonZero()) { JLOG(journal_.trace()) << "got root node, already have one"; - assert(root_->getNodeHash() == hash); + assert(root_->getHash() == hash); return SHAMapAddNode::duplicate(); } - assert(seq_ >= 1); - auto node = SHAMapAbstractNode::makeFromWire(rootNode); - if (!node || node->getNodeHash() != hash) + assert(cowid_ >= 1); + auto node = SHAMapTreeNode::makeFromWire(rootNode); + if (!node || node->getHash() != hash) return SHAMapAddNode::invalid(); if (backed_) @@ -573,7 +558,7 @@ SHAMap::addRootNode( root_->serializeWithPrefix(s); filter->gotNode( false, - root_->getNodeHash(), + root_->getHash(), ledgerSeq_, std::move(s.modData()), root_->getType()); @@ -597,7 +582,7 @@ SHAMap::addKnownNode( } auto const generation = f_.getFullBelowCache(ledgerSeq_)->getGeneration(); - auto newNode = SHAMapAbstractNode::makeFromWire(rawNode); + auto newNode = SHAMapTreeNode::makeFromWire(rawNode); SHAMapNodeID iNodeID; auto iNode = root_.get(); @@ -626,13 +611,17 @@ SHAMap::addKnownNode( if (iNode == nullptr) { - if (!newNode || childHash != newNode->getNodeHash()) + if (!newNode || childHash != newNode->getHash()) { JLOG(journal_.warn()) << "Corrupt node received"; return SHAMapAddNode::invalid(); } - if (!newNode->isInBounds(iNodeID)) + // Inner nodes must be at a level strictly less than 64 + // but leaf nodes (while notionally at level 64) can be + // at any depth up to and including 64: + if ((iNodeID.getDepth() > leafDepth) || + (newNode->isInner() && iNodeID.getDepth() == leafDepth)) { // Map is provably invalid state_ = SHAMapState::Invalid; @@ -678,7 +667,7 @@ bool SHAMap::deepCompare(SHAMap& other) const { // Intended for debug/test only - std::stack> stack; + std::stack> stack; stack.push({root_.get(), other.root_.get()}); @@ -692,7 +681,7 @@ SHAMap::deepCompare(SHAMap& other) const JLOG(journal_.info()) << "unable to fetch node"; return false; } - else if (otherNode->getNodeHash() != node->getNodeHash()) + else if (otherNode->getHash() != node->getHash()) { JLOG(journal_.warn()) << "node hash mismatch"; return false; @@ -702,9 +691,9 @@ SHAMap::deepCompare(SHAMap& other) const { if (!otherNode->isLeaf()) return false; - auto& nodePeek = static_cast(node)->peekItem(); + auto& nodePeek = static_cast(node)->peekItem(); auto& otherNodePeek = - static_cast(otherNode)->peekItem(); + static_cast(otherNode)->peekItem(); if (nodePeek->key() != otherNodePeek->key()) return false; if (nodePeek->peekData() != otherNodePeek->peekData()) @@ -765,7 +754,7 @@ SHAMap::hasInnerNode( nodeID = nodeID.getChildNodeID(branch); } - return (node->isInner()) && (node->getNodeHash() == targetNodeHash); + return (node->isInner()) && (node->getHash() == targetNodeHash); } /** Does this map have this leaf node? @@ -777,7 +766,7 @@ SHAMap::hasLeafNode(uint256 const& tag, SHAMapHash const& targetNodeHash) const SHAMapNodeID nodeID; if (!node->isInner()) // only one leaf node in the tree - return node->getNodeHash() == targetNodeHash; + return node->getHash() == targetNodeHash; do { @@ -798,35 +787,4 @@ SHAMap::hasLeafNode(uint256 const& tag, SHAMapHash const& targetNodeHash) const // already } -/** -@param have A pointer to the map that the recipient already has (if any). -@param includeLeaves True if leaf nodes should be included. -@param max The maximum number of nodes to return. -@param func The functor to call for each node added to the FetchPack. - -Note: a caller should set includeLeaves to false for transaction trees. -There's no point in including the leaves of transaction trees. -*/ -void -SHAMap::getFetchPack( - SHAMap const* have, - bool includeLeaves, - int max, - std::function func) const -{ - visitDifferences( - have, [includeLeaves, &max, &func](SHAMapAbstractNode& smn) -> bool { - if (includeLeaves || smn.isInner()) - { - Serializer s; - smn.serializeWithPrefix(s); - func(smn.getNodeHash(), s.peekData()); - - if (--max <= 0) - return false; - } - return true; - }); -} - } // namespace ripple diff --git a/src/ripple/shamap/impl/SHAMapTreeNode.cpp b/src/ripple/shamap/impl/SHAMapTreeNode.cpp index e05f9c3dd..09416ec24 100644 --- a/src/ripple/shamap/impl/SHAMapTreeNode.cpp +++ b/src/ripple/shamap/impl/SHAMapTreeNode.cpp @@ -24,69 +24,21 @@ #include #include #include +#include +#include +#include #include +#include +#include #include #include namespace ripple { -// These are wire-protocol identifiers used during serialization to encode the -// type of a node. They should not be arbitrarily be changed. -static constexpr unsigned char const wireTypeTransaction = 0; -static constexpr unsigned char const wireTypeAccountState = 1; -static constexpr unsigned char const wireTypeInner = 2; -static constexpr unsigned char const wireTypeCompressedInner = 3; -static constexpr unsigned char const wireTypeTransactionWithMeta = 4; - -std::mutex SHAMapInnerNode::childLock; - -SHAMapAbstractNode::~SHAMapAbstractNode() = default; - -std::shared_ptr -SHAMapInnerNode::clone(std::uint32_t seq) const -{ - auto p = std::make_shared(seq); - p->mHash = mHash; - p->mIsBranch = mIsBranch; - p->mFullBelowGen = mFullBelowGen; - p->mHashes = mHashes; - std::lock_guard lock(childLock); - for (int i = 0; i < 16; ++i) - p->mChildren[i] = mChildren[i]; - return p; -} - -std::shared_ptr -SHAMapTreeNode::clone(std::uint32_t seq) const -{ - return std::make_shared(mItem, mType, seq, mHash); -} - -SHAMapTreeNode::SHAMapTreeNode( - std::shared_ptr item, - TNType type, - std::uint32_t seq) - : SHAMapAbstractNode(type, seq), mItem(std::move(item)) -{ - assert(mItem->peekData().size() >= 12); - updateHash(); -} - -SHAMapTreeNode::SHAMapTreeNode( - std::shared_ptr item, - TNType type, - std::uint32_t seq, - SHAMapHash const& hash) - : SHAMapAbstractNode(type, seq, hash), mItem(std::move(item)) -{ - assert(mItem->peekData().size() >= 12); -} - -std::shared_ptr -SHAMapAbstractNode::makeTransaction( +std::shared_ptr +SHAMapTreeNode::makeTransaction( Slice data, - std::uint32_t seq, SHAMapHash const& hash, bool hashValid) { @@ -97,17 +49,14 @@ SHAMapAbstractNode::makeTransaction( sha512Half(HashPrefix::transactionID, data), s); if (hashValid) - return std::make_shared( - std::move(item), tnTRANSACTION_NM, seq, hash); + return std::make_shared(std::move(item), 0, hash); - return std::make_shared( - std::move(item), tnTRANSACTION_NM, seq); + return std::make_shared(std::move(item), 0); } -std::shared_ptr -SHAMapAbstractNode::makeTransactionWithMeta( +std::shared_ptr +SHAMapTreeNode::makeTransactionWithMeta( Slice data, - std::uint32_t seq, SHAMapHash const& hash, bool hashValid) { @@ -128,17 +77,15 @@ SHAMapAbstractNode::makeTransactionWithMeta( auto item = std::make_shared(tag, s.peekData()); if (hashValid) - return std::make_shared( - std::move(item), tnTRANSACTION_MD, seq, hash); + return std::make_shared( + std::move(item), 0, hash); - return std::make_shared( - std::move(item), tnTRANSACTION_MD, seq); + return std::make_shared(std::move(item), 0); } -std::shared_ptr -SHAMapAbstractNode::makeAccountState( +std::shared_ptr +SHAMapTreeNode::makeAccountState( Slice data, - std::uint32_t seq, SHAMapHash const& hash, bool hashValid) { @@ -162,74 +109,14 @@ SHAMapAbstractNode::makeAccountState( auto item = std::make_shared(tag, s.peekData()); if (hashValid) - return std::make_shared( - std::move(item), tnACCOUNT_STATE, seq, hash); + return std::make_shared( + std::move(item), 0, hash); - return std::make_shared( - std::move(item), tnACCOUNT_STATE, seq); + return std::make_shared(std::move(item), 0); } -std::shared_ptr -SHAMapInnerNode::makeFullInner( - Slice data, - std::uint32_t seq, - SHAMapHash const& hash, - bool hashValid) -{ - if (data.size() != 512) - Throw("Invalid FI node"); - - auto ret = std::make_shared(seq); - - Serializer s(data.data(), data.size()); - - for (int i = 0; i < 16; ++i) - { - s.getBitString(ret->mHashes[i].as_uint256(), i * 32); - - if (ret->mHashes[i].isNonZero()) - ret->mIsBranch |= (1 << i); - } - - if (hashValid) - ret->mHash = hash; - else - ret->updateHash(); - return ret; -} - -std::shared_ptr -SHAMapInnerNode::makeCompressedInner(Slice data, std::uint32_t seq) -{ - Serializer s(data.data(), data.size()); - - int len = s.getLength(); - - auto ret = std::make_shared(seq); - - for (int i = 0; i < (len / 33); ++i) - { - int pos; - - if (!s.get8(pos, 32 + (i * 33))) - Throw("short CI node"); - - if ((pos < 0) || (pos >= 16)) - Throw("invalid CI node"); - - s.getBitString(ret->mHashes[pos].as_uint256(), i * 33); - - if (ret->mHashes[pos].isNonZero()) - ret->mIsBranch |= (1 << pos); - } - - ret->updateHash(); - - return ret; -} - -std::shared_ptr -SHAMapAbstractNode::makeFromWire(Slice rawNode) +std::shared_ptr +SHAMapTreeNode::makeFromWire(Slice rawNode) { if (rawNode.empty()) return {}; @@ -241,29 +128,27 @@ SHAMapAbstractNode::makeFromWire(Slice rawNode) bool const hashValid = false; SHAMapHash const hash; - std::uint32_t const seq = 0; - if (type == wireTypeTransaction) - return makeTransaction(rawNode, seq, hash, hashValid); + return makeTransaction(rawNode, hash, hashValid); if (type == wireTypeAccountState) - return makeAccountState(rawNode, seq, hash, hashValid); + return makeAccountState(rawNode, hash, hashValid); if (type == wireTypeInner) - return SHAMapInnerNode::makeFullInner(rawNode, seq, hash, hashValid); + return SHAMapInnerNode::makeFullInner(rawNode, hash, hashValid); if (type == wireTypeCompressedInner) - return SHAMapInnerNode::makeCompressedInner(rawNode, seq); + return SHAMapInnerNode::makeCompressedInner(rawNode); if (type == wireTypeTransactionWithMeta) - return makeTransactionWithMeta(rawNode, seq, hash, hashValid); + return makeTransactionWithMeta(rawNode, hash, hashValid); Throw( "wire: Unknown type (" + std::to_string(type) + ")"); } -std::shared_ptr -SHAMapAbstractNode::makeFromPrefix(Slice rawNode, SHAMapHash const& hash) +std::shared_ptr +SHAMapTreeNode::makeFromPrefix(Slice rawNode, SHAMapHash const& hash) { if (rawNode.size() < 4) Throw("prefix: short node"); @@ -279,19 +164,18 @@ SHAMapAbstractNode::makeFromPrefix(Slice rawNode, SHAMapHash const& hash) rawNode.remove_prefix(4); bool const hashValid = true; - std::uint32_t const seq = 0; if (type == HashPrefix::transactionID) - return makeTransaction(rawNode, seq, hash, hashValid); + return makeTransaction(rawNode, hash, hashValid); if (type == HashPrefix::leafNode) - return makeAccountState(rawNode, seq, hash, hashValid); + return makeAccountState(rawNode, hash, hashValid); if (type == HashPrefix::innerNode) - return SHAMapInnerNode::makeFullInner(rawNode, seq, hash, hashValid); + return SHAMapInnerNode::makeFullInner(rawNode, hash, hashValid); if (type == HashPrefix::txNode) - return makeTransactionWithMeta(rawNode, seq, hash, hashValid); + return makeTransactionWithMeta(rawNode, hash, hashValid); Throw( "prefix: unknown type (" + @@ -299,349 +183,10 @@ SHAMapAbstractNode::makeFromPrefix(Slice rawNode, SHAMapHash const& hash) ")"); } -bool -SHAMapInnerNode::updateHash() -{ - uint256 nh; - if (mIsBranch != 0) - { - sha512_half_hasher h; - using beast::hash_append; - hash_append(h, HashPrefix::innerNode); - for (auto const& hh : mHashes) - hash_append(h, hh); - nh = static_cast(h); - } - if (nh == mHash.as_uint256()) - return false; - mHash = SHAMapHash{nh}; - return true; -} - -void -SHAMapInnerNode::updateHashDeep() -{ - for (auto pos = 0; pos < 16; ++pos) - { - if (mChildren[pos] != nullptr) - mHashes[pos] = mChildren[pos]->getNodeHash(); - } - updateHash(); -} - -bool -SHAMapTreeNode::updateHash() -{ - uint256 nh; - if (mType == tnTRANSACTION_NM) - { - nh = - sha512Half(HashPrefix::transactionID, makeSlice(mItem->peekData())); - } - else if (mType == tnACCOUNT_STATE) - { - nh = sha512Half( - HashPrefix::leafNode, makeSlice(mItem->peekData()), mItem->key()); - } - else if (mType == tnTRANSACTION_MD) - { - nh = sha512Half( - HashPrefix::txNode, makeSlice(mItem->peekData()), mItem->key()); - } - else - assert(false); - - if (nh == mHash.as_uint256()) - return false; - - mHash = SHAMapHash{nh}; - return true; -} - -void -SHAMapInnerNode::serializeForWire(Serializer& s) const -{ - assert(mType == tnINNER); - assert(!isEmpty()); - - // If the node is sparse, then only send non-empty branches: - if (getBranchCount() < 12) - { - // compressed node - for (int i = 0; i < mHashes.size(); ++i) - { - if (!isEmptyBranch(i)) - { - s.addBitString(mHashes[i].as_uint256()); - s.add8(i); - } - } - - s.add8(wireTypeCompressedInner); - } - else - { - for (auto const& hh : mHashes) - s.addBitString(hh.as_uint256()); - - s.add8(wireTypeInner); - } -} - -void -SHAMapInnerNode::serializeWithPrefix(Serializer& s) const -{ - assert(mType == tnINNER); - assert(!isEmpty()); - - s.add32(HashPrefix::innerNode); - for (auto const& hh : mHashes) - s.addBitString(hh.as_uint256()); -} - -void -SHAMapTreeNode::serializeForWire(Serializer& s) const -{ - if (mType == tnACCOUNT_STATE) - { - s.addRaw(mItem->peekData()); - s.addBitString(mItem->key()); - s.add8(wireTypeAccountState); - } - else if (mType == tnTRANSACTION_NM) - { - s.addRaw(mItem->peekData()); - s.add8(wireTypeTransaction); - } - else if (mType == tnTRANSACTION_MD) - { - s.addRaw(mItem->peekData()); - s.addBitString(mItem->key()); - s.add8(wireTypeTransactionWithMeta); - } -} - -void -SHAMapTreeNode::serializeWithPrefix(Serializer& s) const -{ - if (mType == tnACCOUNT_STATE) - { - s.add32(HashPrefix::leafNode); - s.addRaw(mItem->peekData()); - s.addBitString(mItem->key()); - } - else if (mType == tnTRANSACTION_NM) - { - s.add32(HashPrefix::transactionID); - s.addRaw(mItem->peekData()); - } - else if (mType == tnTRANSACTION_MD) - { - s.add32(HashPrefix::txNode); - s.addRaw(mItem->peekData()); - s.addBitString(mItem->key()); - } -} - -bool -SHAMapTreeNode::setItem(std::shared_ptr i, TNType type) -{ - mType = type; - mItem = std::move(i); - assert(isLeaf()); - assert(mSeq != 0); - return updateHash(); -} - -bool -SHAMapInnerNode::isEmpty() const -{ - return mIsBranch == 0; -} - -int -SHAMapInnerNode::getBranchCount() const -{ - assert(isInner()); - int count = 0; - - for (int i = 0; i < 16; ++i) - if (!isEmptyBranch(i)) - ++count; - - return count; -} - std::string -SHAMapAbstractNode::getString(const SHAMapNodeID& id) const +SHAMapTreeNode::getString(const SHAMapNodeID& id) const { return to_string(id); } -std::string -SHAMapInnerNode::getString(const SHAMapNodeID& id) const -{ - std::string ret = SHAMapAbstractNode::getString(id); - for (int i = 0; i < mHashes.size(); ++i) - { - if (!isEmptyBranch(i)) - { - ret += "\n"; - ret += std::to_string(i); - ret += " = "; - ret += to_string(mHashes[i]); - } - } - return ret; -} - -std::string -SHAMapTreeNode::getString(const SHAMapNodeID& id) const -{ - std::string ret = SHAMapAbstractNode::getString(id); - 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()->key()); - ret += "\n Hash="; - ret += to_string(mHash); - ret += "/"; - ret += std::to_string(mItem->size()); - return ret; -} - -// We are modifying an inner node -void -SHAMapInnerNode::setChild( - int m, - std::shared_ptr 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 -SHAMapInnerNode::shareChild( - int m, - std::shared_ptr const& child) -{ - assert((m >= 0) && (m < 16)); - assert(mType == tnINNER); - assert(mSeq != 0); - assert(child); - assert(child.get() != this); - - mChildren[m] = child; -} - -SHAMapAbstractNode* -SHAMapInnerNode::getChildPointer(int branch) -{ - assert(branch >= 0 && branch < 16); - assert(isInner()); - - std::lock_guard lock(childLock); - return mChildren[branch].get(); -} - -std::shared_ptr -SHAMapInnerNode::getChild(int branch) -{ - assert(branch >= 0 && branch < 16); - assert(isInner()); - - std::lock_guard lock(childLock); - return mChildren[branch]; -} - -std::shared_ptr -SHAMapInnerNode::canonicalizeChild( - int branch, - std::shared_ptr node) -{ - assert(branch >= 0 && branch < 16); - assert(isInner()); - assert(node); - assert(node->getNodeHash() == mHashes[branch]); - - std::lock_guard 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; - } - return node; -} - -uint256 const& -SHAMapInnerNode::key() const -{ - Throw("SHAMapInnerNode::key() should never be called"); - static uint256 x; - return x; -} - -uint256 const& -SHAMapTreeNode::key() const -{ - return mItem->key(); -} - -void -SHAMapInnerNode::invariants(bool is_root) const -{ - 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(); - ++count; - } - else - { - assert((mIsBranch & (1 << i)) == 0); - } - } - if (!is_root) - { - assert(mHash.isNonZero()); - assert(count >= 1); - } - assert((count == 0) ? mHash.isZero() : mHash.isNonZero()); -} - -void -SHAMapTreeNode::invariants(bool) const -{ - assert(mType >= tnTRANSACTION_NM); - assert(mHash.isNonZero()); - assert(mItem != nullptr); -} - } // namespace ripple diff --git a/src/test/app/LedgerHistory_test.cpp b/src/test/app/LedgerHistory_test.cpp index 513905a6b..ba4faa9da 100644 --- a/src/test/app/LedgerHistory_test.cpp +++ b/src/test/app/LedgerHistory_test.cpp @@ -72,8 +72,8 @@ public: res->updateSkipList(); { - res->stateMap().flushDirty(hotACCOUNT_NODE, res->info().seq); - res->txMap().flushDirty(hotTRANSACTION_NODE, res->info().seq); + res->stateMap().flushDirty(hotACCOUNT_NODE); + res->txMap().flushDirty(hotTRANSACTION_NODE); } res->unshare(); diff --git a/src/test/nodestore/DatabaseShard_test.cpp b/src/test/nodestore/DatabaseShard_test.cpp index 034db7c7a..2dcc4c67a 100644 --- a/src/test/nodestore/DatabaseShard_test.cpp +++ b/src/test/nodestore/DatabaseShard_test.cpp @@ -283,15 +283,14 @@ class DatabaseShard_test : public TestBase } // Store the state map - auto visitAcc = [&](SHAMapAbstractNode& node) { + auto visitAcc = [&](SHAMapTreeNode const& node) { Serializer s; node.serializeWithPrefix(s); db.store( - node.getType() == SHAMapAbstractNode::TNType::tnINNER - ? hotUNKNOWN - : hotACCOUNT_NODE, + node.getType() == SHAMapNodeType::tnINNER ? hotUNKNOWN + : hotACCOUNT_NODE, std::move(s.modData()), - node.getNodeHash().as_uint256(), + node.getHash().as_uint256(), ledger.info().seq); return true; }; @@ -311,15 +310,14 @@ class DatabaseShard_test : public TestBase } // Store the transaction map - auto visitTx = [&](SHAMapAbstractNode& node) { + auto visitTx = [&](SHAMapTreeNode& node) { Serializer s; node.serializeWithPrefix(s); db.store( - node.getType() == SHAMapAbstractNode::TNType::tnINNER - ? hotUNKNOWN - : hotTRANSACTION_NODE, + node.getType() == SHAMapNodeType::tnINNER ? hotUNKNOWN + : hotTRANSACTION_NODE, std::move(s.modData()), - node.getNodeHash().as_uint256(), + node.getHash().as_uint256(), ledger.info().seq); return true; }; @@ -357,20 +355,19 @@ class DatabaseShard_test : public TestBase LedgerFill{*fetched, LedgerFill::full | LedgerFill::binary})); // walk shamap and validate each node - auto fcompAcc = [&](SHAMapAbstractNode& node) -> bool { + auto fcompAcc = [&](SHAMapTreeNode& node) -> bool { Serializer s; node.serializeWithPrefix(s); auto nSrc{NodeObject::createObject( - node.getType() == SHAMapAbstractNode::TNType::tnINNER - ? hotUNKNOWN - : hotACCOUNT_NODE, + node.getType() == SHAMapNodeType::tnINNER ? hotUNKNOWN + : hotACCOUNT_NODE, std::move(s.modData()), - node.getNodeHash().as_uint256())}; + node.getHash().as_uint256())}; if (!BEAST_EXPECT(nSrc)) return false; auto nDst = db.fetchNodeObject( - node.getNodeHash().as_uint256(), ledger.info().seq); + node.getHash().as_uint256(), ledger.info().seq); if (!BEAST_EXPECT(nDst)) return false; @@ -381,20 +378,19 @@ class DatabaseShard_test : public TestBase if (ledger.stateMap().getHash().isNonZero()) ledger.stateMap().snapShot(false)->visitNodes(fcompAcc); - auto fcompTx = [&](SHAMapAbstractNode& node) -> bool { + auto fcompTx = [&](SHAMapTreeNode& node) -> bool { Serializer s; node.serializeWithPrefix(s); auto nSrc{NodeObject::createObject( - node.getType() == SHAMapAbstractNode::TNType::tnINNER - ? hotUNKNOWN - : hotTRANSACTION_NODE, + node.getType() == SHAMapNodeType::tnINNER ? hotUNKNOWN + : hotTRANSACTION_NODE, std::move(s.modData()), - node.getNodeHash().as_uint256())}; + node.getHash().as_uint256())}; if (!BEAST_EXPECT(nSrc)) return false; auto nDst = db.fetchNodeObject( - node.getNodeHash().as_uint256(), ledger.info().seq); + node.getHash().as_uint256(), ledger.info().seq); if (!BEAST_EXPECT(nDst)) return false; diff --git a/src/test/shamap/FetchPack_test.cpp b/src/test/shamap/FetchPack_test.cpp index f420b814e..ce58ba05e 100644 --- a/src/test/shamap/FetchPack_test.cpp +++ b/src/test/shamap/FetchPack_test.cpp @@ -65,7 +65,7 @@ public: SHAMapHash const& nodeHash, std::uint32_t ledgerSeq, Blob&& nodeData, - SHAMapTreeNode::TNType type) const override + SHAMapNodeType type) const override { } @@ -100,7 +100,8 @@ public: while (n--) { std::shared_ptr item(make_random_item(r)); - auto const result(t.addItem(std::move(*item), false, false)); + auto const result( + t.addItem(SHAMapNodeType::tnACCOUNT_STATE, std::move(*item))); assert(result); (void)result; } diff --git a/src/test/shamap/SHAMapSync_test.cpp b/src/test/shamap/SHAMapSync_test.cpp index 61b4eaae1..a5ca73538 100644 --- a/src/test/shamap/SHAMapSync_test.cpp +++ b/src/test/shamap/SHAMapSync_test.cpp @@ -59,7 +59,7 @@ public: std::shared_ptr item = makeRandomAS(); items.push_back(item->key()); - if (!map.addItem(std::move(*item), false, false)) + if (!map.addItem(SHAMapNodeType::tnACCOUNT_STATE, std::move(*item))) { log << "Unable to add item to map\n"; return false; @@ -98,7 +98,8 @@ public: int items = 10000; for (int i = 0; i < items; ++i) { - source.addItem(std::move(*makeRandomAS()), false, false); + source.addItem( + SHAMapNodeType::tnACCOUNT_STATE, std::move(*makeRandomAS())); if (i % 100 == 0) source.invariants(); } diff --git a/src/test/shamap/SHAMap_test.cpp b/src/test/shamap/SHAMap_test.cpp index 6b1d5c6d6..98f61abec 100644 --- a/src/test/shamap/SHAMap_test.cpp +++ b/src/test/shamap/SHAMap_test.cpp @@ -64,12 +64,12 @@ static_assert(std::is_copy_assignable{}, ""); static_assert(std::is_move_constructible{}, ""); static_assert(std::is_move_assignable{}, ""); -static_assert(!std::is_nothrow_destructible{}, ""); -static_assert(!std::is_default_constructible{}, ""); -static_assert(!std::is_copy_constructible{}, ""); -static_assert(!std::is_copy_assignable{}, ""); -static_assert(!std::is_move_constructible{}, ""); -static_assert(!std::is_move_assignable{}, ""); +static_assert(std::is_nothrow_destructible{}, ""); +static_assert(!std::is_default_constructible{}, ""); +static_assert(!std::is_copy_constructible{}, ""); +static_assert(!std::is_copy_assignable{}, ""); +static_assert(!std::is_move_constructible{}, ""); +static_assert(!std::is_move_assignable{}, ""); static_assert(std::is_nothrow_destructible{}, ""); static_assert(!std::is_default_constructible{}, ""); @@ -78,12 +78,12 @@ static_assert(!std::is_copy_assignable{}, ""); static_assert(!std::is_move_constructible{}, ""); static_assert(!std::is_move_assignable{}, ""); -static_assert(std::is_nothrow_destructible{}, ""); -static_assert(!std::is_default_constructible{}, ""); -static_assert(!std::is_copy_constructible{}, ""); -static_assert(!std::is_copy_assignable{}, ""); -static_assert(!std::is_move_constructible{}, ""); -static_assert(!std::is_move_assignable{}, ""); +static_assert(std::is_nothrow_destructible{}, ""); +static_assert(!std::is_default_constructible{}, ""); +static_assert(!std::is_copy_constructible{}, ""); +static_assert(!std::is_copy_assignable{}, ""); +static_assert(!std::is_move_constructible{}, ""); +static_assert(!std::is_move_assignable{}, ""); #endif inline bool @@ -161,9 +161,13 @@ public: 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"); + unexpected( + !sMap.addItem(SHAMapNodeType::tnTRANSACTION_NM, SHAMapItem{i2}), + "no add"); sMap.invariants(); - unexpected(!sMap.addItem(SHAMapItem{i1}, true, false), "no add"); + unexpected( + !sMap.addItem(SHAMapNodeType::tnTRANSACTION_NM, SHAMapItem{i1}), + "no add"); sMap.invariants(); auto i = sMap.begin(); @@ -173,11 +177,11 @@ public: unexpected(i == e || (*i != i2), "bad traverse"); ++i; unexpected(i != e, "bad traverse"); - sMap.addItem(SHAMapItem{i4}, true, false); + sMap.addItem(SHAMapNodeType::tnTRANSACTION_NM, SHAMapItem{i4}); sMap.invariants(); sMap.delItem(i2.key()); sMap.invariants(); - sMap.addItem(SHAMapItem{i3}, true, false); + sMap.addItem(SHAMapNodeType::tnTRANSACTION_NM, SHAMapItem{i3}); sMap.invariants(); i = sMap.begin(); e = sMap.end(); @@ -282,7 +286,8 @@ public: for (int k = 0; k < keys.size(); ++k) { SHAMapItem item(keys[k], IntToVUC(k)); - BEAST_EXPECT(map.addItem(std::move(item), true, false)); + BEAST_EXPECT(map.addItem( + SHAMapNodeType::tnTRANSACTION_NM, std::move(item))); BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]); map.invariants(); } @@ -333,7 +338,9 @@ public: map.setUnbacked(); for (auto const& k : keys) { - map.addItem(SHAMapItem{k, IntToVUC(0)}, true, false); + map.addItem( + SHAMapNodeType::tnTRANSACTION_NM, + SHAMapItem{k, IntToVUC(0)}); map.invariants(); }