mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Support switchover from 0.27 to 0.28 processing semantics based on time:
Changes made to support autobridging and improve the offer-crossing and
pathfinding logic result in transaction-breaking changes which cause
incompatibilities between 0.27 and 0.28 builds of RippleD.
This patch simplifies deployment of 0.28 on the Ripple network by allowing
RippleD to emulate the 0.27 semantics while the last closed ledger closed
before March 30, 2015 at 13:00:00 PDT, after which time the new 0.28
semantics will become active.
The transaction-breaking changes addressed in this commit are:
3ccbd7c9b2
b203db27a4
This commit is contained in:
committed by
Tom Ritchford
parent
eaa1f47f00
commit
8f88d915ba
@@ -2317,6 +2317,44 @@
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\json\Writer.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\book\Amount.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\book\Amounts.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\book\BookTip.h">
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\legacy\0.27\book\impl\BookTip.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\legacy\0.27\book\impl\OfferStream.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\legacy\0.27\book\impl\Quality.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\legacy\0.27\book\impl\Taker.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\book\Offer.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\book\OfferStream.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\book\Quality.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\book\Taker.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\book\Types.h">
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\legacy\0.27\CreateOffer.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\CreateOffer.h">
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\legacy\0.27\Emulate027.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\Emulate027.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\net\HTTPClient.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\net\HTTPRequest.h">
|
||||
@@ -3410,6 +3448,10 @@
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\unity\legacy.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\unity\lz4.c">
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\unity\net.cpp">
|
||||
|
||||
@@ -355,6 +355,18 @@
|
||||
<Filter Include="ripple\json\tests">
|
||||
<UniqueIdentifier>{BA646284-836B-B151-F2AA-D18535D6F3C1}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="ripple\legacy">
|
||||
<UniqueIdentifier>{312AABAF-FDD8-31C7-C7DB-022D83CEA84F}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="ripple\legacy\0.27">
|
||||
<UniqueIdentifier>{F042ABF9-9B55-1118-07DA-1EECFBF1237B}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="ripple\legacy\0.27\book">
|
||||
<UniqueIdentifier>{73645AAD-9270-EE60-82A6-DFD2DBF65D6C}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="ripple\legacy\0.27\book\impl">
|
||||
<UniqueIdentifier>{381C3650-0CBD-536A-DE2D-301216CB03A6}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="ripple\net">
|
||||
<UniqueIdentifier>{6649BD29-BE86-723F-501A-045E39310112}</UniqueIdentifier>
|
||||
</Filter>
|
||||
@@ -3072,6 +3084,54 @@
|
||||
<ClInclude Include="..\..\src\ripple\json\Writer.h">
|
||||
<Filter>ripple\json</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\book\Amount.h">
|
||||
<Filter>ripple\legacy\0.27\book</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\book\Amounts.h">
|
||||
<Filter>ripple\legacy\0.27\book</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\book\BookTip.h">
|
||||
<Filter>ripple\legacy\0.27\book</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\legacy\0.27\book\impl\BookTip.cpp">
|
||||
<Filter>ripple\legacy\0.27\book\impl</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\legacy\0.27\book\impl\OfferStream.cpp">
|
||||
<Filter>ripple\legacy\0.27\book\impl</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\legacy\0.27\book\impl\Quality.cpp">
|
||||
<Filter>ripple\legacy\0.27\book\impl</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\legacy\0.27\book\impl\Taker.cpp">
|
||||
<Filter>ripple\legacy\0.27\book\impl</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\book\Offer.h">
|
||||
<Filter>ripple\legacy\0.27\book</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\book\OfferStream.h">
|
||||
<Filter>ripple\legacy\0.27\book</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\book\Quality.h">
|
||||
<Filter>ripple\legacy\0.27\book</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\book\Taker.h">
|
||||
<Filter>ripple\legacy\0.27\book</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\book\Types.h">
|
||||
<Filter>ripple\legacy\0.27\book</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\legacy\0.27\CreateOffer.cpp">
|
||||
<Filter>ripple\legacy\0.27</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\CreateOffer.h">
|
||||
<Filter>ripple\legacy\0.27</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\legacy\0.27\Emulate027.cpp">
|
||||
<Filter>ripple\legacy\0.27</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ripple\legacy\0.27\Emulate027.h">
|
||||
<Filter>ripple\legacy\0.27</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\net\HTTPClient.h">
|
||||
<Filter>ripple\net</Filter>
|
||||
</ClInclude>
|
||||
@@ -4185,6 +4245,9 @@
|
||||
<ClCompile Include="..\..\src\ripple\unity\json.cpp">
|
||||
<Filter>ripple\unity</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\unity\legacy.cpp">
|
||||
<Filter>ripple\unity</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\unity\lz4.c">
|
||||
<Filter>ripple\unity</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@@ -603,6 +603,8 @@ for tu_style in ['classic', 'unity']:
|
||||
*list_sources('src/ripple/crypto', '.cpp'))
|
||||
object_builder.add_source_files(
|
||||
*list_sources('src/ripple/json', '.cpp'))
|
||||
object_builder.add_source_files(
|
||||
*list_sources('src/ripple/legacy', '.cpp'))
|
||||
object_builder.add_source_files(
|
||||
*list_sources('src/ripple/net', '.cpp'))
|
||||
object_builder.add_source_files(
|
||||
@@ -641,6 +643,7 @@ for tu_style in ['classic', 'unity']:
|
||||
'src/ripple/unity/json.cpp',
|
||||
'src/ripple/unity/protocol.cpp',
|
||||
'src/ripple/unity/shamap.cpp',
|
||||
'src/ripple/unity/legacy.cpp',
|
||||
)
|
||||
|
||||
object_builder.add_source_files(
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <ripple/app/book/Quality.h>
|
||||
#include <ripple/app/paths/cursor/RippleLiquidity.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/legacy/0.27/Emulate027.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace path {
|
||||
@@ -473,7 +474,14 @@ TER PathCursor::forwardLiquidityForAccount () const
|
||||
|
||||
node().saFwdDeliver.clear (node().saRevDeliver);
|
||||
|
||||
if (previousNode().saFwdDeliver && node().saRevDeliver)
|
||||
bool do_liquidity;
|
||||
|
||||
if (ripple::legacy::emulate027 (rippleCalc_.mActiveLedger.getLedger ()))
|
||||
do_liquidity = previousNode().saFwdDeliver && node().saRevIssue;
|
||||
else
|
||||
do_liquidity = previousNode().saFwdDeliver && node().saRevDeliver;
|
||||
|
||||
if (do_liquidity)
|
||||
{
|
||||
// Rate : 1.0 : transfer_rate
|
||||
rippleLiquidity (
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/json/to_string.h>
|
||||
|
||||
#include <ripple/legacy/0.27/CreateOffer.h>
|
||||
|
||||
#include <beast/cxx14/memory.h>
|
||||
#include <stdexcept>
|
||||
|
||||
@@ -827,6 +829,14 @@ transact_CreateOffer (
|
||||
TransactionEngineParams params,
|
||||
TransactionEngine* engine)
|
||||
{
|
||||
// Attempt to implement legacy offer creation semantics. If successful,
|
||||
// then return the result. Otherwise, attempt to process using the
|
||||
// new semantics.
|
||||
auto ret = ripple::legacy::transact_CreateOffer (txn, params, engine);
|
||||
|
||||
if (ret.first)
|
||||
return ret.second;
|
||||
|
||||
core::CrossType cross_type = core::CrossType::IouToIou;
|
||||
|
||||
bool const pays_xrp = txn.getFieldAmount (sfTakerPays).isNative ();
|
||||
|
||||
625
src/ripple/legacy/0.27/CreateOffer.cpp
Normal file
625
src/ripple/legacy/0.27/CreateOffer.cpp
Normal file
@@ -0,0 +1,625 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/legacy/0.27/book/OfferStream.h>
|
||||
#include <ripple/legacy/0.27/book/Taker.h>
|
||||
#include <ripple/legacy/0.27/book/Types.h>
|
||||
#include <ripple/legacy/0.27/book/Amounts.h>
|
||||
#include <ripple/legacy/0.27/CreateOffer.h>
|
||||
#include <ripple/legacy/0.27/Emulate027.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/core/Config.h>
|
||||
#include <ripple/json/to_string.h>
|
||||
#include <beast/cxx14/memory.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
namespace legacy {
|
||||
|
||||
TER
|
||||
CreateOffer::checkAcceptAsset(IssueRef issue) const
|
||||
{
|
||||
/* Only valid for custom currencies */
|
||||
assert (!isXRP (issue.currency));
|
||||
|
||||
SLE::pointer const issuerAccount = mEngine->entryCache (
|
||||
ltACCOUNT_ROOT, getAccountRootIndex (issue.account));
|
||||
|
||||
if (!issuerAccount)
|
||||
{
|
||||
if (m_journal.warning) m_journal.warning <<
|
||||
"delay: can't receive IOUs from non-existent issuer: " <<
|
||||
to_string (issue.account);
|
||||
|
||||
return (mParams & tapRETRY)
|
||||
? terNO_ACCOUNT
|
||||
: tecNO_ISSUER;
|
||||
}
|
||||
|
||||
if (issuerAccount->getFieldU32 (sfFlags) & lsfRequireAuth)
|
||||
{
|
||||
SLE::pointer const trustLine (mEngine->entryCache (
|
||||
ltRIPPLE_STATE, getRippleStateIndex (
|
||||
mTxnAccountID, issue.account, issue.currency)));
|
||||
|
||||
if (!trustLine)
|
||||
{
|
||||
return (mParams & tapRETRY)
|
||||
? terNO_LINE
|
||||
: tecNO_LINE;
|
||||
}
|
||||
|
||||
// Entries have a canonical representation, determined by a
|
||||
// lexicographical "greater than" comparison employing strict weak
|
||||
// ordering. Determine which entry we need to access.
|
||||
bool const canonical_gt (mTxnAccountID > issue.account);
|
||||
|
||||
bool const is_authorized (trustLine->getFieldU32 (sfFlags) &
|
||||
(canonical_gt ? lsfLowAuth : lsfHighAuth));
|
||||
|
||||
if (!is_authorized)
|
||||
{
|
||||
if (m_journal.debug) m_journal.debug <<
|
||||
"delay: can't receive IOUs from issuer without auth.";
|
||||
|
||||
return (mParams & tapRETRY)
|
||||
? terNO_AUTH
|
||||
: tecNO_AUTH;
|
||||
}
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
std::pair<TER, legacy::core::Amounts>
|
||||
CreateOffer::crossOffers (legacy::core::LedgerView& view,
|
||||
legacy::core::Amounts const& taker_amount)
|
||||
{
|
||||
legacy::core::Taker::Options const options (mTxn.getFlags());
|
||||
|
||||
legacy::core::Clock::time_point const when (
|
||||
mEngine->getLedger ()->getParentCloseTimeNC ());
|
||||
|
||||
legacy::core::LedgerView view_cancel (view.duplicate());
|
||||
legacy::core::OfferStream offers (
|
||||
view, view_cancel,
|
||||
Book (taker_amount.in.issue(), taker_amount.out.issue()),
|
||||
when, m_journal);
|
||||
legacy::core::Taker taker (offers.view(), mTxnAccountID, taker_amount, options);
|
||||
|
||||
TER cross_result (tesSUCCESS);
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Modifying the order or logic of these
|
||||
// operations causes a protocol breaking change.
|
||||
|
||||
// Checks which remove offers are performed early so we
|
||||
// can reduce the size of the order book as much as possible
|
||||
// before terminating the loop.
|
||||
|
||||
if (taker.done())
|
||||
{
|
||||
m_journal.debug << "The taker reports he's done during crossing!";
|
||||
break;
|
||||
}
|
||||
|
||||
if (! offers.step ())
|
||||
{
|
||||
// Place the order since there are no
|
||||
// more offers and the order has a balance.
|
||||
m_journal.debug << "No more offers to consider during crossing!";
|
||||
break;
|
||||
}
|
||||
|
||||
auto const& offer (offers.tip());
|
||||
|
||||
if (taker.reject (offer.quality()))
|
||||
{
|
||||
// Place the order since there are no more offers
|
||||
// at the desired quality, and the order has a balance.
|
||||
break;
|
||||
}
|
||||
|
||||
if (offer.account() == taker.account())
|
||||
{
|
||||
// Skip offer from self. The offer will be considered expired and
|
||||
// will get deleted.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_journal.debug) m_journal.debug <<
|
||||
" Offer: " << offer.entry()->getIndex() << std::endl <<
|
||||
" " << offer.amount().in << " : " << offer.amount().out;
|
||||
|
||||
cross_result = taker.cross (offer);
|
||||
|
||||
if (cross_result != tesSUCCESS)
|
||||
{
|
||||
cross_result = tecFAILED_PROCESSING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(cross_result, taker.remaining_offer ());
|
||||
}
|
||||
|
||||
CreateOffer::CreateOffer (STTx const& txn,
|
||||
TransactionEngineParams params, TransactionEngine* engine)
|
||||
: Transactor (txn, params, engine, deprecatedLogs().journal("CreateOffer"))
|
||||
{
|
||||
}
|
||||
|
||||
TER
|
||||
CreateOffer::doApply()
|
||||
{
|
||||
if (m_journal.debug) m_journal.debug <<
|
||||
"OfferCreate> " << mTxn.getJson (0);
|
||||
|
||||
std::uint32_t const uTxFlags = mTxn.getFlags ();
|
||||
|
||||
bool const bPassive (uTxFlags & tfPassive);
|
||||
bool const bImmediateOrCancel (uTxFlags & tfImmediateOrCancel);
|
||||
bool const bFillOrKill (uTxFlags & tfFillOrKill);
|
||||
bool const bSell (uTxFlags & tfSell);
|
||||
|
||||
STAmount saTakerPays = mTxn.getFieldAmount (sfTakerPays);
|
||||
STAmount saTakerGets = mTxn.getFieldAmount (sfTakerGets);
|
||||
|
||||
if (!isLegalNet (saTakerPays) || !isLegalNet (saTakerGets))
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
auto const& uPaysIssuerID = saTakerPays.getIssuer ();
|
||||
auto const& uPaysCurrency = saTakerPays.getCurrency ();
|
||||
|
||||
auto const& uGetsIssuerID = saTakerGets.getIssuer ();
|
||||
auto const& uGetsCurrency = saTakerGets.getCurrency ();
|
||||
|
||||
bool const bHaveExpiration (mTxn.isFieldPresent (sfExpiration));
|
||||
bool const bHaveCancel (mTxn.isFieldPresent (sfOfferSequence));
|
||||
|
||||
std::uint32_t const uExpiration = mTxn.getFieldU32 (sfExpiration);
|
||||
std::uint32_t const uCancelSequence = mTxn.getFieldU32 (sfOfferSequence);
|
||||
|
||||
// FIXME understand why we use SequenceNext instead of current transaction
|
||||
// sequence to determine the transaction. Why is the offer seuqnce
|
||||
// number insufficient?
|
||||
|
||||
std::uint32_t const uAccountSequenceNext = mTxnAccount->getFieldU32 (sfSequence);
|
||||
std::uint32_t const uSequence = mTxn.getSequence ();
|
||||
|
||||
const uint256 uLedgerIndex = getOfferIndex (mTxnAccountID, uSequence);
|
||||
|
||||
if (m_journal.debug)
|
||||
{
|
||||
m_journal.debug <<
|
||||
"Creating offer node: " << to_string (uLedgerIndex) <<
|
||||
" uSequence=" << uSequence;
|
||||
|
||||
if (bImmediateOrCancel)
|
||||
m_journal.debug << "Transaction: IoC set.";
|
||||
|
||||
if (bFillOrKill)
|
||||
m_journal.debug << "Transaction: FoK set.";
|
||||
}
|
||||
|
||||
// This is the original rate of this offer, and is the rate at which it will
|
||||
// be placed, even if crossing offers change the amounts.
|
||||
std::uint64_t const uRate = getRate (saTakerGets, saTakerPays);
|
||||
|
||||
TER terResult (tesSUCCESS);
|
||||
|
||||
// This is the ledger view that we work against. Transactions are applied
|
||||
// as we go on processing transactions.
|
||||
legacy::core::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.
|
||||
legacy::core::LedgerView view_checkpoint (view);
|
||||
|
||||
view.bumpSeq (); // Begin ledger variance.
|
||||
|
||||
SLE::pointer sleCreator = mEngine->entryCache (
|
||||
ltACCOUNT_ROOT, getAccountRootIndex (mTxnAccountID));
|
||||
|
||||
if (uTxFlags & tfOfferCreateMask)
|
||||
{
|
||||
if (m_journal.debug) m_journal.debug <<
|
||||
"Malformed transaction: Invalid flags set.";
|
||||
|
||||
terResult = temINVALID_FLAG;
|
||||
}
|
||||
else if (bImmediateOrCancel && bFillOrKill)
|
||||
{
|
||||
if (m_journal.debug) m_journal.debug <<
|
||||
"Malformed transaction: both IoC and FoK set.";
|
||||
|
||||
terResult = temINVALID_FLAG;
|
||||
}
|
||||
else if (bHaveExpiration && !uExpiration)
|
||||
{
|
||||
m_journal.warning <<
|
||||
"Malformed offer: bad expiration";
|
||||
|
||||
terResult = temBAD_EXPIRATION;
|
||||
}
|
||||
else if (saTakerPays.isNative () && saTakerGets.isNative ())
|
||||
{
|
||||
m_journal.warning <<
|
||||
"Malformed offer: XRP for XRP";
|
||||
|
||||
terResult = temBAD_OFFER;
|
||||
}
|
||||
else if (saTakerPays <= zero || saTakerGets <= zero)
|
||||
{
|
||||
m_journal.warning <<
|
||||
"Malformed offer: bad amount";
|
||||
|
||||
terResult = temBAD_OFFER;
|
||||
}
|
||||
else if (uPaysCurrency == uGetsCurrency && uPaysIssuerID == uGetsIssuerID)
|
||||
{
|
||||
m_journal.warning <<
|
||||
"Malformed offer: redundant offer";
|
||||
|
||||
terResult = temREDUNDANT;
|
||||
}
|
||||
// We don't allow a non-native currency to use the currency code XRP.
|
||||
else if (badCurrency() == uPaysCurrency || badCurrency() == uGetsCurrency)
|
||||
{
|
||||
m_journal.warning <<
|
||||
"Malformed offer: Bad currency.";
|
||||
|
||||
terResult = temBAD_CURRENCY;
|
||||
}
|
||||
else if (saTakerPays.isNative () != !uPaysIssuerID ||
|
||||
saTakerGets.isNative () != !uGetsIssuerID)
|
||||
{
|
||||
m_journal.warning <<
|
||||
"Malformed offer: bad issuer";
|
||||
|
||||
terResult = temBAD_ISSUER;
|
||||
}
|
||||
else if (view.isGlobalFrozen (uPaysIssuerID) || view.isGlobalFrozen (uGetsIssuerID))
|
||||
{
|
||||
m_journal.warning <<
|
||||
"Offer involves frozen asset";
|
||||
|
||||
terResult = tecFROZEN;
|
||||
}
|
||||
else if (view.accountFunds (
|
||||
mTxnAccountID, saTakerGets, fhZERO_IF_FROZEN) <= zero)
|
||||
{
|
||||
m_journal.warning <<
|
||||
"delay: Offers must be at least partially funded.";
|
||||
|
||||
terResult = tecUNFUNDED_OFFER;
|
||||
}
|
||||
// This can probably be simplified to make sure that you cancel sequences
|
||||
// before the transaction sequence number.
|
||||
else if (bHaveCancel && (!uCancelSequence || uAccountSequenceNext - 1 <= uCancelSequence))
|
||||
{
|
||||
if (m_journal.debug) m_journal.debug <<
|
||||
"uAccountSequenceNext=" << uAccountSequenceNext <<
|
||||
" uOfferSequence=" << uCancelSequence;
|
||||
|
||||
terResult = temBAD_SEQUENCE;
|
||||
}
|
||||
|
||||
if (terResult != tesSUCCESS)
|
||||
{
|
||||
if (m_journal.debug) m_journal.debug <<
|
||||
"final terResult=" << transToken (terResult);
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
// Process a cancellation request that's passed along with an offer.
|
||||
if ((terResult == tesSUCCESS) && bHaveCancel)
|
||||
{
|
||||
uint256 const uCancelIndex (
|
||||
getOfferIndex (mTxnAccountID, uCancelSequence));
|
||||
SLE::pointer sleCancel = mEngine->entryCache (ltOFFER, uCancelIndex);
|
||||
|
||||
// It's not an error to not find the offer to cancel: it might have
|
||||
// been consumed or removed as we are processing.
|
||||
if (sleCancel)
|
||||
{
|
||||
m_journal.warning <<
|
||||
"Cancelling order with sequence " << uCancelSequence;
|
||||
|
||||
terResult = view.offerDelete (sleCancel);
|
||||
}
|
||||
}
|
||||
|
||||
// Expiration is defined in terms of the close time of the parent ledger,
|
||||
// because we definitively know the time that it closed but we do not
|
||||
// know the closing time of the ledger that is under construction.
|
||||
if (bHaveExpiration &&
|
||||
(mEngine->getLedger ()->getParentCloseTimeNC () >= uExpiration))
|
||||
{
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
// Make sure that we are authorized to hold what the taker will pay us.
|
||||
if (terResult == tesSUCCESS && !saTakerPays.isNative ())
|
||||
terResult = checkAcceptAsset (Issue (uPaysCurrency, uPaysIssuerID));
|
||||
|
||||
bool crossed = false;
|
||||
bool const bOpenLedger (mParams & tapOPEN_LEDGER);
|
||||
|
||||
if (terResult == tesSUCCESS)
|
||||
{
|
||||
// We reverse gets and pays because during offer crossing we are taking.
|
||||
legacy::core::Amounts const taker_amount (saTakerGets, saTakerPays);
|
||||
|
||||
// The amount of the offer that we will need to place, after we finish
|
||||
// offer crossing processing. It may be equal to the original amount,
|
||||
// empty (fully crossed), or something in-between.
|
||||
legacy::core::Amounts place_offer;
|
||||
|
||||
std::tie(terResult, place_offer) = crossOffers (view, taker_amount);
|
||||
|
||||
if (terResult == tecFAILED_PROCESSING && bOpenLedger)
|
||||
terResult = telFAILED_PROCESSING;
|
||||
|
||||
if (terResult == tesSUCCESS)
|
||||
{
|
||||
// We now need to reduce the offer by the cross flow. We reverse
|
||||
// in and out here, since during crossing we were takers.
|
||||
assert (saTakerPays.getCurrency () == place_offer.out.getCurrency ());
|
||||
assert (saTakerPays.getIssuer () == place_offer.out.getIssuer ());
|
||||
assert (saTakerGets.getCurrency () == place_offer.in.getCurrency ());
|
||||
assert (saTakerGets.getIssuer () == place_offer.in.getIssuer ());
|
||||
|
||||
if (taker_amount != place_offer)
|
||||
crossed = true;
|
||||
|
||||
if (m_journal.debug)
|
||||
{
|
||||
m_journal.debug << "Offer Crossing: " << transToken (terResult);
|
||||
|
||||
if (terResult == tesSUCCESS)
|
||||
{
|
||||
m_journal.debug <<
|
||||
" takerPays: " << saTakerPays.getFullText () <<
|
||||
" -> " << place_offer.out.getFullText ();
|
||||
m_journal.debug <<
|
||||
" takerGets: " << saTakerGets.getFullText () <<
|
||||
" -> " << place_offer.in.getFullText ();
|
||||
}
|
||||
}
|
||||
|
||||
saTakerPays = place_offer.out;
|
||||
saTakerGets = place_offer.in;
|
||||
}
|
||||
}
|
||||
|
||||
if (terResult != tesSUCCESS)
|
||||
{
|
||||
m_journal.debug <<
|
||||
"final terResult=" << transToken (terResult);
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
if (m_journal.debug)
|
||||
{
|
||||
m_journal.debug <<
|
||||
"takeOffers: saTakerPays=" <<saTakerPays.getFullText ();
|
||||
m_journal.debug <<
|
||||
"takeOffers: saTakerGets=" << saTakerGets.getFullText ();
|
||||
m_journal.debug <<
|
||||
"takeOffers: mTxnAccountID=" <<
|
||||
to_string (mTxnAccountID);
|
||||
m_journal.debug <<
|
||||
"takeOffers: FUNDS=" << view.accountFunds (
|
||||
mTxnAccountID, saTakerGets, fhZERO_IF_FROZEN).getFullText ();
|
||||
}
|
||||
|
||||
if (saTakerPays < zero || saTakerGets < zero)
|
||||
{
|
||||
// Earlier, we verified that the amounts, as specified in the offer,
|
||||
// were not negative. That they are now suggests that something went
|
||||
// very wrong with offer crossing.
|
||||
m_journal.fatal << (crossed ? "Partially consumed" : "Full") <<
|
||||
" offer has negative component:" <<
|
||||
" pays=" << saTakerPays.getFullText () <<
|
||||
" gets=" << saTakerGets.getFullText ();
|
||||
|
||||
assert (saTakerPays >= zero);
|
||||
assert (saTakerGets >= zero);
|
||||
return tefINTERNAL;
|
||||
}
|
||||
|
||||
if (bFillOrKill && (saTakerPays != zero || saTakerGets != zero))
|
||||
{
|
||||
// Fill or kill and have leftovers.
|
||||
view.swapWith (view_checkpoint); // Restore with just fees paid.
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
// What the reserve would be if this offer was placed.
|
||||
auto const accountReserve (mEngine->getLedger ()->getReserve (
|
||||
sleCreator->getFieldU32 (sfOwnerCount) + 1));
|
||||
|
||||
if (saTakerPays == zero || // Wants nothing more.
|
||||
saTakerGets == zero || // Offering nothing more.
|
||||
bImmediateOrCancel) // Do not persist.
|
||||
{
|
||||
// Complete as is.
|
||||
}
|
||||
else if (mPriorBalance.getNValue () < accountReserve)
|
||||
{
|
||||
// If we are here, the signing account had an insufficient reserve
|
||||
// *prior* to our processing. We use the prior balance to simplify
|
||||
// client writing and make the user experience better.
|
||||
|
||||
if (bOpenLedger) // Ledger is not final, can vote no.
|
||||
{
|
||||
// Hope for more reserve to come in or more offers to consume. If we
|
||||
// specified a local error this transaction will not be retried, so
|
||||
// specify a tec to distribute the transaction and allow it to be
|
||||
// retried. In particular, it may have been successful to a
|
||||
// degree (partially filled) and if it hasn't, it might succeed.
|
||||
terResult = tecINSUF_RESERVE_OFFER;
|
||||
}
|
||||
else if (!crossed)
|
||||
{
|
||||
// Ledger is final, insufficent reserve to create offer, processed
|
||||
// nothing.
|
||||
terResult = tecINSUF_RESERVE_OFFER;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ledger is final, insufficent reserve to create offer, processed
|
||||
// something.
|
||||
// Consider the offer unfunded. Treat as tesSUCCESS.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert (saTakerPays > zero);
|
||||
assert (saTakerGets > zero);
|
||||
|
||||
// We need to place the remainder of the offer into its order book.
|
||||
if (m_journal.debug) m_journal.debug <<
|
||||
"offer not fully consumed:" <<
|
||||
" saTakerPays=" << saTakerPays.getFullText () <<
|
||||
" saTakerGets=" << saTakerGets.getFullText ();
|
||||
|
||||
std::uint64_t uOwnerNode;
|
||||
std::uint64_t uBookNode;
|
||||
uint256 uDirectory;
|
||||
|
||||
// Add offer to owner's directory.
|
||||
terResult = view.dirAdd (uOwnerNode,
|
||||
getOwnerDirIndex (mTxnAccountID), uLedgerIndex,
|
||||
std::bind (
|
||||
&Ledger::ownerDirDescriber, std::placeholders::_1,
|
||||
std::placeholders::_2, mTxnAccountID));
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
// Update owner count.
|
||||
view.incrementOwnerCount (sleCreator);
|
||||
|
||||
uint256 const uBookBase (getBookBase (
|
||||
{{uPaysCurrency, uPaysIssuerID},
|
||||
{uGetsCurrency, uGetsIssuerID}}));
|
||||
|
||||
if (m_journal.debug) m_journal.debug <<
|
||||
"adding to book: " << to_string (uBookBase) <<
|
||||
" : " << saTakerPays.getHumanCurrency () <<
|
||||
"/" << to_string (saTakerPays.getIssuer ()) <<
|
||||
" -> " << saTakerGets.getHumanCurrency () <<
|
||||
"/" << to_string (saTakerGets.getIssuer ());
|
||||
|
||||
// We use the original rate to place the offer.
|
||||
uDirectory = getQualityIndex (uBookBase, uRate);
|
||||
|
||||
// Add offer to order book.
|
||||
terResult = view.dirAdd (uBookNode, uDirectory, uLedgerIndex,
|
||||
std::bind (
|
||||
&Ledger::qualityDirDescriber, std::placeholders::_1,
|
||||
std::placeholders::_2, saTakerPays.getCurrency (),
|
||||
uPaysIssuerID, saTakerGets.getCurrency (),
|
||||
uGetsIssuerID, uRate));
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
if (m_journal.debug)
|
||||
{
|
||||
m_journal.debug <<
|
||||
"sfAccount=" <<
|
||||
to_string (mTxnAccountID);
|
||||
m_journal.debug <<
|
||||
"uPaysIssuerID=" <<
|
||||
to_string (uPaysIssuerID);
|
||||
m_journal.debug <<
|
||||
"uGetsIssuerID=" <<
|
||||
to_string (uGetsIssuerID);
|
||||
m_journal.debug <<
|
||||
"saTakerPays.isNative()=" <<
|
||||
saTakerPays.isNative ();
|
||||
m_journal.debug <<
|
||||
"saTakerGets.isNative()=" <<
|
||||
saTakerGets.isNative ();
|
||||
m_journal.debug <<
|
||||
"uPaysCurrency=" <<
|
||||
saTakerPays.getHumanCurrency ();
|
||||
m_journal.debug <<
|
||||
"uGetsCurrency=" <<
|
||||
saTakerGets.getHumanCurrency ();
|
||||
}
|
||||
|
||||
SLE::pointer sleOffer (mEngine->entryCreate (ltOFFER, uLedgerIndex));
|
||||
|
||||
sleOffer->setFieldAccount (sfAccount, mTxnAccountID);
|
||||
sleOffer->setFieldU32 (sfSequence, uSequence);
|
||||
sleOffer->setFieldH256 (sfBookDirectory, uDirectory);
|
||||
sleOffer->setFieldAmount (sfTakerPays, saTakerPays);
|
||||
sleOffer->setFieldAmount (sfTakerGets, saTakerGets);
|
||||
sleOffer->setFieldU64 (sfOwnerNode, uOwnerNode);
|
||||
sleOffer->setFieldU64 (sfBookNode, uBookNode);
|
||||
|
||||
if (uExpiration)
|
||||
sleOffer->setFieldU32 (sfExpiration, uExpiration);
|
||||
|
||||
if (bPassive)
|
||||
sleOffer->setFlag (lsfPassive);
|
||||
|
||||
if (bSell)
|
||||
sleOffer->setFlag (lsfSell);
|
||||
|
||||
if (m_journal.debug) m_journal.debug <<
|
||||
"final terResult=" << transToken (terResult) <<
|
||||
" sleOffer=" << sleOffer->getJson (0);
|
||||
}
|
||||
}
|
||||
|
||||
if (terResult != tesSUCCESS)
|
||||
{
|
||||
m_journal.debug <<
|
||||
"final terResult=" << transToken (terResult);
|
||||
}
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
std::pair <bool, TER>
|
||||
transact_CreateOffer (STTx const& txn,
|
||||
TransactionEngineParams params, TransactionEngine* engine)
|
||||
{
|
||||
// If we are emulating 0.27, we process the transaction using the 0.27
|
||||
// semantics and no autobridging.
|
||||
if (emulate027 (engine->getLedger ()))
|
||||
return std::make_pair (true, CreateOffer (txn, params, engine).apply ());
|
||||
|
||||
// Otherwise, we use 0.28 semantics with autobridging.
|
||||
return std::make_pair (false, tesSUCCESS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
75
src/ripple/legacy/0.27/CreateOffer.h
Normal file
75
src/ripple/legacy/0.27/CreateOffer.h
Normal file
@@ -0,0 +1,75 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_CREATEOFFER_H_INCLUDED
|
||||
#define RIPPLE_APP_CREATEOFFER_H_INCLUDED
|
||||
|
||||
#include <ripple/app/book/OfferStream.h>
|
||||
#include <ripple/app/book/Taker.h>
|
||||
#include <ripple/app/book/Types.h>
|
||||
#include <ripple/app/book/Amounts.h>
|
||||
#include <ripple/app/transactors/Transactor.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/json/to_string.h>
|
||||
#include <beast/cxx14/memory.h>
|
||||
#include <utility>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
namespace legacy {
|
||||
|
||||
class CreateOffer
|
||||
: public Transactor
|
||||
{
|
||||
private:
|
||||
// Determine if we are authorized to hold the asset we want to get
|
||||
TER
|
||||
checkAcceptAsset(IssueRef issue) const;
|
||||
|
||||
/* Fill offer as much as possible by consuming offers already on the books.
|
||||
We adjusts account balances and charges fees on top to taker.
|
||||
|
||||
@param taker_amount.in How much the taker offers
|
||||
@param taker_amount.out How much the taker wants
|
||||
|
||||
@return result.first crossing operation success/failure indicator.
|
||||
result.second amount of offer left unfilled - only meaningful
|
||||
if result.first is tesSUCCESS.
|
||||
*/
|
||||
std::pair<TER, core::Amounts>
|
||||
crossOffers (core::LedgerView& view,
|
||||
core::Amounts const& taker_amount);
|
||||
|
||||
public:
|
||||
CreateOffer (STTx const& txn,
|
||||
TransactionEngineParams params, TransactionEngine* engine);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
std::pair <bool, TER>
|
||||
transact_CreateOffer (STTx const& txn,
|
||||
TransactionEngineParams params, TransactionEngine* engine);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
48
src/ripple/legacy/0.27/Emulate027.cpp
Normal file
48
src/ripple/legacy/0.27/Emulate027.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/core/Config.h>
|
||||
#include <ripple/legacy/0.27/Emulate027.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
namespace legacy {
|
||||
|
||||
bool
|
||||
emulate027 (Ledger::ref ledger)
|
||||
{
|
||||
// In standalone mode, the server always uses 0.28 semantics to allow
|
||||
// unit tests to succeed.
|
||||
if (getConfig ().RUN_STANDALONE)
|
||||
return false;
|
||||
|
||||
// The server also uses 0.28 semantics for all ledgers whose parent
|
||||
// closed after 2015-03-30 13:00:00 PDT.
|
||||
static std::uint32_t const legacy_cutoff = 481060800;
|
||||
|
||||
if (ledger->getParentCloseTimeNC () > legacy_cutoff)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
32
src/ripple/legacy/0.27/Emulate027.h
Normal file
32
src/ripple/legacy/0.27/Emulate027.h
Normal file
@@ -0,0 +1,32 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
namespace legacy {
|
||||
|
||||
bool
|
||||
emulate027 (Ledger::ref ledger);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
216
src/ripple/legacy/0.27/book/Amount.h
Normal file
216
src/ripple/legacy/0.27/book/Amount.h
Normal file
@@ -0,0 +1,216 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2014 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_LEGACY_CORE_AMOUNT_H_INCLUDED
|
||||
#define RIPPLE_LEGACY_CORE_AMOUNT_H_INCLUDED
|
||||
|
||||
#include <ripple/protocol/STObject.h>
|
||||
|
||||
#include <beast/utility/noexcept.h>
|
||||
#include <beast/cxx14/type_traits.h> // <type_traits>
|
||||
|
||||
namespace ripple {
|
||||
namespace legacy {
|
||||
namespace core {
|
||||
|
||||
/** Custom floating point asset amount.
|
||||
The "representation" may be integral or non-integral. For integral
|
||||
representations, the exponent is always zero and the value held in the
|
||||
mantissa is an exact quantity.
|
||||
*/
|
||||
class AmountType
|
||||
{
|
||||
private:
|
||||
std::uint64_t m_mantissa;
|
||||
int m_exponent;
|
||||
bool m_negative;
|
||||
bool m_integral;
|
||||
|
||||
AmountType (std::uint64_t mantissa,
|
||||
int exponent, bool negative, bool integral)
|
||||
: m_mantissa (mantissa)
|
||||
, m_exponent (exponent)
|
||||
, m_negative (negative)
|
||||
, m_integral (integral)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
/** Default construction.
|
||||
The value is uninitialized.
|
||||
*/
|
||||
AmountType() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
/** Construct from an integer.
|
||||
The representation is set to integral.
|
||||
*/
|
||||
/** @{ */
|
||||
template <class Integer>
|
||||
AmountType (Integer value,
|
||||
std::enable_if_t <std::is_signed <Integer>::value>* = 0) noexcept
|
||||
: m_mantissa (value)
|
||||
, m_exponent (0)
|
||||
, m_negative (value < 0)
|
||||
, m_integral (true)
|
||||
{
|
||||
static_assert (std::is_integral<Integer>::value,
|
||||
"Cannot construct from non-integral type.");
|
||||
}
|
||||
|
||||
template <class Integer>
|
||||
AmountType (Integer value,
|
||||
std::enable_if_t <! std::is_signed <Integer>::value>* = 0) noexcept
|
||||
: m_mantissa (value)
|
||||
, m_exponent (0)
|
||||
, m_negative (false)
|
||||
{
|
||||
static_assert (std::is_integral<Integer>::value,
|
||||
"Cannot construct from non-integral type.");
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Assign the value zero.
|
||||
The representation is preserved.
|
||||
*/
|
||||
AmountType&
|
||||
operator= (Zero) noexcept
|
||||
{
|
||||
m_mantissa = 0;
|
||||
// VFALCO Why -100?
|
||||
// "We have to use something in range."
|
||||
// "This makes zero the smallest value."
|
||||
m_exponent = m_integral ? 0 : -100;
|
||||
m_exponent = 0;
|
||||
m_negative = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Returns the value in canonical format. */
|
||||
AmountType
|
||||
normal() const noexcept
|
||||
{
|
||||
if (m_integral)
|
||||
{
|
||||
AmountType result;
|
||||
if (m_mantissa == 0)
|
||||
{
|
||||
result.m_exponent = 0;
|
||||
result.m_negative = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return AmountType();
|
||||
}
|
||||
|
||||
//
|
||||
// Comparison
|
||||
//
|
||||
|
||||
int
|
||||
signum() const noexcept
|
||||
{
|
||||
if (m_mantissa == 0)
|
||||
return 0;
|
||||
return m_negative ? -1 : 1;
|
||||
}
|
||||
|
||||
bool
|
||||
operator== (AmountType const& other) const noexcept
|
||||
{
|
||||
return
|
||||
m_negative == other.m_negative &&
|
||||
m_mantissa == other.m_mantissa &&
|
||||
m_exponent == other.m_exponent;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!= (AmountType const& other) const noexcept
|
||||
{
|
||||
return ! (*this == other);
|
||||
}
|
||||
|
||||
bool
|
||||
operator< (AmountType const& other) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
operator>= (AmountType const& other) const noexcept
|
||||
{
|
||||
return ! (*this < other);
|
||||
}
|
||||
|
||||
bool
|
||||
operator> (AmountType const& other) const noexcept
|
||||
{
|
||||
return other < *this;
|
||||
}
|
||||
|
||||
bool
|
||||
operator<= (AmountType const& other) const noexcept
|
||||
{
|
||||
return ! (other < *this);
|
||||
}
|
||||
|
||||
//
|
||||
// Arithmetic
|
||||
//
|
||||
|
||||
AmountType
|
||||
operator-() const noexcept
|
||||
{
|
||||
return AmountType (m_mantissa, m_exponent, ! m_negative, m_integral);
|
||||
}
|
||||
|
||||
//
|
||||
// Output
|
||||
//
|
||||
|
||||
std::ostream&
|
||||
operator<< (std::ostream& os)
|
||||
{
|
||||
int const sig (signum());
|
||||
|
||||
if (sig == 0)
|
||||
return os << "0";
|
||||
|
||||
if (sig < 0)
|
||||
os << "-";
|
||||
if (m_integral)
|
||||
return os << m_mantissa;
|
||||
if (m_exponent != 0 && (m_exponent < -25 || m_exponent > -5))
|
||||
return os << m_mantissa << "e" << m_exponent;
|
||||
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// TODO(tom): remove this typedef and have exactly one name for STAmount.
|
||||
|
||||
typedef STAmount Amount;
|
||||
|
||||
} // core
|
||||
} // legacy
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
70
src/ripple/legacy/0.27/book/Amounts.h
Normal file
70
src/ripple/legacy/0.27/book/Amounts.h
Normal file
@@ -0,0 +1,70 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2014 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_LEGACY_CORE_AMOUNTS_H_INCLUDED
|
||||
#define RIPPLE_LEGACY_CORE_AMOUNTS_H_INCLUDED
|
||||
|
||||
#include <ripple/legacy/0.27/book/Amount.h>
|
||||
|
||||
#include <beast/utility/noexcept.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace legacy {
|
||||
namespace core {
|
||||
|
||||
struct Amounts
|
||||
{
|
||||
Amounts() = default;
|
||||
|
||||
Amounts (Amount const& in_, Amount const& out_)
|
||||
: in (in_)
|
||||
, out (out_)
|
||||
{
|
||||
}
|
||||
|
||||
/** Returns `true` if either quantity is not positive. */
|
||||
bool
|
||||
empty() const noexcept
|
||||
{
|
||||
return in <= zero || out <= zero;
|
||||
}
|
||||
|
||||
Amount in;
|
||||
Amount out;
|
||||
};
|
||||
|
||||
inline
|
||||
bool
|
||||
operator== (Amounts const& lhs, Amounts const& rhs) noexcept
|
||||
{
|
||||
return lhs.in == rhs.in && lhs.out == rhs.out;
|
||||
}
|
||||
|
||||
inline
|
||||
bool
|
||||
operator!= (Amounts const& lhs, Amounts const& rhs) noexcept
|
||||
{
|
||||
return ! (lhs == rhs);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
96
src/ripple/legacy/0.27/book/BookTip.h
Normal file
96
src/ripple/legacy/0.27/book/BookTip.h
Normal file
@@ -0,0 +1,96 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2014 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_LEGACY_CORE_BOOKTIP_H_INCLUDED
|
||||
#define RIPPLE_LEGACY_CORE_BOOKTIP_H_INCLUDED
|
||||
|
||||
#include <ripple/legacy/0.27/book/Quality.h>
|
||||
#include <ripple/legacy/0.27/book/Types.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <beast/utility/noexcept.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace ripple {
|
||||
namespace legacy {
|
||||
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;
|
||||
Quality m_quality;
|
||||
|
||||
LedgerView&
|
||||
view() const noexcept
|
||||
{
|
||||
return m_view;
|
||||
}
|
||||
|
||||
public:
|
||||
/** Create the iterator. */
|
||||
BookTip (LedgerView& view, BookRef book);
|
||||
|
||||
uint256 const&
|
||||
dir() const noexcept
|
||||
{
|
||||
return m_dir;
|
||||
}
|
||||
|
||||
uint256 const&
|
||||
index() const noexcept
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
Quality const&
|
||||
quality() const noexcept
|
||||
{
|
||||
return m_quality;
|
||||
}
|
||||
|
||||
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 ();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
120
src/ripple/legacy/0.27/book/Offer.h
Normal file
120
src/ripple/legacy/0.27/book/Offer.h
Normal file
@@ -0,0 +1,120 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2014 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_LEGACY_CORE_OFFER_H_INCLUDED
|
||||
#define RIPPLE_LEGACY_CORE_OFFER_H_INCLUDED
|
||||
|
||||
#include <ripple/legacy/0.27/book/Amounts.h>
|
||||
#include <ripple/legacy/0.27/book/Quality.h>
|
||||
#include <ripple/legacy/0.27/book/Types.h>
|
||||
|
||||
#include <ripple/protocol/STLedgerEntry.h>
|
||||
#include <ripple/protocol/SField.h>
|
||||
|
||||
#include <beast/utility/noexcept.h>
|
||||
|
||||
#include <ostream>
|
||||
|
||||
namespace ripple {
|
||||
namespace legacy {
|
||||
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
|
||||
{
|
||||
return Amounts (
|
||||
m_entry->getFieldAmount (sfTakerPays),
|
||||
m_entry->getFieldAmount (sfTakerGets));
|
||||
}
|
||||
|
||||
/** Returns `true` if no more funds can flow through this offer. */
|
||||
bool
|
||||
fully_consumed() const
|
||||
{
|
||||
if (m_entry->getFieldAmount (sfTakerPays) <= zero)
|
||||
return true;
|
||||
if (m_entry->getFieldAmount (sfTakerGets) <= zero)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 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
|
||||
126
src/ripple/legacy/0.27/book/OfferStream.h
Normal file
126
src/ripple/legacy/0.27/book/OfferStream.h
Normal file
@@ -0,0 +1,126 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2014 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_LEGACY_CORE_OFFERSTREAM_H_INCLUDED
|
||||
#define RIPPLE_LEGACY_CORE_OFFERSTREAM_H_INCLUDED
|
||||
|
||||
#include <ripple/legacy/0.27/book/BookTip.h>
|
||||
#include <ripple/legacy/0.27/book/Offer.h>
|
||||
#include <ripple/legacy/0.27/book/Quality.h>
|
||||
#include <ripple/legacy/0.27/book/Types.h>
|
||||
|
||||
#include <beast/utility/noexcept.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace ripple {
|
||||
namespace legacy {
|
||||
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.
|
||||
*/
|
||||
class OfferStream
|
||||
{
|
||||
private:
|
||||
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;
|
||||
|
||||
void
|
||||
erase (LedgerView& view);
|
||||
|
||||
public:
|
||||
OfferStream (LedgerView& view, LedgerView& view_cancel, BookRef book,
|
||||
Clock::time_point when, beast::Journal journal);
|
||||
|
||||
LedgerView&
|
||||
view () noexcept
|
||||
{
|
||||
return m_view;
|
||||
}
|
||||
|
||||
LedgerView&
|
||||
view_cancel () noexcept
|
||||
{
|
||||
return m_view_cancel;
|
||||
}
|
||||
|
||||
Book const&
|
||||
book () const noexcept
|
||||
{
|
||||
return m_book;
|
||||
}
|
||||
|
||||
/** 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 ();
|
||||
|
||||
/** Advance to the next valid offer that is not from the specified account.
|
||||
This automatically removes:
|
||||
- Offers with missing ledger entries
|
||||
- Offers found unfunded
|
||||
- Offers from the same account
|
||||
- Expired offers
|
||||
@return `true` if there is a valid offer.
|
||||
*/
|
||||
bool
|
||||
step_account (Account const& account);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
146
src/ripple/legacy/0.27/book/Quality.h
Normal file
146
src/ripple/legacy/0.27/book/Quality.h
Normal file
@@ -0,0 +1,146 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_LEGACY_CORE_QUALITY_H_INCLUDED
|
||||
#define RIPPLE_LEGACY_CORE_QUALITY_H_INCLUDED
|
||||
|
||||
#include <ripple/legacy/0.27/book/Amount.h>
|
||||
#include <ripple/legacy/0.27/book/Amounts.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <ostream>
|
||||
|
||||
namespace ripple {
|
||||
namespace legacy {
|
||||
namespace core {
|
||||
|
||||
// Ripple specific constant used for parsing qualities and other things
|
||||
#define QUALITY_ONE 1000000000 // 10e9
|
||||
|
||||
/** Represents the logical ratio of output currency to input currency.
|
||||
Internally this is stored using a custom floating point representation,
|
||||
as the inverse of the ratio, so that quality will be descending in
|
||||
a sequence of actual values that represent qualities.
|
||||
*/
|
||||
class Quality
|
||||
{
|
||||
public:
|
||||
// Type of the internal representation. Higher qualities
|
||||
// have lower unsigned integer representations.
|
||||
typedef std::uint64_t value_type;
|
||||
|
||||
private:
|
||||
value_type m_value;
|
||||
|
||||
public:
|
||||
Quality() = default;
|
||||
|
||||
/** Create a quality from the integer encoding of an Amount */
|
||||
explicit
|
||||
Quality (std::uint64_t value);
|
||||
|
||||
/** Create a quality from the ratio of two amounts. */
|
||||
explicit
|
||||
Quality (Amounts const& amount);
|
||||
|
||||
/** Advances to the next higher quality level. */
|
||||
/** @{ */
|
||||
Quality&
|
||||
operator++();
|
||||
|
||||
Quality
|
||||
operator++ (int);
|
||||
/** @} */
|
||||
|
||||
/** Advances to the next lower quality level. */
|
||||
/** @{ */
|
||||
Quality&
|
||||
operator--();
|
||||
|
||||
Quality
|
||||
operator-- (int);
|
||||
/** @} */
|
||||
|
||||
/** Returns the quality as Amount. */
|
||||
Amount
|
||||
rate () const
|
||||
{
|
||||
return amountFromQuality (m_value);
|
||||
}
|
||||
|
||||
/** Returns the scaled amount with in capped.
|
||||
Math is avoided if the result is exact. The output is clamped
|
||||
to prevent money creation.
|
||||
*/
|
||||
Amounts
|
||||
ceil_in (Amounts const& amount, Amount const& limit) const;
|
||||
|
||||
/** Returns the scaled amount with out capped.
|
||||
Math is avoided if the result is exact. The input is clamped
|
||||
to prevent money creation.
|
||||
*/
|
||||
Amounts
|
||||
ceil_out (Amounts const& amount, Amount const& limit) const;
|
||||
|
||||
/** Returns `true` if lhs is lower quality than `rhs`.
|
||||
Lower quality means the taker receives a worse deal.
|
||||
Higher quality is better for the taker.
|
||||
*/
|
||||
friend
|
||||
bool
|
||||
operator< (Quality const& lhs, Quality const& rhs) noexcept
|
||||
{
|
||||
return lhs.m_value > rhs.m_value;
|
||||
}
|
||||
|
||||
friend
|
||||
bool
|
||||
operator== (Quality const& lhs, Quality const& rhs) noexcept
|
||||
{
|
||||
return lhs.m_value == rhs.m_value;
|
||||
}
|
||||
|
||||
friend
|
||||
bool
|
||||
operator!= (Quality const& lhs, Quality const& rhs) noexcept
|
||||
{
|
||||
return ! (lhs == rhs);
|
||||
}
|
||||
|
||||
friend
|
||||
std::ostream&
|
||||
operator<< (std::ostream& os, Quality const& quality)
|
||||
{
|
||||
os << quality.m_value;
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
/** Calculate the quality of a two-hop path given the two hops.
|
||||
@param lhs The first leg of the path: input to intermediate.
|
||||
@param rhs The second leg of the path: intermediate to output.
|
||||
*/
|
||||
Quality
|
||||
composed_quality (Quality const& lhs, Quality const& rhs);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
148
src/ripple/legacy/0.27/book/Taker.h
Normal file
148
src/ripple/legacy/0.27/book/Taker.h
Normal file
@@ -0,0 +1,148 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2014 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_LEGACY_CORE_TAKER_H_INCLUDED
|
||||
#define RIPPLE_LEGACY_CORE_TAKER_H_INCLUDED
|
||||
|
||||
#include <ripple/legacy/0.27/book/Amounts.h>
|
||||
#include <ripple/legacy/0.27/book/Quality.h>
|
||||
#include <ripple/legacy/0.27/book/Offer.h>
|
||||
#include <ripple/legacy/0.27/book/Types.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <beast/utility/noexcept.h>
|
||||
#include <functional>
|
||||
|
||||
namespace ripple {
|
||||
namespace legacy {
|
||||
namespace core {
|
||||
|
||||
/** State for the active party during order book or payment operations. */
|
||||
class Taker
|
||||
{
|
||||
public:
|
||||
struct Options
|
||||
{
|
||||
Options() = delete;
|
||||
|
||||
explicit
|
||||
Options (std::uint32_t tx_flags)
|
||||
: sell (tx_flags & tfSell)
|
||||
, passive (tx_flags & tfPassive)
|
||||
, fill_or_kill (tx_flags & tfFillOrKill)
|
||||
, immediate_or_cancel (tx_flags & tfImmediateOrCancel)
|
||||
{
|
||||
}
|
||||
|
||||
bool const sell;
|
||||
bool const passive;
|
||||
bool const fill_or_kill;
|
||||
bool const immediate_or_cancel;
|
||||
};
|
||||
|
||||
private:
|
||||
std::reference_wrapper <LedgerView> m_view;
|
||||
Account m_account;
|
||||
Options m_options;
|
||||
Quality m_quality;
|
||||
Quality m_threshold;
|
||||
|
||||
// The original in and out quantities.
|
||||
Amounts const m_amount;
|
||||
|
||||
// The amounts still left over for us to try and take.
|
||||
Amounts m_remain;
|
||||
|
||||
Amounts
|
||||
flow (Amounts amount, Offer const& offer, Account const& taker);
|
||||
|
||||
TER
|
||||
fill (Offer const& offer, Amounts const& amount);
|
||||
|
||||
TER
|
||||
fill (Offer const& leg1, Amounts const& amount1,
|
||||
Offer const& leg2, Amounts const& amount2);
|
||||
|
||||
void
|
||||
consume (Offer const& offer, Amounts const& consumed) const;
|
||||
|
||||
public:
|
||||
Taker (LedgerView& view, Account const& account,
|
||||
Amounts const& amount, Options const& options);
|
||||
|
||||
LedgerView&
|
||||
view () const noexcept
|
||||
{
|
||||
return m_view;
|
||||
}
|
||||
|
||||
/** Returns the amount remaining on the offer.
|
||||
This is the amount at which the offer should be placed. It may either
|
||||
be for the full amount when there were no crossing offers, or for zero
|
||||
when the offer fully crossed, or any amount in between.
|
||||
It is always at the original offer quality (m_quality)
|
||||
*/
|
||||
Amounts
|
||||
remaining_offer () const;
|
||||
|
||||
/** Returns the account identifier of the taker. */
|
||||
Account const&
|
||||
account () const noexcept
|
||||
{
|
||||
return m_account;
|
||||
}
|
||||
|
||||
/** Returns `true` if the quality does not meet the taker's requirements. */
|
||||
bool
|
||||
reject (Quality const& quality) const noexcept
|
||||
{
|
||||
return quality < m_threshold;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
|
||||
/** Perform direct crossing through given offer.
|
||||
@return tesSUCCESS on success, error code otherwise.
|
||||
*/
|
||||
TER
|
||||
cross (Offer const& offer);
|
||||
|
||||
/** Perform bridged crossing through given offers.
|
||||
@return tesSUCCESS on success, error code otherwise.
|
||||
*/
|
||||
TER
|
||||
cross (Offer const& leg1, Offer const& leg2);
|
||||
};
|
||||
|
||||
inline
|
||||
std::ostream&
|
||||
operator<< (std::ostream& os, Taker const& taker)
|
||||
{
|
||||
return os << taker.account();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
51
src/ripple/legacy/0.27/book/Types.h
Normal file
51
src/ripple/legacy/0.27/book/Types.h
Normal file
@@ -0,0 +1,51 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2014 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_LEGACY_CORE_TYPES_H_INCLUDED
|
||||
#define RIPPLE_LEGACY_CORE_TYPES_H_INCLUDED
|
||||
|
||||
#include <ripple/app/ledger/LedgerEntrySet.h>
|
||||
#include <ripple/protocol/Book.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
namespace ripple {
|
||||
namespace legacy {
|
||||
namespace core {
|
||||
|
||||
/** A mutable view that overlays an immutable ledger to track changes. */
|
||||
typedef LedgerEntrySet LedgerView;
|
||||
|
||||
/** A clock representing network time.
|
||||
This measures seconds since the Ripple epoch as seen
|
||||
by the ledger close clock.
|
||||
*/
|
||||
class Clock // : public abstract_clock <std::chrono::seconds>
|
||||
{
|
||||
public:
|
||||
typedef std::uint32_t time_point;
|
||||
typedef std::chrono::seconds duration;
|
||||
};
|
||||
|
||||
} // core
|
||||
} // legacy
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
85
src/ripple/legacy/0.27/book/impl/BookTip.cpp
Normal file
85
src/ripple/legacy/0.27/book/impl/BookTip.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2014 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/legacy/0.27/book/BookTip.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace legacy {
|
||||
namespace core {
|
||||
|
||||
BookTip::BookTip (LedgerView& view, BookRef book)
|
||||
: m_view (view)
|
||||
, m_valid (false)
|
||||
, m_book (getBookBase (book))
|
||||
, m_end (getQualityNext (m_book))
|
||||
, m_quality ()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
BookTip::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 first_page (view().getNextLedgerIndex (m_book, m_end));
|
||||
|
||||
if (first_page.isZero())
|
||||
return false;
|
||||
|
||||
unsigned int di (0);
|
||||
SLE::pointer dir;
|
||||
|
||||
if (view().dirFirst (first_page, dir, di, m_index))
|
||||
{
|
||||
m_dir = dir->getIndex();
|
||||
m_entry = view().entryCache (ltOFFER, m_index);
|
||||
m_quality = Quality (getQuality (first_page));
|
||||
m_valid = true;
|
||||
|
||||
// Next query should start before this directory
|
||||
m_book = first_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 = first_page;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
172
src/ripple/legacy/0.27/book/impl/OfferStream.cpp
Normal file
172
src/ripple/legacy/0.27/book/impl/OfferStream.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/legacy/0.27/book/OfferStream.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace legacy {
|
||||
namespace core {
|
||||
|
||||
OfferStream::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)
|
||||
{
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
// NIKB NOTE This should be using LedgerView::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()));
|
||||
|
||||
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 it (std::find (v.begin(), v.end(), m_tip.index()));
|
||||
|
||||
if (it == v.end())
|
||||
{
|
||||
if (m_journal.error) m_journal.error <<
|
||||
"Missing offer " << m_tip.index() <<
|
||||
" for directory " << m_tip.dir();
|
||||
return;
|
||||
}
|
||||
|
||||
v.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();
|
||||
}
|
||||
|
||||
bool
|
||||
OfferStream::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
|
||||
// NIKB 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, fhZERO_IF_FROZEN));
|
||||
|
||||
// Check for unfunded offer
|
||||
if (owner_funds <= zero)
|
||||
{
|
||||
// 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, fhZERO_IF_FROZEN) == 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;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
OfferStream::step_account (Account const& account)
|
||||
{
|
||||
while (step ())
|
||||
{
|
||||
if (tip ().account () != account)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
126
src/ripple/legacy/0.27/book/impl/Quality.cpp
Normal file
126
src/ripple/legacy/0.27/book/impl/Quality.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/legacy/0.27/book/Quality.h>
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
|
||||
namespace ripple {
|
||||
namespace legacy {
|
||||
namespace core {
|
||||
|
||||
Quality::Quality (std::uint64_t value)
|
||||
: m_value (value)
|
||||
{
|
||||
}
|
||||
|
||||
Quality::Quality (Amounts const& amount)
|
||||
: m_value (getRate (amount.out, amount.in))
|
||||
{
|
||||
}
|
||||
|
||||
Quality&
|
||||
Quality::operator++()
|
||||
{
|
||||
assert (m_value > 0);
|
||||
--m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Quality
|
||||
Quality::operator++ (int)
|
||||
{
|
||||
Quality prev (*this);
|
||||
++*this;
|
||||
return prev;
|
||||
}
|
||||
|
||||
Quality&
|
||||
Quality::operator--()
|
||||
{
|
||||
assert (m_value < std::numeric_limits<value_type>::max());
|
||||
++m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Quality
|
||||
Quality::operator-- (int)
|
||||
{
|
||||
Quality prev (*this);
|
||||
--*this;
|
||||
return prev;
|
||||
}
|
||||
|
||||
Amounts
|
||||
Quality::ceil_in (Amounts const& amount, Amount const& limit) const
|
||||
{
|
||||
if (amount.in > limit)
|
||||
{
|
||||
Amounts result (limit, divRound (
|
||||
limit, rate(), amount.out, true));
|
||||
// Clamp out
|
||||
if (result.out > amount.out)
|
||||
result.out = amount.out;
|
||||
assert (result.in == limit);
|
||||
return result;
|
||||
}
|
||||
assert (amount.in <= limit);
|
||||
return amount;
|
||||
}
|
||||
|
||||
Amounts
|
||||
Quality::ceil_out (Amounts const& amount, Amount const& limit) const
|
||||
{
|
||||
if (amount.out > limit)
|
||||
{
|
||||
Amounts result (mulRound (
|
||||
limit, rate(), amount.in, true), limit);
|
||||
// Clamp in
|
||||
if (result.in > amount.in)
|
||||
result.in = amount.in;
|
||||
assert (result.out == limit);
|
||||
return result;
|
||||
}
|
||||
assert (amount.out <= limit);
|
||||
return amount;
|
||||
}
|
||||
|
||||
Quality
|
||||
composed_quality (Quality const& lhs, Quality const& rhs)
|
||||
{
|
||||
Amount const lhs_rate (lhs.rate ());
|
||||
assert (lhs_rate != zero);
|
||||
|
||||
Amount const rhs_rate (rhs.rate ());
|
||||
assert (rhs_rate != zero);
|
||||
|
||||
Amount const rate (mulRound (lhs_rate, rhs_rate, true));
|
||||
|
||||
std::uint64_t const stored_exponent (rate.exponent () + 100);
|
||||
std::uint64_t const stored_mantissa (rate.mantissa());
|
||||
|
||||
assert ((stored_exponent > 0) && (stored_exponent <= 255));
|
||||
|
||||
return Quality ((stored_exponent << (64 - 8)) | stored_mantissa);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
286
src/ripple/legacy/0.27/book/impl/Taker.cpp
Normal file
286
src/ripple/legacy/0.27/book/impl/Taker.cpp
Normal file
@@ -0,0 +1,286 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2014 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/legacy/0.27/book/Taker.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace legacy {
|
||||
namespace core {
|
||||
|
||||
Taker::Taker (LedgerView& view, Account const& account,
|
||||
Amounts const& amount, Options const& options)
|
||||
: m_view (view)
|
||||
, m_account (account)
|
||||
, m_options (options)
|
||||
, m_quality (amount)
|
||||
, m_threshold (m_quality)
|
||||
, m_amount (amount)
|
||||
, m_remain (amount)
|
||||
{
|
||||
assert (m_remain.in > zero);
|
||||
assert (m_remain.out > zero);
|
||||
|
||||
// If this is a passive order (tfPassive), this prevents
|
||||
// offers at the same quality level from being consumed.
|
||||
if (m_options.passive)
|
||||
++m_threshold;
|
||||
}
|
||||
|
||||
Amounts
|
||||
Taker::remaining_offer () const
|
||||
{
|
||||
// If the taker is done, then there's no offer to place.
|
||||
if (done ())
|
||||
return Amounts (m_amount.in.zeroed(), m_amount.out.zeroed());
|
||||
|
||||
// Avoid math altogether if we didn't cross.
|
||||
if (m_amount == m_remain)
|
||||
return m_amount;
|
||||
|
||||
if (m_options.sell)
|
||||
{
|
||||
assert (m_remain.in > zero);
|
||||
|
||||
// We scale the output based on the remaining input:
|
||||
return Amounts (m_remain.in, divRound (
|
||||
m_remain.in, m_quality.rate (), m_remain.out, true));
|
||||
}
|
||||
|
||||
assert (m_remain.out > zero);
|
||||
|
||||
// We scale the input based on the remaining output:
|
||||
return Amounts (mulRound (
|
||||
m_remain.out, m_quality.rate (), m_remain.in, true), m_remain.out);
|
||||
}
|
||||
|
||||
/** Calculate the amount particular user could get through an offer.
|
||||
@param amount the maximum flow that is available to the taker.
|
||||
@param offer the offer to flow through.
|
||||
@param taker the person taking the offer.
|
||||
@return the maximum amount that can flow through this offer.
|
||||
*/
|
||||
Amounts
|
||||
Taker::flow (Amounts amount, Offer const& offer, Account const& taker)
|
||||
{
|
||||
// Limit taker's input by available funds less fees
|
||||
Amount const taker_funds (view ().accountFunds (
|
||||
taker, amount.in, fhZERO_IF_FROZEN));
|
||||
|
||||
// Get fee rate paid by taker
|
||||
std::uint32_t const taker_charge_rate (rippleTransferRate (view (),
|
||||
taker, offer.account (), amount.in.getIssuer()));
|
||||
|
||||
// Skip some math when there's no fee
|
||||
if (taker_charge_rate == QUALITY_ONE)
|
||||
{
|
||||
amount = offer.quality ().ceil_in (amount, taker_funds);
|
||||
}
|
||||
else
|
||||
{
|
||||
Amount const taker_charge (amountFromRate (taker_charge_rate));
|
||||
amount = offer.quality ().ceil_in (amount,
|
||||
divide (taker_funds, taker_charge));
|
||||
}
|
||||
|
||||
// Best flow the owner can get.
|
||||
// Start out assuming entire offer will flow.
|
||||
Amounts owner_amount (amount);
|
||||
|
||||
// Limit owner's output by available funds less fees
|
||||
Amount const owner_funds (view ().accountFunds (
|
||||
offer.account (), owner_amount.out, fhZERO_IF_FROZEN));
|
||||
|
||||
// Get fee rate paid by owner
|
||||
std::uint32_t const owner_charge_rate (rippleTransferRate (view (),
|
||||
offer.account (), taker, amount.out.getIssuer()));
|
||||
|
||||
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
|
||||
{
|
||||
Amount const owner_charge (amountFromRate (owner_charge_rate));
|
||||
owner_amount = offer.quality ().ceil_out (owner_amount,
|
||||
divide (owner_funds, owner_charge));
|
||||
}
|
||||
|
||||
// Calculate the amount that will flow through the offer
|
||||
// This does not include the fees.
|
||||
return (owner_amount.in < amount.in)
|
||||
? owner_amount
|
||||
: amount;
|
||||
}
|
||||
|
||||
// Adjust an offer to indicate that we are consuming some (or all) of it.
|
||||
void
|
||||
Taker::consume (Offer const& offer, Amounts const& consumed) const
|
||||
{
|
||||
Amounts const& remaining (offer.amount ());
|
||||
|
||||
assert (remaining.in > zero && remaining.out > zero);
|
||||
assert (remaining.in >= consumed.in && remaining.out >= consumed.out);
|
||||
|
||||
offer.entry ()->setFieldAmount (sfTakerPays, remaining.in - consumed.in);
|
||||
offer.entry ()->setFieldAmount (sfTakerGets, remaining.out - consumed.out);
|
||||
|
||||
view ().entryModify (offer.entry());
|
||||
|
||||
assert (offer.entry ()->getFieldAmount (sfTakerPays) >= zero);
|
||||
assert (offer.entry ()->getFieldAmount (sfTakerGets) >= zero);
|
||||
}
|
||||
|
||||
// Fill a direct offer.
|
||||
// @param offer the offer we are going to use.
|
||||
// @param amount the amount to flow through the offer.
|
||||
// @returns: tesSUCCESS if successful, or an error code otherwise.
|
||||
TER
|
||||
Taker::fill (Offer const& offer, Amounts const& amount)
|
||||
{
|
||||
consume (offer, amount);
|
||||
|
||||
// Pay the taker, then the owner
|
||||
TER result = view ().accountSend (offer.account(), account(), amount.out);
|
||||
|
||||
if (result == tesSUCCESS)
|
||||
result = view ().accountSend (account(), offer.account(), amount.in);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Fill a bridged offer.
|
||||
// @param leg1 the first leg we are going to use.
|
||||
// @param amount1 the amount to flow through the first leg of the offer.
|
||||
// @param leg2 the second leg we are going to use.
|
||||
// @param amount2 the amount to flow through the second leg of the offer.
|
||||
// @return tesSUCCESS if successful, or an error code otherwise.
|
||||
TER
|
||||
Taker::fill (
|
||||
Offer const& leg1, Amounts const& amount1,
|
||||
Offer const& leg2, Amounts const& amount2)
|
||||
{
|
||||
assert (amount1.out == amount2.in);
|
||||
|
||||
consume (leg1, amount1);
|
||||
consume (leg2, amount2);
|
||||
|
||||
/* It is possible that m_account is the same as leg1.account, leg2.account
|
||||
* or both. This could happen when bridging over one's own offer. In that
|
||||
* case, accountSend won't actually do a send, which is what we want.
|
||||
*/
|
||||
TER result = view ().accountSend (m_account, leg1.account (), amount1.in);
|
||||
|
||||
if (result == tesSUCCESS)
|
||||
result = view ().accountSend (leg1.account (), leg2.account (), amount1.out);
|
||||
|
||||
if (result == tesSUCCESS)
|
||||
result = view ().accountSend (leg2.account (), m_account, amount2.out);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
Taker::done () const
|
||||
{
|
||||
if (m_options.sell && (m_remain.in <= zero))
|
||||
{
|
||||
// Sell semantics: we consumed all the input currency
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!m_options.sell && (m_remain.out <= zero))
|
||||
{
|
||||
// Buy semantics: we received the desired amount of output currency
|
||||
return true;
|
||||
}
|
||||
|
||||
// We are finished if the taker is out of funds
|
||||
return view().accountFunds (
|
||||
account(), m_remain.in, fhZERO_IF_FROZEN) <= zero;
|
||||
}
|
||||
|
||||
TER
|
||||
Taker::cross (Offer const& offer)
|
||||
{
|
||||
assert (!done ());
|
||||
|
||||
/* Before we call flow we must set the limit right; for buy semantics we
|
||||
need to clamp the output. And we always want to clamp the input.
|
||||
*/
|
||||
Amounts limit (offer.amount());
|
||||
|
||||
if (! m_options.sell)
|
||||
limit = offer.quality ().ceil_out (limit, m_remain.out);
|
||||
limit = offer.quality().ceil_in (limit, m_remain.in);
|
||||
|
||||
assert (limit.in <= offer.amount().in);
|
||||
assert (limit.out <= offer.amount().out);
|
||||
assert (limit.in <= m_remain.in);
|
||||
|
||||
Amounts const amount (flow (limit, offer, account ()));
|
||||
|
||||
m_remain.out -= amount.out;
|
||||
m_remain.in -= amount.in;
|
||||
|
||||
assert (m_remain.in >= zero);
|
||||
return fill (offer, amount);
|
||||
}
|
||||
|
||||
TER
|
||||
Taker::cross (Offer const& leg1, Offer const& leg2)
|
||||
{
|
||||
assert (!done ());
|
||||
|
||||
assert (leg1.amount ().out.isNative ());
|
||||
assert (leg2.amount ().in.isNative ());
|
||||
|
||||
Amounts amount1 (leg1.amount());
|
||||
Amounts amount2 (leg2.amount());
|
||||
|
||||
if (m_options.sell)
|
||||
amount1 = leg1.quality().ceil_in (amount1, m_remain.in);
|
||||
else
|
||||
amount2 = leg2.quality().ceil_out (amount2, m_remain.out);
|
||||
|
||||
if (amount1.out <= amount2.in)
|
||||
amount2 = leg2.quality().ceil_in (amount2, amount1.out);
|
||||
else
|
||||
amount1 = leg1.quality().ceil_out (amount1, amount2.in);
|
||||
|
||||
assert (amount1.out == amount2.in);
|
||||
|
||||
// As written, flow can't handle a 3-party transfer, but this works for
|
||||
// us because the output of leg1 and the input leg2 are XRP.
|
||||
Amounts flow1 (flow (amount1, leg1, m_account));
|
||||
|
||||
amount2 = leg2.quality().ceil_in (amount2, flow1.out);
|
||||
|
||||
Amounts flow2 (flow (amount2, leg2, m_account));
|
||||
|
||||
m_remain.out -= amount2.out;
|
||||
m_remain.in -= amount1.in;
|
||||
|
||||
return fill (leg1, flow1, leg2, flow2);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/ripple/unity/legacy.cpp
Normal file
28
src/ripple/unity/legacy.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
|
||||
#include <ripple/legacy/0.27/book/impl/BookTip.cpp>
|
||||
#include <ripple/legacy/0.27/book/impl/OfferStream.cpp>
|
||||
#include <ripple/legacy/0.27/book/impl/Quality.cpp>
|
||||
#include <ripple/legacy/0.27/book/impl/Taker.cpp>
|
||||
#include <ripple/legacy/0.27/CreateOffer.cpp>
|
||||
#include <ripple/legacy/0.27/Emulate027.cpp>
|
||||
|
||||
Reference in New Issue
Block a user