Autobridging future support:

* Refactor and cleanup transactors
* Introduce new direct and bridged transactors
* Rename existing transactor to indicate legacy status
* New direct transactor defaults to being turned off (preserve legacy behavior)
This commit is contained in:
Nik Bougalis
2014-03-30 17:44:06 -07:00
committed by Vinnie Falco
parent 1a9fbab165
commit bee12fb89d
31 changed files with 2049 additions and 186 deletions

View File

@@ -34,6 +34,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\common\ripple_common.cpp" />
<ClCompile Include="..\..\src\ripple\common\tests\cross_offer.test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\http\impl\Port.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
@@ -884,27 +890,57 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\AccountSetTransactor.cpp">
<ClCompile Include="..\..\src\ripple_app\transactors\SetAccount.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\ChangeTransactor.cpp">
<ClCompile Include="..\..\src\ripple_app\transactors\Change.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\OfferCancelTransactor.cpp">
<ClCompile Include="..\..\src\ripple_app\transactors\CancelOffer.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\OfferCreateTransactor.cpp">
<ClCompile Include="..\..\src\ripple_app\transactors\CreateOffer.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\PaymentTransactor.cpp">
<ClCompile Include="..\..\src\ripple_app\transactors\CreateOfferDirect.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\RegularKeySetTransactor.cpp">
<ClCompile Include="..\..\src\ripple_app\transactors\CreateOfferBridged.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\CreateOfferLegacy.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\Payment.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\SetRegularKey.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
@@ -912,11 +948,15 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\TrustSetTransactor.cpp">
<ClCompile Include="..\..\src\ripple_app\transactors\SetTrust.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\WalletAddTransactor.cpp">
<ClCompile Include="..\..\src\ripple_app\transactors\AddWallet.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
@@ -2107,15 +2147,18 @@
<ClInclude Include="..\..\src\ripple_app\shamap\SHAMapSyncFilters.h" />
<ClInclude Include="..\..\src\ripple_app\shamap\RadixMapTest.h" />
<ClInclude Include="..\..\src\ripple_app\shamap\SHAMapTreeNode.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\AccountSetTransactor.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\ChangeTransactor.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\OfferCancelTransactor.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\OfferCreateTransactor.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\PaymentTransactor.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\RegularKeySetTransactor.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\SetAccount.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\Change.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\CancelOffer.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\CreateOffer.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\CreateOfferDirect.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\CreateOfferBridged.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\CreateOfferOriginal.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\Payment.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\SetRegularKey.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\Transactor.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\TrustSetTransactor.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\WalletAddTransactor.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\SetTrust.h" />
<ClInclude Include="..\..\src\ripple_app\transactors\AddWallet.h" />
<ClInclude Include="..\..\src\ripple_app\tx\LocalTxs.h" />
<ClInclude Include="..\..\src\ripple_app\tx\Transaction.h" />
<ClInclude Include="..\..\src\ripple_app\tx\TransactionAcquire.h" />

View File

@@ -1455,31 +1455,40 @@
<ClCompile Include="..\..\src\ripple_app\tx\TxQueueEntry.cpp">
<Filter>[2] Old Ripple\ripple_app\tx</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\AccountSetTransactor.cpp">
<ClCompile Include="..\..\src\ripple_app\transactors\SetAccount.cpp">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\ChangeTransactor.cpp">
<ClCompile Include="..\..\src\ripple_app\transactors\Change.cpp">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\OfferCancelTransactor.cpp">
<ClCompile Include="..\..\src\ripple_app\transactors\CancelOffer.cpp">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\OfferCreateTransactor.cpp">
<ClCompile Include="..\..\src\ripple_app\transactors\CreateOffer.cpp">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\PaymentTransactor.cpp">
<ClCompile Include="..\..\src\ripple_app\transactors\CreateOfferBridged.cpp">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\RegularKeySetTransactor.cpp">
<ClCompile Include="..\..\src\ripple_app\transactors\CreateOfferDirect.cpp">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\CreateOfferLegacy.cpp">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\Payment.cpp">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\SetRegularKey.cpp">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\Transactor.cpp">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\TrustSetTransactor.cpp">
<ClCompile Include="..\..\src\ripple_app\transactors\SetTrust.cpp">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\transactors\WalletAddTransactor.cpp">
<ClCompile Include="..\..\src\ripple_app\transactors\AddWallet.cpp">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\common\impl\MultiSocket.cpp">
@@ -3218,31 +3227,40 @@
<ClInclude Include="..\..\src\ripple_app\tx\TxQueueEntry.h">
<Filter>[2] Old Ripple\ripple_app\tx</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple_app\transactors\AccountSetTransactor.h">
<ClInclude Include="..\..\src\ripple_app\transactors\SetAccount.h">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple_app\transactors\ChangeTransactor.h">
<ClInclude Include="..\..\src\ripple_app\transactors\Change.h">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple_app\transactors\OfferCancelTransactor.h">
<ClInclude Include="..\..\src\ripple_app\transactors\CancelOffer.h">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple_app\transactors\OfferCreateTransactor.h">
<ClInclude Include="..\..\src\ripple_app\transactors\CreateOffer.h">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple_app\transactors\PaymentTransactor.h">
<ClInclude Include="..\..\src\ripple_app\transactors\CreateOfferDirect.h">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple_app\transactors\RegularKeySetTransactor.h">
<ClInclude Include="..\..\src\ripple_app\transactors\CreateOfferBridged.h">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple_app\transactors\CreateOfferOriginal.h">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple_app\transactors\Payment.h">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple_app\transactors\SetRegularKey.h">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple_app\transactors\Transactor.h">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple_app\transactors\TrustSetTransactor.h">
<ClInclude Include="..\..\src\ripple_app\transactors\SetTrust.h">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple_app\transactors\WalletAddTransactor.h">
<ClInclude Include="..\..\src\ripple_app\transactors\AddWallet.h">
<Filter>[2] Old Ripple\ripple_app\transactors</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\common\impl\MultiSocketType.h">

View File

@@ -470,3 +470,103 @@ rippled = env.Program('build/rippled', OBJECT_FILES)
tags = env.CTags('tags', TAG_SRCS)
Default(rippled, tags)
#-------------------------------------------------------------------------------
# Returns the list of libraries needed by the test source file. This is
# accomplished by scanning the source file for a special comment line
# with this format, which must match exactly:
#
# // LIBS: <name>...
#
# path = path to source file
#
def get_libs(path):
prefix = '// LIBS:'
with open(path, 'rb') as f:
for line in f:
line = line.strip()
if line.startswith(prefix):
items = line.split(prefix, 1)[1].strip()
return [x.strip() for x in items.split(' ')]
# Returns the list of source modules needed by the test source file. This
#
# // MODULES: <module>...
#
# path = path to source file
#
def get_mods(path):
prefix = '// MODULES:'
with open(path, 'rb') as f:
for line in f:
line = line.strip()
if line.startswith(prefix):
items = line.split(prefix, 1)[1].strip()
items = [os.path.normpath(os.path.join(
os.path.dirname(path), x.strip())) for
x in items.split(' ')]
return items
# Build a stand alone executable that runs
# all the test suites in one source file
#
def build_test(env,path):
libs = get_libs(path)
mods = get_mods(path)
bin = os.path.basename(os.path.splitext(path)[0])
bin = os.path.join ("build", bin)
srcs = ['src/beast/beast/unit_test/tests/main.cpp']
srcs.append (path)
if mods:
srcs.extend (mods)
# All paths get normalized here, so we can use posix
# forward slashes for everything including on Windows
srcs = [os.path.normpath(os.path.join ('build', x)) for x in srcs]
objs = [os.path.splitext(x)[0]+'.o' for x in srcs]
env_ = env
if libs:
env_.Append(LIBS = libs)
env_.Program (bin, srcs)
#-------------------------------------------------------------------------------
def main():
env = Environment()
env['PRINT_CMD_LINE_FUNC'] = print_cmd_line
env.VariantDir (os.path.join ('build', 'src'), 'src', duplicate=0)
# Copy important os environment variables into env
if os.environ.get ('CC', None):
env.Replace (CC = os.environ['CC'])
if os.environ.get ('CXX', None):
env.Replace (CXX = os.environ['CXX'])
if os.environ.get ('PATH', None):
env.Replace (PATH = os.environ['PATH'])
# Set up boost variables
home = os.environ.get("BOOST_HOME", None)
if home is not None:
env.Prepend (CPPPATH = home)
env.Append (LIBPATH = os.path.join (home, 'stage', 'lib'))
# Set up flags
env.Append(CXXFLAGS = [
'-std=c++11',
'-frtti',
'-g'
])
for root, dirs, files in os.walk('src/ripple'):
for path in files:
path = os.path.join(root,path)
if (path.endswith(".test.cpp")):
build_test(env,path)
print_build_config (env)
main()

View File

