mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-23 23:20:33 +00:00
Merge branch 'XRPLF:develop' into nudbBlockSize
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)
|
||||
|
||||
@@ -361,6 +361,7 @@ enum TECcodes : TERUnderlyingType {
|
||||
tecLIMIT_EXCEEDED = 195,
|
||||
tecPSEUDO_ACCOUNT = 196,
|
||||
tecPRECISION_LOSS = 197,
|
||||
tecNO_DELEGATE_PERMISSION = 198,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -122,13 +122,7 @@ constexpr std::uint32_t tfClearDeepFreeze = 0x00800000;
|
||||
constexpr std::uint32_t tfTrustSetMask =
|
||||
~(tfUniversal | tfSetfAuth | tfSetNoRipple | tfClearNoRipple | tfSetFreeze |
|
||||
tfClearFreeze | tfSetDeepFreeze | tfClearDeepFreeze);
|
||||
|
||||
// valid flags for granular permission
|
||||
constexpr std::uint32_t tfTrustSetGranularMask = tfSetfAuth | tfSetFreeze | tfClearFreeze;
|
||||
|
||||
// bits representing supportedGranularMask are set to 0 and the bits
|
||||
// representing other flags are set to 1 in tfPermissionMask.
|
||||
constexpr std::uint32_t tfTrustSetPermissionMask = (~tfTrustSetMask) & (~tfTrustSetGranularMask);
|
||||
constexpr std::uint32_t tfTrustSetPermissionMask = ~(tfUniversal | tfSetfAuth | tfSetFreeze | tfClearFreeze);
|
||||
|
||||
// EnableAmendment flags:
|
||||
constexpr std::uint32_t tfGotMajority = 0x00010000;
|
||||
@@ -165,8 +159,7 @@ constexpr std::uint32_t const tfMPTokenAuthorizeMask = ~(tfUniversal | tfMPTUna
|
||||
constexpr std::uint32_t const tfMPTLock = 0x00000001;
|
||||
constexpr std::uint32_t const tfMPTUnlock = 0x00000002;
|
||||
constexpr std::uint32_t const tfMPTokenIssuanceSetMask = ~(tfUniversal | tfMPTLock | tfMPTUnlock);
|
||||
constexpr std::uint32_t const tfMPTokenIssuanceSetGranularMask = tfMPTLock | tfMPTUnlock;
|
||||
constexpr std::uint32_t const tfMPTokenIssuanceSetPermissionMask = (~tfMPTokenIssuanceSetMask) & (~tfMPTokenIssuanceSetGranularMask);
|
||||
constexpr std::uint32_t const tfMPTokenIssuanceSetPermissionMask = ~(tfUniversal | tfMPTLock | tfMPTUnlock);
|
||||
|
||||
// MPTokenIssuanceDestroy flags:
|
||||
constexpr std::uint32_t const tfMPTokenIssuanceDestroyMask = ~tfUniversal;
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace BuildInfo {
|
||||
// and follow the format described at http://semver.org/
|
||||
//------------------------------------------------------------------------------
|
||||
// clang-format off
|
||||
char const* const versionString = "2.5.0-b1"
|
||||
char const* const versionString = "2.5.0-rc1"
|
||||
// clang-format on
|
||||
|
||||
#if defined(DEBUG) || defined(SANITIZER)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -127,6 +127,7 @@ transResults()
|
||||
MAKE_ERROR(tecLIMIT_EXCEEDED, "Limit exceeded."),
|
||||
MAKE_ERROR(tecPSEUDO_ACCOUNT, "This operation is not allowed against a pseudo-account."),
|
||||
MAKE_ERROR(tecPRECISION_LOSS, "The amounts used by the transaction cannot interact."),
|
||||
MAKE_ERROR(tecNO_DELEGATE_PERMISSION, "Delegated account lacks permission to perform this transaction."),
|
||||
|
||||
MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."),
|
||||
MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."),
|
||||
|
||||
@@ -209,10 +209,10 @@ class Delegate_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
// when authorizing account which does not exist, should return
|
||||
// terNO_ACCOUNT
|
||||
// tecNO_TARGET
|
||||
{
|
||||
env(delegate::set(gw, Account("unknown"), {"Payment"}),
|
||||
ter(terNO_ACCOUNT));
|
||||
ter(tecNO_TARGET));
|
||||
}
|
||||
|
||||
// non-delegatable transaction
|
||||
@@ -310,8 +310,9 @@ class Delegate_test : public beast::unit_test::suite
|
||||
|
||||
{
|
||||
// Fee should be checked before permission check,
|
||||
// otherwise tecNO_PERMISSION returned when permission check fails
|
||||
// could cause context reset to pay fee because it is tec error
|
||||
// otherwise tecNO_DELEGATE_PERMISSION returned when permission
|
||||
// check fails could cause context reset to pay fee because it is
|
||||
// tec error
|
||||
auto aliceBalance = env.balance(alice);
|
||||
auto bobBalance = env.balance(bob);
|
||||
auto carolBalance = env.balance(carol);
|
||||
@@ -526,12 +527,12 @@ class Delegate_test : public beast::unit_test::suite
|
||||
// bob does not have permission to create check
|
||||
env(check::create(alice, bob, XRP(10)),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
|
||||
// carol does not have permission to create check
|
||||
env(check::create(alice, bob, XRP(10)),
|
||||
delegate::as(carol),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -566,7 +567,7 @@ class Delegate_test : public beast::unit_test::suite
|
||||
// delegate ledger object is not created yet
|
||||
env(pay(gw, alice, USD(50)),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
env.require(balance(bob, bobBalance - drops(baseFee)));
|
||||
bobBalance = env.balance(bob, XRP);
|
||||
|
||||
@@ -579,7 +580,7 @@ class Delegate_test : public beast::unit_test::suite
|
||||
// bob sends a payment transaction on behalf of gw
|
||||
env(pay(gw, alice, USD(50)),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
env.close();
|
||||
env.require(balance(bob, bobBalance - drops(baseFee)));
|
||||
bobBalance = env.balance(bob, XRP);
|
||||
@@ -596,7 +597,7 @@ class Delegate_test : public beast::unit_test::suite
|
||||
// can not send XRP
|
||||
env(pay(gw, alice, XRP(50)),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
env.close();
|
||||
env.require(balance(bob, bobBalance - drops(baseFee)));
|
||||
bobBalance = env.balance(bob, XRP);
|
||||
@@ -684,7 +685,7 @@ class Delegate_test : public beast::unit_test::suite
|
||||
// permission
|
||||
env(pay(gw, alice, USD(50)),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
env.close();
|
||||
env.require(balance(bob, bobBalance - drops(baseFee)));
|
||||
bobBalance = env.balance(bob, XRP);
|
||||
@@ -729,7 +730,7 @@ class Delegate_test : public beast::unit_test::suite
|
||||
// has unfreeze permission
|
||||
env(trust(alice, gw["USD"](50)),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
env.close();
|
||||
|
||||
// alice creates trustline by herself
|
||||
@@ -743,38 +744,38 @@ class Delegate_test : public beast::unit_test::suite
|
||||
// unsupported flags
|
||||
env(trust(alice, gw["USD"](50), tfSetNoRipple),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
env(trust(alice, gw["USD"](50), tfClearNoRipple),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
env(trust(gw, gw["USD"](0), alice, tfSetDeepFreeze),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
env(trust(gw, gw["USD"](0), alice, tfClearDeepFreeze),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
env.close();
|
||||
|
||||
// supported flags with wrong permission
|
||||
env(trust(gw, gw["USD"](0), alice, tfSetfAuth),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
env(trust(gw, gw["USD"](0), alice, tfSetFreeze),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
env.close();
|
||||
|
||||
env(delegate::set(gw, bob, {"TrustlineAuthorize"}));
|
||||
env.close();
|
||||
env(trust(gw, gw["USD"](0), alice, tfClearFreeze),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
env.close();
|
||||
// although trustline authorize is granted, bob can not change the
|
||||
// limit number
|
||||
env(trust(gw, gw["USD"](50), alice, tfSetfAuth),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
env.close();
|
||||
|
||||
// supported flags with correct permission
|
||||
@@ -795,30 +796,30 @@ class Delegate_test : public beast::unit_test::suite
|
||||
// permission
|
||||
env(trust(gw, gw["USD"](0), alice, tfSetFreeze),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
|
||||
// cannot update LimitAmount with granular permission, both high and
|
||||
// low account
|
||||
env(trust(alice, gw["USD"](100)),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
env(trust(gw, alice["USD"](100)),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
|
||||
// can not set QualityIn or QualityOut
|
||||
auto tx = trust(alice, gw["USD"](50));
|
||||
tx["QualityIn"] = "1000";
|
||||
env(tx, delegate::as(bob), ter(tecNO_PERMISSION));
|
||||
env(tx, delegate::as(bob), ter(tecNO_DELEGATE_PERMISSION));
|
||||
auto tx2 = trust(alice, gw["USD"](50));
|
||||
tx2["QualityOut"] = "1000";
|
||||
env(tx2, delegate::as(bob), ter(tecNO_PERMISSION));
|
||||
env(tx2, delegate::as(bob), ter(tecNO_DELEGATE_PERMISSION));
|
||||
auto tx3 = trust(gw, alice["USD"](50));
|
||||
tx3["QualityIn"] = "1000";
|
||||
env(tx3, delegate::as(bob), ter(tecNO_PERMISSION));
|
||||
env(tx3, delegate::as(bob), ter(tecNO_DELEGATE_PERMISSION));
|
||||
auto tx4 = trust(gw, alice["USD"](50));
|
||||
tx4["QualityOut"] = "1000";
|
||||
env(tx4, delegate::as(bob), ter(tecNO_PERMISSION));
|
||||
env(tx4, delegate::as(bob), ter(tecNO_DELEGATE_PERMISSION));
|
||||
|
||||
// granting TrustSet can make it work
|
||||
env(delegate::set(gw, bob, {"TrustSet"}));
|
||||
@@ -828,7 +829,7 @@ class Delegate_test : public beast::unit_test::suite
|
||||
env(tx5, delegate::as(bob));
|
||||
auto tx6 = trust(alice, gw["USD"](50));
|
||||
tx6["QualityOut"] = "1000";
|
||||
env(tx6, delegate::as(bob), ter(tecNO_PERMISSION));
|
||||
env(tx6, delegate::as(bob), ter(tecNO_DELEGATE_PERMISSION));
|
||||
env(delegate::set(alice, bob, {"TrustSet"}));
|
||||
env.close();
|
||||
env(tx6, delegate::as(bob));
|
||||
@@ -847,14 +848,14 @@ class Delegate_test : public beast::unit_test::suite
|
||||
// bob does not have permission
|
||||
env(trust(alice, gw["USD"](50)),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
env(delegate::set(
|
||||
alice, bob, {"TrustlineUnfreeze", "NFTokenCreateOffer"}));
|
||||
env.close();
|
||||
// bob still does not have permission
|
||||
env(trust(alice, gw["USD"](50)),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
|
||||
// add TrustSet permission and some unrelated permission
|
||||
env(delegate::set(
|
||||
@@ -893,6 +894,56 @@ class Delegate_test : public beast::unit_test::suite
|
||||
env(trust(alice, gw["USD"](50), tfClearNoRipple),
|
||||
delegate::as(bob));
|
||||
}
|
||||
|
||||
// tfFullyCanonicalSig won't block delegated transaction
|
||||
{
|
||||
Env env(*this);
|
||||
Account gw{"gw"};
|
||||
Account alice{"alice"};
|
||||
Account bob{"bob"};
|
||||
env.fund(XRP(10000), gw, alice, bob);
|
||||
env(fset(gw, asfRequireAuth));
|
||||
env.close();
|
||||
env(trust(alice, gw["USD"](50)));
|
||||
env.close();
|
||||
|
||||
env(delegate::set(gw, bob, {"TrustlineAuthorize"}));
|
||||
env.close();
|
||||
env(trust(
|
||||
gw, gw["USD"](0), alice, tfSetfAuth | tfFullyCanonicalSig),
|
||||
delegate::as(bob));
|
||||
}
|
||||
|
||||
// tfInnerBatchTxn won't block delegated transaction
|
||||
{
|
||||
Env env(*this);
|
||||
Account gw{"gw"};
|
||||
Account alice{"alice"};
|
||||
Account bob{"bob"};
|
||||
env.fund(XRP(10000), gw, alice, bob);
|
||||
env(fset(gw, asfRequireAuth));
|
||||
env.close();
|
||||
env(trust(alice, gw["USD"](50)));
|
||||
env.close();
|
||||
|
||||
env(delegate::set(
|
||||
gw, bob, {"TrustlineAuthorize", "TrustlineFreeze"}));
|
||||
env.close();
|
||||
|
||||
auto const seq = env.seq(gw);
|
||||
auto const batchFee = batch::calcBatchFee(env, 0, 2);
|
||||
auto jv1 = trust(gw, gw["USD"](0), alice, tfSetfAuth);
|
||||
jv1[sfDelegate] = bob.human();
|
||||
auto jv2 = trust(gw, gw["USD"](0), alice, tfSetFreeze);
|
||||
jv2[sfDelegate] = bob.human();
|
||||
|
||||
// batch::inner will set tfInnerBatchTxn, this should not
|
||||
// block delegated transaction
|
||||
env(batch::outer(gw, seq, batchFee, tfAllOrNothing),
|
||||
batch::inner(jv1, seq + 1),
|
||||
batch::inner(jv2, seq + 2));
|
||||
env.close();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -920,16 +971,15 @@ class Delegate_test : public beast::unit_test::suite
|
||||
// on behalf of alice
|
||||
std::string const domain = "example.com";
|
||||
auto jt = noop(alice);
|
||||
jt[sfDomain.fieldName] = strHex(domain);
|
||||
jt[sfDelegate.fieldName] = bob.human();
|
||||
jt[sfFlags.fieldName] = tfFullyCanonicalSig;
|
||||
jt[sfDomain] = strHex(domain);
|
||||
jt[sfDelegate] = bob.human();
|
||||
|
||||
// add granular permission related to AccountSet but is not the
|
||||
// correct permission for domain set
|
||||
env(delegate::set(
|
||||
alice, bob, {"TrustlineUnfreeze", "AccountEmailHashSet"}));
|
||||
env.close();
|
||||
env(jt, ter(tecNO_PERMISSION));
|
||||
env(jt, ter(tecNO_DELEGATE_PERMISSION));
|
||||
|
||||
// alice give granular permission of AccountDomainSet to bob
|
||||
env(delegate::set(alice, bob, {"AccountDomainSet"}));
|
||||
@@ -940,25 +990,24 @@ class Delegate_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
|
||||
|
||||
// bob can reset domain
|
||||
jt[sfDomain.fieldName] = "";
|
||||
jt[sfDomain] = "";
|
||||
env(jt);
|
||||
BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfDomain));
|
||||
|
||||
// if flag is not equal to tfFullyCanonicalSig, which means bob
|
||||
// is trying to set the flag at the same time, it will fail
|
||||
// bob tries to set unauthorized flag, it will fail
|
||||
std::string const failDomain = "fail_domain_update";
|
||||
jt[sfFlags.fieldName] = tfRequireAuth;
|
||||
jt[sfDomain.fieldName] = strHex(failDomain);
|
||||
env(jt, ter(tecNO_PERMISSION));
|
||||
jt[sfFlags] = tfRequireAuth;
|
||||
jt[sfDomain] = strHex(failDomain);
|
||||
env(jt, ter(tecNO_DELEGATE_PERMISSION));
|
||||
// reset flag number
|
||||
jt[sfFlags.fieldName] = tfFullyCanonicalSig;
|
||||
jt[sfFlags] = 0;
|
||||
|
||||
// bob tries to update domain and set email hash,
|
||||
// but he does not have permission to set email hash
|
||||
jt[sfDomain.fieldName] = strHex(domain);
|
||||
jt[sfDomain] = strHex(domain);
|
||||
std::string const mh("5F31A79367DC3137FADA860C05742EE6");
|
||||
jt[sfEmailHash.fieldName] = mh;
|
||||
env(jt, ter(tecNO_PERMISSION));
|
||||
jt[sfEmailHash] = mh;
|
||||
env(jt, ter(tecNO_DELEGATE_PERMISSION));
|
||||
|
||||
// alice give granular permission of AccountEmailHashSet to bob
|
||||
env(delegate::set(
|
||||
@@ -970,8 +1019,8 @@ class Delegate_test : public beast::unit_test::suite
|
||||
|
||||
// bob does not have permission to set message key for alice
|
||||
auto const rkp = randomKeyPair(KeyType::ed25519);
|
||||
jt[sfMessageKey.fieldName] = strHex(rkp.first.slice());
|
||||
env(jt, ter(tecNO_PERMISSION));
|
||||
jt[sfMessageKey] = strHex(rkp.first.slice());
|
||||
env(jt, ter(tecNO_DELEGATE_PERMISSION));
|
||||
|
||||
// alice give granular permission of AccountMessageKeySet to bob
|
||||
env(delegate::set(
|
||||
@@ -987,12 +1036,14 @@ class Delegate_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(
|
||||
strHex((*env.le(alice))[sfMessageKey]) ==
|
||||
strHex(rkp.first.slice()));
|
||||
jt[sfMessageKey.fieldName] = "";
|
||||
jt[sfMessageKey] = "";
|
||||
env(jt);
|
||||
BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfMessageKey));
|
||||
|
||||
// bob does not have permission to set transfer rate for alice
|
||||
env(rate(alice, 2.0), delegate::as(bob), ter(tecNO_PERMISSION));
|
||||
env(rate(alice, 2.0),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
|
||||
// alice give granular permission of AccountTransferRateSet to bob
|
||||
env(delegate::set(
|
||||
@@ -1004,14 +1055,13 @@ class Delegate_test : public beast::unit_test::suite
|
||||
"AccountTransferRateSet"}));
|
||||
env.close();
|
||||
auto jtRate = rate(alice, 2.0);
|
||||
jtRate[sfDelegate.fieldName] = bob.human();
|
||||
jtRate[sfFlags.fieldName] = tfFullyCanonicalSig;
|
||||
jtRate[sfDelegate] = bob.human();
|
||||
env(jtRate, delegate::as(bob));
|
||||
BEAST_EXPECT((*env.le(alice))[sfTransferRate] == 2000000000);
|
||||
|
||||
// bob does not have permission to set ticksize for alice
|
||||
jt[sfTickSize.fieldName] = 8;
|
||||
env(jt, ter(tecNO_PERMISSION));
|
||||
jt[sfTickSize] = 8;
|
||||
env(jt, ter(tecNO_DELEGATE_PERMISSION));
|
||||
|
||||
// alice give granular permission of AccountTickSizeSet to bob
|
||||
env(delegate::set(
|
||||
@@ -1029,7 +1079,7 @@ class Delegate_test : public beast::unit_test::suite
|
||||
// can not set asfRequireAuth flag for alice
|
||||
env(fset(alice, asfRequireAuth),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
|
||||
// reset Delegate will delete the Delegate
|
||||
// object
|
||||
@@ -1038,15 +1088,15 @@ class Delegate_test : public beast::unit_test::suite
|
||||
// alice
|
||||
env(fset(alice, asfRequireAuth),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
// alice can set for herself
|
||||
env(fset(alice, asfRequireAuth));
|
||||
env.require(flags(alice, asfRequireAuth));
|
||||
env.close();
|
||||
|
||||
// can not update tick size because bob no longer has permission
|
||||
jt[sfTickSize.fieldName] = 7;
|
||||
env(jt, ter(tecNO_PERMISSION));
|
||||
jt[sfTickSize] = 7;
|
||||
env(jt, ter(tecNO_DELEGATE_PERMISSION));
|
||||
|
||||
env(delegate::set(
|
||||
alice,
|
||||
@@ -1060,12 +1110,11 @@ class Delegate_test : public beast::unit_test::suite
|
||||
std::string const locator =
|
||||
"9633EC8AF54F16B5286DB1D7B519EF49EEFC050C0C8AC4384F1D88ACD1BFDF"
|
||||
"05";
|
||||
auto jt2 = noop(alice);
|
||||
jt2[sfDomain.fieldName] = strHex(domain);
|
||||
jt2[sfDelegate.fieldName] = bob.human();
|
||||
jt2[sfWalletLocator.fieldName] = locator;
|
||||
jt2[sfFlags.fieldName] = tfFullyCanonicalSig;
|
||||
env(jt2, ter(tecNO_PERMISSION));
|
||||
auto jv2 = noop(alice);
|
||||
jv2[sfDomain] = strHex(domain);
|
||||
jv2[sfDelegate] = bob.human();
|
||||
jv2[sfWalletLocator] = locator;
|
||||
env(jv2, ter(tecNO_DELEGATE_PERMISSION));
|
||||
}
|
||||
|
||||
// can not set AccountSet flags on behalf of other account
|
||||
@@ -1080,7 +1129,7 @@ class Delegate_test : public beast::unit_test::suite
|
||||
// bob can not set flag on behalf of alice
|
||||
env(fset(alice, flag),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
// alice set by herself
|
||||
env(fset(alice, flag));
|
||||
env.close();
|
||||
@@ -1088,7 +1137,7 @@ class Delegate_test : public beast::unit_test::suite
|
||||
// bob can not clear on behalf of alice
|
||||
env(fclear(alice, flag),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
};
|
||||
|
||||
// testSetClearFlag(asfNoFreeze);
|
||||
@@ -1117,19 +1166,19 @@ class Delegate_test : public beast::unit_test::suite
|
||||
// bob can not set asfAccountTxnID on behalf of alice
|
||||
env(fset(alice, asfAccountTxnID),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
env(fset(alice, asfAccountTxnID));
|
||||
env.close();
|
||||
BEAST_EXPECT(env.le(alice)->isFieldPresent(sfAccountTxnID));
|
||||
env(fclear(alice, asfAccountTxnID),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
|
||||
// bob can not set asfAuthorizedNFTokenMinter on behalf of alice
|
||||
Json::Value jt = fset(alice, asfAuthorizedNFTokenMinter);
|
||||
jt[sfDelegate.fieldName] = bob.human();
|
||||
jt[sfNFTokenMinter.fieldName] = bob.human();
|
||||
env(jt, ter(tecNO_PERMISSION));
|
||||
jt[sfDelegate] = bob.human();
|
||||
jt[sfNFTokenMinter] = bob.human();
|
||||
env(jt, ter(tecNO_DELEGATE_PERMISSION));
|
||||
|
||||
// bob gives alice some permissions
|
||||
env(delegate::set(
|
||||
@@ -1145,14 +1194,14 @@ class Delegate_test : public beast::unit_test::suite
|
||||
// behalf of bob.
|
||||
env(fset(alice, asfNoFreeze),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
env(fset(bob, asfNoFreeze));
|
||||
env.close();
|
||||
env.require(flags(bob, asfNoFreeze));
|
||||
// alice can not clear on behalf of bob
|
||||
env(fclear(alice, asfNoFreeze),
|
||||
delegate::as(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
|
||||
// bob can not set asfDisableMaster on behalf of alice
|
||||
Account const bobKey{"bobKey", KeyType::secp256k1};
|
||||
@@ -1161,7 +1210,76 @@ class Delegate_test : public beast::unit_test::suite
|
||||
env(fset(alice, asfDisableMaster),
|
||||
delegate::as(bob),
|
||||
sig(bob),
|
||||
ter(tecNO_PERMISSION));
|
||||
ter(tecNO_DELEGATE_PERMISSION));
|
||||
}
|
||||
|
||||
// tfFullyCanonicalSig won't block delegated transaction
|
||||
{
|
||||
Env env(*this);
|
||||
Account alice{"alice"};
|
||||
Account bob{"bob"};
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
env.close();
|
||||
|
||||
env(delegate::set(
|
||||
alice, bob, {"AccountDomainSet", "AccountEmailHashSet"}));
|
||||
env.close();
|
||||
|
||||
std::string const domain = "example.com";
|
||||
auto jt = noop(alice);
|
||||
jt[sfDomain] = strHex(domain);
|
||||
jt[sfDelegate] = bob.human();
|
||||
jt[sfFlags] = tfFullyCanonicalSig;
|
||||
|
||||
env(jt);
|
||||
BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
|
||||
}
|
||||
|
||||
// tfInnerBatchTxn won't block delegated transaction
|
||||
{
|
||||
Env env(*this);
|
||||
Account alice{"alice"};
|
||||
Account bob{"bob"};
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
env.close();
|
||||
|
||||
env(delegate::set(
|
||||
alice, bob, {"AccountDomainSet", "AccountEmailHashSet"}));
|
||||
env.close();
|
||||
|
||||
auto const seq = env.seq(alice);
|
||||
auto const batchFee = batch::calcBatchFee(env, 0, 3);
|
||||
|
||||
auto jv1 = noop(alice);
|
||||
std::string const domain1 = "example1.com";
|
||||
jv1[sfDomain] = strHex(domain1);
|
||||
jv1[sfDelegate] = bob.human();
|
||||
jv1[sfSequence] = seq + 1;
|
||||
|
||||
auto jv2 = noop(alice);
|
||||
std::string const domain2 = "example2.com";
|
||||
jv2[sfDomain] = strHex(domain2);
|
||||
jv2[sfDelegate] = bob.human();
|
||||
jv2[sfSequence] = seq + 2;
|
||||
|
||||
// bob set domain back and add email hash for alice
|
||||
auto jv3 = noop(alice);
|
||||
std::string const mh("5F31A79367DC3137FADA860C05742EE6");
|
||||
jv3[sfDomain] = strHex(domain1);
|
||||
jv3[sfEmailHash] = mh;
|
||||
jv3[sfDelegate] = bob.human();
|
||||
jv3[sfSequence] = seq + 3;
|
||||
|
||||
// batch::inner will set tfInnerBatchTxn, this should not
|
||||
// block delegated transaction
|
||||
env(batch::outer(alice, seq, batchFee, tfAllOrNothing),
|
||||
batch::inner(jv1, seq + 1),
|
||||
batch::inner(jv2, seq + 2),
|
||||
batch::inner(jv3, seq + 3));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain1));
|
||||
BEAST_EXPECT(to_string((*env.le(alice))[sfEmailHash]) == mh);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1189,7 +1307,7 @@ class Delegate_test : public beast::unit_test::suite
|
||||
{.account = alice,
|
||||
.flags = tfMPTLock,
|
||||
.delegate = bob,
|
||||
.err = tecNO_PERMISSION});
|
||||
.err = tecNO_DELEGATE_PERMISSION});
|
||||
|
||||
// alice gives granular permission to bob of MPTokenIssuanceUnlock
|
||||
env(delegate::set(alice, bob, {"MPTokenIssuanceUnlock"}));
|
||||
@@ -1199,7 +1317,7 @@ class Delegate_test : public beast::unit_test::suite
|
||||
{.account = alice,
|
||||
.flags = tfMPTLock,
|
||||
.delegate = bob,
|
||||
.err = tecNO_PERMISSION});
|
||||
.err = tecNO_DELEGATE_PERMISSION});
|
||||
// bob now has lock permission, but does not have unlock permission
|
||||
env(delegate::set(alice, bob, {"MPTokenIssuanceLock"}));
|
||||
env.close();
|
||||
@@ -1208,7 +1326,7 @@ class Delegate_test : public beast::unit_test::suite
|
||||
{.account = alice,
|
||||
.flags = tfMPTUnlock,
|
||||
.delegate = bob,
|
||||
.err = tecNO_PERMISSION});
|
||||
.err = tecNO_DELEGATE_PERMISSION});
|
||||
|
||||
// now bob can lock and unlock
|
||||
env(delegate::set(
|
||||
@@ -1241,7 +1359,7 @@ class Delegate_test : public beast::unit_test::suite
|
||||
{.account = alice,
|
||||
.flags = tfMPTUnlock,
|
||||
.delegate = bob,
|
||||
.err = tecNO_PERMISSION});
|
||||
.err = tecNO_DELEGATE_PERMISSION});
|
||||
|
||||
// alice gives bob some unrelated permission with
|
||||
// MPTokenIssuanceLock
|
||||
@@ -1255,7 +1373,7 @@ class Delegate_test : public beast::unit_test::suite
|
||||
{.account = alice,
|
||||
.flags = tfMPTUnlock,
|
||||
.delegate = bob,
|
||||
.err = tecNO_PERMISSION});
|
||||
.err = tecNO_DELEGATE_PERMISSION});
|
||||
|
||||
// alice add MPTokenIssuanceSet to permissions
|
||||
env(delegate::set(
|
||||
@@ -1271,6 +1389,74 @@ class Delegate_test : public beast::unit_test::suite
|
||||
mpt.set({.account = alice, .flags = tfMPTUnlock, .delegate = bob});
|
||||
mpt.set({.account = alice, .flags = tfMPTLock, .delegate = bob});
|
||||
}
|
||||
|
||||
// tfFullyCanonicalSig won't block delegated transaction
|
||||
{
|
||||
Env env(*this);
|
||||
Account alice{"alice"};
|
||||
Account bob{"bob"};
|
||||
env.fund(XRP(100000), alice, bob);
|
||||
env.close();
|
||||
|
||||
MPTTester mpt(env, alice, {.fund = false});
|
||||
env.close();
|
||||
mpt.create({.flags = tfMPTCanLock});
|
||||
env.close();
|
||||
|
||||
// alice gives granular permission to bob of MPTokenIssuanceLock
|
||||
env(delegate::set(alice, bob, {"MPTokenIssuanceLock"}));
|
||||
env.close();
|
||||
mpt.set(
|
||||
{.account = alice,
|
||||
.flags = tfMPTLock | tfFullyCanonicalSig,
|
||||
.delegate = bob});
|
||||
}
|
||||
|
||||
// tfInnerBatchTxn won't block delegated transaction
|
||||
{
|
||||
Env env(*this);
|
||||
Account alice{"alice"};
|
||||
Account bob{"bob"};
|
||||
env.fund(XRP(100000), alice, bob);
|
||||
env.close();
|
||||
|
||||
auto const mptID = makeMptID(env.seq(alice), alice);
|
||||
MPTTester mpt(env, alice, {.fund = false});
|
||||
env.close();
|
||||
mpt.create({.flags = tfMPTCanLock});
|
||||
env.close();
|
||||
|
||||
// alice gives granular permission to bob of MPTokenIssuanceLock
|
||||
env(delegate::set(
|
||||
alice, bob, {"MPTokenIssuanceLock", "MPTokenIssuanceUnlock"}));
|
||||
env.close();
|
||||
|
||||
auto const seq = env.seq(alice);
|
||||
auto const batchFee = batch::calcBatchFee(env, 0, 2);
|
||||
|
||||
Json::Value jv1;
|
||||
jv1[sfTransactionType] = jss::MPTokenIssuanceSet;
|
||||
jv1[sfAccount] = alice.human();
|
||||
jv1[sfDelegate] = bob.human();
|
||||
jv1[sfSequence] = seq + 1;
|
||||
jv1[sfMPTokenIssuanceID] = to_string(mptID);
|
||||
jv1[sfFlags] = tfMPTLock;
|
||||
|
||||
Json::Value jv2;
|
||||
jv2[sfTransactionType] = jss::MPTokenIssuanceSet;
|
||||
jv2[sfAccount] = alice.human();
|
||||
jv2[sfDelegate] = bob.human();
|
||||
jv2[sfSequence] = seq + 2;
|
||||
jv2[sfMPTokenIssuanceID] = to_string(mptID);
|
||||
jv2[sfFlags] = tfMPTUnlock;
|
||||
|
||||
// batch::inner will set tfInnerBatchTxn, this should not
|
||||
// block delegated transaction
|
||||
env(batch::outer(alice, seq, batchFee, tfAllOrNothing),
|
||||
batch::inner(jv1, seq + 1),
|
||||
batch::inner(jv2, seq + 2));
|
||||
env.close();
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
|
||||
@@ -762,6 +762,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
|
||||
|
||||
@@ -502,15 +502,17 @@ InboundLedger::trigger(std::shared_ptr<Peer> const& peer, TriggerReason reason)
|
||||
|
||||
if (auto stream = journal_.debug())
|
||||
{
|
||||
stream << "Trigger acquiring ledger " << hash_;
|
||||
std::stringstream ss;
|
||||
ss << "Trigger acquiring ledger " << hash_;
|
||||
if (peer)
|
||||
stream << " from " << peer;
|
||||
ss << " from " << peer;
|
||||
|
||||
if (complete_ || failed_)
|
||||
stream << "complete=" << complete_ << " failed=" << failed_;
|
||||
ss << " complete=" << complete_ << " failed=" << failed_;
|
||||
else
|
||||
stream << "header=" << mHaveHeader << " tx=" << mHaveTransactions
|
||||
<< " as=" << mHaveState;
|
||||
ss << " header=" << mHaveHeader << " tx=" << mHaveTransactions
|
||||
<< " as=" << mHaveState;
|
||||
stream << ss.str();
|
||||
}
|
||||
|
||||
if (!mHaveHeader)
|
||||
|
||||
@@ -256,8 +256,8 @@ public:
|
||||
if ((cores == 1) || ((config.NODE_SIZE == 0) && (cores == 2)))
|
||||
return 1;
|
||||
|
||||
// Otherwise, prefer two threads.
|
||||
return 2;
|
||||
// Otherwise, prefer six threads.
|
||||
return 6;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@ namespace ripple {
|
||||
* Check if the delegate account has permission to execute the transaction.
|
||||
* @param delegate The delegate account.
|
||||
* @param tx The transaction that the delegate account intends to execute.
|
||||
* @return tesSUCCESS if the transaction is allowed, tecNO_PERMISSION if not.
|
||||
* @return tesSUCCESS if the transaction is allowed, tecNO_DELEGATE_PERMISSION
|
||||
* if not.
|
||||
*/
|
||||
TER
|
||||
checkTxPermission(std::shared_ptr<SLE const> const& delegate, STTx const& tx);
|
||||
|
||||
@@ -26,7 +26,7 @@ TER
|
||||
checkTxPermission(std::shared_ptr<SLE const> const& delegate, STTx const& tx)
|
||||
{
|
||||
if (!delegate)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
return tecNO_DELEGATE_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
auto const permissionArray = delegate->getFieldArray(sfPermissions);
|
||||
auto const txPermission = tx.getTxnType() + 1;
|
||||
@@ -38,7 +38,7 @@ checkTxPermission(std::shared_ptr<SLE const> const& delegate, STTx const& tx)
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -63,7 +63,7 @@ DelegateSet::preclaim(PreclaimContext const& ctx)
|
||||
return terNO_ACCOUNT; // LCOV_EXCL_LINE
|
||||
|
||||
if (!ctx.view.exists(keylet::account(ctx.tx[sfAuthorize])))
|
||||
return terNO_ACCOUNT;
|
||||
return tecNO_TARGET;
|
||||
|
||||
auto const& permissions = ctx.tx.getFieldArray(sfPermissions);
|
||||
for (auto const& permission : permissions)
|
||||
|
||||
@@ -595,7 +595,7 @@ EscrowCreate::doApply()
|
||||
},
|
||||
amount.asset().value());
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
return ret; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
// increment owner count
|
||||
@@ -766,26 +766,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;
|
||||
}
|
||||
@@ -1015,7 +1015,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
|
||||
@@ -1245,7 +1250,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
|
||||
@@ -1261,26 +1266,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;
|
||||
}
|
||||
@@ -1291,7 +1297,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))
|
||||
{
|
||||
|
||||
@@ -271,26 +271,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())
|
||||
{
|
||||
@@ -300,7 +280,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;
|
||||
};
|
||||
|
||||
|
||||
@@ -59,7 +59,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;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ MPTokenIssuanceSet::checkPermission(ReadView const& view, STTx const& tx)
|
||||
auto const sle = view.read(delegateKey);
|
||||
|
||||
if (!sle)
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
if (checkTxPermission(sle, tx) == tesSUCCESS)
|
||||
return tesSUCCESS;
|
||||
@@ -72,18 +72,18 @@ MPTokenIssuanceSet::checkPermission(ReadView const& view, STTx const& tx)
|
||||
// this is added in case more flags will be added for MPTokenIssuanceSet
|
||||
// in the future. Currently unreachable.
|
||||
if (txFlags & tfMPTokenIssuanceSetPermissionMask)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
return tecNO_DELEGATE_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
std::unordered_set<GranularPermissionType> granularPermissions;
|
||||
loadGranularPermission(sle, ttMPTOKEN_ISSUANCE_SET, granularPermissions);
|
||||
|
||||
if (txFlags & tfMPTLock &&
|
||||
!granularPermissions.contains(MPTokenIssuanceLock))
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
if (txFlags & tfMPTUnlock &&
|
||||
!granularPermissions.contains(MPTokenIssuanceUnlock))
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -255,7 +255,7 @@ Payment::checkPermission(ReadView const& view, STTx const& tx)
|
||||
auto const sle = view.read(delegateKey);
|
||||
|
||||
if (!sle)
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
if (checkTxPermission(sle, tx) == tesSUCCESS)
|
||||
return tesSUCCESS;
|
||||
@@ -274,7 +274,7 @@ Payment::checkPermission(ReadView const& view, STTx const& tx)
|
||||
amountIssue.account == tx[sfDestination])
|
||||
return tesSUCCESS;
|
||||
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
}
|
||||
|
||||
TER
|
||||
|
||||
@@ -202,7 +202,7 @@ SetAccount::checkPermission(ReadView const& view, STTx const& tx)
|
||||
auto const sle = view.read(delegateKey);
|
||||
|
||||
if (!sle)
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
std::unordered_set<GranularPermissionType> granularPermissions;
|
||||
loadGranularPermission(sle, ttACCOUNT_SET, granularPermissions);
|
||||
@@ -215,31 +215,31 @@ SetAccount::checkPermission(ReadView const& view, STTx const& tx)
|
||||
// update the flag on behalf of another account, it is not
|
||||
// authorized.
|
||||
if (uSetFlag != 0 || uClearFlag != 0 || uTxFlags & tfUniversalMask)
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
if (tx.isFieldPresent(sfEmailHash) &&
|
||||
!granularPermissions.contains(AccountEmailHashSet))
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
if (tx.isFieldPresent(sfWalletLocator) ||
|
||||
tx.isFieldPresent(sfNFTokenMinter))
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
if (tx.isFieldPresent(sfMessageKey) &&
|
||||
!granularPermissions.contains(AccountMessageKeySet))
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
if (tx.isFieldPresent(sfDomain) &&
|
||||
!granularPermissions.contains(AccountDomainSet))
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
if (tx.isFieldPresent(sfTransferRate) &&
|
||||
!granularPermissions.contains(AccountTransferRateSet))
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
if (tx.isFieldPresent(sfTickSize) &&
|
||||
!granularPermissions.contains(AccountTickSizeSet))
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ SetTrust::checkPermission(ReadView const& view, STTx const& tx)
|
||||
auto const sle = view.read(delegateKey);
|
||||
|
||||
if (!sle)
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
if (checkTxPermission(sle, tx) == tesSUCCESS)
|
||||
return tesSUCCESS;
|
||||
@@ -152,10 +152,10 @@ SetTrust::checkPermission(ReadView const& view, STTx const& tx)
|
||||
// TrustlineUnfreeze granular permission. Setting other flags returns
|
||||
// error.
|
||||
if (txFlags & tfTrustSetPermissionMask)
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
if (tx.isFieldPresent(sfQualityIn) || tx.isFieldPresent(sfQualityOut))
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
auto const saLimitAmount = tx.getFieldAmount(sfLimitAmount);
|
||||
auto const sleRippleState = view.read(keylet::line(
|
||||
@@ -164,19 +164,19 @@ SetTrust::checkPermission(ReadView const& view, STTx const& tx)
|
||||
// if the trustline does not exist, granular permissions are
|
||||
// not allowed to create trustline
|
||||
if (!sleRippleState)
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
std::unordered_set<GranularPermissionType> granularPermissions;
|
||||
loadGranularPermission(sle, ttTRUST_SET, granularPermissions);
|
||||
|
||||
if (txFlags & tfSetfAuth &&
|
||||
!granularPermissions.contains(TrustlineAuthorize))
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
if (txFlags & tfSetFreeze && !granularPermissions.contains(TrustlineFreeze))
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
if (txFlags & tfClearFreeze &&
|
||||
!granularPermissions.contains(TrustlineUnfreeze))
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
// updating LimitAmount is not allowed only with granular permissions,
|
||||
// unless there's a new granular permission for this in the future.
|
||||
@@ -188,7 +188,7 @@ SetTrust::checkPermission(ReadView const& view, STTx const& tx)
|
||||
saLimitAllow.setIssuer(tx[sfAccount]);
|
||||
|
||||
if (curLimit != saLimitAllow)
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ Transactor::checkPermission(ReadView const& view, STTx const& tx)
|
||||
auto const sle = view.read(delegateKey);
|
||||
|
||||
if (!sle)
|
||||
return tecNO_PERMISSION;
|
||||
return tecNO_DELEGATE_PERMISSION;
|
||||
|
||||
return checkTxPermission(sle, tx);
|
||||
}
|
||||
|
||||
@@ -2747,18 +2747,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
|
||||
@@ -2766,23 +2766,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;
|
||||
|
||||
@@ -2790,12 +2790,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;
|
||||
@@ -2815,13 +2815,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;
|
||||
@@ -2846,21 +2846,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();
|
||||
@@ -2868,12 +2868,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)
|
||||
@@ -2889,23 +2889,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);
|
||||
@@ -2919,55 +2919,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