mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 19:15:54 +00:00
New classes for processing offers in order books:
* BookTip provides consume-and-step offer traversal * OfferStream applies offer business rules and presents offers to callers * Taker class manages state for the active party during order processing * Offer class wraps book offers for presentation
This commit is contained in:
@@ -820,6 +820,12 @@
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\validators\ripple_validators.cpp" />
|
||||
<ClCompile Include="..\..\src\ripple_app\book\tests\OfferStream.test.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple_app\book\tests\Quality.test.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
@@ -2485,7 +2491,11 @@
|
||||
<ClInclude Include="..\..\src\ripple\validators\ripple_validators.h" />
|
||||
<ClInclude Include="..\..\src\ripple_app\book\Amount.h" />
|
||||
<ClInclude Include="..\..\src\ripple_app\book\Amounts.h" />
|
||||
<ClInclude Include="..\..\src\ripple_app\book\BookTip.h" />
|
||||
<ClInclude Include="..\..\src\ripple_app\book\Offer.h" />
|
||||
<ClInclude Include="..\..\src\ripple_app\book\OfferStream.h" />
|
||||
<ClInclude Include="..\..\src\ripple_app\book\Quality.h" />
|
||||
<ClInclude Include="..\..\src\ripple_app\book\Taker.h" />
|
||||
<ClInclude Include="..\..\src\ripple_app\book\Types.h" />
|
||||
<ClInclude Include="..\..\src\ripple_app\consensus\DisputedTx.h" />
|
||||
<ClInclude Include="..\..\src\ripple_app\consensus\LedgerConsensus.h" />
|
||||
|
||||
@@ -1515,6 +1515,9 @@
|
||||
<ClCompile Include="..\..\src\ripple_app\book\tests\Quality.test.cpp">
|
||||
<Filter>[2] Old Ripple\ripple_app\book\tests</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple_app\book\tests\OfferStream.test.cpp">
|
||||
<Filter>[2] Old Ripple\ripple_app\book\tests</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\src\ripple_basics\containers\RangeSet.h">
|
||||
@@ -3090,6 +3093,18 @@
|
||||
<ClInclude Include="..\..\src\ripple_app\book\Types.h">
|
||||
<Filter>[2] Old Ripple\ripple_app\book</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple_app\book\OfferStream.h">
|
||||
<Filter>[2] Old Ripple\ripple_app\book</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple_app\book\Taker.h">
|
||||
<Filter>[2] Old Ripple\ripple_app\book</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple_app\book\BookTip.h">
|
||||
<Filter>[2] Old Ripple\ripple_app\book</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple_app\book\Offer.h">
|
||||
<Filter>[2] Old Ripple\ripple_app\book</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="..\..\src\ripple_data\protocol\ripple.proto">
|
||||
|
||||
@@ -75,11 +75,10 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/** Assignment.
|
||||
This is only valid when ByValue == `true`
|
||||
*/
|
||||
template <bool OtherByValue>
|
||||
RippleAssetType& operator= (RippleAssetType <OtherByValue> const& other)
|
||||
/** Assignment. */
|
||||
template <bool MaybeByValue = ByValue, bool OtherByValue>
|
||||
std::enable_if_t <MaybeByValue, RippleAssetType&>
|
||||
operator= (RippleAssetType <OtherByValue> const& other)
|
||||
{
|
||||
currency = other.currency;
|
||||
issuer = other.issuer;
|
||||
|
||||
144
src/ripple_app/book/BookTip.h
Normal file
144
src/ripple_app/book/BookTip.h
Normal file
@@ -0,0 +1,144 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_CORE_BOOKTIP_H_INCLUDED
|
||||
#define RIPPLE_CORE_BOOKTIP_H_INCLUDED
|
||||
|
||||
#include "Quality.h"
|
||||
#include "Types.h"
|
||||
|
||||
#include "../../beast/beast/utility/noexcept.h"
|
||||
|
||||
#include <ostream>
|
||||
#include <utility>
|
||||
|
||||
namespace ripple {
|
||||
namespace core {
|
||||
|
||||
/** Iterates and consumes raw offers in an order book.
|
||||
Offers are presented from highest quality to lowest quality. This will
|
||||
return all offers present including missing, invalid, unfunded, etc.
|
||||
*/
|
||||
class BookTip
|
||||
{
|
||||
private:
|
||||
std::reference_wrapper <LedgerView> m_view;
|
||||
bool m_valid;
|
||||
uint256 m_book;
|
||||
uint256 m_end;
|
||||
uint256 m_dir;
|
||||
uint256 m_index;
|
||||
SLE::pointer m_entry;
|
||||
|
||||
LedgerView&
|
||||
view() const noexcept
|
||||
{
|
||||
return m_view;
|
||||
}
|
||||
|
||||
public:
|
||||
/** Create the iterator. */
|
||||
BookTip (LedgerView& view, BookRef book)
|
||||
: m_view (view)
|
||||
, m_valid (false)
|
||||
, m_book (Ledger::getBookBase (
|
||||
book.in.currency, book.in.issuer,
|
||||
book.out.currency, book.out.issuer))
|
||||
, m_end (Ledger::getQualityNext (m_book))
|
||||
{
|
||||
}
|
||||
|
||||
uint256 const&
|
||||
dir() const noexcept
|
||||
{
|
||||
return m_dir;
|
||||
}
|
||||
|
||||
uint256 const&
|
||||
index() const noexcept
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
Quality const
|
||||
quality() const noexcept
|
||||
{
|
||||
return Quality (Ledger::getQuality (m_dir));
|
||||
}
|
||||
|
||||
SLE::pointer const&
|
||||
entry() const noexcept
|
||||
{
|
||||
return m_entry;
|
||||
}
|
||||
|
||||
/** Erases the current offer and advance to the next offer.
|
||||
Complexity: Constant
|
||||
@return `true` if there is a next offer
|
||||
*/
|
||||
bool
|
||||
step ()
|
||||
{
|
||||
if (m_valid)
|
||||
{
|
||||
if (m_entry)
|
||||
{
|
||||
view().offerDelete (m_index);
|
||||
m_entry = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
for(;;)
|
||||
{
|
||||
// See if there's an entry at or worse than current quality.
|
||||
auto const page (
|
||||
view().getNextLedgerIndex (m_book, m_end));
|
||||
|
||||
if (page.isZero())
|
||||
return false;
|
||||
|
||||
unsigned int di (0);
|
||||
SLE::pointer dir;
|
||||
if (view().dirFirst (page, dir, di, m_index))
|
||||
{
|
||||
m_dir = dir->getIndex();
|
||||
m_entry = view().entryCache (ltOFFER, m_index);
|
||||
m_valid = true;
|
||||
|
||||
// Next query should start before this directory
|
||||
m_book = page;
|
||||
|
||||
// The quality immediately before the next quality
|
||||
--m_book;
|
||||
|
||||
break;
|
||||
}
|
||||
// There should never be an empty directory but just in case,
|
||||
// we handle that case by advancing to the next directory.
|
||||
m_book = page;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
104
src/ripple_app/book/Offer.h
Normal file
104
src/ripple_app/book/Offer.h
Normal file
@@ -0,0 +1,104 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_CORE_OFFER_H_INCLUDED
|
||||
#define RIPPLE_CORE_OFFER_H_INCLUDED
|
||||
|
||||
#include "Amounts.h"
|
||||
#include "Quality.h"
|
||||
#include "Types.h"
|
||||
|
||||
#include "../misc/SerializedLedger.h"
|
||||
#include "../../ripple_data/protocol/FieldNames.h"
|
||||
|
||||
#include <ostream>
|
||||
|
||||
namespace ripple {
|
||||
namespace core {
|
||||
|
||||
class Offer
|
||||
{
|
||||
public:
|
||||
typedef Amount amount_type;
|
||||
|
||||
private:
|
||||
SLE::pointer m_entry;
|
||||
Quality m_quality;
|
||||
|
||||
public:
|
||||
Offer() = default;
|
||||
|
||||
Offer (SLE::pointer const& entry, Quality quality)
|
||||
: m_entry (entry)
|
||||
, m_quality (quality)
|
||||
{
|
||||
}
|
||||
|
||||
/** Returns the quality of the offer.
|
||||
Conceptually, the quality is the ratio of output to input currency.
|
||||
The implementation calculates it as the ratio of input to output
|
||||
currency (so it sorts ascending). The quality is computed at the time
|
||||
the offer is placed, and never changes for the lifetime of the offer.
|
||||
This is an important business rule that maintains accuracy when an
|
||||
offer is partially filled; Subsequent partial fills will use the
|
||||
original quality.
|
||||
*/
|
||||
Quality const
|
||||
quality() const noexcept
|
||||
{
|
||||
return m_quality;
|
||||
}
|
||||
|
||||
/** Returns the account id of the offer's owner. */
|
||||
Account const
|
||||
account() const
|
||||
{
|
||||
return m_entry->getFieldAccount160 (sfAccount);
|
||||
}
|
||||
|
||||
/** Returns the in and out amounts.
|
||||
Some or all of the out amount may be unfunded.
|
||||
*/
|
||||
Amounts const
|
||||
amount() const noexcept
|
||||
{
|
||||
return Amounts (m_entry->getFieldAmount (sfTakerPays),
|
||||
m_entry->getFieldAmount (sfTakerGets));
|
||||
}
|
||||
|
||||
/** Returns the ledger entry underlying the offer. */
|
||||
// AVOID USING THIS
|
||||
SLE::pointer
|
||||
entry() const noexcept
|
||||
{
|
||||
return m_entry;
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
std::ostream&
|
||||
operator<< (std::ostream& os, Offer const& offer)
|
||||
{
|
||||
return os << offer.entry()->getIndex();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
324
src/ripple_app/book/OfferStream.h
Normal file
324
src/ripple_app/book/OfferStream.h
Normal file
@@ -0,0 +1,324 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_CORE_OFFERSTREAM_H_INCLUDED
|
||||
#define RIPPLE_CORE_OFFERSTREAM_H_INCLUDED
|
||||
|
||||
#include "BookTip.h"
|
||||
#include "Offer.h"
|
||||
#include "Quality.h"
|
||||
#include "Types.h"
|
||||
|
||||
#include "../../beast/beast/utility/noexcept.h"
|
||||
|
||||
#include <ostream>
|
||||
#include <utility>
|
||||
|
||||
namespace ripple {
|
||||
namespace core {
|
||||
|
||||
/** Presents and consumes the offers in an order book.
|
||||
|
||||
Two `LedgerView` objects accumulate changes to the ledger. `view`
|
||||
is applied when the calling transaction succeeds. If the calling
|
||||
transaction fails, then `view_cancel` is applied.
|
||||
|
||||
Certain invalid offers are automatically removed:
|
||||
- Offers with missing ledger entries
|
||||
- Offers that expired
|
||||
- Offers found unfunded:
|
||||
An offer is found unfunded when the corresponding balance is zero
|
||||
and the caller has not modified the balance. This is accomplished
|
||||
by also looking up the balance in the cancel view.
|
||||
|
||||
When an offer is removed, it is removed from both views. This grooms the
|
||||
order book regardless of whether or not the transaction is successful.
|
||||
|
||||
TODO: Remove offers belonging to the taker
|
||||
*/
|
||||
class OfferStream
|
||||
{
|
||||
protected:
|
||||
beast::Journal m_journal;
|
||||
std::reference_wrapper <LedgerView> m_view;
|
||||
std::reference_wrapper <LedgerView> m_view_cancel;
|
||||
Book m_book;
|
||||
Clock::time_point m_when;
|
||||
BookTip m_tip;
|
||||
Offer m_offer;
|
||||
|
||||
// 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
|
||||
erase (LedgerView& view)
|
||||
{
|
||||
// VFALCO NOTE
|
||||
//
|
||||
// This should be using LedgerView::dirDelete, which will
|
||||
// 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()));
|
||||
|
||||
if (p == nullptr)
|
||||
{
|
||||
if (m_journal.error) m_journal.error <<
|
||||
"Missing directory " << m_tip.dir() <<
|
||||
" for offer " << m_tip.index();
|
||||
return;
|
||||
}
|
||||
|
||||
auto v (p->getFieldV256 (sfIndexes));
|
||||
auto& x (v.peekValue());
|
||||
auto it (std::find (x.begin(), x.end(), m_tip.index()));
|
||||
|
||||
if (it == x.end())
|
||||
{
|
||||
if (m_journal.error) m_journal.error <<
|
||||
"Missing offer " << m_tip.index() <<
|
||||
" for directory " << m_tip.dir();
|
||||
return;
|
||||
}
|
||||
|
||||
x.erase (it);
|
||||
p->setFieldV256 (sfIndexes, v);
|
||||
view.entryModify (p);
|
||||
|
||||
if (m_journal.trace) m_journal.trace <<
|
||||
"Missing offer " << m_tip.index() <<
|
||||
" removed from directory " << m_tip.dir();
|
||||
}
|
||||
|
||||
public:
|
||||
OfferStream (LedgerView& view, LedgerView& view_cancel, BookRef book,
|
||||
Clock::time_point when, beast::Journal journal)
|
||||
: m_journal (journal)
|
||||
, m_view (view)
|
||||
, m_view_cancel (view_cancel)
|
||||
, m_book (book)
|
||||
, m_when (when)
|
||||
, m_tip (view, book)
|
||||
{
|
||||
}
|
||||
|
||||
LedgerView&
|
||||
view() noexcept
|
||||
{
|
||||
return m_view;
|
||||
}
|
||||
|
||||
LedgerView&
|
||||
view_cancel() noexcept
|
||||
{
|
||||
return m_view_cancel;
|
||||
}
|
||||
|
||||
Book const&
|
||||
book() const noexcept
|
||||
{
|
||||
return m_book;
|
||||
}
|
||||
|
||||
uint256 const&
|
||||
dir() const noexcept
|
||||
{
|
||||
return m_tip.dir();
|
||||
}
|
||||
|
||||
/** Returns the offer at the tip of the order book.
|
||||
Offers are always presented in decreasing quality.
|
||||
Only valid if step() returned `true`.
|
||||
*/
|
||||
Offer const&
|
||||
tip() const
|
||||
{
|
||||
return m_offer;
|
||||
}
|
||||
|
||||
/** Advance to the next valid offer.
|
||||
This automatically removes:
|
||||
- Offers with missing ledger entries
|
||||
- Offers found unfunded
|
||||
- expired offers
|
||||
@return `true` if there is a valid offer.
|
||||
*/
|
||||
bool
|
||||
step()
|
||||
{
|
||||
// Modifying the order or logic of these
|
||||
// operations causes a protocol breaking change.
|
||||
|
||||
for(;;)
|
||||
{
|
||||
// BookTip::step deletes the current offer from the view before
|
||||
// advancing to the next (unless the ledger entry is missing).
|
||||
if (! m_tip.step())
|
||||
return false;
|
||||
|
||||
SLE::pointer const& entry (m_tip.entry());
|
||||
|
||||
// Remove if missing
|
||||
if (! entry)
|
||||
{
|
||||
erase (view());
|
||||
erase (view_cancel());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove if expired
|
||||
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();
|
||||
continue;
|
||||
}
|
||||
|
||||
m_offer = Offer (entry, m_tip.quality());
|
||||
|
||||
Amounts const amount (m_offer.amount());
|
||||
|
||||
// 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();
|
||||
m_offer = Offer{};
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate owner funds
|
||||
// VFALCO NOTE The calling code also checks the funds,
|
||||
// how expensive is looking up the funds twice?
|
||||
Amount const owner_funds (view().accountFunds (
|
||||
m_offer.account(), m_offer.amount().out));
|
||||
|
||||
// Check for unfunded offer
|
||||
if (! owner_funds.isPositive())
|
||||
{
|
||||
// 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"
|
||||
if (view_cancel().accountFunds (m_offer.account(),
|
||||
m_offer.amount().out) == owner_funds)
|
||||
{
|
||||
view_cancel().offerDelete (entry->getIndex());
|
||||
if (m_journal.trace) m_journal.trace <<
|
||||
"Removing unfunded offer " << entry->getIndex();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_journal.trace) m_journal.trace <<
|
||||
"Removing became unfunded offer " << entry->getIndex();
|
||||
}
|
||||
m_offer = Offer{};
|
||||
continue;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Remove if its our own offer
|
||||
//
|
||||
// VFALCO NOTE We might not want this for payments
|
||||
//
|
||||
if (m_account == owner)
|
||||
{
|
||||
view_cancel().offerDelete (entry->getIndex());
|
||||
if (m_journal.trace) m_journal.trace <<
|
||||
"Removing self offer " << entry->getIndex();
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Updates the offer to reflect remaining funds.
|
||||
The caller is responsible for following all the rounding rules.
|
||||
The offer will be considered fully consumed if either the in
|
||||
or the out amount is zero.
|
||||
@return `true` If the offer had no funds remaining.
|
||||
*/
|
||||
bool
|
||||
fill (Amounts const& remaining_funds)
|
||||
{
|
||||
// Erase the offer if it is fully consumed (in==0 || out==0)
|
||||
// This is the same as becoming unfunded
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Does everything an OfferStream does, and:
|
||||
- remove offers that became unfunded (if path is used)
|
||||
*/
|
||||
#if 0
|
||||
class PaymentOfferStream : public OfferStream
|
||||
{
|
||||
public:
|
||||
PaymentOfferStream (LedgerView& view_base, BookRef book,
|
||||
Clock::time_point when, beast::Journal journal)
|
||||
: m_journal (journal)
|
||||
, m_view (view_base.duplicate())
|
||||
, m_view_apply (view_base.duplicate())
|
||||
, m_book (book)
|
||||
, m_when (when)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
TakerOfferStream
|
||||
Does everything a PaymentOfferStream does, and:
|
||||
- remove offers owned by the taker (if tx succeeds?)
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
OfferStream
|
||||
- remove offers with missing ledger entries (always)
|
||||
- remove expired offers (always)
|
||||
- remove offers found unfunded (always)
|
||||
|
||||
PaymentOfferStream
|
||||
Does everything an OfferStream does, and:
|
||||
- remove offers that became unfunded (if path is used)
|
||||
|
||||
TakerOfferStream
|
||||
Does everything a PaymentOfferStream does, and:
|
||||
- remove offers owned by the taker (if tx succeeds?)
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
351
src/ripple_app/book/Taker.h
Normal file
351
src/ripple_app/book/Taker.h
Normal file
@@ -0,0 +1,351 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_CORE_TAKER_H_INCLUDED
|
||||
#define RIPPLE_CORE_TAKER_H_INCLUDED
|
||||
|
||||
#include "Amounts.h"
|
||||
#include "Quality.h"
|
||||
#include "Types.h"
|
||||
|
||||
#include "../../beast/beast/streams/debug_ostream.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace ripple {
|
||||
namespace core {
|
||||
|
||||
/** State for the active party during order book or payment operations. */
|
||||
class Taker
|
||||
{
|
||||
public:
|
||||
struct Options
|
||||
{
|
||||
Options() = default;
|
||||
|
||||
explicit
|
||||
Options (std::uint32_t tx_flags)
|
||||
: sell (isSetBit (tx_flags, tfSell))
|
||||
, passive (isSetBit (tx_flags, tfPassive))
|
||||
{
|
||||
}
|
||||
|
||||
bool sell;
|
||||
bool passive;
|
||||
};
|
||||
|
||||
private:
|
||||
std::reference_wrapper <LedgerView> m_view;
|
||||
Book m_book;
|
||||
Account m_account;
|
||||
Options m_options;
|
||||
Quality m_quality;
|
||||
Quality m_threshold;
|
||||
|
||||
// The original in and out quantities
|
||||
Amounts m_amount;
|
||||
|
||||
// Amount of input currency remaining.
|
||||
Amount m_in;
|
||||
|
||||
// Amount of output currency we have received.
|
||||
Amount m_out;
|
||||
|
||||
// Returns the balance of the taker's input currency,
|
||||
Amount
|
||||
funds() const
|
||||
{
|
||||
return view().accountFunds (account(), m_in);
|
||||
}
|
||||
|
||||
public:
|
||||
Taker (LedgerView& view, BookRef const& book,
|
||||
Account const& account, Amounts const& amount,
|
||||
Options const& options)
|
||||
: m_view (view)
|
||||
, m_book (book)
|
||||
, m_account (account)
|
||||
, m_options (options)
|
||||
, m_quality (amount)
|
||||
, m_threshold (m_quality)
|
||||
, m_amount (amount)
|
||||
, m_in (amount.in)
|
||||
, m_out (amount.out.getCurrency(), amount.out.getIssuer())
|
||||
{
|
||||
// If this is a passive order (tfPassive), this prevents
|
||||
// offers at the same quality level from being consumed.
|
||||
if (m_options.passive)
|
||||
--m_threshold;
|
||||
}
|
||||
|
||||
LedgerView&
|
||||
view() const noexcept
|
||||
{
|
||||
return m_view;
|
||||
}
|
||||
|
||||
/** Returns the input and output asset pair identifier. */
|
||||
Book const&
|
||||
book() const noexcept
|
||||
{
|
||||
return m_book;
|
||||
}
|
||||
|
||||
/** Returns the account identifier of the taker. */
|
||||
Account const&
|
||||
account() const noexcept
|
||||
{
|
||||
return m_account;
|
||||
}
|
||||
|
||||
/** Returns `true` if order crossing should not continue.
|
||||
Order processing is stopped if the taker's order quantities have
|
||||
been reached, or if the taker has run out of input funds.
|
||||
*/
|
||||
bool
|
||||
done() const noexcept
|
||||
{
|
||||
if (m_options.sell)
|
||||
{
|
||||
// With the sell option, we are finished when
|
||||
// we have consumed all the input currency.
|
||||
if (! m_in.isPositive())
|
||||
return true;
|
||||
}
|
||||
else if (m_out >= m_amount.out)
|
||||
{
|
||||
// With the buy option (!sell) we are finished when we
|
||||
// have received the desired amount of output currency.
|
||||
return true;
|
||||
}
|
||||
|
||||
// We are finished if the taker is out of funds
|
||||
return ! funds().isPositive();
|
||||
}
|
||||
|
||||
Quality
|
||||
threshold() const noexcept
|
||||
{
|
||||
return m_threshold;
|
||||
}
|
||||
|
||||
/** Returns `true` if the quality does not meet the taker's requirements. */
|
||||
bool
|
||||
reject (Quality const& quality) const noexcept
|
||||
{
|
||||
return quality < m_threshold;
|
||||
}
|
||||
|
||||
/** Calcualtes the result of applying the taker's funds to the offer.
|
||||
@return The flow and flag indicating if the order was consumed.
|
||||
*/
|
||||
std::pair <Amounts, bool>
|
||||
fill (Offer const& offer) const
|
||||
{
|
||||
// Best flow the owner can get.
|
||||
// Start out assuming entire offer will flow.
|
||||
Amounts owner_amount (offer.amount());
|
||||
|
||||
// Limit owner's output by available funds less fees
|
||||
|
||||
// VFALCO TODO Rename accountFounds to make it clear that
|
||||
// it can return a clamped value.
|
||||
Amount const owner_funds (view().accountFunds (
|
||||
offer.account(), owner_amount.out));
|
||||
|
||||
// Get fee rate paid by owner
|
||||
std::uint32_t const owner_charge_rate (view().rippleTransferRate (
|
||||
offer.account(), account(), offer.entry()->getFieldAmount (
|
||||
sfTakerGets).getIssuer()));
|
||||
Amount const owner_charge (Amount::saFromRate (owner_charge_rate));
|
||||
|
||||
// VFALCO Make Amount::divide skip math if v2 == QUALITY_ONE
|
||||
if (owner_charge_rate == QUALITY_ONE)
|
||||
{
|
||||
// Skip some math when there's no fee
|
||||
owner_amount = offer.quality().ceil_out (
|
||||
owner_amount, owner_funds);
|
||||
}
|
||||
else
|
||||
{
|
||||
owner_amount = offer.quality().ceil_out (owner_amount,
|
||||
Amount::divide (owner_funds, owner_charge));
|
||||
}
|
||||
|
||||
// Best flow the taker can get.
|
||||
// Start out assuming entire offer will flow.
|
||||
Amounts taker_amount (offer.amount());
|
||||
|
||||
// Limit taker's input by available funds less fees
|
||||
|
||||
Amount const taker_funds (view().accountFunds (account(), m_in));
|
||||
|
||||
// Get fee rate paid by taker
|
||||
std::uint32_t const taker_charge_rate (view().rippleTransferRate (
|
||||
account(), offer.account(), offer.entry()->getFieldAmount (
|
||||
sfTakerPays).getIssuer()));
|
||||
Amount const taker_charge (Amount::saFromRate (taker_charge_rate));
|
||||
|
||||
// VFALCO Make Amount::divide skip math if v2 == QUALITY_ONE
|
||||
if (taker_charge_rate == QUALITY_ONE)
|
||||
{
|
||||
// Skip some math when there's no fee
|
||||
taker_amount = offer.quality().ceil_in (
|
||||
taker_amount, taker_funds);
|
||||
}
|
||||
else
|
||||
{
|
||||
taker_amount = offer.quality().ceil_in (taker_amount,
|
||||
Amount::divide (taker_funds, taker_charge));
|
||||
}
|
||||
|
||||
// Limit taker's input by options
|
||||
if (! m_options.sell)
|
||||
{
|
||||
assert (m_out < m_amount.out);
|
||||
taker_amount = offer.quality().ceil_out (
|
||||
taker_amount, m_amount.out - m_out);
|
||||
assert (! taker_amount.in.isZero());
|
||||
}
|
||||
|
||||
// Calculate the amount that will flow through the offer
|
||||
// This does not include the fees.
|
||||
Amounts const flow ((owner_amount.in < taker_amount.in) ?
|
||||
owner_amount : taker_amount);
|
||||
|
||||
bool const consumed (flow.out >= owner_amount.out);
|
||||
|
||||
return std::make_pair (flow, consumed);
|
||||
}
|
||||
|
||||
/** Process the result of fee and funds calculation on the offer.
|
||||
|
||||
To protect the ledger, conditions which should never occur are
|
||||
checked. If the invariants are broken, the processing fails.
|
||||
|
||||
If processing succeeds, the funds are distributed to the taker,
|
||||
owner, and issuers.
|
||||
|
||||
@return `false` if processing failed (due to math errors).
|
||||
*/
|
||||
TER
|
||||
process (Amounts const& flow, Offer const& offer)
|
||||
{
|
||||
TER result (tesSUCCESS);
|
||||
|
||||
// VFALCO For the case of !sell, is it possible for the taker
|
||||
// to get a tiny bit more than he asked for?
|
||||
assert (m_options.sell || flow.out <= m_amount.out);
|
||||
|
||||
// Calculate remaining portion of offer
|
||||
Amounts const remain (
|
||||
offer.entry()->getFieldAmount (sfTakerPays) - flow.in,
|
||||
offer.entry()->getFieldAmount (sfTakerGets) - flow.out);
|
||||
|
||||
offer.entry()->setFieldAmount (sfTakerPays, remain.in);
|
||||
offer.entry()->setFieldAmount (sfTakerGets, remain.out);
|
||||
view().entryModify (offer.entry());
|
||||
|
||||
// Pay the taker
|
||||
result = view().accountSend (offer.account(), account(), flow.out);
|
||||
|
||||
if (result == tesSUCCESS)
|
||||
{
|
||||
m_out += flow.out;
|
||||
|
||||
// Pay the owner
|
||||
result = view().accountSend (account(), offer.account(), flow.in);
|
||||
|
||||
if (result == tesSUCCESS)
|
||||
{
|
||||
m_in -= flow.in;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline
|
||||
std::ostream&
|
||||
operator<< (std::ostream& os, Taker const& taker)
|
||||
{
|
||||
return os << taker.account();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
// This code calculates the fees but then we discovered
|
||||
// that LedgerEntrySet::accountSend does it for you.
|
||||
|
||||
Amounts fees;
|
||||
|
||||
// Calculate taker fee
|
||||
if (taker_charge_rate == QUALITY_ONE)
|
||||
{
|
||||
// No fee, skip math
|
||||
fees.in = Amount (flow.in.getCurrency(),
|
||||
flow.in.getIssuer());
|
||||
}
|
||||
else
|
||||
{
|
||||
// VFALCO TODO Check the units (versus 4-arg version of mulRound)
|
||||
Amount const in_plus_fees (Amount::mulRound (
|
||||
flow.in, taker_charge, true));
|
||||
// Make sure the taker has enough to pay the fee
|
||||
if (in_plus_fees > taker_funds)
|
||||
{
|
||||
// Not enough funds, stiff the issuer
|
||||
fees.in = taker_funds - flow.in;
|
||||
}
|
||||
else
|
||||
{
|
||||
fees.in = in_plus_fees - flow.in;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate owner fee
|
||||
if (owner_charge_rate == QUALITY_ONE)
|
||||
{
|
||||
// No fee, skip math
|
||||
fees.out = Amount (flow.out.getCurrency(),
|
||||
flow.out.getIssuer());
|
||||
}
|
||||
else
|
||||
{
|
||||
Amount const out_plus_fees (Amount::mulRound (
|
||||
flow.out, owner_charge, true));
|
||||
// Make sure the owner has enough to pay the fee
|
||||
if (out_plus_fees > owner_funds)
|
||||
{
|
||||
// Not enough funds, stiff the issuer
|
||||
fees.out = owner_funds - flow.out;
|
||||
}
|
||||
else
|
||||
{
|
||||
fees.out = out_plus_fees - flow.out;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#endif
|
||||
46
src/ripple_app/book/tests/OfferStream.test.cpp
Normal file
46
src/ripple_app/book/tests/OfferStream.test.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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 "../OfferStream.h"
|
||||
|
||||
#include "../../../beast/beast/unit_test/suite.h"
|
||||
|
||||
namespace ripple {
|
||||
namespace core {
|
||||
|
||||
class OfferStream_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
test()
|
||||
{
|
||||
pass();
|
||||
}
|
||||
|
||||
void
|
||||
run()
|
||||
{
|
||||
test();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE_MANUAL(OfferStream,core,ripple);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -27,12 +27,15 @@ class DirectoryEntryIterator
|
||||
{
|
||||
|
||||
public:
|
||||
DirectoryEntryIterator ()
|
||||
: mEntry(0)
|
||||
{
|
||||
}
|
||||
|
||||
DirectoryEntryIterator () : mEntry(0)
|
||||
{ ; }
|
||||
|
||||
DirectoryEntryIterator (uint256 const& index) : mRootIndex(index), mEntry(0)
|
||||
{ ; }
|
||||
DirectoryEntryIterator (uint256 const& index)
|
||||
: mRootIndex(index), mEntry(0)
|
||||
{
|
||||
}
|
||||
|
||||
/** Construct from a reference to the root directory
|
||||
*/
|
||||
@@ -42,24 +45,19 @@ public:
|
||||
mRootIndex = mDirNode->getIndex();
|
||||
}
|
||||
|
||||
/** Get the SLE this iterator currently references
|
||||
*/
|
||||
/** Get the SLE this iterator currently references */
|
||||
SLE::pointer getEntry (LedgerEntrySet& les, LedgerEntryType type);
|
||||
|
||||
/** Make this iterator point to the first offer
|
||||
*/
|
||||
/** Make this iterator point to the first offer */
|
||||
bool firstEntry (LedgerEntrySet&);
|
||||
|
||||
/** Make this iterator point to the next offer
|
||||
*/
|
||||
/** Make this iterator point to the next offer */
|
||||
bool nextEntry (LedgerEntrySet&);
|
||||
|
||||
/** Add this iterator's position to a JSON object
|
||||
*/
|
||||
/** Add this iterator's position to a JSON object */
|
||||
bool addJson (Json::Value&) const;
|
||||
|
||||
/** Set this iterator's position from a JSON object
|
||||
*/
|
||||
/** Set this iterator's position from a JSON object */
|
||||
bool setJson (Json::Value const&, LedgerEntrySet& les);
|
||||
|
||||
uint256 const& getEntryLedgerIndex () const
|
||||
@@ -72,8 +70,19 @@ public:
|
||||
return mDirNode ? mDirNode->getIndex () : uint256();
|
||||
}
|
||||
|
||||
private:
|
||||
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)
|
||||
|
||||
@@ -29,7 +29,8 @@ class BookDirIterator
|
||||
public:
|
||||
|
||||
BookDirIterator ()
|
||||
{ ; }
|
||||
{
|
||||
}
|
||||
|
||||
BookDirIterator (
|
||||
uint160 const& uInCurrency, uint160 const& uInIssuer,
|
||||
@@ -93,26 +94,40 @@ public:
|
||||
bool setJson (Json::Value const&);
|
||||
|
||||
// Does this iterator currently point to a valid directory
|
||||
explicit
|
||||
operator bool () const
|
||||
{
|
||||
return mOfferDir && (mOfferDir->getIndex() == mIndex);
|
||||
}
|
||||
|
||||
private:
|
||||
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,
|
||||
uint160 const& uInCurrency,
|
||||
@@ -121,7 +136,11 @@ public:
|
||||
uint160 const& uOutIssuer) :
|
||||
mEntrySet (set),
|
||||
mDirectoryIterator (uInCurrency, uInIssuer, uOutCurrency, uOutIssuer)
|
||||
{ ; }
|
||||
{
|
||||
}
|
||||
|
||||
OrderBookIterator&
|
||||
operator= (OrderBookIterator const&) = default;
|
||||
|
||||
bool addJson (Json::Value&) const;
|
||||
|
||||
@@ -193,8 +212,23 @@ public:
|
||||
return mOfferIterator;
|
||||
}
|
||||
|
||||
bool
|
||||
operator== (OrderBookIterator const& other) const
|
||||
{
|
||||
return
|
||||
std::addressof(mEntrySet) == std::addressof(other.mEntrySet) &&
|
||||
mDirectoryIterator == other.mDirectoryIterator &&
|
||||
mOfferIterator == mOfferIterator;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!= (OrderBookIterator const& other) const
|
||||
{
|
||||
return ! (*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
LedgerEntrySet& mEntrySet;
|
||||
std::reference_wrapper <LedgerEntrySet> mEntrySet;
|
||||
BookDirIterator mDirectoryIterator;
|
||||
DirectoryEntryIterator mOfferIterator;
|
||||
};
|
||||
|
||||
@@ -38,4 +38,5 @@
|
||||
#include "tx/TransactionEngine.cpp"
|
||||
#include "tx/TransactionMeta.cpp"
|
||||
|
||||
#include "book/tests/OfferStream.test.cpp"
|
||||
#include "book/tests/Quality.test.cpp"
|
||||
|
||||
@@ -55,7 +55,7 @@ TER AccountSetTransactor::doApply ()
|
||||
|
||||
if (bSetRequireAuth && !isSetBit (uFlagsIn, lsfRequireAuth))
|
||||
{
|
||||
if (!mEngine->getNodes ().dirIsEmpty (Ledger::getOwnerDirIndex (mTxnAccountID)))
|
||||
if (!mEngine->view().dirIsEmpty (Ledger::getOwnerDirIndex (mTxnAccountID)))
|
||||
{
|
||||
m_journal.trace << "Retry: Owner directory not empty.";
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ TER OfferCancelTransactor::doApply ()
|
||||
m_journal.debug <<
|
||||
"OfferCancel: uOfferSequence=" << uOfferSequence;
|
||||
|
||||
return mEngine->getNodes ().offerDelete (sleOffer);
|
||||
return mEngine->view ().offerDelete (sleOffer);
|
||||
}
|
||||
|
||||
m_journal.warning <<
|
||||
|
||||
@@ -68,7 +68,7 @@ bool OfferCreateTransactor::isValidOffer (
|
||||
m_journal.trace <<
|
||||
"isValidOffer: saOfferPays=" << saOfferPays.getFullText ();
|
||||
|
||||
saOfferFunds = mEngine->getNodes ().accountFunds (uOfferOwnerID, saOfferPays);
|
||||
saOfferFunds = mEngine->view ().accountFunds (uOfferOwnerID, saOfferPays);
|
||||
|
||||
if (!saOfferFunds.isPositive ())
|
||||
{
|
||||
@@ -353,7 +353,7 @@ TER OfferCreateTransactor::takeOffers (
|
||||
"takeOffers: bSell: " << bSell <<
|
||||
": against book: " << uBookBase.ToString ();
|
||||
|
||||
LedgerEntrySet& lesActive = mEngine->getNodes ();
|
||||
LedgerEntrySet& lesActive = mEngine->view ();
|
||||
std::uint64_t const uTakeQuality = STAmount::getRate (saTakerGets, saTakerPays);
|
||||
STAmount saTakerRate = STAmount::setRate (uTakeQuality);
|
||||
uint160 const uTakerPaysAccountID = saTakerPays.getIssuer ();
|
||||
@@ -776,7 +776,7 @@ TER OfferCreateTransactor::doApply ()
|
||||
std::uint64_t uOwnerNode;
|
||||
std::uint64_t uBookNode;
|
||||
|
||||
LedgerEntrySet& lesActive = mEngine->getNodes ();
|
||||
LedgerEntrySet& lesActive = mEngine->view ();
|
||||
LedgerEntrySet lesCheckpoint = lesActive; // Checkpoint with just fees paid.
|
||||
lesActive.bumpSeq (); // Begin ledger variance.
|
||||
|
||||
@@ -869,7 +869,7 @@ TER OfferCreateTransactor::doApply ()
|
||||
m_journal.warning <<
|
||||
"uCancelSequence=" << uCancelSequence;
|
||||
|
||||
terResult = mEngine->getNodes ().offerDelete (sleCancel);
|
||||
terResult = mEngine->view ().offerDelete (sleCancel);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -217,7 +217,7 @@ TER PaymentTransactor::doApply ()
|
||||
terResult = openLedger && tooManyPaths
|
||||
? telBAD_PATH_COUNT // Too many paths for proposed ledger.
|
||||
: RippleCalc::rippleCalc (
|
||||
mEngine->getNodes (),
|
||||
mEngine->view (),
|
||||
saMaxAmountAct,
|
||||
saDstAmountAct,
|
||||
vpsExpanded,
|
||||
@@ -236,7 +236,7 @@ TER PaymentTransactor::doApply ()
|
||||
terResult = tecPATH_DRY;
|
||||
|
||||
if ((tesSUCCESS == terResult) && (saDstAmountAct != saDstAmount))
|
||||
mEngine->getNodes().setDeliveredAmount (saDstAmountAct);
|
||||
mEngine->view().setDeliveredAmount (saDstAmountAct);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
|
||||
@@ -101,7 +101,7 @@ TER TrustSetTransactor::doApply ()
|
||||
m_journal.warning <<
|
||||
"Clearing redundant line.";
|
||||
|
||||
return mEngine->getNodes ().trustDelete (
|
||||
return mEngine->view ().trustDelete (
|
||||
selDelete, mTxnAccountID, uDstAccountID);
|
||||
}
|
||||
else
|
||||
@@ -273,7 +273,7 @@ TER TrustSetTransactor::doApply ()
|
||||
{
|
||||
// Set reserve for low account.
|
||||
|
||||
mEngine->getNodes ().ownerCountAdjust (uLowAccountID, 1, sleLowAccount);
|
||||
mEngine->view ().ownerCountAdjust (uLowAccountID, 1, sleLowAccount);
|
||||
uFlagsOut |= lsfLowReserve;
|
||||
|
||||
if (!bHigh)
|
||||
@@ -284,7 +284,7 @@ TER TrustSetTransactor::doApply ()
|
||||
{
|
||||
// Clear reserve for low account.
|
||||
|
||||
mEngine->getNodes ().ownerCountAdjust (uLowAccountID, -1, sleLowAccount);
|
||||
mEngine->view ().ownerCountAdjust (uLowAccountID, -1, sleLowAccount);
|
||||
uFlagsOut &= ~lsfLowReserve;
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ TER TrustSetTransactor::doApply ()
|
||||
{
|
||||
// Set reserve for high account.
|
||||
|
||||
mEngine->getNodes ().ownerCountAdjust (uHighAccountID, 1, sleHighAccount);
|
||||
mEngine->view ().ownerCountAdjust (uHighAccountID, 1, sleHighAccount);
|
||||
uFlagsOut |= lsfHighReserve;
|
||||
|
||||
if (bHigh)
|
||||
@@ -303,7 +303,7 @@ TER TrustSetTransactor::doApply ()
|
||||
{
|
||||
// Clear reserve for high account.
|
||||
|
||||
mEngine->getNodes ().ownerCountAdjust (uHighAccountID, -1, sleHighAccount);
|
||||
mEngine->view ().ownerCountAdjust (uHighAccountID, -1, sleHighAccount);
|
||||
uFlagsOut &= ~lsfHighReserve;
|
||||
}
|
||||
|
||||
@@ -314,7 +314,7 @@ TER TrustSetTransactor::doApply ()
|
||||
{
|
||||
// Delete.
|
||||
|
||||
terResult = mEngine->getNodes ().trustDelete (sleRippleState, uLowAccountID, uHighAccountID);
|
||||
terResult = mEngine->view ().trustDelete (sleRippleState, uLowAccountID, uHighAccountID);
|
||||
}
|
||||
else if (bReserveIncrease
|
||||
&& mPriorBalance.getNValue () < uReserveCreate) // Reserve is not scaled by load.
|
||||
@@ -366,7 +366,7 @@ TER TrustSetTransactor::doApply ()
|
||||
Ledger::getRippleStateIndex (mTxnAccountID, uDstAccountID, uCurrencyID).ToString ();
|
||||
|
||||
// Create a new ripple line.
|
||||
terResult = mEngine->getNodes ().trustCreate (
|
||||
terResult = mEngine->view ().trustCreate (
|
||||
bHigh,
|
||||
mTxnAccountID,
|
||||
uDstAccountID,
|
||||
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
assert (mLedger);
|
||||
}
|
||||
|
||||
LedgerEntrySet& getNodes ()
|
||||
LedgerEntrySet& view ()
|
||||
{
|
||||
return mNodes;
|
||||
}
|
||||
@@ -84,18 +84,22 @@ public:
|
||||
mLedger = ledger;
|
||||
}
|
||||
|
||||
// VFALCO TODO Remove these pointless wrappers
|
||||
SLE::pointer entryCreate (LedgerEntryType type, uint256 const & index)
|
||||
{
|
||||
return mNodes.entryCreate (type, index);
|
||||
}
|
||||
|
||||
SLE::pointer entryCache (LedgerEntryType type, uint256 const & index)
|
||||
{
|
||||
return mNodes.entryCache (type, index);
|
||||
}
|
||||
|
||||
void entryDelete (SLE::ref sleEntry)
|
||||
{
|
||||
mNodes.entryDelete (sleEntry);
|
||||
}
|
||||
|
||||
void entryModify (SLE::ref sleEntry)
|
||||
{
|
||||
mNodes.entryModify (sleEntry);
|
||||
|
||||
@@ -635,8 +635,10 @@ public:
|
||||
{
|
||||
if (!isZero ()) mIsNegative = !mIsNegative;
|
||||
}
|
||||
|
||||
void zero ()
|
||||
{
|
||||
// VFALCO: Why -100?
|
||||
mOffset = mIsNative ? 0 : -100;
|
||||
mValue = 0;
|
||||
mIsNegative = false;
|
||||
@@ -733,6 +735,11 @@ public:
|
||||
return multiply (v1, v2, v1);
|
||||
}
|
||||
|
||||
/* addRound, subRound can end up rounding if the amount subtracted is too small
|
||||
to make a change. Consder (X-d) where d is very small relative to X.
|
||||
If you ask to round down, then (X-d) should not be X unless d is zero.
|
||||
If you ask to round up, (X+d) should never be X unless d is zero. (Assuming X and d are positive).
|
||||
*/
|
||||
// Add, subtract, multiply, or divide rounding result in specified direction
|
||||
static STAmount addRound (const STAmount& v1, const STAmount& v2, bool roundUp);
|
||||
static STAmount subRound (const STAmount& v1, const STAmount& v2, bool roundUp);
|
||||
|
||||
Reference in New Issue
Block a user