Enhance AccountTx unit test

This commit is contained in:
Scott Schurr
2019-06-17 17:09:42 -07:00
committed by Nik Bougalis
parent 4900e3081d
commit 28b942b186
10 changed files with 453 additions and 107 deletions

View File

@@ -2511,6 +2511,7 @@ else ()
src/test/jtx/impl/WSClient.cpp src/test/jtx/impl/WSClient.cpp
src/test/jtx/impl/amount.cpp src/test/jtx/impl/amount.cpp
src/test/jtx/impl/balance.cpp src/test/jtx/impl/balance.cpp
src/test/jtx/impl/check.cpp
src/test/jtx/impl/delivermin.cpp src/test/jtx/impl/delivermin.cpp
src/test/jtx/impl/deposit.cpp src/test/jtx/impl/deposit.cpp
src/test/jtx/impl/envconfig.cpp src/test/jtx/impl/envconfig.cpp

View File

@@ -20,79 +20,10 @@
#include <test/jtx.h> #include <test/jtx.h>
#include <ripple/protocol/Feature.h> #include <ripple/protocol/Feature.h>
#include <ripple/protocol/jss.h> #include <ripple/protocol/jss.h>
#include <ripple/protocol/TxFlags.h>
namespace ripple { namespace ripple {
// For the time being Checks seem pretty much self contained. So the
// functions that operate on jtx are defined here, locally. If they are
// needed by other unit tests they could put in another file.
namespace test { namespace test {
namespace jtx { namespace jtx {
namespace check {
// Create a check.
Json::Value
create (jtx::Account const& account,
jtx::Account const& dest, STAmount const& sendMax)
{
Json::Value jv;
jv[sfAccount.jsonName] = account.human();
jv[sfSendMax.jsonName] = sendMax.getJson(JsonOptions::none);
jv[sfDestination.jsonName] = dest.human();
jv[sfTransactionType.jsonName] = jss::CheckCreate;
jv[sfFlags.jsonName] = tfUniversal;
return jv;
}
// Type used to specify DeliverMin for cashing a check.
struct DeliverMin
{
STAmount value;
explicit DeliverMin (STAmount const& deliverMin)
: value (deliverMin) { }
};
// Cash a check.
Json::Value
cash (jtx::Account const& dest,
uint256 const& checkId, STAmount const& amount)
{
Json::Value jv;
jv[sfAccount.jsonName] = dest.human();
jv[sfAmount.jsonName] = amount.getJson(JsonOptions::none);
jv[sfCheckID.jsonName] = to_string (checkId);
jv[sfTransactionType.jsonName] = jss::CheckCash;
jv[sfFlags.jsonName] = tfUniversal;
return jv;
}
Json::Value
cash (jtx::Account const& dest,
uint256 const& checkId, DeliverMin const& atLeast)
{
Json::Value jv;
jv[sfAccount.jsonName] = dest.human();
jv[sfDeliverMin.jsonName] = atLeast.value.getJson(JsonOptions::none);
jv[sfCheckID.jsonName] = to_string (checkId);
jv[sfTransactionType.jsonName] = jss::CheckCash;
jv[sfFlags.jsonName] = tfUniversal;
return jv;
}
// Cancel a check.
Json::Value
cancel (jtx::Account const& dest, uint256 const& checkId)
{
Json::Value jv;
jv[sfAccount.jsonName] = dest.human();
jv[sfCheckID.jsonName] = to_string (checkId);
jv[sfTransactionType.jsonName] = jss::CheckCancel;
jv[sfFlags.jsonName] = tfUniversal;
return jv;
}
} // namespace check
/** Set Expiration on a JTx. */ /** Set Expiration on a JTx. */
class expiration class expiration

View File

@@ -26,6 +26,7 @@
#include <test/jtx/Account.h> #include <test/jtx/Account.h>
#include <test/jtx/amount.h> #include <test/jtx/amount.h>
#include <test/jtx/balance.h> #include <test/jtx/balance.h>
#include <test/jtx/check.h>
#include <test/jtx/delivermin.h> #include <test/jtx/delivermin.h>
#include <test/jtx/deposit.h> #include <test/jtx/deposit.h>
#include <test/jtx/Env.h> #include <test/jtx/Env.h>

71
src/test/jtx/check.h Normal file
View File

@@ -0,0 +1,71 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2019 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_TEST_JTX_CHECK_H_INCLUDED
#define RIPPLE_TEST_JTX_CHECK_H_INCLUDED
#include <test/jtx/Env.h>
#include <test/jtx/Account.h>
#include <test/jtx/owners.h>
namespace ripple {
namespace test {
namespace jtx {
/** Check operations. */
namespace check {
/** Create a check. */
Json::Value
create (jtx::Account const& account,
jtx::Account const& dest, STAmount const& sendMax);
/** Cash a check requiring that a specific amount be delivered. */
Json::Value
cash (jtx::Account const& dest,
uint256 const& checkId, STAmount const& amount);
/** Type used to specify DeliverMin for cashing a check. */
struct DeliverMin
{
STAmount value;
explicit DeliverMin (STAmount const& deliverMin)
: value (deliverMin) { }
};
/** Cash a check requiring that at least a minimum amount be delivered. */
Json::Value
cash (jtx::Account const& dest,
uint256 const& checkId, DeliverMin const& atLeast);
/** Cancel a check. */
Json::Value
cancel (jtx::Account const& dest, uint256 const& checkId);
} // check
/** Match the number of checks on the account. */
using checks = owner_count<ltCHECK>;
} // jtx
} // test
} // ripple
#endif

View File

@@ -0,0 +1,88 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2019 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 <test/jtx/check.h>
#include <ripple/protocol/jss.h>
#include <ripple/protocol/TxFlags.h>
namespace ripple {
namespace test {
namespace jtx {
namespace check {
// Create a check.
Json::Value
create (jtx::Account const& account,
jtx::Account const& dest, STAmount const& sendMax)
{
Json::Value jv;
jv[sfAccount.jsonName] = account.human();
jv[sfSendMax.jsonName] = sendMax.getJson(JsonOptions::none);
jv[sfDestination.jsonName] = dest.human();
jv[sfTransactionType.jsonName] = jss::CheckCreate;
jv[sfFlags.jsonName] = tfUniversal;
return jv;
}
// Cash a check requiring that a specific amount be delivered.
Json::Value
cash (jtx::Account const& dest,
uint256 const& checkId, STAmount const& amount)
{
Json::Value jv;
jv[sfAccount.jsonName] = dest.human();
jv[sfAmount.jsonName] = amount.getJson(JsonOptions::none);
jv[sfCheckID.jsonName] = to_string (checkId);
jv[sfTransactionType.jsonName] = jss::CheckCash;
jv[sfFlags.jsonName] = tfUniversal;
return jv;
}
// Cash a check requiring that at least a minimum amount be delivered.
Json::Value
cash (jtx::Account const& dest,
uint256 const& checkId, DeliverMin const& atLeast)
{
Json::Value jv;
jv[sfAccount.jsonName] = dest.human();
jv[sfDeliverMin.jsonName] = atLeast.value.getJson(JsonOptions::none);
jv[sfCheckID.jsonName] = to_string (checkId);
jv[sfTransactionType.jsonName] = jss::CheckCash;
jv[sfFlags.jsonName] = tfUniversal;
return jv;
}
// Cancel a check.
Json::Value
cancel (jtx::Account const& dest, uint256 const& checkId)
{
Json::Value jv;
jv[sfAccount.jsonName] = dest.human();
jv[sfCheckID.jsonName] = to_string (checkId);
jv[sfTransactionType.jsonName] = jss::CheckCancel;
jv[sfFlags.jsonName] = tfUniversal;
return jv;
}
} // check
} // jtx
} // test
} // ripple

View File

@@ -380,18 +380,9 @@ public:
BEAST_EXPECT (state[sfBalance.jsonName][jss::value].asInt() == -5); BEAST_EXPECT (state[sfBalance.jsonName][jss::value].asInt() == -5);
BEAST_EXPECT (state[sfHighLimit.jsonName][jss::value].asUInt() == 1000); BEAST_EXPECT (state[sfHighLimit.jsonName][jss::value].asUInt() == 1000);
} }
{ // gw writes a check for USD(10) to alice.
// gw writes a check for USD(10) to alice. env (check::create (gw, alice, USD(10)));
Json::Value jvCheck; env.close();
jvCheck[sfAccount.jsonName] = gw.human();
jvCheck[sfSendMax.jsonName] =
USD(10).value().getJson(JsonOptions::none);
jvCheck[sfDestination.jsonName] = alice.human();
jvCheck[sfTransactionType.jsonName] = jss::CheckCreate;
jvCheck[sfFlags.jsonName] = tfUniversal;
env (jvCheck);
env.close();
}
{ {
// Find the check. // Find the check.
Json::Value const resp = acct_objs (gw, jss::check); Json::Value const resp = acct_objs (gw, jss::check);

View File

@@ -26,7 +26,7 @@ namespace ripple {
namespace test { namespace test {
class AccountTX_test : public beast::unit_test::suite class AccountTx_test : public beast::unit_test::suite
{ {
void void
testParameters() testParameters()
@@ -164,15 +164,298 @@ class AccountTX_test : public beast::unit_test::suite
} }
} }
void
testContents()
{
// Get results for all transaction types that can be associated
// with an account. Start by generating all transaction types.
using namespace test::jtx;
using namespace std::chrono_literals;
Env env(*this);
Account const alice {"alice"};
Account const alie {"alie"};
Account const gw {"gw"};
auto const USD {gw["USD"]};
env.fund(XRP(1000000), alice, gw);
env.close();
// AccountSet
env (noop (alice));
// Payment
env (pay (alice, gw, XRP (100)));
// Regular key set
env (regkey(alice, alie));
env.close();
// Trust and Offers
env (trust (alice, USD (200)), sig (alie));
std::uint32_t const offerSeq {env.seq(alice)};
env (offer (alice, USD (50), XRP (150)), sig (alie));
env.close();
{
Json::Value cancelOffer;
cancelOffer[jss::Account] = alice.human();
cancelOffer[jss::OfferSequence] = offerSeq;
cancelOffer[jss::TransactionType] = jss::OfferCancel;
env (cancelOffer, sig (alie));
}
env.close();
// SignerListSet
env (signers (alice, 1, {{"bogie", 1}, {"demon", 1}}), sig (alie));
// Escrow
{
// Create an escrow. Requires either a CancelAfter or FinishAfter.
auto escrow = [] (Account const& account,
Account const& to, STAmount const& amount)
{
Json::Value escro;
escro[jss::TransactionType] = jss::EscrowCreate;
escro[jss::Flags] = tfUniversal;
escro[jss::Account] = account.human();
escro[jss::Destination] = to.human();
escro[jss::Amount] = amount.getJson(JsonOptions::none);
return escro;
};
NetClock::time_point const nextTime {env.now() + 2s};
Json::Value escrowWithFinish {escrow (alice, alice, XRP (500))};
escrowWithFinish[sfFinishAfter.jsonName] =
nextTime.time_since_epoch().count();
std::uint32_t const escrowFinishSeq {env.seq(alice)};
env (escrowWithFinish, sig (alie));
Json::Value escrowWithCancel {escrow (alice, alice, XRP (500))};
escrowWithCancel[sfFinishAfter.jsonName] =
nextTime.time_since_epoch().count();
escrowWithCancel[sfCancelAfter.jsonName] =
nextTime.time_since_epoch().count() + 1;
std::uint32_t const escrowCancelSeq {env.seq(alice)};
env (escrowWithCancel, sig (alie));
env.close();
{
Json::Value escrowFinish;
escrowFinish[jss::TransactionType] = jss::EscrowFinish;
escrowFinish[jss::Flags] = tfUniversal;
escrowFinish[jss::Account] = alice.human();
escrowFinish[sfOwner.jsonName] = alice.human();
escrowFinish[sfOfferSequence.jsonName] = escrowFinishSeq;
env (escrowFinish, sig (alie));
}
{
Json::Value escrowCancel;
escrowCancel[jss::TransactionType] = jss::EscrowCancel;
escrowCancel[jss::Flags] = tfUniversal;
escrowCancel[jss::Account] = alice.human();
escrowCancel[sfOwner.jsonName] = alice.human();
escrowCancel[sfOfferSequence.jsonName] = escrowCancelSeq;
env (escrowCancel, sig (alie));
}
env.close();
}
// PayChan
{
std::uint32_t payChanSeq {env.seq (alice)};
Json::Value payChanCreate;
payChanCreate[jss::TransactionType] = jss::PaymentChannelCreate;
payChanCreate[jss::Flags] = tfUniversal;
payChanCreate[jss::Account] = alice.human();
payChanCreate[jss::Destination] = gw.human();
payChanCreate[jss::Amount] =
XRP (500).value().getJson (JsonOptions::none);
payChanCreate[sfSettleDelay.jsonName] =
NetClock::duration{100s}.count();
payChanCreate[sfPublicKey.jsonName] = strHex (alice.pk().slice());
env (payChanCreate, sig (alie));
env.close();
std::string const payChanIndex {
strHex (keylet::payChan (alice, gw, payChanSeq).key)};
{
Json::Value payChanFund;
payChanFund[jss::TransactionType] = jss::PaymentChannelFund;
payChanFund[jss::Flags] = tfUniversal;
payChanFund[jss::Account] = alice.human();
payChanFund[sfPayChannel.jsonName] = payChanIndex;
payChanFund[jss::Amount] =
XRP (200).value().getJson (JsonOptions::none);
env (payChanFund, sig (alie));
env.close();
}
{
Json::Value payChanClaim;
payChanClaim[jss::TransactionType] = jss::PaymentChannelClaim;
payChanClaim[jss::Flags] = tfClose;
payChanClaim[jss::Account] = gw.human();
payChanClaim[sfPayChannel.jsonName] = payChanIndex;
payChanClaim[sfPublicKey.jsonName] = strHex(alice.pk().slice());
env (payChanClaim);
env.close();
}
}
// Check
{
uint256 const aliceCheckId {
getCheckIndex (alice, env.seq (alice))};
env (check::create (alice, gw, XRP (300)), sig (alie));
uint256 const gwCheckId {
getCheckIndex (gw, env.seq (gw))};
env (check::create (gw, alice, XRP (200)));
env.close();
env (check::cash (alice, gwCheckId, XRP (200)), sig (alie));
env (check::cancel (alice, aliceCheckId), sig (alie));
env.close();
}
// Deposit preauthorization.
env (deposit::auth (alice, gw), sig (alie));
env.close();
// Setup is done. Look at the transactions returned by account_tx.
Json::Value params;
params[jss::account] = alice.human();
params[jss::ledger_index_min] = -1;
params[jss::ledger_index_max] = -1;
Json::Value const result {
env.rpc("json", "account_tx", to_string(params))};
BEAST_EXPECT (result[jss::result][jss::status] == "success");
BEAST_EXPECT (result[jss::result][jss::transactions].isArray());
Json::Value const& txs {result[jss::result][jss::transactions]};
// Do a sanity check on each returned transaction. They should
// be returned in the reverse order of application to the ledger.
struct NodeSanity
{
int const index;
Json::StaticString const& txType;
std::initializer_list<char const*> created;
std::initializer_list<char const*> deleted;
std::initializer_list<char const*> modified;
};
auto checkSanity = [this] (
Json::Value const& txNode, NodeSanity const& sane)
{
BEAST_EXPECT(txNode[jss::validated].asBool() == true);
BEAST_EXPECT(
txNode[jss::tx][sfTransactionType.jsonName].asString() ==
sane.txType);
// Make sure all of the expected node types are present.
std::vector<std::string> createdNodes {};
std::vector<std::string> deletedNodes {};
std::vector<std::string> modifiedNodes{};
for (Json::Value const& metaNode :
txNode[jss::meta][sfAffectedNodes.jsonName])
{
if (metaNode.isMember (sfCreatedNode.jsonName))
createdNodes.push_back (
metaNode[sfCreatedNode.jsonName]
[sfLedgerEntryType.jsonName].asString());
else if (metaNode.isMember (sfDeletedNode.jsonName))
deletedNodes.push_back (
metaNode[sfDeletedNode.jsonName]
[sfLedgerEntryType.jsonName].asString());
else if (metaNode.isMember (sfModifiedNode.jsonName))
modifiedNodes.push_back (
metaNode[sfModifiedNode.jsonName]
[sfLedgerEntryType.jsonName].asString());
else
fail ("Unexpected or unlabeled node type in metadata.",
__FILE__, __LINE__);
}
auto cmpNodeTypes = [this] (
char const* const errMsg,
std::vector<std::string>& got,
std::initializer_list<char const*> expList)
{
std::sort (got.begin(), got.end());
std::vector<std::string> exp;
exp.reserve (expList.size());
for (char const* nodeType : expList)
exp.push_back (nodeType);
std::sort (exp.begin(), exp.end());
if (got != exp)
{
fail (errMsg, __FILE__, __LINE__);
}
};
cmpNodeTypes ("Created mismatch", createdNodes, sane.created);
cmpNodeTypes ("Deleted mismatch", deletedNodes, sane.deleted);
cmpNodeTypes ("Modified mismatch", modifiedNodes, sane.modified);
};
static const NodeSanity sanity[]
{
// txType, created, deleted, modified
{ 0, jss::DepositPreauth, {jss::DepositPreauth}, {}, {jss::AccountRoot, jss::DirectoryNode}},
{ 1, jss::CheckCancel, {}, {jss::Check}, {jss::AccountRoot, jss::DirectoryNode, jss::DirectoryNode}},
{ 2, jss::CheckCash, {}, {jss::Check}, {jss::AccountRoot, jss::AccountRoot, jss::DirectoryNode, jss::DirectoryNode}},
{ 3, jss::CheckCreate, {jss::Check}, {}, {jss::AccountRoot, jss::DirectoryNode, jss::DirectoryNode}},
{ 4, jss::CheckCreate, {jss::Check}, {}, {jss::AccountRoot, jss::DirectoryNode, jss::DirectoryNode}},
{ 5, jss::PaymentChannelClaim, {}, {jss::PayChannel}, {jss::AccountRoot, jss::AccountRoot, jss::DirectoryNode}},
{ 6, jss::PaymentChannelFund, {}, {}, {jss::AccountRoot, jss::PayChannel }},
{ 7, jss::PaymentChannelCreate, {jss::PayChannel}, {}, {jss::AccountRoot, jss::AccountRoot, jss::DirectoryNode}},
{ 8, jss::EscrowCancel, {}, {jss::Escrow}, {jss::AccountRoot, jss::DirectoryNode}},
{ 9, jss::EscrowFinish, {}, {jss::Escrow}, {jss::AccountRoot, jss::DirectoryNode}},
{ 10, jss::EscrowCreate, {jss::Escrow}, {}, {jss::AccountRoot, jss::DirectoryNode}},
{ 11, jss::EscrowCreate, {jss::Escrow}, {}, {jss::AccountRoot, jss::DirectoryNode}},
{ 12, jss::SignerListSet, {jss::SignerList}, {}, {jss::AccountRoot, jss::DirectoryNode}},
{ 13, jss::OfferCancel, {}, {jss::Offer, jss::DirectoryNode}, {jss::AccountRoot, jss::DirectoryNode}},
{ 14, jss::OfferCreate, {jss::Offer, jss::DirectoryNode}, {}, {jss::AccountRoot, jss::DirectoryNode}},
{ 15, jss::TrustSet, {jss::RippleState, jss::DirectoryNode, jss::DirectoryNode}, {}, {jss::AccountRoot, jss::AccountRoot}},
{ 16, jss::SetRegularKey, {}, {}, {jss::AccountRoot}},
{ 17, jss::Payment, {}, {}, {jss::AccountRoot, jss::AccountRoot}},
{ 18, jss::AccountSet, {}, {}, {jss::AccountRoot}},
{ 19, jss::AccountSet, {}, {}, {jss::AccountRoot}},
{ 20, jss::Payment, {jss::AccountRoot}, {}, {jss::AccountRoot}},
};
BEAST_EXPECT (std::extent<decltype (sanity)>::value ==
result[jss::result][jss::transactions].size());
for (unsigned int index {0};
index < std::extent<decltype (sanity)>::value; ++index)
{
checkSanity (txs[index], sanity[index]);
}
}
public: public:
void void
run() override run() override
{ {
testParameters(); testParameters();
testContents();
} }
}; };
BEAST_DEFINE_TESTSUITE(AccountTX, app, ripple); BEAST_DEFINE_TESTSUITE(AccountTx, app, ripple);
} // namespace test } // namespace test
} // namespace ripple } // namespace ripple

View File

@@ -325,15 +325,7 @@ public:
env(jv); env(jv);
} }
{ env (check::create ("bob6", "bob7", XRP (100)));
Json::Value jv;
jv[sfAccount.jsonName] = Account{"bob6"}.human ();
jv[sfSendMax.jsonName] = "100000000";
jv[sfDestination.jsonName] = Account{"bob7"}.human ();
jv[sfTransactionType.jsonName] = jss::CheckCreate;
jv[sfFlags.jsonName] = tfUniversal;
env(jv);
}
// bob9 DepositPreauths bob4 and bob8. // bob9 DepositPreauths bob4 and bob8.
env (deposit::auth (Account {"bob9"}, Account {"bob4"})); env (deposit::auth (Account {"bob9"}, Account {"bob4"}));

View File

@@ -334,20 +334,7 @@ class LedgerRPC_test : public beast::unit_test::suite
uint256 const checkId { uint256 const checkId {
getCheckIndex (env.master, env.seq (env.master))}; getCheckIndex (env.master, env.seq (env.master))};
// Lambda to create a check. env (check::create (env.master, alice, XRP(100)));
auto checkCreate = [] (test::jtx::Account const& account,
test::jtx::Account const& dest, STAmount const& sendMax)
{
Json::Value jv;
jv[sfAccount.jsonName] = account.human();
jv[sfSendMax.jsonName] = sendMax.getJson(JsonOptions::none);
jv[sfDestination.jsonName] = dest.human();
jv[sfTransactionType.jsonName] = jss::CheckCreate;
jv[sfFlags.jsonName] = tfUniversal;
return jv;
};
env (checkCreate (env.master, alice, XRP(100)));
env.close(); env.close();
std::string const ledgerHash {to_string (env.closed()->info().hash)}; std::string const ledgerHash {to_string (env.closed()->info().hash)};

View File

@@ -24,6 +24,7 @@
#include <test/jtx/impl/Account.cpp> #include <test/jtx/impl/Account.cpp>
#include <test/jtx/impl/amount.cpp> #include <test/jtx/impl/amount.cpp>
#include <test/jtx/impl/balance.cpp> #include <test/jtx/impl/balance.cpp>
#include <test/jtx/impl/check.cpp>
#include <test/jtx/impl/delivermin.cpp> #include <test/jtx/impl/delivermin.cpp>
#include <test/jtx/impl/deposit.cpp> #include <test/jtx/impl/deposit.cpp>
#include <test/jtx/impl/Env.cpp> #include <test/jtx/impl/Env.cpp>