mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
New Env transaction testing framework:
This adds a suite of tools used to write unit tests. The Env provides a context containing a ledger, and routines that assemble transactions from JSON with optional "funclets" that add details in an exensible, terse notation.
This commit is contained in:
@@ -1,19 +1,19 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2015 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.
|
||||
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.
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
|
||||
107
src/ripple/app/tests/Common.cpp
Normal file
107
src/ripple/app/tests/Common.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2015 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/app/tests/Common.h>
|
||||
#include <ripple/protocol/SystemParameters.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
namespace detail {
|
||||
|
||||
STAmount
|
||||
XRP_t::operator()(double v) const
|
||||
{
|
||||
if (v < 0)
|
||||
return STAmount(std::uint64_t(
|
||||
-v * SYSTEM_CURRENCY_PARTS), true);
|
||||
return STAmount(std::uint64_t(
|
||||
v * SYSTEM_CURRENCY_PARTS), false);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
detail::XRP_t XRP;
|
||||
|
||||
STAmount
|
||||
IOU::operator()(double v) const
|
||||
{
|
||||
return amountFromString(issue_,
|
||||
std::to_string(v));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef _MSC_VER
|
||||
Account::Account (Account&& other)
|
||||
: name_(std::move(other.name_))
|
||||
, pk_(std::move(other.pk_))
|
||||
, sk_(std::move(other.sk_))
|
||||
, id_(std::move(other.id_))
|
||||
, human_(std::move(other.human_))
|
||||
{
|
||||
}
|
||||
|
||||
Account&
|
||||
Account::operator= (Account&& rhs)
|
||||
{
|
||||
name_ = std::move(rhs.name_);
|
||||
pk_ = std::move(rhs.pk_);
|
||||
sk_ = std::move(rhs.sk_);
|
||||
id_ = std::move(rhs.id_);
|
||||
human_ = std::move(rhs.human_);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
Account::Account(
|
||||
std::string name, KeyPair&& keys)
|
||||
: name_(std::move(name))
|
||||
{
|
||||
pk_ = std::move(keys.publicKey);
|
||||
sk_ = std::move(keys.secretKey);
|
||||
id_ = pk_.getAccountID();
|
||||
human_ = pk_.humanAccountID();
|
||||
}
|
||||
|
||||
Account::Account (std::string name,
|
||||
KeyType type)
|
||||
#ifndef _MSC_VER
|
||||
: Account(name,
|
||||
#else
|
||||
// Fails on Clang and possibly gcc
|
||||
: Account(std::move(name),
|
||||
#endif
|
||||
generateKeysFromSeed(type,
|
||||
RippleAddress::createSeedGeneric(
|
||||
name)))
|
||||
{
|
||||
}
|
||||
|
||||
IOU
|
||||
Account::operator[](std::string const& s) const
|
||||
{
|
||||
auto const currency = to_currency(s);
|
||||
assert(currency != noCurrency());
|
||||
return IOU(Issue(currency, id()));
|
||||
}
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
211
src/ripple/app/tests/Common.h
Normal file
211
src/ripple/app/tests/Common.h
Normal file
@@ -0,0 +1,211 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2015 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_APP_TESTS_COMMON_H_INCLUDED
|
||||
#define RIPPLE_APP_TESTS_COMMON_H_INCLUDED
|
||||
|
||||
#include <ripple/protocol/Issue.h>
|
||||
#include <ripple/protocol/RippleAddress.h>
|
||||
#include <ripple/protocol/STAmount.h>
|
||||
#include <ripple/protocol/UintTypes.h>
|
||||
#include <ripple/crypto/KeyType.h>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct XRP_t
|
||||
{
|
||||
XRP_t() = default;
|
||||
|
||||
/** Implicit conversion to Issue.
|
||||
|
||||
This allows passing XRP where
|
||||
an Issue is expected.
|
||||
*/
|
||||
operator Issue() const
|
||||
{
|
||||
return xrpIssue();
|
||||
}
|
||||
|
||||
/** Returns an amount of XRP as STAmount
|
||||
|
||||
@param v The number of XRP (not drops)
|
||||
*/
|
||||
STAmount operator()(double v) const;
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
/** Converts to XRP Issue or STAmount.
|
||||
|
||||
Examples:
|
||||
XRP Converts to the XRP Issue
|
||||
XRP(10) Returns STAmount of 10 XRP
|
||||
*/
|
||||
extern detail::XRP_t XRP;
|
||||
|
||||
/** Returns an XRP STAmount.
|
||||
|
||||
Example:
|
||||
drops(10) Returns STAmount of 10 drops
|
||||
*/
|
||||
inline
|
||||
STAmount
|
||||
drops (std::uint64_t v)
|
||||
{
|
||||
return STAmount(v, false);
|
||||
}
|
||||
|
||||
/** Converts to IOU Issue or STAmount.
|
||||
|
||||
Examples:
|
||||
IOU Converts to the underlying Issue
|
||||
IOU(10) Returns STAmount of 10 of
|
||||
the underlying Issue.
|
||||
*/
|
||||
class IOU
|
||||
{
|
||||
private:
|
||||
Issue issue_;
|
||||
|
||||
public:
|
||||
IOU(Issue const& issue)
|
||||
: issue_(issue)
|
||||
{
|
||||
}
|
||||
|
||||
/** Implicit conversion to Issue.
|
||||
|
||||
This allows passing an IOU
|
||||
value where an Issue is expected.
|
||||
*/
|
||||
operator Issue() const
|
||||
{
|
||||
return issue_;
|
||||
}
|
||||
|
||||
STAmount operator()(double v) const;
|
||||
|
||||
// VFALCO TODO
|
||||
// STAmount operator()(char const* s) const;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Immutable cryptographic account descriptor. */
|
||||
class Account
|
||||
{
|
||||
private:
|
||||
std::string name_;
|
||||
// VFALCO TODO use AnyPublicKey, AnySecretKey
|
||||
// instead of RippleAddress
|
||||
RippleAddress pk_;
|
||||
RippleAddress sk_;
|
||||
ripple::Account id_;
|
||||
std::string human_; // base58 public key string
|
||||
|
||||
public:
|
||||
Account() = default;
|
||||
Account (Account const&) = default;
|
||||
Account& operator= (Account const&) = default;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
Account (Account&&);
|
||||
Account& operator= (Account&&);
|
||||
#else
|
||||
Account (Account&&) = default;
|
||||
Account& operator= (Account&&) = default;
|
||||
#endif
|
||||
|
||||
/** Create an account from a key pair. */
|
||||
Account (std::string name, KeyPair&& keys);
|
||||
|
||||
/** Create an account from a simple string name. */
|
||||
/** @{ */
|
||||
Account (std::string name,
|
||||
KeyType type = KeyType::secp256k1);
|
||||
Account (char const* name,
|
||||
KeyType type = KeyType::secp256k1)
|
||||
: Account(std::string(name), type)
|
||||
{
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Return the public key. */
|
||||
RippleAddress const&
|
||||
pk() const
|
||||
{
|
||||
return pk_;
|
||||
}
|
||||
|
||||
/** Return the secret key. */
|
||||
RippleAddress const&
|
||||
sk() const
|
||||
{
|
||||
return sk_;
|
||||
}
|
||||
|
||||
/** Returns the Account ID.
|
||||
|
||||
The Account ID is the uint160 hash of the public key.
|
||||
*/
|
||||
ripple::Account
|
||||
id() const
|
||||
{
|
||||
return id_;
|
||||
}
|
||||
|
||||
/** Returns the human readable public key. */
|
||||
std::string const&
|
||||
human() const
|
||||
{
|
||||
return human_;
|
||||
}
|
||||
|
||||
/** Implicit conversion to AccountID.
|
||||
|
||||
This allows passing an Account
|
||||
where a ripple::Account is expected.
|
||||
*/
|
||||
operator ripple::Account() const
|
||||
{
|
||||
return id_;
|
||||
}
|
||||
|
||||
/** Returns an IOU for the specified gateway currency. */
|
||||
IOU
|
||||
operator[](std::string const& s) const;
|
||||
|
||||
/** Meet the requirements of StrictWeakOrdering. */
|
||||
friend
|
||||
bool
|
||||
operator< (Account const& lhs, Account const& rhs)
|
||||
{
|
||||
return lhs.id() < rhs.id();
|
||||
}
|
||||
};
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
217
src/ripple/app/tests/Env.cpp
Normal file
217
src/ripple/app/tests/Env.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2015 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/app/tests/Env.h>
|
||||
#include <ripple/app/paths/FindPaths.h>
|
||||
#include <ripple/app/tx/TransactionEngine.h>
|
||||
#include <ripple/basics/Slice.h>
|
||||
#include <ripple/json/to_string.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <ripple/protocol/HashPrefix.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/protocol/LedgerFormats.h>
|
||||
#include <ripple/protocol/Serializer.h>
|
||||
#include <ripple/protocol/STParsedJSON.h>
|
||||
#include <ripple/protocol/SystemParameters.h>
|
||||
#include <ripple/protocol/TER.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
// VFALCO TODO Use AnyPublicKey, AnySecretKey, AccountID
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
STAmount
|
||||
AccountInfo::balance(
|
||||
Issue const& issue) const
|
||||
{
|
||||
if (! root_)
|
||||
return STAmount(issue, 0, 0);
|
||||
if (isXRP(issue))
|
||||
return root_->getFieldAmount(sfBalance);
|
||||
auto amount = ledger_->getRippleState(
|
||||
account_, issue.account,
|
||||
issue.currency)->getFieldAmount(
|
||||
sfBalance);
|
||||
amount.setIssuer(issue.account);
|
||||
if (account_.id() > issue.account)
|
||||
amount.negate();
|
||||
return amount;
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
AccountInfo::seq() const
|
||||
{
|
||||
return root_->getFieldU32(sfSequence);
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
AccountInfo::flags() const
|
||||
{
|
||||
return root_->getFieldU32(sfFlags);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Env::Env (beast::unit_test::suite& test_)
|
||||
: test(test_)
|
||||
, master("master", generateKeysFromSeed(
|
||||
KeyType::secp256k1, RippleAddress::createSeedGeneric(
|
||||
"masterpassphrase")))
|
||||
{
|
||||
memoize(master);
|
||||
initializePathfinding();
|
||||
ledger = std::make_shared<Ledger>(
|
||||
master.pk(), SYSTEM_CURRENCY_START);
|
||||
}
|
||||
|
||||
void
|
||||
Env::memoize (Account const& account)
|
||||
{
|
||||
map_.emplace(account.id(), account);
|
||||
}
|
||||
|
||||
Account const&
|
||||
Env::lookup (std::string const& base58ID) const
|
||||
{
|
||||
RippleAddress ra;
|
||||
if (! ra.setAccountID(base58ID))
|
||||
throw std::runtime_error(
|
||||
"Env::lookup: invalid account ID");
|
||||
return lookup(ra.getAccountID());
|
||||
}
|
||||
|
||||
Account const&
|
||||
Env::lookup (ripple::Account const& id) const
|
||||
{
|
||||
auto const iter = map_.find(id);
|
||||
if (iter == map_.end())
|
||||
throw std::runtime_error(
|
||||
"Env::lookup:: unknown account ID");
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
void
|
||||
Env::fund (STAmount const& amount,
|
||||
Account const& account)
|
||||
{
|
||||
using namespace jtx;
|
||||
memoize(account);
|
||||
apply(pay(master, account, amount),
|
||||
seq(jtx::autofill),
|
||||
fee(jtx::autofill),
|
||||
sig(jtx::autofill));
|
||||
}
|
||||
|
||||
void
|
||||
Env::trust (STAmount const& amount,
|
||||
Account const& account)
|
||||
{
|
||||
using namespace jtx;
|
||||
apply(jtx::trust(account, amount),
|
||||
seq(jtx::autofill),
|
||||
fee(jtx::autofill),
|
||||
sig(jtx::autofill));
|
||||
}
|
||||
|
||||
void
|
||||
Env::submit (JTx const& tx)
|
||||
{
|
||||
boost::optional<STTx> stx;
|
||||
{
|
||||
// The parse must succeed, since we
|
||||
// generated the JSON ourselves.
|
||||
boost::optional<STObject> st;
|
||||
try
|
||||
{
|
||||
st = jtx::parse(tx.jv);
|
||||
}
|
||||
catch(jtx::parse_error const&)
|
||||
{
|
||||
test.log << pretty(tx.jv);
|
||||
throw;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
stx.emplace(std::move(*st));
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
TER ter;
|
||||
bool didApply;
|
||||
if (stx)
|
||||
{
|
||||
TransactionEngine txe (ledger, multisign);
|
||||
std::tie(ter, didApply) = txe.applyTransaction(
|
||||
*stx, tapOPEN_LEDGER |
|
||||
(true ? tapNONE : tapNO_CHECK_SIGN));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert the exception into a TER so that
|
||||
// callers can expect it using ter(temMALFORMED)
|
||||
ter = temMALFORMED;
|
||||
didApply = false;
|
||||
}
|
||||
if (! test.expect(ter == tx.ter,
|
||||
"apply: " + transToken(ter) +
|
||||
" (" + transHuman(ter) + ")"))
|
||||
test.log << pretty(tx.jv);
|
||||
}
|
||||
|
||||
void
|
||||
Env::autofill (JTx& jt)
|
||||
{
|
||||
auto& jv = jt.jv;
|
||||
auto const should = [](boost::tribool v, bool b)
|
||||
{
|
||||
if (boost::indeterminate(v))
|
||||
return b;
|
||||
return bool(v);
|
||||
};
|
||||
|
||||
if(should(jt.fill_fee, fill_fee_))
|
||||
jtx::fill_fee(jv, *ledger);
|
||||
|
||||
if(should(jt.fill_seq, fill_seq_))
|
||||
jtx::fill_seq(jv, *ledger);
|
||||
|
||||
// Must come last
|
||||
if (jt.signer)
|
||||
jt.signer(*this, jt);
|
||||
else if(should(jt.fill_sig, fill_sig_))
|
||||
{
|
||||
auto const account =
|
||||
lookup(jv[jss::Account].asString());
|
||||
auto const ar =
|
||||
ledger->getAccountRoot(account);
|
||||
if (ar->isFieldPresent(sfRegularKey))
|
||||
jtx::sign(jv, lookup(
|
||||
ar->getFieldAccount160(sfRegularKey)));
|
||||
else
|
||||
jtx::sign(jv, account);
|
||||
}
|
||||
}
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
257
src/ripple/app/tests/Env.h
Normal file
257
src/ripple/app/tests/Env.h
Normal file
@@ -0,0 +1,257 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2015 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_APP_TESTS_ENV_H_INCLUDED
|
||||
#define RIPPLE_APP_TESTS_ENV_H_INCLUDED
|
||||
|
||||
#include <ripple/app/tests/Common.h>
|
||||
#include <ripple/app/tests/JTx.h>
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/json/json_value.h>
|
||||
#include <ripple/json/to_string.h>
|
||||
#include <ripple/protocol/Issue.h>
|
||||
#include <ripple/protocol/RippleAddress.h>
|
||||
#include <ripple/protocol/STAmount.h>
|
||||
#include <ripple/protocol/STObject.h>
|
||||
#include <ripple/protocol/STTx.h>
|
||||
#include <beast/unit_test/suite.h>
|
||||
#include <boost/logic/tribool.hpp>
|
||||
#include <beast/cxx14/type_traits.h> // <type_traits>
|
||||
#include <utility>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
/** A view to an account's account root. */
|
||||
class AccountInfo
|
||||
{
|
||||
private:
|
||||
Account account_;
|
||||
std::shared_ptr<Ledger> ledger_;
|
||||
std::shared_ptr<STLedgerEntry> root_;
|
||||
|
||||
public:
|
||||
AccountInfo(Account const& account,
|
||||
std::shared_ptr<Ledger> ledger)
|
||||
: account_(account)
|
||||
, ledger_(std::move(ledger))
|
||||
, root_(ledger_->getAccountRoot(
|
||||
account.id()))
|
||||
{
|
||||
}
|
||||
|
||||
STAmount
|
||||
balance (Issue const& issue) const;
|
||||
|
||||
std::uint32_t
|
||||
seq() const;
|
||||
|
||||
std::uint32_t
|
||||
flags() const;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A transaction testing environment. */
|
||||
class Env
|
||||
{
|
||||
public:
|
||||
beast::unit_test::suite& test;
|
||||
|
||||
/** The master account. */
|
||||
Account const master;
|
||||
|
||||
/** The open ledger. */
|
||||
std::shared_ptr<Ledger> ledger;
|
||||
|
||||
public:
|
||||
Env (beast::unit_test::suite& test_);
|
||||
|
||||
/** Associate AccountID with account. */
|
||||
void
|
||||
memoize (Account const& account);
|
||||
|
||||
/** Returns the Account given the AccountID. */
|
||||
/** @{ */
|
||||
Account const&
|
||||
lookup (std::string const& base58ID) const;
|
||||
|
||||
Account const&
|
||||
lookup (ripple::Account const& id) const;
|
||||
/** @} */
|
||||
|
||||
/** Returns info on an Account. */
|
||||
/** @{ */
|
||||
AccountInfo
|
||||
info (Account const& account) const
|
||||
{
|
||||
return AccountInfo(account, ledger);
|
||||
}
|
||||
|
||||
AccountInfo
|
||||
operator[](Account const& account) const
|
||||
{
|
||||
return info(account);
|
||||
}
|
||||
/** @} */
|
||||
|
||||
void auto_fee (bool value)
|
||||
{
|
||||
fill_fee_ = value;
|
||||
}
|
||||
|
||||
void auto_seq (bool value)
|
||||
{
|
||||
fill_seq_ = value;
|
||||
}
|
||||
|
||||
void auto_sig (bool value)
|
||||
{
|
||||
fill_sig_ = value;
|
||||
}
|
||||
|
||||
/** Create a JTx from parameters. */
|
||||
template <class JsonValue,
|
||||
class... FN>
|
||||
JTx
|
||||
tx (JsonValue&& jv, FN const&... fN)
|
||||
{
|
||||
JTx jt(std::forward<JsonValue>(jv));
|
||||
invoke(jt, fN...);
|
||||
autofill(jt);
|
||||
return jt;
|
||||
}
|
||||
|
||||
/** Create JSON from parameters.
|
||||
This will apply funclets and autofill.
|
||||
*/
|
||||
template <class JsonValue,
|
||||
class... FN>
|
||||
Json::Value
|
||||
json (JsonValue&&jv, FN const&... fN)
|
||||
{
|
||||
auto jt = tx(
|
||||
std::forward<JsonValue>(jv),
|
||||
fN...);
|
||||
return std::move(jt.jv);
|
||||
}
|
||||
|
||||
/** Submit an existing JTx. */
|
||||
void
|
||||
submit (JTx const& tx);
|
||||
|
||||
/** Apply funclets and submit. */
|
||||
/** @{ */
|
||||
template <class JsonValue, class... FN>
|
||||
void
|
||||
apply (JsonValue&& jv, FN const&... fN)
|
||||
{
|
||||
submit(tx(std::forward<
|
||||
JsonValue>(jv), fN...));
|
||||
}
|
||||
|
||||
template <class JsonValue,
|
||||
class... FN>
|
||||
void
|
||||
operator()(JsonValue&& jv,
|
||||
FN const&... fN)
|
||||
{
|
||||
apply(std::forward<
|
||||
JsonValue>(jv), fN...);
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Create a new account with some XRP.
|
||||
|
||||
These convenience functions are for easy set-up
|
||||
of the environment, they bypass fee, seq, and sig
|
||||
settings. The XRP is transferred from the master
|
||||
account.
|
||||
|
||||
@param amount The amount of XRP to transfer.
|
||||
*/
|
||||
/** @{ */
|
||||
void
|
||||
fund (STAmount const& amount, Account const& account);
|
||||
|
||||
template<class... Accounts>
|
||||
void
|
||||
fund (STAmount const& amount, Account const& account0,
|
||||
Account const& account1, Accounts const&... accountN)
|
||||
{
|
||||
fund(amount, account0);
|
||||
fund(amount, account1, accountN...);
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Establish trust lines.
|
||||
|
||||
These convenience functions are for easy set-up
|
||||
of the environment, they bypass fee, seq, and sig
|
||||
settings.
|
||||
*/
|
||||
/** @{ */
|
||||
void
|
||||
trust (STAmount const& amount,
|
||||
Account const& account);
|
||||
|
||||
template<class... Accounts>
|
||||
void
|
||||
trust (STAmount const& amount, Account const& to0,
|
||||
Account const& to1, Accounts const&... toN)
|
||||
{
|
||||
trust(amount, to0);
|
||||
trust(amount, to1, toN...);
|
||||
}
|
||||
/** @} */
|
||||
|
||||
private:
|
||||
void
|
||||
autofill (JTx& jt);
|
||||
|
||||
inline
|
||||
void
|
||||
invoke (JTx&)
|
||||
{
|
||||
}
|
||||
|
||||
// Invoke funclets on tx
|
||||
template <class F, class... FN>
|
||||
void
|
||||
invoke (JTx& tx, F const& f,
|
||||
FN const&... fN)
|
||||
{
|
||||
f(*this, tx);
|
||||
invoke(tx, fN...);
|
||||
}
|
||||
|
||||
// Map of account IDs to Account
|
||||
std::unordered_map<
|
||||
ripple::Account, Account> map_;
|
||||
|
||||
bool fill_fee_ = true;
|
||||
bool fill_seq_ = true;
|
||||
bool fill_sig_ = true;
|
||||
};
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
212
src/ripple/app/tests/Env_test.cpp
Normal file
212
src/ripple/app/tests/Env_test.cpp
Normal file
@@ -0,0 +1,212 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2015 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/app/tests/Env.h>
|
||||
#include <ripple/json/to_string.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <beast/unit_test/suite.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class Env_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
testAutofill()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob");
|
||||
env(noop("alice"));
|
||||
env(noop("alice"), seq(none), fee(10), ter(temMALFORMED));
|
||||
env(noop("alice"), fee(none), ter(temMALFORMED));
|
||||
}
|
||||
|
||||
// Signing with secp256k1 and ed25519 keys
|
||||
void
|
||||
testKeyType()
|
||||
{
|
||||
using namespace jtx;
|
||||
|
||||
Env env(*this);
|
||||
Account const alice("alice", KeyType::ed25519);
|
||||
Account const bob("bob", KeyType::secp256k1);
|
||||
Account const carol("carol");
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
|
||||
// Master key only
|
||||
env(noop(alice));
|
||||
env(noop(bob));
|
||||
env(noop(alice), sig("alice"), ter(tefBAD_AUTH_MASTER));
|
||||
env(noop(alice), sig(Account("alice",
|
||||
KeyType::secp256k1)), ter(tefBAD_AUTH_MASTER));
|
||||
env(noop(bob), sig(Account("bob",
|
||||
KeyType::ed25519)), ter(tefBAD_AUTH_MASTER));
|
||||
env(noop(alice), sig(carol), ter(tefBAD_AUTH_MASTER));
|
||||
|
||||
// Master and Regular key
|
||||
env(regkey(alice, bob));
|
||||
env(noop(alice));
|
||||
env(noop(alice), sig(bob));
|
||||
env(noop(alice), sig(alice));
|
||||
|
||||
// Regular key only
|
||||
env(set(alice, asfDisableMaster), sig(alice));
|
||||
env(noop(alice));
|
||||
env(noop(alice), sig(bob));
|
||||
env(noop(alice), sig(alice), ter(tefMASTER_DISABLED));
|
||||
env(clear(alice, asfDisableMaster), sig(alice), ter(tefMASTER_DISABLED));
|
||||
env(clear(alice, asfDisableMaster), sig(bob));
|
||||
env(noop(alice), sig(alice));
|
||||
}
|
||||
|
||||
// Multi-sign basics
|
||||
void
|
||||
testMultiSign()
|
||||
{
|
||||
using namespace jtx;
|
||||
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice");
|
||||
env(signers("alice", 1,
|
||||
{ { "alice", 1 }, { "bob", 2 } }), ter(temBAD_SIGNER));
|
||||
env(signers("alice", 1,
|
||||
{ { "bob", 1 }, { "carol", 2 } }));
|
||||
env(noop("alice"));
|
||||
|
||||
env(noop("alice"), msig("bob"));
|
||||
env(noop("alice"), msig("carol"));
|
||||
env(noop("alice"), msig("bob", "carol"));
|
||||
env(noop("alice"), msig("bob", "carol", "dilbert"), ter(tefBAD_SIGNATURE));
|
||||
}
|
||||
|
||||
// Two level Multi-sign
|
||||
void
|
||||
testMultiSign2()
|
||||
{
|
||||
using namespace jtx;
|
||||
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob", "carol");
|
||||
env.fund(XRP(10000), "david", "eric", "frank", "greg");
|
||||
env(signers("alice", 2, { { "bob", 1 }, { "carol", 1 } }));
|
||||
env(signers("bob", 1, { { "david", 1 }, { "eric", 1 } }));
|
||||
env(signers("carol", 1, { { "frank", 1 }, { "greg", 1 } }));
|
||||
|
||||
env(noop("alice"), msig2(
|
||||
{ { "bob", "david" } }), ter(tefBAD_QUORUM));
|
||||
|
||||
env(noop("alice"), msig2(
|
||||
{ { "bob", "david" }, { "bob", "eric" } }), ter(tefBAD_QUORUM));
|
||||
|
||||
env(noop("alice"), msig2(
|
||||
{ { "carol", "frank" } }), ter(tefBAD_QUORUM));
|
||||
|
||||
env(noop("alice"), msig2(
|
||||
{ { "carol", "frank" }, { "carol", "greg" } }), ter(tefBAD_QUORUM));
|
||||
|
||||
env(noop("alice"), msig2(
|
||||
{ { "bob", "david" }, { "carol", "frank" } }));
|
||||
|
||||
env(noop("alice"), msig2(
|
||||
{ { "bob", "david" }, { "bob", "eric" },
|
||||
{ "carol", "frank" }, { "carol", "greg" } }));
|
||||
}
|
||||
|
||||
// Payment basics
|
||||
void
|
||||
testPayments()
|
||||
{
|
||||
using namespace jtx;
|
||||
|
||||
Env env(*this);
|
||||
auto const gw = Account("gateway");
|
||||
auto const USD = gw["USD"];
|
||||
|
||||
env(pay(env.master, "alice", XRP(1000)), fee(none), ter(temMALFORMED));
|
||||
env(pay(env.master, "alice", XRP(1000)), fee(1), ter(telINSUF_FEE_P));
|
||||
env(pay(env.master, "alice", XRP(1000)), seq(none), ter(temMALFORMED));
|
||||
env(pay(env.master, "alice", XRP(1000)), seq(2), ter(terPRE_SEQ));
|
||||
env(pay(env.master, "alice", XRP(1000)), sig(none), ter(temMALFORMED));
|
||||
env(pay(env.master, "alice", XRP(1000)), sig("bob"), ter(tefBAD_AUTH_MASTER));
|
||||
|
||||
env(pay(env.master, "dilbert", XRP(1000)), sig(env.master));
|
||||
|
||||
env.fund(XRP(10000), "alice", "bob", "carol", gw);
|
||||
expect(env["alice"].balance(XRP) == XRP(10000));
|
||||
expect(env["bob"].balance(XRP) == XRP(10000));
|
||||
expect(env["carol"].balance(XRP) == XRP(10000));
|
||||
expect(env[gw].balance(XRP) == XRP(10000));
|
||||
|
||||
env.trust(USD(100), "alice", "bob", "carol");
|
||||
env(rate(gw, 1.05));
|
||||
|
||||
env(pay(gw, "carol", USD(50)));
|
||||
expect(env["carol"].balance(USD) == USD(50));
|
||||
expect(env[gw].balance(Account("carol")["USD"]) == USD(-50));
|
||||
|
||||
env(offer("carol", XRP(50), USD(50)));
|
||||
env(pay("alice", "bob", USD(10)), ter(tecPATH_DRY));
|
||||
env(pay("alice", "bob", USD(10)), path(XRP(10)), ter(tecPATH_PARTIAL));
|
||||
env(pay("alice", "bob", USD(10)), path(XRP(20)));
|
||||
expect(env["bob"].balance(USD) == USD(10));
|
||||
expect(env["carol"].balance(USD) == USD(39.5));
|
||||
|
||||
env.memoize("eric");
|
||||
env(regkey("alice", "eric"));
|
||||
env(noop("alice"));
|
||||
env(noop("alice"), sig("alice"));
|
||||
env(noop("alice"), sig("eric"));
|
||||
env(noop("alice"), sig("bob"), ter(tefBAD_AUTH));
|
||||
env(set("alice", asfDisableMaster), ter(tecNEED_MASTER_KEY));
|
||||
env(set("alice", asfDisableMaster), sig("eric"), ter(tecNEED_MASTER_KEY));
|
||||
expect(! (env["alice"].flags() & lsfDisableMaster));
|
||||
env(set("alice", asfDisableMaster), sig("alice"));
|
||||
expect(env["alice"].flags() & lsfDisableMaster);
|
||||
env(regkey("alice", disabled), ter(tecMASTER_DISABLED));
|
||||
env(noop("alice"));
|
||||
env(noop("alice"), sig("alice"), ter(tefMASTER_DISABLED));
|
||||
env(noop("alice"), sig("eric"));
|
||||
env(noop("alice"), sig("bob"), ter(tefBAD_AUTH));
|
||||
env(clear("alice", asfDisableMaster), sig("bob"), ter(tefBAD_AUTH));
|
||||
env(clear("alice", asfDisableMaster), sig("alice"), ter(tefMASTER_DISABLED));
|
||||
env(clear("alice", asfDisableMaster));
|
||||
expect(! (env["alice"].flags() & lsfDisableMaster));
|
||||
env(regkey("alice", disabled));
|
||||
env(noop("alice"), sig("eric"), ter(tefBAD_AUTH_MASTER));
|
||||
env(noop("alice"));
|
||||
}
|
||||
|
||||
void
|
||||
run()
|
||||
{
|
||||
testAutofill();
|
||||
testKeyType();
|
||||
testMultiSign();
|
||||
testMultiSign2();
|
||||
testPayments();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Env,app,ripple)
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
392
src/ripple/app/tests/JTx.cpp
Normal file
392
src/ripple/app/tests/JTx.cpp
Normal file
@@ -0,0 +1,392 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2015 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/app/tests/JTx.h>
|
||||
#include <ripple/app/tests/Env.h>
|
||||
#include <ripple/app/paths/FindPaths.h>
|
||||
#include <ripple/basics/Slice.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <ripple/protocol/HashPrefix.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/protocol/STParsedJSON.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
namespace jtx {
|
||||
|
||||
Json::Value
|
||||
pay (Account const& account,
|
||||
Account const& to, STAmount const& amount)
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::Account] = account.human();
|
||||
jv[jss::Amount] = amount.getJson(0);
|
||||
jv[jss::Destination] = to.human();
|
||||
jv[jss::TransactionType] = "Payment";
|
||||
jv[jss::Flags] = tfUniversal;
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
offer (Account const& account,
|
||||
STAmount const& in, STAmount const& out)
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::Account] = account.human();
|
||||
jv[jss::TakerPays] = in.getJson(0);
|
||||
jv[jss::TakerGets] = out.getJson(0);
|
||||
jv[jss::TransactionType] = "OfferCreate";
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
rate (Account const& account, double multiplier)
|
||||
{
|
||||
if (multiplier > 4)
|
||||
throw std::runtime_error(
|
||||
"rate multiplier out of range");
|
||||
Json::Value jv;
|
||||
jv[jss::Account] = account.human();
|
||||
jv[jss::TransferRate] = std::uint32_t(
|
||||
1000000000 * multiplier);
|
||||
jv[jss::TransactionType] = "AccountSet";
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
regkey (Account const& account,
|
||||
disabled_t)
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::Account] = account.human();
|
||||
jv[jss::TransactionType] = "SetRegularKey";
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
regkey (Account const& account,
|
||||
Account const& signer)
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::Account] = account.human();
|
||||
jv["RegularKey"] = to_string(signer.id());
|
||||
jv[jss::TransactionType] = "SetRegularKey";
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
set (Account const& account,
|
||||
std::uint32_t on, std::uint32_t off)
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::Account] = account.human();
|
||||
jv[jss::TransactionType] = "AccountSet";
|
||||
if (on != 0)
|
||||
jv[jss::SetFlag] = on;
|
||||
if (off != 0)
|
||||
jv[jss::ClearFlag] = off;
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
signers (Account const& account,
|
||||
std::uint32_t quorum,
|
||||
std::vector<signer> const& v)
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::Account] = account.human();
|
||||
jv[jss::TransactionType] = "SignerListSet";
|
||||
jv["SignerQuorum"] = quorum;
|
||||
auto& ja = jv["SignerEntries"];
|
||||
ja.resize(v.size());
|
||||
for(std::size_t i = 0; i < v.size(); ++i)
|
||||
{
|
||||
auto const& e = v[i];
|
||||
auto& je = ja[i]["SignerEntry"];
|
||||
je[jss::Account] = e.account.human();
|
||||
je["SignerWeight"] = e.weight;
|
||||
}
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
signers (Account const& account, none_t)
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::Account] = account.human();
|
||||
jv[jss::TransactionType] = "SignerListSet";
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
trust (Account const& account,
|
||||
STAmount const& amount)
|
||||
{
|
||||
if (isXRP(amount))
|
||||
throw std::runtime_error(
|
||||
"trust() requires IOU");
|
||||
Json::Value jv;
|
||||
jv[jss::Account] = account.human();
|
||||
jv[jss::LimitAmount] = amount.getJson(0);
|
||||
jv[jss::TransactionType] = "TrustSet";
|
||||
jv[jss::Flags] = 0; // tfClearNoRipple;
|
||||
return jv;
|
||||
}
|
||||
|
||||
void
|
||||
fill_fee (Json::Value& jv,
|
||||
Ledger const& ledger)
|
||||
{
|
||||
if (jv.isMember(jss::Fee))
|
||||
return;
|
||||
jv[jss::Fee] = std::to_string(
|
||||
ledger.getBaseFee());
|
||||
}
|
||||
|
||||
void
|
||||
fill_seq (Json::Value& jv,
|
||||
Ledger const& ledger)
|
||||
{
|
||||
if (jv.isMember(jss::Sequence))
|
||||
return;
|
||||
RippleAddress ra;
|
||||
ra.setAccountID(jv[jss::Account].asString());
|
||||
auto const ar = ledger.getAccountRoot(
|
||||
ra.getAccountID());
|
||||
jv[jss::Sequence] =
|
||||
ar->getFieldU32(sfSequence);
|
||||
}
|
||||
|
||||
void
|
||||
sign (Json::Value& jv,
|
||||
Account const& account)
|
||||
{
|
||||
jv[jss::SigningPubKey] =
|
||||
strHex(make_Slice(
|
||||
account.pk().getAccountPublic()));
|
||||
Serializer ss;
|
||||
ss.add32 (HashPrefix::txSign);
|
||||
parse(jv).add(ss);
|
||||
jv[jss::TxnSignature] = strHex(make_Slice(
|
||||
account.sk().accountPrivateSign(
|
||||
ss.getData())));
|
||||
}
|
||||
|
||||
|
||||
STObject
|
||||
parse (Json::Value const& jv)
|
||||
{
|
||||
STParsedJSONObject p("tx_json", jv);
|
||||
if (! p.object)
|
||||
throw parse_error(
|
||||
rpcErrorString(p.error));
|
||||
return std::move(*p.object);
|
||||
}
|
||||
|
||||
void
|
||||
fee::operator()(Env const&, JTx& tx) const
|
||||
{
|
||||
if (boost::indeterminate(b_))
|
||||
tx[jss::Fee] =
|
||||
v_.getJson(0);
|
||||
else
|
||||
tx.fill_fee = b_;
|
||||
}
|
||||
|
||||
void
|
||||
flags::operator()(Env const&, JTx& tx) const
|
||||
{
|
||||
tx[jss::Flags] =
|
||||
v_ /*| tfUniversal*/;
|
||||
}
|
||||
|
||||
void
|
||||
path::operator()(Env const& env, JTx& jtx) const
|
||||
{
|
||||
auto& jv = jtx.jv;
|
||||
auto const from = env.lookup(
|
||||
jv[jss::Account].asString());
|
||||
auto const to = env.lookup(
|
||||
jv[jss::Destination].asString());
|
||||
jv[jss::SendMax] = sendmax_.getJson(0);
|
||||
STPath fp;
|
||||
STPathSet ps;
|
||||
auto const found = findPathsForOneIssuer(
|
||||
std::make_shared<RippleLineCache>(
|
||||
env.ledger), from, to,
|
||||
sendmax_.issue(), sendmax_,
|
||||
depth_, limit_,
|
||||
ps, fp);
|
||||
// VFALCO TODO API to allow caller to examine the STPathSet
|
||||
// VFALCO isDefault should be renamed to empty()
|
||||
if (found && ! ps.isDefault())
|
||||
jv[jss::Paths] = ps.getJson(0);
|
||||
}
|
||||
|
||||
void
|
||||
msig::operator()(Env const& env, JTx& tx) const
|
||||
{
|
||||
// VFALCO Inefficient pre-C++14
|
||||
auto accounts = accounts_;
|
||||
std::sort(accounts.begin(), accounts.end(),
|
||||
[](Account const& lhs, Account const& rhs)
|
||||
{
|
||||
return lhs.id() < rhs.id();
|
||||
});
|
||||
tx.signer = [accounts, &env](Env&, JTx& jt)
|
||||
{
|
||||
jt["SigningPubKey"] = "";
|
||||
boost::optional<STObject> st;
|
||||
try
|
||||
{
|
||||
st = parse(jt.jv);
|
||||
}
|
||||
catch(parse_error const&)
|
||||
{
|
||||
env.test.log << pretty(jt.jv);
|
||||
throw;
|
||||
}
|
||||
auto const signingForID = [](Json::Value const& jv)
|
||||
{
|
||||
RippleAddress ra;
|
||||
ra.setAccountID(jv[jss::Account].asString());
|
||||
return ra.getAccountID();
|
||||
}(jt.jv);
|
||||
auto& jv = jt["MultiSigners"][0u]["SigningFor"];
|
||||
jv[jss::Account] = jt[jss::Account];
|
||||
auto& js = jv["SigningAccounts"];
|
||||
js.resize(accounts.size());
|
||||
for(std::size_t i = 0; i < accounts.size(); ++i)
|
||||
{
|
||||
auto const& e = accounts[i];
|
||||
auto& jo = js[i]["SigningAccount"];
|
||||
jo[jss::Account] = e.human();
|
||||
jo[jss::SigningPubKey] = strHex(make_Slice(
|
||||
e.pk().getAccountPublic()));
|
||||
|
||||
Serializer ss;
|
||||
ss.add32 (HashPrefix::txMultiSign);
|
||||
st->addWithoutSigningFields(ss);
|
||||
ss.add160(signingForID);
|
||||
ss.add160(e.id());
|
||||
jo["MultiSignature"] = strHex(make_Slice(
|
||||
e.sk().accountPrivateSign(ss.getData())));
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
msig2_t::msig2_t (std::vector<std::pair<
|
||||
Account, Account>> sigs)
|
||||
{
|
||||
for (auto& sig : sigs)
|
||||
{
|
||||
auto result = sigs_.emplace(
|
||||
std::piecewise_construct,
|
||||
std::make_tuple(std::move(sig.first)),
|
||||
std::make_tuple());
|
||||
result.first->second.emplace(
|
||||
std::move(sig.second));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
msig2_t::operator()(Env const& env, JTx& tx) const
|
||||
{
|
||||
// VFALCO Inefficient pre-C++14
|
||||
auto const sigs = sigs_;
|
||||
tx.signer = [sigs, &env](Env&, JTx& jt)
|
||||
{
|
||||
jt["SigningPubKey"] = "";
|
||||
boost::optional<STObject> st;
|
||||
try
|
||||
{
|
||||
st = parse(jt.jv);
|
||||
}
|
||||
catch(parse_error const&)
|
||||
{
|
||||
env.test.log << pretty(jt.jv);
|
||||
throw;
|
||||
}
|
||||
auto& ja = jt["MultiSigners"];
|
||||
ja.resize(sigs.size());
|
||||
for (auto i = std::make_pair(0, sigs.begin());
|
||||
i.first < sigs.size(); ++i.first, ++i.second)
|
||||
{
|
||||
auto const& sign_for = i.second->first;
|
||||
auto const& list = i.second->second;
|
||||
auto& ji = ja[i.first]["SigningFor"];
|
||||
ji[jss::Account] = sign_for.human();
|
||||
auto& js = ji["SigningAccounts"];
|
||||
js.resize(list.size());
|
||||
for (auto j = std::make_pair(0, list.begin());
|
||||
j.first < list.size(); ++j.first, ++j.second)
|
||||
{
|
||||
auto& jj = js[j.first]["SigningAccount"];
|
||||
jj[jss::Account] = j.second->human();
|
||||
jj[jss::SigningPubKey] = strHex(make_Slice(
|
||||
j.second->pk().getAccountPublic()));
|
||||
|
||||
Serializer ss;
|
||||
ss.add32 (HashPrefix::txMultiSign);
|
||||
st->addWithoutSigningFields(ss);
|
||||
ss.add160(sign_for.id());
|
||||
ss.add160(j.second->id());
|
||||
jj["MultiSignature"] = strHex(make_Slice(
|
||||
j.second->sk().accountPrivateSign(
|
||||
ss.getData())));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
seq::operator()(Env const&, JTx& tx) const
|
||||
{
|
||||
if (boost::indeterminate(b_))
|
||||
tx[jss::Sequence] = v_;
|
||||
else
|
||||
tx.fill_seq = b_;
|
||||
}
|
||||
|
||||
void
|
||||
sig::operator()(Env const&, JTx& tx) const
|
||||
{
|
||||
if(boost::indeterminate(b_))
|
||||
{
|
||||
// VFALCO Inefficient pre-C++14
|
||||
auto const account = account_;
|
||||
tx.signer = [account](Env&, JTx& jt)
|
||||
{
|
||||
jtx::sign(jt.jv, account);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
tx.fill_sig = b_;
|
||||
}
|
||||
}
|
||||
|
||||
} // jtx
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
453
src/ripple/app/tests/JTx.h
Normal file
453
src/ripple/app/tests/JTx.h
Normal file
@@ -0,0 +1,453 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2015 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_APP_TESTS_JTX_H_INCLUDED
|
||||
#define RIPPLE_APP_TESTS_JTX_H_INCLUDED
|
||||
|
||||
#include <ripple/app/tests/Common.h>
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/json/json_value.h>
|
||||
#include <ripple/protocol/Issue.h>
|
||||
#include <ripple/protocol/RippleAddress.h>
|
||||
#include <ripple/protocol/STAmount.h>
|
||||
#include <ripple/protocol/STObject.h>
|
||||
#include <ripple/protocol/STTx.h>
|
||||
#include <beast/unit_test/suite.h>
|
||||
#include <boost/logic/tribool.hpp>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class Env;
|
||||
|
||||
BOOST_TRIBOOL_THIRD_STATE(use_default)
|
||||
|
||||
/** Execution context for applying a JSON transaction.
|
||||
This augments the transaction with various settings.
|
||||
*/
|
||||
struct JTx
|
||||
{
|
||||
Json::Value jv;
|
||||
boost::tribool fill_fee = boost::logic::indeterminate;
|
||||
boost::tribool fill_seq = boost::logic::indeterminate;
|
||||
boost::tribool fill_sig = boost::logic::indeterminate;
|
||||
std::function<void(Env&, JTx&)> signer;
|
||||
TER ter = tesSUCCESS;
|
||||
|
||||
JTx() = default;
|
||||
|
||||
JTx (Json::Value&& jv_)
|
||||
: jv(std::move(jv_))
|
||||
{
|
||||
}
|
||||
|
||||
JTx (Json::Value const& jv_)
|
||||
: jv(jv_)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
Json::Value&
|
||||
operator[](Key const& key)
|
||||
{
|
||||
return jv[key];
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace jtx {
|
||||
|
||||
struct none_t { none_t() { } };
|
||||
static none_t const none;
|
||||
|
||||
struct autofill_t { autofill_t() { } };
|
||||
static autofill_t const autofill;
|
||||
|
||||
struct disabled_t { disabled_t() { } };
|
||||
static disabled_t const disabled;
|
||||
|
||||
//
|
||||
// JSON generators
|
||||
//
|
||||
|
||||
/** Create a payment. */
|
||||
Json::Value
|
||||
pay (Account const& account,
|
||||
Account const& to, STAmount const& amount);
|
||||
|
||||
/** Create an offer. */
|
||||
Json::Value
|
||||
offer (Account const& account,
|
||||
STAmount const& in, STAmount const& out);
|
||||
|
||||
/** Set a transfer rate. */
|
||||
Json::Value
|
||||
rate (Account const& account,
|
||||
double multiplier);
|
||||
|
||||
/** Disable the regular key. */
|
||||
Json::Value
|
||||
regkey (Account const& account,
|
||||
disabled_t);
|
||||
|
||||
/** Set a regular key. */
|
||||
Json::Value
|
||||
regkey (Account const& account,
|
||||
Account const& signer);
|
||||
|
||||
/** Add and/or remove flag. */
|
||||
Json::Value
|
||||
set (Account const& account,
|
||||
std::uint32_t on, std::uint32_t off = 0);
|
||||
|
||||
/** Remove account flag. */
|
||||
inline
|
||||
Json::Value
|
||||
clear (Account const& account,
|
||||
std::uint32_t off)
|
||||
{
|
||||
return set(account, 0, off);
|
||||
}
|
||||
|
||||
/** The null transaction. */
|
||||
inline
|
||||
Json::Value
|
||||
noop (Account const& account)
|
||||
{
|
||||
return set(account, 0);
|
||||
}
|
||||
|
||||
struct signer
|
||||
{
|
||||
std::uint32_t weight;
|
||||
Account account;
|
||||
|
||||
signer (Account account_,
|
||||
std::uint32_t weight_ = 1)
|
||||
: weight(weight_)
|
||||
, account(std::move(account_))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
Json::Value
|
||||
signers (Account const& account,
|
||||
std::uint32_t quorum,
|
||||
std::vector<signer> const& v);
|
||||
|
||||
/** Remove a signer list. */
|
||||
Json::Value
|
||||
signers (Account const& account, none_t);
|
||||
|
||||
/** Modify a trust line. */
|
||||
Json::Value
|
||||
trust (Account const& account,
|
||||
STAmount const& amount);
|
||||
|
||||
/** Set the fee automatically. */
|
||||
void
|
||||
fill_fee (Json::Value& jv,
|
||||
Ledger const& ledger);
|
||||
|
||||
/** Set the sequence number automatically. */
|
||||
void
|
||||
fill_seq (Json::Value& jv,
|
||||
Ledger const& ledger);
|
||||
|
||||
/** Sign automatically.
|
||||
@note This only works on accounts with multi-signing off.
|
||||
*/
|
||||
void
|
||||
sign (Json::Value& jv,
|
||||
Account const& account);
|
||||
|
||||
/** Thrown when parse fails. */
|
||||
struct parse_error : std::logic_error
|
||||
{
|
||||
template <class String>
|
||||
explicit
|
||||
parse_error (String const& s)
|
||||
: logic_error(s)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/** Convert JSON to STObject.
|
||||
This throws on failure, the JSON must be correct.
|
||||
@note Testing malformed JSON is beyond the scope of
|
||||
this set of unit test routines.
|
||||
*/
|
||||
STObject
|
||||
parse (Json::Value const& jv);
|
||||
|
||||
//
|
||||
// Funclets
|
||||
//
|
||||
|
||||
/** Set the fee on a JTx. */
|
||||
class fee
|
||||
{
|
||||
private:
|
||||
STAmount v_;
|
||||
boost::tribool b_ =
|
||||
boost::logic::indeterminate;
|
||||
|
||||
public:
|
||||
explicit
|
||||
fee (autofill_t)
|
||||
: b_(true)
|
||||
{
|
||||
}
|
||||
|
||||
explicit
|
||||
fee (none_t)
|
||||
: b_(false)
|
||||
{
|
||||
}
|
||||
|
||||
explicit
|
||||
fee (STAmount const& v)
|
||||
: v_(v)
|
||||
{
|
||||
if (! isXRP(v_))
|
||||
throw std::runtime_error(
|
||||
"fee: not XRP");
|
||||
}
|
||||
|
||||
void
|
||||
operator()(Env const&, JTx& tx) const;
|
||||
};
|
||||
|
||||
/** Set the flags on a JTx. */
|
||||
class flags
|
||||
{
|
||||
private:
|
||||
std::uint32_t v_;
|
||||
|
||||
public:
|
||||
explicit
|
||||
flags (std::uint32_t v)
|
||||
: v_(v)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(Env const&, JTx& tx) const;
|
||||
};
|
||||
|
||||
/** Set Paths, SendMax on a JTx. */
|
||||
class path
|
||||
{
|
||||
private:
|
||||
int depth_;
|
||||
unsigned int limit_;
|
||||
STAmount sendmax_;
|
||||
|
||||
public:
|
||||
path (STAmount const& sendmax, int depth = 7,
|
||||
unsigned int limit = 4)
|
||||
: depth_(depth)
|
||||
, limit_(limit)
|
||||
, sendmax_(sendmax)
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(Env const&, JTx& jtx) const;
|
||||
};
|
||||
|
||||
/** Set a multisignature on a JTx. */
|
||||
class msig
|
||||
{
|
||||
private:
|
||||
std::vector<Account> accounts_;
|
||||
|
||||
public:
|
||||
msig (std::vector<Account> accounts)
|
||||
: accounts_(std::move(accounts))
|
||||
{
|
||||
}
|
||||
|
||||
template <class AccountType, class... Accounts>
|
||||
msig (AccountType&& a0, Accounts&&... aN)
|
||||
: msig(make_vector(
|
||||
std::forward<AccountType>(a0),
|
||||
std::forward<Accounts>(aN)...))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(Env const&, JTx& tx) const;
|
||||
|
||||
private:
|
||||
template <class AccountType>
|
||||
static
|
||||
void
|
||||
helper (std::vector<Account>& v,
|
||||
AccountType&& account)
|
||||
{
|
||||
v.emplace_back(std::forward<
|
||||
Account>(account));
|
||||
}
|
||||
|
||||
template <class AccountType, class... Accounts>
|
||||
static
|
||||
void
|
||||
helper (std::vector<Account>& v,
|
||||
AccountType&& a0, Accounts&&... aN)
|
||||
{
|
||||
helper(v, std::forward<AccountType>(a0));
|
||||
helper(v, std::forward<Accounts>(aN)...);
|
||||
}
|
||||
|
||||
template <class... Accounts>
|
||||
static
|
||||
std::vector<Account>
|
||||
make_vector(Accounts&&... accounts)
|
||||
{
|
||||
std::vector<Account> v;
|
||||
v.reserve(sizeof...(accounts));
|
||||
helper(v, std::forward<
|
||||
Accounts>(accounts)...);
|
||||
return v;
|
||||
}
|
||||
};
|
||||
|
||||
/** Set a multisignature on a JTx. */
|
||||
class msig2_t
|
||||
{
|
||||
private:
|
||||
std::map<Account,
|
||||
std::set<Account>> sigs_;
|
||||
|
||||
public:
|
||||
msig2_t (std::vector<std::pair<
|
||||
Account, Account>> sigs);
|
||||
|
||||
void
|
||||
operator()(Env const&, JTx& tx) const;
|
||||
};
|
||||
|
||||
inline
|
||||
msig2_t
|
||||
msig2 (std::vector<std::pair<
|
||||
Account, Account>> sigs)
|
||||
{
|
||||
return msig2_t(std::move(sigs));
|
||||
}
|
||||
|
||||
/** Set the sequence number on a JTx. */
|
||||
struct seq
|
||||
{
|
||||
private:
|
||||
std::uint32_t v_;
|
||||
boost::tribool b_ =
|
||||
boost::logic::indeterminate;
|
||||
|
||||
public:
|
||||
explicit
|
||||
seq (autofill_t)
|
||||
: b_(true)
|
||||
{
|
||||
}
|
||||
|
||||
explicit
|
||||
seq (none_t)
|
||||
: b_(false)
|
||||
{
|
||||
}
|
||||
|
||||
explicit
|
||||
seq (std::uint32_t v)
|
||||
: v_(v)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(Env const&, JTx& tx) const;
|
||||
};
|
||||
|
||||
/** Set the regular signature on a JTx.
|
||||
@note For multisign, use msig.
|
||||
*/
|
||||
class sig
|
||||
{
|
||||
private:
|
||||
Account account_;
|
||||
boost::tribool b_ =
|
||||
boost::logic::indeterminate;
|
||||
|
||||
public:
|
||||
explicit
|
||||
sig (autofill_t)
|
||||
: b_(true)
|
||||
{
|
||||
}
|
||||
|
||||
explicit
|
||||
sig (none_t)
|
||||
: b_(false)
|
||||
{
|
||||
}
|
||||
|
||||
explicit
|
||||
sig (Account const& account)
|
||||
: account_(account)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(Env const&, JTx& tx) const;
|
||||
};
|
||||
|
||||
/** Set the expected result code for a JTx
|
||||
The test will fail if the code doesn't match.
|
||||
*/
|
||||
class ter
|
||||
{
|
||||
private:
|
||||
TER v_;
|
||||
|
||||
public:
|
||||
explicit
|
||||
ter (TER v)
|
||||
: v_(v)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(Env const&, JTx& tx) const
|
||||
{
|
||||
tx.ter = v_;
|
||||
}
|
||||
};
|
||||
|
||||
} // jtx
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
@@ -45,11 +45,12 @@ static
|
||||
std::uint64_t const
|
||||
SYSTEM_CURRENCY_USERS = 100000000;
|
||||
|
||||
/** Number of drops per 1 XRP */
|
||||
static
|
||||
std::uint64_t const
|
||||
SYSTEM_CURRENCY_PARTS = 1000000;
|
||||
|
||||
/** Calculate the amount of native currency created at genesis. */
|
||||
/** Number of drops in the genesis account. */
|
||||
static
|
||||
std::uint64_t const
|
||||
SYSTEM_CURRENCY_START = SYSTEM_CURRENCY_GIFT * SYSTEM_CURRENCY_USERS * SYSTEM_CURRENCY_PARTS;
|
||||
|
||||
26
src/ripple/unity/app_tests.cpp
Normal file
26
src/ripple/unity/app_tests.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
|
||||
#include <ripple/app/tests/Common.cpp>
|
||||
#include <ripple/app/tests/Env.cpp>
|
||||
#include <ripple/app/tests/JTx.cpp>
|
||||
|
||||
#include <ripple/app/tests/Env_test.cpp>
|
||||
Reference in New Issue
Block a user