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 (