mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
433 lines
14 KiB
C++
433 lines
14 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
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 <ripple/ledger/ApplyViewImpl.h>
|
|
#include <ripple/ledger/PaymentSandbox.h>
|
|
#include <ripple/ledger/View.h>
|
|
#include <ripple/protocol/AmountConversions.h>
|
|
#include <ripple/protocol/Feature.h>
|
|
#include <test/jtx/PathSet.h>
|
|
|
|
namespace ripple {
|
|
namespace test {
|
|
|
|
class PaymentSandbox_test : public beast::unit_test::suite
|
|
{
|
|
/*
|
|
Create paths so one path funds another path.
|
|
|
|
Two accounts: sender and receiver.
|
|
Two gateways: gw1 and gw2.
|
|
Sender and receiver both have trust lines to the gateways.
|
|
Sender has 2 gw1/USD and 4 gw2/USD.
|
|
Sender has offer to exchange 2 gw1 for gw2 and gw2 for gw1 1-for-1.
|
|
Paths are:
|
|
1) GW1 -> [OB GW1/USD->GW2/USD] -> GW2
|
|
2) GW2 -> [OB GW2/USD->GW1/USD] -> GW1
|
|
|
|
sender pays receiver 4 USD.
|
|
Path 1:
|
|
1) Sender exchanges 2 GW1/USD for 2 GW2/USD
|
|
2) Old code: the 2 GW1/USD is available to sender
|
|
New code: the 2 GW1/USD is not available until the
|
|
end of the transaction.
|
|
3) Receiver gets 2 GW2/USD
|
|
Path 2:
|
|
1) Old code: Sender exchanges 2 GW2/USD for 2 GW1/USD
|
|
2) Old code: Receiver get 2 GW1
|
|
2) New code: Path is dry because sender does not have any
|
|
GW1 to spend until the end of the transaction.
|
|
*/
|
|
void
|
|
testSelfFunding(FeatureBitset features)
|
|
{
|
|
testcase("selfFunding");
|
|
|
|
using namespace jtx;
|
|
Env env(*this, features);
|
|
Account const gw1("gw1");
|
|
Account const gw2("gw2");
|
|
Account const snd("snd");
|
|
Account const rcv("rcv");
|
|
|
|
env.fund(XRP(10000), snd, rcv, gw1, gw2);
|
|
|
|
auto const USD_gw1 = gw1["USD"];
|
|
auto const USD_gw2 = gw2["USD"];
|
|
|
|
env.trust(USD_gw1(10), snd);
|
|
env.trust(USD_gw2(10), snd);
|
|
env.trust(USD_gw1(100), rcv);
|
|
env.trust(USD_gw2(100), rcv);
|
|
|
|
env(pay(gw1, snd, USD_gw1(2)));
|
|
env(pay(gw2, snd, USD_gw2(4)));
|
|
|
|
env(offer(snd, USD_gw1(2), USD_gw2(2)), txflags(tfPassive));
|
|
env(offer(snd, USD_gw2(2), USD_gw1(2)), txflags(tfPassive));
|
|
|
|
PathSet paths(Path(gw1, USD_gw2, gw2), Path(gw2, USD_gw1, gw1));
|
|
|
|
env(pay(snd, rcv, any(USD_gw1(4))),
|
|
json(paths.json()),
|
|
txflags(tfNoRippleDirect | tfPartialPayment));
|
|
|
|
env.require(balance("rcv", USD_gw1(0)));
|
|
env.require(balance("rcv", USD_gw2(2)));
|
|
}
|
|
|
|
void
|
|
testSubtractCredits(FeatureBitset features)
|
|
{
|
|
testcase("subtractCredits");
|
|
|
|
using namespace jtx;
|
|
Env env(*this, features);
|
|
Account const gw1("gw1");
|
|
Account const gw2("gw2");
|
|
Account const alice("alice");
|
|
|
|
env.fund(XRP(10000), alice, gw1, gw2);
|
|
|
|
auto j = env.app().journal("View");
|
|
|
|
auto const USD_gw1 = gw1["USD"];
|
|
auto const USD_gw2 = gw2["USD"];
|
|
|
|
env.trust(USD_gw1(100), alice);
|
|
env.trust(USD_gw2(100), alice);
|
|
|
|
env(pay(gw1, alice, USD_gw1(50)));
|
|
env(pay(gw2, alice, USD_gw2(50)));
|
|
|
|
STAmount const toCredit(USD_gw1(30));
|
|
STAmount const toDebit(USD_gw1(20));
|
|
{
|
|
// accountSend, no deferredCredits
|
|
ApplyViewImpl av(&*env.current(), tapNONE);
|
|
|
|
auto const iss = USD_gw1.issue();
|
|
auto const startingAmount = accountHolds(
|
|
av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j);
|
|
{
|
|
auto r = accountSend(av, gw1, alice, toCredit, j);
|
|
BEAST_EXPECT(r == tesSUCCESS);
|
|
}
|
|
BEAST_EXPECT(
|
|
accountHolds(
|
|
av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) ==
|
|
startingAmount + toCredit);
|
|
{
|
|
auto r = accountSend(av, alice, gw1, toDebit, j);
|
|
BEAST_EXPECT(r == tesSUCCESS);
|
|
}
|
|
BEAST_EXPECT(
|
|
accountHolds(
|
|
av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) ==
|
|
startingAmount + toCredit - toDebit);
|
|
}
|
|
|
|
{
|
|
// rippleCredit, no deferredCredits
|
|
ApplyViewImpl av(&*env.current(), tapNONE);
|
|
|
|
auto const iss = USD_gw1.issue();
|
|
auto const startingAmount = accountHolds(
|
|
av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j);
|
|
|
|
rippleCredit(av, gw1, alice, toCredit, true, j);
|
|
BEAST_EXPECT(
|
|
accountHolds(
|
|
av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) ==
|
|
startingAmount + toCredit);
|
|
|
|
rippleCredit(av, alice, gw1, toDebit, true, j);
|
|
BEAST_EXPECT(
|
|
accountHolds(
|
|
av, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) ==
|
|
startingAmount + toCredit - toDebit);
|
|
}
|
|
|
|
{
|
|
// accountSend, w/ deferredCredits
|
|
ApplyViewImpl av(&*env.current(), tapNONE);
|
|
PaymentSandbox pv(&av);
|
|
|
|
auto const iss = USD_gw1.issue();
|
|
auto const startingAmount = accountHolds(
|
|
pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j);
|
|
|
|
{
|
|
auto r = accountSend(pv, gw1, alice, toCredit, j);
|
|
BEAST_EXPECT(r == tesSUCCESS);
|
|
}
|
|
BEAST_EXPECT(
|
|
accountHolds(
|
|
pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) ==
|
|
startingAmount);
|
|
|
|
{
|
|
auto r = accountSend(pv, alice, gw1, toDebit, j);
|
|
BEAST_EXPECT(r == tesSUCCESS);
|
|
}
|
|
BEAST_EXPECT(
|
|
accountHolds(
|
|
pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) ==
|
|
startingAmount - toDebit);
|
|
}
|
|
|
|
{
|
|
// rippleCredit, w/ deferredCredits
|
|
ApplyViewImpl av(&*env.current(), tapNONE);
|
|
PaymentSandbox pv(&av);
|
|
|
|
auto const iss = USD_gw1.issue();
|
|
auto const startingAmount = accountHolds(
|
|
pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j);
|
|
|
|
rippleCredit(pv, gw1, alice, toCredit, true, j);
|
|
BEAST_EXPECT(
|
|
accountHolds(
|
|
pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) ==
|
|
startingAmount);
|
|
}
|
|
|
|
{
|
|
// redeemIOU, w/ deferredCredits
|
|
ApplyViewImpl av(&*env.current(), tapNONE);
|
|
PaymentSandbox pv(&av);
|
|
|
|
auto const iss = USD_gw1.issue();
|
|
auto const startingAmount = accountHolds(
|
|
pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j);
|
|
|
|
BEAST_EXPECT(redeemIOU(pv, alice, toDebit, iss, j) == tesSUCCESS);
|
|
BEAST_EXPECT(
|
|
accountHolds(
|
|
pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) ==
|
|
startingAmount - toDebit);
|
|
}
|
|
|
|
{
|
|
// issueIOU, w/ deferredCredits
|
|
ApplyViewImpl av(&*env.current(), tapNONE);
|
|
PaymentSandbox pv(&av);
|
|
|
|
auto const iss = USD_gw1.issue();
|
|
auto const startingAmount = accountHolds(
|
|
pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j);
|
|
|
|
BEAST_EXPECT(issueIOU(pv, alice, toCredit, iss, j) == tesSUCCESS);
|
|
BEAST_EXPECT(
|
|
accountHolds(
|
|
pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) ==
|
|
startingAmount);
|
|
}
|
|
|
|
{
|
|
// accountSend, w/ deferredCredits and stacked views
|
|
ApplyViewImpl av(&*env.current(), tapNONE);
|
|
PaymentSandbox pv(&av);
|
|
|
|
auto const iss = USD_gw1.issue();
|
|
auto const startingAmount = accountHolds(
|
|
pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j);
|
|
|
|
{
|
|
auto r = accountSend(pv, gw1, alice, toCredit, j);
|
|
BEAST_EXPECT(r == tesSUCCESS);
|
|
}
|
|
BEAST_EXPECT(
|
|
accountHolds(
|
|
pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) ==
|
|
startingAmount);
|
|
|
|
{
|
|
PaymentSandbox pv2(&pv);
|
|
BEAST_EXPECT(
|
|
accountHolds(
|
|
pv2,
|
|
alice,
|
|
iss.currency,
|
|
iss.account,
|
|
fhIGNORE_FREEZE,
|
|
j) == startingAmount);
|
|
{
|
|
auto r = accountSend(pv2, gw1, alice, toCredit, j);
|
|
BEAST_EXPECT(r == tesSUCCESS);
|
|
}
|
|
BEAST_EXPECT(
|
|
accountHolds(
|
|
pv2,
|
|
alice,
|
|
iss.currency,
|
|
iss.account,
|
|
fhIGNORE_FREEZE,
|
|
j) == startingAmount);
|
|
}
|
|
|
|
{
|
|
auto r = accountSend(pv, alice, gw1, toDebit, j);
|
|
BEAST_EXPECT(r == tesSUCCESS);
|
|
}
|
|
BEAST_EXPECT(
|
|
accountHolds(
|
|
pv, alice, iss.currency, iss.account, fhIGNORE_FREEZE, j) ==
|
|
startingAmount - toDebit);
|
|
}
|
|
}
|
|
|
|
void
|
|
testTinyBalance(FeatureBitset features)
|
|
{
|
|
testcase("Tiny balance");
|
|
|
|
// Add and subtract a huge credit from a tiny balance, expect the tiny
|
|
// balance back. Numerical stability problems could cause the balance to
|
|
// be zero.
|
|
|
|
using namespace jtx;
|
|
|
|
Env env(*this, features);
|
|
|
|
Account const gw("gw");
|
|
Account const alice("alice");
|
|
auto const USD = gw["USD"];
|
|
|
|
auto const issue = USD.issue();
|
|
STAmount tinyAmt(
|
|
issue,
|
|
STAmount::cMinValue,
|
|
STAmount::cMinOffset + 1,
|
|
false,
|
|
false,
|
|
STAmount::unchecked{});
|
|
STAmount hugeAmt(
|
|
issue,
|
|
STAmount::cMaxValue,
|
|
STAmount::cMaxOffset - 1,
|
|
false,
|
|
false,
|
|
STAmount::unchecked{});
|
|
|
|
ApplyViewImpl av(&*env.current(), tapNONE);
|
|
PaymentSandbox pv(&av);
|
|
pv.creditHook(gw, alice, hugeAmt, -tinyAmt);
|
|
BEAST_EXPECT(pv.balanceHook(alice, gw, hugeAmt) == tinyAmt);
|
|
}
|
|
|
|
void
|
|
testReserve(FeatureBitset features)
|
|
{
|
|
testcase("Reserve");
|
|
using namespace jtx;
|
|
|
|
auto accountFundsXRP = [](ReadView const& view,
|
|
AccountID const& id,
|
|
beast::Journal j) -> XRPAmount {
|
|
return toAmount<XRPAmount>(accountHolds(
|
|
view, id, xrpCurrency(), xrpAccount(), fhZERO_IF_FROZEN, j));
|
|
};
|
|
|
|
auto reserve = [](jtx::Env& env, std::uint32_t count) -> XRPAmount {
|
|
return env.current()->fees().accountReserve(count);
|
|
};
|
|
|
|
Env env(*this, features);
|
|
|
|
Account const alice("alice");
|
|
env.fund(reserve(env, 1), alice);
|
|
|
|
env.close();
|
|
ApplyViewImpl av(&*env.current(), tapNONE);
|
|
PaymentSandbox sb(&av);
|
|
{
|
|
// Send alice an amount and spend it. The deferredCredits will cause
|
|
// her balance to drop below the reserve. Make sure her funds are
|
|
// zero (there was a bug that caused her funds to become negative).
|
|
|
|
{
|
|
auto r =
|
|
accountSend(sb, xrpAccount(), alice, XRP(100), env.journal);
|
|
BEAST_EXPECT(r == tesSUCCESS);
|
|
}
|
|
{
|
|
auto r =
|
|
accountSend(sb, alice, xrpAccount(), XRP(100), env.journal);
|
|
BEAST_EXPECT(r == tesSUCCESS);
|
|
}
|
|
BEAST_EXPECT(
|
|
accountFundsXRP(sb, alice, env.journal) == beast::zero);
|
|
}
|
|
}
|
|
|
|
void
|
|
testBalanceHook(FeatureBitset features)
|
|
{
|
|
// Make sure the Issue::Account returned by PAymentSandbox::balanceHook
|
|
// is correct.
|
|
testcase("balanceHook");
|
|
|
|
using namespace jtx;
|
|
Env env(*this, features);
|
|
|
|
Account const gw("gw");
|
|
auto const USD = gw["USD"];
|
|
Account const alice("alice");
|
|
|
|
ApplyViewImpl av(&*env.current(), tapNONE);
|
|
PaymentSandbox sb(&av);
|
|
|
|
// The currency we pass for the last argument mimics the currency that
|
|
// is typically passed to creditHook, since it comes from a trust line.
|
|
Issue tlIssue = noIssue();
|
|
tlIssue.currency = USD.issue().currency;
|
|
|
|
sb.creditHook(gw.id(), alice.id(), {USD, 400}, {tlIssue, 600});
|
|
sb.creditHook(gw.id(), alice.id(), {USD, 100}, {tlIssue, 600});
|
|
|
|
// Expect that the STAmount issuer returned by balanceHook() is correct.
|
|
STAmount const balance =
|
|
sb.balanceHook(gw.id(), alice.id(), {USD, 600});
|
|
BEAST_EXPECT(balance.getIssuer() == USD.issue().account);
|
|
}
|
|
|
|
public:
|
|
void
|
|
run() override
|
|
{
|
|
auto testAll = [this](FeatureBitset features) {
|
|
testSelfFunding(features);
|
|
testSubtractCredits(features);
|
|
testTinyBalance(features);
|
|
testReserve(features);
|
|
testBalanceHook(features);
|
|
};
|
|
using namespace jtx;
|
|
auto const sa = supported_amendments();
|
|
testAll(sa - featureFlowCross);
|
|
testAll(sa);
|
|
}
|
|
};
|
|
|
|
BEAST_DEFINE_TESTSUITE(PaymentSandbox, ledger, ripple);
|
|
|
|
} // namespace test
|
|
} // namespace ripple
|