From d468deee127d6255f754d1d4c643e44133357364 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sat, 13 Jun 2015 20:33:47 -0700 Subject: [PATCH] Refactor Ledger and LedgerEntrySet: Member functions and free functions on Ledger and LedgerEntrySet are rewritten in terms of new abstract interfaces `BasicView` and `View`, representing the set of non-decomposable primitives necessary to read and write state map items in a ledger, and to overlay a discardable view onto a Ledger that can calculate metadata during transaction processing. const-correctness is enforced through the parameter and return types. The MetaView now supports multi-level stacking: A MetaView can be stacked on top of either a Ledger or another MetaView, up to any number of levels. The getSLEi member function is removed. The CachedView wrapper replaces it, wrapping a View such that any function called with a CachedView will go through the SLECache. * Add BasicView, View, CachedView * Rename LedgerEntrySet to MetaView * Factor out free functions * Consolidate free functions in ViewAPI * Remove unused class members and free functions --- Builds/VisualStudio2013/RippleD.vcxproj | 80 +- .../VisualStudio2013/RippleD.vcxproj.filters | 90 +- SConstruct | 3 + src/ripple/app/ledger/AcceptedLedger.cpp | 2 +- src/ripple/app/ledger/AcceptedLedgerTx.cpp | 12 +- src/ripple/app/ledger/DeferredCredits.h | 59 - .../app/ledger/DirectoryEntryIterator.cpp | 121 - .../app/ledger/DirectoryEntryIterator.h | 99 - src/ripple/app/ledger/Ledger.cpp | 557 ++--- src/ripple/app/ledger/Ledger.h | 265 ++- src/ripple/app/ledger/LedgerConsensus.h | 2 +- src/ripple/app/ledger/LedgerEntrySet.cpp | 2119 ----------------- src/ripple/app/ledger/LedgerEntrySet.h | 370 --- src/ripple/app/ledger/LedgerHistory.cpp | 12 +- src/ripple/app/ledger/LedgerMaster.h | 2 +- src/ripple/app/ledger/LedgerToJson.h | 16 +- src/ripple/app/ledger/MetaView.h | 272 +++ src/ripple/app/ledger/OrderBookIterator.cpp | 237 -- src/ripple/app/ledger/OrderBookIterator.h | 240 -- src/ripple/app/ledger/SLECache.h | 37 - src/ripple/app/ledger/impl/InboundLedger.cpp | 36 +- .../app/ledger/impl/LedgerConsensus.cpp | 27 +- src/ripple/app/ledger/impl/LedgerMaster.cpp | 2 +- src/ripple/app/ledger/impl/MetaView.cpp | 833 +++++++ .../app/ledger/tests/DeferredCredits.test.cpp | 81 +- src/ripple/app/ledger/tests/MetaView_test.cpp | 343 +++ src/ripple/app/ledger/tests/common_ledger.cpp | 21 +- src/ripple/app/main/Application.cpp | 13 +- src/ripple/app/main/Application.h | 2 +- src/ripple/app/misc/NetworkOPs.cpp | 57 +- src/ripple/app/misc/SHAMapStoreImp.cpp | 2 +- src/ripple/app/paths/Credit.cpp | 17 +- src/ripple/app/paths/Credit.h | 6 +- src/ripple/app/paths/NodeDirectory.h | 17 +- src/ripple/app/paths/PathRequest.cpp | 8 +- src/ripple/app/paths/PathState.cpp | 38 +- src/ripple/app/paths/PathState.h | 17 +- src/ripple/app/paths/Pathfinder.cpp | 22 +- src/ripple/app/paths/RippleCalc.cpp | 42 +- src/ripple/app/paths/RippleCalc.h | 12 +- src/ripple/app/paths/RippleState.cpp | 6 +- src/ripple/app/paths/RippleState.h | 2 +- src/ripple/app/paths/cursor/AdvanceNode.cpp | 21 +- .../app/paths/cursor/DeliverNodeForward.cpp | 9 +- .../app/paths/cursor/DeliverNodeReverse.cpp | 5 +- .../cursor/ForwardLiquidityForAccount.cpp | 16 +- src/ripple/app/paths/cursor/Liquidity.cpp | 6 +- src/ripple/app/paths/cursor/NextIncrement.cpp | 2 +- src/ripple/app/paths/cursor/PathCursor.h | 10 +- .../app/paths/cursor/ReverseLiquidity.cpp | 1 + .../cursor/ReverseLiquidityForAccount.cpp | 3 +- .../app/paths/cursor/RippleLiquidity.cpp | 14 +- src/ripple/app/paths/cursor/RippleLiquidity.h | 7 +- src/ripple/app/tests/Regression_test.cpp | 49 + src/ripple/app/tx/TransactionEngine.h | 14 +- src/ripple/app/tx/impl/BookTip.cpp | 28 +- src/ripple/app/tx/impl/BookTip.h | 14 +- src/ripple/app/tx/impl/CancelOffer.cpp | 7 +- src/ripple/app/tx/impl/CancelTicket.cpp | 13 +- src/ripple/app/tx/impl/Change.cpp | 23 +- src/ripple/app/tx/impl/CreateOffer.cpp | 128 +- src/ripple/app/tx/impl/CreateTicket.cpp | 10 +- src/ripple/app/tx/impl/LocalTxs.cpp | 4 +- src/ripple/app/tx/impl/Offer.h | 9 +- src/ripple/app/tx/impl/OfferStream.cpp | 34 +- src/ripple/app/tx/impl/OfferStream.h | 20 +- src/ripple/app/tx/impl/Payment.cpp | 15 +- src/ripple/app/tx/impl/PaymentView.h | 147 ++ src/ripple/app/tx/impl/SetAccount.cpp | 6 +- src/ripple/app/tx/impl/SetSignerList.cpp | 17 +- src/ripple/app/tx/impl/SetTrust.cpp | 52 +- src/ripple/app/tx/impl/Taker.cpp | 55 +- src/ripple/app/tx/impl/Taker.h | 19 +- src/ripple/app/tx/impl/TransactionEngine.cpp | 11 +- src/ripple/app/tx/impl/Transactor.cpp | 15 +- src/ripple/app/tx/tests/common_transactor.cpp | 9 +- src/ripple/ledger/CachedView.h | 96 + src/ripple/ledger/DeferredCredits.h | 66 + src/ripple/ledger/SLECache.h | 37 + src/ripple/ledger/View.h | 302 +++ src/ripple/ledger/ViewAPI.h | 271 +++ src/ripple/ledger/ViewAPIBasics.h | 33 + src/ripple/ledger/impl/CachedView.cpp | 53 + .../impl}/DeferredCredits.cpp | 43 +- src/ripple/ledger/impl/ViewAPI.cpp | 1504 ++++++++++++ src/ripple/overlay/impl/PeerImp.cpp | 55 +- src/ripple/protocol/Indexes.h | 120 +- src/ripple/protocol/JsonFields.h | 11 +- src/ripple/protocol/Keylet.h | 57 + src/ripple/protocol/LedgerFormats.h | 14 + src/ripple/protocol/Serializer.h | 4 +- src/ripple/protocol/impl/Indexes.cpp | 72 +- src/ripple/protocol/impl/Keylet.cpp | 42 + src/ripple/rpc/handlers/AccountInfo.cpp | 2 +- src/ripple/rpc/handlers/AccountLines.cpp | 44 +- src/ripple/rpc/handlers/AccountObjects.cpp | 2 +- src/ripple/rpc/handlers/AccountOffers.cpp | 36 +- src/ripple/rpc/handlers/GatewayBalances.cpp | 78 +- src/ripple/rpc/handlers/LedgerData.cpp | 2 +- src/ripple/rpc/handlers/LedgerEntry.cpp | 2 +- src/ripple/rpc/handlers/NoRippleCheck.cpp | 8 +- src/ripple/rpc/handlers/RipplePathFind.cpp | 8 +- src/ripple/rpc/handlers/TransactionEntry.cpp | 3 +- src/ripple/rpc/handlers/Tx.cpp | 4 +- src/ripple/rpc/impl/GetAccountObjects.cpp | 6 +- src/ripple/rpc/impl/TransactionSign.cpp | 2 +- src/ripple/test/jtx/Env.h | 3 +- src/ripple/test/jtx/impl/Env.cpp | 9 +- src/ripple/test/jtx/impl/balance.cpp | 5 +- src/ripple/test/jtx/impl/owners.cpp | 4 +- src/ripple/test/jtx/impl/utility.cpp | 8 +- src/ripple/unity/app_ledger.cpp | 6 +- src/ripple/unity/app_tests.cpp | 22 + src/ripple/unity/ledger.cpp | 24 + src/ripple/unity/protocol.cpp | 1 + 115 files changed, 5530 insertions(+), 4521 deletions(-) delete mode 100644 src/ripple/app/ledger/DeferredCredits.h delete mode 100644 src/ripple/app/ledger/DirectoryEntryIterator.cpp delete mode 100644 src/ripple/app/ledger/DirectoryEntryIterator.h delete mode 100644 src/ripple/app/ledger/LedgerEntrySet.cpp delete mode 100644 src/ripple/app/ledger/LedgerEntrySet.h create mode 100644 src/ripple/app/ledger/MetaView.h delete mode 100644 src/ripple/app/ledger/OrderBookIterator.cpp delete mode 100644 src/ripple/app/ledger/OrderBookIterator.h delete mode 100644 src/ripple/app/ledger/SLECache.h create mode 100644 src/ripple/app/ledger/impl/MetaView.cpp create mode 100644 src/ripple/app/ledger/tests/MetaView_test.cpp create mode 100644 src/ripple/app/tests/Regression_test.cpp create mode 100644 src/ripple/app/tx/impl/PaymentView.h create mode 100644 src/ripple/ledger/CachedView.h create mode 100644 src/ripple/ledger/DeferredCredits.h create mode 100644 src/ripple/ledger/SLECache.h create mode 100644 src/ripple/ledger/View.h create mode 100644 src/ripple/ledger/ViewAPI.h create mode 100644 src/ripple/ledger/ViewAPIBasics.h create mode 100644 src/ripple/ledger/impl/CachedView.cpp rename src/ripple/{app/ledger => ledger/impl}/DeferredCredits.cpp (72%) create mode 100644 src/ripple/ledger/impl/ViewAPI.cpp create mode 100644 src/ripple/protocol/Keylet.h create mode 100644 src/ripple/protocol/impl/Keylet.cpp create mode 100644 src/ripple/unity/app_tests.cpp create mode 100644 src/ripple/unity/ledger.cpp diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj index 3ca3c12677..81e142e47f 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj +++ b/Builds/VisualStudio2013/RippleD.vcxproj @@ -1355,18 +1355,6 @@ - - True - True - - - - - True - True - - - True True @@ -1403,6 +1391,10 @@ True True + + True + True + @@ -1415,12 +1407,6 @@ - - True - True - - - @@ -1443,22 +1429,16 @@ + + True True - - True - True - - - - - True True @@ -1473,6 +1453,10 @@ True True + + True + True + True True @@ -1719,6 +1703,10 @@ + + True + True + True True @@ -1765,6 +1753,8 @@ True True + + True True @@ -2257,6 +2247,30 @@ + + + + + + True + True + + + True + True + + + True + True + + + + + + + + + @@ -2651,6 +2665,10 @@ True True + + True + True + True True @@ -2757,6 +2775,8 @@ + + @@ -3509,6 +3529,10 @@ True True + + True + True + True True @@ -3543,6 +3567,10 @@ True True + + True + True + diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters index ef5c986d5e..d396f798a3 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters @@ -274,6 +274,9 @@ {1025719B-6A8F-D9FB-A6BA-02B93756DE09} + + {2E791662-6ED0-D1E1-03A4-0CB35473EC56} + {50FDCDC1-EC9C-9F3B-34C9-EF4137E132B4} @@ -319,6 +322,12 @@ {BA646284-836B-B151-F2AA-D18535D6F3C1} + + {33BBF793-1734-8439-B367-C4A48AB37EFC} + + + {EA35E0D0-6876-9DC8-10FA-1E6A0486C574} + {6649BD29-BE86-723F-501A-045E39310112} @@ -2079,18 +2088,6 @@ ripple\app\ledger - - ripple\app\ledger - - - ripple\app\ledger - - - ripple\app\ledger - - - ripple\app\ledger - ripple\app\ledger\impl @@ -2121,6 +2118,9 @@ ripple\app\ledger\impl + + ripple\app\ledger\impl + ripple\app\ledger @@ -2136,12 +2136,6 @@ ripple\app\ledger - - ripple\app\ledger - - - ripple\app\ledger - ripple\app\ledger @@ -2169,24 +2163,18 @@ ripple\app\ledger + + ripple\app\ledger + ripple\app\ledger ripple\app\ledger - - ripple\app\ledger - - - ripple\app\ledger - ripple\app\ledger - - ripple\app\ledger - ripple\app\ledger\tests @@ -2199,6 +2187,9 @@ ripple\app\ledger\tests + + ripple\app\ledger\tests + ripple\app\ledger @@ -2439,6 +2430,9 @@ ripple\app\paths + + ripple\app\tests + ripple\app\tx\impl @@ -2478,6 +2472,9 @@ ripple\app\tx\impl + + ripple\app\tx\impl + ripple\app\tx\impl @@ -2952,6 +2949,33 @@ ripple\json + + ripple\ledger + + + ripple\ledger + + + ripple\ledger\impl + + + ripple\ledger\impl + + + ripple\ledger\impl + + + ripple\ledger + + + ripple\ledger + + + ripple\ledger + + + ripple\ledger + ripple\net @@ -3333,6 +3357,9 @@ ripple\protocol\impl + + ripple\protocol\impl + ripple\protocol\impl @@ -3420,6 +3447,9 @@ ripple\protocol + + ripple\protocol + ripple\protocol @@ -4242,6 +4272,9 @@ ripple\unity + + ripple\unity + ripple\unity @@ -4272,6 +4305,9 @@ ripple\unity + + ripple\unity + ripple\unity diff --git a/SConstruct b/SConstruct index cb36bec7c7..1409f4513b 100644 --- a/SConstruct +++ b/SConstruct @@ -658,6 +658,7 @@ def get_classic_sources(): append_sources(result, *list_sources('src/ripple/basics', '.cpp')) append_sources(result, *list_sources('src/ripple/crypto', '.cpp')) append_sources(result, *list_sources('src/ripple/json', '.cpp')) + append_sources(result, *list_sources('src/ripple/ledger', '.cpp')) append_sources(result, *list_sources('src/ripple/legacy', '.cpp')) append_sources(result, *list_sources('src/ripple/net', '.cpp')) append_sources(result, *list_sources('src/ripple/overlay', '.cpp')) @@ -686,10 +687,12 @@ def get_unity_sources(): 'src/ripple/unity/app_main.cpp', 'src/ripple/unity/app_misc.cpp', 'src/ripple/unity/app_paths.cpp', + 'src/ripple/unity/app_tests.cpp', 'src/ripple/unity/app_tx.cpp', 'src/ripple/unity/core.cpp', 'src/ripple/unity/basics.cpp', 'src/ripple/unity/crypto.cpp', + 'src/ripple/unity/ledger.cpp', 'src/ripple/unity/net.cpp', 'src/ripple/unity/overlay.cpp', 'src/ripple/unity/peerfinder.cpp', diff --git a/src/ripple/app/ledger/AcceptedLedger.cpp b/src/ripple/app/ledger/AcceptedLedger.cpp index dda8edc995..9c6027eaac 100644 --- a/src/ripple/app/ledger/AcceptedLedger.cpp +++ b/src/ripple/app/ledger/AcceptedLedger.cpp @@ -33,7 +33,7 @@ TaggedCache AcceptedLedger::s_cache ( AcceptedLedger::AcceptedLedger (Ledger::ref ledger) : mLedger (ledger) { - for (auto const& item : *ledger->peekTransactionMap()) + for (auto const& item : ledger->txMap()) { SerialIter sit (item->slice()); insert (std::make_shared( diff --git a/src/ripple/app/ledger/AcceptedLedgerTx.cpp b/src/ripple/app/ledger/AcceptedLedgerTx.cpp index 43916a7214..373120cc1a 100644 --- a/src/ripple/app/ledger/AcceptedLedgerTx.cpp +++ b/src/ripple/app/ledger/AcceptedLedgerTx.cpp @@ -18,8 +18,10 @@ //============================================================================== #include +#include #include -#include +#include +#include #include #include @@ -98,10 +100,10 @@ void AcceptedLedgerTx::buildJson () // If the offer create is not self funded then add the owner balance if (account != amount.issue ().account) { - LedgerEntrySet les (mLedger, tapNONE, true); - auto const ownerFunds (funds( - les, account, amount, fhIGNORE_FREEZE)); - + CachedView const view( + *mLedger, getApp().getSLECache()); + auto const ownerFunds = accountFunds(view, + account, amount, fhIGNORE_FREEZE, getConfig()); mJson[jss::transaction][jss::owner_funds] = ownerFunds.getText (); } } diff --git a/src/ripple/app/ledger/DeferredCredits.h b/src/ripple/app/ledger/DeferredCredits.h deleted file mode 100644 index 8c991043d3..0000000000 --- a/src/ripple/app/ledger/DeferredCredits.h +++ /dev/null @@ -1,59 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012-2015 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_APP_LEDGER_CACHEDCREDITS_H_INCLUDED -#define RIPPLE_APP_LEDGER_CACHEDCREDITS_H_INCLUDED - -#include -#include -#include -#include - -namespace ripple { -class DeferredCredits -{ -private: - // lowAccount, highAccount - using Key = std::tuple; - // lowAccountCredits, highAccountCredits - using Value = std::tuple; - static inline - Key makeKey(AccountID const& a1, AccountID const& a2, Currency const& c) - { - if (a1 < a2) - return std::make_tuple(a1, a2, c); - else - return std::make_tuple(a2, a1, c); - } - - std::map map_; - -public: - void credit (AccountID const& sender, - AccountID const& receiver, - STAmount const& amount); - // Get the adjusted balance of main for the - // balance between main and other. - STAmount adjustedBalance (AccountID const& main, - AccountID const& other, - STAmount const& curBalance) const; - void clear (); -}; -} -#endif diff --git a/src/ripple/app/ledger/DirectoryEntryIterator.cpp b/src/ripple/app/ledger/DirectoryEntryIterator.cpp deleted file mode 100644 index 5019ecc762..0000000000 --- a/src/ripple/app/ledger/DirectoryEntryIterator.cpp +++ /dev/null @@ -1,121 +0,0 @@ -//------------------------------------------------------------------------------ -/* - 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 - -namespace ripple { - -/** Get the current ledger entry */ -SLE::pointer DirectoryEntryIterator::getEntry (LedgerEntrySet& les, LedgerEntryType type) -{ - return les.entryCache (type, mEntryIndex); -} - -/** Position the iterator at the first entry -*/ -bool DirectoryEntryIterator::firstEntry (LedgerEntrySet& les) -{ - WriteLog (lsTRACE, Ledger) << "DirectoryEntryIterator::firstEntry(" << - to_string (mRootIndex) << ")"; - mEntry = 0; - mDirNode.reset (); - - return nextEntry (les); -} - -/** Advance the iterator to the next entry -*/ -bool DirectoryEntryIterator::nextEntry (LedgerEntrySet& les) -{ - if (!mDirNode) - { - WriteLog (lsTRACE, Ledger) << "DirectoryEntryIterator::nextEntry(" << - to_string (mRootIndex) << ") need dir node"; - // Are we already at the end - if (mDirIndex.isZero()) - { - WriteLog (lsTRACE, Ledger) << "DirectoryEntryIterator::nextEntry(" << - to_string (mRootIndex) << ") at end"; - return false; - } - - // Fetch the current directory - mDirNode = les.entryCache (ltDIR_NODE, mRootIndex); - if (!mDirNode) - { - WriteLog (lsTRACE, Ledger) << "DirectoryEntryIterator::nextEntry(" - << to_string (mRootIndex) << ") no dir node"; - mEntryIndex.zero(); - return false; - } - } - - if (!les.dirNext (mRootIndex, mDirNode, mEntry, mEntryIndex)) - { - mDirIndex.zero(); - mDirNode.reset(); - WriteLog (lsTRACE, Ledger) << "DirectoryEntryIterator::nextEntry(" << - to_string (mRootIndex) << ") now at end"; - return false; - } - - WriteLog (lsTRACE, Ledger) << "DirectoryEntryIterator::nextEntry(" << - to_string (mRootIndex) << ") now at " << mEntry; - return true; -} - -bool DirectoryEntryIterator::addJson (Json::Value& j) const -{ - if (mDirNode && (mEntry != 0)) - { - j[jss::dir_root] = to_string (mRootIndex); - j[jss::dir_entry] = static_cast (mEntry); - - if (mDirNode) - j[jss::dir_index] = to_string (mDirIndex); - - return true; - } - return false; -} - -bool DirectoryEntryIterator::setJson (Json::Value const& j, LedgerEntrySet& les) -{ - if (!j.isMember(jss::dir_root) || !j.isMember(jss::dir_index) || !j.isMember(jss::dir_entry)) - return false; -#if 0 // WRITEME - Json::Value const& dirRoot = j[jss::dir_root]; - Json::Value const& dirIndex = j[jss::dir_index]; - Json::Value const& dirEntry = j[jss::dir_entry]; - - assert(false); // CAUTION: This function is incomplete - - mEntry = j[jss::dir_entry].asUInt (); - - if (!mDirIndex.SetHex(j[jss::dir_index].asString())) - return false; -#endif - - return true; -} - -} // ripple diff --git a/src/ripple/app/ledger/DirectoryEntryIterator.h b/src/ripple/app/ledger/DirectoryEntryIterator.h deleted file mode 100644 index b2a534c589..0000000000 --- a/src/ripple/app/ledger/DirectoryEntryIterator.h +++ /dev/null @@ -1,99 +0,0 @@ -//------------------------------------------------------------------------------ -/* - 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_APP_LEDGER_DIRECTORYENTRYITERATOR_H_INCLUDED -#define RIPPLE_APP_LEDGER_DIRECTORYENTRYITERATOR_H_INCLUDED - -#include -#include -#include - -namespace ripple { - -/** An iterator that walks the ledger entries in a single directory */ -class DirectoryEntryIterator -{ - -public: - DirectoryEntryIterator () - : mEntry(0) - { - } - - DirectoryEntryIterator (uint256 const& index) - : mRootIndex(index), mEntry(0) - { - } - - /** Construct from a reference to the root directory - */ - DirectoryEntryIterator (SLE::ref directory) : mEntry (0), mDirNode (directory) - { - if (mDirNode) - mRootIndex = mDirNode->getIndex(); - } - - /** Get the SLE this iterator currently references */ - SLE::pointer getEntry (LedgerEntrySet& les, LedgerEntryType type); - - /** Make this iterator point to the first offer */ - bool firstEntry (LedgerEntrySet&); - - /** Make this iterator point to the next offer */ - bool nextEntry (LedgerEntrySet&); - - /** Add this iterator's position to a JSON object */ - bool addJson (Json::Value&) const; - - /** Set this iterator's position from a JSON object */ - bool setJson (Json::Value const&, LedgerEntrySet& les); - - uint256 const& getEntryLedgerIndex () const - { - return mEntryIndex; - } - - uint256 getDirectory () const - { - return mDirNode ? mDirNode->getIndex () : uint256(); - } - - bool - operator== (DirectoryEntryIterator const& other) const - { - return mEntry == other.mEntry && mDirIndex == other.mDirIndex; - } - - bool - operator!= (DirectoryEntryIterator const& other) const - { - return ! (*this == other); - } - -private: - uint256 mRootIndex; // ledger index of the root directory - uint256 mDirIndex; // ledger index of the current directory - unsigned int mEntry; // entry index we are on (0 means first is next) - uint256 mEntryIndex; // ledger index of the current entry - SLE::pointer mDirNode; // SLE for the entry we are on -}; - -} // ripple - -#endif diff --git a/src/ripple/app/ledger/Ledger.cpp b/src/ripple/app/ledger/Ledger.cpp index 7fc07dae0a..4065a8c1b9 100644 --- a/src/ripple/app/ledger/Ledger.cpp +++ b/src/ripple/app/ledger/Ledger.cpp @@ -47,6 +47,7 @@ #include #include #include +#include namespace ripple { @@ -57,7 +58,7 @@ namespace ripple { @param drops The number of drops to start with */ static -std::shared_ptr +std::shared_ptr makeGenesisAccount (AccountID const& id, std::uint64_t drops) { @@ -70,6 +71,11 @@ makeGenesisAccount (AccountID const& id, return sle; } +// VFALCO This constructor could be eliminating by providing +// a free function createGenesisLedger, call the +// other constructor with appropriate parameters, and +// then create the master account / flush dirty. +// Ledger::Ledger (RippleAddress const& masterID, std::uint64_t startAmount) : mTotCoins (startAmount) , seq_ (1) // First Ledger @@ -78,17 +84,17 @@ Ledger::Ledger (RippleAddress const& masterID, std::uint64_t startAmount) , mCloseResolution (ledgerDefaultTimeResolution) , mCloseFlags (0) , mImmutable (false) - , mTransactionMap (std::make_shared (SHAMapType::TRANSACTION, + , txMap_ (std::make_shared (SHAMapType::TRANSACTION, getApp().family(), deprecatedLogs().journal("SHAMap"))) - , mAccountStateMap (std::make_shared (SHAMapType::STATE, + , stateMap_ (std::make_shared (SHAMapType::STATE, getApp().family(), deprecatedLogs().journal("SHAMap"))) { - auto const sle = makeGenesisAccount( + auto sle = makeGenesisAccount( masterID.getAccountID(), startAmount); WriteLog (lsTRACE, Ledger) << "root account: " << sle->getJson(0); - insert(*sle); - mAccountStateMap->flushDirty (hotACCOUNT_NODE, seq_); + unchecked_insert(std::move(sle)); + stateMap_->flushDirty (hotACCOUNT_NODE, seq_); } Ledger::Ledger (uint256 const& parentHash, @@ -111,31 +117,31 @@ Ledger::Ledger (uint256 const& parentHash, , mCloseResolution (closeResolution) , mCloseFlags (closeFlags) , mImmutable (true) - , mTransactionMap (std::make_shared ( + , txMap_ (std::make_shared ( SHAMapType::TRANSACTION, transHash, getApp().family(), deprecatedLogs().journal("SHAMap"))) - , mAccountStateMap (std::make_shared (SHAMapType::STATE, accountHash, + , stateMap_ (std::make_shared (SHAMapType::STATE, accountHash, getApp().family(), deprecatedLogs().journal("SHAMap"))) { updateHash (); loaded = true; if (mTransHash.isNonZero () && - !mTransactionMap->fetchRoot (mTransHash, nullptr)) + !txMap_->fetchRoot (mTransHash, nullptr)) { loaded = false; WriteLog (lsWARNING, Ledger) << "Don't have TX root for ledger"; } if (mAccountHash.isNonZero () && - !mAccountStateMap->fetchRoot (mAccountHash, nullptr)) + !stateMap_->fetchRoot (mAccountHash, nullptr)) { loaded = false; WriteLog (lsWARNING, Ledger) << "Don't have AS root for ledger"; } - mTransactionMap->setImmutable (); - mAccountStateMap->setImmutable (); + txMap_->setImmutable (); + stateMap_->setImmutable (); } // Create a new ledger that's a snapshot of this one @@ -152,8 +158,8 @@ Ledger::Ledger (Ledger const& ledger, , mValidated (ledger.mValidated) , mAccepted (ledger.mAccepted) , mImmutable (!isMutable) - , mTransactionMap (ledger.mTransactionMap->snapShot (isMutable)) - , mAccountStateMap (ledger.mAccountStateMap->snapShot (isMutable)) + , txMap_ (ledger.txMap_->snapShot (isMutable)) + , stateMap_ (ledger.stateMap_->snapShot (isMutable)) { updateHash (); } @@ -167,9 +173,9 @@ Ledger::Ledger (bool /* dummy */, , mCloseResolution (prevLedger.mCloseResolution) , mCloseFlags (0) , mImmutable (false) - , mTransactionMap (std::make_shared (SHAMapType::TRANSACTION, + , txMap_ (std::make_shared (SHAMapType::TRANSACTION, getApp().family(), deprecatedLogs().journal("SHAMap"))) - , mAccountStateMap (prevLedger.mAccountStateMap->snapShot (true)) + , stateMap_ (prevLedger.stateMap_->snapShot (true)) { prevLedger.updateHash (); @@ -207,10 +213,10 @@ Ledger::Ledger (std::uint32_t ledgerSeq, std::uint32_t closeTime) , mCloseResolution (ledgerDefaultTimeResolution) , mCloseFlags (0) , mImmutable (false) - , mTransactionMap (std::make_shared ( + , txMap_ (std::make_shared ( SHAMapType::TRANSACTION, getApp().family(), deprecatedLogs().journal("SHAMap"))) - , mAccountStateMap (std::make_shared ( + , stateMap_ (std::make_shared ( SHAMapType::STATE, getApp().family(), deprecatedLogs().journal("SHAMap"))) { @@ -229,24 +235,24 @@ void Ledger::setImmutable () updateHash (); mImmutable = true; - if (mTransactionMap) - mTransactionMap->setImmutable (); + if (txMap_) + txMap_->setImmutable (); - if (mAccountStateMap) - mAccountStateMap->setImmutable (); + if (stateMap_) + stateMap_->setImmutable (); } void Ledger::updateHash() { if (! mImmutable) { - if (mTransactionMap) - mTransHash = mTransactionMap->getHash (); + if (txMap_) + mTransHash = txMap_->getHash (); else mTransHash.zero (); - if (mAccountStateMap) - mAccountHash = mAccountStateMap->getHash (); + if (stateMap_) + mAccountHash = stateMap_->getHash (); else mAccountHash.zero (); } @@ -281,9 +287,9 @@ void Ledger::setRaw (SerialIter& sit, bool hasPrefix) mCloseResolution = sit.get8 (); mCloseFlags = sit.get8 (); updateHash (); - mTransactionMap = std::make_shared (SHAMapType::TRANSACTION, mTransHash, + txMap_ = std::make_shared (SHAMapType::TRANSACTION, mTransHash, getApp().family(), deprecatedLogs().journal("SHAMap")); - mAccountStateMap = std::make_shared (SHAMapType::STATE, mAccountHash, + stateMap_ = std::make_shared (SHAMapType::STATE, mAccountHash, getApp().family(), deprecatedLogs().journal("SHAMap")); } @@ -328,26 +334,29 @@ void Ledger::setAccepted () bool Ledger::addSLE (SLE const& sle) { SHAMapItem item (sle.getIndex(), sle.getSerializer()); - return mAccountStateMap->addItem(item, false, false); + return stateMap_->addItem(item, false, false); } -bool Ledger::addTransaction (uint256 const& txID, const Serializer& txn) +bool +addTransaction (Ledger& ledger, + uint256 const& txID, const Serializer& txn) { // low-level - just add to table auto item = std::make_shared (txID, txn.peekData ()); - if (!mTransactionMap->addGiveItem (item, true, false)) + if (! ledger.txMap().addGiveItem (item, true, false)) { WriteLog (lsWARNING, Ledger) << "Attempt to add transaction to ledger that already had it"; return false; } - mValidHash = false; + // VFALCO TODO We could touch only the txMap + ledger.touch(); return true; } -bool Ledger::addTransaction ( +bool addTransaction (Ledger& ledger, uint256 const& txID, const Serializer& txn, const Serializer& md) { // low-level - just add to table @@ -356,26 +365,30 @@ bool Ledger::addTransaction ( s.addVL (md.peekData ()); auto item = std::make_shared (txID, s.peekData ()); - if (!mTransactionMap->addGiveItem (item, true, true)) + if (! ledger.txMap().addGiveItem (item, true, true)) { WriteLog (lsFATAL, Ledger) << "Attempt to add transaction+MD to ledger that already had it"; return false; } - mValidHash = false; + // VFALCO TODO We could touch only the txMap + ledger.touch(); return true; } -Transaction::pointer Ledger::getTransaction (uint256 const& transID) const +Transaction::pointer +getTransaction (Ledger const& ledger, + uint256 const& transID, TransactionMaster& cache) { SHAMapTreeNode::TNType type; - std::shared_ptr item = mTransactionMap->peekItem (transID, type); + auto const item = + ledger.txMap().peekItem (transID, type); if (!item) return Transaction::pointer (); - auto txn = getApp().getMasterTransaction ().fetch (transID, false); + auto txn = cache.fetch (transID, false); if (txn) return txn; @@ -399,71 +412,28 @@ Transaction::pointer Ledger::getTransaction (uint256 const& transID) const } if (txn->getStatus () == NEW) - txn->setStatus (mClosed ? COMMITTED : INCLUDED, seq_); + txn->setStatus (ledger.isClosed() ? COMMITTED : INCLUDED, ledger.getLedgerSeq()); - getApp().getMasterTransaction ().canonicalize (&txn); + cache.canonicalize (&txn); return txn; } -STTx::pointer Ledger::getSTransaction ( - std::shared_ptr const& item, SHAMapTreeNode::TNType type) -{ - SerialIter sit (item->slice()); - - if (type == SHAMapTreeNode::tnTRANSACTION_NM) - return std::make_shared (sit); - - if (type == SHAMapTreeNode::tnTRANSACTION_MD) - { - // VFALCO This is making a needless copy - auto const vl = sit.getVL(); - SerialIter tSit (make_Slice(vl)); - return std::make_shared (tSit); - } - - return STTx::pointer (); -} - -STTx::pointer Ledger::getSMTransaction ( - std::shared_ptr const& item, SHAMapTreeNode::TNType type, - TransactionMetaSet::pointer& txMeta) const -{ - SerialIter sit (item->slice()); - - if (type == SHAMapTreeNode::tnTRANSACTION_NM) - { - txMeta.reset (); - return std::make_shared (sit); - } - else if (type == SHAMapTreeNode::tnTRANSACTION_MD) - { - // VFALCO This is making a needless copy - auto const vl = sit.getVL(); - SerialIter tSit (make_Slice(vl)); - - txMeta = std::make_shared ( - item->key(), seq_, sit.getVL ()); - return std::make_shared (tSit); - } - - txMeta.reset (); - return STTx::pointer (); -} - -bool Ledger::getTransaction ( +bool +getTransaction (Ledger const& ledger, uint256 const& txID, Transaction::pointer& txn, - TransactionMetaSet::pointer& meta) const + TransactionMetaSet::pointer& meta, + TransactionMaster& cache) { SHAMapTreeNode::TNType type; - std::shared_ptr item = mTransactionMap->peekItem (txID, type); - + auto const item = + ledger.txMap().peekItem (txID, type); if (!item) return false; if (type == SHAMapTreeNode::tnTRANSACTION_NM) { // in tree with no metadata - txn = getApp().getMasterTransaction ().fetch (txID, false); + txn = cache.fetch (txID, false); meta.reset (); if (!txn) @@ -484,23 +454,24 @@ bool Ledger::getTransaction ( it.getVL (); // skip transaction meta = std::make_shared ( - txID, seq_, it.getVL ()); + txID, ledger.seq(), it.getVL ()); } else return false; if (txn->getStatus () == NEW) - txn->setStatus (mClosed ? COMMITTED : INCLUDED, seq_); + txn->setStatus (ledger.isClosed() ? COMMITTED : INCLUDED, ledger.seq()); - getApp().getMasterTransaction ().canonicalize (&txn); + cache.canonicalize (&txn); return true; } -bool Ledger::getTransactionMeta ( - uint256 const& txID, TransactionMetaSet::pointer& meta) const +bool getTransactionMeta (Ledger const& ledger, + uint256 const& txID, TransactionMetaSet::pointer& meta) { SHAMapTreeNode::TNType type; - std::shared_ptr item = mTransactionMap->peekItem (txID, type); + auto const item = + ledger.txMap().peekItem (txID, type); if (!item) return false; @@ -510,28 +481,11 @@ bool Ledger::getTransactionMeta ( SerialIter it (item->slice()); it.getVL (); // skip transaction - meta = std::make_shared (txID, seq_, it.getVL ()); + meta = std::make_shared (txID, ledger.seq(), it.getVL ()); return true; } -bool Ledger::getMetaHex (uint256 const& transID, std::string& hex) const -{ - SHAMapTreeNode::TNType type; - std::shared_ptr item = mTransactionMap->peekItem (transID, type); - - if (!item) - return false; - - if (type != SHAMapTreeNode::tnTRANSACTION_MD) - return false; - - SerialIter it (item->slice()); - it.getVL (); // skip transaction - hex = strHex (it.getVL ()); - return true; -} - uint256 const& Ledger::getHash() { @@ -572,16 +526,16 @@ bool Ledger::saveValidatedLedger (bool current) assert (false); } - if (getAccountHash () != mAccountStateMap->getHash ()) + if (getAccountHash () != stateMap_->getHash ()) { WriteLog (lsFATAL, Ledger) << "sAL: " << getAccountHash () - << " != " << mAccountStateMap->getHash (); + << " != " << stateMap_->getHash (); WriteLog (lsFATAL, Ledger) << "saveAcceptedLedger: seq=" << seq_ << ", current=" << current; assert (false); } - assert (getTransHash () == mTransactionMap->getHash ()); + assert (getTransHash () == txMap_->getHash ()); // Save the ledger header in the hashed object store { @@ -923,11 +877,11 @@ Ledger::getHashesByIndex (std::uint32_t minSeq, std::uint32_t maxSeq) void Ledger::setAcquiring (void) { - if (!mTransactionMap || !mAccountStateMap) + if (!txMap_ || !stateMap_) throw std::runtime_error ("invalid map"); - mTransactionMap->setSynching (); - mAccountStateMap->setSynching (); + txMap_->setSynching (); + stateMap_->setSynching (); } bool Ledger::isAcquiring (void) const @@ -937,12 +891,12 @@ bool Ledger::isAcquiring (void) const bool Ledger::isAcquiringTx (void) const { - return mTransactionMap->isSynching (); + return txMap_->isSynching (); } bool Ledger::isAcquiringAS (void) const { - return mAccountStateMap->isSynching (); + return stateMap_->isSynching (); } boost::posix_time::ptime Ledger::getCloseTime () const @@ -959,90 +913,108 @@ void Ledger::setCloseTime (boost::posix_time::ptime ptm) //------------------------------------------------------------------------------ bool -Ledger::exists (uint256 const& key) const +Ledger::exists (Keylet const& k) const { - return mAccountStateMap->hasItem(key); + // VFALCO NOTE Perhaps check the type for debug builds? + return stateMap_->hasItem(k.key); } -std::shared_ptr -Ledger::find (uint256 const& key) const +boost::optional +Ledger::succ (uint256 const& key, + boost::optional last) const { - return mAccountStateMap->peekItem(key); + auto const item = + stateMap_->peekNextItem(key); + if (! item) + return boost::none; + if (last && item->key() >= last) + return boost::none; + return item->key(); +} + +std::shared_ptr +Ledger::read (Keylet const& k) const +{ + auto const& value = + stateMap_->peekItem(k.key); + if (! value) + return nullptr; + auto sle = std::make_shared( + value->peekSerializer(), value->key()); + if (! k.check(*sle)) + return nullptr; + // VFALCO TODO Eliminate "immutable" runtime property + sle->setImmutable(); + // need move otherwise makes a copy + // because return type is different + return std::move(sle); +} + +bool +Ledger::unchecked_erase( + uint256 const& key) +{ + return stateMap_->delItem(key); } void -Ledger::insert (SLE const& sle) +Ledger::unchecked_insert( + std::shared_ptr&& sle) { - assert(! mAccountStateMap->hasItem(sle.getIndex())); - auto item = std::make_shared( - sle.getIndex()); - sle.add(item->peekSerializer()); + assert(! stateMap_->hasItem(sle->getIndex())); + Serializer ss; + sle->add(ss); + auto item = std::make_shared< + SHAMapItem>(sle->key(), + std::move(ss)); + // VFALCO NOTE addGiveItem should take ownership auto const success = - mAccountStateMap->addGiveItem( - item, false, false); + stateMap_->addGiveItem( + std::move(item), false, false); (void)success; assert(success); + auto const ours = std::move(sle); +} + +void +Ledger::unchecked_replace( + std::shared_ptr&& sle) +{ + assert(stateMap_->hasItem(sle->getIndex())); + Serializer ss; + sle->add(ss); + auto item = std::make_shared< + SHAMapItem>(sle->key(), + std::move(ss)); + // VFALCO NOTE updateGiveItem should take ownership + auto const success = + stateMap_->updateGiveItem( + std::move(item), false, false); + (void)success; + assert(success); + auto const ours = std::move(sle); } std::shared_ptr -Ledger::fetch (uint256 const& key, - boost::optional type) const +Ledger::peek (Keylet const& k) const { - auto const item = - mAccountStateMap->peekItem(key); - if (! item) + auto const& value = + stateMap_->peekItem(k.key); + if (! value) return nullptr; - auto const sle = std::make_shared( - item->peekSerializer(), item->getTag()); - if (type && sle->getType() != type) + auto sle = std::make_shared( + value->peekSerializer(), value->key()); + if (! k.check(*sle)) return nullptr; - return sle; -} - -void -Ledger::replace (SLE const& sle) -{ - assert(mAccountStateMap->hasItem(sle.getIndex())); - auto item = std::make_shared( - sle.getIndex()); - sle.add(item->peekSerializer()); - auto const success = - mAccountStateMap->updateGiveItem( - item, false, false); - (void)success; - assert(success); -} - -void -Ledger::erase (uint256 const& key) -{ - assert(mAccountStateMap->hasItem(key)); - mAccountStateMap->delItem(key); + // VFALCO TODO Eliminate "immutable" runtime property + sle->setImmutable(); + // need move otherwise makes a copy + // because return type is different + return std::move(sle); } //------------------------------------------------------------------------------ -SLE::pointer Ledger::getSLEi (uint256 const& uId) const -{ - uint256 hash; - - std::shared_ptr node = mAccountStateMap->peekItem (uId, hash); - - if (!node) - return SLE::pointer (); - - SLE::pointer ret = getApp().getSLECache ().fetch (hash); - - if (!ret) - { - ret = std::make_shared (node->peekSerializer (), node->key()); - ret->setImmutable (); - getApp().getSLECache ().canonicalize (hash, ret); - } - - return ret; -} - static void visitHelper ( std::function& function, std::shared_ptr const& item) { @@ -1053,9 +1025,9 @@ void Ledger::visitStateItems (std::function function) const { try { - if (mAccountStateMap) + if (stateMap_) { - mAccountStateMap->visitLeaves( + stateMap_->visitLeaves( std::bind(&visitHelper, std::ref(function), std::placeholders::_1)); } @@ -1075,7 +1047,7 @@ uint256 Ledger::getNextLedgerIndex (uint256 const& hash, boost::optional const& last) const { auto const node = - mAccountStateMap->peekNextItem(hash); + stateMap_->peekNextItem(hash); if ((! node) || (last && node->key() >= *last)) return {}; return node->key(); @@ -1086,15 +1058,15 @@ bool Ledger::walkLedger () const std::vector missingNodes1; std::vector missingNodes2; - if (mAccountStateMap->getHash().isZero() && + if (stateMap_->getHash().isZero() && ! mAccountHash.isZero() && - ! mAccountStateMap->fetchRoot (mAccountHash, nullptr)) + ! stateMap_->fetchRoot (mAccountHash, nullptr)) { missingNodes1.emplace_back (SHAMapType::STATE, mAccountHash); } else { - mAccountStateMap->walkMap (missingNodes1, 32); + stateMap_->walkMap (missingNodes1, 32); } if (ShouldLog (lsINFO, Ledger) && !missingNodes1.empty ()) @@ -1105,15 +1077,15 @@ bool Ledger::walkLedger () const << "First: " << missingNodes1[0]; } - if (mTransactionMap->getHash().isZero() && + if (txMap_->getHash().isZero() && mTransHash.isNonZero() && - ! mTransactionMap->fetchRoot (mTransHash, nullptr)) + ! txMap_->fetchRoot (mTransHash, nullptr)) { missingNodes2.emplace_back (SHAMapType::TRANSACTION, mTransHash); } else { - mTransactionMap->walkMap (missingNodes2, 32); + txMap_->walkMap (missingNodes2, 32); } if (ShouldLog (lsINFO, Ledger) && !missingNodes2.empty ()) @@ -1131,10 +1103,10 @@ bool Ledger::assertSane () { if (mHash.isNonZero () && mAccountHash.isNonZero () && - mAccountStateMap && - mTransactionMap && - (mAccountHash == mAccountStateMap->getHash ()) && - (mTransHash == mTransactionMap->getHash ())) + stateMap_ && + txMap_ && + (mAccountHash == stateMap_->getHash ()) && + (mTransHash == txMap_->getHash ())) { return true; } @@ -1163,15 +1135,14 @@ void Ledger::updateSkipList () // update record of every 256th ledger if ((prevIndex & 0xff) == 0) { - auto const key = getLedgerHashIndex(prevIndex); - auto sle = fetch(key, ltLEDGER_HASHES); + auto const k = keylet::skip(prevIndex); + auto sle = peek(k); std::vector hashes; bool created; if (! sle) { - sle = std::make_shared( - ltLEDGER_HASHES, key); + sle = std::make_shared(k); created = true; } else @@ -1186,20 +1157,19 @@ void Ledger::updateSkipList () sle->setFieldV256 (sfHashes, STVector256 (hashes)); sle->setFieldU32 (sfLastLedgerSequence, prevIndex); if (created) - insert(*sle); + unchecked_insert(std::move(sle)); else - replace(*sle); + unchecked_replace(std::move(sle)); } // update record of past 256 ledger - auto const key = getLedgerHashIndex(); - auto sle = fetch(key, ltLEDGER_HASHES); + auto const k = keylet::skip(); + auto sle = peek(k); std::vector hashes; bool created; if (! sle) { - sle = std::make_shared( - ltLEDGER_HASHES, key); + sle = std::make_shared(k); created = true; } else @@ -1215,9 +1185,9 @@ void Ledger::updateSkipList () sle->setFieldV256 (sfHashes, STVector256 (hashes)); sle->setFieldU32 (sfLastLedgerSequence, prevIndex); if (created) - insert(*sle); + unchecked_insert(std::move(sle)); else - replace(*sle); + unchecked_replace(std::move(sle)); } /** Save, or arrange to save, a fully-validated ledger @@ -1260,12 +1230,14 @@ bool Ledger::pendSaveValidated (bool isSynchronous, bool isCurrent) return true; } -void Ledger::ownerDirDescriber (SLE::ref sle, bool, AccountID const& owner) +void +ownerDirDescriber (SLE::ref sle, bool, AccountID const& owner) { sle->setFieldAccount (sfOwner, owner); } -void Ledger::qualityDirDescriber ( +void +qualityDirDescriber ( SLE::ref sle, bool isNew, Currency const& uTakerPaysCurrency, AccountID const& uTakerPaysIssuer, Currency const& uTakerGetsCurrency, AccountID const& uTakerGetsIssuer, @@ -1294,8 +1266,7 @@ void Ledger::deprecatedUpdateCachedFees() const std::int64_t reserveIncrement = getConfig ().FEE_OWNER_RESERVE; // VFALCO NOTE this doesn't go through the SLECache - auto const sle = this->fetch( - getLedgerFeeIndex(), ltFEE_SETTINGS); + auto const sle = this->read(keylet::fees()); if (sle) { if (sle->getFieldIndex (sfBaseFee) != -1) @@ -1332,10 +1303,10 @@ std::vector Ledger::getNeededTransactionHashes ( if (mTransHash.isNonZero ()) { - if (mTransactionMap->getHash ().isZero ()) + if (txMap_->getHash ().isZero ()) ret.push_back (mTransHash); else - ret = mTransactionMap->getNeededHashes (max, filter); + ret = txMap_->getNeededHashes (max, filter); } return ret; @@ -1348,10 +1319,10 @@ std::vector Ledger::getNeededAccountStateHashes ( if (mAccountHash.isNonZero ()) { - if (mAccountStateMap->getHash ().isZero ()) + if (stateMap_->getHash ().isZero ()) ret.push_back (mAccountHash); else - ret = mAccountStateMap->getNeededHashes (max, filter); + ret = stateMap_->getNeededHashes (max, filter); } return ret; @@ -1364,12 +1335,12 @@ std::vector Ledger::getNeededAccountStateHashes ( //------------------------------------------------------------------------------ std::shared_ptr -fetch (Ledger const& ledger, uint256 const& key, +cachedRead (Ledger const& ledger, uint256 const& key, SLECache& cache, boost::optional type) { uint256 hash; auto const item = - ledger.peekAccountStateMap()->peekItem(key, hash); + ledger.stateMap().peekItem(key, hash); if (! item) return {}; if (auto const sle = cache.fetch(hash)) @@ -1388,105 +1359,12 @@ fetch (Ledger const& ledger, uint256 const& key, return sle; } -void -forEachItem (Ledger const& ledger, AccountID const& id, SLECache& cache, - std::function const&)> f) -{ - auto rootIndex = getOwnerDirIndex (id); - auto currentIndex = rootIndex; - for(;;) - { - auto ownerDir = fetch( - ledger, currentIndex, cache, ltDIR_NODE); - if (! ownerDir) - return; - for (auto const& key : ownerDir->getFieldV256 (sfIndexes)) - f(fetch(ledger, key, cache)); - auto uNodeNext = - ownerDir->getFieldU64 (sfIndexNext); - if (! uNodeNext) - return; - currentIndex = getDirNodeIndex (rootIndex, uNodeNext); - } -} - -bool -forEachItemAfter (Ledger const& ledger, AccountID const& id, SLECache& cache, - uint256 const& after, std::uint64_t const hint, unsigned int limit, - std::function const&)> f) -{ - auto const rootIndex = getOwnerDirIndex(id); - auto currentIndex = rootIndex; - - // If startAfter is not zero try jumping to that page using the hint - if (after.isNonZero ()) - { - auto const hintIndex = getDirNodeIndex (rootIndex, hint); - auto hintDir = fetch(ledger, hintIndex, cache); - if (hintDir) - { - for (auto const& key : hintDir->getFieldV256 (sfIndexes)) - { - if (key == after) - { - // We found the hint, we can start here - currentIndex = hintIndex; - break; - } - } - } - - bool found = false; - for (;;) - { - auto const ownerDir = fetch(ledger, currentIndex, cache); - if (! ownerDir || ownerDir->getType () != ltDIR_NODE) - return found; - for (auto const& key : ownerDir->getFieldV256 (sfIndexes)) - { - if (! found) - { - if (key == after) - found = true; - } - else if (f (fetch (ledger, key, cache)) && limit-- <= 1) - { - return found; - } - } - - auto const uNodeNext = - ownerDir->getFieldU64(sfIndexNext); - if (uNodeNext == 0) - return found; - currentIndex = getDirNodeIndex (rootIndex, uNodeNext); - } - } - else - { - for (;;) - { - auto const ownerDir = fetch(ledger, currentIndex, cache); - if (! ownerDir || ownerDir->getType () != ltDIR_NODE) - return true; - for (auto const& key : ownerDir->getFieldV256 (sfIndexes)) - if (f (fetch(ledger, key, cache)) && limit-- <= 1) - return true; - auto const uNodeNext = - ownerDir->getFieldU64 (sfIndexNext); - if (uNodeNext == 0) - return true; - currentIndex = getDirNodeIndex (rootIndex, uNodeNext); - } - } -} - AccountState::pointer getAccountState (Ledger const& ledger, RippleAddress const& accountID, SLECache& cache) { - auto const sle = fetch (ledger, + auto const sle = cachedRead (ledger, getAccountRootIndex(accountID.getAccountID()), cache); if (!sle) @@ -1528,7 +1406,7 @@ hashOfSeq (Ledger& ledger, LedgerIndex seq, int diff = ledger.seq() - seq; if (diff <= 256) { - auto const hashIndex = fetch( + auto const hashIndex = cachedRead( ledger, getLedgerHashIndex(), cache); if (hashIndex) { @@ -1559,7 +1437,7 @@ hashOfSeq (Ledger& ledger, LedgerIndex seq, } // in skiplist - auto const hashIndex = fetch(ledger, + auto const hashIndex = cachedRead(ledger, getLedgerHashIndex(seq), cache); if (hashIndex) { @@ -1578,4 +1456,51 @@ hashOfSeq (Ledger& ledger, LedgerIndex seq, return boost::none; } +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 (it.getVL ()); + return true; +} + +static +Ledger const* +ledgerFromView(BasicView const* view) +{ + do + { + auto const ledger = + dynamic_cast(view); + if (ledger) + return ledger; + view = view->parent(); + } + while (view != nullptr); + return nullptr; +} + +// This hack traverses the View chain until +// it finds the underlying ledger then extracts +// the parent close time. +// +std::uint32_t +getParentCloseTimeNC (BasicView const& view) +{ + auto const& ledger = *ledgerFromView(&view); + return ledger.getParentCloseTimeNC(); +} + } // ripple diff --git a/src/ripple/app/ledger/Ledger.h b/src/ripple/app/ledger/Ledger.h index 288a72207a..faebf53e50 100644 --- a/src/ripple/app/ledger/Ledger.h +++ b/src/ripple/app/ledger/Ledger.h @@ -23,7 +23,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -37,6 +38,7 @@ namespace ripple { class Job; +class TransactionMaster; class SqliteStatement; @@ -69,6 +71,7 @@ class SqliteStatement; */ class Ledger : public std::enable_shared_from_this + , public BasicView , public CountedObject { public: @@ -106,76 +109,51 @@ public: ~Ledger(); + //-------------------------------------------------------------------------- + // + // BasicView + // //-------------------------------------------------------------------------- - /** Returns `true` if a ledger entry exists. */ bool - exists (uint256 const& key) const; + exists (Keylet const& k) const override; - /** Return the state item for a key. - The item may not be modified. - @return The serialized ledger entry or empty - if the key does not exist. - */ - std::shared_ptr - find (uint256 const& key) const; + boost::optional + succ (uint256 const& key, boost::optional< + uint256> last = boost::none) const override; + + std::shared_ptr + read (Keylet const& k) const override; + + bool + unchecked_erase (uint256 const& key) override; - /** Add a new state SLE. - Effects: - assert if the key already exists. - The key in the state map is associated - with an unflattened copy of the SLE. - @note The key is taken from the SLE. - */ void - insert (SLE const& sle); + unchecked_insert (std::shared_ptr&& sle) override; - /** Fetch a modifiable state SLE. - Effects: - Gives the caller ownership of an - unflattened copy of the SLE. - @param type An optional LedgerEntryType. If type is - engaged and the SLE's type does not match, - then boost::none is returned. - @return `empty` if the key is not present - */ - std::shared_ptr - fetch (uint256 const& key, boost::optional< - LedgerEntryType> type = boost::none) const; + void + unchecked_replace (std::shared_ptr&& sle) override; - std::shared_ptr - fetch (Keylet const& k) const + BasicView const* + parent() const override { - return fetch(k.key, k.type); + return nullptr; } - // DEPRECATED - // Retrieve immutable ledger entry - SLE::pointer getSLEi (uint256 const& uHash) const; - - /** Replace an existing state SLE. - Effects: - assert if key does not already exist. - The previous flattened SLE associated with - the key is released. - The key in the state map is associated - with a flattened copy of the SLE. - @note The key is taken from the SLE - */ - void - replace (SLE const& sle); - - /** Remove an state SLE. - Effects: - assert if the key does not exist. - The flattened SLE associated with the key - is released from the state map. - */ - void - erase (uint256 const& key); - //-------------------------------------------------------------------------- + /** Hint that the contents have changed. + Thread Safety: + Not thread safe + Effects: + The next call to getHash will return updated hashes + */ + void + touch() + { + mValidHash = false; + } + void setClosed () { mClosed = true; @@ -193,6 +171,7 @@ public: void setImmutable (); + // VFALCO Rename to closed bool isClosed () const { return mClosed; @@ -215,8 +194,8 @@ public: void setFull () { - mTransactionMap->setLedgerSeq (seq_); - mAccountStateMap->setLedgerSeq (seq_); + txMap_->setLedgerSeq (seq_); + stateMap_->setLedgerSeq (seq_); } // ledger signature operations @@ -302,15 +281,43 @@ public: boost::posix_time::ptime getCloseTime () const; - // low level functions - std::shared_ptr const& peekTransactionMap () const + // VFALCO NOTE We should ensure that there are + // always valid state and tx maps + // and get rid of these functions. + bool + haveStateMap() const { - return mTransactionMap; + return stateMap_ != nullptr; } - std::shared_ptr const& peekAccountStateMap () const + bool + haveTxMap() const { - return mAccountStateMap; + return txMap_ != nullptr; + } + + SHAMap const& + stateMap() const + { + return *stateMap_; + } + + SHAMap& + stateMap() + { + return *stateMap_; + } + + SHAMap const& + txMap() const + { + return *txMap_; + } + + SHAMap& + txMap() + { + return *txMap_; } // returns false on error @@ -322,34 +329,7 @@ public: bool isAcquiringTx (void) const; bool isAcquiringAS (void) const; - // Transaction Functions - bool addTransaction (uint256 const& id, Serializer const& txn); - - bool addTransaction ( - uint256 const& id, Serializer const& txn, Serializer const& metaData); - - bool hasTransaction (uint256 const& TransID) const - { - return mTransactionMap->hasItem (TransID); - } - - Transaction::pointer getTransaction (uint256 const& transID) const; - - bool getTransaction ( - uint256 const& transID, - Transaction::pointer & txn, TransactionMetaSet::pointer & txMeta) const; - - bool getTransactionMeta ( - uint256 const& transID, TransactionMetaSet::pointer & txMeta) const; - - bool getMetaHex (uint256 const& transID, std::string & hex) const; - - static STTx::pointer getSTransaction ( - std::shared_ptr const&, SHAMapTreeNode::TNType); - - STTx::pointer getSMTransaction ( - std::shared_ptr const&, SHAMapTreeNode::TNType, - TransactionMetaSet::pointer & txMeta) const; + //-------------------------------------------------------------------------- void updateSkipList (); @@ -367,22 +347,6 @@ public: std::vector getNeededAccountStateHashes ( int max, SHAMapSyncFilter* filter) const; - // Directory functions - // Directories are doubly linked lists of nodes. - - // Given a directory root and and index compute the index of a node. - static void ownerDirDescriber (SLE::ref, bool, AccountID const& owner); - - // - // Quality - // - - static void qualityDirDescriber ( - SLE::ref, bool, - Currency const& uTakerPaysCurrency, AccountID const& uTakerPaysIssuer, - Currency const& uTakerGetsCurrency, AccountID const& uTakerGetsIssuer, - const std::uint64_t & uRate); - std::uint32_t getReferenceFeeUnits() const { // Returns the cost of the reference transaction in fee units @@ -439,6 +403,9 @@ private: // ledger close flags static const std::uint32_t sLCF_NoConsensusTime = 1; + std::shared_ptr + peek (Keylet const& k) const; + void updateHash(); @@ -474,8 +441,8 @@ private: bool mAccepted = false; bool mImmutable; - std::shared_ptr mTransactionMap; - std::shared_ptr mAccountStateMap; + std::shared_ptr txMap_; + std::shared_ptr stateMap_; // Protects fee variables std::mutex mutable mutex_; @@ -486,9 +453,9 @@ private: // Fee units for the reference transaction std::uint32_t mutable mReferenceFeeUnits = 0; - // Reserve base in fee units + // Reserve base in drops std::uint32_t mutable mReserveBase = 0; - // Reserve increment in fee units + // Reserve increment in drops std::uint32_t mutable mReserveIncrement = 0; }; @@ -512,25 +479,9 @@ loadLedgerHelper(std::string const& sqlSuffix); @return `empty` if the key is not present */ std::shared_ptr -fetch (Ledger const& ledger, uint256 const& key, SLECache& cache, +cachedRead (Ledger const& ledger, uint256 const& key, SLECache& cache, boost::optional type = boost::none); -/** Iterate all items in an account's owner directory. */ -void -forEachItem (Ledger const& ledger, AccountID const& id, SLECache& cache, - std::function const&)> f); - -/** Iterate all items after an item in an owner directory. - @param after The key of the item to start after - @param hint The directory page containing `after` - @param limit The maximum number of items to return - @return `false` if the iteration failed -*/ -bool -forEachItemAfter (Ledger const& ledger, AccountID const& id, SLECache& cache, - uint256 const& after, std::uint64_t const hint, unsigned int limit, - std::function const&)>); - // DEPRECATED // VFALCO This could return by value // This should take AccountID parameter @@ -571,6 +522,60 @@ getCandidateLedger (LedgerIndex requested) return (requested + 255) & (~255); } +//------------------------------------------------------------------------------ + +// VFALCO Should this take Slice? Should id be called key or hash? Or txhash? +bool addTransaction (Ledger& ledger, + uint256 const& id, Serializer const& txn); + +bool addTransaction (Ledger& ledger, + uint256 const& id, Serializer const& txn, Serializer const& metaData); + +inline +bool hasTransaction (Ledger const& ledger, + uint256 const& TransID) +{ + return ledger.txMap().hasItem (TransID); +} + +// VFALCO NOTE This is called from only one place +Transaction::pointer +getTransaction (Ledger const& ledger, + uint256 const& transID, TransactionMaster& cache); + +// VFALCO NOTE This is called from only one place +bool +getTransaction (Ledger const& ledger, + uint256 const& transID, Transaction::pointer & txn, + TransactionMetaSet::pointer & txMeta, + TransactionMaster& cache); + +bool +getTransactionMeta (Ledger const&, + uint256 const& transID, + TransactionMetaSet::pointer & txMeta); + +// VFALCO NOTE This is called from only one place +bool +getMetaHex (Ledger const& ledger, + uint256 const& transID, std::string & hex); + +void +ownerDirDescriber (SLE::ref, bool, AccountID const& owner); + +// VFALCO NOTE This is referenced from only one place +void +qualityDirDescriber ( + SLE::ref, bool, + Currency const& uTakerPaysCurrency, AccountID const& uTakerPaysIssuer, + Currency const& uTakerGetsCurrency, AccountID const& uTakerGetsIssuer, + const std::uint64_t & uRate); + +//------------------------------------------------------------------------------ + +std::uint32_t +getParentCloseTimeNC (BasicView const& view); + } // ripple #endif diff --git a/src/ripple/app/ledger/LedgerConsensus.h b/src/ripple/app/ledger/LedgerConsensus.h index 650bc53a1f..a4d9e8a8f7 100644 --- a/src/ripple/app/ledger/LedgerConsensus.h +++ b/src/ripple/app/ledger/LedgerConsensus.h @@ -74,7 +74,7 @@ make_LedgerConsensus ( std::uint32_t closeTime, FeeVote& feeVote); void -applyTransactions(std::shared_ptr const& set, Ledger::ref applyLedger, +applyTransactions(SHAMap const* set, Ledger::ref applyLedger, Ledger::ref checkLedger, CanonicalTXSet& retriableTransactions, bool openLgr); diff --git a/src/ripple/app/ledger/LedgerEntrySet.cpp b/src/ripple/app/ledger/LedgerEntrySet.cpp deleted file mode 100644 index 1434f8fa4c..0000000000 --- a/src/ripple/app/ledger/LedgerEntrySet.cpp +++ /dev/null @@ -1,2119 +0,0 @@ -//------------------------------------------------------------------------------ -/* - 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 - -namespace ripple { - -// #define META_DEBUG - -// VFALCO TODO Replace this macro with a documented language constant -// NOTE Is this part of the protocol? -// -#define DIR_NODE_MAX 32 - -LedgerEntrySet::LedgerEntrySet (LedgerEntrySet const& other) - : mLedger(other.mLedger) - , mEntries(other.mEntries) - , mDeferredCredits(other.mDeferredCredits) - , mSet(other.mSet) - // VFALCO NOTE This is a change in behavior, - // previous version set tapNONE - , mParams(other.mParams) - , mSeq(other.mSeq + 1) -{ -} - -LedgerEntrySet::LedgerEntrySet(Ledger::ref ledger, - uint256 const& transactionID, - std::uint32_t ledgerID, - TransactionEngineParams params) - : mLedger(ledger) - , mParams(params) -{ - mSet.init (transactionID, ledgerID); -} - -LedgerEntrySet::LedgerEntrySet (Ledger::ref ledger, - TransactionEngineParams tep, bool immutable) - : mLedger (ledger) - , mParams (tep) - , mImmutable (immutable) -{ -} - -void LedgerEntrySet::apply() -{ - // Write back the account states - for (auto const& item : mEntries) - { - // VFALCO TODO rvalue move the mEntry, make - // sure the mNodes is not used after - // this function is called. - auto sle = item.second.mEntry; - switch (item.second.mAction) - { - case taaNONE: - assert (false); - break; - - case taaCACHED: - break; - - case taaCREATE: - // VFALCO Is this logging necessary anymore? - WriteLog (lsDEBUG, LedgerEntrySet) << - "applyTransaction: taaCREATE: " << sle->getText (); - mLedger->insert(*sle); - break; - - case taaMODIFY: - WriteLog (lsDEBUG, LedgerEntrySet) << - "applyTransaction: taaMODIFY: " << sle->getText (); - mLedger->replace(*sle); - break; - - case taaDELETE: - WriteLog (lsDEBUG, LedgerEntrySet) << - "applyTransaction: taaDELETE: " << sle->getText (); - mLedger->erase(item.first); - break; - } - } -} - -void LedgerEntrySet::swapWith (LedgerEntrySet& e) -{ - using std::swap; - swap (mLedger, e.mLedger); - mEntries.swap (e.mEntries); - mSet.swap (e.mSet); - swap (mParams, e.mParams); - swap (mSeq, e.mSeq); - swap (mDeferredCredits, e.mDeferredCredits); -} - -// Find an entry in the set. If it has the wrong sequence number, copy it and update the sequence number. -// This is basically: copy-on-read. -SLE::pointer LedgerEntrySet::getEntry (uint256 const& index, Action& action) -{ - auto it = mEntries.find (index); - - if (it == mEntries.end ()) - { - action = taaNONE; - return SLE::pointer (); - } - - if (it->second.mSeq != mSeq) - { - assert (it->second.mSeq < mSeq); - it->second.mEntry = std::make_shared (*it->second.mEntry); - it->second.mSeq = mSeq; - } - - action = it->second.mAction; - return it->second.mEntry; -} - -SLE::pointer LedgerEntrySet::entryCache (LedgerEntryType letType, uint256 const& key) -{ - assert(key.isNonZero ()); - assert (mLedger); - SLE::pointer sle; - - if (key.isNonZero ()) - { - Action action; - sle = getEntry (key, action); - - if (! sle) - { - assert (action != taaDELETE); - if (mImmutable) - { - // VFALCO NOTE We'd like all immutable callers to go through - // entryCacheI, then we can avoid calling getSLEi. - sle = mLedger->getSLEi(key); - } - else - { - sle = mLedger->fetch(key, letType); - } - - if (sle) - entryCache (sle); - } - else if (action == taaDELETE) - { - sle = nullptr; - } - } - - return sle; -} - -std::shared_ptr -LedgerEntrySet::entryCacheI (LedgerEntryType letType, uint256 const& key) -{ - return entryCache(letType, key); -} - -void LedgerEntrySet::entryCache (SLE::ref sle) -{ - assert (mLedger); - assert (sle->isMutable () || mImmutable); // Don't put an immutable SLE in a mutable LES - auto it = mEntries.find (sle->getIndex ()); - - if (it == mEntries.end ()) - { - mEntries.insert (std::make_pair (sle->getIndex (), Item (sle, taaCACHED, mSeq))); - return; - } - - switch (it->second.mAction) - { - case taaCACHED: - assert (sle == it->second.mEntry); - it->second.mSeq = mSeq; - it->second.mEntry = sle; - return; - - default: - throw std::runtime_error ("Cache after modify/delete/create"); - } -} - -void LedgerEntrySet::entryCreate (SLE::ref sle) -{ - assert (mLedger && !mImmutable); - assert (sle->isMutable ()); - auto it = mEntries.find (sle->getIndex ()); - - if (it == mEntries.end ()) - { - mEntries.insert (std::make_pair (sle->getIndex (), Item (sle, taaCREATE, mSeq))); - return; - } - - switch (it->second.mAction) - { - - case taaDELETE: - WriteLog (lsDEBUG, LedgerEntrySet) << "Create after Delete = Modify"; - it->second.mEntry = sle; - it->second.mAction = taaMODIFY; - it->second.mSeq = mSeq; - break; - - case taaMODIFY: - throw std::runtime_error ("Create after modify"); - - case taaCREATE: - throw std::runtime_error ("Create after create"); // This could be made to work - - case taaCACHED: - throw std::runtime_error ("Create after cache"); - - default: - throw std::runtime_error ("Unknown taa"); - } - - assert (it->second.mSeq == mSeq); -} - -void LedgerEntrySet::entryModify (SLE::ref sle) -{ - assert (sle->isMutable () && !mImmutable); - assert (mLedger); - auto it = mEntries.find (sle->getIndex ()); - - if (it == mEntries.end ()) - { - mEntries.insert (std::make_pair (sle->getIndex (), Item (sle, taaMODIFY, mSeq))); - return; - } - - assert (it->second.mSeq == mSeq); - assert (it->second.mEntry == sle); - - switch (it->second.mAction) - { - case taaCACHED: - it->second.mAction = taaMODIFY; - - // Fall through - - case taaCREATE: - case taaMODIFY: - it->second.mSeq = mSeq; - it->second.mEntry = sle; - break; - - case taaDELETE: - throw std::runtime_error ("Modify after delete"); - - default: - throw std::runtime_error ("Unknown taa"); - } -} - -void LedgerEntrySet::entryDelete (SLE::ref sle) -{ - assert (sle->isMutable () && !mImmutable); - assert (mLedger); - auto it = mEntries.find (sle->getIndex ()); - - if (it == mEntries.end ()) - { - assert (false); // deleting an entry not cached? - mEntries.insert (std::make_pair (sle->getIndex (), Item (sle, taaDELETE, mSeq))); - return; - } - - assert (it->second.mSeq == mSeq); - assert (it->second.mEntry == sle); - - switch (it->second.mAction) - { - case taaCACHED: - case taaMODIFY: - it->second.mSeq = mSeq; - it->second.mEntry = sle; - it->second.mAction = taaDELETE; - break; - - case taaCREATE: - mEntries.erase (it); - break; - - case taaDELETE: - break; - - default: - throw std::runtime_error ("Unknown taa"); - } -} - -Json::Value LedgerEntrySet::getJson (int) const -{ - Json::Value ret (Json::objectValue); - - Json::Value nodes (Json::arrayValue); - - for (auto it = mEntries.begin (), end = mEntries.end (); it != end; ++it) - { - Json::Value entry (Json::objectValue); - entry[jss::node] = to_string (it->first); - - switch (it->second.mEntry->getType ()) - { - case ltINVALID: - entry[jss::type] = "invalid"; - break; - - case ltACCOUNT_ROOT: - entry[jss::type] = "acccount_root"; - break; - - case ltDIR_NODE: - entry[jss::type] = "dir_node"; - break; - - case ltRIPPLE_STATE: - entry[jss::type] = "ripple_state"; - break; - - case ltNICKNAME: - entry[jss::type] = "nickname"; - break; - - case ltOFFER: - entry[jss::type] = "offer"; - break; - - default: - assert (false); - } - - switch (it->second.mAction) - { - case taaCACHED: - entry[jss::action] = "cache"; - break; - - case taaMODIFY: - entry[jss::action] = "modify"; - break; - - case taaDELETE: - entry[jss::action] = "delete"; - break; - - case taaCREATE: - entry[jss::action] = "create"; - break; - - default: - assert (false); - } - - nodes.append (entry); - } - - ret[jss::nodes] = nodes; - - ret[jss::metaData] = mSet.getJson (0); - - return ret; -} - -SLE::pointer LedgerEntrySet::getForMod (uint256 const& node, Ledger::ref ledger, - NodeToLedgerEntry& newMods) -{ - auto it = mEntries.find (node); - - if (it != mEntries.end ()) - { - if (it->second.mAction == taaDELETE) - { - WriteLog (lsFATAL, LedgerEntrySet) << "Trying to thread to deleted node"; - return SLE::pointer (); - } - - if (it->second.mAction == taaCACHED) - it->second.mAction = taaMODIFY; - - if (it->second.mSeq != mSeq) - { - it->second.mEntry = - std::make_shared (*it->second.mEntry); - it->second.mSeq = mSeq; - } - - return it->second.mEntry; - } - - auto me = newMods.find (node); - - if (me != newMods.end ()) - { - assert (me->second); - return me->second; - } - - auto sle = ledger->fetch(node); - if (! sle) - return nullptr; - newMods.insert (std::make_pair (node, sle)); - return sle; -} - -bool LedgerEntrySet::threadTx (RippleAddress const& threadTo, Ledger::ref ledger, - NodeToLedgerEntry& newMods) -{ - SLE::pointer sle = getForMod ( - getAccountRootIndex (threadTo.getAccountID ()), ledger, newMods); - -#ifdef META_DEBUG - WriteLog (lsTRACE, LedgerEntrySet) << "Thread to " << threadTo.getAccountID (); -#endif - - if (!sle) - { - WriteLog (lsFATAL, LedgerEntrySet) << - "Threading to non-existent account: " << threadTo.humanAccountID (); - assert (false); - return false; - } - - return threadTx (sle, ledger, newMods); -} - -bool LedgerEntrySet::threadTx (SLE::ref threadTo, Ledger::ref ledger, - NodeToLedgerEntry& newMods) -{ - // node = the node that was modified/deleted/created - // threadTo = the node that needs to know - uint256 prevTxID; - std::uint32_t prevLgrID; - - if (!threadTo->thread (mSet.getTxID (), mSet.getLgrSeq (), prevTxID, prevLgrID)) - return false; - - if (prevTxID.isZero () || - TransactionMetaSet::thread (mSet.getAffectedNode (threadTo, sfModifiedNode), prevTxID, prevLgrID)) - return true; - - assert (false); - return false; -} - -bool LedgerEntrySet::threadOwners( - std::shared_ptr const& node, - Ledger::ref ledger, NodeToLedgerEntry& newMods) -{ - // thread new or modified node to owner or owners - if (node->hasOneOwner ()) // thread to owner's account - { -#ifdef META_DEBUG - WriteLog (lsTRACE, LedgerEntrySet) << "Thread to single owner"; -#endif - return threadTx (node->getOwner (), ledger, newMods); - } - else if (node->hasTwoOwners ()) // thread to owner's accounts - { -#ifdef META_DEBUG - WriteLog (lsTRACE, LedgerEntrySet) << "Thread to two owners"; -#endif - return - threadTx (node->getFirstOwner (), ledger, newMods) && - threadTx (node->getSecondOwner (), ledger, newMods); - } - else - return false; -} - -void LedgerEntrySet::calcRawMeta (Serializer& s, TER result, std::uint32_t index) -{ - // calculate the raw meta data and return it. This must be called before the set is committed - - // Entries modified only as a result of building the transaction metadata - NodeToLedgerEntry newMod; - - for (auto& it : mEntries) - { - auto type = &sfGeneric; - - switch (it.second.mAction) - { - case taaMODIFY: -#ifdef META_DEBUG - WriteLog (lsTRACE, LedgerEntrySet) << "Modified Node " << it.first; -#endif - type = &sfModifiedNode; - break; - - case taaDELETE: -#ifdef META_DEBUG - WriteLog (lsTRACE, LedgerEntrySet) << "Deleted Node " << it.first; -#endif - type = &sfDeletedNode; - break; - - case taaCREATE: -#ifdef META_DEBUG - WriteLog (lsTRACE, LedgerEntrySet) << "Created Node " << it.first; -#endif - type = &sfCreatedNode; - break; - - default: // ignore these - break; - } - - if (type == &sfGeneric) - continue; - - std::shared_ptr const origNode = - mLedger->getSLEi(it.first); - SLE::pointer curNode = it.second.mEntry; - - if ((type == &sfModifiedNode) && (*curNode == *origNode)) - continue; - - std::uint16_t nodeType = curNode - ? curNode->getFieldU16 (sfLedgerEntryType) - : origNode->getFieldU16 (sfLedgerEntryType); - - mSet.setAffectedNode (it.first, *type, nodeType); - - if (type == &sfDeletedNode) - { - assert (origNode && curNode); - threadOwners (origNode, mLedger, newMod); // thread transaction to owners - - STObject prevs (sfPreviousFields); - for (auto const& obj : *origNode) - { - // go through the original node for modified fields saved on modification - if (obj.getFName ().shouldMeta (SField::sMD_ChangeOrig) && !curNode->hasMatchingEntry (obj)) - prevs.emplace_back (obj); - } - - if (!prevs.empty ()) - mSet.getAffectedNode (it.first).emplace_back (std::move(prevs)); - - STObject finals (sfFinalFields); - for (auto const& obj : *curNode) - { - // go through the final node for final fields - if (obj.getFName ().shouldMeta (SField::sMD_Always | SField::sMD_DeleteFinal)) - finals.emplace_back (obj); - } - - if (!finals.empty ()) - mSet.getAffectedNode (it.first).emplace_back (std::move(finals)); - } - else if (type == &sfModifiedNode) - { - assert (curNode && origNode); - - if (curNode->isThreadedType ()) // thread transaction to node it modified - threadTx (curNode, mLedger, newMod); - - STObject prevs (sfPreviousFields); - for (auto const& obj : *origNode) - { - // search the original node for values saved on modify - if (obj.getFName ().shouldMeta (SField::sMD_ChangeOrig) && !curNode->hasMatchingEntry (obj)) - prevs.emplace_back (obj); - } - - if (!prevs.empty ()) - mSet.getAffectedNode (it.first).emplace_back (std::move(prevs)); - - STObject finals (sfFinalFields); - for (auto const& obj : *curNode) - { - // search the final node for values saved always - if (obj.getFName ().shouldMeta (SField::sMD_Always | SField::sMD_ChangeNew)) - finals.emplace_back (obj); - } - - if (!finals.empty ()) - mSet.getAffectedNode (it.first).emplace_back (std::move(finals)); - } - else if (type == &sfCreatedNode) // if created, thread to owner(s) - { - assert (curNode && !origNode); - threadOwners (curNode, mLedger, newMod); - - if (curNode->isThreadedType ()) // always thread to self - threadTx (curNode, mLedger, newMod); - - STObject news (sfNewFields); - for (auto const& obj : *curNode) - { - // save non-default values - if (!obj.isDefault () && obj.getFName ().shouldMeta (SField::sMD_Create | SField::sMD_Always)) - news.emplace_back (obj); - } - - if (!news.empty ()) - mSet.getAffectedNode (it.first).emplace_back (std::move(news)); - } - else assert (false); - } - - // add any new modified nodes to the modification set - for (auto& it : newMod) - entryModify (it.second); - - mSet.addRaw (s, result, index); - WriteLog (lsTRACE, LedgerEntrySet) << "Metadata:" << mSet.getJson (0); -} - -bool LedgerEntrySet::dirIsEmpty (uint256 const& uRootIndex) -{ - std::uint64_t uNodeDir = 0; - - SLE::pointer sleNode = entryCache (ltDIR_NODE, getDirNodeIndex (uRootIndex, uNodeDir)); - - if (!sleNode) - return true; - - if (!sleNode->getFieldV256 (sfIndexes).empty ()) - return false; - - // If there's another page, it must be non-empty - return sleNode->getFieldU64 (sfIndexNext) == 0; -} - -// <-- uNodeDir: For deletion, present to make dirDelete efficient. -// --> uRootIndex: The index of the base of the directory. Nodes are based off of this. -// --> uLedgerIndex: Value to add to directory. -// Only append. This allow for things that watch append only structure to just monitor from the last node on ward. -// Within a node with no deletions order of elements is sequential. Otherwise, order of elements is random. -TER LedgerEntrySet::dirAdd ( - std::uint64_t& uNodeDir, - uint256 const& uRootIndex, - uint256 const& uLedgerIndex, - std::function fDescriber) -{ - WriteLog (lsTRACE, LedgerEntrySet) << "dirAdd:" << - " uRootIndex=" << to_string (uRootIndex) << - " uLedgerIndex=" << to_string (uLedgerIndex); - - SLE::pointer sleNode; - STVector256 svIndexes; - SLE::pointer sleRoot = entryCache (ltDIR_NODE, uRootIndex); - - if (!sleRoot) - { - // No root, make it. - sleRoot = std::make_shared(ltDIR_NODE, uRootIndex); - sleRoot->setFieldH256 (sfRootIndex, uRootIndex); - entryCreate (sleRoot); - fDescriber (sleRoot, true); - sleNode = sleRoot; - uNodeDir = 0; - } - else - { - uNodeDir = sleRoot->getFieldU64 (sfIndexPrevious); // Get index to last directory node. - - if (uNodeDir) - { - // Try adding to last node. - sleNode = entryCache (ltDIR_NODE, getDirNodeIndex (uRootIndex, uNodeDir)); - - assert (sleNode); - } - else - { - // Try adding to root. Didn't have a previous set to the last node. - sleNode = sleRoot; - } - - svIndexes = sleNode->getFieldV256 (sfIndexes); - - if (DIR_NODE_MAX != svIndexes.size ()) - { - // Add to current node. - entryModify (sleNode); - } - // Add to new node. - else if (!++uNodeDir) - { - return tecDIR_FULL; - } - else - { - // Have old last point to new node - sleNode->setFieldU64 (sfIndexNext, uNodeDir); - entryModify (sleNode); - - // Have root point to new node. - sleRoot->setFieldU64 (sfIndexPrevious, uNodeDir); - entryModify (sleRoot); - - // Create the new node. - sleNode = std::make_shared( - ltDIR_NODE, getDirNodeIndex(uRootIndex, uNodeDir)); - sleNode->setFieldH256 (sfRootIndex, uRootIndex); - entryCreate (sleNode); - - if (uNodeDir != 1) - sleNode->setFieldU64 (sfIndexPrevious, uNodeDir - 1); - - fDescriber (sleNode, false); - - svIndexes = STVector256 (); - } - } - - svIndexes.push_back (uLedgerIndex); // Append entry. - sleNode->setFieldV256 (sfIndexes, svIndexes); // Save entry. - - WriteLog (lsTRACE, LedgerEntrySet) << - "dirAdd: creating: root: " << to_string (uRootIndex); - WriteLog (lsTRACE, LedgerEntrySet) << - "dirAdd: appending: Entry: " << to_string (uLedgerIndex); - WriteLog (lsTRACE, LedgerEntrySet) << - "dirAdd: appending: Node: " << strHex (uNodeDir); - - return tesSUCCESS; -} - -// Ledger must be in a state for this to work. -TER LedgerEntrySet::dirDelete ( - const bool bKeepRoot, // --> True, if we never completely clean up, after we overflow the root node. - const std::uint64_t& uNodeDir, // --> Node containing entry. - uint256 const& uRootIndex, // --> The index of the base of the directory. Nodes are based off of this. - uint256 const& uLedgerIndex, // --> Value to remove from directory. - const bool bStable, // --> True, not to change relative order of entries. - const bool bSoft) // --> True, uNodeDir is not hard and fast (pass uNodeDir=0). -{ - std::uint64_t uNodeCur = uNodeDir; - SLE::pointer sleNode = entryCache (ltDIR_NODE, getDirNodeIndex (uRootIndex, uNodeCur)); - - if (!sleNode) - { - WriteLog (lsWARNING, LedgerEntrySet) << "dirDelete: no such node:" << - " uRootIndex=" << to_string (uRootIndex) << - " uNodeDir=" << strHex (uNodeDir) << - " uLedgerIndex=" << to_string (uLedgerIndex); - - if (!bSoft) - { - assert (false); - return tefBAD_LEDGER; - } - else if (uNodeDir < 20) - { - // Go the extra mile. Even if node doesn't exist, try the next node. - - return dirDelete (bKeepRoot, uNodeDir + 1, uRootIndex, uLedgerIndex, bStable, true); - } - else - { - return tefBAD_LEDGER; - } - } - - STVector256 svIndexes = sleNode->getFieldV256 (sfIndexes); - - auto it = std::find (svIndexes.begin (), svIndexes.end (), uLedgerIndex); - - if (svIndexes.end () == it) - { - if (!bSoft) - { - assert (false); - WriteLog (lsWARNING, LedgerEntrySet) << "dirDelete: no such entry"; - return tefBAD_LEDGER; - } - - if (uNodeDir < 20) - { - // Go the extra mile. Even if entry not in node, try the next node. - return dirDelete (bKeepRoot, uNodeDir + 1, uRootIndex, uLedgerIndex, - bStable, true); - } - - return tefBAD_LEDGER; - } - - // Remove the element. - if (svIndexes.size () > 1) - { - if (bStable) - { - svIndexes.erase (it); - } - else - { - *it = svIndexes[svIndexes.size () - 1]; - svIndexes.resize (svIndexes.size () - 1); - } - } - else - { - svIndexes.clear (); - } - - sleNode->setFieldV256 (sfIndexes, svIndexes); - entryModify (sleNode); - - if (svIndexes.empty ()) - { - // May be able to delete nodes. - std::uint64_t uNodePrevious = sleNode->getFieldU64 (sfIndexPrevious); - std::uint64_t uNodeNext = sleNode->getFieldU64 (sfIndexNext); - - if (!uNodeCur) - { - // Just emptied root node. - - if (!uNodePrevious) - { - // Never overflowed the root node. Delete it. - entryDelete (sleNode); - } - // Root overflowed. - else if (bKeepRoot) - { - // If root overflowed and not allowed to delete overflowed root node. - } - else if (uNodePrevious != uNodeNext) - { - // Have more than 2 nodes. Can't delete root node. - } - else - { - // Have only a root node and a last node. - SLE::pointer sleLast = entryCache (ltDIR_NODE, getDirNodeIndex (uRootIndex, uNodeNext)); - - assert (sleLast); - - if (sleLast->getFieldV256 (sfIndexes).empty ()) - { - // Both nodes are empty. - - entryDelete (sleNode); // Delete root. - entryDelete (sleLast); // Delete last. - } - else - { - // Have an entry, can't delete root node. - } - } - } - // Just emptied a non-root node. - else if (uNodeNext) - { - // Not root and not last node. Can delete node. - - SLE::pointer slePrevious = entryCache (ltDIR_NODE, getDirNodeIndex (uRootIndex, uNodePrevious)); - - assert (slePrevious); - - SLE::pointer sleNext = entryCache (ltDIR_NODE, getDirNodeIndex (uRootIndex, uNodeNext)); - - assert (slePrevious); - assert (sleNext); - - if (!slePrevious) - { - WriteLog (lsWARNING, LedgerEntrySet) << "dirDelete: previous node is missing"; - - return tefBAD_LEDGER; - } - - if (!sleNext) - { - WriteLog (lsWARNING, LedgerEntrySet) << "dirDelete: next node is missing"; - - return tefBAD_LEDGER; - } - - // Fix previous to point to its new next. - slePrevious->setFieldU64 (sfIndexNext, uNodeNext); - entryModify (slePrevious); - - // Fix next to point to its new previous. - sleNext->setFieldU64 (sfIndexPrevious, uNodePrevious); - entryModify (sleNext); - - entryDelete(sleNode); - } - // Last node. - else if (bKeepRoot || uNodePrevious) - { - // Not allowed to delete last node as root was overflowed. - // Or, have pervious entries preventing complete delete. - } - else - { - // Last and only node besides the root. - SLE::pointer sleRoot = entryCache (ltDIR_NODE, uRootIndex); - - assert (sleRoot); - - if (sleRoot->getFieldV256 (sfIndexes).empty ()) - { - // Both nodes are empty. - - entryDelete (sleRoot); // Delete root. - entryDelete (sleNode); // Delete last. - } - else - { - // Root has an entry, can't delete. - } - } - } - - return tesSUCCESS; -} - -// Return the first entry and advance uDirEntry. -// <-- true, if had a next entry. -bool LedgerEntrySet::dirFirst ( - uint256 const& uRootIndex, // --> Root of directory. - SLE::pointer& sleNode, // <-- current node - unsigned int& uDirEntry, // <-- next entry - uint256& uEntryIndex) // <-- The entry, if available. Otherwise, zero. -{ - sleNode = entryCache (ltDIR_NODE, uRootIndex); - uDirEntry = 0; - - assert (sleNode); // Never probe for directories. - - return LedgerEntrySet::dirNext (uRootIndex, sleNode, uDirEntry, uEntryIndex); -} - -// Return the first entry and advance uDirEntry. -// <-- true, if had a next entry. -bool LedgerEntrySet::dirFirst ( - uint256 const& uRootIndex, // --> Root of directory. - std::shared_ptr& sleNode, // <-- current node - unsigned int& uDirEntry, // <-- next entry - uint256& uEntryIndex) // <-- The entry, if available. Otherwise, zero. -{ - sleNode = entryCacheI (ltDIR_NODE, uRootIndex); - uDirEntry = 0; - - assert (sleNode); // Never probe for directories. - - return LedgerEntrySet::dirNext (uRootIndex, sleNode, uDirEntry, uEntryIndex); -} - -// Return the current entry and advance uDirEntry. -// <-- true, if had a next entry. -bool LedgerEntrySet::dirNext ( - uint256 const& uRootIndex, // --> Root of directory - SLE::pointer& sleNode, // <-> current node - unsigned int& uDirEntry, // <-> next entry - uint256& uEntryIndex) // <-- The entry, if available. Otherwise, zero. -{ - STVector256 svIndexes = sleNode->getFieldV256 (sfIndexes); - - assert (uDirEntry <= svIndexes.size ()); - - if (uDirEntry >= svIndexes.size ()) - { - std::uint64_t uNodeNext = sleNode->getFieldU64 (sfIndexNext); - - if (!uNodeNext) - { - uEntryIndex.zero (); - - return false; - } - else - { - SLE::pointer sleNext = entryCache (ltDIR_NODE, getDirNodeIndex (uRootIndex, uNodeNext)); - uDirEntry = 0; - - if (!sleNext) - { // This should never happen - WriteLog (lsFATAL, LedgerEntrySet) - << "Corrupt directory: index:" - << uRootIndex << " next:" << uNodeNext; - return false; - } - - sleNode = sleNext; - // TODO(tom): make this iterative. - return dirNext (uRootIndex, sleNode, uDirEntry, uEntryIndex); - } - } - - uEntryIndex = svIndexes[uDirEntry++]; - - WriteLog (lsTRACE, LedgerEntrySet) << "dirNext:" << - " uDirEntry=" << uDirEntry << - " uEntryIndex=" << uEntryIndex; - - return true; -} - -// Return the current entry and advance uDirEntry. -// <-- true, if had a next entry. -bool LedgerEntrySet::dirNext ( - uint256 const& uRootIndex, // --> Root of directory - std::shared_ptr& sleNode, // <-> current node - unsigned int& uDirEntry, // <-> next entry - uint256& uEntryIndex) // <-- The entry, if available. Otherwise, zero. -{ - STVector256 const svIndexes = sleNode->getFieldV256 (sfIndexes); - - assert (uDirEntry <= svIndexes.size ()); - - if (uDirEntry >= svIndexes.size ()) - { - std::uint64_t const uNodeNext = sleNode->getFieldU64 (sfIndexNext); - - if (!uNodeNext) - { - uEntryIndex.zero (); - - return false; - } - else - { - auto const sleNext = entryCacheI( - ltDIR_NODE, getDirNodeIndex (uRootIndex, uNodeNext)); - uDirEntry = 0; - - if (!sleNext) - { // This should never happen - WriteLog (lsFATAL, LedgerEntrySet) - << "Corrupt directory: index:" - << uRootIndex << " next:" << uNodeNext; - return false; - } - - sleNode = sleNext; - // TODO(tom): make this iterative. - return dirNext (uRootIndex, sleNode, uDirEntry, uEntryIndex); - } - } - - uEntryIndex = svIndexes[uDirEntry++]; - - WriteLog (lsTRACE, LedgerEntrySet) << "dirNext:" << - " uDirEntry=" << uDirEntry << - " uEntryIndex=" << uEntryIndex; - - return true; -} - -uint256 LedgerEntrySet::getNextLedgerIndex (uint256 const& uHash) -{ - // find next node in ledger that isn't deleted by LES - uint256 ledgerNext = uHash; - std::map::const_iterator it; - - do - { - ledgerNext = mLedger->getNextLedgerIndex (ledgerNext); - it = mEntries.find (ledgerNext); - } - while ((it != mEntries.end ()) && (it->second.mAction == taaDELETE)); - - // find next node in LES that isn't deleted - for (it = mEntries.upper_bound (uHash); it != mEntries.end (); ++it) - { - // node found in LES, node found in ledger, return earliest - if (it->second.mAction != taaDELETE) - return (ledgerNext.isNonZero () && (ledgerNext < it->first)) ? - ledgerNext : it->first; - } - - // nothing next in LES, return next ledger node - return ledgerNext; -} - -uint256 LedgerEntrySet::getNextLedgerIndex ( - uint256 const& uHash, uint256 const& uEnd) -{ - uint256 next = getNextLedgerIndex (uHash); - - if (next > uEnd) - return uint256 (); - - return next; -} - -TER LedgerEntrySet::offerDelete (SLE::pointer sleOffer) -{ - if (!sleOffer) - return tesSUCCESS; - - auto offerIndex = sleOffer->getIndex (); - auto owner = sleOffer->getFieldAccount160 (sfAccount); - - // Detect legacy directories. - bool bOwnerNode = sleOffer->isFieldPresent (sfOwnerNode); - std::uint64_t uOwnerNode = sleOffer->getFieldU64 (sfOwnerNode); - uint256 uDirectory = sleOffer->getFieldH256 (sfBookDirectory); - std::uint64_t uBookNode = sleOffer->getFieldU64 (sfBookNode); - - TER terResult = dirDelete ( - false, uOwnerNode, - getOwnerDirIndex (owner), offerIndex, false, !bOwnerNode); - TER terResult2 = dirDelete ( - false, uBookNode, uDirectory, offerIndex, true, false); - - if (tesSUCCESS == terResult) - adjustOwnerCount(*this, entryCache(ltACCOUNT_ROOT, - getAccountRootIndex(owner)), -1); - - entryDelete (sleOffer); - - return (terResult == tesSUCCESS) ? terResult2 : terResult; -} - -// Return how much of issuer's currency IOUs that account holds. May be -// negative. -// <-- IOU's account has of issuer. -STAmount LedgerEntrySet::rippleHolds ( - AccountID const& account, - Currency const& currency, - AccountID const& issuer, - FreezeHandling zeroIfFrozen) -{ - STAmount saBalance; - SLE::pointer sleRippleState = entryCache (ltRIPPLE_STATE, - getRippleStateIndex (account, issuer, currency)); - - if (!sleRippleState) - { - saBalance.clear ({currency, issuer}); - } - else if ((zeroIfFrozen == fhZERO_IF_FROZEN) && isFrozen (account, currency, issuer)) - { - saBalance.clear (IssueRef (currency, issuer)); - } - else if (account > issuer) - { - saBalance = sleRippleState->getFieldAmount (sfBalance); - saBalance.negate (); // Put balance in account terms. - - saBalance.setIssuer (issuer); - } - else - { - saBalance = sleRippleState->getFieldAmount (sfBalance); - - saBalance.setIssuer (issuer); - } - - return adjustedBalance(account, issuer, saBalance); -} - -// Returns the amount an account can spend without going into debt. -// -// <-- saAmount: amount of currency held by account. May be negative. -STAmount LedgerEntrySet::accountHolds ( - AccountID const& account, - Currency const& currency, - AccountID const& issuer, - FreezeHandling zeroIfFrozen) -{ - STAmount saAmount; - - if (!currency) - { - SLE::pointer sleAccount = entryCache (ltACCOUNT_ROOT, - getAccountRootIndex (account)); - std::uint64_t uReserve = mLedger->getReserve ( - sleAccount->getFieldU32 (sfOwnerCount)); - - STAmount const saBalance = sleAccount->getFieldAmount (sfBalance); - STAmount const saReserve (uReserve); - - if (saBalance < saReserve) - { - saAmount.clear (); - } - else - { - saAmount = saBalance - saReserve; - } - - WriteLog (lsTRACE, LedgerEntrySet) << "accountHolds:" << - " account=" << to_string (account) << - " saAmount=" << saAmount.getFullText () << - " saBalance=" << saBalance.getFullText () << - " saReserve=" << saReserve.getFullText (); - - return adjustedBalance(account, issuer, saAmount); - } - else - { - saAmount = rippleHolds (account, currency, issuer, zeroIfFrozen); - - WriteLog (lsTRACE, LedgerEntrySet) << "accountHolds:" << - " account=" << to_string (account) << - " saAmount=" << saAmount.getFullText (); - - return saAmount; - } - -} - -bool LedgerEntrySet::isGlobalFrozen (AccountID const& issuer) -{ - if (isXRP (issuer)) - return false; - - SLE::pointer sle = entryCache (ltACCOUNT_ROOT, getAccountRootIndex (issuer)); - if (sle && sle->isFlag (lsfGlobalFreeze)) - return true; - - return false; -} - -// Can the specified account spend the specified currency issued by -// the specified issuer or does the freeze flag prohibit it? -bool LedgerEntrySet::isFrozen( - AccountID const& account, - Currency const& currency, - AccountID const& issuer) -{ - if (isXRP (currency)) - return false; - - SLE::pointer sle = entryCache (ltACCOUNT_ROOT, getAccountRootIndex (issuer)); - if (sle && sle->isFlag (lsfGlobalFreeze)) - return true; - - if (issuer != account) - { - // Check if the issuer froze the line - sle = entryCache (ltRIPPLE_STATE, - getRippleStateIndex (account, issuer, currency)); - if (sle && sle->isFlag ((issuer > account) ? lsfHighFreeze : lsfLowFreeze)) - { - return true; - } - } - - return false; -} - -TER LedgerEntrySet::trustCreate ( - const bool bSrcHigh, - AccountID const& uSrcAccountID, - AccountID const& uDstAccountID, - uint256 const& uIndex, // --> ripple state entry - SLE::ref sleAccount, // --> the account being set. - const bool bAuth, // --> authorize account. - const bool bNoRipple, // --> others cannot ripple through - const bool bFreeze, // --> funds cannot leave - STAmount const& saBalance, // --> balance of account being set. - // Issuer should be noAccount() - STAmount const& saLimit, // --> limit for account being set. - // Issuer should be the account being set. - const std::uint32_t uQualityIn, - const std::uint32_t uQualityOut) -{ - WriteLog (lsTRACE, LedgerEntrySet) - << "trustCreate: " << to_string (uSrcAccountID) << ", " - << to_string (uDstAccountID) << ", " << saBalance.getFullText (); - - auto const& uLowAccountID = !bSrcHigh ? uSrcAccountID : uDstAccountID; - auto const& uHighAccountID = bSrcHigh ? uSrcAccountID : uDstAccountID; - - auto sleRippleState = std::make_shared( - ltRIPPLE_STATE, uIndex); - entryCreate (sleRippleState); - - std::uint64_t uLowNode; - std::uint64_t uHighNode; - - TER terResult = dirAdd ( - uLowNode, - getOwnerDirIndex (uLowAccountID), - sleRippleState->getIndex (), - std::bind (&Ledger::ownerDirDescriber, - std::placeholders::_1, std::placeholders::_2, - uLowAccountID)); - - if (tesSUCCESS == terResult) - { - terResult = dirAdd ( - uHighNode, - getOwnerDirIndex (uHighAccountID), - sleRippleState->getIndex (), - std::bind (&Ledger::ownerDirDescriber, - std::placeholders::_1, std::placeholders::_2, - uHighAccountID)); - } - - if (tesSUCCESS == terResult) - { - const bool bSetDst = saLimit.getIssuer () == uDstAccountID; - const bool bSetHigh = bSrcHigh ^ bSetDst; - - assert (sleAccount->getFieldAccount160 (sfAccount) == - (bSetHigh ? uHighAccountID : uLowAccountID)); - SLE::pointer slePeer = entryCache (ltACCOUNT_ROOT, - getAccountRootIndex (bSetHigh ? uLowAccountID : uHighAccountID)); - assert (slePeer); - - // Remember deletion hints. - sleRippleState->setFieldU64 (sfLowNode, uLowNode); - sleRippleState->setFieldU64 (sfHighNode, uHighNode); - - sleRippleState->setFieldAmount ( - bSetHigh ? sfHighLimit : sfLowLimit, saLimit); - sleRippleState->setFieldAmount ( - bSetHigh ? sfLowLimit : sfHighLimit, - STAmount ({saBalance.getCurrency (), - bSetDst ? uSrcAccountID : uDstAccountID})); - - if (uQualityIn) - sleRippleState->setFieldU32 ( - bSetHigh ? sfHighQualityIn : sfLowQualityIn, uQualityIn); - - if (uQualityOut) - sleRippleState->setFieldU32 ( - bSetHigh ? sfHighQualityOut : sfLowQualityOut, uQualityOut); - - std::uint32_t uFlags = bSetHigh ? lsfHighReserve : lsfLowReserve; - - if (bAuth) - { - uFlags |= (bSetHigh ? lsfHighAuth : lsfLowAuth); - } - if (bNoRipple) - { - uFlags |= (bSetHigh ? lsfHighNoRipple : lsfLowNoRipple); - } - if (bFreeze) - { - uFlags |= (!bSetHigh ? lsfLowFreeze : lsfHighFreeze); - } - - if ((slePeer->getFlags() & lsfDefaultRipple) == 0) - { - // The other side's default is no rippling - uFlags |= (bSetHigh ? lsfLowNoRipple : lsfHighNoRipple); - } - - sleRippleState->setFieldU32 (sfFlags, uFlags); - adjustOwnerCount(*this, sleAccount, 1); - - // ONLY: Create ripple balance. - sleRippleState->setFieldAmount (sfBalance, bSetHigh ? -saBalance : saBalance); - - cacheCredit (uSrcAccountID, uDstAccountID, saBalance); - } - - return terResult; -} - -TER LedgerEntrySet::trustDelete ( - SLE::ref sleRippleState, AccountID const& uLowAccountID, - AccountID const& uHighAccountID) -{ - // Detect legacy dirs. - bool bLowNode = sleRippleState->isFieldPresent (sfLowNode); - bool bHighNode = sleRippleState->isFieldPresent (sfHighNode); - std::uint64_t uLowNode = sleRippleState->getFieldU64 (sfLowNode); - std::uint64_t uHighNode = sleRippleState->getFieldU64 (sfHighNode); - TER terResult; - - WriteLog (lsTRACE, LedgerEntrySet) - << "trustDelete: Deleting ripple line: low"; - terResult = dirDelete ( - false, - uLowNode, - getOwnerDirIndex (uLowAccountID), - sleRippleState->getIndex (), - false, - !bLowNode); - - if (tesSUCCESS == terResult) - { - WriteLog (lsTRACE, LedgerEntrySet) - << "trustDelete: Deleting ripple line: high"; - terResult = dirDelete ( - false, - uHighNode, - getOwnerDirIndex (uHighAccountID), - sleRippleState->getIndex (), - false, - !bHighNode); - } - - WriteLog (lsTRACE, LedgerEntrySet) << "trustDelete: Deleting ripple line: state"; - entryDelete (sleRippleState); - - return terResult; -} - -void LedgerEntrySet::enableDeferredCredits (bool enable) -{ - assert(enable == !mDeferredCredits); - - if (!enable) - { - mDeferredCredits.reset (); - return; - } - - if (!mDeferredCredits) - mDeferredCredits.emplace (); -} - -bool LedgerEntrySet::areCreditsDeferred () const -{ - return static_cast (mDeferredCredits); -} - -STAmount LedgerEntrySet::adjustedBalance (AccountID const& main, - AccountID const& other, - STAmount const& amount) const -{ - if (mDeferredCredits) - return mDeferredCredits->adjustedBalance (main, other, amount); - return amount; -} - -void LedgerEntrySet::cacheCredit (AccountID const& sender, - AccountID const& receiver, - STAmount const& amount) -{ - if (mDeferredCredits) - return mDeferredCredits->credit (sender, receiver, amount); -} - -// Direct send w/o fees: -// - Redeeming IOUs and/or sending sender's own IOUs. -// - Create trust line of needed. -// --> bCheckIssuer : normally require issuer to be involved. -TER LedgerEntrySet::rippleCredit ( - AccountID const& uSenderID, AccountID const& uReceiverID, - STAmount const& saAmount, bool bCheckIssuer) -{ - auto issuer = saAmount.getIssuer (); - auto currency = saAmount.getCurrency (); - - // Make sure issuer is involved. - assert ( - !bCheckIssuer || uSenderID == issuer || uReceiverID == issuer); - (void) issuer; - - // Disallow sending to self. - assert (uSenderID != uReceiverID); - - bool bSenderHigh = uSenderID > uReceiverID; - uint256 uIndex = getRippleStateIndex ( - uSenderID, uReceiverID, saAmount.getCurrency ()); - auto sleRippleState = entryCache (ltRIPPLE_STATE, uIndex); - - TER terResult; - - assert (!isXRP (uSenderID) && uSenderID != noAccount()); - assert (!isXRP (uReceiverID) && uReceiverID != noAccount()); - - if (!sleRippleState) - { - STAmount saReceiverLimit({currency, uReceiverID}); - STAmount saBalance = saAmount; - - saBalance.setIssuer (noAccount()); - - WriteLog (lsDEBUG, LedgerEntrySet) << "rippleCredit: " - "create line: " << to_string (uSenderID) << - " -> " << to_string (uReceiverID) << - " : " << saAmount.getFullText (); - - SLE::pointer sleAccount = entryCache (ltACCOUNT_ROOT, - getAccountRootIndex (uReceiverID)); - - bool noRipple = (sleAccount->getFlags() & lsfDefaultRipple) == 0; - - terResult = trustCreate ( - bSenderHigh, - uSenderID, - uReceiverID, - uIndex, - sleAccount, - false, - noRipple, - false, - saBalance, - saReceiverLimit); - } - else - { - cacheCredit (uSenderID, uReceiverID, saAmount); - - STAmount saBalance = sleRippleState->getFieldAmount (sfBalance); - - if (bSenderHigh) - saBalance.negate (); // Put balance in sender terms. - - STAmount saBefore = saBalance; - - saBalance -= saAmount; - - WriteLog (lsTRACE, LedgerEntrySet) << "rippleCredit: " << - to_string (uSenderID) << - " -> " << to_string (uReceiverID) << - " : before=" << saBefore.getFullText () << - " amount=" << saAmount.getFullText () << - " after=" << saBalance.getFullText (); - - std::uint32_t const uFlags (sleRippleState->getFieldU32 (sfFlags)); - bool bDelete = false; - - // YYY Could skip this if rippling in reverse. - if (saBefore > zero - // Sender balance was positive. - && saBalance <= zero - // Sender is zero or negative. - && (uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) - // Sender reserve is set. - && static_cast (uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) != - static_cast (entryCache (ltACCOUNT_ROOT, - getAccountRootIndex (uSenderID))->getFlags() & lsfDefaultRipple) - && !(uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) - && !sleRippleState->getFieldAmount ( - !bSenderHigh ? sfLowLimit : sfHighLimit) - // Sender trust limit is 0. - && !sleRippleState->getFieldU32 ( - !bSenderHigh ? sfLowQualityIn : sfHighQualityIn) - // Sender quality in is 0. - && !sleRippleState->getFieldU32 ( - !bSenderHigh ? sfLowQualityOut : sfHighQualityOut)) - // Sender quality out is 0. - { - // Clear the reserve of the sender, possibly delete the line! - adjustOwnerCount(*this, entryCache(ltACCOUNT_ROOT, - getAccountRootIndex(uSenderID)), -1); - - // Clear reserve flag. - sleRippleState->setFieldU32 ( - sfFlags, - uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); - - // Balance is zero, receiver reserve is clear. - bDelete = !saBalance // Balance is zero. - && !(uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)); - // Receiver reserve is clear. - } - - if (bSenderHigh) - saBalance.negate (); - - // Want to reflect balance to zero even if we are deleting line. - sleRippleState->setFieldAmount (sfBalance, saBalance); - // ONLY: Adjust ripple balance. - - if (bDelete) - { - terResult = trustDelete ( - sleRippleState, - bSenderHigh ? uReceiverID : uSenderID, - !bSenderHigh ? uReceiverID : uSenderID); - } - else - { - entryModify (sleRippleState); - terResult = tesSUCCESS; - } - } - - return terResult; -} - -// Calculate the fee needed to transfer IOU assets between two parties. -STAmount LedgerEntrySet::rippleTransferFee ( - AccountID const& from, - AccountID const& to, - AccountID const& issuer, - STAmount const& saAmount) -{ - if (from != issuer && to != issuer) - { - std::uint32_t uTransitRate = rippleTransferRate (*this, issuer); - - if (QUALITY_ONE != uTransitRate) - { - STAmount saTransferTotal = multiply ( - saAmount, amountFromRate (uTransitRate), saAmount.issue ()); - STAmount saTransferFee = saTransferTotal - saAmount; - - WriteLog (lsDEBUG, LedgerEntrySet) << "rippleTransferFee:" << - " saTransferFee=" << saTransferFee.getFullText (); - - return saTransferFee; - } - } - - return saAmount.zeroed(); -} - -// Send regardless of limits. -// --> saAmount: Amount/currency/issuer to deliver to reciever. -// <-- saActual: Amount actually cost. Sender pay's fees. -TER LedgerEntrySet::rippleSend ( - AccountID const& uSenderID, AccountID const& uReceiverID, - STAmount const& saAmount, STAmount& saActual) -{ - auto const issuer = saAmount.getIssuer (); - TER terResult; - - assert (!isXRP (uSenderID) && !isXRP (uReceiverID)); - assert (uSenderID != uReceiverID); - - if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount()) - { - // Direct send: redeeming IOUs and/or sending own IOUs. - terResult = rippleCredit (uSenderID, uReceiverID, saAmount, false); - saActual = saAmount; - terResult = tesSUCCESS; - } - else - { - // Sending 3rd party IOUs: transit. - - STAmount saTransitFee = rippleTransferFee ( - uSenderID, uReceiverID, issuer, saAmount); - - saActual = !saTransitFee ? saAmount : saAmount + saTransitFee; - - saActual.setIssuer (issuer); // XXX Make sure this done in + above. - - WriteLog (lsDEBUG, LedgerEntrySet) << "rippleSend> " << - to_string (uSenderID) << - " - > " << to_string (uReceiverID) << - " : deliver=" << saAmount.getFullText () << - " fee=" << saTransitFee.getFullText () << - " cost=" << saActual.getFullText (); - - terResult = rippleCredit (issuer, uReceiverID, saAmount); - - if (tesSUCCESS == terResult) - terResult = rippleCredit (uSenderID, issuer, saActual); - } - - return terResult; -} - -TER LedgerEntrySet::accountSend ( - AccountID const& uSenderID, AccountID const& uReceiverID, - STAmount const& saAmount) -{ - assert (saAmount >= zero); - - /* If we aren't sending anything or if the sender is the same as the - * receiver then we don't need to do anything. - */ - if (!saAmount || (uSenderID == uReceiverID)) - return tesSUCCESS; - - if (!saAmount.native ()) - { - STAmount saActual; - - WriteLog (lsTRACE, LedgerEntrySet) << "accountSend: " << - to_string (uSenderID) << " -> " << to_string (uReceiverID) << - " : " << saAmount.getFullText (); - - return rippleSend (uSenderID, uReceiverID, saAmount, saActual); - } - - cacheCredit (uSenderID, uReceiverID, saAmount); - - /* XRP send which does not check reserve and can do pure adjustment. - * Note that sender or receiver may be null and this not a mistake; this - * setup is used during pathfinding and it is carefully controlled to - * ensure that transfers are balanced. - */ - - TER terResult (tesSUCCESS); - - SLE::pointer sender = uSenderID != beast::zero - ? entryCache (ltACCOUNT_ROOT, getAccountRootIndex (uSenderID)) - : SLE::pointer (); - SLE::pointer receiver = uReceiverID != beast::zero - ? entryCache (ltACCOUNT_ROOT, getAccountRootIndex (uReceiverID)) - : SLE::pointer (); - - if (ShouldLog (lsTRACE, LedgerEntrySet)) - { - std::string sender_bal ("-"); - std::string receiver_bal ("-"); - - if (sender) - sender_bal = sender->getFieldAmount (sfBalance).getFullText (); - - if (receiver) - receiver_bal = receiver->getFieldAmount (sfBalance).getFullText (); - - WriteLog (lsTRACE, LedgerEntrySet) << "accountSend> " << - to_string (uSenderID) << " (" << sender_bal << - ") -> " << to_string (uReceiverID) << " (" << receiver_bal << - ") : " << saAmount.getFullText (); - } - - if (sender) - { - if (sender->getFieldAmount (sfBalance) < saAmount) - { - terResult = (mParams & tapOPEN_LEDGER) - ? telFAILED_PROCESSING - : tecFAILED_PROCESSING; - } - else - { - // Decrement XRP balance. - sender->setFieldAmount (sfBalance, - sender->getFieldAmount (sfBalance) - saAmount); - entryModify (sender); - } - } - - if (tesSUCCESS == terResult && receiver) - { - // Increment XRP balance. - receiver->setFieldAmount (sfBalance, - receiver->getFieldAmount (sfBalance) + saAmount); - entryModify (receiver); - } - - if (ShouldLog (lsTRACE, LedgerEntrySet)) - { - std::string sender_bal ("-"); - std::string receiver_bal ("-"); - - if (sender) - sender_bal = sender->getFieldAmount (sfBalance).getFullText (); - - if (receiver) - receiver_bal = receiver->getFieldAmount (sfBalance).getFullText (); - - WriteLog (lsTRACE, LedgerEntrySet) << "accountSend< " << - to_string (uSenderID) << " (" << sender_bal << - ") -> " << to_string (uReceiverID) << " (" << receiver_bal << - ") : " << saAmount.getFullText (); - } - - return terResult; -} - -bool LedgerEntrySet::checkState ( - SLE::pointer state, - bool bSenderHigh, - AccountID const& sender, - STAmount const& before, - STAmount const& after) -{ - std::uint32_t const flags (state->getFieldU32 (sfFlags)); - - auto sle = entryCache (ltACCOUNT_ROOT, - getAccountRootIndex (sender)); - assert (sle); - - // YYY Could skip this if rippling in reverse. - if (before > zero - // Sender balance was positive. - && after <= zero - // Sender is zero or negative. - && (flags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) - // Sender reserve is set. - && static_cast (flags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) != - static_cast (sle->getFlags() & lsfDefaultRipple) - && !(flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) - && !state->getFieldAmount ( - !bSenderHigh ? sfLowLimit : sfHighLimit) - // Sender trust limit is 0. - && !state->getFieldU32 ( - !bSenderHigh ? sfLowQualityIn : sfHighQualityIn) - // Sender quality in is 0. - && !state->getFieldU32 ( - !bSenderHigh ? sfLowQualityOut : sfHighQualityOut)) - // Sender quality out is 0. - { - // VFALCO Where is the line being deleted? - // Clear the reserve of the sender, possibly delete the line! - adjustOwnerCount(*this, sle, -1); - - // Clear reserve flag. - state->setFieldU32 (sfFlags, - flags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); - - // Balance is zero, receiver reserve is clear. - if (!after // Balance is zero. - && !(flags & (bSenderHigh ? lsfLowReserve : lsfHighReserve))) - return true; - } - - return false; -} - -TER LedgerEntrySet::issue_iou ( - AccountID const& account, - STAmount const& amount, - Issue const& issue) -{ - assert (!isXRP (account) && !isXRP (issue.account)); - - // Consistency check - assert (issue == amount.issue ()); - - // Can't send to self! - assert (issue.account != account); - - WriteLog (lsTRACE, LedgerEntrySet) << "issue_iou: " << - to_string (account) << ": " << - amount.getFullText (); - - bool bSenderHigh = issue.account > account; - uint256 const index = getRippleStateIndex ( - issue.account, account, issue.currency); - auto state = entryCache (ltRIPPLE_STATE, index); - - if (!state) - { - // NIKB TODO: The limit uses the receiver's account as the issuer and - // this is unnecessarily inefficient as copying which could be avoided - // is now required. Consider available options. - STAmount limit({issue.currency, account}); - STAmount final_balance = amount; - - final_balance.setIssuer (noAccount()); - - SLE::pointer receiverAccount = entryCache (ltACCOUNT_ROOT, - getAccountRootIndex (account)); - - bool noRipple = (receiverAccount->getFlags() & lsfDefaultRipple) == 0; - - return trustCreate (bSenderHigh, issue.account, account, index, - receiverAccount, false, noRipple, false, final_balance, limit); - } - - STAmount final_balance = state->getFieldAmount (sfBalance); - - if (bSenderHigh) - final_balance.negate (); // Put balance in sender terms. - - STAmount const start_balance = final_balance; - - final_balance -= amount; - - auto const must_delete = checkState (state, bSenderHigh, issue.account, - start_balance, final_balance); - - if (bSenderHigh) - final_balance.negate (); - - cacheCredit (issue.account, account, amount); - - // Adjust the balance on the trust line if necessary. We do this even if we - // are going to delete the line to reflect the correct balance at the time - // of deletion. - state->setFieldAmount (sfBalance, final_balance); - - if (must_delete) - { - return trustDelete (state, - bSenderHigh ? account : issue.account, - bSenderHigh ? issue.account : account); - } - - entryModify (state); - return tesSUCCESS; -} - -TER LedgerEntrySet::redeem_iou ( - AccountID const& account, - STAmount const& amount, - Issue const& issue) -{ - assert (!isXRP (account) && !isXRP (issue.account)); - - // Consistency check - assert (issue == amount.issue ()); - - // Can't send to self! - assert (issue.account != account); - - WriteLog (lsTRACE, LedgerEntrySet) << "redeem_iou: " << - to_string (account) << ": " << - amount.getFullText (); - - bool bSenderHigh = account > issue.account; - uint256 const index = getRippleStateIndex ( - account, issue.account, issue.currency); - auto state = entryCache (ltRIPPLE_STATE, index); - - if (!state) - { - // In order to hold an IOU, a trust line *MUST* exist to track the - // balance. If it doesn't, then something is very wrong. Don't try - // to continue. - WriteLog (lsFATAL, LedgerEntrySet) << "redeem_iou: " << - to_string (account) << " attempts to redeem " << - amount.getFullText () << " but no trust line exists!"; - - return tefINTERNAL; - } - - STAmount final_balance = state->getFieldAmount (sfBalance); - - if (bSenderHigh) - final_balance.negate (); // Put balance in sender terms. - - STAmount const start_balance = final_balance; - - final_balance -= amount; - - auto const must_delete = checkState (state, bSenderHigh, account, - start_balance, final_balance); - - if (bSenderHigh) - final_balance.negate (); - - cacheCredit (account, issue.account, amount); - - // Adjust the balance on the trust line if necessary. We do this even if we - // are going to delete the line to reflect the correct balance at the time - // of deletion. - state->setFieldAmount (sfBalance, final_balance); - - if (must_delete) - { - return trustDelete (state, - bSenderHigh ? issue.account : account, - bSenderHigh ? account : issue.account); - } - - entryModify (state); - return tesSUCCESS; -} - -TER LedgerEntrySet::transfer_xrp ( - AccountID const& from, - AccountID const& to, - STAmount const& amount) -{ - assert (from != beast::zero); - assert (to != beast::zero); - assert (from != to); - assert (amount.native ()); - - SLE::pointer sender = entryCache (ltACCOUNT_ROOT, - getAccountRootIndex (from)); - SLE::pointer receiver = entryCache (ltACCOUNT_ROOT, - getAccountRootIndex (to)); - - WriteLog (lsTRACE, LedgerEntrySet) << "transfer_xrp: " << - to_string (from) << " -> " << to_string (to) << - ") : " << amount.getFullText (); - - if (sender->getFieldAmount (sfBalance) < amount) - { - // FIXME: this logic should be moved to callers maybe? - return (mParams & tapOPEN_LEDGER) - ? telFAILED_PROCESSING - : tecFAILED_PROCESSING; - } - - // Decrement XRP balance. - sender->setFieldAmount (sfBalance, - sender->getFieldAmount (sfBalance) - amount); - entryModify (sender); - - receiver->setFieldAmount (sfBalance, - receiver->getFieldAmount (sfBalance) + amount); - entryModify (receiver); - - return tesSUCCESS; -} - -std::uint32_t -rippleTransferRate (LedgerEntrySet& ledger, AccountID const& issuer) -{ - SLE::pointer sleAccount (ledger.entryCache ( - ltACCOUNT_ROOT, getAccountRootIndex (issuer))); - - std::uint32_t quality = QUALITY_ONE; - - if (sleAccount && sleAccount->isFieldPresent (sfTransferRate)) - quality = sleAccount->getFieldU32 (sfTransferRate); - - return quality; -} - -std::uint32_t -rippleTransferRate (LedgerEntrySet& ledger, AccountID const& uSenderID, - AccountID const& uReceiverID, AccountID const& issuer) -{ - // If calculating the transfer rate from or to the issuer of the currency - // no fees are assessed. - return (uSenderID == issuer || uReceiverID == issuer) - ? QUALITY_ONE - : rippleTransferRate (ledger, issuer); -} - -ScopedDeferCredits::ScopedDeferCredits (LedgerEntrySet& l) - : les_ (l), enabled_ (false) -{ - if (!les_.areCreditsDeferred ()) - { - WriteLog (lsTRACE, DeferredCredits) << "Enable"; - les_.enableDeferredCredits (true); - enabled_ = true; - } -} - -ScopedDeferCredits::~ScopedDeferCredits () -{ - if (enabled_) - { - WriteLog (lsTRACE, DeferredCredits) << "Disable"; - les_.enableDeferredCredits (false); - } -} - -//------------------------------------------------------------------------------ - -void -adjustOwnerCount (LedgerEntrySet& view, - std::shared_ptr const& sle, int amount) -{ - assert(amount != 0); - auto const current = - sle->getFieldU32 (sfOwnerCount); - auto adjusted = current + amount; - if (amount > 0) - { - // Overflow is well defined on unsigned - if (adjusted < current) - { - WriteLog (lsFATAL, LedgerEntrySet) << - "Account " << sle->getFieldAccount160(sfAccount) << - " owner count exceeds max!"; - adjusted = - std::numeric_limits::max (); - } - } - else - { - // Underflow is well defined on unsigned - if (adjusted > current) - { - WriteLog (lsFATAL, LedgerEntrySet) << - "Account " << sle->getFieldAccount160 (sfAccount) << - " owner count set below 0!"; - adjusted = 0; - assert(false); - } - } - sle->setFieldU32 (sfOwnerCount, adjusted); - view.entryModify (sle); -} - -STAmount -funds (LedgerEntrySet& view, AccountID const& id, - STAmount const& saDefault, FreezeHandling freezeHandling) -{ - STAmount saFunds; - - if (!saDefault.native () && - saDefault.getIssuer () == id) - { - saFunds = saDefault; - WriteLog (lsTRACE, LedgerEntrySet) << "accountFunds:" << - " account=" << to_string (id) << - " saDefault=" << saDefault.getFullText () << - " SELF-FUNDED"; - } - else - { - saFunds = view.accountHolds(id, - saDefault.getCurrency(), saDefault.getIssuer(), - freezeHandling); - WriteLog (lsTRACE, LedgerEntrySet) << "accountFunds:" << - " account=" << to_string (id) << - " saDefault=" << saDefault.getFullText () << - " saFunds=" << saFunds.getFullText (); - } - return saFunds; -} - -} // ripple diff --git a/src/ripple/app/ledger/LedgerEntrySet.h b/src/ripple/app/ledger/LedgerEntrySet.h deleted file mode 100644 index 80b5ad58af..0000000000 --- a/src/ripple/app/ledger/LedgerEntrySet.h +++ /dev/null @@ -1,370 +0,0 @@ -//------------------------------------------------------------------------------ -/* - 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_APP_LEDGER_LEDGERENTRYSET_H_INCLUDED -#define RIPPLE_APP_LEDGER_LEDGERENTRYSET_H_INCLUDED - -#include -#include -#include -#include -#include -#include -#include - -namespace ripple { - -// VFALCO Does this belong here? Is it correctly named? - -enum TransactionEngineParams -{ - tapNONE = 0x00, - - // Signature already checked - tapNO_CHECK_SIGN = 0x01, - - // Transaction is running against an open ledger - // true = failures are not forwarded, check transaction fee - // false = debit ledger for consumed funds - tapOPEN_LEDGER = 0x10, - - // This is not the transaction's last pass - // Transaction can be retried, soft failures allowed - tapRETRY = 0x20, - - // Transaction came from a privileged source - tapADMIN = 0x400, -}; - -enum FreezeHandling -{ - fhIGNORE_FREEZE, - fhZERO_IF_FROZEN -}; - -/** An LES is a LedgerEntrySet. - - It's a view into a ledger used while a transaction is processing. - The transaction manipulates the LES rather than the ledger - (because it's cheaper, can be checkpointed, and so on). When the - transaction finishes, the LES is committed into the ledger to make - the modifications. The transaction metadata is built from the LES too. -*/ -class LedgerEntrySet -{ -private: - using NodeToLedgerEntry = - hash_map; - - enum Action - { - taaNONE, - taaCACHED, // Unmodified. - taaMODIFY, // Modifed, must have previously been taaCACHED. - taaDELETE, // Delete, must have previously been taaDELETE or taaMODIFY. - taaCREATE, // Newly created. - }; - - class Item - { - public: - int mSeq; - Action mAction; - std::shared_ptr mEntry; - - Item (SLE::ref e, Action a, int s) - : mSeq (s) - , mAction (a) - , mEntry (e) - { - } - }; - - Ledger::pointer mLedger; - // Implementation requires an ordered container - std::map mEntries; - boost::optional mDeferredCredits; - TransactionMetaSet mSet; - TransactionEngineParams mParams = tapNONE; - int mSeq = 0; - bool mImmutable = false; - -public: - LedgerEntrySet& operator= (LedgerEntrySet const&) = delete; - - /** Construct a copy. - Effects: - The copy is identical except that - the sequence number is one higher. - */ - LedgerEntrySet (LedgerEntrySet const&); - - LedgerEntrySet (Ledger::ref ledger, - uint256 const& transactionID, - std::uint32_t ledgerID, - TransactionEngineParams params); - - LedgerEntrySet (Ledger::ref ledger, - TransactionEngineParams tep, - bool immutable = false); - - /** Apply changes to the backing ledger. */ - void - apply(); - - // Swap the contents of two sets - void swapWith (LedgerEntrySet&); - - // VFALCO Only called from RippleCalc.cpp - void deprecatedInvalidate() - { - mLedger.reset (); - mDeferredCredits.reset (); - } - - bool isValid () const - { - return mLedger != nullptr; - } - - int getSeq () const - { - return mSeq; - } - - void bumpSeq () - { - ++mSeq; - } - - Ledger::pointer& getLedger () - { - return mLedger; - } - - void entryCache (SLE::ref); // Add this entry to the cache - void entryCreate (SLE::ref); // This entry will be created - void entryDelete (SLE::ref); // This entry will be deleted - void entryModify (SLE::ref); // This entry will be modified - - // higher-level ledger functions - SLE::pointer entryCache (LedgerEntryType letType, uint256 const& key); - - std::shared_ptr - entryCacheI (LedgerEntryType letType, uint256 const& uIndex); - - // Directory functions. - TER dirAdd ( - std::uint64_t& uNodeDir, // Node of entry. - uint256 const& uRootIndex, - uint256 const& uLedgerIndex, - std::function fDescriber); - - TER dirDelete ( - const bool bKeepRoot, - const std::uint64_t& uNodeDir, // Node item is mentioned in. - uint256 const& uRootIndex, - uint256 const& uLedgerIndex, // Item being deleted - const bool bStable, - const bool bSoft); - - bool dirFirst (uint256 const& uRootIndex, SLE::pointer& sleNode, - unsigned int & uDirEntry, uint256 & uEntryIndex); - - bool dirFirst (uint256 const& uRootIndex, std::shared_ptr& sleNode, - unsigned int & uDirEntry, uint256 & uEntryIndex); - - bool dirNext (uint256 const& uRootIndex, SLE::pointer& sleNode, - unsigned int & uDirEntry, uint256 & uEntryIndex); - - bool dirNext (uint256 const& uRootIndex, std::shared_ptr& sleNode, - unsigned int & uDirEntry, uint256 & uEntryIndex); - - bool dirIsEmpty (uint256 const& uDirIndex); - - uint256 getNextLedgerIndex (uint256 const& uHash); - uint256 getNextLedgerIndex (uint256 const& uHash, uint256 const& uEnd); - - // Offer functions. - TER offerDelete (SLE::pointer); - - TER offerDelete (uint256 const& offerIndex) - { - return offerDelete( entryCache (ltOFFER, offerIndex)); - } - - // Balance functions. - bool isFrozen ( - AccountID const& account, - Currency const& currency, - AccountID const& issuer); - - bool isGlobalFrozen (AccountID const& issuer); - - void enableDeferredCredits (bool enable=true); - - bool areCreditsDeferred () const; - - TER rippleCredit ( - AccountID const& uSenderID, AccountID const& uReceiverID, - const STAmount & saAmount, bool bCheckIssuer = true); - - STAmount accountHolds ( - AccountID const& account, Currency const& currency, - AccountID const& issuer, FreezeHandling freezeHandling); - - TER accountSend ( - AccountID const& uSenderID, AccountID const& uReceiverID, - const STAmount & saAmount); - - TER trustCreate ( - const bool bSrcHigh, - AccountID const& uSrcAccountID, - AccountID const& uDstAccountID, - uint256 const& uIndex, - SLE::ref sleAccount, - const bool bAuth, - const bool bNoRipple, - const bool bFreeze, - STAmount const& saSrcBalance, - STAmount const& saSrcLimit, - const std::uint32_t uSrcQualityIn = 0, - const std::uint32_t uSrcQualityOut = 0); - - TER trustDelete ( - SLE::ref sleRippleState, AccountID const& uLowAccountID, - AccountID const& uHighAccountID); - - Json::Value getJson (int) const; - - void calcRawMeta (Serializer&, TER result, std::uint32_t index); - - void setDeliveredAmount (STAmount const& amt) - { - mSet.setDeliveredAmount (amt); - } - - TER issue_iou (AccountID const& account, - STAmount const& amount, Issue const& issue); - - TER redeem_iou (AccountID const& account, - STAmount const& amount, Issue const& issue); - - TER transfer_xrp (AccountID const& from, AccountID const& to, STAmount const& amount); - -private: - SLE::pointer getEntry (uint256 const& index, Action&); - - SLE::pointer getForMod ( - uint256 const& node, Ledger::ref ledger, - NodeToLedgerEntry& newMods); - - bool threadTx ( - const RippleAddress & threadTo, Ledger::ref ledger, - NodeToLedgerEntry& newMods); - - bool threadTx ( - SLE::ref threadTo, Ledger::ref ledger, NodeToLedgerEntry& newMods); - - bool threadOwners (std::shared_ptr const& node, - Ledger::ref ledger, NodeToLedgerEntry& newMods); - - TER rippleSend ( - AccountID const& uSenderID, AccountID const& uReceiverID, - const STAmount & saAmount, STAmount & saActual); - - STAmount rippleHolds ( - AccountID const& account, Currency const& currency, - AccountID const& issuer, FreezeHandling zeroIfFrozen); - - STAmount rippleTransferFee ( - AccountID const& from, AccountID const& to, - AccountID const& issuer, STAmount const& saAmount); - - bool checkState (SLE::pointer state, bool bSenderHigh, - AccountID const& sender, STAmount const& before, STAmount const& after); - - STAmount adjustedBalance (AccountID const& main, - AccountID const& other, - STAmount const& amount) const; - - void cacheCredit (AccountID const& sender, - AccountID const& receiver, - STAmount const& amount); -}; - -template -inline -void -reconstruct (LedgerEntrySet& v, Args&&... args) noexcept -{ - v.~LedgerEntrySet(); - new(&v) LedgerEntrySet( - std::forward(args)...); -} - -using LedgerView = LedgerEntrySet; - -//------------------------------------------------------------------------------ - -class ScopedDeferCredits -{ -private: - LedgerEntrySet& les_; - bool enabled_; -public: - ScopedDeferCredits(LedgerEntrySet& l); - ~ScopedDeferCredits (); -}; - -// NIKB FIXME: move these to the right place -std::uint32_t -rippleTransferRate (LedgerEntrySet& ledger, AccountID const& issuer); - -std::uint32_t -rippleTransferRate (LedgerEntrySet& ledger, AccountID const& uSenderID, - AccountID const& uReceiverID, AccountID const& issuer); - -//------------------------------------------------------------------------------ -// -// API -// -//------------------------------------------------------------------------------ - -/** Adjust the owner count up or down. */ -void -adjustOwnerCount (LedgerEntrySet& view, - std::shared_ptr const& sle, int amount); - -// Returns the funds available for account for a currency/issuer. -// Use when you need a default for rippling account's currency. -// XXX Should take into account quality? -// --> saDefault/currency/issuer -// <-- saFunds: Funds available. May be negative. -// -// If the issuer is the same as account, funds are unlimited, use result is -// saDefault. -STAmount -funds (LedgerEntrySet& view, AccountID const& id, - STAmount const& saDefault, - FreezeHandling freezeHandling); - -} // ripple - -#endif diff --git a/src/ripple/app/ledger/LedgerHistory.cpp b/src/ripple/app/ledger/LedgerHistory.cpp index 7535f11c07..ccacf29397 100644 --- a/src/ripple/app/ledger/LedgerHistory.cpp +++ b/src/ripple/app/ledger/LedgerHistory.cpp @@ -51,7 +51,7 @@ LedgerHistory::LedgerHistory ( bool LedgerHistory::addLedger (Ledger::pointer ledger, bool validated) { assert (ledger && ledger->isImmutable ()); - assert (ledger->peekAccountStateMap ()->getHash ().isNonZero ()); + assert (ledger->stateMap().getHash ().isNonZero ()); LedgersByHash::ScopedLockType sl (m_ledgers_by_hash.peekMutex ()); @@ -134,7 +134,7 @@ void log_one(Ledger::pointer ledger, uint256 const& tx, char const* msg) { TransactionMetaSet::pointer metaData; - ledger->getTransactionMeta(tx, metaData); + getTransactionMeta(*ledger, tx, metaData); if (metaData != nullptr) { @@ -155,9 +155,9 @@ log_metadata_difference(Ledger::pointer builtLedger, Ledger::pointer validLedger uint256 const& tx) { TransactionMetaSet::pointer validMetaData; - validLedger->getTransactionMeta(tx, validMetaData); + getTransactionMeta(*validLedger, tx, validMetaData); TransactionMetaSet::pointer builtMetaData; - builtLedger->getTransactionMeta(tx, builtMetaData); + getTransactionMeta(*builtLedger, tx, builtMetaData); assert(validMetaData != nullptr || builtMetaData != nullptr); if (validMetaData != nullptr && builtMetaData != nullptr) @@ -309,13 +309,13 @@ void LedgerHistory::handleMismatch (LedgerHash const& built, LedgerHash const& v using SHAMapItemInfo = std::pair; std::vector builtTx, validTx; // Get built ledger hashes and metadata - builtLedger->peekTransactionMap()->visitLeaves( + builtLedger->txMap().visitLeaves( [&builtTx](std::shared_ptr const& item) { builtTx.push_back({item->getTag(), item->peekData()}); }); // Get valid ledger hashes and metadata - validLedger->peekTransactionMap()->visitLeaves( + validLedger->txMap().visitLeaves( [&validTx](std::shared_ptr const& item) { validTx.push_back({item->getTag(), item->peekData()}); diff --git a/src/ripple/app/ledger/LedgerMaster.h b/src/ripple/app/ledger/LedgerMaster.h index 7af43d5c68..1c5735f993 100644 --- a/src/ripple/app/ledger/LedgerMaster.h +++ b/src/ripple/app/ledger/LedgerMaster.h @@ -20,7 +20,7 @@ #ifndef RIPPLE_APP_LEDGER_LEDGERMASTER_H_INCLUDED #define RIPPLE_APP_LEDGER_LEDGERMASTER_H_INCLUDED -#include +#include #include #include #include diff --git a/src/ripple/app/ledger/LedgerToJson.h b/src/ripple/app/ledger/LedgerToJson.h index 1daf4109ba..4457a88a01 100644 --- a/src/ripple/app/ledger/LedgerToJson.h +++ b/src/ripple/app/ledger/LedgerToJson.h @@ -120,16 +120,16 @@ void fillJson (Object& json, LedgerFill const& fill) json[jss::closed] = false; } - auto &transactionMap = ledger.peekTransactionMap(); - if (transactionMap && (bFull || fill.options & LedgerFill::dumpTxrp)) + if (ledger.haveTxMap() && (bFull || fill.options & LedgerFill::dumpTxrp)) { + auto const& txMap = ledger.txMap(); auto&& txns = setArray (json, jss::transactions); SHAMapTreeNode::TNType type; CountedYield count ( fill.yieldStrategy.transactionYieldCount, fill.yield); - for (auto item = transactionMap->peekFirstItem (type); item; - item = transactionMap->peekNextItem (item->getTag (), type)) + for (auto item = txMap.peekFirstItem (type); item; + item = txMap.peekNextItem (item->getTag (), type)) { count.yield(); if (bFull || bExpand) @@ -187,9 +187,9 @@ void fillJson (Object& json, LedgerFill const& fill) } } - auto& accountStateMap = ledger.peekAccountStateMap(); - if (accountStateMap && (bFull || fill.options & LedgerFill::dumpState)) + if (ledger.haveStateMap() && (bFull || fill.options & LedgerFill::dumpState)) { + auto const& stateMap = ledger.stateMap(); auto&& array = Json::setArray (json, jss::accountState); RPC::CountedYield count ( fill.yieldStrategy.accountYieldCount, fill.yield); @@ -197,7 +197,7 @@ void fillJson (Object& json, LedgerFill const& fill) { if (bBinary) { - ledger.peekAccountStateMap()->visitLeaves ( + stateMap.visitLeaves ( [&array] (std::shared_ptr const& smi) { auto&& obj = appendObject (array); @@ -217,7 +217,7 @@ void fillJson (Object& json, LedgerFill const& fill) } else { - accountStateMap->visitLeaves( + stateMap.visitLeaves( [&array, &count] (std::shared_ptr const& smi) { count.yield(); diff --git a/src/ripple/app/ledger/MetaView.h b/src/ripple/app/ledger/MetaView.h new file mode 100644 index 0000000000..8c19510042 --- /dev/null +++ b/src/ripple/app/ledger/MetaView.h @@ -0,0 +1,272 @@ +//------------------------------------------------------------------------------ +/* + 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_LEDGER_METAVIEW_H_INCLUDED +#define RIPPLE_LEDGER_METAVIEW_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ripple { + +// VFALCO Does this belong here? Is it correctly named? + +enum TransactionEngineParams +{ + tapNONE = 0x00, + + // Signature already checked + tapNO_CHECK_SIGN = 0x01, + + // Transaction is running against an open ledger + // true = failures are not forwarded, check transaction fee + // false = debit ledger for consumed funds + tapOPEN_LEDGER = 0x10, + + // This is not the transaction's last pass + // Transaction can be retried, soft failures allowed + tapRETRY = 0x20, + + // Transaction came from a privileged source + tapADMIN = 0x400, +}; + +/** A MetaView can produce tx metadata and is attached to a parent. + + It's a view into a ledger used while a transaction is processing. + The transaction manipulates the MetaView rather than the ledger + (because it's cheaper, can be checkpointed, and so on). When the + transaction finishes, the MetaView is committed into the ledger to make + the modifications. The transaction metadata is built from the LES too. +*/ +class MetaView : public View +{ +private: + using Mods = + hash_map; + + enum Action + { + taaCACHED, // Unmodified. + taaMODIFY, // Modifed, must have previously been taaCACHED. + taaDELETE, // Delete, must have previously been taaDELETE or taaMODIFY. + taaCREATE, // Newly created. + }; + + class Item + { + public: + int mSeq; + Action mAction; + std::shared_ptr mEntry; + + Item (SLE::ref e, Action a, int s) + : mSeq (s) + , mAction (a) + , mEntry (e) + { + } + }; + + using list_type = std::map; + + BasicView* parent_; + list_type items_; + boost::optional mDeferredCredits; + TransactionMetaSet mSet; + TransactionEngineParams mParams = tapNONE; + int mSeq = 0; + +public: + MetaView& operator= (MetaView const&) = delete; + + MetaView (Ledger::ref ledger, + uint256 const& transactionID, + std::uint32_t ledgerID, + TransactionEngineParams params); + + MetaView (BasicView& parent, + bool openLedger); + + // DEPRECATED + MetaView (Ledger::ref ledger, + TransactionEngineParams tep); + + /** Construct a copy. + Effects: + The copy is identical except that + the sequence number is one higher. + */ + // DEPRECATED + MetaView (MetaView const&); + + //-------------------------------------------------------------------------- + // + // View + // + //-------------------------------------------------------------------------- + + bool + exists (Keylet const& k) const override; + + boost::optional + succ (uint256 const& key, boost::optional< + uint256> last = boost::none) const override; + + std::shared_ptr + read (Keylet const& k) const override; + + bool + unchecked_erase( + uint256 const& key) override; + + void + unchecked_insert ( + std::shared_ptr&& sle) override; + + void + unchecked_replace( + std::shared_ptr&& sle) override; + + BasicView const* + parent() const override + { + return parent_; + } + + STAmount + deprecatedBalance (AccountID const& account, + AccountID const& issuer, + STAmount const& amount) const override; + + //--------------------------------------------- + + std::shared_ptr + peek (Keylet const& k) override; + + void + erase (std::shared_ptr const& sle) override; + + void + insert (std::shared_ptr const& sle) override; + + void + update (std::shared_ptr const& sle) override; + + bool + openLedger() const override; + + void + deprecatedCreditHint (AccountID const& from, + AccountID const& to, STAmount const& amount) override; + + //-------------------------------------------------------------------------- + + /** Apply changes to the parent View */ + void + apply(); + + // Swap the contents of two sets + void swapWith (MetaView&); + + // VFALCO Only called from RippleCalc.cpp + void deprecatedInvalidate() + { + parent_ = nullptr; + mDeferredCredits.reset (); + } + + bool isValid () const + { + return parent_ != nullptr; + } + + void bumpSeq () + { + ++mSeq; + } + + void enableDeferredCredits (bool enable=true); + + bool areCreditsDeferred () const; + + // For diagnostics + Json::Value getJson (int) const; + + void calcRawMeta (Serializer&, TER result, std::uint32_t index); + + void setDeliveredAmount (STAmount const& amt) + { + mSet.setDeliveredAmount (amt); + } + +private: + std::shared_ptr const& + copyOnRead (list_type::iterator iter); + + std::shared_ptr + getForMod (uint256 const& key, + Mods& mods); + + bool + threadTx (RippleAddress const& to, + Mods& mods); + + bool + threadTx (std::shared_ptr const& to, + Mods& mods); + + bool + threadOwners (std::shared_ptr< + SLE const> const& sle, Mods& mods); +}; + +// DEPRECATED Temporary measure, remove ASAP +template +inline +void +reconstruct (MetaView& v, Args&&... args) noexcept +{ + v.~MetaView(); + new(&v) MetaView( + std::forward(args)...); +} + +//------------------------------------------------------------------------------ + +class ScopedDeferCredits +{ +private: + MetaView& les_; + bool enabled_; +public: + ScopedDeferCredits(MetaView& l); + ~ScopedDeferCredits (); +}; + +} // ripple + +#endif diff --git a/src/ripple/app/ledger/OrderBookIterator.cpp b/src/ripple/app/ledger/OrderBookIterator.cpp deleted file mode 100644 index 74e1ffc167..0000000000 --- a/src/ripple/app/ledger/OrderBookIterator.cpp +++ /dev/null @@ -1,237 +0,0 @@ -//------------------------------------------------------------------------------ -/* - 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 { - -/** Iterate through the directories in an order book */ -BookDirIterator::BookDirIterator( - Currency const& currencyIn, AccountID const& issuerIn, - Currency const& currencyOut, AccountID const& issuerOut) -{ - mBase = ripple::getBookBase({{currencyIn, issuerIn}, - {currencyOut, issuerOut}}); - mEnd = getQualityNext(mBase); - mIndex = mBase; -} - -bool BookDirIterator::nextDirectory (LedgerEntrySet& les) -{ - WriteLog (lsTRACE, Ledger) << "BookDirectoryIterator:: nextDirectory"; - - // Are we already at the end? - if (mIndex.isZero ()) - return false; - - // Get the ledger index of the next directory - mIndex = les.getNextLedgerIndex (mIndex, mEnd); - - if (mIndex.isZero ()) - { - // We ran off the end of the book - WriteLog (lsTRACE, Ledger) << - "BookDirectoryIterator:: no next ledger index"; - return false; - } - assert (mIndex < mEnd); - - WriteLog (lsTRACE, Ledger) << - "BookDirectoryIterator:: index " << to_string (mIndex); - - // Retrieve the SLE from the LES - mOfferDir = les.entryCache (ltDIR_NODE, mIndex); - assert (mOfferDir); - - return bool(mOfferDir); -} - -bool BookDirIterator::firstDirectory (LedgerEntrySet& les) -{ - WriteLog (lsTRACE, Ledger) << - "BookDirIterator(" << to_string (mBase) << ") firstDirectory"; - - /** Jump to the beginning - */ - mIndex = mBase; - - return nextDirectory (les); -} - -/** The LES may have changed. Repoint to the current directory if it still exists, - Otherwise, go to the next one. -*/ -bool BookDirIterator::resync (LedgerEntrySet& les) -{ - if (mIndex.isZero ()) - mIndex = mBase; - else if (mIndex != mBase) - --mIndex; - - return nextDirectory (les); -} - -DirectoryEntryIterator BookDirIterator::getOfferIterator () const -{ - WriteLog (lsTRACE, Ledger) << "BookDirIterator(" << - to_string (mBase) << ") get offer iterator"; - return DirectoryEntryIterator (mOfferDir); -} - -std::uint64_t BookDirIterator::getRate () const -{ - return getQuality(mIndex); -} - -bool BookDirIterator::addJson (Json::Value& jv) const -{ - if (! (*this)) - return false; - - jv["book_index"] = to_string (mIndex); - return true; -} - -bool BookDirIterator::setJson(Json::Value const& jv) -{ - if (!jv.isMember("book_index")) - return false; - Json::Value const& bi = jv["book_index"]; - if (!bi.isString ()) - return false; - mIndex.SetHexExact(bi.asString()); - return true; -} - -bool OrderBookIterator::addJson (Json::Value& jv) const -{ - return mOfferIterator.addJson(jv) && mDirectoryIterator.addJson(jv); -} - -bool OrderBookIterator::setJson (Json::Value const& jv) -{ - return mDirectoryIterator.setJson (jv) && mOfferIterator.setJson (jv, mEntrySet); -} - -STAmount OrderBookIterator::getCurrentRate () const -{ - return mDirectoryIterator.getCurrentRate(); -} - -std::uint64_t OrderBookIterator::getCurrentQuality () const -{ - return mDirectoryIterator.getCurrentQuality(); -} - -uint256 OrderBookIterator::getCurrentDirectory () const -{ - return mOfferIterator.getDirectory (); -} - -uint256 OrderBookIterator::getCurrentIndex () const -{ - return mOfferIterator.getEntryLedgerIndex(); -} - -/** Retrieve the offer the iterator points to -*/ -SLE::pointer OrderBookIterator::getCurrentOffer () -{ - return mOfferIterator.getEntry (mEntrySet, ltOFFER); -} - -/** Go to the first offer in the first directory -*/ -bool OrderBookIterator::firstOffer () -{ - WriteLog (lsTRACE, Ledger) << "OrderBookIterator: first offer"; - // Go to first directory in order book - if (!mDirectoryIterator.firstDirectory (mEntrySet)) - { - WriteLog (lsTRACE, Ledger) << "OrderBookIterator: no first directory"; - return false; - } - mOfferIterator = mDirectoryIterator.getOfferIterator (); - - // Take the next offer - return nextOffer(); -} - -/** Go to the next offer, possibly changing directories -*/ -bool OrderBookIterator::nextOffer () -{ - WriteLog (lsTRACE, Ledger) << "OrderBookIterator: next offer"; - do - { - - // Is there a next offer in the current directory - if (mOfferIterator.nextEntry (mEntrySet)) - { - WriteLog (lsTRACE, Ledger) << "OrderBookIterator: there is a next offer in this directory"; - return true; - } - - // Is there a next directory - if (!mDirectoryIterator.nextDirectory (mEntrySet)) - { - WriteLog (lsTRACE, Ledger) << "OrderBookIterator: there is no next directory"; - return false; - } - WriteLog (lsTRACE, Ledger) << "OrderBookIterator: going to next directory"; - - // Set to before its first offer - mOfferIterator = mDirectoryIterator.getOfferIterator (); - } - while (1); -} - -/** Rewind to the beginning of this directory, then take the next offer -*/ -bool OrderBookIterator::rewind () -{ - if (!mDirectoryIterator.resync (mEntrySet)) - return false; - - mOfferIterator = mDirectoryIterator.getOfferIterator (); - return nextOffer (); -} - -/** Go to before the first offer in the next directory -*/ -bool OrderBookIterator::nextDir () -{ - if (!mDirectoryIterator.nextDirectory (mEntrySet)) - return false; - - mOfferIterator = mDirectoryIterator.getOfferIterator (); - - return true; -} - -/** Advance to the next offer in this directory -*/ -bool OrderBookIterator::nextOfferInDir () -{ - return mOfferIterator.nextEntry (mEntrySet); -} - -} // ripple diff --git a/src/ripple/app/ledger/OrderBookIterator.h b/src/ripple/app/ledger/OrderBookIterator.h deleted file mode 100644 index a8fc1f0c0b..0000000000 --- a/src/ripple/app/ledger/OrderBookIterator.h +++ /dev/null @@ -1,240 +0,0 @@ -//------------------------------------------------------------------------------ -/* - 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_APP_LEDGER_ORDERBOOKITERATOR_H_INCLUDED -#define RIPPLE_APP_LEDGER_ORDERBOOKITERATOR_H_INCLUDED - -#include -#include - -// VFALCO TODO Split these to separate files and de-inline the definitions - -namespace ripple { - -/** An iterator that walks the directories in a book */ -class BookDirIterator -{ - -public: - - BookDirIterator () - { - } - - BookDirIterator ( - Currency const& currencyIn, AccountID const& issuerIn, - Currency const& currencyOut, AccountID const& issuerOut); - - uint256 const& getBookBase () const - { - return mBase; - } - - uint256 const& getBookEnd () const - { - return mEnd; - } - - uint256 const& getCurrentIndex() const - { - return mIndex; - } - - void setCurrentIndex(uint256 const& index) - { - mIndex = index; - } - - /** Get the current exchange rate - */ - STAmount getCurrentRate () const - { - return amountFromQuality (getCurrentQuality()); - } - - /** Get the current quality - */ - std::uint64_t getCurrentQuality () const - { - return getQuality(mIndex); - } - - /** Make this iterator refer to the next book - */ - bool nextDirectory (LedgerEntrySet&); - - /** Make this iterator refer to the first book - */ - bool firstDirectory (LedgerEntrySet&); - - /** The LES may have changed - Resync the iterator - */ - bool resync (LedgerEntrySet&); - - /** Get an iterator to the offers in this directory - */ - DirectoryEntryIterator getOfferIterator () const; - - std::uint64_t getRate () const; - - bool addJson (Json::Value&) const; - bool setJson (Json::Value const&); - - // Does this iterator currently point to a valid directory - explicit - operator bool () const - { - return mOfferDir && (mOfferDir->getIndex() == mIndex); - } - - bool operator== (BookDirIterator const& other) const - { - assert (! mIndex.isZero() && ! other.mIndex.isZero()); - return mIndex == other.mIndex; - } - - bool operator!= (BookDirIterator const& other) const - { - return ! (*this == other); - } - -private: - uint256 mBase; // The first index a directory in the book can have - uint256 mEnd; // The first index a directory in the book cannot have - uint256 mIndex; // The index we are currently on - SLE::pointer mOfferDir; // The directory page we are currently on -}; - -//------------------------------------------------------------------------------ - -/** An iterator that walks the offers in a book - CAUTION: The LedgerEntrySet must remain valid for the life of the iterator -*/ -class OrderBookIterator -{ -public: - OrderBookIterator ( - LedgerEntrySet& set, - Currency const& currencyIn, - AccountID const& issuerIn, - Currency const& currencyOut, - AccountID const& issuerOut) : - mEntrySet (set), - mDirectoryIterator (currencyIn, issuerIn, currencyOut, issuerOut) - { - } - - OrderBookIterator& - operator= (OrderBookIterator const&) = default; - - bool addJson (Json::Value&) const; - - bool setJson (Json::Value const&); - - STAmount getCurrentRate () const; - - std::uint64_t getCurrentQuality () const; - - uint256 getCurrentIndex () const; - - uint256 getCurrentDirectory () const; - - SLE::pointer getCurrentOffer (); - - /** Position the iterator at the first offer in the first directory. - Returns whether there is an offer to point to. - */ - bool firstOffer (); - - /** Position the iterator at the next offer, going to the next directory if needed - Returns whether there is a next offer. - */ - bool nextOffer (); - - /** Position the iterator at the first offer in the next directory. - Returns whether there is a next directory to point to. - */ - bool nextDir (); - - /** Position the iterator at the first offer in the current directory. - Returns whether there is an offer in the directory. - */ - bool firstOfferInDir (); - - /** Position the iterator at the next offer in the current directory. - Returns whether there is a next offer in the directory. - */ - bool nextOfferInDir (); - - /** Position the iterator at the first offer at the current quality. - If none, position the iterator at the first offer at the next quality. - This rather odd semantic is required by the payment engine. - */ - bool rewind (); - - LedgerEntrySet& peekEntrySet () - { - return mEntrySet; - } - - BookDirIterator const& peekDirIterator () const - { - return mDirectoryIterator; - } - - DirectoryEntryIterator const& peekDirectoryEntryIterator () const - { - return mOfferIterator; - } - - BookDirIterator& peekDirIterator () - { - return mDirectoryIterator; - } - - DirectoryEntryIterator& peekDirectoryEntryIterator () - { - return mOfferIterator; - } - - bool - operator== (OrderBookIterator const& other) const - { - return - std::addressof(mEntrySet) == std::addressof(other.mEntrySet) && - mDirectoryIterator == other.mDirectoryIterator && - mOfferIterator == other.mOfferIterator; - } - - bool - operator!= (OrderBookIterator const& other) const - { - return ! (*this == other); - } - -private: - std::reference_wrapper mEntrySet; - BookDirIterator mDirectoryIterator; - DirectoryEntryIterator mOfferIterator; -}; - -} // ripple - -#endif diff --git a/src/ripple/app/ledger/SLECache.h b/src/ripple/app/ledger/SLECache.h deleted file mode 100644 index ead0e955ff..0000000000 --- a/src/ripple/app/ledger/SLECache.h +++ /dev/null @@ -1,37 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012-2015 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_APP_SLECACHE_H_INCLUDED -#define RIPPLE_APP_SLECACHE_H_INCLUDED - -#include -#include - -namespace ripple { - -/** STLedgerEntry cache. - This maps keys to the deserialized ledger entries, - to improve performance where the same item in - the ledger is accessed often. -*/ -using SLECache = TaggedCache ; - -} - -#endif diff --git a/src/ripple/app/ledger/impl/InboundLedger.cpp b/src/ripple/app/ledger/impl/InboundLedger.cpp index b2b50122ac..682f31fdf5 100644 --- a/src/ripple/app/ledger/impl/InboundLedger.cpp +++ b/src/ripple/app/ledger/impl/InboundLedger.cpp @@ -196,7 +196,7 @@ bool InboundLedger::tryLocal () { TransactionStateSF filter; - if (mLedger->peekTransactionMap ()->fetchRoot ( + if (mLedger->txMap().fetchRoot ( mLedger->getTransHash (), &filter)) { auto h (mLedger->getNeededTransactionHashes (1, &filter)); @@ -224,7 +224,7 @@ bool InboundLedger::tryLocal () { AccountStateSF filter; - if (mLedger->peekAccountStateMap ()->fetchRoot ( + if (mLedger->stateMap().fetchRoot ( mLedger->getAccountHash (), &filter)) { auto h (mLedger->getNeededAccountStateHashes (1, &filter)); @@ -521,11 +521,11 @@ void InboundLedger::trigger (Peer::ptr const& peer) { assert (mLedger); - if (!mLedger->peekAccountStateMap ()->isValid ()) + if (!mLedger->stateMap().isValid ()) { mFailed = true; } - else if (mLedger->peekAccountStateMap ()->getHash ().isZero ()) + else if (mLedger->stateMap().getHash ().isZero ()) { // we need the root node tmGL.set_itype (protocol::liAS_NODE); @@ -546,7 +546,7 @@ void InboundLedger::trigger (Peer::ptr const& peer) // Release the lock while we process the large state map sl.unlock(); - mLedger->peekAccountStateMap ()->getMissingNodes ( + mLedger->stateMap().getMissingNodes ( nodeIDs, nodeHashes, 256, &filter); sl.lock(); @@ -555,7 +555,7 @@ void InboundLedger::trigger (Peer::ptr const& peer) { if (nodeIDs.empty ()) { - if (!mLedger->peekAccountStateMap ()->isValid ()) + if (!mLedger->stateMap().isValid ()) mFailed = true; else { @@ -605,11 +605,11 @@ void InboundLedger::trigger (Peer::ptr const& peer) { assert (mLedger); - if (!mLedger->peekTransactionMap ()->isValid ()) + if (!mLedger->txMap().isValid ()) { mFailed = true; } - else if (mLedger->peekTransactionMap ()->getHash ().isZero ()) + else if (mLedger->txMap().getHash ().isZero ()) { // we need the root node tmGL.set_itype (protocol::liTX_NODE); @@ -627,12 +627,12 @@ void InboundLedger::trigger (Peer::ptr const& peer) nodeIDs.reserve (256); nodeHashes.reserve (256); TransactionStateSF filter; - mLedger->peekTransactionMap ()->getMissingNodes ( + mLedger->txMap().getMissingNodes ( nodeIDs, nodeHashes, 256, &filter); if (nodeIDs.empty ()) { - if (!mLedger->peekTransactionMap ()->isValid ()) + if (!mLedger->txMap().isValid ()) mFailed = true; else { @@ -823,14 +823,14 @@ bool InboundLedger::takeTxNode (const std::vector& nodeIDs, { if (nodeIDit->isRoot ()) { - san += mLedger->peekTransactionMap ()->addRootNode ( + san += mLedger->txMap().addRootNode ( mLedger->getTransHash (), *nodeDatait, snfWIRE, &tFilter); if (!san.isGood()) return false; } else { - san += mLedger->peekTransactionMap ()->addKnownNode ( + san += mLedger->txMap().addKnownNode ( *nodeIDit, *nodeDatait, &tFilter); if (!san.isGood()) return false; @@ -840,7 +840,7 @@ bool InboundLedger::takeTxNode (const std::vector& nodeIDs, ++nodeDatait; } - if (!mLedger->peekTransactionMap ()->isSynching ()) + if (!mLedger->txMap().isSynching ()) { mHaveTransactions = true; @@ -890,7 +890,7 @@ bool InboundLedger::takeAsNode (const std::vector& nodeIDs, { if (nodeIDit->isRoot ()) { - san += mLedger->peekAccountStateMap ()->addRootNode ( + san += mLedger->stateMap().addRootNode ( mLedger->getAccountHash (), *nodeDatait, snfWIRE, &tFilter); if (!san.isGood ()) { @@ -901,7 +901,7 @@ bool InboundLedger::takeAsNode (const std::vector& nodeIDs, } else { - san += mLedger->peekAccountStateMap ()->addKnownNode ( + san += mLedger->stateMap().addKnownNode ( *nodeIDit, *nodeDatait, &tFilter); if (!san.isGood ()) { @@ -915,7 +915,7 @@ bool InboundLedger::takeAsNode (const std::vector& nodeIDs, ++nodeDatait; } - if (!mLedger->peekAccountStateMap ()->isSynching ()) + if (!mLedger->stateMap().isSynching ()) { mHaveState = true; @@ -949,7 +949,7 @@ bool InboundLedger::takeAsRootNode (Blob const& data, SHAMapAddNode& san) } AccountStateSF tFilter; - san += mLedger->peekAccountStateMap ()->addRootNode ( + san += mLedger->stateMap().addRootNode ( mLedger->getAccountHash (), data, snfWIRE, &tFilter); return san.isGood(); } @@ -973,7 +973,7 @@ bool InboundLedger::takeTxRootNode (Blob const& data, SHAMapAddNode& san) } TransactionStateSF tFilter; - san += mLedger->peekTransactionMap ()->addRootNode ( + san += mLedger->txMap().addRootNode ( mLedger->getTransHash (), data, snfWIRE, &tFilter); return san.isGood(); } diff --git a/src/ripple/app/ledger/impl/LedgerConsensus.cpp b/src/ripple/app/ledger/impl/LedgerConsensus.cpp index b2a4d25225..eb46d63e4c 100644 --- a/src/ripple/app/ledger/impl/LedgerConsensus.cpp +++ b/src/ripple/app/ledger/impl/LedgerConsensus.cpp @@ -646,7 +646,7 @@ public: // it is shortly before ledger close time bool anyTransactions = getApp().getLedgerMaster ().getCurrentLedger () - ->peekTransactionMap ()->getHash ().isNonZero (); + ->txMap().getHash ().isNonZero (); int proposersClosed = mPeerPositions.size (); int proposersValidated = getApp().getValidations ().getTrustedValidationCount @@ -942,13 +942,13 @@ private: WriteLog (lsDEBUG, LedgerConsensus) << "Applying consensus set transactions to the" << " last closed ledger"; - applyTransactions (set, newLCL, newLCL, retriableTransactions, false); + applyTransactions (set.get(), newLCL, newLCL, retriableTransactions, false); newLCL->updateSkipList (); newLCL->setClosed (); - int asf = newLCL->peekAccountStateMap ()->flushDirty ( + int asf = newLCL->stateMap().flushDirty ( hotACCOUNT_NODE, newLCL->getLedgerSeq()); - int tmf = newLCL->peekTransactionMap ()->flushDirty ( + int tmf = newLCL->txMap().flushDirty ( hotTRANSACTION_NODE, newLCL->getLedgerSeq()); WriteLog (lsDEBUG, LedgerConsensus) << "Flushed " << asf << " accounts and " << tmf << " transaction nodes"; @@ -1047,7 +1047,7 @@ private: if (anyDisputes) { - applyTransactions (std::shared_ptr(), + applyTransactions (nullptr, newOL, newLCL, retriableTransactions, true); } @@ -1059,11 +1059,11 @@ private: // Apply transactions from the old open ledger Ledger::pointer oldOL = getApp().getLedgerMaster().getCurrentLedger(); - if (oldOL->peekTransactionMap()->getHash().isNonZero ()) + if (oldOL->txMap().getHash().isNonZero ()) { WriteLog (lsDEBUG, LedgerConsensus) << "Applying transactions from current open ledger"; - applyTransactions (oldOL->peekTransactionMap (), + applyTransactions (&oldOL->txMap(), newOL, newLCL, retriableTransactions, true); } @@ -1346,13 +1346,13 @@ private: { // previous ledger was flag ledger std::shared_ptr preSet - = initialLedger.peekTransactionMap ()->snapShot (true); + = initialLedger.txMap().snapShot (true); m_feeVote.doVoting (mPreviousLedger, preSet); getApp().getAmendmentTable ().doVoting (mPreviousLedger, preSet); initialSet = preSet->snapShot (false); } else - initialSet = initialLedger.peekTransactionMap ()->snapShot (false); + initialSet = initialLedger.txMap().snapShot (false); // Tell the ledger master not to acquire the ledger we're probably building getApp().getLedgerMaster().setBuildingLedger (mPreviousLedger->getLedgerSeq () + 1); @@ -1366,7 +1366,7 @@ private: for (auto& it : mDisputes) { - it.second->setOurVote (initialLedger.hasTransaction (it.first)); + it.second->setOurVote (hasTransaction (initialLedger, it.first)); } // if any peers have taken a contrary position, process disputes @@ -1849,18 +1849,17 @@ int applyTransaction (TransactionEngine& engine @param retriableTransactions collect failed transactions in this set @param openLgr true if applyLedger is open, else false. */ -void applyTransactions (std::shared_ptr const& set, +void applyTransactions (SHAMap const* set, Ledger::ref applyLedger, Ledger::ref checkLedger, CanonicalTXSet& retriableTransactions, bool openLgr) { TransactionEngine engine (applyLedger); - if (set) { for (auto const item : *set) { // If the checkLedger doesn't have the transaction - if (!checkLedger->hasTransaction (item->getTag ())) + if (! hasTransaction (*checkLedger, item->getTag ())) { // Then try to apply the transaction to applyLedger WriteLog (lsDEBUG, LedgerConsensus) << @@ -1871,7 +1870,7 @@ void applyTransactions (std::shared_ptr const& set, STTx::pointer txn = std::make_shared(sit); if (applyTransaction (engine, txn, - openLgr, true) == LedgerConsensusImp::resultRetry) + openLgr, true) == LedgerConsensusImp::resultRetry) { // On failure, stash the failed transaction for // later retry. diff --git a/src/ripple/app/ledger/impl/LedgerMaster.cpp b/src/ripple/app/ledger/impl/LedgerMaster.cpp index 021ac311f1..6cb8aee58b 100644 --- a/src/ripple/app/ledger/impl/LedgerMaster.cpp +++ b/src/ripple/app/ledger/impl/LedgerMaster.cpp @@ -661,7 +661,7 @@ public: { // A new ledger has been accepted as part of the trusted chain WriteLog (lsDEBUG, LedgerMaster) << "Ledger " << ledger->getLedgerSeq () << " accepted :" << ledger->getHash (); - assert (ledger->peekAccountStateMap ()->getHash ().isNonZero ()); + assert (ledger->stateMap().getHash ().isNonZero ()); ledger->setValidated(); ledger->setFull(); diff --git a/src/ripple/app/ledger/impl/MetaView.cpp b/src/ripple/app/ledger/impl/MetaView.cpp new file mode 100644 index 0000000000..f14330568a --- /dev/null +++ b/src/ripple/app/ledger/impl/MetaView.cpp @@ -0,0 +1,833 @@ +//------------------------------------------------------------------------------ +/* + 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 + +namespace ripple { + +// #define META_DEBUG + +// VFALCO TODO Replace this macro with a documented language constant +// +/** Maximum number of entries in a directory page + A change would be protocol-breaking. +*/ +#ifndef DIR_NODE_MAX +#define DIR_NODE_MAX 32 +#endif + +MetaView::MetaView(Ledger::ref ledger, + uint256 const& transactionID, + std::uint32_t ledgerID, + TransactionEngineParams params) + : parent_(&*ledger) + , mParams(params) +{ + mSet.init (transactionID, ledgerID); +} + +MetaView::MetaView (BasicView& parent, + bool openLedger) + : parent_ (&parent) + , mParams (openLedger + ? tapOPEN_LEDGER : tapNONE) +{ +} + +MetaView::MetaView (Ledger::ref ledger, + TransactionEngineParams tep) + : parent_(&*ledger) + , mParams (tep) +{ +} + +MetaView::MetaView (MetaView const& other) + : parent_(other.parent_) + , items_(other.items_) + , mDeferredCredits(other.mDeferredCredits) + , mSet(other.mSet) + // VFALCO NOTE This is a change in behavior, + // previous version set tapNONE + , mParams(other.mParams) + , mSeq(other.mSeq + 1) +{ +} + +//------------------------------------------------------------------------------ + +std::shared_ptr const& +MetaView::copyOnRead ( + list_type::iterator iter) +{ + if (iter->second.mSeq != mSeq) + { + iter->second.mSeq = mSeq; + iter->second.mEntry = std::make_shared( + *iter->second.mEntry); + } + return iter->second.mEntry; +} + +bool +MetaView::exists (Keylet const& k) const +{ + assert(k.key.isNonZero()); + auto const iter = items_.find(k.key); + if (iter == items_.end()) + return parent_->exists(k); + if (iter->second.mAction == taaDELETE) + return false; + if (! k.check(*iter->second.mEntry)) + return false; + return true; +} + +/* This works by first calculating succ() on the parent, + then calculating succ() our internal list, and taking + the lower of the two. +*/ +boost::optional +MetaView::succ (uint256 const& key, + boost::optional last) const +{ + boost::optional next = key; + list_type::const_iterator iter; + // Find parent successor that is + // not also deleted in our list + do + { + next = parent_->succ(*next, last); + if (! next) + break; + iter = items_.find(*next); + } + while (iter != items_.end() && + iter->second.mAction == taaDELETE); + // Find non-deleted successor in our list + for (iter = items_.upper_bound(key); + iter != items_.end (); ++iter) + { + if (iter->second.mAction != taaDELETE) + { + // Found both, return the lower key + if (! next || next > iter->first) + next = iter->first; + break; + } + } + // Nothing in our list, return + // what we got from the parent. + if (last && next >= last) + return boost::none; + return next; +} + +std::shared_ptr +MetaView::read (Keylet const& k) const +{ + assert(k.key.isNonZero()); + if (k.key.isZero()) + return nullptr; + // VFALCO TODO Shouldn't we create taaCACHED + // items to optimize reads? + auto const iter = items_.find(k.key); + if (iter == items_.end()) + { + auto const sle = + parent_->read(k); + if (! sle) + return nullptr; + return sle; + } + if (iter->second.mAction == taaDELETE) + return nullptr; + auto const& sle = + iter->second.mEntry; + if (! k.check(*sle)) + return nullptr; + return sle; +} + +bool +MetaView::unchecked_erase (uint256 const& key) +{ + auto const iter = + items_.lower_bound(key); + if (iter == items_.end() || + iter->first != key) + { + assert(parent_->exists( + keylet::unchecked(key))); + using namespace std; + items_.emplace_hint(iter, piecewise_construct, + forward_as_tuple(key), forward_as_tuple( + make_shared(*parent_->read( + keylet::unchecked(key))), + taaDELETE, mSeq)); + return true; + } + if (iter->second.mAction == taaCREATE) + { + items_.erase(iter); + return true; + } + assert(iter->second.mAction != taaDELETE); + iter->second.mAction = taaDELETE; + return true; +} + +void +MetaView::unchecked_insert( + std::shared_ptr&& sle) +{ + auto const iter = + items_.lower_bound(sle->key()); + if (iter == items_.end() || + iter->first != sle->key()) + { + // VFALCO return Keylet from SLE + assert(! parent_->exists(Keylet{ + sle->getType(), sle->key()})); + using namespace std; + items_.emplace_hint(iter, piecewise_construct, + forward_as_tuple(sle->key()), + forward_as_tuple(move(sle), + taaCREATE, mSeq)); + return; + } + switch(iter->second.mAction) + { + case taaMODIFY: + throw std::runtime_error( + "insert after modify"); + case taaCREATE: + throw std::runtime_error( + "insert after create"); + case taaCACHED: + throw std::runtime_error( + "insert after peek"); + case taaDELETE: + default: + break; + }; + // VFALCO return Keylet from SLE + assert(parent_->exists( + Keylet{sle->getType(), sle->key()})); + iter->second.mSeq = mSeq; + iter->second.mEntry = std::move(sle); + iter->second.mAction = taaMODIFY; +} + +void +MetaView::unchecked_replace (std::shared_ptr&& sle) +{ + auto const iter = + items_.lower_bound(sle->key()); + if (iter == items_.end() || + iter->first != sle->key()) + { + // VFALCO return Keylet from SLE + assert(parent_->exists(Keylet{ + sle->getType(), sle->key()})); + using namespace std; + items_.emplace_hint(iter, piecewise_construct, + forward_as_tuple(sle->key()), + forward_as_tuple(move(sle), + taaMODIFY, mSeq)); + return; + } + if (iter->second.mAction == taaDELETE) + throw std::runtime_error( + "replace after delete"); + if (iter->second.mAction != taaCREATE) + iter->second.mAction = taaMODIFY; + iter->second.mSeq = mSeq; + iter->second.mEntry = std::move(sle); +} + +STAmount +MetaView::deprecatedBalance( + AccountID const& account, AccountID const& issuer, + STAmount const& amount) const +{ + if (mDeferredCredits) + return mDeferredCredits->adjustedBalance( + account, issuer, amount); + return amount; +} + +std::shared_ptr +MetaView::peek (Keylet const& k) +{ + assert(k.key.isNonZero()); + if (k.key.isZero()) + return nullptr; + auto iter = items_.lower_bound(k.key); + if (iter == items_.end() || + iter->first != k.key) + { + auto const sle = + parent_->read(k); + if (! sle) + return nullptr; + // Make our own copy + iter = items_.emplace_hint (iter, + std::piecewise_construct, + std::forward_as_tuple(sle->getIndex()), + std::forward_as_tuple( + std::make_shared( + *sle), taaCACHED, mSeq)); + return iter->second.mEntry; + } + if (iter->second.mAction == taaDELETE) + return nullptr; + auto sle = + copyOnRead(iter); + if (! k.check(*sle)) + return nullptr; + return sle; +} + +void +MetaView::erase (std::shared_ptr const& sle) +{ + auto const iter = + items_.find(sle->getIndex()); + assert(iter != items_.end()); + if (iter == items_.end()) + return; + assert(iter->second.mSeq == mSeq); + assert(iter->second.mEntry == sle); + assert(iter->second.mAction != taaDELETE); + if (iter->second.mAction == taaDELETE) + return; + if (iter->second.mAction == taaCREATE) + { + items_.erase(iter); + return; + } + assert(iter->second.mAction == taaCACHED || + iter->second.mAction == taaMODIFY); + iter->second.mAction = taaDELETE; +} + +void +MetaView::insert (std::shared_ptr const& sle) +{ + auto const iter = items_.lower_bound(sle->key()); + if (iter == items_.end() || + iter->first != sle->key()) + { + // VFALCO return Keylet from SLE + assert(! parent_->exists( + Keylet{sle->getType(), sle->key()})); + items_.emplace_hint(iter, std::piecewise_construct, + std::forward_as_tuple(sle->getIndex()), + std::forward_as_tuple(sle, taaCREATE, mSeq)); + return; + } + switch(iter->second.mAction) + { + case taaMODIFY: + throw std::runtime_error( + "insert after modify"); + case taaCREATE: + // This could be made to work (?) + throw std::runtime_error( + "insert after create"); + case taaCACHED: + throw std::runtime_error( + "insert after copy"); + default: + break; + } + // Existed in parent, deleted here + assert(parent_->exists( + Keylet{sle->getType(), sle->key()})); + iter->second.mSeq = mSeq; + iter->second.mEntry = sle; + iter->second.mAction = taaMODIFY; +} + +void +MetaView::update (std::shared_ptr const& sle) +{ + auto const iter = items_.lower_bound(sle->key()); + if (iter == items_.end() || + iter->first != sle->key()) + { + // VFALCO return Keylet from SLE + assert(parent_->exists( + Keylet{sle->getType(), sle->key()})); + items_.emplace_hint(iter, std::piecewise_construct, + std::forward_as_tuple(sle->key()), + std::forward_as_tuple(sle, taaMODIFY, mSeq)); + return; + } + // VFALCO Should we throw? + assert(iter->second.mSeq == mSeq); + assert(iter->second.mEntry == sle); + if (iter->second.mAction == taaDELETE) + throw std::runtime_error( + "update after delete"); + if (iter->second.mAction != taaCREATE) + iter->second.mAction = taaMODIFY; +} + +bool +MetaView::openLedger() const +{ + return mParams & tapOPEN_LEDGER; +} + +void +MetaView::deprecatedCreditHint( + AccountID const& from, AccountID const& to, + STAmount const& amount) +{ + if (mDeferredCredits) + return mDeferredCredits->credit( + from, to, amount); +} + +//------------------------------------------------------------------------------ + +void MetaView::apply() +{ + // Write back the account states + for (auto& item : items_) + { + // VFALCO TODO rvalue move the mEntry, make + // sure the mNodes is not used after + // this function is called. + auto& sle = item.second.mEntry; + switch (item.second.mAction) + { + case taaCACHED: + assert(parent_->exists( + keylet::child(item.first))); + break; + + case taaCREATE: + // VFALCO Is this logging necessary anymore? + WriteLog (lsDEBUG, View) << + "applyTransaction: taaCREATE: " << sle->getText (); + parent_->unchecked_insert(std::move(sle)); + break; + + case taaMODIFY: + { + WriteLog (lsDEBUG, View) << + "applyTransaction: taaMODIFY: " << sle->getText (); + parent_->unchecked_replace(std::move(sle)); + break; + } + + case taaDELETE: + WriteLog (lsDEBUG, View) << + "applyTransaction: taaDELETE: " << sle->getText (); + parent_->unchecked_erase(sle->key()); + break; + } + } + // Safety precaution since we moved the + // entries out, apply() cannot be called twice. + items_.clear(); +} + +void MetaView::swapWith (MetaView& e) +{ + using std::swap; + swap (parent_, e.parent_); + items_.swap (e.items_); + mSet.swap (e.mSet); + swap (mParams, e.mParams); + swap (mSeq, e.mSeq); + swap (mDeferredCredits, e.mDeferredCredits); +} + +Json::Value MetaView::getJson (int) const +{ + Json::Value ret (Json::objectValue); + + Json::Value nodes (Json::arrayValue); + + for (auto it = items_.begin (), end = items_.end (); it != end; ++it) + { + Json::Value entry (Json::objectValue); + entry[jss::node] = to_string (it->first); + + switch (it->second.mEntry->getType ()) + { + case ltINVALID: + entry[jss::type] = "invalid"; + break; + + case ltACCOUNT_ROOT: + entry[jss::type] = "acccount_root"; + break; + + case ltDIR_NODE: + entry[jss::type] = "dir_node"; + break; + + case ltRIPPLE_STATE: + entry[jss::type] = "ripple_state"; + break; + + case ltNICKNAME: + entry[jss::type] = "nickname"; + break; + + case ltOFFER: + entry[jss::type] = "offer"; + break; + + default: + assert (false); + } + + switch (it->second.mAction) + { + case taaCACHED: + entry[jss::action] = "cache"; + break; + + case taaMODIFY: + entry[jss::action] = "modify"; + break; + + case taaDELETE: + entry[jss::action] = "delete"; + break; + + case taaCREATE: + entry[jss::action] = "create"; + break; + + default: + assert (false); + } + + nodes.append (entry); + } + + ret[jss::nodes] = nodes; + + ret[jss::metaData] = mSet.getJson (0); + + return ret; +} + +//------------------------------------------------------------------------------ + +std::shared_ptr +MetaView::getForMod (uint256 const& key, + Mods& mods) +{ + auto iter = items_.find (key); + if (iter != items_.end ()) + { + if (iter->second.mAction == taaDELETE) + { + WriteLog (lsFATAL, View) << + "Trying to thread to deleted node"; + return nullptr; + } + if (iter->second.mAction == taaCACHED) + iter->second.mAction = taaMODIFY; + return copyOnRead(iter); + } + { + auto miter = mods.find (key); + if (miter != mods.end ()) + { + assert (miter->second); + return miter->second; + } + } + // VFALCO NOTE Should this be read() or peek()? + auto const csle = parent_->read( + keylet::unchecked(key)); + if (! csle) + return nullptr; + // Need to make a copy here + auto sle = + std::make_shared(*csle); + mods.emplace(key, sle); + return sle; +} + +bool +MetaView::threadTx (RippleAddress const& to, + Mods& mods) +{ + auto const sle = getForMod(keylet::account( + to.getAccountID()).key, mods); +#ifdef META_DEBUG + WriteLog (lsTRACE, View) << "Thread to " << threadTo.getAccountID (); +#endif + if (! sle) + { + WriteLog (lsFATAL, View) << + "Threading to non-existent account: " << to.humanAccountID (); + assert (false); + return false; + } + + return threadTx (sle, mods); +} + +bool +MetaView::threadTx( + std::shared_ptr const& to, + Mods& mods) +{ + uint256 prevTxID; + std::uint32_t prevLgrID; + if (! to->thread(mSet.getTxID(), + mSet.getLgrSeq(), prevTxID, prevLgrID)) + return false; + if (prevTxID.isZero () || + TransactionMetaSet::thread( + mSet.getAffectedNode(to, + sfModifiedNode), prevTxID, + prevLgrID)) + return true; + assert (false); + return false; +} + +bool +MetaView::threadOwners(std::shared_ptr< + SLE const> const& sle, Mods& mods) +{ + // thread new or modified sle to owner or owners + if (sle->hasOneOwner()) + { + // thread to owner's account + #ifdef META_DEBUG + WriteLog (lsTRACE, View) << "Thread to single owner"; + #endif + return threadTx (sle->getOwner(), mods); + } + else if (sle->hasTwoOwners ()) // thread to owner's accounts + { + #ifdef META_DEBUG + WriteLog (lsTRACE, View) << "Thread to two owners"; + #endif + return threadTx(sle->getFirstOwner(), mods) && + threadTx(sle->getSecondOwner(), mods); + } + return false; +} + +void +MetaView::calcRawMeta (Serializer& s, + TER result, std::uint32_t index) +{ + // calculate the raw meta data and return it. This must be called before the set is committed + + // Entries modified only as a result of building the transaction metadata + Mods newMod; + + for (auto& it : items_) + { + auto type = &sfGeneric; + + switch (it.second.mAction) + { + case taaMODIFY: +#ifdef META_DEBUG + WriteLog (lsTRACE, View) << "Modified Node " << it.first; +#endif + type = &sfModifiedNode; + break; + + case taaDELETE: +#ifdef META_DEBUG + WriteLog (lsTRACE, View) << "Deleted Node " << it.first; +#endif + type = &sfDeletedNode; + break; + + case taaCREATE: +#ifdef META_DEBUG + WriteLog (lsTRACE, View) << "Created Node " << it.first; +#endif + type = &sfCreatedNode; + break; + + default: // ignore these + break; + } + + if (type == &sfGeneric) + continue; + + auto const origNode = + parent_->read(keylet::unchecked(it.first)); + auto curNode = it.second.mEntry; + + if ((type == &sfModifiedNode) && (*curNode == *origNode)) + continue; + + std::uint16_t nodeType = curNode + ? curNode->getFieldU16 (sfLedgerEntryType) + : origNode->getFieldU16 (sfLedgerEntryType); + + mSet.setAffectedNode (it.first, *type, nodeType); + + if (type == &sfDeletedNode) + { + assert (origNode && curNode); + threadOwners (origNode, newMod); // thread transaction to owners + + STObject prevs (sfPreviousFields); + for (auto const& obj : *origNode) + { + // go through the original node for modified fields saved on modification + if (obj.getFName ().shouldMeta (SField::sMD_ChangeOrig) && !curNode->hasMatchingEntry (obj)) + prevs.emplace_back (obj); + } + + if (!prevs.empty ()) + mSet.getAffectedNode (it.first).emplace_back (std::move(prevs)); + + STObject finals (sfFinalFields); + for (auto const& obj : *curNode) + { + // go through the final node for final fields + if (obj.getFName ().shouldMeta (SField::sMD_Always | SField::sMD_DeleteFinal)) + finals.emplace_back (obj); + } + + if (!finals.empty ()) + mSet.getAffectedNode (it.first).emplace_back (std::move(finals)); + } + else if (type == &sfModifiedNode) + { + assert (curNode && origNode); + + if (curNode->isThreadedType ()) // thread transaction to node it modified + threadTx (curNode, newMod); + + STObject prevs (sfPreviousFields); + for (auto const& obj : *origNode) + { + // search the original node for values saved on modify + if (obj.getFName ().shouldMeta (SField::sMD_ChangeOrig) && !curNode->hasMatchingEntry (obj)) + prevs.emplace_back (obj); + } + + if (!prevs.empty ()) + mSet.getAffectedNode (it.first).emplace_back (std::move(prevs)); + + STObject finals (sfFinalFields); + for (auto const& obj : *curNode) + { + // search the final node for values saved always + if (obj.getFName ().shouldMeta (SField::sMD_Always | SField::sMD_ChangeNew)) + finals.emplace_back (obj); + } + + if (!finals.empty ()) + mSet.getAffectedNode (it.first).emplace_back (std::move(finals)); + } + else if (type == &sfCreatedNode) // if created, thread to owner(s) + { + assert (curNode && !origNode); + threadOwners (curNode, newMod); + + if (curNode->isThreadedType ()) // always thread to self + threadTx (curNode, newMod); + + STObject news (sfNewFields); + for (auto const& obj : *curNode) + { + // save non-default values + if (!obj.isDefault () && obj.getFName ().shouldMeta (SField::sMD_Create | SField::sMD_Always)) + news.emplace_back (obj); + } + + if (!news.empty ()) + mSet.getAffectedNode (it.first).emplace_back (std::move(news)); + } + else assert (false); + } + + // add any new modified nodes to the modification set + for (auto& it : newMod) + update (it.second); + + mSet.addRaw (s, result, index); + WriteLog (lsTRACE, View) << "Metadata:" << mSet.getJson (0); +} + +void MetaView::enableDeferredCredits (bool enable) +{ + assert(enable == !mDeferredCredits); + + if (!enable) + { + mDeferredCredits.reset (); + return; + } + + if (!mDeferredCredits) + mDeferredCredits.emplace (); +} + +bool MetaView::areCreditsDeferred () const +{ + return static_cast (mDeferredCredits); +} + +ScopedDeferCredits::ScopedDeferCredits (MetaView& l) + : les_ (l), enabled_ (false) +{ + if (!les_.areCreditsDeferred ()) + { + WriteLog (lsTRACE, DeferredCredits) << "Enable"; + les_.enableDeferredCredits (true); + enabled_ = true; + } +} + +ScopedDeferCredits::~ScopedDeferCredits () +{ + if (enabled_) + { + WriteLog (lsTRACE, DeferredCredits) << "Disable"; + les_.enableDeferredCredits (false); + } +} + +} // ripple diff --git a/src/ripple/app/ledger/tests/DeferredCredits.test.cpp b/src/ripple/app/ledger/tests/DeferredCredits.test.cpp index 4640744f5a..4adf71a173 100644 --- a/src/ripple/app/ledger/tests/DeferredCredits.test.cpp +++ b/src/ripple/app/ledger/tests/DeferredCredits.test.cpp @@ -18,6 +18,7 @@ //============================================================================== #include +#include namespace ripple { namespace test { @@ -159,120 +160,120 @@ class DeferredCredits_test : public beast::unit_test::suite STAmount const toDebit (issue, 20); { // accountSend, no FT - LedgerEntrySet les (ledger, tapNONE); + MetaView les (ledger, tapNONE); expect (!les.areCreditsDeferred ()); STAmount const startingAmount = - les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE); + accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()); - les.accountSend (gw1Acc, aliceAcc, toCredit); - expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) == + accountSend (les, gw1Acc, aliceAcc, toCredit); + expect (accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()) == startingAmount + toCredit); - les.accountSend (aliceAcc, gw1Acc, toDebit); - expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) == + accountSend (les, aliceAcc, gw1Acc, toDebit); + expect (accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()) == startingAmount + toCredit - toDebit); } { // rippleCredit, no FT - LedgerEntrySet les (ledger, tapNONE); + MetaView les (ledger, tapNONE); expect (!les.areCreditsDeferred ()); STAmount const startingAmount = - les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE); + accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()); - les.rippleCredit (gw1Acc, aliceAcc, toCredit); - expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) == + rippleCredit (les, gw1Acc, aliceAcc, toCredit); + expect (accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()) == startingAmount + toCredit); - les.rippleCredit (aliceAcc, gw1Acc, toDebit); - expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) == + rippleCredit (les, aliceAcc, gw1Acc, toDebit); + expect (accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()) == startingAmount + toCredit - toDebit); } { // accountSend, w/ FT - LedgerEntrySet les (ledger, tapNONE); + MetaView les (ledger, tapNONE); les.enableDeferredCredits (); expect (les.areCreditsDeferred ()); STAmount const startingAmount = - les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE); + accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()); - les.accountSend (gw1Acc, aliceAcc, toCredit); - expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) == + accountSend (les, gw1Acc, aliceAcc, toCredit); + expect (accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()) == startingAmount); - les.accountSend (aliceAcc, gw1Acc, toDebit); - expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) == + accountSend (les, aliceAcc, gw1Acc, toDebit); + expect (accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()) == startingAmount - toDebit); } { // rippleCredit, w/ FT - LedgerEntrySet les (ledger, tapNONE); + MetaView les (ledger, tapNONE); les.enableDeferredCredits (); expect (les.areCreditsDeferred ()); STAmount const startingAmount = - les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE); + accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()); - les.rippleCredit (gw1Acc, aliceAcc, toCredit); - expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) == + rippleCredit (les, gw1Acc, aliceAcc, toCredit); + expect (accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()) == startingAmount); - les.rippleCredit (aliceAcc, gw1Acc, toDebit); - expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) == + rippleCredit (les, aliceAcc, gw1Acc, toDebit); + expect (accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()) == startingAmount - toDebit); } { // rippleCredit, w/ FT & ScopedDeferCredits - LedgerEntrySet les (ledger, tapNONE); + MetaView les (ledger, tapNONE); STAmount const startingAmount = - les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE); + accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()); { ScopedDeferCredits g (les); - les.rippleCredit (gw1Acc, aliceAcc, toCredit); + rippleCredit (les, gw1Acc, aliceAcc, toCredit); expect ( - les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) == + accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()) == startingAmount); } - expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) == + expect (accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()) == startingAmount + toCredit); } { - // issue_iou - LedgerEntrySet les (ledger, tapNONE); + // issueIOU + MetaView les (ledger, tapNONE); STAmount const startingAmount = - les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE); + accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()); les.enableDeferredCredits (); expect (les.areCreditsDeferred ()); - les.redeem_iou (aliceAcc, toDebit, issue); - expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) == + redeemIOU (les, aliceAcc, toDebit, issue); + expect (accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()) == startingAmount - toDebit); } { - // redeem_iou - LedgerEntrySet les (ledger, tapNONE); + // redeemIOU + MetaView les (ledger, tapNONE); STAmount const startingAmount = - les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE); + accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()); { ScopedDeferCredits g (les); expect (les.areCreditsDeferred ()); - les.issue_iou (aliceAcc, toCredit, issue); + issueIOU (les, aliceAcc, toCredit, issue); expect ( - les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) == + accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()) == startingAmount); } - expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) == + expect (accountHolds (les, aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE, getConfig()) == startingAmount + toCredit); } } diff --git a/src/ripple/app/ledger/tests/MetaView_test.cpp b/src/ripple/app/ledger/tests/MetaView_test.cpp new file mode 100644 index 0000000000..15371a2205 --- /dev/null +++ b/src/ripple/app/ledger/tests/MetaView_test.cpp @@ -0,0 +1,343 @@ +//------------------------------------------------------------------------------ +/* + 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 // + +namespace ripple { +namespace test { + +class MetaView_test + : public beast::unit_test::suite +{ + // Convert a small integer to a key + static + Keylet + k (std::uint64_t id) + { + return Keylet{ + ltACCOUNT_ROOT, uint256(id)}; + } + + // Create SLE with key and payload + static + std::shared_ptr + sle (std::uint64_t id, + std::uint32_t seq = 1) + { + auto const le = + std::make_shared(k(id)); + le->setFieldU32(sfSequence, seq); + return le; + } + + // Return payload for SLE + template + static + std::uint32_t + seq (std::shared_ptr const& le) + { + return le->getFieldU32(sfSequence); + } + + // Set payload on SLE + static + void + seq (std::shared_ptr const& le, + std::uint32_t seq) + { + le->setFieldU32(sfSequence, seq); + } + + // Erase all state items + static + void + wipe (BasicView& v) + { + // HACK! + boost::optional next; + next.emplace(0); + for(;;) + { + next = v.succ(*next); + if (! next) + break; + v.unchecked_erase(*next); + } + } + + // Test succ correctness + void + succ (BasicView const& v, + std::uint32_t id, + boost::optional< + std::uint32_t> answer) + { + auto const next = + v.succ(k(id).key); + if (answer) + { + if (expect(next)) + expect(*next == + k(*answer).key); + } + else + { + expect( ! next); + } + } + + template + static + std::shared_ptr< + std::remove_const_t> + copy (std::shared_ptr const& sp) + { + return std::make_shared< + std::remove_const_t>(*sp); + } + + // Exercise Ledger implementation of View + void + testLedger() + { + using namespace jtx; + Env env(*this); + wipe(*env.ledger); + BasicView& v = *env.ledger; + succ(v, 0, boost::none); + v.unchecked_insert(sle(1, 1)); + expect(v.exists(k(1))); + expect(seq(v.read(k(1))) == 1); + succ(v, 0, 1); + succ(v, 1, boost::none); + v.unchecked_insert(sle(2, 2)); + expect(seq(v.read(k(2))) == 2); + v.unchecked_insert(sle(3, 3)); + expect(seq(v.read(k(3))) == 3); + auto s = copy(v.read(k(2))); + seq(s, 4); + v.unchecked_replace(std::move(s)); + expect(seq(v.read(k(2))) == 4); + expect(v.unchecked_erase(k(2).key)); + expect(! v.exists(k(2))); + expect(v.exists(k(1))); + expect(v.exists(k(3))); + expect(! v.unchecked_erase(k(5).key)); + } + + void + testMeta() + { + using namespace jtx; + Env env(*this); + wipe(*env.ledger); + MetaView v(*env.ledger, false); + succ(v, 0, boost::none); + v.insert(sle(1)); + expect(v.exists(k(1))); + expect(seq(v.read(k(1))) == 1); + expect(seq(v.peek(k(1))) == 1); + succ(v, 0, 1); + succ(v, 1, boost::none); + v.insert(sle(2, 2)); + expect(seq(v.read(k(2))) == 2); + v.insert(sle(3, 3)); + auto s = v.peek(k(3)); + expect(seq(s) == 3); + s = v.peek(k(2)); + seq(s, 4); + v.update(s); + expect(seq(v.read(k(2))) == 4); + v.erase(s); + expect(! v.exists(k(2))); + expect(v.exists(k(1))); + expect(v.exists(k(3))); + } + + // Exercise all succ paths + void + testMetaSucc() + { + using namespace jtx; + Env env(*this); + wipe(*env.ledger); + BasicView& v0 = *env.ledger; + + v0.unchecked_insert(sle(1)); + v0.unchecked_insert(sle(2)); + v0.unchecked_insert(sle(4)); + v0.unchecked_insert(sle(7)); + { + MetaView v1(v0, false); + v1.insert(sle(3)); + v1.insert(sle(5)); + v1.insert(sle(6)); + + // v0: 12-4--7 + // v1: --3-56- + + succ(v0, 0, 1); + succ(v0, 1, 2); + succ(v0, 2, 4); + succ(v0, 3, 4); + succ(v0, 4, 7); + succ(v0, 5, 7); + succ(v0, 6, 7); + succ(v0, 7, boost::none); + + succ(v1, 0, 1); + succ(v1, 1, 2); + succ(v1, 2, 3); + succ(v1, 3, 4); + succ(v1, 4, 5); + succ(v1, 5, 6); + succ(v1, 6, 7); + succ(v1, 7, boost::none); + + v1.erase(v1.peek(k(4))); + succ(v1, 3, 5); + + v1.erase(v1.peek(k(6))); + succ(v1, 5, 7); + succ(v1, 6, 7); + + // v0: 12----7 + // v1: --3-5-- + + v1.apply(); + } + + // v0: 123-5-7 + + succ(v0, 0, 1); + succ(v0, 1, 2); + succ(v0, 2, 3); + succ(v0, 3, 5); + succ(v0, 4, 5); + succ(v0, 5, 7); + succ(v0, 6, 7); + succ(v0, 7, boost::none); + } + + void + testStacked() + { + using namespace jtx; + Env env(*this); + wipe(*env.ledger); + BasicView& v0 = *env.ledger; + v0.unchecked_insert(sle(1, 1)); + v0.unchecked_insert(sle(2, 2)); + v0.unchecked_insert(sle(4, 4)); + + { + MetaView v1(v0, true); + v1.erase(v1.peek(k(2))); + v1.insert(sle(3, 3)); + auto s = v1.peek(k(4)); + seq(s, 5); + v1.update(s); + expect(seq(v1.read(k(1))) == 1); + expect(! v1.exists(k(2))); + expect(seq(v1.read(k(3))) == 3); + expect(seq(v1.read(k(4))) == 5); + { + MetaView v2(v1, true); + auto s = v2.peek(k(3)); + seq(s, 6); + v2.update(s); + v2.erase(v2.peek(k(4))); + expect(seq(v2.read(k(1))) == 1); + expect(! v2.exists(k(2))); + expect(seq(v2.read(k(3))) == 6); + expect(! v2.exists(k(4))); + // discard v2 + } + expect(seq(v1.read(k(1))) == 1); + expect(! v1.exists(k(2))); + expect(seq(v1.read(k(3))) == 3); + expect(seq(v1.read(k(4))) == 5); + + { + MetaView v2(v1, true); + auto s = v2.peek(k(3)); + seq(s, 6); + v2.update(s); + v2.erase(v2.peek(k(4))); + expect(seq(v2.read(k(1))) == 1); + expect(! v2.exists(k(2))); + expect(seq(v2.read(k(3))) == 6); + expect(! v2.exists(k(4))); + v2.apply(); + } + expect(seq(v1.read(k(1))) == 1); + expect(! v1.exists(k(2))); + expect(seq(v1.read(k(3))) == 6); + expect(! v1.exists(k(4))); + v1.apply(); + } + expect(seq(v0.read(k(1))) == 1); + expect(! v0.exists(k(2))); + expect(seq(v0.read(k(3))) == 6); + expect(! v0.exists(k(4))); + } + + // Regression test: + // Create a ledger with 1 item, put a + // MetaView on that, then another MetaView, + // erase the item, apply. + void + testStackRegress() + { + using namespace jtx; + Env env(*this); + wipe(*env.ledger); + BasicView& v0 = *env.ledger; + v0.unchecked_insert(sle(1)); + MetaView v1(v0, true); + { + MetaView v2(v1, true); + v2.erase(v2.peek(k(1))); + v2.apply(); + } + expect(! v1.exists(k(1))); + } + + void run() + { + // This had better work, or else + expect(k(0).key < k(1).key); + + testLedger(); + testMeta(); + testMetaSucc(); + testStacked(); + testStackRegress(); + } +}; + +BEAST_DEFINE_TESTSUITE(MetaView,app,ripple); + +} // test +} // ripple diff --git a/src/ripple/app/ledger/tests/common_ledger.cpp b/src/ripple/app/ledger/tests/common_ledger.cpp index 622ddfd80f..81ca991f4b 100644 --- a/src/ripple/app/ledger/tests/common_ledger.cpp +++ b/src/ripple/app/ledger/tests/common_ledger.cpp @@ -432,20 +432,20 @@ trust(TestAccount& from, TestAccount const& issuer, void close_and_advance(Ledger::pointer& ledger, std::shared_ptr& LCL) { - std::shared_ptr set = ledger->peekTransactionMap(); - CanonicalTXSet retriableTransactions(set->getHash()); + auto const& set = ledger->txMap(); + CanonicalTXSet retriableTransactions(set.getHash()); // Make a non-const copy of LCL. This won't be necessary once // that other Ledger constructor can take a const Ledger. Ledger oldLCL(*LCL, false); Ledger::pointer newLCL = std::make_shared(false, oldLCL); // Set up to write SHAMap changes to our database, // perform updates, extract changes - applyTransactions(set, newLCL, newLCL, retriableTransactions, false); + applyTransactions(&set, newLCL, newLCL, retriableTransactions, false); newLCL->updateSkipList(); newLCL->setClosed(); - newLCL->peekAccountStateMap()->flushDirty( + newLCL->stateMap().flushDirty( hotACCOUNT_NODE, newLCL->getLedgerSeq()); - newLCL->peekTransactionMap()->flushDirty( + newLCL->txMap().flushDirty( hotTRANSACTION_NODE, newLCL->getLedgerSeq()); using namespace std::chrono; auto const epoch_offset = days(10957); // 2000-01-01 @@ -500,16 +500,15 @@ getLedgerEntryRippleState(Ledger::pointer ledger, TestAccount const& account1, TestAccount const& account2, Currency currency) { - auto uNodeIndex = getRippleStateIndex( + auto k = keylet::line( account1.pk.getAccountID(), account2.pk.getAccountID(), - to_currency(currency.getCurrency())); + to_currency(currency.getCurrency())); - if (!uNodeIndex.isNonZero()) + if (! k.key.isNonZero()) throw std::runtime_error( - "!uNodeIndex.isNonZero()"); + "!k.key.isNonZero()"); - return fetch(*ledger, uNodeIndex, - getApp().getSLECache()); + return ledger->read(k); } void diff --git a/src/ripple/app/main/Application.cpp b/src/ripple/app/main/Application.cpp index ade17c7a2d..19e107f4d4 100644 --- a/src/ripple/app/main/Application.cpp +++ b/src/ripple/app/main/Application.cpp @@ -1042,7 +1042,7 @@ void ApplicationImp::startNewLedger () { Ledger::pointer firstLedger = std::make_shared (rootAddress, SYSTEM_CURRENCY_START); - assert (firstLedger->exists(getAccountRootIndex(rootAddress.getAccountID()))); + assert (firstLedger->exists(keylet::account(rootAddress.getAccountID()))); // TODO(david): Add any default amendments // TODO(david): Set default fee/reserve firstLedger->getHash(); // updates the hash @@ -1054,7 +1054,7 @@ void ApplicationImp::startNewLedger () secondLedger->setClosed (); secondLedger->setAccepted (); m_ledgerMaster->pushLedger (secondLedger, std::make_shared (true, std::ref (*secondLedger))); - assert (secondLedger->exists(getAccountRootIndex(rootAddress.getAccountID()))); + assert (secondLedger->exists(keylet::account(rootAddress.getAccountID()))); m_networkOPs->setLastCloseTime (secondLedger->getCloseTimeNC ()); } } @@ -1310,22 +1310,23 @@ bool ApplicationImp::loadOldLedger ( if (replay) { // inject transaction(s) from the replayLedger into our open ledger - std::shared_ptr const& txns = replayLedger->peekTransactionMap(); + auto const& txns = replayLedger->txMap(); // Get a mutable snapshot of the open ledger Ledger::pointer cur = getLedgerMaster().getCurrentLedger(); cur = std::make_shared (*cur, true); assert (!cur->isImmutable()); - for (auto const& item : *txns) + for (auto const& item : txns) { auto const txn = - replayLedger->getTransaction(item->getTag()); + getTransaction(*replayLedger, item->getTag(), + getApp().getMasterTransaction()); if (m_journal.info) m_journal.info << txn->getJson(0); Serializer s; txn->getSTransaction()->add(s); - if (! cur->addTransaction(item->getTag(), s)) + if (! addTransaction(*cur, item->getTag(), s)) if (m_journal.warning) m_journal.warning << "Unable to add transaction " << item->getTag(); getApp().getHashRouter().setFlag (item->getTag(), SF_SIGGOOD); diff --git a/src/ripple/app/main/Application.h b/src/ripple/app/main/Application.h index 62b48700c8..e46764f78a 100644 --- a/src/ripple/app/main/Application.h +++ b/src/ripple/app/main/Application.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include // #include diff --git a/src/ripple/app/misc/NetworkOPs.cpp b/src/ripple/app/misc/NetworkOPs.cpp index 2455a00bcd..f48211d918 100644 --- a/src/ripple/app/misc/NetworkOPs.cpp +++ b/src/ripple/app/misc/NetworkOPs.cpp @@ -24,12 +24,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -1267,7 +1269,7 @@ STVector256 NetworkOPsImp::getDirNodeInfo ( std::uint64_t& uNodeNext) { STVector256 svIndexes; - auto const sleNode = fetch(*lrLedger, uNodeIndex, + auto const sleNode = cachedRead(*lrLedger, uNodeIndex, getApp().getSLECache(), ltDIR_NODE); if (sleNode) @@ -1313,7 +1315,7 @@ Json::Value NetworkOPsImp::getOwnerInfo ( { Json::Value jvObjects (Json::objectValue); auto uRootIndex = getOwnerDirIndex (naAccount.getAccountID ()); - auto sleNode = fetch(*lpLedger, uRootIndex, + auto sleNode = cachedRead(*lpLedger, uRootIndex, getApp().getSLECache(), ltDIR_NODE); if (sleNode) { @@ -1323,7 +1325,7 @@ Json::Value NetworkOPsImp::getOwnerInfo ( { for (auto const& uDirEntry : sleNode->getFieldV256 (sfIndexes)) { - auto sleCur = fetch(*lpLedger, uDirEntry, + auto sleCur = cachedRead(*lpLedger, uDirEntry, getApp().getSLECache()); switch (sleCur->getType ()) @@ -1357,7 +1359,7 @@ Json::Value NetworkOPsImp::getOwnerInfo ( if (uNodeDir) { - sleNode = fetch(*lpLedger, getDirNodeIndex( + sleNode = cachedRead(*lpLedger, getDirNodeIndex( uRootIndex, uNodeDir), getApp().getSLECache(), ltDIR_NODE); assert (sleNode); @@ -2496,10 +2498,10 @@ Json::Value NetworkOPsImp::transJson( // If the offer create is not self funded then add the owner balance if (account != amount.issue ().account) { - // VFALCO Why are we doing this hack? - LedgerEntrySet les (lpCurrent, tapNONE, true); - auto const ownerFunds = funds( - les,account, amount, fhIGNORE_FREEZE); + CachedView const view( + *lpCurrent, getApp().getSLECache()); + auto const ownerFunds = accountFunds(view, + account, amount, fhIGNORE_FREEZE, getConfig()); jvObj[jss::transaction][jss::owner_funds] = ownerFunds.getText (); } } @@ -2921,20 +2923,22 @@ void NetworkOPsImp::getBookPage ( m_journal.trace << "getBookPage: uTipIndex=" << uTipIndex; } - LedgerEntrySet lesActive (lpLedger, tapNONE, true); + CachedView const view( + *lpLedger, getApp().getSLECache()); - const bool bGlobalFreeze = lesActive.isGlobalFrozen (book.out.account) || - lesActive.isGlobalFrozen (book.in.account); + bool const bGlobalFreeze = + isGlobalFrozen(view, book.out.account) || + isGlobalFrozen(view, book.in.account); bool bDone = false; bool bDirectAdvance = true; - SLE::pointer sleOfferDir; + std::shared_ptr sleOfferDir; uint256 offerIndex; unsigned int uBookEntry; STAmount saDirRate; - auto uTransferRate = rippleTransferRate (lesActive, book.out.account); + auto uTransferRate = rippleTransferRate(view, book.out.account); unsigned int left (iLimit == 0 ? 300 : iLimit); if (! bAdmin && left > 300) @@ -2948,11 +2952,9 @@ void NetworkOPsImp::getBookPage ( m_journal.trace << "getBookPage: bDirectAdvance"; - uint256 const ledgerIndex = - lpLedger->getNextLedgerIndex (uTipIndex, uBookEnd); - if (ledgerIndex.isNonZero()) - sleOfferDir = lesActive.entryCache ( - ltDIR_NODE, ledgerIndex); + auto const ledgerIndex = view.succ(uTipIndex, uBookEnd); + if (ledgerIndex) + sleOfferDir = view.read(keylet::page(*ledgerIndex)); else sleOfferDir.reset(); @@ -2966,7 +2968,7 @@ void NetworkOPsImp::getBookPage ( uTipIndex = sleOfferDir->getIndex (); saDirRate = amountFromQuality (getQuality (uTipIndex)); - lesActive.dirFirst ( + cdirFirst (view, uTipIndex, sleOfferDir, uBookEntry, offerIndex); m_journal.trace << "getBookPage: uTipIndex=" << uTipIndex; @@ -2976,7 +2978,7 @@ void NetworkOPsImp::getBookPage ( if (!bDone) { - auto sleOffer = lesActive.entryCache (ltOFFER, offerIndex); + auto sleOffer = view.read(keylet::offer(offerIndex)); if (sleOffer) { @@ -3015,9 +3017,10 @@ void NetworkOPsImp::getBookPage ( { // Did not find balance in table. - saOwnerFunds = lesActive.accountHolds ( + saOwnerFunds = accountHolds (view, uOfferOwnerID, book.out.currency, - book.out.account, fhZERO_IF_FROZEN); + book.out.account, fhZERO_IF_FROZEN, + getConfig()); if (saOwnerFunds < zero) { @@ -3096,7 +3099,7 @@ void NetworkOPsImp::getBookPage ( m_journal.warning << "Missing offer"; } - if (!lesActive.dirNext ( + if (! cdirNext(view, uTipIndex, sleOfferDir, uBookEntry, offerIndex)) { bDirectAdvance = true; @@ -3133,7 +3136,7 @@ void NetworkOPsImp::getBookPage ( std::map umBalance; - LedgerEntrySet lesActive (lpLedger, tapNONE, true); + MetaView lesActive (lpLedger, tapNONE, true); OrderBookIterator obIterator (lesActive, book); auto uTransferRate = rippleTransferRate (lesActive, book.out.account); @@ -3365,13 +3368,13 @@ void NetworkOPsImp::makeFetchPack ( newObj.set_data (s.getDataPtr (), s.getLength ()); newObj.set_ledgerseq (lSeq); - wantLedger->peekAccountStateMap ()->getFetchPack - (haveLedger->peekAccountStateMap ().get (), true, 16384, + wantLedger->stateMap().getFetchPack + (&haveLedger->stateMap(), true, 16384, std::bind (fpAppender, &reply, lSeq, std::placeholders::_1, std::placeholders::_2)); if (wantLedger->getTransHash ().isNonZero ()) - wantLedger->peekTransactionMap ()->getFetchPack ( + wantLedger->txMap().getFetchPack ( nullptr, true, 512, std::bind (fpAppender, &reply, lSeq, std::placeholders::_1, std::placeholders::_2)); diff --git a/src/ripple/app/misc/SHAMapStoreImp.cpp b/src/ripple/app/misc/SHAMapStoreImp.cpp index a7d74fabc1..034772e979 100644 --- a/src/ripple/app/misc/SHAMapStoreImp.cpp +++ b/src/ripple/app/misc/SHAMapStoreImp.cpp @@ -331,7 +331,7 @@ SHAMapStoreImp::run() } std::uint64_t nodeCount = 0; - validatedLedger_->peekAccountStateMap()->snapShot ( + validatedLedger_->stateMap().snapShot ( false)->visitNodes ( std::bind (&SHAMapStoreImp::copyNode, this, std::ref(nodeCount), std::placeholders::_1)); diff --git a/src/ripple/app/paths/Credit.cpp b/src/ripple/app/paths/Credit.cpp index d5e0db5421..35da25fa94 100644 --- a/src/ripple/app/paths/Credit.cpp +++ b/src/ripple/app/paths/Credit.cpp @@ -18,22 +18,23 @@ //============================================================================== #include -#include +#include #include #include namespace ripple { -STAmount creditLimit ( - LedgerEntrySet& ledger, +STAmount +creditLimit ( + BasicView const& view, AccountID const& account, AccountID const& issuer, Currency const& currency) { STAmount result ({currency, account}); - auto sleRippleState = ledger.entryCache (ltRIPPLE_STATE, - getRippleStateIndex (account, issuer, currency)); + auto sleRippleState = view.read( + keylet::line(account, issuer, currency)); if (sleRippleState) { @@ -48,15 +49,15 @@ STAmount creditLimit ( } STAmount creditBalance ( - LedgerEntrySet& ledger, + BasicView const& view, AccountID const& account, AccountID const& issuer, Currency const& currency) { STAmount result ({currency, account}); - auto sleRippleState = ledger.entryCache (ltRIPPLE_STATE, - getRippleStateIndex (account, issuer, currency)); + auto sleRippleState = view.read( + keylet::line(account, issuer, currency)); if (sleRippleState) { diff --git a/src/ripple/app/paths/Credit.h b/src/ripple/app/paths/Credit.h index 82dcea8449..473fec2edd 100644 --- a/src/ripple/app/paths/Credit.h +++ b/src/ripple/app/paths/Credit.h @@ -20,7 +20,7 @@ #ifndef RIPPLE_APP_PATHS_CREDIT_H_INCLUDED #define RIPPLE_APP_PATHS_CREDIT_H_INCLUDED -#include +#include #include namespace ripple { @@ -33,7 +33,7 @@ namespace ripple { @return The maximum amount that can be held. */ STAmount creditLimit ( - LedgerEntrySet& ledger, + BasicView const& view, AccountID const& account, AccountID const& issuer, Currency const& currency); @@ -45,7 +45,7 @@ STAmount creditLimit ( @param currency the IOU to check. */ STAmount creditBalance ( - LedgerEntrySet& ledger, + BasicView const& view, AccountID const& account, AccountID const& issuer, Currency const& currency); diff --git a/src/ripple/app/paths/NodeDirectory.h b/src/ripple/app/paths/NodeDirectory.h index 1d8791761b..b1b17578da 100644 --- a/src/ripple/app/paths/NodeDirectory.h +++ b/src/ripple/app/paths/NodeDirectory.h @@ -20,7 +20,7 @@ #ifndef RIPPLE_APP_PATHS_NODEDIRECTORY_H_INCLUDED #define RIPPLE_APP_PATHS_NODEDIRECTORY_H_INCLUDED -#include +#include #include namespace ripple { @@ -52,7 +52,7 @@ class NodeDirectory { restartNeeded = true; // Restart at same quality. } - bool initialize (Book const& book, LedgerEntrySet& les) + bool initialize (Book const& book, MetaView& les) { if (current != zero) return false; @@ -63,7 +63,7 @@ class NodeDirectory { // TODO(tom): it seems impossible that any actual offers with // quality == 0 could occur - we should disallow them, and clear // directory.ledgerEntry without the database call in the next line. - ledgerEntry = les.entryCache (ltDIR_NODE, current); + ledgerEntry = les.peek (keylet::page(current)); // Advance, if didn't find it. Normal not to be unable to lookup // firstdirectory. Maybe even skip this lookup. @@ -75,7 +75,7 @@ class NodeDirectory { } enum Advance {NO_ADVANCE, NEW_QUALITY, END_ADVANCE}; - Advance advance(LedgerEntrySet& les) + Advance advance(MetaView& les) { if (!(advanceNeeded || restartNeeded)) return NO_ADVANCE; @@ -84,15 +84,18 @@ class NodeDirectory { // The Merkel radix tree is ordered by key so we can go to the next // quality in O(1). if (advanceNeeded) - current = les.getNextLedgerIndex (current, next); - + { + auto const opt = + les.succ (current, next); + current = opt ? *opt : uint256{}; + } advanceNeeded = false; restartNeeded = false; if (current == zero) return END_ADVANCE; - ledgerEntry = les.entryCache (ltDIR_NODE, current); + ledgerEntry = les.peek (keylet::page(current)); return NEW_QUALITY; } }; diff --git a/src/ripple/app/paths/PathRequest.cpp b/src/ripple/app/paths/PathRequest.cpp index 9eb7b92aae..e5a19352da 100644 --- a/src/ripple/app/paths/PathRequest.cpp +++ b/src/ripple/app/paths/PathRequest.cpp @@ -480,7 +480,7 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast) if (valid) { - LedgerEntrySet lesSandbox( + MetaView sandbox( cache->getLedger(), tapNONE); auto& sourceAccount = !isXRP (currIssuer.account) ? currIssuer.account @@ -493,7 +493,7 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast) m_journal.debug << iIdentifier << " Paths found, calling rippleCalc"; auto rc = path::RippleCalc::rippleCalculate ( - lesSandbox, + sandbox, saMaxAmount, saDstAmount, raDstAccount.getAccountID (), @@ -506,9 +506,9 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast) m_journal.debug << iIdentifier << " Trying with an extra path element"; spsPaths.push_back (fullLiquidityPath); - reconstruct(lesSandbox, cache->getLedger (), tapNONE); + reconstruct(sandbox, cache->getLedger (), tapNONE); rc = path::RippleCalc::rippleCalculate ( - lesSandbox, + sandbox, saMaxAmount, saDstAmount, raDstAccount.getAccountID (), diff --git a/src/ripple/app/paths/PathState.cpp b/src/ripple/app/paths/PathState.cpp index 4b9bbc6a65..ab9937b884 100644 --- a/src/ripple/app/paths/PathState.cpp +++ b/src/ripple/app/paths/PathState.cpp @@ -268,12 +268,8 @@ TER PathState::pushNode ( auto const& backNode = nodes_.back (); if (backNode.isAccount()) { - auto sleRippleState = lesEntries_->entryCache ( - ltRIPPLE_STATE, - getRippleStateIndex ( - backNode.account_, - node.account_, - backNode.issue_.currency)); + auto sleRippleState = metaView_->peek( + keylet::line(backNode.account_, node.account_, backNode.issue_.currency)); // A "RippleState" means a balance betweeen two accounts for a // specific currency. @@ -295,9 +291,8 @@ TER PathState::pushNode ( << backNode.account_ << " and " << node.account_ << " for " << node.issue_.currency << "." ; - auto sleBck = lesEntries_->entryCache ( - ltACCOUNT_ROOT, - getAccountRootIndex (backNode.account_)); + auto sleBck = metaView_->peek ( + keylet::account(backNode.account_)); // Is the source account the highest numbered account ID? bool bHigh = backNode.account_ > node.account_; @@ -323,13 +318,13 @@ TER PathState::pushNode ( if (resultCode == tesSUCCESS) { - STAmount saOwed = creditBalance (*lesEntries_, + STAmount saOwed = creditBalance (*metaView_, node.account_, backNode.account_, node.issue_.currency); STAmount saLimit; if (saOwed <= zero) { - saLimit = creditLimit (*lesEntries_, + saLimit = creditLimit (*metaView_, node.account_, backNode.account_, node.issue_.currency); @@ -418,7 +413,7 @@ TER PathState::pushNode ( // terStatus = tesSUCCESS, temBAD_PATH, terNO_LINE, terNO_ACCOUNT, terNO_AUTH, // or temBAD_PATH_LOOP TER PathState::expandPath ( - const LedgerEntrySet& lesSource, + MetaView const& viewSource, STPath const& spSourcePath, AccountID const& uReceiverID, AccountID const& uSenderID) @@ -437,7 +432,7 @@ TER PathState::expandPath ( WriteLog (lsTRACE, RippleCalc) << "expandPath> " << spSourcePath.getJson (0); - lesEntries_.emplace(lesSource); + metaView_.emplace(viewSource); terStatus = tesSUCCESS; @@ -632,8 +627,7 @@ void PathState::checkFreeze() // Check each order book for a global freeze if (nodes_[i].uFlags & STPathElement::typeIssuer) { - sle = lesEntries_->entryCache (ltACCOUNT_ROOT, - getAccountRootIndex (nodes_[i].issue_.account)); + sle = metaView_->peek (keylet::account(nodes_[i].issue_.account)); if (sle && sle->isFlag (lsfGlobalFreeze)) { @@ -651,8 +645,7 @@ void PathState::checkFreeze() if (inAccount != outAccount) { - sle = lesEntries_->entryCache (ltACCOUNT_ROOT, - getAccountRootIndex (outAccount)); + sle = metaView_->peek (keylet::account(outAccount)); if (sle && sle->isFlag (lsfGlobalFreeze)) { @@ -660,8 +653,7 @@ void PathState::checkFreeze() return; } - sle = lesEntries_->entryCache (ltRIPPLE_STATE, - getRippleStateIndex (inAccount, + sle = metaView_->peek (keylet::line(inAccount, outAccount, currencyID)); if (sle && sle->isFlag ( @@ -688,10 +680,10 @@ TER PathState::checkNoRipple ( Currency const& currency) { // fetch the ripple lines into and out of this node - SLE::pointer sleIn = lesEntries_->entryCache (ltRIPPLE_STATE, - getRippleStateIndex (firstAccount, secondAccount, currency)); - SLE::pointer sleOut = lesEntries_->entryCache (ltRIPPLE_STATE, - getRippleStateIndex (secondAccount, thirdAccount, currency)); + SLE::pointer sleIn = metaView_->peek ( + keylet::line(firstAccount, secondAccount, currency)); + SLE::pointer sleOut = metaView_->peek ( + keylet::line(secondAccount, thirdAccount, currency)); if (!sleIn || !sleOut) { diff --git a/src/ripple/app/paths/PathState.h b/src/ripple/app/paths/PathState.h index 6363a87712..b5285d63b3 100644 --- a/src/ripple/app/paths/PathState.h +++ b/src/ripple/app/paths/PathState.h @@ -20,7 +20,7 @@ #ifndef RIPPLE_APP_PATHS_PATHSTATE_H_INCLUDED #define RIPPLE_APP_PATHS_PATHSTATE_H_INCLUDED -#include +#include #include #include #include @@ -48,10 +48,10 @@ class PathState : public CountedObject void reset(STAmount const& in, STAmount const& out); TER expandPath ( - LedgerEntrySet const& lesSource, - STPath const& spSourcePath, - AccountID const& uReceiverID, - AccountID const& uSenderID + MetaView const& viewSource, + STPath const& spSourcePath, + AccountID const& uReceiverID, + AccountID const& uSenderID ); path::Node::List& nodes() { return nodes_; } @@ -103,10 +103,9 @@ class PathState : public CountedObject static bool lessPriority (PathState const& lhs, PathState const& rhs); - // VFALCO Remove or rename to view, - LedgerEntrySet& ledgerEntries() + MetaView& metaView() { - return *lesEntries_; + return *metaView_; } bool isDry() const @@ -150,7 +149,7 @@ private: // Source may only be used there if not mentioned by an account. AccountIssueToNodeIndex umReverse; - boost::optional lesEntries_; + boost::optional metaView_; int mIndex; // Index/rank amoung siblings. std::uint64_t uQuality; // 0 = no quality/liquity left. diff --git a/src/ripple/app/paths/Pathfinder.cpp b/src/ripple/app/paths/Pathfinder.cpp index 179ba41636..f4ffdd4eec 100644 --- a/src/ripple/app/paths/Pathfinder.cpp +++ b/src/ripple/app/paths/Pathfinder.cpp @@ -271,7 +271,7 @@ bool Pathfinder::findPaths (int searchLevel) bool bSrcXrp = isXRP (mSrcCurrency); bool bDstXrp = isXRP (mDstAmount.getCurrency()); - if (! mLedger->exists (getAccountRootIndex (mSrcAccount))) + if (! mLedger->exists (keylet::account(mSrcAccount))) { // We can't even start without a source account. WriteLog (lsDEBUG, Pathfinder) << "invalid source account"; @@ -279,14 +279,14 @@ bool Pathfinder::findPaths (int searchLevel) } if ((mEffectiveDst != mDstAccount) && - ! mLedger->exists (getAccountRootIndex (mEffectiveDst))) + ! mLedger->exists (keylet::account(mEffectiveDst))) { WriteLog (lsDEBUG, Pathfinder) << "Non-existent gateway"; return false; } - if (! mLedger->exists (getAccountRootIndex (mDstAccount))) + if (! mLedger->exists (keylet::account (mDstAccount))) { // Can't find the destination account - we must be funding a new // account. @@ -377,13 +377,13 @@ TER Pathfinder::getPathLiquidity ( path::RippleCalc::Input rcInput; rcInput.defaultPathsAllowed = false; - LedgerEntrySet lesSandbox (mLedger, tapNONE); + MetaView sandbox (mLedger, tapNONE); try { // Compute a path that provides at least the minimum liquidity. auto rc = path::RippleCalc::rippleCalculate ( - lesSandbox, + sandbox, mSrcAmount, minDstAmount, mDstAccount, @@ -401,7 +401,7 @@ TER Pathfinder::getPathLiquidity ( // Now try to compute the remaining liquidity. rcInput.partialPaymentAllowed = true; rc = path::RippleCalc::rippleCalculate ( - lesSandbox, + sandbox, mSrcAmount, mDstAmount - amountOut, mDstAccount, @@ -442,12 +442,12 @@ void Pathfinder::computePathRanks (int maxPaths) // Must subtract liquidity in default path from remaining amount. try { - LedgerEntrySet lesSandbox (mLedger, tapNONE); + MetaView sandbox (mLedger, tapNONE); path::RippleCalc::Input rcInput; rcInput.partialPaymentAllowed = true; auto rc = path::RippleCalc::rippleCalculate ( - lesSandbox, + sandbox, mSrcAmount, mDstAmount, mDstAccount, @@ -703,7 +703,7 @@ int Pathfinder::getPathsOut ( if (!it.second) return it.first->second; - auto sleAccount = fetch(*mLedger, getAccountRootIndex (account), + auto sleAccount = cachedRead(*mLedger, getAccountRootIndex (account), getApp().getSLECache()); if (!sleAccount) @@ -840,7 +840,7 @@ bool Pathfinder::isNoRipple ( AccountID const& toAccount, Currency const& currency) { - auto sleRipple = fetch (*mLedger, + auto sleRipple = cachedRead (*mLedger, getRippleStateIndex (toAccount, fromAccount, currency), getApp().getSLECache()); @@ -920,7 +920,7 @@ void Pathfinder::addLink ( else { // search for accounts to add - auto const sleEnd = fetch( + auto const sleEnd = cachedRead( *mLedger, getAccountRootIndex (uEndAccount), getApp().getSLECache()); diff --git a/src/ripple/app/paths/RippleCalc.cpp b/src/ripple/app/paths/RippleCalc.cpp index 87d1488430..b2d2fb661e 100644 --- a/src/ripple/app/paths/RippleCalc.cpp +++ b/src/ripple/app/paths/RippleCalc.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include namespace ripple { @@ -28,21 +29,22 @@ namespace path { namespace { -TER deleteOffers ( - LedgerEntrySet& activeLedger, OfferSet& offers) +static +TER +deleteOffers (View& view, + OfferSet const& offers) { - for (auto& o: offers) - { - if (TER r = activeLedger.offerDelete (o)) + for (auto& e: offers) + if (TER r = offerDelete (view, + view.peek(keylet::offer(e)))) return r; - } return tesSUCCESS; } } // namespace RippleCalc::Output RippleCalc::rippleCalculate ( - LedgerEntrySet& activeLedger, + MetaView& activeLedger, // Compute paths using this ledger entry set. Up to caller to actually // apply to ledger. @@ -101,7 +103,7 @@ bool RippleCalc::addPathState(STPath const& path, TER& resultCode) } pathState->expandPath ( - mActiveLedger, + metaView, path, uDstAccountID_, uSrcAccountID_); @@ -144,7 +146,7 @@ bool RippleCalc::addPathState(STPath const& path, TER& resultCode) // <-- TER: Only returns tepPATH_PARTIAL if partialPaymentAllowed. TER RippleCalc::rippleCalculate () { - assert (mActiveLedger.isValid ()); + assert (metaView.isValid ()); WriteLog (lsTRACE, RippleCalc) << "rippleCalc>" << " saMaxAmountReq_:" << saMaxAmountReq_ @@ -206,7 +208,7 @@ TER RippleCalc::rippleCalculate () while (resultCode == temUNCERTAIN) { int iBest = -1; - LedgerEntrySet lesCheckpoint = mActiveLedger; + MetaView lesCheckpoint = metaView; int iDry = 0; // True, if ever computed multi-quality. @@ -285,12 +287,12 @@ TER RippleCalc::rippleCalculate () << " inPass()=" << pathState->inPass() << " saOutPass=" << pathState->outPass(); - assert (mActiveLedger.isValid ()); - mActiveLedger.swapWith (pathState->ledgerEntries()); + assert (metaView.isValid ()); + metaView.swapWith (pathState->metaView()); // For the path, save ledger state. // VFALCO Can this be done without the function call? - mActiveLedger.deprecatedInvalidate(); + metaView.deprecatedInvalidate(); iBest = pathState->index (); } @@ -337,14 +339,14 @@ TER RippleCalc::rippleCalculate () pathState->unfundedOffers().begin (), pathState->unfundedOffers().end ()); - // Record best pass' LedgerEntrySet to build off of and potentially + // Record best pass' MetaView to build off of and potentially // return. - assert (pathState->ledgerEntries().isValid ()); - mActiveLedger.swapWith (pathState->ledgerEntries()); + assert (pathState->metaView().isValid ()); + metaView.swapWith (pathState->metaView()); // VFALCO Why is this needed? Can it be done // without the function call? - pathState->ledgerEntries().deprecatedInvalidate(); + pathState->metaView().deprecatedInvalidate(); actualAmountIn_ += pathState->inPass(); actualAmountOut_ += pathState->outPass(); @@ -422,16 +424,16 @@ TER RippleCalc::rippleCalculate () // We must restore the activeLedger from lesCheckpoint in the case // when iBest is -1 and just before the result is set to tesSUCCESS. - mActiveLedger.swapWith (lesCheckpoint); + metaView.swapWith (lesCheckpoint); resultCode = tesSUCCESS; } } if (resultCode == tesSUCCESS) { - resultCode = deleteOffers(mActiveLedger, unfundedOffersFromBestPaths); + resultCode = deleteOffers(metaView, unfundedOffersFromBestPaths); if (resultCode == tesSUCCESS) - resultCode = deleteOffers(mActiveLedger, permanentlyUnfundedOffers_); + resultCode = deleteOffers(metaView, permanentlyUnfundedOffers_); } // If isOpenLedger, then ledger is not final, can vote no. diff --git a/src/ripple/app/paths/RippleCalc.h b/src/ripple/app/paths/RippleCalc.h index 385c932bbc..7e9b7e257e 100644 --- a/src/ripple/app/paths/RippleCalc.h +++ b/src/ripple/app/paths/RippleCalc.h @@ -20,7 +20,7 @@ #ifndef RIPPLE_APP_PATHS_RIPPLECALC_H_INCLUDED #define RIPPLE_APP_PATHS_RIPPLECALC_H_INCLUDED -#include +#include #include #include #include @@ -73,7 +73,7 @@ public: }; static Output rippleCalculate ( - LedgerEntrySet& activeLedger, + MetaView& activeLedger, // Compute paths using this ledger entry set. Up to caller to actually // apply to ledger. @@ -99,7 +99,9 @@ public: Input const* const pInputs = nullptr); /** The active ledger. */ - LedgerEntrySet& mActiveLedger; + // VFALCO TODO Fix this comment its not the ledger + // VFALCO TODO Rename this to view + MetaView& metaView; // If the transaction fails to meet some constraint, still need to delete // unfunded offers. @@ -115,14 +117,14 @@ public: private: RippleCalc ( - LedgerEntrySet& activeLedger, + MetaView& activeLedger, STAmount const& saMaxAmountReq, // --> -1 = no limit. STAmount const& saDstAmountReq, AccountID const& uDstAccountID, AccountID const& uSrcAccountID, STPathSet const& spsPaths) - : mActiveLedger (activeLedger), + : metaView (activeLedger), saDstAmountReq_(saDstAmountReq), saMaxAmountReq_(saMaxAmountReq), uDstAccountID_(uDstAccountID), diff --git a/src/ripple/app/paths/RippleState.cpp b/src/ripple/app/paths/RippleState.cpp index 3e84f9e794..db1db3eae4 100644 --- a/src/ripple/app/paths/RippleState.cpp +++ b/src/ripple/app/paths/RippleState.cpp @@ -18,6 +18,8 @@ //============================================================================== #include +#include +#include #include #include #include @@ -75,8 +77,10 @@ getRippleStateItems ( AccountID const& accountID, Ledger::ref ledger) { + CachedView const view( + *ledger, getApp().getSLECache()); std::vector items; - forEachItem(*ledger, accountID, getApp().getSLECache(), + forEachItem(view, accountID, [&items,&accountID]( std::shared_ptr const&sleCur) { diff --git a/src/ripple/app/paths/RippleState.h b/src/ripple/app/paths/RippleState.h index f406661120..2ce0a3c479 100644 --- a/src/ripple/app/paths/RippleState.h +++ b/src/ripple/app/paths/RippleState.h @@ -20,7 +20,7 @@ #ifndef RIPPLE_APP_PATHS_RIPPLESTATE_H_INCLUDED #define RIPPLE_APP_PATHS_RIPPLESTATE_H_INCLUDED -#include +#include #include #include #include diff --git a/src/ripple/app/paths/cursor/AdvanceNode.cpp b/src/ripple/app/paths/cursor/AdvanceNode.cpp index af20c4acfa..1e54c1faa0 100644 --- a/src/ripple/app/paths/cursor/AdvanceNode.cpp +++ b/src/ripple/app/paths/cursor/AdvanceNode.cpp @@ -19,6 +19,7 @@ #include #include +#include #include namespace ripple { @@ -128,10 +129,11 @@ TER PathCursor::advanceNode (bool const bReverse) const = node().sleOffer->getFieldAmount (sfTakerGets); // Funds left. - node().saOfferFunds = funds (ledger(), + node().saOfferFunds = accountFunds (ledger(), node().offerOwnerAccount_, node().saTakerGets, - fhZERO_IF_FROZEN); + fhZERO_IF_FROZEN, + getConfig()); node().bFundsDirty = false; WriteLog (lsTRACE, RippleCalc) @@ -143,7 +145,7 @@ TER PathCursor::advanceNode (bool const bReverse) const WriteLog (lsTRACE, RippleCalc) << "advanceNode: as is"; } } - else if (!ledger().dirNext ( + else if (!dirNext (ledger(), node().directory.current, node().directory.ledgerEntry, node().uEntry, @@ -180,8 +182,7 @@ TER PathCursor::advanceNode (bool const bReverse) const else { // Got a new offer. - node().sleOffer = ledger().entryCache ( - ltOFFER, node().offerIndex_); + node().sleOffer = ledger().peek (keylet::offer(node().offerIndex_)); if (!node().sleOffer) { @@ -211,9 +212,8 @@ TER PathCursor::advanceNode (bool const bReverse) const << " node.offerIndex_=" << node().offerIndex_; if (node().sleOffer->isFieldPresent (sfExpiration) && - (node().sleOffer->getFieldU32 (sfExpiration) <= - ledger().getLedger ()-> - getParentCloseTimeNC ())) + (node().sleOffer->getFieldU32 (sfExpiration) <= + getParentCloseTimeNC(ledger()))) { // Offer is expired. WriteLog (lsTRACE, RippleCalc) @@ -329,10 +329,11 @@ TER PathCursor::advanceNode (bool const bReverse) const // Only the current node is allowed to use the source. - node().saOfferFunds = funds(ledger(), + node().saOfferFunds = accountFunds(ledger(), node().offerOwnerAccount_, node().saTakerGets, - fhZERO_IF_FROZEN); + fhZERO_IF_FROZEN, + getConfig()); // Funds held. if (node().saOfferFunds <= zero) diff --git a/src/ripple/app/paths/cursor/DeliverNodeForward.cpp b/src/ripple/app/paths/cursor/DeliverNodeForward.cpp index 136dcdedee..67fd89a518 100644 --- a/src/ripple/app/paths/cursor/DeliverNodeForward.cpp +++ b/src/ripple/app/paths/cursor/DeliverNodeForward.cpp @@ -19,6 +19,7 @@ #include #include +#include #include namespace ripple { @@ -195,7 +196,7 @@ TER PathCursor::deliverNodeForward ( // Output: Debit offer owner, send XRP or non-XPR to next // account. - resultCode = ledger().accountSend ( + resultCode = accountSend (ledger(), node().offerOwnerAccount_, nextNode().account_, saOutPassAct); @@ -252,7 +253,7 @@ TER PathCursor::deliverNodeForward ( auto const& id = isXRP(node().issue_) ? xrpAccount() : node().issue_.account; auto outPassTotal = saOutPassAct + saOutPassFees; - ledger().accountSend ( + accountSend (ledger(), node().offerOwnerAccount_, id, outPassTotal); @@ -286,7 +287,7 @@ TER PathCursor::deliverNodeForward ( { auto id = !isXRP(previousNode().issue_.currency) ? uInAccountID : xrpAccount(); - resultCode = ledger().accountSend ( + resultCode = accountSend (ledger(), id, node().offerOwnerAccount_, saInPassAct); @@ -316,7 +317,7 @@ TER PathCursor::deliverNodeForward ( node().sleOffer->setFieldAmount (sfTakerGets, saTakerGetsNew); node().sleOffer->setFieldAmount (sfTakerPays, saTakerPaysNew); - ledger().entryModify (node().sleOffer); + ledger().update (node().sleOffer); if (saOutPassAct == saOutFunded || saTakerGetsNew == zero) { diff --git a/src/ripple/app/paths/cursor/DeliverNodeReverse.cpp b/src/ripple/app/paths/cursor/DeliverNodeReverse.cpp index 9872c5b66a..5e241d4804 100644 --- a/src/ripple/app/paths/cursor/DeliverNodeReverse.cpp +++ b/src/ripple/app/paths/cursor/DeliverNodeReverse.cpp @@ -19,6 +19,7 @@ #include #include +#include #include namespace ripple { @@ -279,7 +280,7 @@ TER PathCursor::deliverNodeReverse ( // visited. However, these deductions and adjustments are tenative. // // Must reset balances when going forward to perform actual transfers. - resultCode = ledger().accountSend ( + resultCode = accountSend (ledger(), node().offerOwnerAccount_, node().issue_.account, saOutPassAct); if (resultCode != tesSUCCESS) @@ -303,7 +304,7 @@ TER PathCursor::deliverNodeReverse ( node().sleOffer->setFieldAmount (sfTakerGets, saTakerGetsNew); node().sleOffer->setFieldAmount (sfTakerPays, saTakerPaysNew); - ledger().entryModify (node().sleOffer); + ledger().update (node().sleOffer); if (saOutPassAct == node().saTakerGets) { diff --git a/src/ripple/app/paths/cursor/ForwardLiquidityForAccount.cpp b/src/ripple/app/paths/cursor/ForwardLiquidityForAccount.cpp index 276b2260e6..907c7a0e6e 100644 --- a/src/ripple/app/paths/cursor/ForwardLiquidityForAccount.cpp +++ b/src/ripple/app/paths/cursor/ForwardLiquidityForAccount.cpp @@ -18,9 +18,10 @@ //============================================================================== #include -#include #include +#include #include +#include namespace ripple { namespace path { @@ -166,7 +167,7 @@ TER PathCursor::forwardLiquidityForAccount () const if (saCurReceive) { // Actually receive. - resultCode = ledger().rippleCredit ( + resultCode = rippleCredit (ledger(), previousAccountID, node().account_, previousNode().saFwdRedeem + previousNode().saFwdIssue, @@ -266,7 +267,7 @@ TER PathCursor::forwardLiquidityForAccount () const // Adjust prv --> cur balance : take all inbound resultCode = saProvide - ? ledger().rippleCredit ( + ? rippleCredit (ledger(), previousAccountID, node().account_, previousNode().saFwdRedeem + previousNode().saFwdIssue, @@ -331,7 +332,7 @@ TER PathCursor::forwardLiquidityForAccount () const // Adjust prv --> cur balance : take all inbound resultCode = node().saFwdDeliver - ? ledger().rippleCredit ( + ? rippleCredit (ledger(), previousAccountID, node().account_, previousNode().saFwdRedeem + previousNode().saFwdIssue, false) @@ -353,11 +354,12 @@ TER PathCursor::forwardLiquidityForAccount () const if (isXRP (node().issue_)) node().saFwdDeliver = std::min ( node().saFwdDeliver, - ledger().accountHolds ( + accountHolds (ledger(), node().account_, xrpCurrency(), xrpAccount(), - fhIGNORE_FREEZE)); // XRP can't be frozen + fhIGNORE_FREEZE, + getConfig())); // XRP can't be frozen } @@ -389,7 +391,7 @@ TER PathCursor::forwardLiquidityForAccount () const << "ACCOUNT -- XRP --> offer"; // Deliver XRP to limbo. - resultCode = ledger().accountSend ( + resultCode = accountSend (ledger(), node().account_, xrpAccount(), node().saFwdDeliver); } } diff --git a/src/ripple/app/paths/cursor/Liquidity.cpp b/src/ripple/app/paths/cursor/Liquidity.cpp index ff8077a4e0..52a580c984 100644 --- a/src/ripple/app/paths/cursor/Liquidity.cpp +++ b/src/ripple/app/paths/cursor/Liquidity.cpp @@ -25,13 +25,13 @@ namespace ripple { namespace path { -TER PathCursor::liquidity (LedgerEntrySet const& lesCheckpoint) const +TER PathCursor::liquidity (MetaView const& lesCheckpoint) const { TER resultCode = tecPATH_DRY; PathCursor pc = *this; // duplicate - reconstruct(rippleCalc_.mActiveLedger, lesCheckpoint); + reconstruct(rippleCalc_.metaView, lesCheckpoint); for (pc.nodeIndex_ = pc.nodeSize(); pc.nodeIndex_--; ) { @@ -62,7 +62,7 @@ TER PathCursor::liquidity (LedgerEntrySet const& lesCheckpoint) const // Do forward. // duplicate - reconstruct(rippleCalc_.mActiveLedger, lesCheckpoint); + reconstruct(rippleCalc_.metaView, lesCheckpoint); for (pc.nodeIndex_ = 0; pc.nodeIndex_ < pc.nodeSize(); ++pc.nodeIndex_) { diff --git a/src/ripple/app/paths/cursor/NextIncrement.cpp b/src/ripple/app/paths/cursor/NextIncrement.cpp index c7c2a78e80..09e6ca51ab 100644 --- a/src/ripple/app/paths/cursor/NextIncrement.cpp +++ b/src/ripple/app/paths/cursor/NextIncrement.cpp @@ -34,7 +34,7 @@ namespace path { // This is the wrapper that restores a checkpointed version of the ledger so we // can write all over it without consequence. -void PathCursor::nextIncrement (LedgerEntrySet const& lesCheckpoint) const +void PathCursor::nextIncrement (MetaView const& lesCheckpoint) const { // The next state is what is available in preference order. // This is calculated when referenced accounts changed. diff --git a/src/ripple/app/paths/cursor/PathCursor.h b/src/ripple/app/paths/cursor/PathCursor.h index b83026ca0f..c66ae9ab62 100644 --- a/src/ripple/app/paths/cursor/PathCursor.h +++ b/src/ripple/app/paths/cursor/PathCursor.h @@ -50,7 +50,7 @@ public: { } - void nextIncrement(LedgerEntrySet const& checkpoint) const; + void nextIncrement(MetaView const& checkpoint) const; private: PathCursor(PathCursor const&) = default; @@ -60,7 +60,7 @@ private: return {rippleCalc_, pathState_, multiQuality_, nodeIndex_ + delta}; } - TER liquidity(LedgerEntrySet const& lesCheckpoint) const; + TER liquidity(MetaView const& lesCheckpoint) const; TER reverseLiquidity () const; TER forwardLiquidity () const; @@ -91,9 +91,11 @@ private: STAmount& saInAct, STAmount& saInFees) const; - LedgerEntrySet& ledger() const + // VFALCO TODO Rename this to view() + MetaView& ledger() const { - return rippleCalc_.mActiveLedger; + // VFALCO Rename metaView to view + return rippleCalc_.metaView; } NodeIndex nodeSize() const diff --git a/src/ripple/app/paths/cursor/ReverseLiquidity.cpp b/src/ripple/app/paths/cursor/ReverseLiquidity.cpp index c16f539402..aa0d1412c3 100644 --- a/src/ripple/app/paths/cursor/ReverseLiquidity.cpp +++ b/src/ripple/app/paths/cursor/ReverseLiquidity.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include diff --git a/src/ripple/app/paths/cursor/ReverseLiquidityForAccount.cpp b/src/ripple/app/paths/cursor/ReverseLiquidityForAccount.cpp index d95334b1eb..4caa457ad3 100644 --- a/src/ripple/app/paths/cursor/ReverseLiquidityForAccount.cpp +++ b/src/ripple/app/paths/cursor/ReverseLiquidityForAccount.cpp @@ -18,10 +18,11 @@ //============================================================================== #include -#include #include #include +#include #include +#include namespace ripple { namespace path { diff --git a/src/ripple/app/paths/cursor/RippleLiquidity.cpp b/src/ripple/app/paths/cursor/RippleLiquidity.cpp index 2bc8852acf..bad0284980 100644 --- a/src/ripple/app/paths/cursor/RippleLiquidity.cpp +++ b/src/ripple/app/paths/cursor/RippleLiquidity.cpp @@ -203,7 +203,7 @@ void rippleLiquidity ( static std::uint32_t rippleQuality ( - LedgerEntrySet& ledger, + BasicView const& view, AccountID const& destination, AccountID const& source, Currency const& currency, @@ -214,8 +214,8 @@ rippleQuality ( if (destination != source) { - SLE::pointer sleRippleState (ledger.entryCache (ltRIPPLE_STATE, - getRippleStateIndex (destination, source, currency))); + auto const sleRippleState = view.read( + keylet::line(destination, source, currency)); // we should be able to assert(sleRippleState) here @@ -237,23 +237,23 @@ rippleQuality ( std::uint32_t quality_in ( - LedgerEntrySet& ledger, + BasicView const& view, AccountID const& uToAccountID, AccountID const& uFromAccountID, Currency const& currency) { - return rippleQuality (ledger, uToAccountID, uFromAccountID, currency, + return rippleQuality (view, uToAccountID, uFromAccountID, currency, sfLowQualityIn, sfHighQualityIn); } std::uint32_t quality_out ( - LedgerEntrySet& ledger, + BasicView const& view, AccountID const& uToAccountID, AccountID const& uFromAccountID, Currency const& currency) { - return rippleQuality (ledger, uToAccountID, uFromAccountID, currency, + return rippleQuality (view, uToAccountID, uFromAccountID, currency, sfLowQualityOut, sfHighQualityOut); } diff --git a/src/ripple/app/paths/cursor/RippleLiquidity.h b/src/ripple/app/paths/cursor/RippleLiquidity.h index d2e0677268..a91653be11 100644 --- a/src/ripple/app/paths/cursor/RippleLiquidity.h +++ b/src/ripple/app/paths/cursor/RippleLiquidity.h @@ -20,11 +20,10 @@ #ifndef RIPPLE_APP_PATHS_CURSOR_RIPPLELIQUIDITY_H_INCLUDED #define RIPPLE_APP_PATHS_CURSOR_RIPPLELIQUIDITY_H_INCLUDED -#include - #include #include #include +#include namespace ripple { namespace path { @@ -41,14 +40,14 @@ void rippleLiquidity ( std::uint32_t quality_in ( - LedgerEntrySet& ledger, + BasicView const& view, AccountID const& uToAccountID, AccountID const& uFromAccountID, Currency const& currency); std::uint32_t quality_out ( - LedgerEntrySet& ledger, + BasicView const& view, AccountID const& uToAccountID, AccountID const& uFromAccountID, Currency const& currency); diff --git a/src/ripple/app/tests/Regression_test.cpp b/src/ripple/app/tests/Regression_test.cpp new file mode 100644 index 0000000000..9717f99a55 --- /dev/null +++ b/src/ripple/app/tests/Regression_test.cpp @@ -0,0 +1,49 @@ +//------------------------------------------------------------------------------ +/* + 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 + +namespace ripple { +namespace test { + +struct Regression_test : public beast::unit_test::suite +{ + // OfferCreate, then OfferCreate with cancel + void testOffer1() + { + using namespace jtx; + Env env(*this); + auto const gw = Account("gw"); + auto const USD = gw["USD"]; + env.fund(XRP(10000), "alice", gw); + env(offer("alice", USD(10), XRP(10)), require(owners("alice", 1))); + env(offer("alice", USD(20), XRP(10)), json(R"raw( + { "OfferSequence" : 2 } + )raw"), require(owners("alice", 1))); + } + + void run() override + { + testOffer1(); + } +}; + +BEAST_DEFINE_TESTSUITE(Regression,app,ripple); + +} // test +} // ripple diff --git a/src/ripple/app/tx/TransactionEngine.h b/src/ripple/app/tx/TransactionEngine.h index f50956f62b..ef8dddf0b8 100644 --- a/src/ripple/app/tx/TransactionEngine.h +++ b/src/ripple/app/tx/TransactionEngine.h @@ -21,7 +21,7 @@ #define RIPPLE_APP_TX_TRANSACTIONENGINE_H_INCLUDED #include -#include +#include #include #include @@ -52,7 +52,7 @@ private: false; #endif - boost::optional mNodes; + boost::optional mNodes; void txnWrite(); @@ -91,7 +91,8 @@ public: return enableTickets_; } - LedgerEntrySet& + // VFALCO TODO Change to return `View&` + MetaView& view () { return *mNodes; @@ -110,6 +111,13 @@ public: mLedger = ledger; } + /** Sets the DeliveredAmount field in the metadata */ + void + deliverAmount (STAmount const& delivered) + { + mNodes->setDeliveredAmount(delivered); + } + std::pair applyTransaction (STTx const&, TransactionEngineParams); diff --git a/src/ripple/app/tx/impl/BookTip.cpp b/src/ripple/app/tx/impl/BookTip.cpp index f85a6a374e..a7b8f2e079 100644 --- a/src/ripple/app/tx/impl/BookTip.cpp +++ b/src/ripple/app/tx/impl/BookTip.cpp @@ -19,11 +19,12 @@ #include #include +#include namespace ripple { -BookTip::BookTip (LedgerView& view, BookRef book) - : m_view (view) +BookTip::BookTip (View& view, BookRef book) + : view_ (view) , m_valid (false) , m_book (getBookBase (book)) , m_end (getQualityNext (m_book)) @@ -37,7 +38,7 @@ BookTip::step () { if (m_entry) { - view().offerDelete (m_index); + offerDelete (view_, m_entry); m_entry = nullptr; } } @@ -47,23 +48,24 @@ BookTip::step () // See if there's an entry at or worse than current quality. Notice // that the quality is encoded only in the index of the first page // of a directory. - auto const first_page (view().getNextLedgerIndex (m_book, m_end)); + auto const first_page = + view_.succ (m_book, m_end); - if (first_page.isZero()) + if (! first_page) return false; - unsigned int di (0); - SLE::pointer dir; + unsigned int di = 0; + std::shared_ptr dir; - if (view().dirFirst (first_page, dir, di, m_index)) + if (dirFirst (view_, *first_page, dir, di, m_index)) { - m_dir = dir->getIndex(); - m_entry = view().entryCache (ltOFFER, m_index); - m_quality = Quality (getQuality (first_page)); + m_dir = dir->key(); + m_entry = view_.peek(keylet::offer(m_index)); + m_quality = Quality (getQuality (*first_page)); m_valid = true; // Next query should start before this directory - m_book = first_page; + m_book = *first_page; // The quality immediately before the next quality --m_book; @@ -73,7 +75,7 @@ BookTip::step () // There should never be an empty directory but just in case, // we handle that case by advancing to the next directory. - m_book = first_page; + m_book = *first_page; } return true; diff --git a/src/ripple/app/tx/impl/BookTip.h b/src/ripple/app/tx/impl/BookTip.h index 462d913806..d7f7e63d04 100644 --- a/src/ripple/app/tx/impl/BookTip.h +++ b/src/ripple/app/tx/impl/BookTip.h @@ -21,7 +21,7 @@ #define RIPPLE_APP_BOOK_BOOKTIP_H_INCLUDED #include -#include +#include #include #include @@ -36,24 +36,18 @@ namespace ripple { class BookTip { private: - std::reference_wrapper m_view; + View& view_; bool m_valid; uint256 m_book; uint256 m_end; uint256 m_dir; uint256 m_index; - SLE::pointer m_entry; + std::shared_ptr m_entry; Quality m_quality; - LedgerView& - view() const noexcept - { - return m_view; - } - public: /** Create the iterator. */ - BookTip (LedgerView& view, BookRef book); + BookTip (View& view, BookRef book); uint256 const& dir() const noexcept diff --git a/src/ripple/app/tx/impl/CancelOffer.cpp b/src/ripple/app/tx/impl/CancelOffer.cpp index 37e36235b8..11b9117f36 100644 --- a/src/ripple/app/tx/impl/CancelOffer.cpp +++ b/src/ripple/app/tx/impl/CancelOffer.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -78,13 +79,13 @@ public: uint256 const offerIndex (getOfferIndex (mTxnAccountID, uOfferSequence)); - SLE::pointer sleOffer (mEngine->view().entryCache (ltOFFER, - offerIndex)); + auto sleOffer = mEngine->view().peek ( + keylet::offer(offerIndex)); if (sleOffer) { m_journal.debug << "Trying to cancel offer #" << uOfferSequence; - return mEngine->view ().offerDelete (sleOffer); + return offerDelete (mEngine->view(), sleOffer); } m_journal.debug << "Offer #" << uOfferSequence << " can't be found."; diff --git a/src/ripple/app/tx/impl/CancelTicket.cpp b/src/ripple/app/tx/impl/CancelTicket.cpp index 64b7e727b1..61722a155e 100644 --- a/src/ripple/app/tx/impl/CancelTicket.cpp +++ b/src/ripple/app/tx/impl/CancelTicket.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -47,7 +48,9 @@ public: uint256 const ticketId = mTxn.getFieldH256 (sfTicketID); - SLE::pointer sleTicket = mEngine->view ().entryCache (ltTICKET, ticketId); + // VFALCO This is highly suspicious, we're requiring that the + // transaction provide the return value of getTicketIndex? + SLE::pointer sleTicket = mEngine->view().peek (keylet::ticket(ticketId)); if (!sleTicket) return tecNO_ENTRY; @@ -76,12 +79,12 @@ public: std::uint64_t const hint (sleTicket->getFieldU64 (sfOwnerNode)); - TER const result = mEngine->view ().dirDelete (false, hint, + TER const result = dirDelete (mEngine->view (), false, hint, getOwnerDirIndex (ticket_owner), ticketId, false, (hint == 0)); - adjustOwnerCount(mEngine->view(), mEngine->view().entryCache( - ltACCOUNT_ROOT, getAccountRootIndex(ticket_owner)), -1); - mEngine->view ().entryDelete (sleTicket); + adjustOwnerCount(mEngine->view(), mEngine->view().peek( + keylet::account(ticket_owner)), -1); + mEngine->view ().erase (sleTicket); return result; } diff --git a/src/ripple/app/tx/impl/Change.cpp b/src/ripple/app/tx/impl/Change.cpp index 13e2b3bd76..6b8dbec66c 100644 --- a/src/ripple/app/tx/impl/Change.cpp +++ b/src/ripple/app/tx/impl/Change.cpp @@ -117,15 +117,15 @@ private: { uint256 amendment (mTxn.getFieldH256 (sfAmendment)); - auto const index = getLedgerAmendmentIndex (); + auto const k = keylet::amendments(); - SLE::pointer amendmentObject (mEngine->view().entryCache (ltAMENDMENTS, index)); + SLE::pointer amendmentObject = + mEngine->view().peek (k); if (!amendmentObject) { - amendmentObject = std::make_shared( - ltAMENDMENTS, index); - mEngine->view().entryCreate(amendmentObject); + amendmentObject = std::make_shared(k); + mEngine->view().insert(amendmentObject); } STVector256 amendments = @@ -137,7 +137,7 @@ private: amendments.push_back (amendment); amendmentObject->setFieldV256 (sfAmendments, amendments); - mEngine->view().entryModify (amendmentObject); + mEngine->view().update (amendmentObject); getApp().getAmendmentTable ().enable (amendment); @@ -149,15 +149,14 @@ private: TER applyFee () { - auto const index = getLedgerFeeIndex (); + auto const k = keylet::fees(); - SLE::pointer feeObject = mEngine->view().entryCache (ltFEE_SETTINGS, index); + SLE::pointer feeObject = mEngine->view().peek (k); if (!feeObject) { - feeObject = std::make_shared( - ltFEE_SETTINGS, index); - mEngine->view().entryCreate(feeObject); + feeObject = std::make_shared(k); + mEngine->view().insert(feeObject); } // VFALCO-FIXME this generates errors @@ -173,7 +172,7 @@ private: feeObject->setFieldU32 ( sfReserveIncrement, mTxn.getFieldU32 (sfReserveIncrement)); - mEngine->view().entryModify (feeObject); + mEngine->view().update (feeObject); // VFALCO-FIXME this generates errors // m_journal.trace << diff --git a/src/ripple/app/tx/impl/CreateOffer.cpp b/src/ripple/app/tx/impl/CreateOffer.cpp index 8fa8c74c40..dd4162ae9a 100644 --- a/src/ripple/app/tx/impl/CreateOffer.cpp +++ b/src/ripple/app/tx/impl/CreateOffer.cpp @@ -21,10 +21,10 @@ #include #include #include +#include #include #include #include - #include #include #include @@ -46,8 +46,8 @@ private: // Only valid for custom currencies assert (!isXRP (issue.currency)); - SLE::pointer const issuerAccount = mEngine->view().entryCache ( - ltACCOUNT_ROOT, getAccountRootIndex (issue.account)); + auto const issuerAccount = mEngine->view().read( + keylet::account(issue.account)); if (!issuerAccount) { @@ -62,9 +62,8 @@ private: if (issuerAccount->getFieldU32 (sfFlags) & lsfRequireAuth) { - SLE::pointer const trustLine (mEngine->view().entryCache ( - ltRIPPLE_STATE, getRippleStateIndex ( - mTxnAccountID, issue.account, issue.currency))); + auto const trustLine = mEngine->view().read( + keylet::line(mTxnAccountID, issue.account, issue.currency)); if (!trustLine) { @@ -97,12 +96,12 @@ private: static bool - dry_offer (LedgerView& view, Offer const& offer) + dry_offer (View& view, Offer const& offer) { if (offer.fully_consumed ()) return true; - auto const amount = funds(view, offer.owner(), - offer.amount().out, fhZERO_IF_FROZEN); + auto const amount = accountFunds(view, offer.owner(), + offer.amount().out, fhZERO_IF_FROZEN, getConfig()); return (amount <= zero); } @@ -140,8 +139,8 @@ private: std::pair bridged_cross ( Taker& taker, - LedgerView& view, - LedgerView& view_cancel, + View& view, + View& view_cancel, Clock::time_point const when) { auto const& taker_amount = taker.original_offer (); @@ -200,10 +199,11 @@ private: m_journal.debug << " in: " << offers_direct.tip ().amount().in; m_journal.debug << " out: " << offers_direct.tip ().amount ().out; m_journal.debug << " owner: " << offers_direct.tip ().owner (); - m_journal.debug << " funds: " << funds(view, + m_journal.debug << " funds: " << accountFunds(view, offers_direct.tip ().owner (), offers_direct.tip ().amount ().out, - fhIGNORE_FREEZE); + fhIGNORE_FREEZE, + getConfig()); } cross_result = taker.cross(offers_direct.tip ()); @@ -220,15 +220,17 @@ private: { if (m_journal.debug) { - auto const owner1_funds_before = funds(view, + auto const owner1_funds_before = accountFunds(view, offers_leg1.tip ().owner (), offers_leg1.tip ().amount ().out, - fhIGNORE_FREEZE); + fhIGNORE_FREEZE, + getConfig()); - auto const owner2_funds_before = funds(view, + auto const owner2_funds_before = accountFunds(view, offers_leg2.tip ().owner (), offers_leg2.tip ().amount ().out, - fhIGNORE_FREEZE); + fhIGNORE_FREEZE, + getConfig()); m_journal.debug << count << " Bridge:"; m_journal.debug << " offer1: " << offers_leg1.tip (); @@ -285,8 +287,8 @@ private: std::pair direct_cross ( Taker& taker, - LedgerView& view, - LedgerView& view_cancel, + View& view, + View& view_cancel, Clock::time_point const when) { OfferStream offers ( @@ -319,8 +321,8 @@ private: m_journal.debug << " in: " << offer.amount ().in; m_journal.debug << " out: " << offer.amount ().out; m_journal.debug << " owner: " << offer.owner (); - m_journal.debug << " funds: " << funds(view, - offer.owner (), offer.amount ().out, fhIGNORE_FREEZE); + m_journal.debug << " funds: " << accountFunds(view, + offer.owner (), offer.amount ().out, fhIGNORE_FREEZE, getConfig()); } cross_result = taker.cross (offer); @@ -386,8 +388,8 @@ private: // Charges fees on top to taker. std::pair cross ( - LedgerView& view, - LedgerView& cancel_view, + View& view, + View& cancel_view, Amounts const& taker_amount) { Clock::time_point const when ( @@ -541,8 +543,8 @@ public: return Transactor::preCheck (); } - TER - doApply () override + std::pair + applyGuts (View& view, View& view_cancel) { std::uint32_t const uTxFlags = mTxn.getFlags (); @@ -555,7 +557,7 @@ public: STAmount saTakerGets = mTxn.getFieldAmount (sfTakerGets); if (!isLegalNet (saTakerPays) || !isLegalNet (saTakerGets)) - return temBAD_AMOUNT; + return { temBAD_AMOUNT, true }; auto const& uPaysIssuerID = saTakerPays.getIssuer (); auto const& uPaysCurrency = saTakerPays.getCurrency (); @@ -584,26 +586,19 @@ public: // This is the ledger view that we work against. Transactions are applied // as we go on processing transactions. - LedgerView& view (mEngine->view ()); - // This is a checkpoint with just the fees paid. If something goes wrong - // with this transaction, we roll back to this ledger. - LedgerView view_checkpoint (view); + auto const sleCreator = view.peek ( + keylet::account(mTxnAccountID)); - view.bumpSeq (); // Begin ledger variance. - - SLE::pointer sleCreator = mEngine->view().entryCache ( - ltACCOUNT_ROOT, getAccountRootIndex (mTxnAccountID)); - - if (view.isGlobalFrozen (uPaysIssuerID) || view.isGlobalFrozen (uGetsIssuerID)) + if (isGlobalFrozen (view, uPaysIssuerID) || isGlobalFrozen (view, uGetsIssuerID)) { if (m_journal.warning) m_journal.warning << "Offer involves frozen asset"; result = tecFROZEN; } - else if (funds(view, - mTxnAccountID, saTakerGets, fhZERO_IF_FROZEN) <= zero) + else if (accountFunds(view, mTxnAccountID, saTakerGets, + fhZERO_IF_FROZEN, getConfig()) <= zero) { if (m_journal.debug) m_journal.debug << "delay: Offers must be at least partially funded."; @@ -624,14 +619,14 @@ public: if (result != tesSUCCESS) { m_journal.debug << "final result: " << transToken (result); - return result; + return { result, true }; } // Process a cancellation request that's passed along with an offer. if (bHaveCancel) { - SLE::pointer sleCancel = mEngine->view().entryCache (ltOFFER, - getOfferIndex (mTxnAccountID, uCancelSequence)); + auto const sleCancel = view.peek( + keylet::offer(mTxnAccountID, uCancelSequence)); // It's not an error to not find the offer to cancel: it might have // been consumed or removed. If it is found, however, it's an error @@ -639,7 +634,7 @@ public: if (sleCancel) { m_journal.debug << "Create cancels order " << uCancelSequence; - result = view.offerDelete (sleCancel); + result = offerDelete (view, sleCancel); } } @@ -649,7 +644,7 @@ public: if (bHaveExpiration && (mEngine->getLedger ()->getParentCloseTimeNC () >= uExpiration)) { - return tesSUCCESS; + return { tesSUCCESS, true }; } // Make sure that we are authorized to hold what the taker will pay us. @@ -682,7 +677,7 @@ public: m_journal.trace << " out: " << format_amount (taker_amount.out); } - std::tie(result, place_offer) = cross (view, view_checkpoint, taker_amount); + std::tie(result, place_offer) = cross (view, view_cancel, taker_amount); assert (result != tefINTERNAL); if (m_journal.trace) @@ -698,7 +693,7 @@ public: if (result != tesSUCCESS) { m_journal.debug << "final result: " << transToken (result); - return result; + return { result, true }; } assert (saTakerGets.issue () == place_offer.in.issue ()); @@ -714,13 +709,13 @@ public: m_journal.fatal << "Cross left offer negative!" << " in: " << format_amount (place_offer.in) << " out: " << format_amount (place_offer.out); - return tefINTERNAL; + return { tefINTERNAL, true }; } if (place_offer.in == zero || place_offer.out == zero) { m_journal.debug << "Offer fully crossed!"; - return result; + return { result, true }; } // We now need to adjust the offer to reflect the amount left after @@ -735,7 +730,7 @@ public: if (result != tesSUCCESS) { m_journal.debug << "final result: " << transToken (result); - return result; + return { result, true }; } if (m_journal.trace) @@ -750,8 +745,7 @@ public: if (bFillOrKill) { m_journal.trace << "Fill or Kill: offer killed"; - view.swapWith (view_checkpoint); - return tesSUCCESS; + return { tesSUCCESS, false }; } // For 'immediate or cancel' offers, the amount remaining doesn't get @@ -759,7 +753,7 @@ public: if (bImmediateOrCancel) { m_journal.trace << "Immediate or cancel: offer cancelled"; - return tesSUCCESS; + return { tesSUCCESS, true }; } if (mPriorBalance < getAccountReserve (sleCreator)) @@ -773,7 +767,7 @@ public: if (result != tesSUCCESS) m_journal.debug << "final result: " << transToken (result); - return result; + return { result, true }; } // We need to place the remainder of the offer into its order book. @@ -784,10 +778,10 @@ public: uint256 uDirectory; // Add offer to owner's directory. - result = view.dirAdd (uOwnerNode, + result = dirAdd(view, uOwnerNode, getOwnerDirIndex (mTxnAccountID), offer_index, std::bind ( - &Ledger::ownerDirDescriber, std::placeholders::_1, + &ownerDirDescriber, std::placeholders::_1, std::placeholders::_2, mTxnAccountID)); if (result == tesSUCCESS) @@ -806,9 +800,9 @@ public: uDirectory = getQualityIndex (book_base, uRate); // Add offer to order book. - result = view.dirAdd (uBookNode, uDirectory, offer_index, + result = dirAdd (view, uBookNode, uDirectory, offer_index, std::bind ( - &Ledger::qualityDirDescriber, std::placeholders::_1, + &qualityDirDescriber, std::placeholders::_1, std::placeholders::_2, saTakerPays.getCurrency (), uPaysIssuerID, saTakerGets.getCurrency (), uGetsIssuerID, uRate)); @@ -830,13 +824,31 @@ public: sleOffer->setFlag (lsfPassive); if (bSell) sleOffer->setFlag (lsfSell); - mEngine->view().entryCreate(sleOffer); + view.insert(sleOffer); } if (result != tesSUCCESS) m_journal.debug << "final result: " << transToken (result); - return result; + return { result, true }; + } + + TER + doApply() override + { + bool const openLedger = mParams & tapOPEN_LEDGER; + // This is the ledger view that we work against. Transactions are applied + // as we go on processing transactions. + MetaView view (mEngine->view(), openLedger); + // This is a checkpoint with just the fees paid. If something goes wrong + // with this transaction, we roll back to this ledger. + MetaView viewCancel (mEngine->view(), openLedger); + auto const result = applyGuts(view, viewCancel); + if (result.second) + view.apply(); + else + viewCancel.apply(); + return result.first; } }; diff --git a/src/ripple/app/tx/impl/CreateTicket.cpp b/src/ripple/app/tx/impl/CreateTicket.cpp index 9f68c966c0..055a7f7ecf 100644 --- a/src/ripple/app/tx/impl/CreateTicket.cpp +++ b/src/ripple/app/tx/impl/CreateTicket.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -91,14 +92,13 @@ public: sleTicket->setFieldU32 (sfSequence, mTxn.getSequence ()); if (expiration != 0) sleTicket->setFieldU32 (sfExpiration, expiration); - mEngine->view().entryCreate (sleTicket); + mEngine->view().insert (sleTicket); if (mTxn.isFieldPresent (sfTarget)) { AccountID const target_account (mTxn.getFieldAccount160 (sfTarget)); - SLE::pointer sleTarget = mEngine->view().entryCache (ltACCOUNT_ROOT, - getAccountRootIndex (target_account)); + SLE::pointer sleTarget = mEngine->view().peek (keylet::account(target_account)); // Destination account does not exist. if (!sleTarget) @@ -114,10 +114,10 @@ public: auto describer = [&](SLE::pointer p, bool b) { - Ledger::ownerDirDescriber(p, b, mTxnAccountID); + ownerDirDescriber(p, b, mTxnAccountID); }; - TER result = mEngine->view ().dirAdd ( + TER result = dirAdd(mEngine->view(), hint, getOwnerDirIndex (mTxnAccountID), sleTicket->getIndex (), diff --git a/src/ripple/app/tx/impl/LocalTxs.cpp b/src/ripple/app/tx/impl/LocalTxs.cpp index 81208e0a6a..1a442c6797 100644 --- a/src/ripple/app/tx/impl/LocalTxs.cpp +++ b/src/ripple/app/tx/impl/LocalTxs.cpp @@ -124,9 +124,9 @@ public: { if (txn.isExpired (ledger->getLedgerSeq ())) return true; - if (ledger->hasTransaction (txn.getID ())) + if (hasTransaction (*ledger, txn.getID ())) return true; - auto const sle = fetch(*ledger, + auto const sle = cachedRead(*ledger, getAccountRootIndex(txn.getAccount().getAccountID()), getApp().getSLECache()); if (! sle) diff --git a/src/ripple/app/tx/impl/Offer.h b/src/ripple/app/tx/impl/Offer.h index 152a28efbd..4795b3cd6a 100644 --- a/src/ripple/app/tx/impl/Offer.h +++ b/src/ripple/app/tx/impl/Offer.h @@ -20,13 +20,11 @@ #ifndef RIPPLE_APP_BOOK_OFFER_H_INCLUDED #define RIPPLE_APP_BOOK_OFFER_H_INCLUDED +#include #include -#include #include #include - #include - #include #include @@ -98,7 +96,8 @@ public: /** Adjusts the offer to indicate that we consumed some (or all) of it. */ void - consume (LedgerView& view, Amounts const& consumed) const + consume (View& view, + Amounts const& consumed) const { if (consumed.in > m_amounts.in) throw std::logic_error ("can't consume more than is available."); @@ -112,7 +111,7 @@ public: m_entry->setFieldAmount (sfTakerPays, m_amounts.in); m_entry->setFieldAmount (sfTakerGets, m_amounts.out); - view.entryModify (m_entry); + view.update (m_entry); } std::string id () const diff --git a/src/ripple/app/tx/impl/OfferStream.cpp b/src/ripple/app/tx/impl/OfferStream.cpp index 4a9ccfd480..890565717d 100644 --- a/src/ripple/app/tx/impl/OfferStream.cpp +++ b/src/ripple/app/tx/impl/OfferStream.cpp @@ -19,10 +19,11 @@ #include #include +#include namespace ripple { -OfferStream::OfferStream (LedgerView& view, LedgerView& view_cancel, +OfferStream::OfferStream (View& view, View& view_cancel, BookRef book, Clock::time_point when, beast::Journal journal) : m_journal (journal) , m_view (view) @@ -36,13 +37,13 @@ OfferStream::OfferStream (LedgerView& view, LedgerView& view_cancel, // Handle the case where a directory item with no corresponding ledger entry // is found. This shouldn't happen but if it does we clean it up. void -OfferStream::erase (LedgerView& view) +OfferStream::erase (View& view) { - // NIKB NOTE This should be using LedgerView::dirDelete, which would + // NIKB NOTE This should be using View::dirDelete, which would // correctly remove the directory if its the last entry. // Unfortunately this is a protocol breaking change. - auto p (view.entryCache (ltDIR_NODE, m_tip.dir())); + auto p = view.peek (keylet::page(m_tip.dir())); if (p == nullptr) { @@ -65,7 +66,7 @@ OfferStream::erase (LedgerView& view) v.erase (it); p->setFieldV256 (sfIndexes, v); - view.entryModify (p); + view.update (p); if (m_journal.trace) m_journal.trace << "Missing offer " << m_tip.index() << @@ -85,7 +86,7 @@ OfferStream::step () if (! m_tip.step()) return false; - SLE::pointer const& entry (m_tip.entry()); + std::shared_ptr entry = m_tip.entry(); // Remove if missing if (! entry) @@ -99,9 +100,11 @@ OfferStream::step () if (entry->isFieldPresent (sfExpiration) && entry->getFieldU32 (sfExpiration) <= m_when) { - view_cancel().offerDelete (entry->getIndex()); if (m_journal.trace) m_journal.trace << "Removing expired offer " << entry->getIndex(); + offerDelete (view_cancel(), + view_cancel().peek( + keylet::offer(entry->key()))); continue; } @@ -112,9 +115,11 @@ OfferStream::step () // Remove if either amount is zero if (amount.empty()) { - view_cancel().offerDelete (entry->getIndex()); if (m_journal.warning) m_journal.warning << "Removing bad offer " << entry->getIndex(); + offerDelete (view_cancel(), + view_cancel().peek( + keylet::offer(entry->key()))); m_offer = Offer{}; continue; } @@ -122,8 +127,9 @@ OfferStream::step () // Calculate owner funds // NIKB NOTE The calling code also checks the funds, how expensive is // looking up the funds twice? - auto const owner_funds = funds(view(), - m_offer.owner(), amount.out, fhZERO_IF_FROZEN); + auto const owner_funds = accountFunds(view(), + m_offer.owner(), amount.out, fhZERO_IF_FROZEN, + getConfig()); // Check for unfunded offer if (owner_funds <= zero) @@ -131,12 +137,14 @@ OfferStream::step () // If the owner's balance in the pristine view is the same, // we haven't modified the balance and therefore the // offer is "found unfunded" versus "became unfunded" - auto const original_funds = funds(view_cancel(), - m_offer.owner(), amount.out, fhZERO_IF_FROZEN); + auto const original_funds = accountFunds(view_cancel(), + m_offer.owner(), amount.out, fhZERO_IF_FROZEN, + getConfig()); if (original_funds == owner_funds) { - view_cancel().offerDelete (entry->getIndex()); + offerDelete (view_cancel(), view_cancel().peek( + keylet::offer(entry->key()))); if (m_journal.trace) m_journal.trace << "Removing unfunded offer " << entry->getIndex(); } diff --git a/src/ripple/app/tx/impl/OfferStream.h b/src/ripple/app/tx/impl/OfferStream.h index 909a29fb4a..37b8472c36 100644 --- a/src/ripple/app/tx/impl/OfferStream.h +++ b/src/ripple/app/tx/impl/OfferStream.h @@ -21,18 +21,18 @@ #define RIPPLE_APP_BOOK_OFFERSTREAM_H_INCLUDED #include -#include +#include +#include #include - +#include #include - #include namespace ripple { /** Presents and consumes the offers in an order book. - Two `LedgerView` objects accumulate changes to the ledger. `view` + Two `View` objects accumulate changes to the ledger. `view` is applied when the calling transaction succeeds. If the calling transaction fails, then `view_cancel` is applied. @@ -51,27 +51,27 @@ class OfferStream { private: beast::Journal m_journal; - std::reference_wrapper m_view; - std::reference_wrapper m_view_cancel; + std::reference_wrapper m_view; + std::reference_wrapper m_view_cancel; Book m_book; Clock::time_point m_when; BookTip m_tip; Offer m_offer; void - erase (LedgerView& view); + erase (View& view); public: - OfferStream (LedgerView& view, LedgerView& view_cancel, BookRef book, + OfferStream (View& view, View& view_cancel, BookRef book, Clock::time_point when, beast::Journal journal); - LedgerView& + View& view () noexcept { return m_view; } - LedgerView& + View& view_cancel () noexcept { return m_view_cancel; diff --git a/src/ripple/app/tx/impl/Payment.cpp b/src/ripple/app/tx/impl/Payment.cpp index f001ee8c5d..26654e3f0f 100644 --- a/src/ripple/app/tx/impl/Payment.cpp +++ b/src/ripple/app/tx/impl/Payment.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -191,8 +192,8 @@ public: " saDstAmount=" << saDstAmount.getFullText (); // Open a ledger for editing. - auto const index = getAccountRootIndex (uDstAccountID); - SLE::pointer sleDst (mEngine->view().entryCache (ltACCOUNT_ROOT, index)); + auto const k = keylet::account(uDstAccountID); + SLE::pointer sleDst = mEngine->view().peek (k); if (!sleDst) { @@ -233,11 +234,10 @@ public: } // Create the account. - sleDst = std::make_shared(ltACCOUNT_ROOT, - getAccountRootIndex (uDstAccountID)); + sleDst = std::make_shared(k); sleDst->setFieldAccount (sfAccount, uDstAccountID); sleDst->setFieldU32 (sfSequence, 1); - mEngine->view().entryCreate(sleDst); + mEngine->view().insert(sleDst); } else if ((sleDst->getFlags () & lsfRequireDestTag) && !mTxn.isFieldPresent (sfDestinationTag)) @@ -256,7 +256,7 @@ public: // Tell the engine that we are intending to change the the destination // account. The source account gets always charged a fee so it's always // marked as modified. - mEngine->view().entryModify (sleDst); + mEngine->view().update (sleDst); } TER terResult; @@ -293,7 +293,6 @@ public: } else { - path::RippleCalc::Output rc; { ScopedDeferCredits g (mEngine->view ()); @@ -310,7 +309,7 @@ public: // TODO: is this right? If the amount is the correct amount, was // the delivered amount previously set? if (rc.result () == tesSUCCESS && rc.actualAmountOut != saDstAmount) - mEngine->view ().setDeliveredAmount (rc.actualAmountOut); + mEngine->deliverAmount (rc.actualAmountOut); terResult = rc.result (); } diff --git a/src/ripple/app/tx/impl/PaymentView.h b/src/ripple/app/tx/impl/PaymentView.h new file mode 100644 index 0000000000..b2189c1d58 --- /dev/null +++ b/src/ripple/app/tx/impl/PaymentView.h @@ -0,0 +1,147 @@ +//------------------------------------------------------------------------------ +/* + 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_APP_PAYMENTVIEW_H_INCLUDED + +#include +#include + +namespace ripple { + +/** A View wrapper which makes credits unavailable to balances. + + This is used for payments, so that consuming liquidity + from a path never causes portions of that path or other + paths to gain liquidity. +*/ +class PaymentView : public View +{ +private: + View& view_; + DeferredCredits tab_; + +public: + explicit + PaymentView (View& view) + : view_(view) + { + } + + bool + exists (Keylet const& k) const override + { + return view_.exists(k); + } + + boost::optional + succ (uint256 const& key, + boost::optional last = + boost::none) const override + { + return view_.succ(key, last); + } + + std::shared_ptr + read (Keylet const& k) const override + { + return view_.read(k); + } + + bool + unchecked_erase (uint256 const& key) override + { + return view_.unchecked_erase(key); + } + + void + unchecked_insert( + std::shared_ptr&& sle) override + { + view_.unchecked_insert( + std::move(sle)); + } + + void + unchecked_replace ( + std::shared_ptr&& sle) override + { + view_.unchecked_replace( + std::move(sle)); + } + + BasicView const* + parent() const override + { + return &view_; + } + + STAmount + deprecatedBalance (AccountID const& account, + AccountID const& issuer, + STAmount const& amount) const override + { + return tab_.adjustedBalance( + account, issuer, amount); + } + + //--------------------------------------------- + + std::shared_ptr + peek (Keylet const& k) override + { + return view_.peek(k); + } + + void + erase (std::shared_ptr const& sle) override + { + return view_.erase(sle); + } + + void + insert (std::shared_ptr const& sle) override + { + return view_.insert(sle); + } + + void + update (std::shared_ptr const& sle) override + { + return view_.update(sle); + } + + bool + openLedger() const override + { + return view_.openLedger(); + } + + // Unfortunately necessary for DeferredCredits + void + deprecatedCreditHint (AccountID const& from, + AccountID const& to, + STAmount const& amount) override + { + tab_.credit(from, to, amount); + } +}; + +} // ripple + +#endif diff --git a/src/ripple/app/tx/impl/SetAccount.cpp b/src/ripple/app/tx/impl/SetAccount.cpp index 725faf6bdf..595b5bfa41 100644 --- a/src/ripple/app/tx/impl/SetAccount.cpp +++ b/src/ripple/app/tx/impl/SetAccount.cpp @@ -18,11 +18,12 @@ //============================================================================== #include -#include #include +#include #include #include #include +#include #include namespace ripple { @@ -140,7 +141,8 @@ public: // if (bSetRequireAuth && !(uFlagsIn & lsfRequireAuth)) { - if (!mEngine->view().dirIsEmpty (getOwnerDirIndex (mTxnAccountID))) + if (! dirIsEmpty (mEngine->view(), + keylet::ownerDir(mTxnAccountID))) { m_journal.trace << "Retry: Owner directory not empty."; return (mParams & tapRETRY) ? terOWNERS : tecOWNERS; diff --git a/src/ripple/app/tx/impl/SetSignerList.cpp b/src/ripple/app/tx/impl/SetSignerList.cpp index 0af8ca5386..5c616b1495 100644 --- a/src/ripple/app/tx/impl/SetSignerList.cpp +++ b/src/ripple/app/tx/impl/SetSignerList.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -235,18 +236,18 @@ SetSignerList::replaceSignerList (uint256 const& index) // Everything's ducky. Add the ltSIGNER_LIST to the ledger. auto signerList = std::make_shared(ltSIGNER_LIST, index); - mEngine->view().entryCreate (signerList); + mEngine->view().insert (signerList); writeSignersToLedger (signerList); // Lambda for call to dirAdd. auto describer = [&] (SLE::ref sle, bool dummy) { - Ledger::ownerDirDescriber (sle, dummy, mTxnAccountID); + ownerDirDescriber (sle, dummy, mTxnAccountID); }; // Add the signer list to the account's directory. std::uint64_t hint; - TER result = mEngine->view ().dirAdd ( + TER result = dirAdd(mEngine->view (), hint, getOwnerDirIndex (mTxnAccountID), index, describer); if (m_journal.trace) m_journal.trace << @@ -270,7 +271,7 @@ SetSignerList::destroySignerList (uint256 const& index) { // See if there's an ltSIGNER_LIST for this account. SLE::pointer signerList = - mEngine->view ().entryCache (ltSIGNER_LIST, index); + mEngine->view().peek (keylet::signers(index)); // If the signer list doesn't exist we've already succeeded in deleting it. if (!signerList) @@ -279,9 +280,9 @@ SetSignerList::destroySignerList (uint256 const& index) // We have to examine the current SignerList so we know how much to // reduce the OwnerCount. std::uint32_t removeFromOwnerCount = 0; - uint256 const signerListIndex = getSignerListIndex (mTxnAccountID); + auto const k = keylet::signers(mTxnAccountID); SLE::pointer accountSignersList = - mEngine->view ().entryCache (ltSIGNER_LIST, signerListIndex); + mEngine->view().peek (k); if (accountSignersList) { STArray const& actualList = @@ -292,14 +293,14 @@ SetSignerList::destroySignerList (uint256 const& index) // Remove the node from the account directory. std::uint64_t const hint (signerList->getFieldU64 (sfOwnerNode)); - TER const result = mEngine->view ().dirDelete (false, hint, + TER const result = dirDelete(mEngine->view (), false, hint, getOwnerDirIndex (mTxnAccountID), index, false, (hint == 0)); if (result == tesSUCCESS) adjustOwnerCount(mEngine->view(), mTxnAccount, removeFromOwnerCount); - mEngine->view ().entryDelete (signerList); + mEngine->view ().erase (signerList); return result; } diff --git a/src/ripple/app/tx/impl/SetTrust.cpp b/src/ripple/app/tx/impl/SetTrust.cpp index 0821626a8c..672704dbd2 100644 --- a/src/ripple/app/tx/impl/SetTrust.cpp +++ b/src/ripple/app/tx/impl/SetTrust.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -152,18 +153,16 @@ public: // trust line to oneself to be deleted. If no such trust // lines exist now, why not remove this code and simply // return an error? - SLE::pointer selDelete ( - mEngine->view().entryCache (ltRIPPLE_STATE, - getRippleStateIndex ( - mTxnAccountID, uDstAccountID, currency))); + SLE::pointer sleDelete = mEngine->view().peek ( + keylet::line(mTxnAccountID, uDstAccountID, currency)); - if (selDelete) + if (sleDelete) { m_journal.warning << "Clearing redundant line."; - return mEngine->view ().trustDelete ( - selDelete, mTxnAccountID, uDstAccountID); + return trustDelete (mEngine->view(), + sleDelete, mTxnAccountID, uDstAccountID); } else { @@ -173,8 +172,8 @@ public: } } - SLE::pointer sleDst (mEngine->view().entryCache ( - ltACCOUNT_ROOT, getAccountRootIndex (uDstAccountID))); + SLE::pointer sleDst = + mEngine->view().peek (keylet::account(uDstAccountID)); if (!sleDst) { @@ -186,8 +185,8 @@ public: STAmount saLimitAllow = saLimitAmount; saLimitAllow.setIssuer (mTxnAccountID); - SLE::pointer sleRippleState (mEngine->view().entryCache (ltRIPPLE_STATE, - getRippleStateIndex (mTxnAccountID, uDstAccountID, currency))); + SLE::pointer sleRippleState = mEngine->view().peek ( + keylet::line(mTxnAccountID, uDstAccountID, currency)); if (sleRippleState) { @@ -381,7 +380,8 @@ public: { // Delete. - terResult = mEngine->view ().trustDelete (sleRippleState, uLowAccountID, uHighAccountID); + terResult = trustDelete (mEngine->view(), + sleRippleState, uLowAccountID, uHighAccountID); } // Reserve is not scaled by load. else if (bReserveIncrease && mPriorBalance < reserveCreate) @@ -395,7 +395,7 @@ public: } else { - mEngine->view().entryModify (sleRippleState); + mEngine->view().update (sleRippleState); m_journal.trace << "Modify ripple line"; } @@ -430,19 +430,19 @@ public: to_string (index); // Create a new ripple line. - terResult = mEngine->view ().trustCreate ( - bHigh, - mTxnAccountID, - uDstAccountID, - index, - mTxnAccount, - bSetAuth, - bSetNoRipple && !bClearNoRipple, - bSetFreeze && !bClearFreeze, - saBalance, - saLimitAllow, // Limit for who is being charged. - uQualityIn, - uQualityOut); + terResult = trustCreate (mEngine->view(), + bHigh, + mTxnAccountID, + uDstAccountID, + index, + mTxnAccount, + bSetAuth, + bSetNoRipple && !bClearNoRipple, + bSetFreeze && !bClearFreeze, + saBalance, + saLimitAllow, // Limit for who is being charged. + uQualityIn, + uQualityOut); } return terResult; diff --git a/src/ripple/app/tx/impl/Taker.cpp b/src/ripple/app/tx/impl/Taker.cpp index be1d6ba2f2..a0a18178b7 100644 --- a/src/ripple/app/tx/impl/Taker.cpp +++ b/src/ripple/app/tx/impl/Taker.cpp @@ -19,6 +19,7 @@ #include #include +#include namespace ripple { @@ -517,14 +518,16 @@ BasicTaker::do_cross ( std::uint32_t Taker::calculateRate ( - LedgerView& view, AccountID const& issuer, AccountID const& account) + View const& view, + AccountID const& issuer, + AccountID const& account) { return isXRP (issuer) || (account == issuer) ? QUALITY_ONE : rippleTransferRate (view, issuer); } -Taker::Taker (CrossType cross_type, LedgerView& view, AccountID const& account, +Taker::Taker (CrossType cross_type, View& view, AccountID const& account, Amounts const& offer, std::uint32_t flags, beast::Journal journal) : BasicTaker (cross_type, account, offer, Quality(offer), flags, calculateRate(view, offer.in.getIssuer(), account), @@ -580,19 +583,21 @@ Taker::consume_offer (Offer const& offer, Amounts const& order) offer.consume (view_, order); } +// VFALCO This function should take a config parameter STAmount Taker::get_funds (AccountID const& account, STAmount const& amount) const { - return funds(view_, account, amount, fhZERO_IF_FROZEN); + return accountFunds(view_, account, amount, fhZERO_IF_FROZEN, + getConfig()); } -TER Taker::transfer_xrp ( +TER Taker::transferXRP ( AccountID const& from, AccountID const& to, STAmount const& amount) { if (!isXRP (amount)) - throw std::logic_error ("Using transfer_xrp with IOU"); + throw std::logic_error ("Using transferXRP with IOU"); if (from == to) return tesSUCCESS; @@ -601,16 +606,16 @@ TER Taker::transfer_xrp ( if (amount == zero) return tesSUCCESS; - return view_.transfer_xrp (from, to, amount); + return ripple::transferXRP (view_, from, to, amount); } -TER Taker::redeem_iou ( +TER Taker::redeemIOU ( AccountID const& account, STAmount const& amount, Issue const& issue) { if (isXRP (amount)) - throw std::logic_error ("Using redeem_iou with XRP"); + throw std::logic_error ("Using redeemIOU with XRP"); if (account == issue.account) return tesSUCCESS; @@ -622,23 +627,23 @@ TER Taker::redeem_iou ( // If we are trying to redeem some amount, then the account // must have a credit balance. if (get_funds (account, amount) <= zero) - throw std::logic_error ("redeem_iou has no funds to redeem"); + throw std::logic_error ("redeemIOU has no funds to redeem"); - auto ret = view_.redeem_iou (account, amount, issue); + auto ret = ripple::redeemIOU (view_, account, amount, issue); if (get_funds (account, amount) < zero) - throw std::logic_error ("redeem_iou redeemed more funds than available"); + throw std::logic_error ("redeemIOU redeemed more funds than available"); return ret; } -TER Taker::issue_iou ( +TER Taker::issueIOU ( AccountID const& account, STAmount const& amount, Issue const& issue) { if (isXRP (amount)) - throw std::logic_error ("Using issue_iou with XRP"); + throw std::logic_error ("Using issueIOU with XRP"); if (account == issue.account) return tesSUCCESS; @@ -647,7 +652,7 @@ TER Taker::issue_iou ( if (amount == zero) return tesSUCCESS; - return view_.issue_iou (account, amount, issue); + return ripple::issueIOU (view_, account, amount, issue); } // Performs funds transfers to fill the given offer and adjusts offer. @@ -664,17 +669,17 @@ Taker::fill (BasicTaker::Flow const& flow, Offer const& offer) assert (!isXRP (flow.order.in)); if(result == tesSUCCESS) - result = redeem_iou (account (), flow.issuers.in, flow.issuers.in.issue ()); + result = redeemIOU (account (), flow.issuers.in, flow.issuers.in.issue ()); if (result == tesSUCCESS) - result = issue_iou (offer.owner (), flow.order.in, flow.order.in.issue ()); + result = issueIOU (offer.owner (), flow.order.in, flow.order.in.issue ()); } else { assert (isXRP (flow.order.in)); if (result == tesSUCCESS) - result = transfer_xrp (account (), offer.owner (), flow.order.in); + result = transferXRP (account (), offer.owner (), flow.order.in); } // Now send funds from the account whose offer we're taking @@ -683,17 +688,17 @@ Taker::fill (BasicTaker::Flow const& flow, Offer const& offer) assert (!isXRP (flow.order.out)); if(result == tesSUCCESS) - result = redeem_iou (offer.owner (), flow.issuers.out, flow.issuers.out.issue ()); + result = redeemIOU (offer.owner (), flow.issuers.out, flow.issuers.out.issue ()); if (result == tesSUCCESS) - result = issue_iou (account (), flow.order.out, flow.order.out.issue ()); + result = issueIOU (account (), flow.order.out, flow.order.out.issue ()); } else { assert (isXRP (flow.order.out)); if (result == tesSUCCESS) - result = transfer_xrp (offer.owner (), account (), flow.order.out); + result = transferXRP (offer.owner (), account (), flow.order.out); } if (result == tesSUCCESS) @@ -718,24 +723,24 @@ Taker::fill ( if (leg1.owner () != account ()) { if (result == tesSUCCESS) - result = redeem_iou (account (), flow1.issuers.in, flow1.issuers.in.issue ()); + result = redeemIOU (account (), flow1.issuers.in, flow1.issuers.in.issue ()); if (result == tesSUCCESS) - result = issue_iou (leg1.owner (), flow1.order.in, flow1.order.in.issue ()); + result = issueIOU (leg1.owner (), flow1.order.in, flow1.order.in.issue ()); } // leg1 to leg2: bridging over XRP if (result == tesSUCCESS) - result = transfer_xrp (leg1.owner (), leg2.owner (), flow1.order.out); + result = transferXRP (leg1.owner (), leg2.owner (), flow1.order.out); // leg2 to Taker: IOU if (leg2.owner () != account ()) { if (result == tesSUCCESS) - result = redeem_iou (leg2.owner (), flow2.issuers.out, flow2.issuers.out.issue ()); + result = redeemIOU (leg2.owner (), flow2.issuers.out, flow2.issuers.out.issue ()); if (result == tesSUCCESS) - result = issue_iou (account (), flow2.order.out, flow2.order.out.issue ()); + result = issueIOU (account (), flow2.order.out, flow2.order.out.issue ()); } if (result == tesSUCCESS) diff --git a/src/ripple/app/tx/impl/Taker.h b/src/ripple/app/tx/impl/Taker.h index 187b8f67b4..bc4ff7303e 100644 --- a/src/ripple/app/tx/impl/Taker.h +++ b/src/ripple/app/tx/impl/Taker.h @@ -21,9 +21,10 @@ #define RIPPLE_APP_BOOK_TAKER_H_INCLUDED #include +#include #include +#include #include - #include #include #include @@ -233,16 +234,20 @@ public: get_funds (AccountID const& account, STAmount const& funds) const = 0; }; +//------------------------------------------------------------------------------ + class Taker : public BasicTaker { private: static std::uint32_t - calculateRate (LedgerView& view, AccountID const& issuer, AccountID const& account); + calculateRate (View const& view, + AccountID const& issuer, + AccountID const& account); // The underlying ledger entry we are dealing with - LedgerView& view_; + View& view_; // The amount of XRP that flowed if we were autobridging STAmount xrp_flow_; @@ -262,19 +267,19 @@ private: BasicTaker::Flow const& flow2, Offer const& leg2); TER - transfer_xrp (AccountID const& from, AccountID const& to, STAmount const& amount); + transferXRP (AccountID const& from, AccountID const& to, STAmount const& amount); TER - redeem_iou (AccountID const& account, STAmount const& amount, Issue const& issue); + redeemIOU (AccountID const& account, STAmount const& amount, Issue const& issue); TER - issue_iou (AccountID const& account, STAmount const& amount, Issue const& issue); + issueIOU (AccountID const& account, STAmount const& amount, Issue const& issue); public: Taker () = delete; Taker (Taker const&) = delete; - Taker (CrossType cross_type, LedgerView& view, AccountID const& account, + Taker (CrossType cross_type, View& view, AccountID const& account, Amounts const& offer, std::uint32_t flags, beast::Journal journal); ~Taker () = default; diff --git a/src/ripple/app/tx/impl/TransactionEngine.cpp b/src/ripple/app/tx/impl/TransactionEngine.cpp index ae16c348b0..d8255a67bd 100644 --- a/src/ripple/app/tx/impl/TransactionEngine.cpp +++ b/src/ripple/app/tx/impl/TransactionEngine.cpp @@ -103,8 +103,8 @@ TransactionEngine::applyTransaction ( mNodes.emplace(mLedger, txID, mLedger->getLedgerSeq(), params); - SLE::pointer txnAcct = mNodes->entryCache (ltACCOUNT_ROOT, - getAccountRootIndex (txn.getSourceAccount ())); + SLE::pointer txnAcct = view().peek( + keylet::account(txn.getSourceAccount())); if (!txnAcct) terResult = terNO_ACCOUNT; @@ -137,7 +137,7 @@ TransactionEngine::applyTransaction ( fee = balance; txnAcct->setFieldAmount (sfBalance, balance - fee); txnAcct->setFieldU32 (sfSequence, t_seq + 1); - mNodes->entryModify (txnAcct); + view().update (txnAcct); didApply = true; } } @@ -169,7 +169,6 @@ TransactionEngine::applyTransaction ( Serializer m; mNodes->calcRawMeta (m, terResult, mTxnSeq++); - assert(mLedger == mNodes->getLedger()); mNodes->apply(); Serializer s; @@ -177,7 +176,7 @@ TransactionEngine::applyTransaction ( if (params & tapOPEN_LEDGER) { - if (!mLedger->addTransaction (txID, s)) + if (! addTransaction (*mLedger, txID, s)) { WriteLog (lsFATAL, TransactionEngine) << "Duplicate transaction applied"; @@ -187,7 +186,7 @@ TransactionEngine::applyTransaction ( } else { - if (!mLedger->addTransaction (txID, s, m)) + if (! addTransaction (*mLedger, txID, s, m)) { WriteLog (lsFATAL, TransactionEngine) << "Duplicate transaction applied to closed ledger"; diff --git a/src/ripple/app/tx/impl/Transactor.cpp b/src/ripple/app/tx/impl/Transactor.cpp index 647e2b3546..58270140f8 100644 --- a/src/ripple/app/tx/impl/Transactor.cpp +++ b/src/ripple/app/tx/impl/Transactor.cpp @@ -171,7 +171,7 @@ TER Transactor::checkSeq () return terPRE_SEQ; } - if (mEngine->getLedger ()->hasTransaction (mTxn.getTransactionID ())) + if (hasTransaction (*mEngine->getLedger (), mTxn.getTransactionID ())) return tefALREADY; m_journal.trace << "applyTransaction: has past sequence number " << @@ -259,8 +259,7 @@ TER Transactor::apply () return terResult; // Find source account - mTxnAccount = mEngine->view().entryCache (ltACCOUNT_ROOT, - getAccountRootIndex (mTxnAccountID)); + mTxnAccount = mEngine->view().peek (keylet::account(mTxnAccountID)); calculateFee (); @@ -296,7 +295,7 @@ TER Transactor::apply () if (terResult != tesSUCCESS) return (terResult); if (mTxnAccount) - mEngine->view().entryModify (mTxnAccount); + mEngine->view().update (mTxnAccount); return doApply (); } @@ -363,9 +362,9 @@ getSignerList ( { GetSignerListResult ret; - uint256 const index = getSignerListIndex (signingForAcctID); + auto const k = keylet::signers(signingForAcctID); SLE::pointer accountSignersList = - engine->view ().entryCache (ltSIGNER_LIST, index); + engine->view().peek (k); // If the signer list doesn't exist the account is not multi-signing. if (!accountSignersList) @@ -474,10 +473,8 @@ checkSigningAccounts ( // In any of these cases we need to know whether the account is in // the ledger. Determine that now. - uint256 const signerAccountIndex = getAccountRootIndex (signingAcctID); - SLE::pointer signersAccountRoot = - engine->view ().entryCache (ltACCOUNT_ROOT, signerAccountIndex); + engine->view().peek (keylet::account(signingAcctID)); if (signingAcctIDFromPubKey == signingAcctID) { diff --git a/src/ripple/app/tx/tests/common_transactor.cpp b/src/ripple/app/tx/tests/common_transactor.cpp index eb73aadd1e..fb9869c235 100644 --- a/src/ripple/app/tx/tests/common_transactor.cpp +++ b/src/ripple/app/tx/tests/common_transactor.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -106,7 +107,7 @@ std::pair TestLedger::applyTransaction (STTx const& tx, bool check) // Check for the transaction in the closed ledger. bool const foundTx = - lastClosedLedger_->hasTransaction(tx.getTransactionID()); + hasTransaction(*lastClosedLedger_, tx.getTransactionID()); suite_.expect (r.second == foundTx); return {r.first, r.second && foundTx}; @@ -378,7 +379,7 @@ std::vector getRippleStates ( { std::vector states; - forEachItem(*ledger.openLedger(), acct.getID(), getApp().getSLECache(), + forEachItem(*ledger.openLedger(), acct.getID(), [&states, &acct, &peer]( std::shared_ptr const& sleCur) { @@ -402,7 +403,7 @@ getOffersOnAccount (TestLedger& ledger, UserAccount const& acct) { std::vector > offers; - forEachItem(*ledger.openLedger(), acct.getID(), getApp().getSLECache(), + forEachItem(*ledger.openLedger(), acct.getID(), [&offers, &acct]( std::shared_ptr const& sleCur) { @@ -418,7 +419,7 @@ getTicketsOnAccount (TestLedger& ledger, UserAccount const& acct) { std::vector > offers; - forEachItem(*ledger.openLedger(), acct.getID(), getApp().getSLECache(), + forEachItem(*ledger.openLedger(), acct.getID(), [&offers, &acct]( std::shared_ptr const& sleCur) { diff --git a/src/ripple/ledger/CachedView.h b/src/ripple/ledger/CachedView.h new file mode 100644 index 0000000000..5cbae24298 --- /dev/null +++ b/src/ripple/ledger/CachedView.h @@ -0,0 +1,96 @@ +//------------------------------------------------------------------------------ +/* + 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_LEDGER_CACHEDVIEW_H_INCLUDED +#define RIPPLE_LEDGER_CACHEDVIEW_H_INCLUDED + +#include +#include + +namespace ripple { + +/** Cache-aware view to a ledger */ +class CachedView : public BasicView +{ +private: + Ledger& ledger_; + SLECache& cache_; + +public: + CachedView(CachedView const&) = delete; + CachedView& operator=(CachedView const&) = delete; + + /** Wrap a ledger with a cache. + @note Only ledgers may be wrapped with a cache. + */ + CachedView(Ledger& ledger, + SLECache& cache) + : ledger_(ledger) + , cache_(cache) + { + } + + bool + exists (Keylet const& k) const override + { + return ledger_.exists(k); + } + + boost::optional + succ (uint256 const& key, boost::optional< + uint256> last = boost::none) const override + { + return ledger_.succ(key, last); + } + + BasicView const* + parent() const override + { + return &ledger_; + } + + std::shared_ptr + read (Keylet const& k) const override; + + bool + unchecked_erase (uint256 const& key) override + { + return ledger_.unchecked_erase(key); + } + + void + unchecked_insert( + std::shared_ptr&& sle) override + { + ledger_.unchecked_insert( + std::move(sle)); + } + + void + unchecked_replace ( + std::shared_ptr&& sle) override + { + ledger_.unchecked_replace( + std::move(sle)); + } +}; + +} // ripple + +#endif diff --git a/src/ripple/ledger/DeferredCredits.h b/src/ripple/ledger/DeferredCredits.h new file mode 100644 index 0000000000..376313190c --- /dev/null +++ b/src/ripple/ledger/DeferredCredits.h @@ -0,0 +1,66 @@ +//------------------------------------------------------------------------------ +/* + 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_LEDGER_DEFERREDCREDITS_H_INCLUDED +#define RIPPLE_LEDGER_DEFERREDCREDITS_H_INCLUDED + +#include +#include +#include +#include + +namespace ripple { + +class DeferredCredits +{ +private: + // lowAccount, highAccount + using Key = std::tuple< + AccountID, AccountID, Currency>; + + // lowAccountCredits, highAccountCredits + using Value = std::tuple< + STAmount, STAmount>; + + static + Key + makeKey (AccountID const& a1, + AccountID const& a2, + Currency const& c); + + std::map map_; + +public: + // Get the adjusted balance of main for the + // balance between main and other. + STAmount + adjustedBalance (AccountID const& main, + AccountID const& other, + STAmount const& curBalance) const; + + void credit (AccountID const& sender, + AccountID const& receiver, + STAmount const& amount); + + void clear (); +}; + +} // ripple + +#endif diff --git a/src/ripple/ledger/SLECache.h b/src/ripple/ledger/SLECache.h new file mode 100644 index 0000000000..4d8ba39ed8 --- /dev/null +++ b/src/ripple/ledger/SLECache.h @@ -0,0 +1,37 @@ +//------------------------------------------------------------------------------ +/* + 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_LEDGER_SLECACHE_H_INCLUDED +#define RIPPLE_LEDGER_SLECACHE_H_INCLUDED + +#include +#include + +namespace ripple { + +/** STLedgerEntry cache. + This maps keys to the deserialized ledger entries, + to improve performance where the same item in + the ledger is accessed often. +*/ +using SLECache = TaggedCache ; + +} + +#endif diff --git a/src/ripple/ledger/View.h b/src/ripple/ledger/View.h new file mode 100644 index 0000000000..3d0b6ee2af --- /dev/null +++ b/src/ripple/ledger/View.h @@ -0,0 +1,302 @@ +//------------------------------------------------------------------------------ +/* + 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_LEDGER_VIEW_H_INCLUDED +#define RIPPLE_LEDGER_VIEW_H_INCLUDED + +#include +#include + +namespace ripple { + +/** A view into a ledger's state items. + + The interface provides raw access for state item + modification operations. There is no checkpointing + or calculation of metadata. +*/ +class BasicView +{ +public: + using key_type = uint256; + using mapped_type = + std::shared_ptr; + + BasicView() = default; + BasicView(BasicView const&) = delete; + BasicView& operator=(BasicView const&) = delete; + virtual ~BasicView() = default; + + /** Determine if a state item exists. + + @note This can be more efficient than calling read. + + @return `true` if a SLE is associated with the + specified key. + */ + virtual + bool + exists (Keylet const& k) const = 0; + + /** Return the key of the next state item. + + This returns the key of the first state item + whose key is greater than the specified key. If + no such key is present, boost::none is returned. + + If `last` is engaged, returns boost::none when + the key returned would be outside the open + interval (key, last). + */ + virtual + boost::optional + succ (uint256 const& key, boost::optional< + uint256> last = boost::none) const = 0; + + /** Return the state item associated with a key. + + Effects: + If the key exists, gives the caller ownership + of the non-modifiable corresponding SLE. + + @note While the returned SLE is `const` from the + perspective of the caller, it can be changed + by other callers through raw operations. + + @return `nullptr` if the key is not present or + if the type does not match. + */ + virtual + std::shared_ptr + read (Keylet const& k) const = 0; + + /** Unconditionally erase a state item. + + Requirements: + key must exist + + Effects: + The item associated with key is + unconditionally removed. + + This can break invariants + + @return `true` if the key was found + */ + virtual + bool + unchecked_erase (uint256 const& key) = 0; + + /** Unconditionally insert a state item. + + Requirements: + The key must not already exist. + + Effects: + The key is associated with the SLE. + + Ownership of the SLE is transferred + to the view. + + This can break invariants. + + @note The key is taken from the SLE + */ + virtual + void + unchecked_insert (std::shared_ptr&& sle) = 0; + + /** Unconditionally replace a state item. + + Requirements: + The key must exist. + + Effects: + The key is associated with the SLE. + + Ownership of the SLE is transferred + to the view. + + This can break invariants. + + @note The key is taken from the SLE + */ + virtual + void + unchecked_replace (std::shared_ptr&& sle) = 0; + + /** Return the parent view or nullptr. + @note Changing views with children breaks invariants. + */ + // VFALCO This is a bit of a hack used to walk the parent + // list to gain access to the underlying ledger. + // Its an alternative to exposing things like the + // previous ledger close time. + virtual + BasicView const* + parent() const = 0; + + //-------------------------------------------------------------------------- + + // Unfortunately necessary for DeferredCredits + virtual + STAmount + deprecatedBalance (AccountID const& account, + AccountID const& issuer, STAmount const& amount) const + { + return amount; + } +}; + +//------------------------------------------------------------------------------ + +/** A contextual view into a ledger's state items. + + This refinement of BasicView provides an interface where + the SLE can be "checked out" for modifications and put + back in an updated or removed state. Also added is an + interface to provide contextual information necessary + to calculate the results of transaction processing, + including the metadata if the view is later applied to + the parent (using an interface in the derived class). + + This allows the MetaView implementation to journal + changes made to the state items in a ledger, with the + option to apply those changes to the parent ledger + or view or discard the changes without affecting the + parent. + + Typical usage is to call read() for non-mutating + operations. This can be done by calling any function that + takes a BasicView parameter. + + For mutating operations the sequence is as follows: + + // Add a new value + v.insert(sle); + + // Check out a value for modification + sle = v.peek(k); + + // Indicate that changes were made + v.update(sle) + + // Or, erase the value + v.erase(sle) + + The invariant is that insert, update, and erase may not + be called with any SLE which belongs to different View. +*/ +class View : public BasicView +{ +public: + View() = default; + View(BasicView const&) = delete; + View& operator=(BasicView const&) = delete; + virtual ~View() = default; + + /** Prepare to modify the SLE associated with key. + + Effects: + Gives the caller ownership of the SLE associated + with the specified key. + + The returned SLE may be used in a subsequent + call to erase or update. + + The SLE must not be passed to any other View. + + @return `nullptr` if the key is not present + */ + virtual + std::shared_ptr + peek (Keylet const& k) = 0; + + /** Remove a peeked SLE. + + Requirements: + `sle` was obtained from prior call to peek() + on this instance of the View. + + Effects: + The key is no longer associated with the SLE. + */ + virtual + void + erase (std::shared_ptr const& sle) = 0; + + /** Insert a new state SLE + + Requirements: + `sle` was not obtained from any calls to + peek() on any instances of View. + + Effects: + assert if the key already exists + + The key in the state map is associated + with the SLE. + + The View acquires ownership of the shared_ptr. + + @note The key is taken from the SLE + */ + virtual + void + insert (std::shared_ptr const& sle) = 0; + + /** Indicate changes to a peeked SLE + + Requirements: + `sle` was obtained from prior call to peek() + on this instance of the View. + + Effects: + The View is notified that the SLE changed. + + @note The key is taken from the SLE + */ + /** @{ */ + virtual + void + update (std::shared_ptr const& sle) = 0; + + //-------------------------------------------------------------------------- + + /** Returns `true` if the context is an open ledger. + + Open ledgers have different rules for what TER + codes are returned when a transaction fails. + */ + virtual + bool + openLedger() const = 0; + + // Unfortunately necessary for DeferredCredits + virtual + void + deprecatedCreditHint (AccountID const& from, + AccountID const& to, STAmount const& amount) + { + } +}; + +} // ripple + +#endif diff --git a/src/ripple/ledger/ViewAPI.h b/src/ripple/ledger/ViewAPI.h new file mode 100644 index 0000000000..f03f3f9a54 --- /dev/null +++ b/src/ripple/ledger/ViewAPI.h @@ -0,0 +1,271 @@ +//------------------------------------------------------------------------------ +/* + 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_LEDGER_VIEWAPI_H_INCLUDED +#define RIPPLE_LEDGER_VIEWAPI_H_INCLUDED + +#include +#include +#include +#include + +namespace ripple { + +/* + C++ API for reading and writing information in a View. +*/ + +//------------------------------------------------------------------------------ +// +// Observers +// +//------------------------------------------------------------------------------ + +/** Reflects the fee settings for a particular ledger. */ +class Fees +{ +private: + std::uint64_t base_; // Reference tx cost (drops) + std::uint32_t units_; // Reference fee units + std::uint32_t reserve_; // Reserve base (drops) + std::uint32_t increment_; // Reserve increment (drops) + +public: + Fees (BasicView const& view, + Config const& config); + + /** Returns the account reserve given the owner count, in drops. + + The reserve is calculated as the reserve base plus + the reserve increment times the number of increments. + */ + std::uint64_t + reserve (std::size_t ownerCount) const + { + return reserve_ + ownerCount * increment_; + } +}; + +bool +isGlobalFrozen (BasicView const& view, + AccountID const& issuer); + +// Returns the amount an account can spend without going into debt. +// +// <-- saAmount: amount of currency held by account. May be negative. +STAmount +accountHolds (BasicView const& view, + AccountID const& account, Currency const& currency, + AccountID const& issuer, FreezeHandling zeroIfFrozen, + Config const& config); + +STAmount +accountFunds (BasicView const& view, AccountID const& id, + STAmount const& saDefault, FreezeHandling freezeHandling, + Config const& config); + +/** Iterate all items in an account's owner directory. */ +void +forEachItem (BasicView const& view, AccountID const& id, + std::function const&)> f); + +/** Iterate all items after an item in an owner directory. + @param after The key of the item to start after + @param hint The directory page containing `after` + @param limit The maximum number of items to return + @return `false` if the iteration failed +*/ +bool +forEachItemAfter (BasicView const& view, AccountID const& id, + uint256 const& after, std::uint64_t const hint, + unsigned int limit, std::function< + bool (std::shared_ptr const&)> f); + +std::uint32_t +rippleTransferRate (BasicView const& view, + AccountID const& issuer); + +std::uint32_t +rippleTransferRate (BasicView const& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + AccountID const& issuer); + +/** Returns `true` if the directory is empty + @param key The key of the directory +*/ +bool +dirIsEmpty (BasicView const& view, + Keylet const& k); + +// Return the first entry and advance uDirEntry. +// <-- true, if had a next entry. +// VFALCO Fix these clumsy routines with an iterator +bool +cdirFirst (BasicView const& view, + uint256 const& uRootIndex, // --> Root of directory. + std::shared_ptr& sleNode, // <-> current node + unsigned int& uDirEntry, // <-- next entry + uint256& uEntryIndex); // <-- The entry, if available. Otherwise, zero. + +// Return the current entry and advance uDirEntry. +// <-- true, if had a next entry. +// VFALCO Fix these clumsy routines with an iterator +bool +cdirNext (BasicView const& view, + uint256 const& uRootIndex, // --> Root of directory + std::shared_ptr& sleNode, // <-> current node + unsigned int& uDirEntry, // <-> next entry + uint256& uEntryIndex); // <-- The entry, if available. Otherwise, zero. + +//------------------------------------------------------------------------------ +// +// Modifiers +// +//------------------------------------------------------------------------------ + +/** Adjust the owner count up or down. */ +void +adjustOwnerCount (View& view, + std::shared_ptr const& sle, + int amount); + +// Return the first entry and advance uDirEntry. +// <-- true, if had a next entry. +// VFALCO Fix these clumsy routines with an iterator +bool +dirFirst (View& view, + uint256 const& uRootIndex, // --> Root of directory. + std::shared_ptr& sleNode, // <-> current node + unsigned int& uDirEntry, // <-- next entry + uint256& uEntryIndex); // <-- The entry, if available. Otherwise, zero. + +// Return the current entry and advance uDirEntry. +// <-- true, if had a next entry. +// VFALCO Fix these clumsy routines with an iterator +bool +dirNext (View& view, + uint256 const& uRootIndex, // --> Root of directory + std::shared_ptr& sleNode, // <-> current node + unsigned int& uDirEntry, // <-> next entry + uint256& uEntryIndex); // <-- The entry, if available. Otherwise, zero. + +// <-- uNodeDir: For deletion, present to make dirDelete efficient. +// --> uRootIndex: The index of the base of the directory. Nodes are based off of this. +// --> uLedgerIndex: Value to add to directory. +// Only append. This allow for things that watch append only structure to just monitor from the last node on ward. +// Within a node with no deletions order of elements is sequential. Otherwise, order of elements is random. +TER +dirAdd (View& view, + std::uint64_t& uNodeDir, // Node of entry. + uint256 const& uRootIndex, + uint256 const& uLedgerIndex, + std::function fDescriber); + +TER +dirDelete (View& view, + const bool bKeepRoot, + const std::uint64_t& uNodeDir, // Node item is mentioned in. + uint256 const& uRootIndex, + uint256 const& uLedgerIndex, // Item being deleted + const bool bStable, + const bool bSoft); + +// VFALCO NOTE Both STAmount parameters should just +// be "Amount", a unit-less number. +// +/** Create a trust line + + This can set an initial balance. +*/ +TER +trustCreate (View& view, + const bool bSrcHigh, + AccountID const& uSrcAccountID, + AccountID const& uDstAccountID, + uint256 const& uIndex, // --> ripple state entry + SLE::ref sleAccount, // --> the account being set. + const bool bAuth, // --> authorize account. + const bool bNoRipple, // --> others cannot ripple through + const bool bFreeze, // --> funds cannot leave + STAmount const& saBalance, // --> balance of account being set. + // Issuer should be noAccount() + STAmount const& saLimit, // --> limit for account being set. + // Issuer should be the account being set. + const std::uint32_t uSrcQualityIn = 0, + const std::uint32_t uSrcQualityOut = 0); + +TER +trustDelete (View& view, + std::shared_ptr const& sleRippleState, + AccountID const& uLowAccountID, + AccountID const& uHighAccountID); + +/** Delete an offer. + + Requirements: + The passed `sle` be obtained from a prior + call to view.peek() +*/ +TER +offerDelete (View& view, + std::shared_ptr const& sle); + +//------------------------------------------------------------------------------ + +// +// Money Transfers +// + +// Direct send w/o fees: +// - Redeeming IOUs and/or sending sender's own IOUs. +// - Create trust line of needed. +// --> bCheckIssuer : normally require issuer to be involved. +TER +rippleCredit (View& view, + AccountID const& uSenderID, AccountID const& uReceiverID, + const STAmount & saAmount, bool bCheckIssuer = true); + +TER +accountSend (View& view, + AccountID const& from, + AccountID const& to, + const STAmount & saAmount); + +TER +issueIOU (View& view, + AccountID const& account, + STAmount const& amount, + Issue const& issue); + +TER +redeemIOU (View& view, + AccountID const& account, + STAmount const& amount, + Issue const& issue); + +TER +transferXRP (View& view, + AccountID const& from, + AccountID const& to, + STAmount const& amount); + +} // ripple + +#endif diff --git a/src/ripple/ledger/ViewAPIBasics.h b/src/ripple/ledger/ViewAPIBasics.h new file mode 100644 index 0000000000..77ecacad15 --- /dev/null +++ b/src/ripple/ledger/ViewAPIBasics.h @@ -0,0 +1,33 @@ +//------------------------------------------------------------------------------ +/* + 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_LEDGER_VIEWAPIBASICS_H_INCLUDED +#define RIPPLE_LEDGER_VIEWAPIBASICS_H_INCLUDED + +namespace ripple { + +enum FreezeHandling +{ + fhIGNORE_FREEZE, + fhZERO_IF_FROZEN +}; + +} // ripple + +#endif diff --git a/src/ripple/ledger/impl/CachedView.cpp b/src/ripple/ledger/impl/CachedView.cpp new file mode 100644 index 0000000000..5dcd7c1f34 --- /dev/null +++ b/src/ripple/ledger/impl/CachedView.cpp @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +/* + 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 { + +std::shared_ptr +CachedView::read (Keylet const& k) const +{ + uint256 hash; + // get hash since SLECache needs to know + auto const item = + ledger_.stateMap().peekItem(k.key, hash); + if (! item) + return nullptr; + if (auto sle = cache_.fetch(hash)) + { + if(! k.check(*sle)) + return nullptr; + return sle; + } + SerialIter sit(make_Slice(item->peekData())); + auto sle = std::make_shared(sit, item->key()); + if (! k.check(*sle)) + return nullptr; + // VFALCO TODO Eliminate "immutable" runtime property + sle->setImmutable (); + cache_.canonicalize(hash, sle); + // need move otherwise makes a copy + // because return type is different + return std::move(sle); +} + +} // ripple diff --git a/src/ripple/app/ledger/DeferredCredits.cpp b/src/ripple/ledger/impl/DeferredCredits.cpp similarity index 72% rename from src/ripple/app/ledger/DeferredCredits.cpp rename to src/ripple/ledger/impl/DeferredCredits.cpp index 2b9a7c8a4b..577dbe0631 100644 --- a/src/ripple/app/ledger/DeferredCredits.cpp +++ b/src/ripple/ledger/impl/DeferredCredits.cpp @@ -1,26 +1,39 @@ //------------------------------------------------------------------------------ /* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012-2015 Ripple Labs Inc. + 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. + 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. + 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 namespace ripple { + +auto +DeferredCredits::makeKey (AccountID const& a1, + AccountID const& a2, Currency const& c) -> + Key +{ + if (a1 < a2) + return std::make_tuple(a1, a2, c); + else + return std::make_tuple(a2, a1, c); +} + template void maybeLogCredit (AccountID const& sender, AccountID const& receiver, @@ -133,4 +146,6 @@ void DeferredCredits::clear () { map_.clear (); } -} + +} // ripple + diff --git a/src/ripple/ledger/impl/ViewAPI.cpp b/src/ripple/ledger/impl/ViewAPI.cpp new file mode 100644 index 0000000000..f225f0edaf --- /dev/null +++ b/src/ripple/ledger/impl/ViewAPI.cpp @@ -0,0 +1,1504 @@ +//------------------------------------------------------------------------------ +/* + 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 + +namespace ripple { + +// VFALCO NOTE A copy of the other one for now +/** Maximum number of entries in a directory page + A change would be protocol-breaking. +*/ +#ifndef DIR_NODE_MAX +#define DIR_NODE_MAX 32 +#endif + +//------------------------------------------------------------------------------ +// +// Observers +// +//------------------------------------------------------------------------------ + +Fees::Fees (BasicView const& view, Config const& config) + : base_(config.FEE_DEFAULT) + , units_(config.TRANSACTION_FEE_BASE) + , reserve_(config.FEE_ACCOUNT_RESERVE) + , increment_(config.FEE_OWNER_RESERVE) +{ + auto const sle = view.read(keylet::fees()); + if (sle) + { + // VFALCO NOTE Why getFieldIndex and not isFieldPresent? + if (sle->getFieldIndex (sfBaseFee) != -1) + base_ = sle->getFieldU64 (sfBaseFee); + + if (sle->getFieldIndex (sfReferenceFeeUnits) != -1) + units_ = sle->getFieldU32 (sfReferenceFeeUnits); + + if (sle->getFieldIndex (sfReserveBase) != -1) + reserve_ = sle->getFieldU32 (sfReserveBase); + + if (sle->getFieldIndex (sfReserveIncrement) != -1) + increment_ = sle->getFieldU32 (sfReserveIncrement); + } +} + +//------------------------------------------------------------------------------ + +bool +isGlobalFrozen (BasicView const& view, + AccountID const& issuer) +{ + // VFALCO Perhaps this should assert + if (isXRP (issuer)) + return false; + auto const sle = + view.read(keylet::account(issuer)); + if (sle && sle->isFlag (lsfGlobalFreeze)) + return true; + return false; +} + +// Can the specified account spend the specified currency issued by +// the specified issuer or does the freeze flag prohibit it? +static +bool +isFrozen (BasicView const& view, AccountID const& account, + Currency const& currency, AccountID const& issuer) +{ + if (isXRP (currency)) + return false; + auto sle = + view.read(keylet::account(issuer)); + if (sle && sle->isFlag (lsfGlobalFreeze)) + return true; + if (issuer != account) + { + // Check if the issuer froze the line + sle = view.read(keylet::line( + account, issuer, currency)); + if (sle && sle->isFlag( + (issuer > account) ? + lsfHighFreeze : lsfLowFreeze)) + return true; + } + return false; +} + +STAmount +accountHolds (BasicView const& view, + AccountID const& account, Currency const& currency, + AccountID const& issuer, FreezeHandling zeroIfFrozen, + Config const& config) +{ + STAmount amount; + if (isXRP(currency)) + { + Fees const fees{view, config}; + // XRP: return balance minus reserve + auto const sle = view.read( + keylet::account(account)); + auto const reserve = STAmount{fees.reserve( + sle->getFieldU32(sfOwnerCount))}; + auto const balance = + sle->getFieldAmount(sfBalance); + if (balance < reserve) + amount.clear (); + else + amount = balance - reserve; + WriteLog (lsTRACE, View) << "accountHolds:" << + " account=" << to_string (account) << + " amount=" << amount.getFullText () << + " balance=" << balance.getFullText () << + " reserve=" << reserve.getFullText (); + } + else + { + // IOU: Return balance on trust line modulo freeze + auto const sle = view.read(keylet::line( + account, issuer, currency)); + if (! sle) + { + amount.clear ({currency, issuer}); + } + else if ((zeroIfFrozen == fhZERO_IF_FROZEN) && + isFrozen(view, account, currency, issuer)) + { + amount.clear (IssueRef (currency, issuer)); + } + else if (account > issuer) + { + // Put balance in account terms. + amount = sle->getFieldAmount (sfBalance); + amount.negate(); + amount.setIssuer (issuer); + } + else + { + amount = sle->getFieldAmount (sfBalance); + amount.setIssuer (issuer); + } + WriteLog (lsTRACE, View) << "accountHolds:" << + " account=" << to_string (account) << + " amount=" << amount.getFullText (); + } + + return view.deprecatedBalance(account, issuer, amount); +} + +STAmount +accountFunds (BasicView const& view, AccountID const& id, + STAmount const& saDefault, FreezeHandling freezeHandling, + Config const& config) +{ + STAmount saFunds; + + if (!saDefault.native () && + saDefault.getIssuer () == id) + { + saFunds = saDefault; + WriteLog (lsTRACE, View) << "accountFunds:" << + " account=" << to_string (id) << + " saDefault=" << saDefault.getFullText () << + " SELF-FUNDED"; + } + else + { + saFunds = accountHolds(view, id, + saDefault.getCurrency(), saDefault.getIssuer(), + freezeHandling, config); + WriteLog (lsTRACE, View) << "accountFunds:" << + " account=" << to_string (id) << + " saDefault=" << saDefault.getFullText () << + " saFunds=" << saFunds.getFullText (); + } + return saFunds; +} + +void +forEachItem (BasicView const& view, AccountID const& id, + std::function const&)> f) +{ + auto const root = keylet::ownerDir(id); + auto pos = root; + for(;;) + { + auto sle = view.read(pos); + if (! sle) + return; + // VFALCO NOTE We aren't checking field exists? + for (auto const& key : sle->getFieldV256(sfIndexes)) + f(view.read(keylet::child(key))); + auto const next = + sle->getFieldU64 (sfIndexNext); + if (! next) + return; + pos = keylet::page(root, next); + } +} + +bool +forEachItemAfter (BasicView const& view, AccountID const& id, + uint256 const& after, std::uint64_t const hint, + unsigned int limit, std::function< + bool (std::shared_ptr const&)> f) +{ + auto const rootIndex = keylet::ownerDir(id); + auto currentIndex = rootIndex; + + // If startAfter is not zero try jumping to that page using the hint + if (after.isNonZero ()) + { + auto const hintIndex = keylet::page(rootIndex, hint); + auto hintDir = view.read(hintIndex); + if (hintDir) + { + for (auto const& key : hintDir->getFieldV256 (sfIndexes)) + { + if (key == after) + { + // We found the hint, we can start here + currentIndex = hintIndex; + break; + } + } + } + + bool found = false; + for (;;) + { + auto const ownerDir = view.read(currentIndex); + if (! ownerDir) + return found; + for (auto const& key : ownerDir->getFieldV256 (sfIndexes)) + { + if (! found) + { + if (key == after) + found = true; + } + else if (f (view.read(keylet::child(key))) && limit-- <= 1) + { + return found; + } + } + + auto const uNodeNext = + ownerDir->getFieldU64(sfIndexNext); + if (uNodeNext == 0) + return found; + currentIndex = keylet::page(rootIndex, uNodeNext); + } + } + else + { + for (;;) + { + auto const ownerDir = view.read(currentIndex); + if (! ownerDir) + return true; + for (auto const& key : ownerDir->getFieldV256 (sfIndexes)) + if (f (view.read(keylet::child(key))) && limit-- <= 1) + return true; + auto const uNodeNext = + ownerDir->getFieldU64 (sfIndexNext); + if (uNodeNext == 0) + return true; + currentIndex = keylet::page(rootIndex, uNodeNext); + } + } +} + +std::uint32_t +rippleTransferRate (BasicView const& view, + AccountID const& issuer) +{ + auto const sle = view.read(keylet::account(issuer)); + std::uint32_t quality; + if (sle && sle->isFieldPresent (sfTransferRate)) + quality = sle->getFieldU32 (sfTransferRate); + else + quality = QUALITY_ONE; + return quality; +} + +std::uint32_t +rippleTransferRate (BasicView const& view, + AccountID const& uSenderID, + AccountID const& uReceiverID, + AccountID const& issuer) +{ + // If calculating the transfer rate from + // or to the issuer of the currency no + // fees are assessed. + return (uSenderID == issuer || uReceiverID == issuer) + ? QUALITY_ONE + : rippleTransferRate(view, issuer); +} + +bool +dirIsEmpty (BasicView const& view, + Keylet const& k) +{ + auto const sleNode = view.read(k); + if (! sleNode) + return true; + if (! sleNode->getFieldV256 (sfIndexes).empty ()) + return false; + // If there's another page, it must be non-empty + return sleNode->getFieldU64 (sfIndexNext) == 0; +} + +bool +cdirFirst (BasicView const& view, + uint256 const& uRootIndex, // --> Root of directory. + std::shared_ptr& sleNode, // <-> current node + unsigned int& uDirEntry, // <-- next entry + uint256& uEntryIndex) // <-- The entry, if available. Otherwise, zero. +{ + sleNode = view.read(keylet::page(uRootIndex)); + uDirEntry = 0; + assert (sleNode); // Never probe for directories. + return cdirNext (view, uRootIndex, sleNode, uDirEntry, uEntryIndex); +} + +bool +cdirNext (BasicView const& view, + uint256 const& uRootIndex, // --> Root of directory + std::shared_ptr& sleNode, // <-> current node + unsigned int& uDirEntry, // <-> next entry + uint256& uEntryIndex) // <-- The entry, if available. Otherwise, zero. +{ + auto const& svIndexes = sleNode->getFieldV256 (sfIndexes); + assert (uDirEntry <= svIndexes.size ()); + if (uDirEntry >= svIndexes.size ()) + { + auto const uNodeNext = + sleNode->getFieldU64 (sfIndexNext); + if (! uNodeNext) + { + uEntryIndex.zero (); + return false; + } + auto const sleNext = view.read( + keylet::page(uRootIndex, uNodeNext)); + uDirEntry = 0; + if (!sleNext) + { + // This should never happen + WriteLog (lsFATAL, View) + << "Corrupt directory: index:" + << uRootIndex << " next:" << uNodeNext; + return false; + } + sleNode = sleNext; + return cdirNext (view, uRootIndex, + sleNode, uDirEntry, uEntryIndex); + } + uEntryIndex = svIndexes[uDirEntry++]; + WriteLog (lsTRACE, View) << "dirNext:" << + " uDirEntry=" << uDirEntry << + " uEntryIndex=" << uEntryIndex; + return true; +} + +//------------------------------------------------------------------------------ +// +// Modifiers +// +//------------------------------------------------------------------------------ + +void +adjustOwnerCount (View& view, + std::shared_ptr const& sle, + int amount) +{ + assert(amount != 0); + auto const current = + sle->getFieldU32 (sfOwnerCount); + auto adjusted = current + amount; + if (amount > 0) + { + // Overflow is well defined on unsigned + if (adjusted < current) + { + WriteLog (lsFATAL, View) << + "Account " << sle->getFieldAccount160(sfAccount) << + " owner count exceeds max!"; + adjusted = + std::numeric_limits::max (); + } + } + else + { + // Underflow is well defined on unsigned + if (adjusted > current) + { + WriteLog (lsFATAL, View) << + "Account " << sle->getFieldAccount160 (sfAccount) << + " owner count set below 0!"; + adjusted = 0; + assert(false); + } + } + sle->setFieldU32 (sfOwnerCount, adjusted); + view.update(sle); +} + +bool +dirFirst (View& view, + uint256 const& uRootIndex, // --> Root of directory. + std::shared_ptr& sleNode, // <-> current node + unsigned int& uDirEntry, // <-- next entry + uint256& uEntryIndex) // <-- The entry, if available. Otherwise, zero. +{ + sleNode = view.peek(keylet::page(uRootIndex)); + uDirEntry = 0; + assert (sleNode); // Never probe for directories. + return dirNext (view, uRootIndex, sleNode, uDirEntry, uEntryIndex); +} + +bool +dirNext (View& view, + uint256 const& uRootIndex, // --> Root of directory + std::shared_ptr& sleNode, // <-> current node + unsigned int& uDirEntry, // <-> next entry + uint256& uEntryIndex) // <-- The entry, if available. Otherwise, zero. +{ + auto const& svIndexes = sleNode->getFieldV256 (sfIndexes); + assert (uDirEntry <= svIndexes.size ()); + if (uDirEntry >= svIndexes.size ()) + { + auto const uNodeNext = + sleNode->getFieldU64 (sfIndexNext); + if (! uNodeNext) + { + uEntryIndex.zero (); + return false; + } + auto const sleNext = view.peek( + keylet::page(uRootIndex, uNodeNext)); + uDirEntry = 0; + if (!sleNext) + { + // This should never happen + WriteLog (lsFATAL, View) + << "Corrupt directory: index:" + << uRootIndex << " next:" << uNodeNext; + return false; + } + sleNode = sleNext; + return dirNext (view, uRootIndex, + sleNode, uDirEntry, uEntryIndex); + } + uEntryIndex = svIndexes[uDirEntry++]; + WriteLog (lsTRACE, View) << "dirNext:" << + " uDirEntry=" << uDirEntry << + " uEntryIndex=" << uEntryIndex; + return true; +} + +TER +dirAdd (View& view, + std::uint64_t& uNodeDir, + uint256 const& uRootIndex, // VFALCO Should be Keylet + uint256 const& uLedgerIndex, + std::function fDescriber) +{ + WriteLog (lsTRACE, View) << "dirAdd:" << + " uRootIndex=" << to_string (uRootIndex) << + " uLedgerIndex=" << to_string (uLedgerIndex); + + SLE::pointer sleNode; + STVector256 svIndexes; + auto const k = keylet::page(uRootIndex); + auto sleRoot = view.peek(k); + + if (! sleRoot) + { + // No root, make it. + sleRoot = std::make_shared(k); + sleRoot->setFieldH256 (sfRootIndex, uRootIndex); + view.insert (sleRoot); + fDescriber (sleRoot, true); + sleNode = sleRoot; + uNodeDir = 0; + } + else + { + uNodeDir = sleRoot->getFieldU64 (sfIndexPrevious); // Get index to last directory node. + + if (uNodeDir) + { + // Try adding to last node. + sleNode = view.peek (keylet::page(uRootIndex, uNodeDir)); + + assert (sleNode); + } + else + { + // Try adding to root. Didn't have a previous set to the last node. + sleNode = sleRoot; + } + + svIndexes = sleNode->getFieldV256 (sfIndexes); + + if (DIR_NODE_MAX != svIndexes.size ()) + { + // Add to current node. + view.update(sleNode); + } + // Add to new node. + else if (!++uNodeDir) + { + return tecDIR_FULL; + } + else + { + // Have old last point to new node + sleNode->setFieldU64 (sfIndexNext, uNodeDir); + view.update(sleNode); + + // Have root point to new node. + sleRoot->setFieldU64 (sfIndexPrevious, uNodeDir); + view.update (sleRoot); + + // Create the new node. + sleNode = std::make_shared( + keylet::page(uRootIndex, uNodeDir)); + sleNode->setFieldH256 (sfRootIndex, uRootIndex); + view.insert (sleNode); + + if (uNodeDir != 1) + sleNode->setFieldU64 (sfIndexPrevious, uNodeDir - 1); + + fDescriber (sleNode, false); + + svIndexes = STVector256 (); + } + } + + svIndexes.push_back (uLedgerIndex); // Append entry. + sleNode->setFieldV256 (sfIndexes, svIndexes); // Save entry. + + WriteLog (lsTRACE, View) << + "dirAdd: creating: root: " << to_string (uRootIndex); + WriteLog (lsTRACE, View) << + "dirAdd: appending: Entry: " << to_string (uLedgerIndex); + WriteLog (lsTRACE, View) << + "dirAdd: appending: Node: " << strHex (uNodeDir); + + return tesSUCCESS; +} + +// Ledger must be in a state for this to work. +TER +dirDelete (View& view, + const bool bKeepRoot, // --> True, if we never completely clean up, after we overflow the root node. + const std::uint64_t& uNodeDir, // --> Node containing entry. + uint256 const& uRootIndex, // --> The index of the base of the directory. Nodes are based off of this. + uint256 const& uLedgerIndex, // --> Value to remove from directory. + const bool bStable, // --> True, not to change relative order of entries. + const bool bSoft) // --> True, uNodeDir is not hard and fast (pass uNodeDir=0). +{ + std::uint64_t uNodeCur = uNodeDir; + SLE::pointer sleNode = + view.peek(keylet::page(uRootIndex, uNodeCur)); + + if (!sleNode) + { + WriteLog (lsWARNING, View) << "dirDelete: no such node:" << + " uRootIndex=" << to_string (uRootIndex) << + " uNodeDir=" << strHex (uNodeDir) << + " uLedgerIndex=" << to_string (uLedgerIndex); + + if (!bSoft) + { + assert (false); + return tefBAD_LEDGER; + } + else if (uNodeDir < 20) + { + // Go the extra mile. Even if node doesn't exist, try the next node. + + return dirDelete (view, bKeepRoot, + uNodeDir + 1, uRootIndex, uLedgerIndex, bStable, true); + } + else + { + return tefBAD_LEDGER; + } + } + + STVector256 svIndexes = sleNode->getFieldV256 (sfIndexes); + + auto it = std::find (svIndexes.begin (), svIndexes.end (), uLedgerIndex); + + if (svIndexes.end () == it) + { + if (!bSoft) + { + assert (false); + WriteLog (lsWARNING, View) << "dirDelete: no such entry"; + return tefBAD_LEDGER; + } + + if (uNodeDir < 20) + { + // Go the extra mile. Even if entry not in node, try the next node. + return dirDelete (view, bKeepRoot, uNodeDir + 1, + uRootIndex, uLedgerIndex, bStable, true); + } + + return tefBAD_LEDGER; + } + + // Remove the element. + if (svIndexes.size () > 1) + { + if (bStable) + { + svIndexes.erase (it); + } + else + { + *it = svIndexes[svIndexes.size () - 1]; + svIndexes.resize (svIndexes.size () - 1); + } + } + else + { + svIndexes.clear (); + } + + sleNode->setFieldV256 (sfIndexes, svIndexes); + view.update(sleNode); + + if (svIndexes.empty ()) + { + // May be able to delete nodes. + std::uint64_t uNodePrevious = sleNode->getFieldU64 (sfIndexPrevious); + std::uint64_t uNodeNext = sleNode->getFieldU64 (sfIndexNext); + + if (!uNodeCur) + { + // Just emptied root node. + + if (!uNodePrevious) + { + // Never overflowed the root node. Delete it. + view.erase(sleNode); + } + // Root overflowed. + else if (bKeepRoot) + { + // If root overflowed and not allowed to delete overflowed root node. + } + else if (uNodePrevious != uNodeNext) + { + // Have more than 2 nodes. Can't delete root node. + } + else + { + // Have only a root node and a last node. + auto sleLast = view.peek(keylet::page(uRootIndex, uNodeNext)); + + assert (sleLast); + + if (sleLast->getFieldV256 (sfIndexes).empty ()) + { + // Both nodes are empty. + + view.erase (sleNode); // Delete root. + view.erase (sleLast); // Delete last. + } + else + { + // Have an entry, can't delete root node. + } + } + } + // Just emptied a non-root node. + else if (uNodeNext) + { + // Not root and not last node. Can delete node. + + auto slePrevious = + view.peek(keylet::page(uRootIndex, uNodePrevious)); + auto sleNext = view.peek(keylet::page(uRootIndex, uNodeNext)); + assert (slePrevious); + if (!slePrevious) + { + WriteLog (lsWARNING, View) << "dirDelete: previous node is missing"; + return tefBAD_LEDGER; + } + assert (sleNext); + if (!sleNext) + { + WriteLog (lsWARNING, View) << "dirDelete: next node is missing"; + return tefBAD_LEDGER; + } + + // Fix previous to point to its new next. + slePrevious->setFieldU64 (sfIndexNext, uNodeNext); + view.update (slePrevious); + + // Fix next to point to its new previous. + sleNext->setFieldU64 (sfIndexPrevious, uNodePrevious); + view.update (sleNext); + + view.erase(sleNode); + } + // Last node. + else if (bKeepRoot || uNodePrevious) + { + // Not allowed to delete last node as root was overflowed. + // Or, have pervious entries preventing complete delete. + } + else + { + // Last and only node besides the root. + auto sleRoot = view.peek (keylet::page(uRootIndex)); + + assert (sleRoot); + + if (sleRoot->getFieldV256 (sfIndexes).empty ()) + { + // Both nodes are empty. + + view.erase(sleRoot); // Delete root. + view.erase(sleNode); // Delete last. + } + else + { + // Root has an entry, can't delete. + } + } + } + + return tesSUCCESS; +} + +TER +trustCreate (View& view, + const bool bSrcHigh, + AccountID const& uSrcAccountID, + AccountID const& uDstAccountID, + uint256 const& uIndex, // --> ripple state entry + SLE::ref sleAccount, // --> the account being set. + const bool bAuth, // --> authorize account. + const bool bNoRipple, // --> others cannot ripple through + const bool bFreeze, // --> funds cannot leave + STAmount const& saBalance, // --> balance of account being set. + // Issuer should be noAccount() + STAmount const& saLimit, // --> limit for account being set. + // Issuer should be the account being set. + const std::uint32_t uQualityIn, + const std::uint32_t uQualityOut) +{ + WriteLog (lsTRACE, View) + << "trustCreate: " << to_string (uSrcAccountID) << ", " + << to_string (uDstAccountID) << ", " << saBalance.getFullText (); + + auto const& uLowAccountID = !bSrcHigh ? uSrcAccountID : uDstAccountID; + auto const& uHighAccountID = bSrcHigh ? uSrcAccountID : uDstAccountID; + + auto const sleRippleState = std::make_shared( + ltRIPPLE_STATE, uIndex); + view.insert (sleRippleState); + + std::uint64_t uLowNode; + std::uint64_t uHighNode; + + TER terResult = dirAdd (view, + uLowNode, + getOwnerDirIndex (uLowAccountID), + sleRippleState->getIndex (), + [uLowAccountID](std::shared_ptr const& sle, bool) + { + sle->setFieldAccount (sfOwner, uLowAccountID); + }); + + if (tesSUCCESS == terResult) + { + terResult = dirAdd (view, + uHighNode, + getOwnerDirIndex (uHighAccountID), + sleRippleState->getIndex (), + [uHighAccountID](std::shared_ptr const& sle, bool) + { + sle->setFieldAccount (sfOwner, uHighAccountID); + }); + } + + if (tesSUCCESS == terResult) + { + const bool bSetDst = saLimit.getIssuer () == uDstAccountID; + const bool bSetHigh = bSrcHigh ^ bSetDst; + + assert (sleAccount->getFieldAccount160 (sfAccount) == + (bSetHigh ? uHighAccountID : uLowAccountID)); + auto slePeer = view.peek (keylet::account( + bSetHigh ? uLowAccountID : uHighAccountID)); + assert (slePeer); + + // Remember deletion hints. + sleRippleState->setFieldU64 (sfLowNode, uLowNode); + sleRippleState->setFieldU64 (sfHighNode, uHighNode); + + sleRippleState->setFieldAmount ( + bSetHigh ? sfHighLimit : sfLowLimit, saLimit); + sleRippleState->setFieldAmount ( + bSetHigh ? sfLowLimit : sfHighLimit, + STAmount ({saBalance.getCurrency (), + bSetDst ? uSrcAccountID : uDstAccountID})); + + if (uQualityIn) + sleRippleState->setFieldU32 ( + bSetHigh ? sfHighQualityIn : sfLowQualityIn, uQualityIn); + + if (uQualityOut) + sleRippleState->setFieldU32 ( + bSetHigh ? sfHighQualityOut : sfLowQualityOut, uQualityOut); + + std::uint32_t uFlags = bSetHigh ? lsfHighReserve : lsfLowReserve; + + if (bAuth) + { + uFlags |= (bSetHigh ? lsfHighAuth : lsfLowAuth); + } + if (bNoRipple) + { + uFlags |= (bSetHigh ? lsfHighNoRipple : lsfLowNoRipple); + } + if (bFreeze) + { + uFlags |= (!bSetHigh ? lsfLowFreeze : lsfHighFreeze); + } + + if ((slePeer->getFlags() & lsfDefaultRipple) == 0) + { + // The other side's default is no rippling + uFlags |= (bSetHigh ? lsfLowNoRipple : lsfHighNoRipple); + } + + sleRippleState->setFieldU32 (sfFlags, uFlags); + adjustOwnerCount(view, sleAccount, 1); + + // ONLY: Create ripple balance. + sleRippleState->setFieldAmount (sfBalance, bSetHigh ? -saBalance : saBalance); + + view.deprecatedCreditHint (uSrcAccountID, uDstAccountID, saBalance); + } + + return terResult; +} + +TER +trustDelete (View& view, + std::shared_ptr const& sleRippleState, + AccountID const& uLowAccountID, + AccountID const& uHighAccountID) +{ + // Detect legacy dirs. + bool bLowNode = sleRippleState->isFieldPresent (sfLowNode); + bool bHighNode = sleRippleState->isFieldPresent (sfHighNode); + std::uint64_t uLowNode = sleRippleState->getFieldU64 (sfLowNode); + std::uint64_t uHighNode = sleRippleState->getFieldU64 (sfHighNode); + TER terResult; + + WriteLog (lsTRACE, View) + << "trustDelete: Deleting ripple line: low"; + terResult = dirDelete(view, + false, + uLowNode, + getOwnerDirIndex (uLowAccountID), + sleRippleState->getIndex (), + false, + !bLowNode); + + if (tesSUCCESS == terResult) + { + WriteLog (lsTRACE, View) + << "trustDelete: Deleting ripple line: high"; + terResult = dirDelete (view, + false, + uHighNode, + getOwnerDirIndex (uHighAccountID), + sleRippleState->getIndex (), + false, + !bHighNode); + } + + WriteLog (lsTRACE, View) << "trustDelete: Deleting ripple line: state"; + view.erase(sleRippleState); + + return terResult; +} + +TER +offerDelete (View& view, + std::shared_ptr const& sle) +{ + if (! sle) + return tesSUCCESS; + auto offerIndex = sle->getIndex (); + auto owner = sle->getFieldAccount160 (sfAccount); + + // Detect legacy directories. + bool bOwnerNode = sle->isFieldPresent (sfOwnerNode); + std::uint64_t uOwnerNode = sle->getFieldU64 (sfOwnerNode); + uint256 uDirectory = sle->getFieldH256 (sfBookDirectory); + std::uint64_t uBookNode = sle->getFieldU64 (sfBookNode); + + TER terResult = dirDelete (view, false, uOwnerNode, + getOwnerDirIndex (owner), offerIndex, false, !bOwnerNode); + TER terResult2 = dirDelete (view, false, uBookNode, + uDirectory, offerIndex, true, false); + + if (tesSUCCESS == terResult) + adjustOwnerCount(view, view.peek( + keylet::account(owner)), -1); + + view.erase(sle); + + return (terResult == tesSUCCESS) ? + terResult2 : terResult; +} + +// Direct send w/o fees: +// - Redeeming IOUs and/or sending sender's own IOUs. +// - Create trust line of needed. +// --> bCheckIssuer : normally require issuer to be involved. +TER +rippleCredit (View& view, + AccountID const& uSenderID, AccountID const& uReceiverID, + STAmount const& saAmount, bool bCheckIssuer) +{ + auto issuer = saAmount.getIssuer (); + auto currency = saAmount.getCurrency (); + + // Make sure issuer is involved. + assert ( + !bCheckIssuer || uSenderID == issuer || uReceiverID == issuer); + (void) issuer; + + // Disallow sending to self. + assert (uSenderID != uReceiverID); + + bool bSenderHigh = uSenderID > uReceiverID; + uint256 uIndex = getRippleStateIndex ( + uSenderID, uReceiverID, saAmount.getCurrency ()); + auto sleRippleState = view.peek (keylet::line(uIndex)); + + TER terResult; + + assert (!isXRP (uSenderID) && uSenderID != noAccount()); + assert (!isXRP (uReceiverID) && uReceiverID != noAccount()); + + if (!sleRippleState) + { + STAmount saReceiverLimit({currency, uReceiverID}); + STAmount saBalance = saAmount; + + saBalance.setIssuer (noAccount()); + + WriteLog (lsDEBUG, View) << "rippleCredit: " + "create line: " << to_string (uSenderID) << + " -> " << to_string (uReceiverID) << + " : " << saAmount.getFullText (); + + auto const sleAccount = + view.peek(keylet::account(uReceiverID)); + + bool noRipple = (sleAccount->getFlags() & lsfDefaultRipple) == 0; + + terResult = trustCreate (view, + bSenderHigh, + uSenderID, + uReceiverID, + uIndex, + sleAccount, + false, + noRipple, + false, + saBalance, + saReceiverLimit); + } + else + { + view.deprecatedCreditHint(uSenderID, uReceiverID, saAmount); + + STAmount saBalance = sleRippleState->getFieldAmount (sfBalance); + + if (bSenderHigh) + saBalance.negate (); // Put balance in sender terms. + + STAmount saBefore = saBalance; + + saBalance -= saAmount; + + WriteLog (lsTRACE, View) << "rippleCredit: " << + to_string (uSenderID) << + " -> " << to_string (uReceiverID) << + " : before=" << saBefore.getFullText () << + " amount=" << saAmount.getFullText () << + " after=" << saBalance.getFullText (); + + std::uint32_t const uFlags (sleRippleState->getFieldU32 (sfFlags)); + bool bDelete = false; + + // YYY Could skip this if rippling in reverse. + if (saBefore > zero + // Sender balance was positive. + && saBalance <= zero + // Sender is zero or negative. + && (uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) + // Sender reserve is set. + && static_cast (uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) != + static_cast (view.read (keylet::account(uSenderID))->getFlags() & lsfDefaultRipple) + && !(uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) + && !sleRippleState->getFieldAmount ( + !bSenderHigh ? sfLowLimit : sfHighLimit) + // Sender trust limit is 0. + && !sleRippleState->getFieldU32 ( + !bSenderHigh ? sfLowQualityIn : sfHighQualityIn) + // Sender quality in is 0. + && !sleRippleState->getFieldU32 ( + !bSenderHigh ? sfLowQualityOut : sfHighQualityOut)) + // Sender quality out is 0. + { + // Clear the reserve of the sender, possibly delete the line! + adjustOwnerCount(view, + view.peek(keylet::account(uSenderID)), -1); + + // Clear reserve flag. + sleRippleState->setFieldU32 ( + sfFlags, + uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); + + // Balance is zero, receiver reserve is clear. + bDelete = !saBalance // Balance is zero. + && !(uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)); + // Receiver reserve is clear. + } + + if (bSenderHigh) + saBalance.negate (); + + // Want to reflect balance to zero even if we are deleting line. + sleRippleState->setFieldAmount (sfBalance, saBalance); + // ONLY: Adjust ripple balance. + + if (bDelete) + { + terResult = trustDelete (view, + sleRippleState, + bSenderHigh ? uReceiverID : uSenderID, + !bSenderHigh ? uReceiverID : uSenderID); + } + else + { + view.update (sleRippleState); + terResult = tesSUCCESS; + } + } + + return terResult; +} + +// Calculate the fee needed to transfer IOU assets between two parties. +static +STAmount +rippleTransferFee (BasicView const& view, + AccountID const& from, + AccountID const& to, + AccountID const& issuer, + STAmount const& saAmount) +{ + if (from != issuer && to != issuer) + { + std::uint32_t uTransitRate = rippleTransferRate (view, issuer); + + if (QUALITY_ONE != uTransitRate) + { + STAmount saTransferTotal = multiply ( + saAmount, amountFromRate (uTransitRate), saAmount.issue ()); + STAmount saTransferFee = saTransferTotal - saAmount; + + WriteLog (lsDEBUG, View) << "rippleTransferFee:" << + " saTransferFee=" << saTransferFee.getFullText (); + + return saTransferFee; + } + } + + return saAmount.zeroed(); +} + +// Send regardless of limits. +// --> saAmount: Amount/currency/issuer to deliver to reciever. +// <-- saActual: Amount actually cost. Sender pay's fees. +static +TER +rippleSend (View& view, + AccountID const& uSenderID, AccountID const& uReceiverID, + STAmount const& saAmount, STAmount& saActual) +{ + auto const issuer = saAmount.getIssuer (); + TER terResult; + + assert (!isXRP (uSenderID) && !isXRP (uReceiverID)); + assert (uSenderID != uReceiverID); + + if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount()) + { + // Direct send: redeeming IOUs and/or sending own IOUs. + terResult = rippleCredit (view, uSenderID, uReceiverID, saAmount, false); + saActual = saAmount; + terResult = tesSUCCESS; + } + else + { + // Sending 3rd party IOUs: transit. + + STAmount saTransitFee = rippleTransferFee (view, + uSenderID, uReceiverID, issuer, saAmount); + + saActual = !saTransitFee ? saAmount : saAmount + saTransitFee; + + saActual.setIssuer (issuer); // XXX Make sure this done in + above. + + WriteLog (lsDEBUG, View) << "rippleSend> " << + to_string (uSenderID) << + " - > " << to_string (uReceiverID) << + " : deliver=" << saAmount.getFullText () << + " fee=" << saTransitFee.getFullText () << + " cost=" << saActual.getFullText (); + + terResult = rippleCredit (view, issuer, uReceiverID, saAmount); + + if (tesSUCCESS == terResult) + terResult = rippleCredit (view, uSenderID, issuer, saActual); + } + + return terResult; +} + +TER +accountSend (View& view, + AccountID const& uSenderID, AccountID const& uReceiverID, + STAmount const& saAmount) +{ + assert (saAmount >= zero); + + /* If we aren't sending anything or if the sender is the same as the + * receiver then we don't need to do anything. + */ + if (!saAmount || (uSenderID == uReceiverID)) + return tesSUCCESS; + + if (!saAmount.native ()) + { + STAmount saActual; + + WriteLog (lsTRACE, View) << "accountSend: " << + to_string (uSenderID) << " -> " << to_string (uReceiverID) << + " : " << saAmount.getFullText (); + + return rippleSend (view, uSenderID, uReceiverID, saAmount, saActual); + } + + view.deprecatedCreditHint (uSenderID, uReceiverID, saAmount); + + /* XRP send which does not check reserve and can do pure adjustment. + * Note that sender or receiver may be null and this not a mistake; this + * setup is used during pathfinding and it is carefully controlled to + * ensure that transfers are balanced. + */ + + TER terResult (tesSUCCESS); + + SLE::pointer sender = uSenderID != beast::zero + ? view.peek (keylet::account(uSenderID)) + : SLE::pointer (); + SLE::pointer receiver = uReceiverID != beast::zero + ? view.peek (keylet::account(uReceiverID)) + : SLE::pointer (); + + if (ShouldLog (lsTRACE, View)) + { + std::string sender_bal ("-"); + std::string receiver_bal ("-"); + + if (sender) + sender_bal = sender->getFieldAmount (sfBalance).getFullText (); + + if (receiver) + receiver_bal = receiver->getFieldAmount (sfBalance).getFullText (); + + WriteLog (lsTRACE, View) << "accountSend> " << + to_string (uSenderID) << " (" << sender_bal << + ") -> " << to_string (uReceiverID) << " (" << receiver_bal << + ") : " << saAmount.getFullText (); + } + + if (sender) + { + if (sender->getFieldAmount (sfBalance) < saAmount) + { + // VFALCO Its laborious to have to mutate the + // TER based on params everywhere + terResult = view.openLedger() + ? telFAILED_PROCESSING + : tecFAILED_PROCESSING; + } + else + { + // Decrement XRP balance. + sender->setFieldAmount (sfBalance, + sender->getFieldAmount (sfBalance) - saAmount); + view.update (sender); + } + } + + if (tesSUCCESS == terResult && receiver) + { + // Increment XRP balance. + receiver->setFieldAmount (sfBalance, + receiver->getFieldAmount (sfBalance) + saAmount); + view.update (receiver); + } + + if (ShouldLog (lsTRACE, View)) + { + std::string sender_bal ("-"); + std::string receiver_bal ("-"); + + if (sender) + sender_bal = sender->getFieldAmount (sfBalance).getFullText (); + + if (receiver) + receiver_bal = receiver->getFieldAmount (sfBalance).getFullText (); + + WriteLog (lsTRACE, View) << "accountSend< " << + to_string (uSenderID) << " (" << sender_bal << + ") -> " << to_string (uReceiverID) << " (" << receiver_bal << + ") : " << saAmount.getFullText (); + } + + return terResult; +} + +static +bool +updateTrustLine ( + View& view, + SLE::pointer state, + bool bSenderHigh, + AccountID const& sender, + STAmount const& before, + STAmount const& after) +{ + std::uint32_t const flags (state->getFieldU32 (sfFlags)); + + auto sle = view.peek (keylet::account(sender)); + assert (sle); + + // YYY Could skip this if rippling in reverse. + if (before > zero + // Sender balance was positive. + && after <= zero + // Sender is zero or negative. + && (flags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) + // Sender reserve is set. + && static_cast (flags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) != + static_cast (sle->getFlags() & lsfDefaultRipple) + && !(flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) + && !state->getFieldAmount ( + !bSenderHigh ? sfLowLimit : sfHighLimit) + // Sender trust limit is 0. + && !state->getFieldU32 ( + !bSenderHigh ? sfLowQualityIn : sfHighQualityIn) + // Sender quality in is 0. + && !state->getFieldU32 ( + !bSenderHigh ? sfLowQualityOut : sfHighQualityOut)) + // Sender quality out is 0. + { + // VFALCO Where is the line being deleted? + // Clear the reserve of the sender, possibly delete the line! + adjustOwnerCount(view, sle, -1); + + // Clear reserve flag. + state->setFieldU32 (sfFlags, + flags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); + + // Balance is zero, receiver reserve is clear. + if (!after // Balance is zero. + && !(flags & (bSenderHigh ? lsfLowReserve : lsfHighReserve))) + return true; + } + + return false; +} + +TER +issueIOU (View& view, + AccountID const& account, + STAmount const& amount, Issue const& issue) +{ + assert (!isXRP (account) && !isXRP (issue.account)); + + // Consistency check + assert (issue == amount.issue ()); + + // Can't send to self! + assert (issue.account != account); + + WriteLog (lsTRACE, View) << "issueIOU: " << + to_string (account) << ": " << + amount.getFullText (); + + bool bSenderHigh = issue.account > account; + uint256 const index = getRippleStateIndex ( + issue.account, account, issue.currency); + auto state = view.peek (keylet::line(index)); + + if (!state) + { + // NIKB TODO: The limit uses the receiver's account as the issuer and + // this is unnecessarily inefficient as copying which could be avoided + // is now required. Consider available options. + STAmount limit({issue.currency, account}); + STAmount final_balance = amount; + + final_balance.setIssuer (noAccount()); + + auto receiverAccount = view.peek (keylet::account(account)); + + bool noRipple = (receiverAccount->getFlags() & lsfDefaultRipple) == 0; + + return trustCreate (view, bSenderHigh, issue.account, account, index, + receiverAccount, false, noRipple, false, final_balance, limit); + } + + STAmount final_balance = state->getFieldAmount (sfBalance); + + if (bSenderHigh) + final_balance.negate (); // Put balance in sender terms. + + STAmount const start_balance = final_balance; + + final_balance -= amount; + + auto const must_delete = updateTrustLine(view, state, bSenderHigh, issue.account, + start_balance, final_balance); + + if (bSenderHigh) + final_balance.negate (); + + view.deprecatedCreditHint (issue.account, account, amount); + + // Adjust the balance on the trust line if necessary. We do this even if we + // are going to delete the line to reflect the correct balance at the time + // of deletion. + state->setFieldAmount (sfBalance, final_balance); + if (must_delete) + return trustDelete (view, state, + bSenderHigh ? account : issue.account, + bSenderHigh ? issue.account : account); + + view.update (state); + + return tesSUCCESS; +} + +TER +redeemIOU (View& view, + AccountID const& account, + STAmount const& amount, + Issue const& issue) +{ + assert (!isXRP (account) && !isXRP (issue.account)); + + // Consistency check + assert (issue == amount.issue ()); + + // Can't send to self! + assert (issue.account != account); + + WriteLog (lsTRACE, View) << "redeemIOU: " << + to_string (account) << ": " << + amount.getFullText (); + + bool bSenderHigh = account > issue.account; + uint256 const index = getRippleStateIndex ( + account, issue.account, issue.currency); + auto state = view.peek (keylet::line(index)); + + if (!state) + { + // In order to hold an IOU, a trust line *MUST* exist to track the + // balance. If it doesn't, then something is very wrong. Don't try + // to continue. + WriteLog (lsFATAL, View) << "redeemIOU: " << + to_string (account) << " attempts to redeem " << + amount.getFullText () << " but no trust line exists!"; + + return tefINTERNAL; + } + + STAmount final_balance = state->getFieldAmount (sfBalance); + + if (bSenderHigh) + final_balance.negate (); // Put balance in sender terms. + + STAmount const start_balance = final_balance; + + final_balance -= amount; + + auto const must_delete = updateTrustLine (view, state, bSenderHigh, account, + start_balance, final_balance); + + if (bSenderHigh) + final_balance.negate (); + + view.deprecatedCreditHint (account, issue.account, amount); + + // Adjust the balance on the trust line if necessary. We do this even if we + // are going to delete the line to reflect the correct balance at the time + // of deletion. + state->setFieldAmount (sfBalance, final_balance); + + if (must_delete) + { + return trustDelete (view, state, + bSenderHigh ? issue.account : account, + bSenderHigh ? account : issue.account); + } + + view.update (state); + return tesSUCCESS; +} + +TER +transferXRP (View& view, + AccountID const& from, + AccountID const& to, + STAmount const& amount) +{ + assert (from != beast::zero); + assert (to != beast::zero); + assert (from != to); + assert (amount.native ()); + + SLE::pointer sender = view.peek (keylet::account(from)); + SLE::pointer receiver = view.peek (keylet::account(to)); + + WriteLog (lsTRACE, View) << "transferXRP: " << + to_string (from) << " -> " << to_string (to) << + ") : " << amount.getFullText (); + + if (sender->getFieldAmount (sfBalance) < amount) + { + // VFALCO Its unfortunate we have to keep + // mutating these TER everywhere + // FIXME: this logic should be moved to callers maybe? + return view.openLedger() + ? telFAILED_PROCESSING + : tecFAILED_PROCESSING; + } + + // Decrement XRP balance. + sender->setFieldAmount (sfBalance, + sender->getFieldAmount (sfBalance) - amount); + view.update (sender); + + receiver->setFieldAmount (sfBalance, + receiver->getFieldAmount (sfBalance) + amount); + view.update (receiver); + + return tesSUCCESS; +} + +} // ripple diff --git a/src/ripple/overlay/impl/PeerImp.cpp b/src/ripple/overlay/impl/PeerImp.cpp index 5fac48cc73..497a13bed9 100644 --- a/src/ripple/overlay/impl/PeerImp.cpp +++ b/src/ripple/overlay/impl/PeerImp.cpp @@ -1862,7 +1862,8 @@ void PeerImp::getLedger (std::shared_ptr const& m) { protocol::TMGetLedger& packet = *m; - std::shared_ptr map; + std::shared_ptr shared; + SHAMap const* map = nullptr; protocol::TMLedgerData reply; bool fatLeaves = true; @@ -1888,9 +1889,10 @@ PeerImp::getLedger (std::shared_ptr const& m) uint256 txHash; memcpy (txHash.begin (), packet.ledgerhash ().data (), 32); - map = getApp().getInboundTransactions().getSet (txHash, false); + shared = getApp().getInboundTransactions().getSet (txHash, false); + map = shared.get(); - if (!map) + if (! map) { if (packet.has_querytype () && !packet.has_requestcookie ()) { @@ -2061,30 +2063,31 @@ PeerImp::getLedger (std::shared_ptr const& m) reply.add_nodes ()->set_nodedata ( nData.getDataPtr (), nData.getLength ()); - std::shared_ptr map = ledger->peekAccountStateMap (); - - if (map && map->getHash ().isNonZero ()) + if (ledger->haveStateMap()) { - // return account state root node if possible - Serializer rootNode (768); - - if (map->getRootNode (rootNode, snfWIRE)) + auto const& stateMap = ledger->stateMap (); + if (stateMap.getHash() != zero) { - reply.add_nodes ()->set_nodedata ( - rootNode.getDataPtr (), rootNode.getLength ()); - - if (ledger->getTransHash ().isNonZero ()) + // return account state root node if possible + Serializer rootNode (768); + if (stateMap.getRootNode(rootNode, snfWIRE)) { - map = ledger->peekTransactionMap (); + reply.add_nodes ()->set_nodedata ( + rootNode.getDataPtr (), rootNode.getLength ()); - if (map && map->getHash ().isNonZero ()) + if (ledger->getTransHash () != zero && ledger->haveTxMap ()) { - rootNode.erase (); + auto const& txMap = ledger->txMap (); - if (map->getRootNode (rootNode, snfWIRE)) - reply.add_nodes ()->set_nodedata ( - rootNode.getDataPtr (), - rootNode.getLength ()); + if (txMap.getHash() != zero) + { + rootNode.erase (); + + if (txMap.getRootNode (rootNode, snfWIRE)) + reply.add_nodes ()->set_nodedata ( + rootNode.getDataPtr (), + rootNode.getLength ()); + } } } } @@ -2098,13 +2101,15 @@ PeerImp::getLedger (std::shared_ptr const& m) if (packet.itype () == protocol::liTX_NODE) { - map = ledger->peekTransactionMap (); + assert (ledger->haveTxMap ()); + map = &ledger->txMap (); logMe += " TX:"; logMe += to_string (map->getHash ()); } else if (packet.itype () == protocol::liAS_NODE) { - map = ledger->peekAccountStateMap (); + assert (ledger->haveStateMap ()); + map = &ledger->stateMap (); logMe += " AS:"; logMe += to_string (map->getHash ()); } @@ -2143,7 +2148,9 @@ PeerImp::getLedger (std::shared_ptr const& m) try { - if (map->getNodeFat (mn, nodeIDs, rawNodes, fatLeaves, depth)) + // We are guaranteed that map is non-null, but we need to check + // to keep the compiler happy. + if (map && map->getNodeFat (mn, nodeIDs, rawNodes, fatLeaves, depth)) { assert (nodeIDs.size () == rawNodes.size ()); if (p_journal_.trace) p_journal_.trace << diff --git a/src/ripple/protocol/Indexes.h b/src/ripple/protocol/Indexes.h index 1991dc59fd..5c741dc575 100644 --- a/src/ripple/protocol/Indexes.h +++ b/src/ripple/protocol/Indexes.h @@ -20,6 +20,7 @@ #ifndef RIPPLE_PROTOCOL_INDEXES_H_INCLUDED #define RIPPLE_PROTOCOL_INDEXES_H_INCLUDED +#include #include #include #include @@ -94,23 +95,13 @@ getSignerListIndex (AccountID const& account); //------------------------------------------------------------------------------ -/** A pair of SHAMap key and LedgerEntryType. - - A Keylet identifies both a key in the state map - and its ledger entry type. +/* VFALCO TODO + For each of these operators that take just the uin256 and + only attach the LedgerEntryType, we can comment out that + operator to see what breaks, and those call sites are + candidates for having the Keylet either passed in a a + parameter, or having a data member that stores the keylet. */ -struct Keylet -{ - LedgerEntryType type; - uint256 key; - - Keylet (LedgerEntryType type_, - uint256 const& key_) - : type(type_) - , key(key_) - { - } -}; /** Keylet computation funclets. */ namespace keylet { @@ -125,12 +116,15 @@ struct account_t }; static account_t const account {}; -/** OWner directory */ -struct owndir_t +/** The amendment table */ +struct amendments_t { - Keylet operator()(AccountID const& id) const; + Keylet operator()() const; }; -static owndir_t const ownerDir {}; +static amendments_t const amendments {}; + +/** Any item that can be in an owner dir. */ +Keylet child (uint256 const& key); /** Skip list */ struct skip_t @@ -141,19 +135,13 @@ struct skip_t }; static skip_t const skip {}; -/** The amendment table */ -struct amendments_t -{ - Keylet operator()() const; -}; -static amendments_t const amendments {}; - /** The ledger fees */ -struct fee_t +struct fees_t { + // VFALCO This could maybe be constexpr Keylet operator()() const; }; -static fee_t const fee {}; +static fees_t const fees {}; /** The beginning of an order book */ struct book_t @@ -162,24 +150,36 @@ struct book_t }; static book_t const book {}; +/** A trust line */ +struct line_t +{ + Keylet operator()(AccountID const& id0, + AccountID const& id1, Currency const& currency) const; + + Keylet operator()(AccountID const& id, + Issue const& issue) const; + + Keylet operator()(uint256 const& key) const + { + return { ltRIPPLE_STATE, key }; + } +}; +static line_t const line {}; + /** An offer from an account */ struct offer_t { Keylet operator()(AccountID const& id, std::uint32_t seq) const; + + Keylet operator()(uint256 const& key) const + { + return { ltOFFER, key }; + } }; static offer_t const offer {}; -/** An item in a directory */ -struct item_t -{ - Keylet operator()(Keylet const& k, - std::uint64_t index, - LedgerEntryType type) const; -}; -static item_t const item {}; - -/** The directory for a specific quality */ +/** The initial directory page for a specific quality */ struct quality_t { Keylet operator()(Keylet const& k, @@ -199,27 +199,47 @@ struct ticket_t { Keylet operator()(AccountID const& id, std::uint32_t seq) const; + + Keylet operator()(uint256 const& key) const + { + return { ltTICKET, key }; + } }; static ticket_t const ticket {}; -/** A trust line */ -struct trust_t -{ - Keylet operator()(AccountID const& id0, - AccountID const& id1, Currency const& currency) const; - - Keylet operator()(AccountID const& id, - Issue const& issue) const; -}; -static trust_t const trust {}; - /** A SignerList */ struct signers_t { Keylet operator()(AccountID const& id) const; + + Keylet operator()(uint256 const& key) const + { + return { ltSIGNER_LIST, key }; + } }; static signers_t const signers {}; +//------------------------------------------------------------------------------ + +/** Any ledger entry */ +Keylet unchecked(uint256 const& key); + +/** The root page of an account's directory */ +Keylet ownerDir (AccountID const& id); + +/** A page in a directory */ +/** @{ */ +Keylet page (uint256 const& root, std::uint64_t index); +Keylet page (Keylet const& root, std::uint64_t index); +/** @} */ + +// DEPRECATED +inline +Keylet page (uint256 const& key) +{ + return { ltDIR_NODE, key }; +} + } // keylet } diff --git a/src/ripple/protocol/JsonFields.h b/src/ripple/protocol/JsonFields.h index f251eb9e43..9c2c338ad3 100644 --- a/src/ripple/protocol/JsonFields.h +++ b/src/ripple/protocol/JsonFields.h @@ -32,6 +32,8 @@ namespace jss { /* The "StaticString" field names are used instead of string literals to optimize the performance of accessing members of Json::Value objects. */ +// VFALCO What are these nonsense in/out comments? + JSS ( AL_hit_rate ); // out: GetCounts JSS ( Account ); // in: TransactionSign; field. JSS ( Amount ); // in: TransactionSign; field. @@ -72,7 +74,7 @@ JSS ( accounts ); // in: LedgerEntry, Subscribe, // handlers/Ledger, Unsubscribe // out: WalletAccounts JSS ( accounts_proposed ); // in: Subscribe, Unsubscribe -JSS ( action ); // out: LedgerEntrySet +JSS ( action ); JSS ( address ); // out: PeerImp JSS ( affected ); // out: AcceptedLedgerTx JSS ( age ); // out: UniqueNodeList, NetworkOPs @@ -232,7 +234,7 @@ JSS ( master_seed_hex ); // out: WalletPropose JSS ( max_ledger ); // in/out: LedgerCleaner JSS ( message ); // error. JSS ( meta ); // out: NetworkOPs, AccountTx*, Tx -JSS ( metaData ); // out: LedgerEntrySet, LedgerToJson +JSS ( metaData ); JSS ( metadata ); // out: TransactionEntry JSS ( method ); // RPC JSS ( min_count ); // in: GetCounts @@ -245,7 +247,6 @@ JSS ( network_ledger ); // out: NetworkOPs JSS ( no_ripple ); // out: AccountLines JSS ( no_ripple_peer ); // out: AccountLines JSS ( node ); // in: UnlAdd, UnlDelete - // out: LedgerEntrySet, LedgerEntry JSS ( node_binary ); // out: LedgerEntry JSS ( node_hit_rate ); // out: GetCounts JSS ( node_read_bytes ); // out: GetCounts @@ -253,7 +254,7 @@ JSS ( node_reads_hit ); // out: GetCounts JSS ( node_reads_total ); // out: GetCounts JSS ( node_writes ); // out: GetCounts JSS ( node_written_bytes ); // out: GetCounts -JSS ( nodes ); // out: LedgerEntrySet, PathState +JSS ( nodes ); // out: PathState JSS ( obligations ); // out: GatewayBalances JSS ( offer ); // in: LedgerEntry JSS ( offers ); // out: NetworkOPs, AccountOffers, Subscribe @@ -362,7 +363,7 @@ JSS ( tx_unsigned ); // out: TransactionSign JSS ( txn_count ); // out: NetworkOPs JSS ( txs ); // out: TxHistory JSS ( type ); // in: AccountObjects - // out: NetworkOPs, LedgerEntrySet + // out: NetworkOPs // paths/Node.cpp, OverlayImpl, Logic JSS ( type_hex ); // out: STPathSet JSS ( unl ); // out: UnlList diff --git a/src/ripple/protocol/Keylet.h b/src/ripple/protocol/Keylet.h new file mode 100644 index 0000000000..50697a1f9d --- /dev/null +++ b/src/ripple/protocol/Keylet.h @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +/* + 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_PROTOCOL_KEYLET_H_INCLUDED +#define RIPPLE_PROTOCOL_KEYLET_H_INCLUDED + +#include +#include + +namespace ripple { + +class STLedgerEntry; + +/** A pair of SHAMap key and LedgerEntryType. + + A Keylet identifies both a key in the state map + and its ledger entry type. + + @note Keylet is a portmanteau of the words key + and LET, an acronym for LedgerEntryType. +*/ +struct Keylet +{ + LedgerEntryType type; + uint256 key; + + Keylet (LedgerEntryType type_, + uint256 const& key_) + : type(type_) + , key(key_) + { + } + + /** Returns true if the SLE matches the type */ + bool + check (STLedgerEntry const&) const; +}; + +} + +#endif diff --git a/src/ripple/protocol/LedgerFormats.h b/src/ripple/protocol/LedgerFormats.h index 5c064c2999..1ee4a0ba35 100644 --- a/src/ripple/protocol/LedgerFormats.h +++ b/src/ripple/protocol/LedgerFormats.h @@ -35,8 +35,22 @@ namespace ripple { // Used as the type of a transaction or the type of a ledger entry. enum LedgerEntryType { + /** Special type, anything + This is used when the type in the Keylet is unknown, + such as when building metadata. + */ + ltANY = -3, + + /** Special type, anything not a directory + This is used when the type in the Keylet is unknown, + such as when iterating + */ + ltCHILD = -2, + ltINVALID = -1, + //--------------------------------------------------------------------------- + ltACCOUNT_ROOT = 'a', /** Directory node. diff --git a/src/ripple/protocol/Serializer.h b/src/ripple/protocol/Serializer.h index b64f62dfe4..d6c851e46e 100644 --- a/src/ripple/protocol/Serializer.h +++ b/src/ripple/protocol/Serializer.h @@ -180,11 +180,11 @@ public: } const void* getDataPtr () const { - return &mData.front (); + return mData.data(); } void* getDataPtr () { - return &mData.front (); + return mData.data(); } int getLength () const { diff --git a/src/ripple/protocol/impl/Indexes.cpp b/src/ripple/protocol/impl/Indexes.cpp index ce1b6e7a42..8c34a0bae9 100644 --- a/src/ripple/protocol/impl/Indexes.cpp +++ b/src/ripple/protocol/impl/Indexes.cpp @@ -42,7 +42,6 @@ getLedgerHashIndex (std::uint32_t desiredLedgerIndex) std::uint16_t(spaceSkipList), std::uint32_t(desiredLedgerIndex >> 16)); } - // get the index of the node that holds the enabled amendments uint256 getLedgerAmendmentIndex () @@ -211,11 +210,9 @@ Keylet account_t::operator()( getAccountRootIndex(ra.getAccountID()) }; } -Keylet owndir_t::operator()( - AccountID const& id) const +Keylet child (uint256 const& key) { - return { ltDIR_NODE, - getOwnerDirIndex(id) }; + return { ltCHILD, key }; } Keylet skip_t::operator()() const @@ -236,7 +233,7 @@ Keylet amendments_t::operator()() const getLedgerAmendmentIndex() }; } -Keylet fee_t::operator()() const +Keylet fees_t::operator()() const { return { ltFEE_SETTINGS, getLedgerFeeIndex() }; @@ -248,6 +245,20 @@ Keylet book_t::operator()(Book const& b) const getBookBase(b) }; } +Keylet line_t::operator()(AccountID const& id0, + AccountID const& id1, Currency const& currency) const +{ + return { ltRIPPLE_STATE, + getRippleStateIndex(id0, id1, currency) }; +} + +Keylet line_t::operator()(AccountID const& id, + Issue const& issue) const +{ + return { ltRIPPLE_STATE, + getRippleStateIndex(id, issue) }; +} + Keylet offer_t::operator()(AccountID const& id, std::uint32_t seq) const { @@ -255,14 +266,6 @@ Keylet offer_t::operator()(AccountID const& id, getOfferIndex(id, seq) }; } -Keylet item_t::operator()(Keylet const& k, - std::uint64_t index, - LedgerEntryType type) const -{ - return { type, - getDirNodeIndex(k.key, index) }; -} - Keylet quality_t::operator()(Keylet const& k, std::uint64_t q) const { @@ -285,26 +288,39 @@ Keylet ticket_t::operator()(AccountID const& id, getTicketIndex(id, seq) }; } -Keylet trust_t::operator()(AccountID const& id0, - AccountID const& id1, Currency const& currency) const -{ - return { ltRIPPLE_STATE, - getRippleStateIndex(id0, id1, currency) }; -} - -Keylet trust_t::operator()(AccountID const& id, - Issue const& issue) const -{ - return { ltRIPPLE_STATE, - getRippleStateIndex(id, issue) }; -} - Keylet signers_t::operator()(AccountID const& id) const { return { ltSIGNER_LIST, getSignerListIndex(id) }; } +//------------------------------------------------------------------------------ + +Keylet unchecked (uint256 const& key) +{ + return { ltANY, key }; +} + +Keylet ownerDir(AccountID const& id) +{ + return { ltDIR_NODE, + getOwnerDirIndex(id) }; +} + +Keylet page(uint256 const& key, + std::uint64_t index) +{ + return { ltDIR_NODE, + getDirNodeIndex(key, index) }; +} + +Keylet page(Keylet const& root, + std::uint64_t index) +{ + assert(root.type == ltDIR_NODE); + return page(root.key, index); +} + } // keylet } // ripple diff --git a/src/ripple/protocol/impl/Keylet.cpp b/src/ripple/protocol/impl/Keylet.cpp new file mode 100644 index 0000000000..772de796f5 --- /dev/null +++ b/src/ripple/protocol/impl/Keylet.cpp @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +/* + 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 { + +bool +Keylet::check (SLE const& sle) const +{ + if (type == ltANY) + return true; + if (type == ltINVALID) + return false; + if (type == ltCHILD) + { + assert(sle.getType() != ltDIR_NODE); + return sle.getType() != ltDIR_NODE; + } + assert(sle.getType() == type); + return sle.getType() == type; +} + +} // ripple diff --git a/src/ripple/rpc/handlers/AccountInfo.cpp b/src/ripple/rpc/handlers/AccountInfo.cpp index a3611b61c3..7e18e1ee2f 100644 --- a/src/ripple/rpc/handlers/AccountInfo.cpp +++ b/src/ripple/rpc/handlers/AccountInfo.cpp @@ -73,7 +73,7 @@ Json::Value doAccountInfo (RPC::Context& context) // See if there's a SignerEntries for this account. AccountID const account = naAccount.getAccountID (); uint256 const signerListIndex = getSignerListIndex (account); - auto const signerList = fetch(*ledger, signerListIndex, + auto const signerList = cachedRead(*ledger, signerListIndex, getApp().getSLECache()); if (signerList) diff --git a/src/ripple/rpc/handlers/AccountLines.cpp b/src/ripple/rpc/handlers/AccountLines.cpp index 1e9e276f3f..8abc820f63 100644 --- a/src/ripple/rpc/handlers/AccountLines.cpp +++ b/src/ripple/rpc/handlers/AccountLines.cpp @@ -19,6 +19,8 @@ #include #include +#include +#include #include #include @@ -101,7 +103,7 @@ Json::Value doAccountLines (RPC::Context& context) return result; } - if (! ledger->exists(getAccountRootIndex( + if (! ledger->exists(keylet::account( rippleAddress.getAccountID()))) return rpcError (rpcACT_NOT_FOUND); @@ -168,7 +170,7 @@ Json::Value doAccountLines (RPC::Context& context) return RPC::expected_field_error (jss::marker, "string"); startAfter.SetHex (marker.asString ()); - auto const sleLine = fetch(*ledger, startAfter, + auto const sleLine = cachedRead(*ledger, startAfter, getApp().getSLECache()); if (sleLine == nullptr || sleLine->getType () != ltRIPPLE_STATE) @@ -196,24 +198,28 @@ Json::Value doAccountLines (RPC::Context& context) visitData.items.reserve (++reserve); } - if (! forEachItemAfter(*ledger, raAccount, getApp().getSLECache(), - startAfter, startHint, reserve, - [&visitData](std::shared_ptr const& sleCur) - { - auto const line = - RippleState::makeItem (visitData.accountID, sleCur); - if (line != nullptr && - (! visitData.rippleAddressPeer.isValid () || - visitData.raPeerAccount == line->getAccountIDPeer ())) - { - visitData.items.emplace_back (line); - return true; - } - - return false; - })) { - return rpcError (rpcINVALID_PARAMS); + CachedView const view( + *ledger, getApp().getSLECache()); + if (! forEachItemAfter(view, raAccount, + startAfter, startHint, reserve, + [&visitData](std::shared_ptr const& sleCur) + { + auto const line = + RippleState::makeItem (visitData.accountID, sleCur); + if (line != nullptr && + (! visitData.rippleAddressPeer.isValid () || + visitData.raPeerAccount == line->getAccountIDPeer ())) + { + visitData.items.emplace_back (line); + return true; + } + + return false; + })) + { + return rpcError (rpcINVALID_PARAMS); + } } if (visitData.items.size () == reserve) diff --git a/src/ripple/rpc/handlers/AccountObjects.cpp b/src/ripple/rpc/handlers/AccountObjects.cpp index da57ffa573..65d43041b1 100644 --- a/src/ripple/rpc/handlers/AccountObjects.cpp +++ b/src/ripple/rpc/handlers/AccountObjects.cpp @@ -68,7 +68,7 @@ Json::Value doAccountObjects (RPC::Context& context) } } - if (! ledger->exists(getAccountRootIndex( + if (! ledger->exists(keylet::account( raAccount.getAccountID()))) return rpcError (rpcACT_NOT_FOUND); diff --git a/src/ripple/rpc/handlers/AccountOffers.cpp b/src/ripple/rpc/handlers/AccountOffers.cpp index 510541dafa..c1ea61ffeb 100644 --- a/src/ripple/rpc/handlers/AccountOffers.cpp +++ b/src/ripple/rpc/handlers/AccountOffers.cpp @@ -19,6 +19,8 @@ #include #include +#include +#include #include namespace ripple { @@ -63,7 +65,7 @@ Json::Value doAccountOffers (RPC::Context& context) if (bIndex) result[jss::account_index] = iIndex; - if (! ledger->exists(getAccountRootIndex( + if (! ledger->exists(keylet::account( rippleAddress.getAccountID()))) return rpcError (rpcACT_NOT_FOUND); @@ -105,7 +107,7 @@ Json::Value doAccountOffers (RPC::Context& context) return RPC::expected_field_error (jss::marker, "string"); startAfter.SetHex (marker.asString ()); - auto const sleOffer = fetch (*ledger, startAfter, + auto const sleOffer = cachedRead (*ledger, startAfter, getApp().getSLECache()); if (sleOffer == nullptr || @@ -133,20 +135,24 @@ Json::Value doAccountOffers (RPC::Context& context) offers.reserve (++reserve); } - if (! forEachItemAfter(*ledger, raAccount, getApp().getSLECache(), - startAfter, startHint, reserve, - [&offers](std::shared_ptr const& offer) - { - if (offer->getType () == ltOFFER) - { - offers.emplace_back (offer); - return true; - } - - return false; - })) { - return rpcError (rpcINVALID_PARAMS); + CachedView const view( + *ledger, getApp().getSLECache()); + if (! forEachItemAfter(*ledger, raAccount, + startAfter, startHint, reserve, + [&offers](std::shared_ptr const& offer) + { + if (offer->getType () == ltOFFER) + { + offers.emplace_back (offer); + return true; + } + + return false; + })) + { + return rpcError (rpcINVALID_PARAMS); + } } if (offers.size () == reserve) diff --git a/src/ripple/rpc/handlers/GatewayBalances.cpp b/src/ripple/rpc/handlers/GatewayBalances.cpp index 6ea2831bf7..8856a88652 100644 --- a/src/ripple/rpc/handlers/GatewayBalances.cpp +++ b/src/ripple/rpc/handlers/GatewayBalances.cpp @@ -18,6 +18,8 @@ //============================================================================== #include +#include +#include #include #include #include @@ -142,46 +144,50 @@ Json::Value doGatewayBalances (RPC::Context& context) std::map > assets; // Traverse the cold wallet's trust lines - forEachItem(*ledger, accountID, getApp().getSLECache(), - [&](std::shared_ptr const& sle) - { - auto rs = RippleState::makeItem (accountID, sle); - - if (!rs) - return; - - int balSign = rs->getBalance().signum(); - if (balSign == 0) - return; - - auto const& peer = rs->getAccountIDPeer(); - - // Here, a negative balance means the cold wallet owes (normal) - // A positive balance means the cold wallet has an asset (unusual) - - if (hotWallets.count (peer) > 0) + { + CachedView const view( + *ledger, getApp().getSLECache()); + forEachItem(view, accountID, + [&](std::shared_ptr const& sle) { - // This is a specified hot wallt - hotBalances[peer].push_back (-rs->getBalance ()); - } - else if (balSign > 0) - { - // This is a gateway asset - assets[peer].push_back (rs->getBalance ()); - } - else - { - // normal negative balance, obligation to customer - auto& bal = sums[rs->getBalance().getCurrency()]; - if (bal == zero) + auto rs = RippleState::makeItem (accountID, sle); + + if (!rs) + return; + + int balSign = rs->getBalance().signum(); + if (balSign == 0) + return; + + auto const& peer = rs->getAccountIDPeer(); + + // Here, a negative balance means the cold wallet owes (normal) + // A positive balance means the cold wallet has an asset (unusual) + + if (hotWallets.count (peer) > 0) { - // This is needed to set the currency code correctly - bal = -rs->getBalance(); + // This is a specified hot wallt + hotBalances[peer].push_back (-rs->getBalance ()); + } + else if (balSign > 0) + { + // This is a gateway asset + assets[peer].push_back (rs->getBalance ()); } else - bal -= rs->getBalance(); - } - }); + { + // normal negative balance, obligation to customer + auto& bal = sums[rs->getBalance().getCurrency()]; + if (bal == zero) + { + // This is needed to set the currency code correctly + bal = -rs->getBalance(); + } + else + bal -= rs->getBalance(); + } + }); + } if (! sums.empty()) { diff --git a/src/ripple/rpc/handlers/LedgerData.cpp b/src/ripple/rpc/handlers/LedgerData.cpp index 9fe1a491df..106f712531 100644 --- a/src/ripple/rpc/handlers/LedgerData.cpp +++ b/src/ripple/rpc/handlers/LedgerData.cpp @@ -75,7 +75,7 @@ Json::Value doLedgerData (RPC::Context& context) jvResult[jss::ledger_index] = std::to_string( lpLedger->getLedgerSeq ()); Json::Value& nodes = (jvResult[jss::state] = Json::arrayValue); - SHAMap& map = *(lpLedger->peekAccountStateMap ()); + auto& map = lpLedger->stateMap(); for (;;) { diff --git a/src/ripple/rpc/handlers/LedgerEntry.cpp b/src/ripple/rpc/handlers/LedgerEntry.cpp index 2624e1a278..931e725245 100644 --- a/src/ripple/rpc/handlers/LedgerEntry.cpp +++ b/src/ripple/rpc/handlers/LedgerEntry.cpp @@ -208,7 +208,7 @@ Json::Value doLedgerEntry (RPC::Context& context) if (uNodeIndex.isNonZero ()) { - auto const sleNode = fetch(*lpLedger, uNodeIndex, + auto const sleNode = cachedRead(*lpLedger, uNodeIndex, getApp().getSLECache()); if (context.params.isMember(jss::binary)) diff --git a/src/ripple/rpc/handlers/NoRippleCheck.cpp b/src/ripple/rpc/handlers/NoRippleCheck.cpp index 3e5b6807bb..3eb4339006 100644 --- a/src/ripple/rpc/handlers/NoRippleCheck.cpp +++ b/src/ripple/rpc/handlers/NoRippleCheck.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include @@ -137,7 +139,9 @@ Json::Value doNoRippleCheck (RPC::Context& context) auto const accountID = rippleAddress.getAccountID (); - forEachItemAfter (*ledger, accountID, getApp().getSLECache(), + CachedView const view( + *ledger, getApp().getSLECache()); + forEachItemAfter (view, accountID, uint256(), 0, limit, [&](std::shared_ptr const& ownedItem) { @@ -146,7 +150,7 @@ Json::Value doNoRippleCheck (RPC::Context& context) bool const bLow = accountID == ownedItem->getFieldAmount(sfLowLimit).getIssuer(); bool const bNoRipple = ownedItem->getFieldU32(sfFlags) & - (bLow ? lsfLowNoRipple : lsfHighNoRipple); + (bLow ? lsfLowNoRipple : lsfHighNoRipple); std::string problem; bool needFix = false; diff --git a/src/ripple/rpc/handlers/RipplePathFind.cpp b/src/ripple/rpc/handlers/RipplePathFind.cpp index cef335108d..cef0f117b5 100644 --- a/src/ripple/rpc/handlers/RipplePathFind.cpp +++ b/src/ripple/rpc/handlers/RipplePathFind.cpp @@ -281,10 +281,10 @@ ripplePathFind(RippleLineCache::pointer const& cache, STAmount saMaxAmount({ uSrcCurrencyID, issuer }, 1); saMaxAmount.negate(); - LedgerEntrySet lesSandbox(lpLedger, tapNONE); + MetaView sandbox(lpLedger, tapNONE); auto rc = path::RippleCalc::rippleCalculate( - lesSandbox, + sandbox, saMaxAmount, // --> Amount to send is unlimited // to get an estimate. saDstAmount, // --> Amount to deliver. @@ -306,9 +306,9 @@ ripplePathFind(RippleLineCache::pointer const& cache, << "Trying with an extra path element"; spsComputed.push_back(fullLiquidityPath); - reconstruct(lesSandbox, lpLedger, tapNONE); + reconstruct(sandbox, lpLedger, tapNONE); rc = path::RippleCalc::rippleCalculate( - lesSandbox, + sandbox, saMaxAmount, // --> Amount to send is unlimited // to get an estimate. saDstAmount, // --> Amount to deliver. diff --git a/src/ripple/rpc/handlers/TransactionEntry.cpp b/src/ripple/rpc/handlers/TransactionEntry.cpp index c737779196..d4883efca3 100644 --- a/src/ripple/rpc/handlers/TransactionEntry.cpp +++ b/src/ripple/rpc/handlers/TransactionEntry.cpp @@ -67,7 +67,8 @@ Json::Value doTransactionEntry (RPC::Context& context) Transaction::pointer tpTrans; TransactionMetaSet::pointer tmTrans; - if (!lpLedger->getTransaction (uTransID, tpTrans, tmTrans)) + if (!getTransaction (*lpLedger, uTransID, tpTrans, tmTrans, + getApp().getMasterTransaction())) { jvResult[jss::error] = "transactionNotFound"; } diff --git a/src/ripple/rpc/handlers/Tx.cpp b/src/ripple/rpc/handlers/Tx.cpp index 1cf9313ff9..28331d84d5 100644 --- a/src/ripple/rpc/handlers/Tx.cpp +++ b/src/ripple/rpc/handlers/Tx.cpp @@ -75,7 +75,7 @@ Json::Value doTx (RPC::Context& context) { std::string meta; - if (lgr->getMetaHex (txn->getID (), meta)) + if (getMetaHex (*lgr, txn->getID (), meta)) { ret[jss::meta] = meta; okay = true; @@ -85,7 +85,7 @@ Json::Value doTx (RPC::Context& context) { TransactionMetaSet::pointer txMeta; - if (lgr->getTransactionMeta (txn->getID (), txMeta)) + if (getTransactionMeta (*lgr, txn->getID (), txMeta)) { okay = true; auto meta = txMeta->getJson (0); diff --git a/src/ripple/rpc/impl/GetAccountObjects.cpp b/src/ripple/rpc/impl/GetAccountObjects.cpp index 66b54d9a3b..9c49b1461e 100644 --- a/src/ripple/rpc/impl/GetAccountObjects.cpp +++ b/src/ripple/rpc/impl/GetAccountObjects.cpp @@ -39,7 +39,7 @@ getAccountObjects (Ledger const& ledger, AccountID const& account, found = true; } - auto dir = fetch(ledger, dirIndex, + auto dir = cachedRead(ledger, dirIndex, getApp().getSLECache(), ltDIR_NODE); if (! dir) return false; @@ -62,7 +62,7 @@ getAccountObjects (Ledger const& ledger, AccountID const& account, for (; iter != entries.end (); ++iter) { - auto const sleNode = fetch(ledger, *iter, + auto const sleNode = cachedRead(ledger, *iter, getApp().getSLECache()); if (type == ltINVALID || sleNode->getType () == type) { @@ -88,7 +88,7 @@ getAccountObjects (Ledger const& ledger, AccountID const& account, return true; dirIndex = getDirNodeIndex (rootDirIndex, nodeIndex); - dir = fetch(ledger, dirIndex, + dir = cachedRead(ledger, dirIndex, getApp().getSLECache(), ltDIR_NODE); if (! dir) return true; diff --git a/src/ripple/rpc/impl/TransactionSign.cpp b/src/ripple/rpc/impl/TransactionSign.cpp index c0ab7d021a..71a3f5b537 100644 --- a/src/ripple/rpc/impl/TransactionSign.cpp +++ b/src/ripple/rpc/impl/TransactionSign.cpp @@ -195,7 +195,7 @@ bool TxnSignApiFacade::hasAccountRoot () const if (!netOPs_) // Unit testing. return true; return ledger_->exists( - getAccountRootIndex(accountID_)); + keylet::account(accountID_)); } error_code_i acctMatchesPubKey ( diff --git a/src/ripple/test/jtx/Env.h b/src/ripple/test/jtx/Env.h index 7f203de1f1..007a49a072 100644 --- a/src/ripple/test/jtx/Env.h +++ b/src/ripple/test/jtx/Env.h @@ -185,9 +185,8 @@ public: /** Return a ledger entry. @return empty if the ledger entry does not exist */ - // VFALCO NOTE Use Keylet here std::shared_ptr - le (uint256 const& key) const; + le (Keylet const& k) const; /** Create a JTx from parameters. */ template Env::le (Account const& account) const { - return ledger->fetch( - getAccountRootIndex(account.id())); + return le(keylet::account(account.id())); } std::shared_ptr -Env::le (uint256 const& key) const +Env::le (Keylet const& k) const { - return ledger->fetch(key); + return ledger->read(k); } void diff --git a/src/ripple/test/jtx/impl/balance.cpp b/src/ripple/test/jtx/impl/balance.cpp index 4109ae2747..ae8eab7eb1 100644 --- a/src/ripple/test/jtx/impl/balance.cpp +++ b/src/ripple/test/jtx/impl/balance.cpp @@ -42,9 +42,8 @@ balance::operator()(Env const& env) const } else { - auto const sle = env.le( - getRippleStateIndex(account_.id(), - value_.issue())); + auto const sle = env.le(keylet::line( + account_.id(), value_.issue())); if (none_) { env.test.expect(! sle); diff --git a/src/ripple/test/jtx/impl/owners.cpp b/src/ripple/test/jtx/impl/owners.cpp index 4ba2fac83c..2f26d7ada1 100644 --- a/src/ripple/test/jtx/impl/owners.cpp +++ b/src/ripple/test/jtx/impl/owners.cpp @@ -18,7 +18,7 @@ //============================================================================== #include -#include +#include #include namespace ripple { @@ -33,7 +33,7 @@ owned_count_of(Ledger const& ledger, LedgerEntryType type) { std::uint32_t count = 0; - forEachItem(ledger, id, getApp().getSLECache(), + forEachItem(ledger, id, [&count, type](std::shared_ptr const& sle) { if (sle->getType() == type) diff --git a/src/ripple/test/jtx/impl/utility.cpp b/src/ripple/test/jtx/impl/utility.cpp index 321571cc34..2fba02e5e3 100644 --- a/src/ripple/test/jtx/impl/utility.cpp +++ b/src/ripple/test/jtx/impl/utility.cpp @@ -71,14 +71,14 @@ fill_seq (Json::Value& jv, { if (jv.isMember(jss::Sequence)) return; + // VFALCO TODO Use + // parseBase58(jv[jss::Account].asString()) RippleAddress ra; ra.setAccountID(jv[jss::Account].asString()); - auto const ar = ledger.fetch( - getAccountRootIndex(ra.getAccountID())); - + auto const ar = ledger.read( + keylet::account(ra.getAccountID())); if (!ar) return; - jv[jss::Sequence] = ar->getFieldU32(sfSequence); } diff --git a/src/ripple/unity/app_ledger.cpp b/src/ripple/unity/app_ledger.cpp index ac07d6fe4d..fdc9f985f7 100644 --- a/src/ripple/unity/app_ledger.cpp +++ b/src/ripple/unity/app_ledger.cpp @@ -24,14 +24,10 @@ #include #include #include -#include -#include #include -#include #include #include #include -#include #include #include @@ -42,7 +38,9 @@ #include #include #include +#include #include #include #include +#include diff --git a/src/ripple/unity/app_tests.cpp b/src/ripple/unity/app_tests.cpp new file mode 100644 index 0000000000..f27ccf0316 --- /dev/null +++ b/src/ripple/unity/app_tests.cpp @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +/* + 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 diff --git a/src/ripple/unity/ledger.cpp b/src/ripple/unity/ledger.cpp new file mode 100644 index 0000000000..a36562aed6 --- /dev/null +++ b/src/ripple/unity/ledger.cpp @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +/* + 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 diff --git a/src/ripple/unity/protocol.cpp b/src/ripple/unity/protocol.cpp index 545cb9c3c4..120e0cae1a 100644 --- a/src/ripple/unity/protocol.cpp +++ b/src/ripple/unity/protocol.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include