@@ -215,4 +215,11 @@ This determines whether to add any features to the proposed transaction set.
#define RIPPLE_PROPOSE_AMENDMENTS 0
#endif
/** Config: RIPPLE_USE_OLD_CREATE_TRANSACTOR
This determines whether ripple uses the legacy OfferCreate transactor.
*/
#ifndef RIPPLE_USE_OLD_CREATE_TRANSACTOR
#define RIPPLE_USE_OLD_CREATE_TRANSACTOR 1
#endif
#endif

View File

@@ -0,0 +1,820 @@
//------------------------------------------------------------------------------
/*
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 "../../../beast/beast/unit_test/suite.h"
#include "../../../beast/beast/utility/noexcept.h"
#include <algorithm>
#include <cassert>
#include <iostream>
#include <iterator>
#include "../../../beast/beast/cxx14/type_traits.h" // <type_traits>
#include <utility>
#include <vector>
namespace ripple {
namespace core {
/** Represents both a quality and amounts of currencies for trade.
Quality is the ratio of output currency to input currency, where
higher means better for the offer taker. The first element of
the pair is the amount of currency available for input into
the offer. The second element of the pair is the amount of currency
that comes out if the full amount of the input currency is provided.
This pair also represents a fraction, for example for specifying a
minimum quality.
Offer requirements:
`X` is the type `Offer`
`a`, `b` are values of type `X`
`X::amount_type`
The return type of `in` and `out`
`X a;`
Constructs an uninitialized offer
`a.in();`
`a.out();
*/
/** Returns `true` if the offer is consumed. */
template <class Offer>
bool
is_offer_consumed (Offer const& offer) noexcept
{
assert ((offer.in() != 0 && offer.out() != 0) ||
(offer.in() == 0 && offer.out() == 0));
return offer.in() == 0 || offer.out() == 0;
}
//------------------------------------------------------------------------------
template <class Amount>
struct AmountTraits
{
/** Returns `true` if `lhs` is of lower quality than `rhs`. */
template <class Offer>
static
bool
less (Offer const& lhs, Offer const& rhs) noexcept
{
assert (! is_offer_consumed (lhs));
assert (! is_offer_consumed (rhs));
assert (lhs.out() != 0);
assert (rhs.out() != 0);
return (lhs.out() / lhs.in()) < (rhs.out() / rhs.in());
}
/** Calculates the result of multiplying amount by the implied ratio. */
template <class Offer>
static
typename Offer::amount_type
multiply (typename Offer::amount_type const& amount, Offer const& rate)
{
// Avoid math when result is exact
if (amount == rate.in())
return rate.out();
typename Offer::amount_type const result (
amount * (rate.out() / rate.in()));
if (result > rate.out())
return rate.out();
return result;
}
template <class Offer>
static
Offer
inverse (Offer const& value) noexcept
{
return Offer (value.out(), value.in());
}
};
/** Returns the offer that would result if the input amount is applied. */
template <class Offer>
Offer
consume_offer (
typename Offer::amount_type const& input,
Offer offer)
{
using Amount = typename Offer::amount_type;
// We need to calculate the most that we can take:
Amount const input_used (std::min (input, offer.in()));
Amount const output (AmountTraits<Amount>::multiply (input_used, offer));
offer.in() -= input_used;
offer.out() -= output;
return offer;
}
/** Fills an order amount in an order book.
@return The resulting amount of currency in and out.
*/
template <class BookIter>
typename std::iterator_traits<BookIter>::value_type
cross_offer_in (
typename std::iterator_traits<BookIter>::value_type::amount_type const& in,
typename std::iterator_traits<BookIter>::value_type const& minimum_quality,
BookIter first, BookIter last)
{
using Amount =
typename std::iterator_traits<BookIter>::value_type::amount_type;
using Offer =
typename std::iterator_traits<BookIter>::value_type;
Offer result {0, 0};
Amount remain (in);
for (auto iter (first); (result.in() < in) && (iter != last); ++iter)
{
Offer const offer (*iter);
if (AmountTraits<Amount>::less (offer, minimum_quality))
break;
Offer const offer_out (consume_offer (remain, offer));
result.in() += offer.in() - offer_out.in();
result.out() += offer.out() - offer_out.out();
*iter = offer_out;
}
return result;
}
//------------------------------------------------------------------------------
/** Returns the composite A->C from offers A->B and B->C, equal liquidity. */
template <class Offer>
Offer
make_bridged_offer (Offer const& leg1, Offer const& leg2)
{
using Amount = typename Offer::amount_type;
// Skip math if both legs can be fully consumed
if (leg1.out() == leg2.in())
return Offer (leg1.in(), leg2.out());
// If leg2 has less liquidity, scale down by leg2
if (leg1.out() > leg2.in())
return Offer (
AmountTraits<Amount>::multiply (leg2.in(),
AmountTraits<Amount>::inverse (leg1)),
leg2.out());
// leg1 has less liquidity
return Offer (leg1.in(),
AmountTraits<Amount>::multiply (leg1.out(), leg2));
}
namespace detail {
/** Presents a set of order books as a single bridged order book. */
template <class BookIter>
class MultiBookIterator
{
private:
using Amount =
typename std::iterator_traits<BookIter>::value_type::amount_type;
using Offer =
typename std::iterator_traits<BookIter>::value_type;
typedef std::is_const <
typename std::iterator_traits<BookIter>::reference
> IsConst;
struct forward_iterator_base_tag
: std::output_iterator_tag
, std::forward_iterator_tag
{
forward_iterator_base_tag()
{
}
};
struct forward_const_iterator_base_tag
: std::forward_iterator_tag
{
forward_const_iterator_base_tag()
{
}
};
template <class>
friend class MultiBookIterator;
BookIter m_direct;
BookIter m_direct_end;
BookIter m_leg1;
BookIter m_leg1_end;
BookIter m_leg2;
BookIter m_leg2_end;
bool m_bridged;
Offer m_offer;
class Proxy : public Offer
{
private:
bool m_bridged;
BookIter m_direct;
BookIter m_leg1;
BookIter m_leg2;
public:
explicit Proxy (BookIter direct)
: Offer (*direct)
, m_bridged (false)
, m_direct (direct)
{
}
Proxy (BookIter leg1, BookIter leg2)
: Offer (make_bridged_offer (*leg1, *leg2))
, m_bridged (true)
, m_leg1 (leg1)
, m_leg2 (leg2)
{
}
Proxy&
operator= (Offer const& offer)
{
if (m_bridged)
{
Offer const result (consume_offer (
offer.in(), *m_leg1));
*m_leg2 = consume_offer (
m_leg1->in() - result.in(), *m_leg2);
*m_leg1 = result;
}
else
{
*m_direct = offer;
}
((Offer&)*this) = offer;
return *this;
}
};
// Returns true if this iterator has reached the one past the end marker
bool
past_end() const noexcept
{
return
(m_direct == m_direct_end) &&
(m_leg1 == m_leg1_end || m_leg2 == m_leg2_end);
}
void
throw_if_past()
{
if (past_end())
throw std::out_of_range ("invalid iterator dereferenced");
}
// Returns true if the iterators are both equal, or both past the end
template <class Iter1, class Iter2>
static
bool
iter_eq (Iter1 iter1, Iter1 iter1_end,
Iter2 iter2, Iter2 iter2_end) noexcept
{
if (iter1 == iter1_end)
return iter2 == iter2_end;
if (iter2 == iter2_end)
return false;
return iter1 == iter2;
}
// Stores the best offer (if any) in m_offer
void
calc_offer()
{
if (past_end())
return;
// FIXME rewrite this - having 3 nested if's is overkill...
if ((m_leg1 != m_leg1_end) && (m_leg2 != m_leg2_end))
{
Offer const bridged (
make_bridged_offer (*m_leg1, *m_leg2));
if (m_direct != m_direct_end)
{
if (AmountTraits<Amount>::less (*m_direct, bridged))
{
m_bridged = true;
m_offer = bridged;
}
else
{
m_bridged = false;
m_offer = *m_direct;
}
}
else
{
m_bridged = true;
m_offer = bridged;
}
}
else
{
m_bridged = false;
m_offer = *m_direct;
}
}
public:
typedef std::ptrdiff_t difference_type;
typedef typename std::iterator_traits <
BookIter>::value_type value_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef std::conditional_t <
std::is_const <std::remove_reference_t <reference>>::value,
forward_const_iterator_base_tag,
forward_iterator_base_tag> iterator_category;
MultiBookIterator () = default;
template <
class OtherBookIter,
class = std::enable_if_t <
std::is_same <value_type, typename OtherBookIter::value_type>::value
>
>
MultiBookIterator (MultiBookIterator <OtherBookIter> const& other)
: m_direct (other.m_direct)
, m_direct_end (other.m_direct_end)
, m_leg1 (other.m_leg1)
, m_leg1_end (other.m_leg1_end)
, m_leg2 (other.m_leg2)
, m_leg2_end (other.m_leg2_end)
{
}
template <
class OtherBookIter,
class = std::enable_if_t <
std::is_same <value_type, typename OtherBookIter::value_type>::value
>
>
MultiBookIterator (
OtherBookIter direct_first, OtherBookIter direct_last,
OtherBookIter leg1_first, OtherBookIter leg1_last,
OtherBookIter leg2_first, OtherBookIter leg2_last)
: m_direct (direct_first)
, m_direct_end (direct_last)
, m_leg1 (leg1_first)
, m_leg1_end (leg1_last)
, m_leg2 (leg2_first)
, m_leg2_end (leg2_last)
{
calc_offer();
}
MultiBookIterator&
operator++()
{
throw_if_past ();
if (m_direct != m_direct_end)
++m_direct;
if (m_leg1 != m_leg1_end)
++m_leg1;
if (m_leg2 != m_leg2_end)
++m_leg2;
calc_offer();
return *this;
}
MultiBookIterator
operator++(int)
{
MultiBookIterator prev (*this);
this->operator++();
return prev;
}
template <class OtherBookIter>
bool
operator== (
MultiBookIterator <OtherBookIter> const& other) const noexcept
{
if (! iter_eq (m_direct, m_direct_end,
other.m_direct, other.m_direct_end))
return false;
if (! iter_eq (m_leg1, m_leg1_end,
other.m_leg1, other.m_leg1_end))
return false;
if (! iter_eq (m_leg2, m_leg2_end,
other.m_leg2, other.m_leg2_end))
return false;
return true;
}
Offer const*
operator->() const
{
throw_if_past();
return &m_offer;
}
Offer const&
operator*() const
{
throw_if_past();
return m_offer;
}
#ifndef _MSC_VER
// This blows up Visual Studio
template <
bool MaybeConst = IsConst::value,
class = std::enable_if_t <! MaybeConst>
>
#endif
Proxy
operator*()
{
static_assert (! IsConst::value,
"invalid call of non-const member function");
throw_if_past();
if (m_bridged)
return Proxy (m_leg1, m_leg2);
return Proxy (m_direct);
}
};
template <class LeftBookIter, class RightBookIter>
bool
operator!= (
MultiBookIterator <LeftBookIter> const& lhs,
MultiBookIterator <RightBookIter> const& rhs) noexcept
{
return !(rhs == lhs);
}
} // detail
//------------------------------------------------------------------------------
// TODO Allow const Book
//
template <class Book>
class MultiBook
{
private:
static_assert (! std::is_const <Book>::value,
"Book cannot be const");
std::reference_wrapper<Book> m_direct;
std::reference_wrapper<Book> m_leg1;
std::reference_wrapper<Book> m_leg2;
public:
typedef typename Book::value_type value_type;
typedef typename Book::reference reference;
typedef typename Book::const_reference const_reference;
typedef detail::MultiBookIterator <
typename Book::iterator> iterator;
typedef detail::MultiBookIterator <
typename Book::const_iterator> const_iterator;
typedef typename Book::difference_type difference_type;
typedef typename Book::size_type size_type;
MultiBook (Book& direct, Book& leg1, Book& leg2)
: m_direct (direct)
, m_leg1 (leg1)
, m_leg2 (leg2)
{
}
bool
empty() const noexcept
{
return cbegin() == cend();
}
// Complexity: linear
size_type
size() const noexcept
{
return std::distance (cbegin(), cend());
}
iterator
begin() noexcept
{
return iterator (
m_direct.get().begin(), m_direct.get().end(),
m_leg1.get().begin(), m_leg1.get().end(),
m_leg2.get().begin(), m_leg2.get().end());
}
iterator
end() noexcept
{
return iterator (
m_direct.get().end(), m_direct.get().end(),
m_leg1.get().end(), m_leg1.get().end(),
m_leg2.get().end(), m_leg2.get().end());
}
const_iterator
begin() const noexcept
{
return const_iterator (
m_direct.get().cbegin(), m_direct.get().cend(),
m_leg1.get().cbegin(), m_leg1.get().cend(),
m_leg2.get().cbegin(), m_leg2.get().cend());
}
const_iterator
end() const noexcept
{
return const_iterator (
m_direct.get().cend(), m_direct.get().cend(),
m_leg1.get().cend(), m_leg1.get().cend(),
m_leg2.get().cend(), m_leg2.get().cend());
}
const_iterator
cbegin() const noexcept
{
return const_iterator (
m_direct.get().cbegin(), m_direct.get().cend(),
m_leg1.get().cbegin(), m_leg1.get().cend(),
m_leg2.get().cbegin(), m_leg2.get().cend());
}
const_iterator
cend() const noexcept
{
return const_iterator (
m_direct.get().cend(), m_direct.get().cend(),
m_leg1.get().cend(), m_leg1.get().cend(),
m_leg2.get().cend(), m_leg2.get().cend());
}
};
//------------------------------------------------------------------------------
template <class Book>
typename std::iterator_traits<typename Book::iterator>::value_type
cross_offer_in (
typename std::iterator_traits<
typename Book::iterator>::value_type::amount_type const& in,
typename std::iterator_traits <
typename Book::iterator>::value_type const& minimum_quality,
Book& book)
{
return cross_offer_in (in, minimum_quality, book.begin(), book.end());
}
template <class Book>
typename std::iterator_traits<typename Book::iterator>::value_type
cross_offer_in (
typename std::iterator_traits<
typename Book::iterator>::value_type::amount_type const& in,
typename std::iterator_traits<typename Book::iterator>::value_type
const& minimum_quality,
Book& direct, Book& leg1, Book& leg2)
{
MultiBook <Book> book (direct, leg1, leg2);
return cross_offer_in (in, minimum_quality, book);
}
//------------------------------------------------------------------------------
class cross_offers_test : public beast::unit_test::suite
{
public:
template <class Amount>
class OfferType
{
private:
Amount m_in;
Amount m_out;
public:
typedef Amount amount_type;
OfferType() = default;
OfferType (Amount const& in, Amount const& out) noexcept
: m_in (in)
, m_out (out)
{
assert ((m_in != 0 && m_out != 0) || (m_in == 0 && m_out == 0));
}
Amount const&
in() const noexcept
{
return m_in;
}
Amount&
in() noexcept
{
return m_in;
}
Amount const&
out() const noexcept
{
return m_out;
}
Amount&
out() noexcept
{
return m_out;
}
};
typedef double Amount;
typedef OfferType<Amount> Offer;
typedef std::vector <Offer> Book;
template <class Amount>
OfferType<Amount>
static make_offer (Amount from, Amount rate)
{
return OfferType<Amount> (from, from * rate);
}
template <class Book>
void
check_iterators (Book& b)
{
using Offer = typename Book::value_type;
// These make sure that expressions are well-formed
std::for_each (b.begin(), b.end(), [](Offer){});
std::for_each (b.cbegin(), b.cend(), [](Offer){});
{
Book const& cb (b);
std::for_each (cb.begin(), cb.end(), [](Offer){});
// Should not compile
//*cb.begin() = Book::value_type();
expect (cb.begin() == cb.end());
expect (cb.begin() == cb.cend());
}
expect (b.cbegin() == b.cend());
expect (b.begin() == b.cend());
typename Book::iterator iter;
typename Book::const_iterator citer (iter);
citer = typename Book::iterator();
}
void
test_iterators()
{
{
Book b;
check_iterators (b);
}
{
Book b1, b2, b3;
MultiBook <Book> b (b1, b2, b3);
check_iterators (b);
}
}
void
test_full_cross_auto_direct ()
{
testcase ("Autobridge (Full Direct Crossing)");
Book a_to_b;
a_to_b.push_back(make_offer(300., 2.0));
Book a_to_x;
a_to_x.push_back(make_offer(300., 0.5));
Book x_to_b;
x_to_b.push_back(make_offer(150., 0.5));
auto const rate = make_offer(50.0, 1.5);
Offer result = cross_offer_in (
50.0,
rate,
a_to_b, a_to_x, x_to_b);
expect ((result.in() == 50.0) && (result.out() == 100.0),
"Expected { 50.0 : 100.0 }");
}
void
test_full_cross_auto_bridge ()
{
testcase ("Autobridge (Full Bridge Crossing)");
Book a_to_b;
a_to_b.push_back(make_offer(300.00, 1.0));
Book a_to_x;
a_to_x.push_back(make_offer(300.00, 2.0));
Book x_to_b;
x_to_b.push_back(make_offer(300.00, 1.0));
auto const rate = make_offer(50.0, 1.5);
Offer result = cross_offer_in (
50.0,
rate,
a_to_b, a_to_x, x_to_b);
expect ((result.in() == 50.0) && (result.out() == 100.0),
"Expected { 50.0 : 100.0 }");
}
void
test_full_cross_direct ()
{
testcase ("Direct (Full Crossing)");
Book a_to_b;
a_to_b.push_back(make_offer(300.00, 2.0));
auto const rate = make_offer(50.0, 1.5);
Offer result = cross_offer_in (
50.0,
rate,
a_to_b);
expect ((result.in() == 50.0) && (result.out() == 100.0),
"Expected { 50.0 : 100.0 }");
}
void
test_partial_cross_direct ()
{
testcase ("Direct (Partial Crossing)");
Book a_to_b;
a_to_b.push_back(make_offer(25.00, 2.0));
auto const rate = make_offer(50.0, 1.5);
Offer result = cross_offer_in (
50.0,
rate,
a_to_b);
expect ((result.in() == 25.0) && (result.out() == 50.0),
"Expected { 25.0 : 50.0 }");
}
void
run()
{
test_iterators();
test_full_cross_direct ();
test_full_cross_auto_direct ();
test_full_cross_auto_bridge ();
test_partial_cross_direct ();
}
};
BEAST_DEFINE_TESTSUITE_MANUAL(cross_offers,orderbook_logic,ripple);
}
}

