mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-04 01:06:48 +00:00
Create Sponsored Account / AccountDelete with ltSponsorship, Sponsored Account
This commit is contained in:
@@ -113,8 +113,9 @@ constexpr std::uint32_t tfOfferCreateMask =
|
||||
constexpr std::uint32_t tfNoRippleDirect = 0x00010000;
|
||||
constexpr std::uint32_t tfPartialPayment = 0x00020000;
|
||||
constexpr std::uint32_t tfLimitQuality = 0x00040000;
|
||||
constexpr std::uint32_t tfSponsorCreatedAccount = 0x00080000;
|
||||
constexpr std::uint32_t tfPaymentMask =
|
||||
~(tfUniversal | tfPartialPayment | tfLimitQuality | tfNoRippleDirect);
|
||||
~(tfUniversal | tfPartialPayment | tfLimitQuality | tfNoRippleDirect | tfSponsorCreatedAccount);
|
||||
constexpr std::uint32_t tfMPTPaymentMask = ~(tfUniversal | tfPartialPayment);
|
||||
|
||||
// TrustSet flags:
|
||||
|
||||
@@ -527,16 +527,48 @@ public:
|
||||
|
||||
Account const alice("alice");
|
||||
Account const sponsor("sponsor");
|
||||
Account const bob("bob");
|
||||
Account const charlie("charlie");
|
||||
env.fund(XRP(10000), alice, sponsor);
|
||||
|
||||
Account const bob("bob");
|
||||
env(pay(alice, bob, STAmount(env.current()->fees().accountReserve(0))),
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor));
|
||||
env.close();
|
||||
// Account is not sponsored by normal Sponsor specification
|
||||
{
|
||||
env(pay(alice,
|
||||
bob,
|
||||
STAmount(env.current()->fees().accountReserve(0))),
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringAccountCount(env, sponsor) == 1);
|
||||
auto const bobSle = env.le(keylet::account(bob));
|
||||
BEAST_EXPECT(!bobSle->isFieldPresent(sfSponsorAccount));
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringAccountCount(env, sponsor) == 0);
|
||||
}
|
||||
|
||||
// Use tfSponsorCreatedAccount to sponsor an account
|
||||
{
|
||||
// to funded accoutn
|
||||
env(pay(sponsor,
|
||||
bob,
|
||||
STAmount(env.current()->fees().accountReserve(0))),
|
||||
txflags(tfSponsorCreatedAccount),
|
||||
ter(tecNO_SPONSOR_PERMISSION));
|
||||
|
||||
// to non-funded account
|
||||
env(pay(sponsor,
|
||||
charlie,
|
||||
STAmount(env.current()->fees().accountReserve(0))),
|
||||
txflags(tfSponsorCreatedAccount));
|
||||
env.close();
|
||||
|
||||
auto const charlieSle = env.le(keylet::account(charlie));
|
||||
BEAST_EXPECT(charlieSle->isFieldPresent(sfSponsorAccount));
|
||||
BEAST_EXPECT(
|
||||
charlieSle->getAccountID(sfSponsorAccount) == sponsor.id());
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, charlie) == 0);
|
||||
BEAST_EXPECT(sponsoringAccountCount(env, sponsor) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -926,25 +958,66 @@ public:
|
||||
{
|
||||
testcase("AccountDelete");
|
||||
using namespace test::jtx;
|
||||
Env env{*this, testable_amendments()};
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const sponsor("sponsor");
|
||||
|
||||
env.fund(XRP(1000000), alice, bob, sponsor);
|
||||
env.close();
|
||||
{
|
||||
// Delete Account with ltSponsorship
|
||||
Env env{*this, testable_amendments()};
|
||||
env.fund(XRP(1000000), alice, bob, sponsor);
|
||||
env.close();
|
||||
|
||||
// set sponsor
|
||||
env(sponsor::set(sponsor, alice, 0, 100, XRP(100)), ter(tesSUCCESS));
|
||||
env.close();
|
||||
// set sponsor
|
||||
env(sponsor::set(sponsor, alice, 0, 100, XRP(100)),
|
||||
ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
// AccountDelete
|
||||
env(acctdelete(alice, bob));
|
||||
env.close();
|
||||
incLgrSeqForAccDel(env, alice);
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
auto const keylet = keylet::sponsor(sponsor, alice);
|
||||
auto const sponsorObj = env.le(keylet);
|
||||
BEAST_EXPECT(sponsorObj);
|
||||
|
||||
// AccountDelete
|
||||
auto const requiredFee = drops(env.current()->fees().increment);
|
||||
env(acctdelete(alice, bob), fee(requiredFee), ter(tesSUCCESS));
|
||||
env.close();
|
||||
}
|
||||
|
||||
{
|
||||
// Delete SponsoredAccount
|
||||
Env env{*this, testable_amendments()};
|
||||
env.memoize(alice);
|
||||
env.fund(XRP(1000000), bob, sponsor);
|
||||
env.close();
|
||||
|
||||
// create SponsoredAccount
|
||||
env(pay(sponsor, alice, XRP(10000)),
|
||||
txflags(tfSponsorCreatedAccount));
|
||||
env.close();
|
||||
|
||||
incLgrSeqForAccDel(env, alice);
|
||||
|
||||
// AccountDelete: destination = non-sponsor
|
||||
auto const requiredFee = drops(env.current()->fees().increment);
|
||||
env(acctdelete(alice, bob),
|
||||
fee(requiredFee),
|
||||
ter(tecNO_SPONSOR_PERMISSION));
|
||||
|
||||
auto const sponsorSle = env.le(keylet::account(sponsor));
|
||||
BEAST_EXPECT(
|
||||
sponsorSle->getFieldU32(sfSponsoringAccountCount) == 1);
|
||||
|
||||
incLgrSeqForAccDel(env, alice);
|
||||
|
||||
// AccountDelete: destination = sponsor
|
||||
env(acctdelete(alice, sponsor), fee(requiredFee), ter(tesSUCCESS));
|
||||
|
||||
auto const sponsorSle2 = env.le(keylet::account(sponsor));
|
||||
BEAST_EXPECT(
|
||||
!sponsorSle2->isFieldPresent(sfSponsoringAccountCount));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -986,7 +1059,7 @@ public:
|
||||
testSponsorReserve();
|
||||
testDisallowIncoming();
|
||||
|
||||
// testAccountDelete();
|
||||
testAccountDelete();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -299,6 +299,15 @@ DeleteAccount::preclaim(PreclaimContext const& ctx)
|
||||
return tecHAS_OBLIGATIONS;
|
||||
}
|
||||
|
||||
if (sleAccount->isFieldPresent(sfSponsorAccount))
|
||||
{
|
||||
if (dst != sleAccount->getAccountID(sfSponsorAccount))
|
||||
return tecNO_SPONSOR_PERMISSION;
|
||||
}
|
||||
if (sleAccount->isFieldPresent(sfSponsoringOwnerCount) ||
|
||||
sleAccount->isFieldPresent(sfSponsoringAccountCount))
|
||||
return tecHAS_OBLIGATIONS;
|
||||
|
||||
// We don't allow an account to be deleted if its sequence number
|
||||
// is within 256 of the current ledger. This prevents replay of old
|
||||
// transactions if this account is resurrected after it is deleted.
|
||||
@@ -430,6 +439,22 @@ DeleteAccount::doApply()
|
||||
(*src)[sfBalance] = (*src)[sfBalance] - mSourceBalance;
|
||||
ctx_.deliver(mSourceBalance);
|
||||
|
||||
if (src->isFieldPresent(sfSponsorAccount))
|
||||
{
|
||||
auto const sponsorAcc = src->getAccountID(sfSponsorAccount);
|
||||
auto sponsorSle = view().peek(keylet::account(sponsorAcc));
|
||||
|
||||
auto const sponsoringAccountCount =
|
||||
sponsorSle->getFieldU32(sfSponsoringAccountCount);
|
||||
|
||||
if (sponsoringAccountCount == 1)
|
||||
sponsorSle->makeFieldAbsent(sfSponsoringAccountCount);
|
||||
else
|
||||
sponsorSle->setFieldU32(
|
||||
sfSponsoringAccountCount, sponsoringAccountCount - 1);
|
||||
view().update(sponsorSle);
|
||||
}
|
||||
|
||||
XRPL_ASSERT(
|
||||
(*src)[sfBalance] == XRPAmount(0),
|
||||
"ripple::DeleteAccount::doApply : source balance is zero");
|
||||
|
||||
@@ -98,6 +98,15 @@ Payment::preflight(PreflightContext const& ctx)
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
|
||||
if (txFlags & tfSponsorCreatedAccount)
|
||||
{
|
||||
if (!ctx.rules.enabled(featureSponsor))
|
||||
return temINVALID_FLAG;
|
||||
|
||||
if (!dstAmount.native())
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
if (mptDirect && ctx.tx.isFieldPresent(sfPaths))
|
||||
return temMALFORMED;
|
||||
|
||||
@@ -330,19 +339,24 @@ Payment::preclaim(PreclaimContext const& ctx)
|
||||
return tecNO_DST_INSUF_XRP;
|
||||
}
|
||||
}
|
||||
else if (
|
||||
(sleDst->getFlags() & lsfRequireDestTag) &&
|
||||
!ctx.tx.isFieldPresent(sfDestinationTag))
|
||||
else
|
||||
{
|
||||
// The tag is basically account-specific information we don't
|
||||
// understand, but we can require someone to fill it in.
|
||||
if (txFlags & tfSponsorCreatedAccount)
|
||||
return tecNO_SPONSOR_PERMISSION;
|
||||
|
||||
// We didn't make this test for a newly-formed account because there's
|
||||
// no way for this field to be set.
|
||||
JLOG(ctx.j.trace())
|
||||
<< "Malformed transaction: DestinationTag required.";
|
||||
if ((sleDst->getFlags() & lsfRequireDestTag) &&
|
||||
!ctx.tx.isFieldPresent(sfDestinationTag))
|
||||
{
|
||||
// The tag is basically account-specific information we don't
|
||||
// understand, but we can require someone to fill it in.
|
||||
|
||||
return tecDST_TAG_NEEDED;
|
||||
// We didn't make this test for a newly-formed account because
|
||||
// there's no way for this field to be set.
|
||||
JLOG(ctx.j.trace())
|
||||
<< "Malformed transaction: DestinationTag required.";
|
||||
|
||||
return tecDST_TAG_NEEDED;
|
||||
}
|
||||
}
|
||||
|
||||
// Payment with at least one intermediate step and uses transitive balances.
|
||||
@@ -415,12 +429,19 @@ Payment::doApply()
|
||||
sleDst->setAccountID(sfAccount, dstAccountID);
|
||||
sleDst->setFieldU32(sfSequence, seqno);
|
||||
|
||||
auto const sponsor = getTxReserveSponsor(view(), ctx_.tx);
|
||||
if (sponsor.has_value())
|
||||
if (txFlags & tfSponsorCreatedAccount)
|
||||
{
|
||||
auto const sponsor = view().peek(keylet::account(account_));
|
||||
if (!sponsor)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
auto const currentSponsoringAccountCount =
|
||||
sponsor->getFieldU32(sfSponsoringAccountCount);
|
||||
sponsor->setFieldU32(
|
||||
sfSponsoringAccountCount, currentSponsoringAccountCount + 1);
|
||||
view().update(sponsor);
|
||||
|
||||
addSponsorToLedgerEntry(sleDst, sponsor);
|
||||
sponsor.value()->setFieldU32(sfSponsoringAccountCount, 1);
|
||||
view().update(sponsor.value());
|
||||
view().update(sponsor);
|
||||
}
|
||||
|
||||
view().insert(sleDst);
|
||||
|
||||
@@ -276,7 +276,7 @@ SponsorshipSet::deleteSponsorship(
|
||||
std::shared_ptr<SLE> const& sle,
|
||||
beast::Journal j)
|
||||
{
|
||||
auto const sponsor = sle->getAccountID(sfSponsorAccount);
|
||||
auto const sponsor = sle->getAccountID(sfAccount);
|
||||
auto const sponsee = sle->getAccountID(sfSponsee);
|
||||
|
||||
// adjust balance
|
||||
@@ -285,9 +285,13 @@ SponsorshipSet::deleteSponsorship(
|
||||
return tecINTERNAL;
|
||||
|
||||
auto const feeAmount = sle->getFieldAmount(sfFeeAmount);
|
||||
|
||||
(*sponsorAccSle)[sfBalance] += feeAmount;
|
||||
|
||||
auto const reserveSponsor = getLedgerEntryReserveSponsor(view, sle);
|
||||
adjustOwnerCount(view, sponsorAccSle, reserveSponsor, -1, j);
|
||||
|
||||
view.update(sponsorAccSle);
|
||||
|
||||
// delete sponsor node
|
||||
view.dirRemove(
|
||||
keylet::ownerDir(sponsor), (*sle)[sfSponsorNode], sle->key(), false);
|
||||
|
||||
Reference in New Issue
Block a user