mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 11:05:54 +00:00
Merge branch 'develop' into ximinez/lending-XLS-66
This commit is contained in:
@@ -2,16 +2,6 @@
|
||||
convenience variables and sanity checks
|
||||
#]===================================================================]
|
||||
|
||||
include(ProcessorCount)
|
||||
|
||||
if (NOT ep_procs)
|
||||
ProcessorCount(ep_procs)
|
||||
if (ep_procs GREATER 1)
|
||||
# never use more than half of cores for EP builds
|
||||
math (EXPR ep_procs "${ep_procs} / 2")
|
||||
message (STATUS "Using ${ep_procs} cores for ExternalProject builds.")
|
||||
endif ()
|
||||
endif ()
|
||||
get_property(is_multiconfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
|
||||
set (CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
|
||||
|
||||
@@ -581,7 +581,10 @@ canAdd(STAmount const& a, STAmount const& b)
|
||||
|
||||
return true;
|
||||
}
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("STAmount::canAdd : unexpected STAmount type");
|
||||
return false;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -653,8 +656,10 @@ canSubtract(STAmount const& a, STAmount const& b)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("STAmount::canSubtract : unexpected STAmount type");
|
||||
return false;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -21,9 +21,11 @@
|
||||
|
||||
#include <xrpld/app/tx/applySteps.h>
|
||||
#include <xrpld/ledger/Dir.h>
|
||||
#include <xrpld/ledger/Sandbox.h>
|
||||
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
@@ -56,27 +58,39 @@ struct EscrowToken_test : public beast::unit_test::suite
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
issuerIOUEscrowed(
|
||||
jtx::PrettyAmount
|
||||
issuerBalance(
|
||||
jtx::Env& env,
|
||||
jtx::Account const& account,
|
||||
Currency const& currency,
|
||||
int const& outstanding,
|
||||
int const& locked)
|
||||
Issue const& issue)
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::account] = account.human();
|
||||
auto jrr = env.rpc("json", "gateway_balances", to_string(params));
|
||||
auto const result = jrr[jss::result];
|
||||
auto const actualOutstanding =
|
||||
result[jss::obligations][to_string(currency)];
|
||||
BEAST_EXPECT(actualOutstanding == to_string(outstanding));
|
||||
if (locked != 0)
|
||||
{
|
||||
auto const actualEscrowed =
|
||||
result[jss::locked][to_string(currency)];
|
||||
BEAST_EXPECT(actualEscrowed == to_string(locked));
|
||||
}
|
||||
auto const obligations =
|
||||
result[jss::obligations][to_string(issue.currency)];
|
||||
if (obligations.isNull())
|
||||
return {STAmount(issue, 0), account.name()};
|
||||
STAmount const amount = amountFromString(issue, obligations.asString());
|
||||
return {amount, account.name()};
|
||||
}
|
||||
|
||||
jtx::PrettyAmount
|
||||
issuerEscrowed(
|
||||
jtx::Env& env,
|
||||
jtx::Account const& account,
|
||||
Issue const& issue)
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::account] = account.human();
|
||||
auto jrr = env.rpc("json", "gateway_balances", to_string(params));
|
||||
auto const result = jrr[jss::result];
|
||||
auto const locked = result[jss::locked][to_string(issue.currency)];
|
||||
if (locked.isNull())
|
||||
return {STAmount(issue, 0), account.name()};
|
||||
STAmount const amount = amountFromString(issue, locked.asString());
|
||||
return {amount, account.name()};
|
||||
}
|
||||
|
||||
void
|
||||
@@ -136,6 +150,37 @@ struct EscrowToken_test : public beast::unit_test::suite
|
||||
env(escrow::cancel(bob, alice, seq2), finishResult);
|
||||
env.close();
|
||||
}
|
||||
|
||||
for (bool const withTokenEscrow : {false, true})
|
||||
{
|
||||
auto const amend =
|
||||
withTokenEscrow ? features : features - featureTokenEscrow;
|
||||
Env env{*this, amend};
|
||||
auto const baseFee = env.current()->fees().base;
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
auto const gw = Account{"gateway"};
|
||||
auto const USD = gw["USD"];
|
||||
env.fund(XRP(5000), alice, bob, gw);
|
||||
env(fset(gw, asfAllowTrustLineLocking));
|
||||
env.close();
|
||||
env.trust(USD(10'000), alice, bob);
|
||||
env.close();
|
||||
env(pay(gw, alice, USD(5000)));
|
||||
env(pay(gw, bob, USD(5000)));
|
||||
env.close();
|
||||
|
||||
auto const seq1 = env.seq(alice);
|
||||
env(escrow::finish(bob, alice, seq1),
|
||||
escrow::condition(escrow::cb1),
|
||||
escrow::fulfillment(escrow::fb1),
|
||||
fee(baseFee * 150),
|
||||
ter(tecNO_TARGET));
|
||||
env.close();
|
||||
|
||||
env(escrow::cancel(bob, alice, seq1), ter(tecNO_TARGET));
|
||||
env.close();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -865,34 +910,76 @@ struct EscrowToken_test : public beast::unit_test::suite
|
||||
env.close();
|
||||
env.trust(USD(10'000), alice, bob);
|
||||
env.close();
|
||||
env(pay(gw, alice, USD(5000)));
|
||||
env(pay(gw, bob, USD(5000)));
|
||||
env(pay(gw, alice, USD(5'000)));
|
||||
env(pay(gw, bob, USD(5'000)));
|
||||
env.close();
|
||||
|
||||
auto const outstandingUSD = USD(10'000);
|
||||
|
||||
// Create & Finish Escrow
|
||||
auto const seq1 = env.seq(alice);
|
||||
env(escrow::create(alice, bob, USD(1'000)),
|
||||
escrow::condition(escrow::cb1),
|
||||
escrow::finish_time(env.now() + 1s),
|
||||
fee(baseFee * 150),
|
||||
ter(tesSUCCESS));
|
||||
env.close();
|
||||
env(escrow::finish(bob, alice, seq1),
|
||||
escrow::condition(escrow::cb1),
|
||||
escrow::fulfillment(escrow::fb1),
|
||||
fee(baseFee * 150),
|
||||
ter(tesSUCCESS));
|
||||
env.close();
|
||||
{
|
||||
auto const preAliceUSD = env.balance(alice, USD);
|
||||
auto const preBobUSD = env.balance(bob, USD);
|
||||
env(escrow::create(alice, bob, USD(1'000)),
|
||||
escrow::condition(escrow::cb1),
|
||||
escrow::finish_time(env.now() + 1s),
|
||||
fee(baseFee * 150),
|
||||
ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(env.balance(alice, USD) == preAliceUSD - USD(1'000));
|
||||
BEAST_EXPECT(env.balance(bob, USD) == preBobUSD);
|
||||
BEAST_EXPECT(
|
||||
issuerBalance(env, gw, USD) == outstandingUSD - USD(1'000));
|
||||
BEAST_EXPECT(issuerEscrowed(env, gw, USD) == USD(1'000));
|
||||
}
|
||||
{
|
||||
auto const preAliceUSD = env.balance(alice, USD);
|
||||
auto const preBobUSD = env.balance(bob, USD);
|
||||
env(escrow::finish(bob, alice, seq1),
|
||||
escrow::condition(escrow::cb1),
|
||||
escrow::fulfillment(escrow::fb1),
|
||||
fee(baseFee * 150),
|
||||
ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(env.balance(alice, USD) == preAliceUSD);
|
||||
BEAST_EXPECT(env.balance(bob, USD) == preBobUSD + USD(1'000));
|
||||
BEAST_EXPECT(issuerBalance(env, gw, USD) == outstandingUSD);
|
||||
BEAST_EXPECT(issuerEscrowed(env, gw, USD) == USD(0));
|
||||
}
|
||||
|
||||
// Create & Cancel Escrow
|
||||
auto const seq2 = env.seq(alice);
|
||||
env(escrow::create(alice, bob, USD(1'000)),
|
||||
escrow::condition(escrow::cb2),
|
||||
escrow::finish_time(env.now() + 1s),
|
||||
escrow::cancel_time(env.now() + 2s),
|
||||
fee(baseFee * 150),
|
||||
ter(tesSUCCESS));
|
||||
env.close();
|
||||
env(escrow::cancel(bob, alice, seq2), ter(tesSUCCESS));
|
||||
env.close();
|
||||
{
|
||||
auto const preAliceUSD = env.balance(alice, USD);
|
||||
auto const preBobUSD = env.balance(bob, USD);
|
||||
env(escrow::create(alice, bob, USD(1'000)),
|
||||
escrow::condition(escrow::cb2),
|
||||
escrow::finish_time(env.now() + 1s),
|
||||
escrow::cancel_time(env.now() + 2s),
|
||||
fee(baseFee * 150),
|
||||
ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(env.balance(alice, USD) == preAliceUSD - USD(1'000));
|
||||
BEAST_EXPECT(env.balance(bob, USD) == preBobUSD);
|
||||
BEAST_EXPECT(
|
||||
issuerBalance(env, gw, USD) == outstandingUSD - USD(1'000));
|
||||
BEAST_EXPECT(issuerEscrowed(env, gw, USD) == USD(1'000));
|
||||
}
|
||||
{
|
||||
auto const preAliceUSD = env.balance(alice, USD);
|
||||
auto const preBobUSD = env.balance(bob, USD);
|
||||
env(escrow::cancel(bob, alice, seq2), ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(env.balance(alice, USD) == preAliceUSD + USD(1'000));
|
||||
BEAST_EXPECT(env.balance(bob, USD) == preBobUSD);
|
||||
BEAST_EXPECT(issuerBalance(env, gw, USD) == outstandingUSD);
|
||||
BEAST_EXPECT(issuerEscrowed(env, gw, USD) == USD(0));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2430,7 +2517,6 @@ struct EscrowToken_test : public beast::unit_test::suite
|
||||
mptGw.authorize({.account = alice});
|
||||
mptGw.authorize({.account = bob});
|
||||
auto const MPT = mptGw["MPT"];
|
||||
env(pay(gw, alice, MPT(10)));
|
||||
env(pay(gw, bob, MPT(10)));
|
||||
env.close();
|
||||
|
||||
@@ -2521,6 +2607,39 @@ struct EscrowToken_test : public beast::unit_test::suite
|
||||
env.close();
|
||||
}
|
||||
|
||||
// tecOBJECT_NOT_FOUND: MPT issuance does not exist
|
||||
{
|
||||
Env env{*this, features};
|
||||
auto const baseFee = env.current()->fees().base;
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
env.fund(XRP(10'000), alice, bob);
|
||||
env.close();
|
||||
|
||||
auto const seq1 = env.seq(alice);
|
||||
env.app().openLedger().modify(
|
||||
[&](OpenView& view, beast::Journal j) {
|
||||
Sandbox sb(&view, tapNONE);
|
||||
auto sleNew =
|
||||
std::make_shared<SLE>(keylet::escrow(alice, seq1));
|
||||
MPTIssue const mpt{
|
||||
MPTIssue{makeMptID(1, AccountID(0x4985601))}};
|
||||
STAmount amt(mpt, 10);
|
||||
sleNew->setAccountID(sfDestination, bob);
|
||||
sleNew->setFieldAmount(sfAmount, amt);
|
||||
sb.insert(sleNew);
|
||||
sb.apply(view);
|
||||
return true;
|
||||
});
|
||||
|
||||
env(escrow::finish(bob, alice, seq1),
|
||||
escrow::condition(escrow::cb1),
|
||||
escrow::fulfillment(escrow::fb1),
|
||||
fee(baseFee * 150),
|
||||
ter(tecOBJECT_NOT_FOUND));
|
||||
env.close();
|
||||
}
|
||||
|
||||
// tecLOCKED: issuer has locked the dest
|
||||
{
|
||||
Env env{*this, features};
|
||||
@@ -2726,6 +2845,36 @@ struct EscrowToken_test : public beast::unit_test::suite
|
||||
env(escrow::cancel(bob, alice, seq1), ter(tecNO_AUTH));
|
||||
env.close();
|
||||
}
|
||||
|
||||
// tecOBJECT_NOT_FOUND: MPT issuance does not exist
|
||||
{
|
||||
Env env{*this, features};
|
||||
auto const baseFee = env.current()->fees().base;
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
env.fund(XRP(10'000), alice, bob);
|
||||
|
||||
auto const seq1 = env.seq(alice);
|
||||
env.app().openLedger().modify(
|
||||
[&](OpenView& view, beast::Journal j) {
|
||||
Sandbox sb(&view, tapNONE);
|
||||
auto sleNew =
|
||||
std::make_shared<SLE>(keylet::escrow(alice, seq1));
|
||||
MPTIssue const mpt{
|
||||
MPTIssue{makeMptID(1, AccountID(0x4985601))}};
|
||||
STAmount amt(mpt, 10);
|
||||
sleNew->setAccountID(sfDestination, bob);
|
||||
sleNew->setFieldAmount(sfAmount, amt);
|
||||
sb.insert(sleNew);
|
||||
sb.apply(view);
|
||||
return true;
|
||||
});
|
||||
|
||||
env(escrow::cancel(bob, alice, seq1),
|
||||
fee(baseFee),
|
||||
ter(tecOBJECT_NOT_FOUND));
|
||||
env.close();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -3603,12 +3752,14 @@ struct EscrowToken_test : public beast::unit_test::suite
|
||||
fee(baseFee * 150));
|
||||
env.close();
|
||||
|
||||
env(pay(alice, gw, MPT(10'000)), ter(tecPATH_PARTIAL));
|
||||
env(pay(alice, gw, MPT(9'990)));
|
||||
env(pay(bob, gw, MPT(10'000)));
|
||||
BEAST_EXPECT(env.balance(alice, MPT) == MPT(0));
|
||||
BEAST_EXPECT(mptEscrowed(env, alice, MPT) == 10);
|
||||
BEAST_EXPECT(env.balance(bob, MPT) == MPT(0));
|
||||
BEAST_EXPECT(mptEscrowed(env, bob, MPT) == 0);
|
||||
BEAST_EXPECT(env.balance(gw, MPT) == MPT(10));
|
||||
mptGw.authorize({.account = bob, .flags = tfMPTUnauthorize});
|
||||
mptGw.destroy(
|
||||
{.id = mptGw.issuanceID(),
|
||||
|
||||
@@ -369,6 +369,12 @@ struct Escrow_test : public beast::unit_test::suite
|
||||
env.fund(XRP(5000), "alice", "bob", "gw");
|
||||
env.close();
|
||||
|
||||
// temINVALID_FLAG
|
||||
env(escrow::create("alice", "bob", XRP(1000)),
|
||||
escrow::finish_time(env.now() + 5s),
|
||||
txflags(tfPassive),
|
||||
ter(temINVALID_FLAG));
|
||||
|
||||
// Finish time is in the past
|
||||
env(escrow::create("alice", "bob", XRP(1000)),
|
||||
escrow::finish_time(env.now() - 5s),
|
||||
|
||||
@@ -810,6 +810,153 @@ class Invariants_test : public beast::unit_test::suite
|
||||
ac.view().insert(sleNew);
|
||||
return true;
|
||||
});
|
||||
|
||||
// IOU < 0
|
||||
doInvariantCheck(
|
||||
{{"escrow specifies invalid amount"}},
|
||||
[](Account const& A1, Account const&, ApplyContext& ac) {
|
||||
// escrow with too-little iou
|
||||
auto const sle = ac.view().peek(keylet::account(A1.id()));
|
||||
if (!sle)
|
||||
return false;
|
||||
auto sleNew = std::make_shared<SLE>(
|
||||
keylet::escrow(A1, (*sle)[sfSequence] + 2));
|
||||
|
||||
Issue const usd{
|
||||
Currency(0x5553440000000000), AccountID(0x4985601)};
|
||||
STAmount amt(usd, -1);
|
||||
sleNew->setFieldAmount(sfAmount, amt);
|
||||
ac.view().insert(sleNew);
|
||||
return true;
|
||||
});
|
||||
|
||||
// IOU bad currency
|
||||
doInvariantCheck(
|
||||
{{"escrow specifies invalid amount"}},
|
||||
[](Account const& A1, Account const&, ApplyContext& ac) {
|
||||
// escrow with bad iou currency
|
||||
auto const sle = ac.view().peek(keylet::account(A1.id()));
|
||||
if (!sle)
|
||||
return false;
|
||||
auto sleNew = std::make_shared<SLE>(
|
||||
keylet::escrow(A1, (*sle)[sfSequence] + 2));
|
||||
|
||||
Issue const bad{badCurrency(), AccountID(0x4985601)};
|
||||
STAmount amt(bad, 1);
|
||||
sleNew->setFieldAmount(sfAmount, amt);
|
||||
ac.view().insert(sleNew);
|
||||
return true;
|
||||
});
|
||||
|
||||
// MPT < 0
|
||||
doInvariantCheck(
|
||||
{{"escrow specifies invalid amount"}},
|
||||
[](Account const& A1, Account const&, ApplyContext& ac) {
|
||||
// escrow with too-little mpt
|
||||
auto const sle = ac.view().peek(keylet::account(A1.id()));
|
||||
if (!sle)
|
||||
return false;
|
||||
auto sleNew = std::make_shared<SLE>(
|
||||
keylet::escrow(A1, (*sle)[sfSequence] + 2));
|
||||
|
||||
MPTIssue const mpt{
|
||||
MPTIssue{makeMptID(1, AccountID(0x4985601))}};
|
||||
STAmount amt(mpt, -1);
|
||||
sleNew->setFieldAmount(sfAmount, amt);
|
||||
ac.view().insert(sleNew);
|
||||
return true;
|
||||
});
|
||||
|
||||
// MPT OutstandingAmount < 0
|
||||
doInvariantCheck(
|
||||
{{"escrow specifies invalid amount"}},
|
||||
[](Account const& A1, Account const&, ApplyContext& ac) {
|
||||
// mpissuance outstanding is negative
|
||||
auto const sle = ac.view().peek(keylet::account(A1.id()));
|
||||
if (!sle)
|
||||
return false;
|
||||
|
||||
MPTIssue const mpt{
|
||||
MPTIssue{makeMptID(1, AccountID(0x4985601))}};
|
||||
auto sleNew =
|
||||
std::make_shared<SLE>(keylet::mptIssuance(mpt.getMptID()));
|
||||
sleNew->setFieldU64(sfOutstandingAmount, -1);
|
||||
ac.view().insert(sleNew);
|
||||
return true;
|
||||
});
|
||||
|
||||
// MPT LockedAmount < 0
|
||||
doInvariantCheck(
|
||||
{{"escrow specifies invalid amount"}},
|
||||
[](Account const& A1, Account const&, ApplyContext& ac) {
|
||||
// mpissuance locked is less than locked
|
||||
auto const sle = ac.view().peek(keylet::account(A1.id()));
|
||||
if (!sle)
|
||||
return false;
|
||||
|
||||
MPTIssue const mpt{
|
||||
MPTIssue{makeMptID(1, AccountID(0x4985601))}};
|
||||
auto sleNew =
|
||||
std::make_shared<SLE>(keylet::mptIssuance(mpt.getMptID()));
|
||||
sleNew->setFieldU64(sfLockedAmount, -1);
|
||||
ac.view().insert(sleNew);
|
||||
return true;
|
||||
});
|
||||
|
||||
// MPT OutstandingAmount < LockedAmount
|
||||
doInvariantCheck(
|
||||
{{"escrow specifies invalid amount"}},
|
||||
[](Account const& A1, Account const&, ApplyContext& ac) {
|
||||
// mpissuance outstanding is less than locked
|
||||
auto const sle = ac.view().peek(keylet::account(A1.id()));
|
||||
if (!sle)
|
||||
return false;
|
||||
|
||||
MPTIssue const mpt{
|
||||
MPTIssue{makeMptID(1, AccountID(0x4985601))}};
|
||||
auto sleNew =
|
||||
std::make_shared<SLE>(keylet::mptIssuance(mpt.getMptID()));
|
||||
sleNew->setFieldU64(sfOutstandingAmount, 1);
|
||||
sleNew->setFieldU64(sfLockedAmount, 10);
|
||||
ac.view().insert(sleNew);
|
||||
return true;
|
||||
});
|
||||
|
||||
// MPT MPTAmount < 0
|
||||
doInvariantCheck(
|
||||
{{"escrow specifies invalid amount"}},
|
||||
[](Account const& A1, Account const&, ApplyContext& ac) {
|
||||
// mptoken amount is negative
|
||||
auto const sle = ac.view().peek(keylet::account(A1.id()));
|
||||
if (!sle)
|
||||
return false;
|
||||
|
||||
MPTIssue const mpt{
|
||||
MPTIssue{makeMptID(1, AccountID(0x4985601))}};
|
||||
auto sleNew =
|
||||
std::make_shared<SLE>(keylet::mptoken(mpt.getMptID(), A1));
|
||||
sleNew->setFieldU64(sfMPTAmount, -1);
|
||||
ac.view().insert(sleNew);
|
||||
return true;
|
||||
});
|
||||
|
||||
// MPT LockedAmount < 0
|
||||
doInvariantCheck(
|
||||
{{"escrow specifies invalid amount"}},
|
||||
[](Account const& A1, Account const&, ApplyContext& ac) {
|
||||
// mptoken locked amount is negative
|
||||
auto const sle = ac.view().peek(keylet::account(A1.id()));
|
||||
if (!sle)
|
||||
return false;
|
||||
|
||||
MPTIssue const mpt{
|
||||
MPTIssue{makeMptID(1, AccountID(0x4985601))}};
|
||||
auto sleNew =
|
||||
std::make_shared<SLE>(keylet::mptoken(mpt.getMptID(), A1));
|
||||
sleNew->setFieldU64(sfLockedAmount, -1);
|
||||
ac.view().insert(sleNew);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -596,7 +596,7 @@ EscrowCreate::doApply()
|
||||
},
|
||||
amount.asset().value());
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
return ret; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
// increment owner count
|
||||
@@ -771,26 +771,26 @@ EscrowFinish::preclaim(PreclaimContext const& ctx)
|
||||
return err;
|
||||
}
|
||||
|
||||
auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
|
||||
auto const slep = ctx.view.read(k);
|
||||
if (!slep)
|
||||
return tecNO_TARGET;
|
||||
|
||||
AccountID const dest = (*slep)[sfDestination];
|
||||
STAmount const amount = (*slep)[sfAmount];
|
||||
|
||||
if (!isXRP(amount))
|
||||
if (ctx.view.rules().enabled(featureTokenEscrow))
|
||||
{
|
||||
if (!ctx.view.rules().enabled(featureTokenEscrow))
|
||||
return temDISABLED; // LCOV_EXCL_LINE
|
||||
auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
|
||||
auto const slep = ctx.view.read(k);
|
||||
if (!slep)
|
||||
return tecNO_TARGET;
|
||||
|
||||
if (auto const ret = std::visit(
|
||||
[&]<typename T>(T const&) {
|
||||
return escrowFinishPreclaimHelper<T>(ctx, dest, amount);
|
||||
},
|
||||
amount.asset().value());
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
AccountID const dest = (*slep)[sfDestination];
|
||||
STAmount const amount = (*slep)[sfAmount];
|
||||
|
||||
if (!isXRP(amount))
|
||||
{
|
||||
if (auto const ret = std::visit(
|
||||
[&]<typename T>(T const&) {
|
||||
return escrowFinishPreclaimHelper<T>(ctx, dest, amount);
|
||||
},
|
||||
amount.asset().value());
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -1020,7 +1020,12 @@ EscrowFinish::doApply()
|
||||
auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
|
||||
auto const slep = ctx_.view().peek(k);
|
||||
if (!slep)
|
||||
{
|
||||
if (ctx_.view().rules().enabled(featureTokenEscrow))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
return tecNO_TARGET;
|
||||
}
|
||||
|
||||
// If a cancel time is present, a finish operation should only succeed prior
|
||||
// to that time. fix1571 corrects a logic error in the check that would make
|
||||
@@ -1251,7 +1256,7 @@ escrowCancelPreclaimHelper<MPTIssue>(
|
||||
keylet::mptIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = ctx.view.read(issuanceKey);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND; // LCOV_EXCL_LINE
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is
|
||||
// authorized
|
||||
@@ -1267,26 +1272,27 @@ escrowCancelPreclaimHelper<MPTIssue>(
|
||||
TER
|
||||
EscrowCancel::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
|
||||
auto const slep = ctx.view.read(k);
|
||||
if (!slep)
|
||||
return tecNO_TARGET;
|
||||
|
||||
AccountID const account = (*slep)[sfAccount];
|
||||
STAmount const amount = (*slep)[sfAmount];
|
||||
|
||||
if (!isXRP(amount))
|
||||
if (ctx.view.rules().enabled(featureTokenEscrow))
|
||||
{
|
||||
if (!ctx.view.rules().enabled(featureTokenEscrow))
|
||||
return temDISABLED; // LCOV_EXCL_LINE
|
||||
auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
|
||||
auto const slep = ctx.view.read(k);
|
||||
if (!slep)
|
||||
return tecNO_TARGET;
|
||||
|
||||
if (auto const ret = std::visit(
|
||||
[&]<typename T>(T const&) {
|
||||
return escrowCancelPreclaimHelper<T>(ctx, account, amount);
|
||||
},
|
||||
amount.asset().value());
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
AccountID const account = (*slep)[sfAccount];
|
||||
STAmount const amount = (*slep)[sfAmount];
|
||||
|
||||
if (!isXRP(amount))
|
||||
{
|
||||
if (auto const ret = std::visit(
|
||||
[&]<typename T>(T const&) {
|
||||
return escrowCancelPreclaimHelper<T>(
|
||||
ctx, account, amount);
|
||||
},
|
||||
amount.asset().value());
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -1297,7 +1303,12 @@ EscrowCancel::doApply()
|
||||
auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
|
||||
auto const slep = ctx_.view().peek(k);
|
||||
if (!slep)
|
||||
{
|
||||
if (ctx_.view().rules().enabled(featureTokenEscrow))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
return tecNO_TARGET;
|
||||
}
|
||||
|
||||
if (ctx_.view().rules().enabled(fix1571))
|
||||
{
|
||||
|
||||
@@ -324,26 +324,6 @@ NoZeroEscrow::visitEntry(
|
||||
std::shared_ptr<SLE const> const& after)
|
||||
{
|
||||
auto isBad = [](STAmount const& amount) {
|
||||
// IOU case
|
||||
if (amount.holds<Issue>())
|
||||
{
|
||||
if (amount <= beast::zero)
|
||||
return true;
|
||||
|
||||
if (badCurrency() == amount.getCurrency())
|
||||
return true;
|
||||
}
|
||||
|
||||
// MPT case
|
||||
if (amount.holds<MPTIssue>())
|
||||
{
|
||||
if (amount <= beast::zero)
|
||||
return true;
|
||||
|
||||
if (amount.mpt() > MPTAmount{maxMPTokenAmount})
|
||||
return true;
|
||||
}
|
||||
|
||||
// XRP case
|
||||
if (amount.native())
|
||||
{
|
||||
@@ -353,7 +333,28 @@ NoZeroEscrow::visitEntry(
|
||||
if (amount.xrp() >= INITIAL_XRP)
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// IOU case
|
||||
if (amount.holds<Issue>())
|
||||
{
|
||||
if (amount <= beast::zero)
|
||||
return true;
|
||||
|
||||
if (badCurrency() == amount.getCurrency())
|
||||
return true;
|
||||
}
|
||||
|
||||
// MPT case
|
||||
if (amount.holds<MPTIssue>())
|
||||
{
|
||||
if (amount <= beast::zero)
|
||||
return true;
|
||||
|
||||
if (amount.mpt() > MPTAmount{maxMPTokenAmount})
|
||||
return true; // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ MPTokenIssuanceDestroy::preclaim(PreclaimContext const& ctx)
|
||||
return tecHAS_OBLIGATIONS;
|
||||
|
||||
if ((*sleMPT)[~sfLockedAmount].value_or(0) != 0)
|
||||
return tecHAS_OBLIGATIONS;
|
||||
return tecHAS_OBLIGATIONS; // LCOV_EXCL_LINE
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -2809,18 +2809,18 @@ rippleLockEscrowMPT(
|
||||
auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
|
||||
auto sleIssuance = view.peek(mptID);
|
||||
if (!sleIssuance)
|
||||
{
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error()) << "rippleLockEscrowMPT: MPT issuance not found for "
|
||||
<< mptIssue.getMptID();
|
||||
return tecOBJECT_NOT_FOUND; // LCOV_EXCL_LINE
|
||||
}
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
if (amount.getIssuer() == sender)
|
||||
{
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error())
|
||||
<< "rippleLockEscrowMPT: sender is the issuer, cannot lock MPTs.";
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
return tecINTERNAL;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
// 1. Decrease the MPT Holder MPTAmount
|
||||
// 2. Increase the MPT Holder EscrowedAmount
|
||||
@@ -2828,23 +2828,23 @@ rippleLockEscrowMPT(
|
||||
auto const mptokenID = keylet::mptoken(mptID.key, sender);
|
||||
auto sle = view.peek(mptokenID);
|
||||
if (!sle)
|
||||
{
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error())
|
||||
<< "rippleLockEscrowMPT: MPToken not found for " << sender;
|
||||
return tecOBJECT_NOT_FOUND; // LCOV_EXCL_LINE
|
||||
}
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
auto const amt = sle->getFieldU64(sfMPTAmount);
|
||||
auto const pay = amount.mpt().value();
|
||||
|
||||
// Underflow check for subtraction
|
||||
if (!canSubtract(STAmount(mptIssue, amt), STAmount(mptIssue, pay)))
|
||||
{
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error())
|
||||
<< "rippleLockEscrowMPT: insufficient MPTAmount for "
|
||||
<< to_string(sender) << ": " << amt << " < " << pay;
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
return tecINTERNAL;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
(*sle)[sfMPTAmount] = amt - pay;
|
||||
|
||||
@@ -2852,12 +2852,12 @@ rippleLockEscrowMPT(
|
||||
uint64_t const locked = (*sle)[~sfLockedAmount].value_or(0);
|
||||
|
||||
if (!canAdd(STAmount(mptIssue, locked), STAmount(mptIssue, pay)))
|
||||
{
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error())
|
||||
<< "rippleLockEscrowMPT: overflow on locked amount for "
|
||||
<< to_string(sender) << ": " << locked << " + " << pay;
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
return tecINTERNAL;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
if (sle->isFieldPresent(sfLockedAmount))
|
||||
(*sle)[sfLockedAmount] += pay;
|
||||
@@ -2877,13 +2877,13 @@ rippleLockEscrowMPT(
|
||||
// Overflow check for addition
|
||||
if (!canAdd(
|
||||
STAmount(mptIssue, issuanceEscrowed), STAmount(mptIssue, pay)))
|
||||
{
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error()) << "rippleLockEscrowMPT: overflow on issuance "
|
||||
"locked amount for "
|
||||
<< mptIssue.getMptID() << ": " << issuanceEscrowed
|
||||
<< " + " << pay;
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
return tecINTERNAL;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
if (sleIssuance->isFieldPresent(sfLockedAmount))
|
||||
(*sleIssuance)[sfLockedAmount] += pay;
|
||||
@@ -2908,21 +2908,21 @@ rippleUnlockEscrowMPT(
|
||||
auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
|
||||
auto sleIssuance = view.peek(mptID);
|
||||
if (!sleIssuance)
|
||||
{
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error()) << "rippleUnlockEscrowMPT: MPT issuance not found for "
|
||||
<< mptIssue.getMptID();
|
||||
return tecOBJECT_NOT_FOUND; // LCOV_EXCL_LINE
|
||||
}
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
// Decrease the Issuance EscrowedAmount
|
||||
{
|
||||
if (!sleIssuance->isFieldPresent(sfLockedAmount))
|
||||
{
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error())
|
||||
<< "rippleUnlockEscrowMPT: no locked amount in issuance for "
|
||||
<< mptIssue.getMptID();
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
return tecINTERNAL;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
auto const locked = sleIssuance->getFieldU64(sfLockedAmount);
|
||||
auto const redeem = amount.mpt().value();
|
||||
@@ -2930,12 +2930,12 @@ rippleUnlockEscrowMPT(
|
||||
// Underflow check for subtraction
|
||||
if (!canSubtract(
|
||||
STAmount(mptIssue, locked), STAmount(mptIssue, redeem)))
|
||||
{
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error())
|
||||
<< "rippleUnlockEscrowMPT: insufficient locked amount for "
|
||||
<< mptIssue.getMptID() << ": " << locked << " < " << redeem;
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
return tecINTERNAL;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
auto const newLocked = locked - redeem;
|
||||
if (newLocked == 0)
|
||||
@@ -2951,23 +2951,23 @@ rippleUnlockEscrowMPT(
|
||||
auto const mptokenID = keylet::mptoken(mptID.key, receiver);
|
||||
auto sle = view.peek(mptokenID);
|
||||
if (!sle)
|
||||
{
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error())
|
||||
<< "rippleUnlockEscrowMPT: MPToken not found for " << receiver;
|
||||
return tecOBJECT_NOT_FOUND; // LCOV_EXCL_LINE
|
||||
}
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
auto current = sle->getFieldU64(sfMPTAmount);
|
||||
auto delta = amount.mpt().value();
|
||||
|
||||
// Overflow check for addition
|
||||
if (!canAdd(STAmount(mptIssue, current), STAmount(mptIssue, delta)))
|
||||
{
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error())
|
||||
<< "rippleUnlockEscrowMPT: overflow on MPTAmount for "
|
||||
<< to_string(receiver) << ": " << current << " + " << delta;
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
return tecINTERNAL;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
(*sle)[sfMPTAmount] += delta;
|
||||
view.update(sle);
|
||||
@@ -2981,55 +2981,56 @@ rippleUnlockEscrowMPT(
|
||||
// Underflow check for subtraction
|
||||
if (!canSubtract(
|
||||
STAmount(mptIssue, outstanding), STAmount(mptIssue, redeem)))
|
||||
{
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error())
|
||||
<< "rippleUnlockEscrowMPT: insufficient outstanding amount for "
|
||||
<< mptIssue.getMptID() << ": " << outstanding << " < "
|
||||
<< redeem;
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
return tecINTERNAL;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
|
||||
view.update(sleIssuance);
|
||||
}
|
||||
|
||||
if (issuer == sender)
|
||||
{
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error()) << "rippleUnlockEscrowMPT: sender is the issuer, "
|
||||
"cannot unlock MPTs.";
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
return tecINTERNAL;
|
||||
} // LCOV_EXCL_STOP
|
||||
else
|
||||
{
|
||||
// Decrease the MPT Holder EscrowedAmount
|
||||
auto const mptokenID = keylet::mptoken(mptID.key, sender);
|
||||
auto sle = view.peek(mptokenID);
|
||||
if (!sle)
|
||||
{
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error())
|
||||
<< "rippleUnlockEscrowMPT: MPToken not found for " << sender;
|
||||
return tecOBJECT_NOT_FOUND; // LCOV_EXCL_LINE
|
||||
}
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
if (!sle->isFieldPresent(sfLockedAmount))
|
||||
{
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error())
|
||||
<< "rippleUnlockEscrowMPT: no locked amount in MPToken for "
|
||||
<< to_string(sender);
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
return tecINTERNAL;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
auto const locked = sle->getFieldU64(sfLockedAmount);
|
||||
auto const delta = amount.mpt().value();
|
||||
|
||||
// Underflow check for subtraction
|
||||
// LCOV_EXCL_START
|
||||
if (!canSubtract(STAmount(mptIssue, locked), STAmount(mptIssue, delta)))
|
||||
{
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error())
|
||||
<< "rippleUnlockEscrowMPT: insufficient locked amount for "
|
||||
<< to_string(sender) << ": " << locked << " < " << delta;
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
return tecINTERNAL;
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
auto const newLocked = locked - delta;
|
||||
if (newLocked == 0)
|
||||
|
||||
Reference in New Issue
Block a user