View File

@@ -24,10 +24,13 @@
#ifdef BEAST_WIN32
# include <Winsock2.h> // for ByteOrder.cpp
// <Winsock2.h> defines 'max' and does other stupid things
// <Winsock2.h> defines min, max and does other stupid things
# ifdef max
# undef max
# endif
# ifdef min
# undef min
# endif
#endif
#include <set>
@@ -45,4 +48,5 @@
#include "impl/RippleIdentifierTests.cpp"
#include "impl/RippleAssets.cpp"
#include "../common/tests/cross_offer.test.cpp"

View File

@@ -43,11 +43,15 @@ public:
Options (std::uint32_t tx_flags)
: sell (is_bit_set (tx_flags, tfSell))
, passive (is_bit_set (tx_flags, tfPassive))
, fill_or_kill (is_bit_set (tx_flags, tfFillOrKill))
, immediate_or_cancel (is_bit_set (tx_flags, tfImmediateOrCancel))
{
}
bool sell;
bool passive;
bool const sell;
bool const passive;
bool const fill_or_kill;
bool const immediate_or_cancel;
};
private:
@@ -91,7 +95,7 @@ public:
// If this is a passive order (tfPassive), this prevents
// offers at the same quality level from being consumed.
if (m_options.passive)
--m_threshold;
++m_threshold;
}
LedgerView&
@@ -125,7 +129,7 @@ public:
{
// With the sell option, we are finished when
// we have consumed all the input currency.
if (! m_in.isPositive())
if (m_in <= zero)
return true;
}
else if (m_out >= m_amount.out)
@@ -136,14 +140,14 @@ public:
}
// We are finished if the taker is out of funds
return ! funds().isPositive();
return funds() <= zero;
}
Quality
threshold() const noexcept
{
return m_threshold;
}
Quality
threshold() const noexcept
{
return m_threshold;
}
/** Returns `true` if the quality does not meet the taker's requirements. */
bool
@@ -221,7 +225,7 @@ threshold() const noexcept
assert (m_out < m_amount.out);
taker_amount = offer.quality().ceil_out (
taker_amount, m_amount.out - m_out);
assert (! taker_amount.in.isZero());
assert (taker_amount.in != zero);
}
// Calculate the amount that will flow through the offer
@@ -251,6 +255,7 @@ threshold() const noexcept
// VFALCO For the case of !sell, is it possible for the taker
// to get a tiny bit more than he asked for?
// DAVIDS Can you verify?
assert (m_options.sell || flow.out <= m_amount.out);
// Calculate remaining portion of offer

