mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-11 06:08:02 +00:00
Compare commits
7 Commits
mvadari/re
...
dangell7/f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32dcfa02f6 | ||
|
|
94edb114ae | ||
|
|
25cffb82e3 | ||
|
|
48727b0341 | ||
|
|
03e1a8f0ac | ||
|
|
9d96c2c085 | ||
|
|
298b95d1f1 |
@@ -387,7 +387,14 @@ isPseudoAccount(ReadView const& view, AccountID const& accountId)
|
||||
* createPseudoAccount.
|
||||
*/
|
||||
[[nodiscard]] std::expected<SLE::pointer, TER>
|
||||
createPseudoAccount(ApplyView& view, UInt256 const& pseudoOwnerKey, SField const& ownerField);
|
||||
createPseudoAccount(
|
||||
ApplyView& view,
|
||||
UInt256 const& pseudoOwnerKey,
|
||||
SField const& ownerField,
|
||||
std::uint32_t additionalFlags = 0);
|
||||
|
||||
[[nodiscard]] bool
|
||||
isBlackholed(ReadView const& view, std::shared_ptr<SLE const> const& sle);
|
||||
|
||||
/**
|
||||
* Checks the destination and tag.
|
||||
|
||||
@@ -26,6 +26,7 @@ XRPL_FEATURE(BatchV1_1, Supported::Yes, VoteBehavior::DefaultN
|
||||
XRPL_FEATURE(LendingProtocolV1_1, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(ConfidentialTransfer, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (Cleanup3_3_0, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (TokenEscrowV1_1, Supported::No, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (Cleanup3_2_0, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(MPTokensV2, Supported::No, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (Cleanup3_1_3, Supported::Yes, VoteBehavior::DefaultYes)
|
||||
|
||||
@@ -556,8 +556,36 @@ isPseudoAccount(SLE::const_pointer sleAcct)
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isBlackholed(ReadView const& view, std::shared_ptr<SLE const> const& sle)
|
||||
{
|
||||
if (!sle || sle->getType() != ltACCOUNT_ROOT)
|
||||
return false; // LCOV_EXCL_LINE
|
||||
|
||||
if (!sle->isFlag(lsfDisableMaster))
|
||||
return false;
|
||||
|
||||
if (sle->isFieldPresent(sfRegularKey))
|
||||
{
|
||||
// A regular key can still sign unless it is one of the blackhole
|
||||
// addresses, which are derived from an integer and not from a key.
|
||||
static AccountID const kAccountTwo(2);
|
||||
AccountID const rk = sle->getAccountID(sfRegularKey);
|
||||
|
||||
if (rk != xrpAccount() && rk != noAccount() && rk != kAccountTwo)
|
||||
return false;
|
||||
}
|
||||
|
||||
AccountID const account = sle->getAccountID(sfAccount);
|
||||
return !view.exists(keylet::signerList(account));
|
||||
}
|
||||
|
||||
std::expected<SLE::pointer, TER>
|
||||
createPseudoAccount(ApplyView& view, UInt256 const& pseudoOwnerKey, SField const& ownerField)
|
||||
createPseudoAccount(
|
||||
ApplyView& view,
|
||||
UInt256 const& pseudoOwnerKey,
|
||||
SField const& ownerField,
|
||||
std::uint32_t additionalFlags)
|
||||
{
|
||||
[[maybe_unused]]
|
||||
auto const& fields = getPseudoAccountFields();
|
||||
@@ -589,7 +617,8 @@ createPseudoAccount(ApplyView& view, UInt256 const& pseudoOwnerKey, SField const
|
||||
// Ignore reserves requirement, disable the master key, allow default
|
||||
// rippling, and enable deposit authorization to prevent payments into
|
||||
// pseudo-account.
|
||||
account->setFieldU32(sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth);
|
||||
account->setFieldU32(
|
||||
sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth | additionalFlags);
|
||||
// Link the pseudo-account with its owner object.
|
||||
account->setFieldH256(ownerField, pseudoOwnerKey);
|
||||
|
||||
|
||||
@@ -792,8 +792,11 @@ ValidNewAccountRoot::finalize(
|
||||
|
||||
if (pseudoAccount)
|
||||
{
|
||||
std::uint32_t const expected = (lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth);
|
||||
if (flags_ != expected)
|
||||
std::uint32_t const base = (lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth);
|
||||
bool valid = (flags_ == base);
|
||||
if (!valid && view.rules().enabled(fixTokenEscrowV1_1))
|
||||
valid = (flags_ == (base | lsfAllowTrustLineLocking));
|
||||
if (!valid)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: pseudo-account created with "
|
||||
"wrong flags";
|
||||
|
||||
@@ -263,7 +263,9 @@ applyCreate(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Jou
|
||||
auto const ammKeylet = keylet::amm(amount.asset(), amount2.asset());
|
||||
|
||||
// Mitigate same account exists possibility
|
||||
auto const maybeAccount = createPseudoAccount(sb, ammKeylet.key, sfAMMID);
|
||||
std::uint32_t const additionalFlags =
|
||||
sb.rules().enabled(fixTokenEscrowV1_1) ? lsfAllowTrustLineLocking : 0u;
|
||||
auto const maybeAccount = createPseudoAccount(sb, ammKeylet.key, sfAMMID, additionalFlags);
|
||||
// AMM account already exists (should not happen)
|
||||
if (!maybeAccount)
|
||||
{
|
||||
|
||||
@@ -211,7 +211,19 @@ escrowCreatePreclaimHelper<Issue>(
|
||||
if (!sleIssuer)
|
||||
return tecNO_ISSUER;
|
||||
if (!sleIssuer->isFlag(lsfAllowTrustLineLocking))
|
||||
return tecNO_PERMISSION;
|
||||
{
|
||||
if (ctx.view.rules().enabled(fixTokenEscrowV1_1))
|
||||
{
|
||||
bool const isAMM = sleIssuer->isFieldPresent(sfAMMID);
|
||||
bool const isBlackholedIssuer = isBlackholed(ctx.view, sleIssuer);
|
||||
if (!isAMM && !isBlackholedIssuer)
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
else
|
||||
{
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
}
|
||||
|
||||
// If the account does not have a trustline to the issuer, return tecNO_LINE
|
||||
auto const sleRippleState = ctx.view.read(keylet::trustLine(account, issuer, issue.currency));
|
||||
@@ -502,6 +514,23 @@ EscrowCreate::doApply()
|
||||
auto const xferRate = transferRate(ctx_.view(), amount);
|
||||
if (xferRate != kParityRate)
|
||||
(*slep)[sfTransferRate] = xferRate.value;
|
||||
|
||||
if (ctx_.view().rules().enabled(fixTokenEscrowV1_1))
|
||||
{
|
||||
AccountID const issuer = amount.getIssuer();
|
||||
auto sleIssuer = ctx_.view().peek(keylet::account(issuer));
|
||||
if (sleIssuer && !sleIssuer->isFlag(lsfAllowTrustLineLocking))
|
||||
{
|
||||
bool const isAMM = sleIssuer->isFieldPresent(sfAMMID);
|
||||
bool const isBlackholedIssuer = isBlackholed(ctx_.view(), sleIssuer);
|
||||
if (isAMM || isBlackholedIssuer)
|
||||
{
|
||||
sleIssuer->setFieldU32(
|
||||
sfFlags, sleIssuer->getFlags() | lsfAllowTrustLineLocking);
|
||||
ctx_.view().update(sleIssuer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx_.view().insert(slep);
|
||||
|
||||
@@ -4453,19 +4453,25 @@ private:
|
||||
}
|
||||
|
||||
void
|
||||
testFlags()
|
||||
testFlags(FeatureBitset features)
|
||||
{
|
||||
testcase("Flags");
|
||||
using namespace jtx;
|
||||
|
||||
testAMM([&](AMM& ammAlice, Env& env) {
|
||||
auto const info = env.rpc(
|
||||
"json",
|
||||
"account_info",
|
||||
std::string(R"({"account": ")" + to_string(ammAlice.ammAccount()) + "\"}"));
|
||||
auto const flags = info[jss::result][jss::account_data][jss::Flags].asUInt();
|
||||
BEAST_EXPECT(flags == (lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth));
|
||||
});
|
||||
Env env{*this, features};
|
||||
fund(env, gw_, {alice_}, {USD(20'000)}, Fund::All);
|
||||
AMM const ammAlice(env, alice_, XRP(10'000), USD(10'000));
|
||||
auto const ammAccount = ammAlice.ammAccount();
|
||||
|
||||
auto const sleAMM = env.le(keylet::account(ammAccount));
|
||||
if (BEAST_EXPECT(sleAMM))
|
||||
{
|
||||
BEAST_EXPECT(sleAMM->isFlag(lsfDisableMaster));
|
||||
BEAST_EXPECT(sleAMM->isFlag(lsfDefaultRipple));
|
||||
BEAST_EXPECT(sleAMM->isFlag(lsfDepositAuth));
|
||||
bool const flag = sleAMM->isFlag(lsfAllowTrustLineLocking);
|
||||
BEAST_EXPECT(features[fixTokenEscrowV1_1] ? flag : !flag);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -7420,7 +7426,8 @@ private:
|
||||
testBasicPaymentEngine(all - fixAMMv1_1 - fixAMMv1_3 - fixReducedOffersV2);
|
||||
testAMMTokens();
|
||||
testAmendment();
|
||||
testFlags();
|
||||
testFlags(all);
|
||||
testFlags(all - fixTokenEscrowV1_1);
|
||||
testRippling();
|
||||
testAMMAndCLOB(all);
|
||||
testAMMAndCLOB(all - fixAMMv1_1 - fixAMMv1_3);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
|
||||
#include <test/jtx/AMM.h>
|
||||
#include <test/jtx/Account.h>
|
||||
#include <test/jtx/Env.h>
|
||||
#include <test/jtx/amount.h>
|
||||
@@ -7,8 +8,11 @@
|
||||
#include <test/jtx/fee.h>
|
||||
#include <test/jtx/flags.h>
|
||||
#include <test/jtx/mpt.h>
|
||||
#include <test/jtx/multisign.h>
|
||||
#include <test/jtx/pay.h>
|
||||
#include <test/jtx/rate.h>
|
||||
#include <test/jtx/regkey.h>
|
||||
#include <test/jtx/sig.h>
|
||||
#include <test/jtx/ter.h>
|
||||
#include <test/jtx/trust.h>
|
||||
#include <test/jtx/txflags.h>
|
||||
@@ -26,6 +30,7 @@
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -373,6 +378,163 @@ struct EscrowToken_test : public beast::unit_test::Suite
|
||||
env.close();
|
||||
}
|
||||
|
||||
// AMM issuer without lsfAllowTrustLineLocking
|
||||
// (succeeds under fixTokenEscrowV1_1, fails otherwise)
|
||||
{
|
||||
bool const withFix = features[fixTokenEscrowV1_1];
|
||||
Env env{*this, features};
|
||||
auto const baseFee = env.current()->fees().base;
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
auto const carol = Account("carol");
|
||||
auto const gw = Account{"gateway"};
|
||||
|
||||
env.fund(XRP(30'000), alice, bob, carol, gw);
|
||||
env.close();
|
||||
|
||||
auto const usd = gw["USD"];
|
||||
env.trust(usd(30'000), alice);
|
||||
env.close();
|
||||
env(pay(gw, alice, usd(10'000)));
|
||||
env.close();
|
||||
|
||||
AMM const ammAlice(env, alice, XRP(10'000), usd(10'000));
|
||||
auto const ammAccount = ammAlice.ammAccount();
|
||||
auto const lpIssue = ammAlice.lptIssue();
|
||||
|
||||
env.trust(STAmount{lpIssue, 10'000}, carol);
|
||||
env.close();
|
||||
env(pay(alice, carol, STAmount{lpIssue, 100}));
|
||||
env.close();
|
||||
|
||||
env(escrow::create(carol, bob, STAmount{lpIssue, 50}),
|
||||
escrow::kFinishTime(env.now() + 1s),
|
||||
Fee(baseFee * 150),
|
||||
Ter(withFix ? TER(tesSUCCESS) : TER(tecNO_PERMISSION)));
|
||||
env.close();
|
||||
|
||||
if (withFix)
|
||||
{
|
||||
auto const sleAMM = env.le(keylet::account(ammAccount));
|
||||
BEAST_EXPECT(sleAMM && sleAMM->isFlag(lsfAllowTrustLineLocking));
|
||||
}
|
||||
}
|
||||
|
||||
// Blackholed issuer without lsfAllowTrustLineLocking
|
||||
// (succeeds under fixTokenEscrowV1_1, fails otherwise)
|
||||
{
|
||||
bool const withFix = features[fixTokenEscrowV1_1];
|
||||
Env env{*this, features};
|
||||
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.close();
|
||||
env.trust(usd(10'000), alice, bob);
|
||||
env.close();
|
||||
env(pay(gw, alice, usd(5000)));
|
||||
env(pay(gw, bob, usd(5000)));
|
||||
env.close();
|
||||
|
||||
Account const blackhole("blackhole", AccountID(1));
|
||||
env(regkey(gw, blackhole));
|
||||
env.close();
|
||||
env(fset(gw, asfDisableMaster), Sig(gw));
|
||||
env.close();
|
||||
|
||||
env(escrow::create(alice, bob, usd(100)),
|
||||
escrow::kFinishTime(env.now() + 1s),
|
||||
Fee(baseFee * 150),
|
||||
Ter(withFix ? TER(tesSUCCESS) : TER(tecNO_PERMISSION)));
|
||||
env.close();
|
||||
|
||||
if (withFix)
|
||||
{
|
||||
auto const sleGW = env.le(keylet::account(gw));
|
||||
BEAST_EXPECT(sleGW && sleGW->isFlag(lsfAllowTrustLineLocking));
|
||||
}
|
||||
}
|
||||
|
||||
// tecNO_PERMISSION: issuer with the master key disabled and a regular
|
||||
// key it can sign with
|
||||
{
|
||||
Env env{*this, features};
|
||||
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.close();
|
||||
env.trust(usd(10'000), alice, bob);
|
||||
env.close();
|
||||
env(pay(gw, alice, usd(5000)));
|
||||
env(pay(gw, bob, usd(5000)));
|
||||
env.close();
|
||||
|
||||
Account const regular("regular");
|
||||
env(regkey(gw, regular));
|
||||
env.close();
|
||||
env(fset(gw, asfDisableMaster), Sig(gw));
|
||||
env.close();
|
||||
|
||||
env(escrow::create(alice, bob, usd(100)),
|
||||
escrow::kFinishTime(env.now() + 1s),
|
||||
Fee(baseFee * 150),
|
||||
Ter(tecNO_PERMISSION));
|
||||
env.close();
|
||||
|
||||
auto const sleGW = env.le(keylet::account(gw));
|
||||
BEAST_EXPECT(sleGW && sleGW->isFlag(lsfDisableMaster));
|
||||
BEAST_EXPECT(
|
||||
sleGW && sleGW->isFieldPresent(sfRegularKey) &&
|
||||
sleGW->getAccountID(sfRegularKey) == regular.id());
|
||||
BEAST_EXPECT(sleGW && !sleGW->isFlag(lsfAllowTrustLineLocking));
|
||||
}
|
||||
|
||||
// tecNO_PERMISSION: issuer with a blackhole regular key and a signer
|
||||
// list
|
||||
{
|
||||
Env env{*this, features};
|
||||
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.close();
|
||||
env.trust(usd(10'000), alice, bob);
|
||||
env.close();
|
||||
env(pay(gw, alice, usd(5000)));
|
||||
env(pay(gw, bob, usd(5000)));
|
||||
env.close();
|
||||
|
||||
env(signers(gw, 1, {{bob, 1}}));
|
||||
env.close();
|
||||
|
||||
Account const blackhole("blackhole", AccountID(1));
|
||||
env(regkey(gw, blackhole));
|
||||
env.close();
|
||||
env(fset(gw, asfDisableMaster), Sig(gw));
|
||||
env.close();
|
||||
|
||||
env(escrow::create(alice, bob, usd(100)),
|
||||
escrow::kFinishTime(env.now() + 1s),
|
||||
Fee(baseFee * 150),
|
||||
Ter(tecNO_PERMISSION));
|
||||
env.close();
|
||||
|
||||
auto const sleGW = env.le(keylet::account(gw));
|
||||
BEAST_EXPECT(sleGW && sleGW->isFlag(lsfDisableMaster));
|
||||
BEAST_EXPECT(env.le(keylet::signerList(gw)));
|
||||
BEAST_EXPECT(sleGW && !sleGW->isFlag(lsfAllowTrustLineLocking));
|
||||
}
|
||||
|
||||
// tecNO_LINE: account does not have a trustline to the issuer
|
||||
{
|
||||
Env env{*this, features};
|
||||
@@ -4337,9 +4499,11 @@ public:
|
||||
{all - featureSingleAssetVault - featureLendingProtocol - featureMPTokensV2, all})
|
||||
{
|
||||
testIOUWithFeats(feats);
|
||||
testIOUWithFeats(feats - fixCleanup3_2_0);
|
||||
testIOUWithFeats(feats - fixTokenEscrowV1_1);
|
||||
testMPTWithFeats(feats);
|
||||
testMPTWithFeats(feats - fixTokenEscrowV1);
|
||||
testMPTWithFeats(feats - fixTokenEscrowV1 - fixTokenEscrowV1_1);
|
||||
testIOUWithFeats(feats - fixCleanup3_2_0);
|
||||
}
|
||||
testMPTSplitEscrowTransferFee(all - fixCleanup3_4_0);
|
||||
testMPTSplitEscrowTransferFee(all);
|
||||
|
||||
Reference in New Issue
Block a user