diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj
index 3ca8e9835..2d9a28618 100644
--- a/Builds/VisualStudio2013/RippleD.vcxproj
+++ b/Builds/VisualStudio2013/RippleD.vcxproj
@@ -34,6 +34,12 @@
true
+
+ true
+ true
+ true
+ true
+
true
true
@@ -884,27 +890,57 @@
true
true
-
+
+ true
+ true
true
true
-
+
+ true
+ true
true
true
-
+
+ true
+ true
true
true
-
+
+ true
+ true
true
true
-
+
+ true
+ true
true
true
-
+
+ true
+ true
+ true
+ true
+
+
+ true
+ true
+ true
+ true
+
+
+ true
+ true
+ true
+ true
+
+
+ true
+ true
true
true
@@ -912,11 +948,15 @@
true
true
-
+
+ true
+ true
true
true
-
+
+ true
+ true
true
true
@@ -2107,15 +2147,18 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
+
+
diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters
index d0c274ceb..163640f6f 100644
--- a/Builds/VisualStudio2013/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters
@@ -1455,31 +1455,40 @@
[2] Old Ripple\ripple_app\tx
-
+
[2] Old Ripple\ripple_app\transactors
-
+
[2] Old Ripple\ripple_app\transactors
-
+
[2] Old Ripple\ripple_app\transactors
-
+
[2] Old Ripple\ripple_app\transactors
-
+
[2] Old Ripple\ripple_app\transactors
-
+
+ [2] Old Ripple\ripple_app\transactors
+
+
+ [2] Old Ripple\ripple_app\transactors
+
+
+ [2] Old Ripple\ripple_app\transactors
+
+
[2] Old Ripple\ripple_app\transactors
[2] Old Ripple\ripple_app\transactors
-
+
[2] Old Ripple\ripple_app\transactors
-
+
[2] Old Ripple\ripple_app\transactors
@@ -3218,31 +3227,40 @@
[2] Old Ripple\ripple_app\tx
-
+
[2] Old Ripple\ripple_app\transactors
-
+
[2] Old Ripple\ripple_app\transactors
-
+
[2] Old Ripple\ripple_app\transactors
-
+
[2] Old Ripple\ripple_app\transactors
-
+
[2] Old Ripple\ripple_app\transactors
-
+
+ [2] Old Ripple\ripple_app\transactors
+
+
+ [2] Old Ripple\ripple_app\transactors
+
+
+ [2] Old Ripple\ripple_app\transactors
+
+
[2] Old Ripple\ripple_app\transactors
[2] Old Ripple\ripple_app\transactors
-
+
[2] Old Ripple\ripple_app\transactors
-
+
[2] Old Ripple\ripple_app\transactors
diff --git a/SConstruct b/SConstruct
index ea8ff8a4c..cfb1b89ff 100644
--- a/SConstruct
+++ b/SConstruct
@@ -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: ...
+#
+# 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: ...
+#
+# 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()
+
+
diff --git a/src/BeastConfig.h b/src/BeastConfig.h
index db2956307..801f7fd24 100644
--- a/src/BeastConfig.h
+++ b/src/BeastConfig.h
@@ -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
diff --git a/src/ripple/common/tests/cross_offer.test.cpp b/src/ripple/common/tests/cross_offer.test.cpp
new file mode 100644
index 000000000..e922b0751
--- /dev/null
+++ b/src/ripple/common/tests/cross_offer.test.cpp
@@ -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
+#include
+#include
+#include
+#include "../../../beast/beast/cxx14/type_traits.h" //
+#include
+#include
+
+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
+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
+struct AmountTraits
+{
+ /** Returns `true` if `lhs` is of lower quality than `rhs`. */
+ template
+ 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
+ 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
+ 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
+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::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
+typename std::iterator_traits::value_type
+cross_offer_in (
+ typename std::iterator_traits::value_type::amount_type const& in,
+ typename std::iterator_traits::value_type const& minimum_quality,
+ BookIter first, BookIter last)
+{
+ using Amount =
+ typename std::iterator_traits::value_type::amount_type;
+ using Offer =
+ typename std::iterator_traits::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::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
+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::multiply (leg2.in(),
+ AmountTraits::inverse (leg1)),
+ leg2.out());
+
+ // leg1 has less liquidity
+ return Offer (leg1.in(),
+ AmountTraits::multiply (leg1.out(), leg2));
+}
+
+namespace detail {
+
+/** Presents a set of order books as a single bridged order book. */
+template
+class MultiBookIterator
+{
+private:
+ using Amount =
+ typename std::iterator_traits::value_type::amount_type;
+
+ using Offer =
+ typename std::iterator_traits::value_type;
+
+ typedef std::is_const <
+ typename std::iterator_traits::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
+ 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
+ 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::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 >::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
+ >
+ >
+ MultiBookIterator (MultiBookIterator 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
+ >
+ >
+ 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
+ bool
+ operator== (
+ MultiBookIterator 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
+ >
+#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
+bool
+operator!= (
+ MultiBookIterator const& lhs,
+ MultiBookIterator const& rhs) noexcept
+{
+ return !(rhs == lhs);
+}
+
+} // detail
+
+//------------------------------------------------------------------------------
+
+// TODO Allow const Book
+//
+template
+class MultiBook
+{
+private:
+ static_assert (! std::is_const ::value,
+ "Book cannot be const");
+
+ std::reference_wrapper m_direct;
+ std::reference_wrapper m_leg1;
+ std::reference_wrapper 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
+typename std::iterator_traits::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
+typename std::iterator_traits::value_type
+cross_offer_in (
+ typename std::iterator_traits<
+ typename Book::iterator>::value_type::amount_type const& in,
+ typename std::iterator_traits::value_type
+ const& minimum_quality,
+ Book& direct, Book& leg1, Book& leg2)
+{
+ MultiBook book (direct, leg1, leg2);
+ return cross_offer_in (in, minimum_quality, book);
+}
+
+//------------------------------------------------------------------------------
+
+class cross_offers_test : public beast::unit_test::suite
+{
+public:
+ template
+ 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 Offer;
+ typedef std::vector Book;
+
+ template
+ OfferType
+ static make_offer (Amount from, Amount rate)
+ {
+ return OfferType (from, from * rate);
+ }
+
+ template
+ 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 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);
+
+}
+}
diff --git a/src/ripple/types/ripple_types.cpp b/src/ripple/types/ripple_types.cpp
index f131a6c28..7547f3cb6 100644
--- a/src/ripple/types/ripple_types.cpp
+++ b/src/ripple/types/ripple_types.cpp
@@ -24,10 +24,13 @@
#ifdef BEAST_WIN32
# include // for ByteOrder.cpp
-// defines 'max' and does other stupid things
+// defines min, max and does other stupid things
# ifdef max
# undef max
# endif
+# ifdef min
+# undef min
+# endif
#endif
#include
@@ -45,4 +48,5 @@
#include "impl/RippleIdentifierTests.cpp"
#include "impl/RippleAssets.cpp"
+#include "../common/tests/cross_offer.test.cpp"
diff --git a/src/ripple_app/book/Taker.h b/src/ripple_app/book/Taker.h
index decf43db6..29cfe541b 100644
--- a/src/ripple_app/book/Taker.h
+++ b/src/ripple_app/book/Taker.h
@@ -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
diff --git a/src/ripple_app/ripple_app_pt9.cpp b/src/ripple_app/ripple_app_pt9.cpp
index c308326e9..d9942787a 100644
--- a/src/ripple_app/ripple_app_pt9.cpp
+++ b/src/ripple_app/ripple_app_pt9.cpp
@@ -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
diff --git a/src/ripple_app/transactors/WalletAddTransactor.cpp b/src/ripple_app/transactors/AddWallet.cpp
similarity index 100%
rename from src/ripple_app/transactors/WalletAddTransactor.cpp
rename to src/ripple_app/transactors/AddWallet.cpp
diff --git a/src/ripple_app/transactors/WalletAddTransactor.h b/src/ripple_app/transactors/AddWallet.h
similarity index 100%
rename from src/ripple_app/transactors/WalletAddTransactor.h
rename to src/ripple_app/transactors/AddWallet.h
diff --git a/src/ripple_app/transactors/OfferCancelTransactor.cpp b/src/ripple_app/transactors/CancelOffer.cpp
similarity index 100%
rename from src/ripple_app/transactors/OfferCancelTransactor.cpp
rename to src/ripple_app/transactors/CancelOffer.cpp
diff --git a/src/ripple_app/transactors/OfferCancelTransactor.h b/src/ripple_app/transactors/CancelOffer.h
similarity index 100%
rename from src/ripple_app/transactors/OfferCancelTransactor.h
rename to src/ripple_app/transactors/CancelOffer.h
diff --git a/src/ripple_app/transactors/ChangeTransactor.cpp b/src/ripple_app/transactors/Change.cpp
similarity index 100%
rename from src/ripple_app/transactors/ChangeTransactor.cpp
rename to src/ripple_app/transactors/Change.cpp
diff --git a/src/ripple_app/transactors/ChangeTransactor.h b/src/ripple_app/transactors/Change.h
similarity index 100%
rename from src/ripple_app/transactors/ChangeTransactor.h
rename to src/ripple_app/transactors/Change.h
diff --git a/src/ripple_app/transactors/CreateOffer.cpp b/src/ripple_app/transactors/CreateOffer.cpp
new file mode 100644
index 000000000..e50b4fc06
--- /dev/null
+++ b/src/ripple_app/transactors/CreateOffer.cpp
@@ -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 ())
+{
+
+}
+
+std::unique_ptr make_OfferCreateTransactor (
+ SerializedTransaction const& txn,
+ TransactionEngineParams params,
+ TransactionEngine* engine)
+{
+#if RIPPLE_USE_OLD_CREATE_TRANSACTOR
+ return std::make_unique (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 (txn, params, engine);
+ }
+
+ return std::make_unique (txn, params, engine);
+#endif
+}
+
+}
diff --git a/src/ripple_app/transactors/CreateOffer.h b/src/ripple_app/transactors/CreateOffer.h
new file mode 100644
index 000000000..36fb58dbf
--- /dev/null
+++ b/src/ripple_app/transactors/CreateOffer.h
@@ -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 ()
+{
+ return "Tx/OfferCreate";
+}
+
+class OfferCreateTransactor
+ : public Transactor
+{
+private:
+ template
+ 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 make_OfferCreateTransactor (
+ SerializedTransaction const& txn,
+ TransactionEngineParams params,
+ TransactionEngine* engine);
+
+}
+
+#endif
+
diff --git a/src/ripple_app/transactors/CreateOfferBridged.cpp b/src/ripple_app/transactors/CreateOfferBridged.cpp
new file mode 100644
index 000000000..398cc1921
--- /dev/null
+++ b/src/ripple_app/transactors/CreateOfferBridged.cpp
@@ -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.
+*/
+//==============================================================================
+
diff --git a/src/ripple_app/transactors/CreateOfferBridged.h b/src/ripple_app/transactors/CreateOfferBridged.h
new file mode 100644
index 000000000..0deb21a69
--- /dev/null
+++ b/src/ripple_app/transactors/CreateOfferBridged.h
@@ -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
diff --git a/src/ripple_app/transactors/CreateOfferDirect.cpp b/src/ripple_app/transactors/CreateOfferDirect.cpp
new file mode 100644
index 000000000..1684f2125
--- /dev/null
+++ b/src/ripple_app/transactors/CreateOfferDirect.cpp
@@ -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
+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 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=" < " << 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;
+}
+
+}
+
diff --git a/src/ripple_app/transactors/CreateOfferDirect.h b/src/ripple_app/transactors/CreateOfferDirect.h
new file mode 100644
index 000000000..d68e25b48
--- /dev/null
+++ b/src/ripple_app/transactors/CreateOfferDirect.h
@@ -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
+
+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 crossOffers (
+ core::LedgerView& view,
+ STAmount const& saTakerPays,
+ STAmount const& saTakerGets,
+ STAmount& saTakerPaid,
+ STAmount& saTakerGot);
+};
+
+}
+
+#endif
diff --git a/src/ripple_app/transactors/OfferCreateTransactor.cpp b/src/ripple_app/transactors/CreateOfferLegacy.cpp
similarity index 89%
rename from src/ripple_app/transactors/OfferCreateTransactor.cpp
rename to src/ripple_app/transactors/CreateOfferLegacy.cpp
index f83cab2ca..e832c59ac 100644
--- a/src/ripple_app/transactors/OfferCreateTransactor.cpp
+++ b/src/ripple_app/transactors/CreateOfferLegacy.cpp
@@ -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 bridgeBookIn;
- std::unique_ptr 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;
}
}
-
diff --git a/src/ripple_app/transactors/OfferCreateTransactor.h b/src/ripple_app/transactors/CreateOfferLegacy.h
similarity index 88%
rename from src/ripple_app/transactors/OfferCreateTransactor.h
rename to src/ripple_app/transactors/CreateOfferLegacy.h
index 500c7ebbd..ff210d849 100644
--- a/src/ripple_app/transactors/OfferCreateTransactor.h
+++ b/src/ripple_app/transactors/CreateOfferLegacy.h
@@ -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
namespace ripple {
-class OfferCreateTransactorLog;
-
-template <>
-char const*
-LogPartition::getPartitionName ()
-{
- return "Tx/OfferCreate";
-}
-
-class OfferCreateTransactor
- : public Transactor
+class ClassicOfferCreateTransactor
+ : public OfferCreateTransactor
{
private:
template
@@ -52,20 +43,19 @@ private:
}
public:
- OfferCreateTransactor (
+ ClassicOfferCreateTransactor (
SerializedTransaction const& txn,
TransactionEngineParams params,
TransactionEngine* engine)
- : Transactor (
+ : OfferCreateTransactor (
txn,
params,
- engine,
- LogPartition::getJournal ())
+ engine)
{
}
- TER doApply ();
+ TER doApply () override;
private:
bool isValidOffer (
diff --git a/src/ripple_app/transactors/PaymentTransactor.cpp b/src/ripple_app/transactors/Payment.cpp
similarity index 100%
rename from src/ripple_app/transactors/PaymentTransactor.cpp
rename to src/ripple_app/transactors/Payment.cpp
diff --git a/src/ripple_app/transactors/PaymentTransactor.h b/src/ripple_app/transactors/Payment.h
similarity index 100%
rename from src/ripple_app/transactors/PaymentTransactor.h
rename to src/ripple_app/transactors/Payment.h
diff --git a/src/ripple_app/transactors/AccountSetTransactor.cpp b/src/ripple_app/transactors/SetAccount.cpp
similarity index 100%
rename from src/ripple_app/transactors/AccountSetTransactor.cpp
rename to src/ripple_app/transactors/SetAccount.cpp
diff --git a/src/ripple_app/transactors/AccountSetTransactor.h b/src/ripple_app/transactors/SetAccount.h
similarity index 100%
rename from src/ripple_app/transactors/AccountSetTransactor.h
rename to src/ripple_app/transactors/SetAccount.h
diff --git a/src/ripple_app/transactors/RegularKeySetTransactor.cpp b/src/ripple_app/transactors/SetRegularKey.cpp
similarity index 100%
rename from src/ripple_app/transactors/RegularKeySetTransactor.cpp
rename to src/ripple_app/transactors/SetRegularKey.cpp
diff --git a/src/ripple_app/transactors/RegularKeySetTransactor.h b/src/ripple_app/transactors/SetRegularKey.h
similarity index 100%
rename from src/ripple_app/transactors/RegularKeySetTransactor.h
rename to src/ripple_app/transactors/SetRegularKey.h
diff --git a/src/ripple_app/transactors/TrustSetTransactor.cpp b/src/ripple_app/transactors/SetTrust.cpp
similarity index 100%
rename from src/ripple_app/transactors/TrustSetTransactor.cpp
rename to src/ripple_app/transactors/SetTrust.cpp
diff --git a/src/ripple_app/transactors/TrustSetTransactor.h b/src/ripple_app/transactors/SetTrust.h
similarity index 100%
rename from src/ripple_app/transactors/TrustSetTransactor.h
rename to src/ripple_app/transactors/SetTrust.h
diff --git a/src/ripple_app/transactors/Transactor.cpp b/src/ripple_app/transactors/Transactor.cpp
index 9e9fc9bfa..de1b6fe4f 100644
--- a/src/ripple_app/transactors/Transactor.cpp
+++ b/src/ripple_app/transactors/Transactor.cpp
@@ -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::makeTransactor (
new TrustSetTransactor (txn, params, engine));
case ttOFFER_CREATE:
- return std::unique_ptr (
- new OfferCreateTransactor (txn, params, engine));
+ return make_OfferCreateTransactor (txn, params, engine);
case ttOFFER_CANCEL:
return std::unique_ptr (