View File

@@ -22,11 +22,18 @@
#include "ripple_app.h"
#include "transactors/Transactor.cpp"
#include "transactors/ChangeTransactor.cpp"
#include "transactors/OfferCancelTransactor.cpp"
#include "transactors/OfferCreateTransactor.cpp"
#include "transactors/PaymentTransactor.cpp"
#include "transactors/RegularKeySetTransactor.cpp"
#include "transactors/AccountSetTransactor.cpp"
#include "transactors/WalletAddTransactor.cpp"
#include "transactors/TrustSetTransactor.cpp"
#include "transactors/Change.cpp"
#include "transactors/CancelOffer.cpp"
#include "transactors/Payment.cpp"
#include "transactors/SetRegularKey.cpp"
#include "transactors/SetAccount.cpp"
#include "transactors/AddWallet.cpp"
#include "transactors/SetTrust.cpp"
#include "transactors/CreateOffer.cpp"
#include "transactors/CreateOfferDirect.cpp"
#include "transactors/CreateOfferBridged.cpp"
#if RIPPLE_USE_OLD_CREATE_TRANSACTOR
#include "transactors/CreateOfferLegacy.cpp"
#endif

View File

@@ -0,0 +1,68 @@
//------------------------------------------------------------------------------
/*
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 "CreateOfferDirect.h"
#include "CreateOfferBridged.h"
#if RIPPLE_USE_OLD_CREATE_TRANSACTOR
#include "CreateOfferLegacy.h"
#endif
#include "../book/OfferStream.h"
#include "../book/Taker.h"
namespace ripple {
OfferCreateTransactor::OfferCreateTransactor (
SerializedTransaction const& txn,
TransactionEngineParams params,
TransactionEngine* engine)
: Transactor (
txn,
params,
engine,
LogPartition::getJournal <OfferCreateTransactorLog> ())
{
}
std::unique_ptr <Transactor> make_OfferCreateTransactor (
SerializedTransaction const& txn,
TransactionEngineParams params,
TransactionEngine* engine)
{
#if RIPPLE_USE_OLD_CREATE_TRANSACTOR
return std::make_unique <ClassicOfferCreateTransactor> (txn, params, engine);
#else
STAmount const& amount_in = txn.getFieldAmount (sfTakerPays);
STAmount const& amount_out = txn.getFieldAmount (sfTakerGets);
// Autobridging is only in effect when an offer does not involve XRP
if (!amount_in.isNative() && !amount_out.isNative ())
{
// no autobridging transactor exists yet - we create a regular, direct
// transactor for now.
return std::make_unique <DirectOfferCreateTransactor> (txn, params, engine);
}
return std::make_unique <DirectOfferCreateTransactor> (txn, params, engine);
#endif
}
}

View File

@@ -0,0 +1,69 @@
//------------------------------------------------------------------------------
/*
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_TX_OFFERCREATE_H_INCLUDED
#define RIPPLE_TX_OFFERCREATE_H_INCLUDED
#include "../../beast/beast/cxx14/memory.h"
namespace ripple {
class OfferCreateTransactorLog;
template <>
char const*
LogPartition::getPartitionName <OfferCreateTransactorLog> ()
{
return "Tx/OfferCreate";
}
class OfferCreateTransactor
: public Transactor
{
private:
template <class T>
static std::string
get_compare_sign (T const& lhs, T const& rhs)
{
if (lhs > rhs)
return ">";
if (rhs > lhs)
return "<";
// If neither is bigger than the other, they must be equal
return "=";
}
public:
OfferCreateTransactor (
SerializedTransaction const& txn,
TransactionEngineParams params,
TransactionEngine* engine);
};
std::unique_ptr <Transactor> make_OfferCreateTransactor (
SerializedTransaction const& txn,
TransactionEngineParams params,
TransactionEngine* engine);
}
#endif

View File

@@ -0,0 +1,19 @@
//------------------------------------------------------------------------------
/*
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.
*/
//==============================================================================

View File

@@ -0,0 +1,23 @@
//------------------------------------------------------------------------------
/*
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_TX_BRIDGE_OFFERCREATE_H_INCLUDED
#define RIPPLE_TX_BRIDGE_OFFERCREATE_H_INCLUDED
#endif

View File

@@ -0,0 +1,688 @@
//------------------------------------------------------------------------------
/*
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 "../book/OfferStream.h"
#include "../book/Taker.h"
#include "../../beast/beast/streams/debug_ostream.h"
namespace ripple {
//------------------------------------------------------------------------------
// NIKB Move this in the right place
std::pair<TER,bool>
process_order (
core::LedgerView& view,
core::BookRef const book,
core::Account const& account,
core::Amounts const& amount,
core::Amounts& cross_flow,
core::Taker::Options const options,
core::Clock::time_point const when,
beast::Journal& journal)
{
TER result (tesSUCCESS);
core::LedgerView view_cancel (view.duplicate());
core::OfferStream offers (view, view_cancel, book, when, journal);
core::Taker taker (offers.view(), book, account, amount, options);
if (journal.debug) journal.debug <<
"process_order: " <<
(options.sell? "sell" : "buy") << " " <<
(options.passive? "passive" : "") << std::endl <<
" taker: " << taker.account() << std::endl <<
" balances: " <<
view.accountFunds (taker.account(), amount.in) << ", " <<
view.accountFunds (taker.account(), amount.out);
cross_flow.in.clear (amount.in);
cross_flow.out.clear (amount.out);
bool place_order (true);
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())
{
journal.debug << "The taker reports he's done during crossing!";
place_order = false;
break;
}
if (! offers.step())
{
// Place the order since there are no
// more offers and the order has a balance.
journal.debug << "No more offers to consider during crossing!";
break;
}
auto const offer (offers.tip());
if (journal.debug) journal.debug <<
"Considering offer: " << std::endl <<
" Id: " << offer.entry()->getIndex() << std::endl <<
" In: " << offer.amount().in << std::endl <<
" Out: " << offer.amount().out << std::endl <<
" By: " << offer.account();
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())
{
if (journal.debug) journal.debug <<
" skipping self-offer " << offer.entry()->getIndex() << std::endl <<
" pays/gets " << offer.amount().in << ", " << offer.amount().out << std::endl <<
" during cross for " << std::endl <<
" pays/gets " << amount.in << ", " << amount.out;
;
// Skip offer from self.
// (Offer will be considered expired, and get deleted)
continue;
}
if (journal.debug) journal.debug <<
" offer " << offer.entry()->getIndex() << std::endl <<
" pays/gets " << offer.amount().in << ", " << offer.amount().out
;
core::Amounts flow;
bool consumed;
std::tie (flow, consumed) = taker.fill (offer);
result = taker.process (flow, offer);
if (journal.debug) journal.debug <<
" flow " <<
flow.in << ", " << flow.out << std::endl <<
" balances " <<
view.accountFunds (taker.account(), amount.in) << ", " <<
view.accountFunds (taker.account(), amount.out)
;
if (result != tesSUCCESS)
{
// VFALCO TODO Return the tec and let a caller higher
// up convert the error if the ledger is open.
//result = bOpenLedger ?
// telFAILED_PROCESSING : tecFAILED_PROCESSING;
result = tecFAILED_PROCESSING;
break;
}
cross_flow.in += flow.in;
cross_flow.out += flow.out;
}
if (result == tesSUCCESS)
{
// No point in placing an offer for a fill-or-kill offer - the offer
// will not succeed, since it wasn't filled.
if (options.fill_or_kill)
place_order = false;
// An immediate or cancel order will fill however much it is possible
// to fill and the remainder is not filled.
if (options.immediate_or_cancel)
place_order = false;
}
if (result == tesSUCCESS)
{
if (place_order)
{
}
}
else
{
}
return std::make_pair(result,place_order);
}
/** Take as much as possible.
We adjusts account balances and charges fees on top to taker.
@param saTakerPays What the taker offers (w/ issuer)
@param saTakerGets What the taker wanted (w/ issuer)
@param saTakerPaid What taker could have paid including saved not including
fees. To reduce an offer.
@param saTakerGot What taker got not including fees. To reduce an offer.
@return tesSUCCESS, terNO_ACCOUNT, telFAILED_PROCESSING, or
tecFAILED_PROCESSING
*/
std::pair<TER,bool> DirectOfferCreateTransactor::crossOffers (
core::LedgerView& view,
const STAmount& saTakerPays,
const STAmount& saTakerGets,
STAmount& saTakerPaid,
STAmount& saTakerGot)
{
if (m_journal.debug) m_journal.debug << "takeOffers: ";
core::Book book (
core::AssetRef (
saTakerPays.getCurrency(), saTakerPays.getIssuer()),
core::AssetRef (
saTakerGets.getCurrency(), saTakerGets.getIssuer()));
core::Amounts cross_flow (
core::Amount (saTakerPays.getCurrency(), saTakerPays.getIssuer()),
core::Amount (saTakerGets.getCurrency(), saTakerGets.getIssuer()));
auto const result (process_order (
view, book, mTxnAccountID,
core::Amounts (saTakerPays, saTakerGets), cross_flow,
core::Taker::Options (mTxn.getFlags()),
mEngine->getLedger ()->getParentCloseTimeNC (),
m_journal));
core::Amounts const funds (
view.accountFunds (mTxnAccountID, saTakerPays),
view.accountFunds (mTxnAccountID, saTakerGets));
if (m_journal.debug) m_journal.debug << " cross_flow: " <<
cross_flow.in << ", " << cross_flow.out;
if (m_journal.debug) m_journal.debug << " balances: " <<
funds.in << ", " << funds.out;
saTakerPaid = cross_flow.in;
saTakerGot = cross_flow.out;
if (m_journal.debug) m_journal.debug <<
" result: " << transToken (result.first) <<
(result.second ? " (consumed)" : "");
return result;
}
TER DirectOfferCreateTransactor::doApply ()
{
if (m_journal.debug) m_journal.debug <<
"OfferCreate> " << mTxn.getJson (0);
std::uint32_t const uTxFlags = mTxn.getFlags ();
bool const bPassive = is_bit_set (uTxFlags, tfPassive);
bool const bImmediateOrCancel = is_bit_set (uTxFlags, tfImmediateOrCancel);
bool const bFillOrKill = is_bit_set (uTxFlags, tfFillOrKill);
bool const bSell = is_bit_set (uTxFlags, tfSell);
STAmount saTakerPays = mTxn.getFieldAmount (sfTakerPays);
STAmount saTakerGets = mTxn.getFieldAmount (sfTakerGets);
if (!saTakerPays.isLegalNet () || !saTakerGets.isLegalNet ())
return temBAD_AMOUNT;
uint160 const uPaysIssuerID = saTakerPays.getIssuer ();
uint160 const uGetsIssuerID = saTakerGets.getIssuer ();
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 = Ledger::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.";
}
uint160 const uPaysCurrency = saTakerPays.getCurrency ();
uint160 const uGetsCurrency = saTakerGets.getCurrency ();
std::uint64_t const uRate = STAmount::getRate (saTakerGets, saTakerPays);
TER terResult (tesSUCCESS);
uint256 uDirectory; // Delete hints.
std::uint64_t uOwnerNode;
std::uint64_t uBookNode;
// This is the ledger view that we work against. Transactions are applied
// as we go on processing transactions.
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.
core::LedgerView view_checkpoint (view);
view.bumpSeq (); // Begin ledger variance.
SLE::pointer sleCreator = mEngine->entryCache (
ltACCOUNT_ROOT, Ledger::getAccountRootIndex (mTxnAccountID));
if (uTxFlags & tfOfferCreateMask)
{
if (m_journal.debug) m_journal.debug <<
"Malformed transaction: Invalid flags set.";
return temINVALID_FLAG;
}
else if (bImmediateOrCancel && bFillOrKill)
{
if (m_journal.debug) m_journal.debug <<
"Malformed transaction: both IoC and FoK set.";
return 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 (CURRENCY_BAD == uPaysCurrency || CURRENCY_BAD == 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.accountFunds (mTxnAccountID, saTakerGets) <= 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 (tesSUCCESS != terResult)
{
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 ((tesSUCCESS == terResult) && bHaveCancel)
{
uint256 const uCancelIndex (
Ledger::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. Additionally, it
// might not even have been an offer - we don't care.
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;
}
// If all is well and this isn't an offer to XRP, then we make sure we are
// authorized to hold what the taker will pay.
if (tesSUCCESS == terResult && !saTakerPays.isNative ())
{
SLE::pointer sleTakerPays = mEngine->entryCache (
ltACCOUNT_ROOT, Ledger::getAccountRootIndex (uPaysIssuerID));
if (!sleTakerPays)
{
m_journal.warning <<
"delay: can't receive IOUs from non-existent issuer: " <<
RippleAddress::createHumanAccountID (uPaysIssuerID);
return is_bit_set (mParams, tapRETRY)
? terNO_ACCOUNT
: tecNO_ISSUER;
}
if (is_bit_set (sleTakerPays->getFieldU32 (sfFlags), lsfRequireAuth))
{
SLE::pointer sleRippleState (mEngine->entryCache (
ltRIPPLE_STATE,
Ledger::getRippleStateIndex (
mTxnAccountID, uPaysIssuerID, uPaysCurrency)));
if (!sleRippleState)
{
return is_bit_set (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 > uPaysIssuerID);
bool const need_auth (is_bit_set (
sleRippleState->getFieldU32 (sfFlags),
(canonical_gt ? lsfLowAuth : lsfHighAuth)));
if (need_auth)
{
if (m_journal.debug) m_journal.debug <<
"delay: can't receive IOUs from issuer without auth.";
return is_bit_set (mParams, tapRETRY)
? terNO_AUTH
: tecNO_AUTH;
}
}
}
STAmount saPaid;
STAmount saGot;
bool const bOpenLedger = is_bit_set (mParams, tapOPEN_LEDGER);
bool const placeOffer = true;
if (tesSUCCESS == terResult)
{
// Take using the parameters of the offer.
if (m_journal.debug) m_journal.debug <<
"takeOffers: BEFORE saTakerGets=" << saTakerGets.getFullText ();
auto ret = crossOffers (
view,
saTakerGets, // Reverse as we are the taker for taking.
saTakerPays,
saPaid, // Buy semantics: how much would have sold at full price. Sell semantics: how much was sold.
saGot); // How much was got.
terResult = ret.first;
if (terResult == tecFAILED_PROCESSING && bOpenLedger)
terResult = telFAILED_PROCESSING;
if (m_journal.debug)
{
m_journal.debug << "takeOffers=" << terResult;
m_journal.debug << "takeOffers: saPaid=" << saPaid.getFullText ();
m_journal.debug << "takeOffers: saGot=" << saGot.getFullText ();
}
if (tesSUCCESS == terResult)
{
// Reduce pay in from takers by what offer just got.
saTakerPays -= saGot;
// Reduce pay out to takers by what srcAccount just paid.
saTakerGets -= saPaid;
if (m_journal.debug)
{
m_journal.debug <<
"takeOffers: AFTER saTakerPays=" <<
saTakerPays.getFullText ();
m_journal.debug <<
"takeOffers: AFTER saTakerGets=" <<
saTakerGets.getFullText ();
}
}
}
if (m_journal.debug)
{
m_journal.debug <<
"takeOffers: saTakerPays=" <<saTakerPays.getFullText ();
m_journal.debug <<
"takeOffers: saTakerGets=" << saTakerGets.getFullText ();
m_journal.debug <<
"takeOffers: mTxnAccountID=" <<
RippleAddress::createHumanAccountID (mTxnAccountID);
m_journal.debug <<
"takeOffers: FUNDS=" <<
view.accountFunds (mTxnAccountID, saTakerGets).getFullText ();
}
if (tesSUCCESS != terResult)
{
m_journal.debug <<
"final terResult=" << transToken (terResult);
return terResult;
}
if (bFillOrKill && (saTakerPays || saTakerGets))
{
// Fill or kill and have leftovers.
view.swapWith (view_checkpoint); // Restore with just fees paid.
return tesSUCCESS;
}
if (!placeOffer
|| saTakerPays <= zero // Wants nothing more.
|| saTakerGets <= zero // Offering nothing more.
|| bImmediateOrCancel // Do not persist.
|| view.accountFunds (mTxnAccountID, saTakerGets) <= zero) // Not funded.
{
// Complete as is.
nothing ();
}
else if (mPriorBalance.getNValue () < mEngine->getLedger ()->getReserve (sleCreator->getFieldU32 (sfOwnerCount) + 1))
{
// 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 (!saPaid && !saGot)
{
// 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.
nothing ();
}
}
else
{
// 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 ();
// Add offer to owner's directory.
terResult = view.dirAdd (uOwnerNode,
Ledger::getOwnerDirIndex (mTxnAccountID), uLedgerIndex,
BIND_TYPE (&Ledger::ownerDirDescriber, P_1, P_2, mTxnAccountID));
if (tesSUCCESS == terResult)
{
// Update owner count.
view.ownerCountAdjust (mTxnAccountID, 1, sleCreator);
uint256 uBookBase = Ledger::getBookBase (
uPaysCurrency,
uPaysIssuerID,
uGetsCurrency,
uGetsIssuerID);
if (m_journal.debug) m_journal.debug <<
"adding to book: " << to_string (uBookBase) <<
" : " << saTakerPays.getHumanCurrency () <<
"/" << RippleAddress::createHumanAccountID (saTakerPays.getIssuer ()) <<
" -> " << saTakerGets.getHumanCurrency () <<
"/" << RippleAddress::createHumanAccountID (saTakerGets.getIssuer ());
// We use the original rate to place the offer.
uDirectory = Ledger::getQualityIndex (uBookBase, uRate);
// Add offer to order book.
terResult = view.dirAdd (uBookNode, uDirectory, uLedgerIndex,
BIND_TYPE (&Ledger::qualityDirDescriber, P_1, P_2,
saTakerPays.getCurrency (), uPaysIssuerID,
saTakerGets.getCurrency (), uGetsIssuerID, uRate));
}
if (tesSUCCESS == terResult)
{
if (m_journal.debug)
{
m_journal.debug <<
"sfAccount=" <<
RippleAddress::createHumanAccountID (mTxnAccountID);
m_journal.debug <<
"uPaysIssuerID=" <<
RippleAddress::createHumanAccountID (uPaysIssuerID);
m_journal.debug <<
"uGetsIssuerID=" <<
RippleAddress::createHumanAccountID (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 (tesSUCCESS != terResult)
{
m_journal.debug <<
"final terResult=" << transToken (terResult);
}
return terResult;
}
}

View File

@@ -0,0 +1,59 @@
//------------------------------------------------------------------------------
/*
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_TX_DIRECT_OFFERCREATE_H_INCLUDED
#define RIPPLE_TX_DIRECT_OFFERCREATE_H_INCLUDED
#include "../book/OfferStream.h"
#include "../book/Taker.h"
#include <unordered_set>
namespace ripple {
class DirectOfferCreateTransactor
: public OfferCreateTransactor
{
public:
DirectOfferCreateTransactor (
SerializedTransaction const& txn,
TransactionEngineParams params,
TransactionEngine* engine)
: OfferCreateTransactor (
txn,
params,
engine)
{
}
TER doApply () override;
private:
std::pair<TER,bool> crossOffers (
core::LedgerView& view,
STAmount const& saTakerPays,
STAmount const& saTakerGets,
STAmount& saTakerPaid,
STAmount& saTakerGot);
};
}
#endif

View File

@@ -22,7 +22,7 @@ namespace ripple {
/** Determine if an order is still valid
If the order is not valid it will be marked as unfunded.
*/
bool OfferCreateTransactor::isValidOffer (
bool ClassicOfferCreateTransactor::isValidOffer (
SLE::ref sleOffer,
uint160 const& uOfferOwnerID,
STAmount const& saOfferPays,
@@ -36,7 +36,7 @@ bool OfferCreateTransactor::isValidOffer (
sleOffer->getFieldU32 (sfExpiration) <= mEngine->getLedger ()->getParentCloseTimeNC ())
{
// Offer is expired. Expired offers are considered unfunded. Delete it.
m_journal.trace << "isValidOffer: encountered expired offer";
m_journal.debug << "isValidOffer: encountered expired offer";
usOfferUnfundedFound.insert (sleOffer->getIndex());
@@ -46,7 +46,7 @@ bool OfferCreateTransactor::isValidOffer (
if (uOfferOwnerID == uTakerAccountID)
{
// Would take own offer. Consider old offer expired. Delete it.
m_journal.trace << "isValidOffer: encountered taker's own old offer";
m_journal.debug << "isValidOffer: encountered taker's own old offer";
usOfferUnfundedFound.insert (sleOffer->getIndex());
@@ -65,7 +65,7 @@ bool OfferCreateTransactor::isValidOffer (
return false;
}
m_journal.trace <<
m_journal.debug <<
"isValidOffer: saOfferPays=" << saOfferPays.getFullText ();
saOfferFunds = mEngine->view ().accountFunds (uOfferOwnerID, saOfferPays);
@@ -97,7 +97,7 @@ bool OfferCreateTransactor::isValidOffer (
/**
*/
bool OfferCreateTransactor::canCross (
bool ClassicOfferCreateTransactor::canCross (
STAmount const& saTakerFunds,
STAmount const& saSubTakerPays,
STAmount const& saSubTakerGets,
@@ -189,7 +189,7 @@ bool OfferCreateTransactor::canCross (
books as: "give 0.57 BTC for 400 USD" which is what is left to sell at the
original rate.
*/
bool OfferCreateTransactor::applyOffer (
bool ClassicOfferCreateTransactor::applyOffer (
const bool bSell,
const std::uint32_t uTakerPaysRate, const std::uint32_t uOfferPaysRate,
const STAmount& saOfferRate,
@@ -210,17 +210,17 @@ bool OfferCreateTransactor::applyOffer (
? saOfferFunds // As is.
: STAmount::divide (saOfferFunds, STAmount (CURRENCY_ONE, ACCOUNT_ONE, uOfferPaysRate, -9)); // Reduce by offer fees.
WriteLog (lsINFO, STAmount) << "applyOffer: uOfferPaysRate=" << uOfferPaysRate;
WriteLog (lsINFO, STAmount) << "applyOffer: saOfferFundsAvailable=" << saOfferFundsAvailable.getFullText ();
m_journal.info << "applyOffer: uOfferPaysRate=" << uOfferPaysRate;
m_journal.info << "applyOffer: saOfferFundsAvailable=" << saOfferFundsAvailable.getFullText ();
// Limit taker funds available, by transfer fees.
STAmount saTakerFundsAvailable = QUALITY_ONE == uTakerPaysRate
? saTakerFunds // As is.
: STAmount::divide (saTakerFunds, STAmount (CURRENCY_ONE, ACCOUNT_ONE, uTakerPaysRate, -9)); // Reduce by taker fees.
WriteLog (lsINFO, STAmount) << "applyOffer: TAKER_FEES=" << STAmount (CURRENCY_ONE, ACCOUNT_ONE, uTakerPaysRate, -9).getFullText ();
WriteLog (lsINFO, STAmount) << "applyOffer: uTakerPaysRate=" << uTakerPaysRate;
WriteLog (lsINFO, STAmount) << "applyOffer: saTakerFundsAvailable=" << saTakerFundsAvailable.getFullText ();
m_journal.info << "applyOffer: TAKER_FEES=" << STAmount (CURRENCY_ONE, ACCOUNT_ONE, uTakerPaysRate, -9).getFullText ();
m_journal.info << "applyOffer: uTakerPaysRate=" << uTakerPaysRate;
m_journal.info << "applyOffer: saTakerFundsAvailable=" << saTakerFundsAvailable.getFullText ();
STAmount saOfferPaysAvailable; // Amount offer can pay out, limited by offer and offerer funds.
STAmount saOfferGetsAvailable; // Amount offer would get, limited by offer funds.
@@ -240,24 +240,24 @@ bool OfferCreateTransactor::applyOffer (
saOfferGetsAvailable = std::min (saOfferGets, STAmount::mulRound (saOfferPaysAvailable, saOfferRate, saOfferGets, true));
}
WriteLog (lsINFO, STAmount) << "applyOffer: saOfferPaysAvailable=" << saOfferPaysAvailable.getFullText ();
WriteLog (lsINFO, STAmount) << "applyOffer: saOfferGetsAvailable=" << saOfferGetsAvailable.getFullText ();
m_journal.info << "applyOffer: saOfferPaysAvailable=" << saOfferPaysAvailable.getFullText ();
m_journal.info << "applyOffer: saOfferGetsAvailable=" << saOfferGetsAvailable.getFullText ();
STAmount saTakerPaysAvailable = std::min (saTakerPays, saTakerFundsAvailable);
WriteLog (lsINFO, STAmount) << "applyOffer: saTakerPaysAvailable=" << saTakerPaysAvailable.getFullText ();
m_journal.info << "applyOffer: saTakerPaysAvailable=" << saTakerPaysAvailable.getFullText ();
// Limited = limited by other sides raw numbers.
// Taker can't pay more to offer than offer can get.
STAmount saTakerPaysLimited = std::min (saTakerPaysAvailable, saOfferGetsAvailable);
WriteLog (lsINFO, STAmount) << "applyOffer: saTakerPaysLimited=" << saTakerPaysLimited.getFullText ();
m_journal.info << "applyOffer: saTakerPaysLimited=" << saTakerPaysLimited.getFullText ();
// Align saTakerGetsLimited with saTakerPaysLimited.
STAmount saTakerGetsLimited = saTakerPaysLimited >= saOfferGetsAvailable // Cannot actually be greater
? saOfferPaysAvailable // Potentially take entire offer. Avoid math shenanigans.
: std::min (saOfferPaysAvailable, STAmount::divRound (saTakerPaysLimited, saOfferRate, saTakerGets, true)); // Take a portion of offer.
WriteLog (lsINFO, STAmount) << "applyOffer: saOfferRate=" << saOfferRate.getFullText ();
WriteLog (lsINFO, STAmount) << "applyOffer: saTakerGetsLimited=" << saTakerGetsLimited.getFullText ();
m_journal.info << "applyOffer: saOfferRate=" << saOfferRate.getFullText ();
m_journal.info << "applyOffer: saTakerGetsLimited=" << saTakerGetsLimited.getFullText ();
// Got & Paid = Calculated by price and transfered without fees.
// Compute from got as when !bSell, we want got to be exact to finish off offer if possible.
@@ -269,8 +269,8 @@ bool OfferCreateTransactor::applyOffer (
? saTakerPaysLimited
: std::min (saTakerPaysLimited, STAmount::mulRound (saTakerGot, saOfferRate, saTakerFunds, true));
WriteLog (lsINFO, STAmount) << "applyOffer: saTakerGot=" << saTakerGot.getFullText ();
WriteLog (lsINFO, STAmount) << "applyOffer: saTakerPaid=" << saTakerPaid.getFullText ();
m_journal.info << "applyOffer: saTakerGot=" << saTakerGot.getFullText ();
m_journal.info << "applyOffer: saTakerPaid=" << saTakerPaid.getFullText ();
if (uTakerPaysRate == QUALITY_ONE)
{
@@ -281,17 +281,17 @@ bool OfferCreateTransactor::applyOffer (
// Compute fees in a rounding safe way.
STAmount saTransferRate = STAmount (CURRENCY_ONE, ACCOUNT_ONE, uTakerPaysRate, -9);
WriteLog (lsINFO, STAmount) << "applyOffer: saTransferRate=" << saTransferRate.getFullText ();
m_journal.info << "applyOffer: saTransferRate=" << saTransferRate.getFullText ();
// TakerCost includes transfer fees.
STAmount saTakerCost = STAmount::mulRound (saTakerPaid, saTransferRate, true);
WriteLog (lsINFO, STAmount) << "applyOffer: saTakerCost=" << saTakerCost.getFullText ();
WriteLog (lsINFO, STAmount) << "applyOffer: saTakerFunds=" << saTakerFunds.getFullText ();
m_journal.info << "applyOffer: saTakerCost=" << saTakerCost.getFullText ();
m_journal.info << "applyOffer: saTakerFunds=" << saTakerFunds.getFullText ();
saTakerIssuerFee = saTakerCost > saTakerFunds
? saTakerFunds - saTakerPaid // Not enough funds to cover fee, stiff issuer the rounding error.
: saTakerCost - saTakerPaid;
WriteLog (lsINFO, STAmount) << "applyOffer: saTakerIssuerFee=" << saTakerIssuerFee.getFullText ();
m_journal.info << "applyOffer: saTakerIssuerFee=" << saTakerIssuerFee.getFullText ();
assert (saTakerIssuerFee >= zero);
}
@@ -309,7 +309,7 @@ bool OfferCreateTransactor::applyOffer (
: saOfferCost - saTakerGot;
}
WriteLog (lsINFO, STAmount) << "applyOffer: saTakerGot=" << saTakerGot.getFullText ();
m_journal.info << "applyOffer: saTakerGot=" << saTakerGot.getFullText ();
return saTakerGot >= saOfferPaysAvailable; // True, if consumed offer.
}
@@ -327,7 +327,7 @@ bool OfferCreateTransactor::applyOffer (
@return tesSUCCESS, terNO_ACCOUNT, telFAILED_PROCESSING, or
tecFAILED_PROCESSING
*/
TER OfferCreateTransactor::takeOffers (
TER ClassicOfferCreateTransactor::takeOffers (
const bool bOpenLedger,
const bool bPassive,
const bool bSell,
@@ -376,38 +376,6 @@ TER OfferCreateTransactor::takeOffers (
saTakerPays.getCurrency(), saTakerPays.getIssuer(),
saTakerGets.getCurrency(), saTakerGets.getIssuer());
#ifdef RIPPLE_AUTOBRIDGE
std::unique_ptr<OrderBookIterator> bridgeBookIn;
std::unique_ptr<OrderBookIterator> bridgeBookOut;
// If XRP isn't an already an endpoint, then we also create two additional
// iterators:
// (1) source -> XPR
// (2) XRP -> target
if (!saTakerPays.isNative () && !saTakerGets.isNative ())
{
if (m_journal.trace)
{
beast::Journal::ScopedStream ss (m_journal.trace);
ss <<
"Autobridging offer '" << saTakerPays.getFullText () <<
"' to '" << saTakerGets.getFullText () << "'" << std::endl;
ss <<
"Selected bridge: '" <<
STAmount::createHumanCurrency(saTakerPays.getCurrency()) <<
"/" <<
RippleAddress::createHumanAccountID (saTakerPays.getIssuer()) <<
"' - XRP - '" <<
STAmount::createHumanCurrency(saTakerGets.getCurrency ()) <<
"/" <<
RippleAddress::createHumanAccountID (saTakerGets.getIssuer()) <<
"'";
}
}
#endif
while ((temUNCERTAIN == terResult) && directBookIter.nextOffer())
{
STAmount saTakerFunds = lesActive.accountFunds (uTakerAccountID, saTakerPays);
@@ -415,28 +383,6 @@ TER OfferCreateTransactor::takeOffers (
STAmount saSubTakerGets = saTakerGets - saTakerGot; // How much more is wanted.
std::uint64_t uTipQuality = directBookIter.getCurrentQuality();
#ifdef RIPPLE_AUTOBRIDGE
if (bridgeBookIn && bridgeBookIn->nextOffer() &&
bridgeBookOut && bridgeBookOut->nextOffer())
{
STAmount bridgeIn = STAmount::setRate (bridgeBookIn->getCurrentQuality());
STAmount bridgeOut = STAmount::setRate (bridgeBookOut->getCurrentQuality());
m_journal.error <<
"The direct quality is: " << STAmount::setRate (uTipQuality);
m_journal.error <<
"The bridge-to-XRP quality is: " << bridgeIn;
m_journal.error <<
"The bridge-from-XRP quality is: " << bridgeOut;
m_journal.error <<
"The bridge synthetic quality is: " <<
STAmount::multiply (bridgeIn, bridgeOut, CURRENCY_ONE, ACCOUNT_ONE);
}
#endif
if (!canCross(
saTakerFunds,
saSubTakerPays,
@@ -495,39 +441,39 @@ TER OfferCreateTransactor::takeOffers (
STAmount saOfferIssuerFee;
STAmount saOfferRate = STAmount::setRate (uTipQuality);
if (m_journal.trace)
if (m_journal.debug)
{
m_journal.trace <<
m_journal.debug <<
"takeOffers: applyOffer: saTakerPays: " <<
saTakerPays.getFullText ();
m_journal.trace <<
m_journal.debug <<
"takeOffers: applyOffer: saTakerPaid: " <<
saTakerPaid.getFullText ();
m_journal.trace <<
m_journal.debug <<
"takeOffers: applyOffer: saTakerFunds: " <<
saTakerFunds.getFullText ();
m_journal.trace <<
m_journal.debug <<
"takeOffers: applyOffer: saOfferFunds: " <<
saOfferFunds.getFullText ();
m_journal.trace <<
m_journal.debug <<
"takeOffers: applyOffer: saOfferPays: " <<
saOfferPays.getFullText ();
m_journal.trace <<
m_journal.debug <<
"takeOffers: applyOffer: saOfferGets: " <<
saOfferGets.getFullText ();
m_journal.trace <<
m_journal.debug <<
"takeOffers: applyOffer: saOfferRate: " <<
saOfferRate.getFullText ();
m_journal.trace <<
m_journal.debug <<
"takeOffers: applyOffer: saSubTakerPays: " <<
saSubTakerPays.getFullText ();
m_journal.trace <<
m_journal.debug <<
"takeOffers: applyOffer: saSubTakerGets: " <<
saSubTakerGets.getFullText ();
m_journal.trace <<
m_journal.debug <<
"takeOffers: applyOffer: saTakerPays: " <<
saTakerPays.getFullText ();
m_journal.trace <<
m_journal.debug <<
"takeOffers: applyOffer: saTakerGets: " <<
saTakerGets.getFullText ();
}
@@ -725,9 +671,9 @@ TER OfferCreateTransactor::takeOffers (
return terResult;
}
TER OfferCreateTransactor::doApply ()
TER ClassicOfferCreateTransactor::doApply ()
{
if (m_journal.trace) m_journal.trace <<
if (m_journal.debug) m_journal.debug <<
"OfferCreate> " << mTxn.getJson (0);
std::uint32_t const uTxFlags = mTxn.getFlags ();
@@ -741,7 +687,7 @@ TER OfferCreateTransactor::doApply ()
if (!saTakerPays.isLegalNet () || !saTakerGets.isLegalNet ())
return temBAD_AMOUNT;
m_journal.trace <<
m_journal.debug <<
"saTakerPays=" << saTakerPays.getFullText () <<
" saTakerGets=" << saTakerGets.getFullText ();
@@ -763,7 +709,7 @@ TER OfferCreateTransactor::doApply ()
const uint256 uLedgerIndex = Ledger::getOfferIndex (mTxnAccountID, uSequence);
m_journal.trace <<
m_journal.debug <<
"Creating offer node: " << to_string (uLedgerIndex) <<
" uSequence=" << uSequence;
@@ -785,14 +731,14 @@ TER OfferCreateTransactor::doApply ()
if (uTxFlags & tfOfferCreateMask)
{
m_journal.trace <<
m_journal.debug <<
"Malformed transaction: Invalid flags set.";
return temINVALID_FLAG;
}
else if (bImmediateOrCancel && bFillOrKill)
{
m_journal.trace <<
m_journal.debug <<
"Malformed transaction: both IoC and FoK set.";
return temINVALID_FLAG;
@@ -851,7 +797,7 @@ TER OfferCreateTransactor::doApply ()
// before the transaction sequence number.
else if (bHaveCancel && (!uCancelSequence || uAccountSequenceNext - 1 <= uCancelSequence))
{
m_journal.trace <<
m_journal.debug <<
"uAccountSequenceNext=" << uAccountSequenceNext <<
" uOfferSequence=" << uCancelSequence;
@@ -1076,7 +1022,7 @@ TER OfferCreateTransactor::doApply ()
else
{
// We need to place the remainder of the offer into its order book.
if (m_journal.trace) m_journal.trace <<
if (m_journal.debug) m_journal.debug <<
"offer not fully consumed:" <<
" saTakerPays=" << saTakerPays.getFullText () <<
" saTakerGets=" << saTakerGets.getFullText ();
@@ -1127,10 +1073,10 @@ TER OfferCreateTransactor::doApply ()
m_journal.debug <<
"uGetsIssuerID=" <<
RippleAddress::createHumanAccountID (uGetsIssuerID);
m_journal.trace <<
m_journal.debug <<
"saTakerPays.isNative()=" <<
saTakerPays.isNative ();
m_journal.trace <<
m_journal.debug <<
"saTakerGets.isNative()=" <<
saTakerGets.isNative ();
m_journal.debug <<
@@ -1160,7 +1106,7 @@ TER OfferCreateTransactor::doApply ()
if (bSell)
sleOffer->setFlag (lsfSell);
if (m_journal.trace) m_journal.trace <<
if (m_journal.debug) m_journal.debug <<
"final terResult=" << transToken (terResult) <<
" sleOffer=" << sleOffer->getJson (0);
}
@@ -1176,7 +1122,6 @@ TER OfferCreateTransactor::doApply ()
{
m_journal.trace <<
"takeOffers: found unfunded: " << to_string (uOfferIndex);
lesActive.offerDelete (uOfferIndex);
}
@@ -1207,7 +1152,7 @@ TER OfferCreateTransactor::doApply ()
}
else
{
m_journal.trace <<
m_journal.debug <<
"takeOffers: offer " << indexes.first <<
" not found in directory " << indexes.second;
}
@@ -1221,11 +1166,10 @@ TER OfferCreateTransactor::doApply ()
}
}
if (tesSUCCESS != terResult) m_journal.trace <<
if (tesSUCCESS != terResult) m_journal.debug <<
"final terResult=" << transToken (terResult);
return terResult;
}
}

View File

@@ -17,24 +17,15 @@
*/
//==============================================================================
#ifndef RIPPLE_TX_OFFERCREATE_H_INCLUDED
#define RIPPLE_TX_OFFERCREATE_H_INCLUDED
#ifndef RIPPLE_TX_CLASSIC_OFFERCREATE_H_INCLUDED
#define RIPPLE_TX_CLASSIC_OFFERCREATE_H_INCLUDED
#include <unordered_set>
namespace ripple {
class OfferCreateTransactorLog;
template <>
char const*
LogPartition::getPartitionName <OfferCreateTransactorLog> ()
{
return "Tx/OfferCreate";
}
class OfferCreateTransactor
: public Transactor
class ClassicOfferCreateTransactor
: public OfferCreateTransactor
{
private:
template <class T>
@@ -52,20 +43,19 @@ private:
}
public:
OfferCreateTransactor (
ClassicOfferCreateTransactor (
SerializedTransaction const& txn,
TransactionEngineParams params,
TransactionEngine* engine)
: Transactor (
: OfferCreateTransactor (
txn,
params,
engine,
LogPartition::getJournal <OfferCreateTransactorLog> ())
engine)
{
}
TER doApply ();
TER doApply () override;
private:
bool isValidOffer (

View File

@@ -18,14 +18,14 @@
//==============================================================================
#include "Transactor.h"
#include "ChangeTransactor.h"
#include "OfferCancelTransactor.h"
#include "OfferCreateTransactor.h"
#include "PaymentTransactor.h"
#include "RegularKeySetTransactor.h"
#include "AccountSetTransactor.h"
#include "TrustSetTransactor.h"
#include "WalletAddTransactor.h"
#include "AddWallet.h"
#include "CancelOffer.h"
#include "Change.h"
#include "CreateOffer.h"
#include "Payment.h"
#include "SetAccount.h"
#include "SetRegularKey.h"
#include "SetTrust.h"
namespace ripple {
@@ -53,8 +53,7 @@ std::unique_ptr<Transactor> Transactor::makeTransactor (
new TrustSetTransactor (txn, params, engine));
case ttOFFER_CREATE:
return std::unique_ptr<Transactor> (
new OfferCreateTransactor (txn, params, engine));
return make_OfferCreateTransactor (txn, params, engine);
case ttOFFER_CANCEL:
return std::unique_ptr<Transactor> (