SponsorReserve,SponsorAccount

- not fully tested
- need to modify: Reserve check where using sfOwnerCount
This commit is contained in:
tequ
2025-07-30 23:39:14 +09:00
parent 5b9c767b07
commit d40f8cc232
44 changed files with 1005 additions and 93 deletions

View File

@@ -169,6 +169,7 @@ LEDGER_ENTRY(ltACCOUNT_ROOT, 0x0061, AccountRoot, account, ({
{sfVaultID, soeOPTIONAL}, // pseudo-account designator
{sfSponsoredOwnerCount, soeOPTIONAL},
{sfSponsoringOwnerCount, soeOPTIONAL},
{sfSponsoringAccountCount,soeOPTIONAL},
}))
/** A ledger object which contains a list of object identifiers.

View File

@@ -116,6 +116,7 @@ TYPED_SFIELD(sfOracleDocumentID, UINT32, 51)
TYPED_SFIELD(sfPermissionValue, UINT32, 52)
TYPED_SFIELD(sfSponsoredOwnerCount, UINT32, 53)
TYPED_SFIELD(sfSponsoringOwnerCount, UINT32, 54)
TYPED_SFIELD(sfSponsoringAccountCount, UINT32, 55)
// 64-bit integers (common)
TYPED_SFIELD(sfIndexNext, UINT64, 1)

View File

@@ -178,7 +178,8 @@ InnerObjectFormats::InnerObjectFormats()
{
{sfAccount, soeREQUIRED},
{sfFlags, soeREQUIRED},
{sfSignature, soeOPTIONAL},
{sfSigningPubKey, soeOPTIONAL},
{sfTxnSignature, soeOPTIONAL},
{sfSigners, soeOPTIONAL},
});
}

View File

@@ -0,0 +1,423 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2025 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <test/jtx.h>
#include <test/jtx/TestHelpers.h>
#include <test/jtx/check.h>
#include <test/jtx/did.h>
#include <test/jtx/sponsor.h>
#include <xrpl/protocol/Feature.h>
namespace ripple {
class Sponsor_test : public beast::unit_test::suite
{
public:
void
testSponsorFee()
{
using namespace test::jtx;
testcase("Sponsor Fee");
Env env{*this, testable_amendments()};
Account const alice("alice");
Account const sponsor("sponsor");
env.fund(XRP(10000), alice, sponsor);
env(noop(alice),
fee(XRP(1)),
sponsor::as(sponsor, tfSponsorFee),
sponsor::sig(sponsor));
env.close();
BEAST_EXPECT(env.balance(alice) == XRP(10000));
BEAST_EXPECT(env.balance(sponsor) == XRP(9999));
}
void
testSponsorAccount()
{
testcase("Sponsor Account");
using namespace test::jtx;
Env env{*this, testable_amendments()};
Account const alice("alice");
Account const sponsor("sponsor");
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();
env.require(sponsored_owners(alice, 0));
env.require(sponsoring_account_count(sponsor, 1));
}
void
testCheck()
{
testcase("Check");
using namespace test::jtx;
Env env{*this, testable_amendments()};
Account const alice("alice");
Account const bob("bob");
Account const sponsor("sponsor");
env.fund(XRP(10000), alice, bob, sponsor);
// CheckCreate
auto const seq = env.seq(alice);
env(check::create(alice, bob, XRP(1)),
sponsor::as(sponsor, tfSponsorReserve),
sponsor::sig(sponsor));
env.close();
env.require(owners(alice, 0));
env.require(sponsored_owners(alice, 1));
env.require(sponsoring_count(sponsor, 1));
// CheckCancel
auto const checkId = keylet::check(alice, seq).key;
env(check::cancel(alice, checkId));
env.close();
env.require(owners(alice, 0));
env.require(sponsored_owners(alice, 0));
env.require(sponsoring_count(sponsor, 0));
auto const seq2 = env.seq(alice);
env(check::create(alice, bob, XRP(1)),
sponsor::as(sponsor, tfSponsorReserve),
sponsor::sig(sponsor));
env.close();
// CheckCash
auto const checkId2 = keylet::check(alice, seq2).key;
env(check::cash(bob, checkId2, XRP(1)));
env.close();
env.require(owners(alice, 0));
env.require(sponsored_owners(alice, 0));
env.require(sponsoring_count(sponsor, 0));
// printf(
// "meta: %s\n",
// env.meta()->getJson(JsonOptions::none).toStyledString().c_str());
}
void
testOfffer()
{
testcase("Offer");
using namespace test::jtx;
Env env{*this, testable_amendments()};
Account const alice("alice");
Account const gw("gw");
Account const sponsor("sponsor");
auto USD = gw["USD"];
env.fund(XRP(10000), alice, gw, sponsor);
// OfferCreate
auto const seq = env.seq(alice);
env(offer(alice, USD(1), XRP(1)),
sponsor::as(sponsor, tfSponsorReserve),
sponsor::sig(sponsor));
env.close();
env.require(owners(alice, 0));
env.require(sponsored_owners(alice, 1));
env.require(sponsoring_count(sponsor, 1));
// OfferCancel
env(offer_cancel(alice, seq));
env.close();
env.require(owners(alice, 0));
env.require(sponsored_owners(alice, 0));
env.require(sponsoring_count(sponsor, 0));
// TODO: test Execution
}
void
testTicket()
{
testcase("Ticket");
using namespace test::jtx;
Env env{*this, testable_amendments()};
Account const alice("alice");
Account const sponsor("master");
env.fund(XRP(1000000), alice, sponsor);
// TicketCreate
std::uint32_t const ticketSeq{env.seq(alice) + 1};
env(ticket::create(alice, 250),
sponsor::as(sponsor, tfSponsorReserve),
sponsor::sig(sponsor));
env.close();
env.require(owners(alice, 0));
env.require(sponsored_owners(alice, 250));
env.require(sponsoring_count(sponsor, 250));
// use a Ticket
env(noop(alice), ticket::use(ticketSeq + 1));
env.close();
env.require(owners(alice, 0));
env.require(sponsored_owners(alice, 249));
env.require(sponsoring_count(sponsor, 249));
}
void
testCredentials()
{
testcase("Credentials");
using namespace test::jtx;
Env env{*this, testable_amendments()};
Account const issuer("issuer");
Account const subject("subject");
Account const sponsor("sponsor");
env.fund(XRP(1000000), issuer, subject, sponsor);
// CredentialsCreate
env(credentials::create(subject, issuer, "credType"),
credentials::uri("uri"),
sponsor::as(sponsor, tfSponsorReserve),
sponsor::sig(sponsor));
env.close();
env.require(owners(issuer, 0));
env.require(sponsored_owners(issuer, 1));
env.require(sponsoring_count(sponsor, 1));
// CredentialsAccept
env(credentials::accept(subject, issuer, "credType"),
sponsor::as(sponsor, tfSponsorReserve),
sponsor::sig(sponsor));
env.close();
env.require(owners(issuer, 0));
env.require(owners(subject, 0));
env.require(sponsored_owners(issuer, 0));
env.require(sponsored_owners(subject, 1));
env.require(sponsoring_count(sponsor, 1));
// CredentialsDelete
env(credentials::deleteCred(subject, subject, issuer, "credType"));
env.close();
env.require(owners(issuer, 0));
env.require(owners(subject, 0));
env.require(sponsored_owners(issuer, 0));
env.require(sponsored_owners(subject, 0));
env.require(sponsoring_count(sponsor, 0));
}
void
testDelegate()
{
testcase("Delegate");
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);
// DelegateSet
env(delegate::set(alice, bob, {"Payment"}),
sponsor::as(sponsor, tfSponsorReserve),
sponsor::sig(sponsor));
env.close();
env.require(owners(alice, 0));
env.require(sponsored_owners(alice, 1));
env.require(sponsoring_count(sponsor, 1));
// delete
env(delegate::set(alice, bob, {}));
env.close();
env.require(owners(alice, 0));
env.require(sponsored_owners(alice, 0));
env.require(sponsoring_count(sponsor, 0));
}
void
testDepositPreauth()
{
}
void
testDID()
{
testcase("DID");
using namespace test::jtx;
Env env{*this, testable_amendments()};
Account const alice("alice");
Account const sponsor("sponsor");
env.fund(XRP(1000000), alice, sponsor);
// DIDSet
env(did::set(alice),
did::uri("uri"),
sponsor::as(sponsor, tfSponsorReserve),
sponsor::sig(sponsor));
env.close();
env.require(owners(alice, 0));
env.require(sponsored_owners(alice, 1));
env.require(sponsoring_count(sponsor, 1));
// DIDDelete
env(did::del(alice));
env.close();
env.require(owners(alice, 0));
env.require(sponsored_owners(alice, 0));
env.require(sponsoring_count(sponsor, 0));
}
void
testEscrow()
{
}
void
testMPToken()
{
}
void
testNFToken()
{
}
void
testNFTokenOffer()
{
}
void
testPayChan()
{
}
void
testPermissionedDomain()
{
}
void
testOracle()
{
}
void
testSignerList()
{
testcase("SignerList");
using namespace test::jtx;
Env env{*this, testable_amendments()};
Account const alice("alice");
Account const sponsor("sponsor");
env.fund(XRP(1000000), alice, sponsor);
Account const bob("bob");
// SignerListSet
env(signers(alice, 1, {{bob, 1}}),
sponsor::as(sponsor, tfSponsorReserve),
sponsor::sig(sponsor));
env.close();
env.require(owners(alice, 0));
env.require(sponsored_owners(alice, 1));
env.require(sponsoring_count(sponsor, 1));
// Delete
env(signers(alice, none));
env.close();
env.require(owners(alice, 0));
env.require(sponsored_owners(alice, 0));
env.require(sponsoring_count(sponsor, 0));
}
void
testTrust()
{
}
void
testVault()
{
}
void
testXChain()
{
}
void
testSponsorReserve()
{
testCheck();
testOfffer();
testTicket();
testCredentials();
testDelegate();
// testDepositPreauth();
testDID();
// testEscrow();
// testMPToken();
// testNFToken();
// testNFTokenOffer();
// testPayChan();
// testPermissionedDomain();
// testOracle();
testSignerList();
// testTrust();
// testVault();
// testXChain();
}
void
run() override
{
testSponsorFee();
testSponsorAccount();
testSponsorReserve();
}
};
BEAST_DEFINE_TESTSUITE(Sponsor, app, ripple);
} // namespace ripple

View File

@@ -55,6 +55,27 @@ owners::operator()(Env& env) const
env.test.expect(env.le(account_)->getFieldU32(sfOwnerCount) == value_);
}
void
sponsored_owners::operator()(Env& env) const
{
env.test.expect(
env.le(account_)->getFieldU32(sfSponsoredOwnerCount) == value_);
}
void
sponsoring_count::operator()(Env& env) const
{
env.test.expect(
env.le(account_)->getFieldU32(sfSponsoringOwnerCount) == value_);
}
void
sponsoring_account_count::operator()(Env& env) const
{
env.test.expect(
env.le(account_)->getFieldU32(sfSponsoringAccountCount) == value_);
}
} // namespace jtx
} // namespace test
} // namespace ripple

View File

@@ -0,0 +1,123 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2025 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <test/jtx/sponsor.h>
#include <test/jtx/utility.h>
#include <xrpl/protocol/Sign.h>
#include <xrpl/protocol/jss.h>
namespace ripple {
namespace test {
namespace jtx {
namespace sponsor {
Json::Value
transferAccount(jtx::Account const& account)
{
Json::Value jv;
jv[jss::TransactionType] = "SponsorTransfer";
jv[sfSponsor.jsonName] = account.human();
return jv;
}
Json::Value
transferObject(uint256 const& id)
{
Json::Value jv;
jv[jss::TransactionType] = "SponsorTransfer";
jv[sfLedgerIndex.jsonName] = to_string(id);
return jv;
}
void
as::operator()(Env& env, JTx& jt) const
{
jt.jv[sfSponsor.jsonName][sfAccount.jsonName] = sponsor_.human();
jt.jv[sfSponsor.jsonName][sfFlags.jsonName] = flags;
}
void
sig::operator()(Env& env, JTx& jt) const
{
std::optional<STObject> st;
try
{
// required to cast the STObject to STTx
jt.jv[jss::SigningPubKey] = "";
st = parse(jt.jv);
}
catch (parse_error const&)
{
env.test.log << pretty(jt.jv) << std::endl;
Rethrow();
}
jt.jv[sfSponsor.jsonName][sfAccount.jsonName] = signer.acct.human();
jt.jv[sfSponsor.jsonName][sfSigningPubKey.jsonName] =
strHex(signer.sig.pk().slice());
Serializer ss;
ss.add32(HashPrefix::txSign);
parse(jt.jv).addWithoutSigningFields(ss);
auto const sig =
ripple::sign(signer.acct.pk(), signer.acct.sk(), ss.slice());
jt.jv[sfSponsor.jsonName][jss::TxnSignature] =
strHex(Slice{sig.data(), sig.size()});
}
void
msig::operator()(Env& env, JTx& jt) const
{
auto const mySigners = signers;
jt.signer = [mySigners, &env](Env&, JTx& jtx) {
jtx[sfSponsor.getJsonName()][sfSigningPubKey.getJsonName()] = "";
std::optional<STObject> st;
try
{
st = parse(jtx.jv);
}
catch (parse_error const&)
{
env.test.log << pretty(jtx.jv) << std::endl;
Rethrow();
}
auto& js = jtx[sfSponsor.getJsonName()][sfSigners.getJsonName()];
for (std::size_t i = 0; i < mySigners.size(); ++i)
{
auto const& e = mySigners[i];
auto& jo = js[i][sfSigner.getJsonName()];
jo[jss::Account] = e.acct.human();
jo[jss::SigningPubKey] = strHex(e.sig.pk().slice());
Serializer ss{buildMultiSigningData(*st, e.acct.id())};
auto const sig = ripple::sign(
*publicKeyType(e.sig.pk().slice()), e.sig.sk(), ss.slice());
jo[sfTxnSignature.getJsonName()] =
strHex(Slice{sig.data(), sig.size()});
}
};
}
} // namespace sponsor
} // namespace jtx
} // namespace test
} // namespace ripple

View File

@@ -85,6 +85,57 @@ public:
operator()(Env& env) const;
};
/** Match the number of items in the account's owner directory */
class sponsored_owners
{
private:
Account account_;
std::uint32_t value_;
public:
sponsored_owners(Account const& account, std::uint32_t value)
: account_(account), value_(value)
{
}
void
operator()(Env& env) const;
};
/** Match the number of items in the account's owner directory */
class sponsoring_count
{
private:
Account account_;
std::uint32_t value_;
public:
sponsoring_count(Account const& account, std::uint32_t value)
: account_(account), value_(value)
{
}
void
operator()(Env& env) const;
};
/** Match the number of items in the account's owner directory */
class sponsoring_account_count
{
private:
Account account_;
std::uint32_t value_;
public:
sponsoring_account_count(Account const& account, std::uint32_t value)
: account_(account), value_(value)
{
}
void
operator()(Env& env) const;
};
/** Match the number of trust lines in the account's owner directory */
using lines = owner_count<ltRIPPLE_STATE>;

87
src/test/jtx/sponsor.h Normal file
View File

@@ -0,0 +1,87 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2025 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#pragma once
#include <test/jtx/Account.h>
#include <test/jtx/Env.h>
#include "test/jtx/SignerUtils.h"
namespace ripple {
namespace test {
namespace jtx {
namespace sponsor {
// Json::Value
// transferAccount(jtx::Account const& account);
// Json::Value
// transferObject(uint256 const& id);
struct as
{
private:
jtx::Account sponsor_;
std::uint32_t flags;
public:
as(jtx::Account const& account, std::uint32_t flags = 0)
: sponsor_(account), flags(flags)
{
}
void
operator()(jtx::Env&, jtx::JTx& jtx) const;
};
struct sig
{
private:
Reg signer;
public:
sig(Reg signer_) : signer(std::move(signer_))
{
}
void
operator()(jtx::Env&, jtx::JTx& jtx) const;
};
struct msig
{
private:
std::vector<Reg> signers;
public:
msig(std::vector<Reg> signers_) : signers(std::move(signers_))
{
sortSigners(signers);
}
void
operator()(jtx::Env&, jtx::JTx& jtx) const;
};
} // namespace sponsor
} // namespace jtx
} // namespace test
} // namespace ripple

View File

@@ -93,7 +93,11 @@ deleteSLE(
} // LCOV_EXCL_STOP
if (isOwner)
adjustOwnerCount(view, sleAccount, -1, j);
{
auto const sponsor =
getLedgerEntryReserveSponsor(view, sleCredential);
adjustOwnerCount(view, sleAccount, sponsor, -1, j);
}
return tesSUCCESS;
};

View File

@@ -123,7 +123,8 @@ CancelCheck::doApply()
// If we succeeded, update the check owner's reserve.
auto const sleSrc = view().peek(keylet::account(srcId));
adjustOwnerCount(view(), sleSrc, -1, viewJ);
auto const sponsor = getLedgerEntryReserveSponsor(view(), sleCheck);
adjustOwnerCount(view(), sleSrc, sponsor, -1, viewJ);
// Remove check from ledger.
view().erase(sleCheck);

View File

@@ -508,7 +508,8 @@ CashCheck::doApply()
}
// If we succeeded, update the check owner's reserve.
adjustOwnerCount(psb, psb.peek(keylet::account(srcId)), -1, viewJ);
auto const sponsor = getLedgerEntryReserveSponsor(psb, sleCheck);
adjustOwnerCount(psb, psb.peek(keylet::account(srcId)), sponsor, -1, viewJ);
// Remove check from ledger.
psb.erase(sleCheck);

View File

@@ -213,11 +213,19 @@ Change::activateTrustLinesToSelfFix()
if (tl->getFlags() & lsfLowReserve)
adjustOwnerCount(
sb, sb.peek(keylet::account(lo.getIssuer())), -1, j_);
sb,
sb.peek(keylet::account(lo.getIssuer())),
std::nullopt,
-1,
j_);
if (tl->getFlags() & lsfHighReserve)
adjustOwnerCount(
sb, sb.peek(keylet::account(hi.getIssuer())), -1, j_);
sb,
sb.peek(keylet::account(hi.getIssuer())),
std::nullopt,
-1,
j_);
sb.erase(tl);

View File

@@ -243,7 +243,9 @@ CreateCheck::doApply()
sleCheck->setFieldU64(sfOwnerNode, *page);
}
// If we succeeded, the new entry counts against the creator's reserve.
adjustOwnerCount(view(), sle, 1, viewJ);
auto const sponsor = getTxReserveSponsor(view(), ctx_.tx);
adjustOwnerCount(view(), sle, sponsor, 1, viewJ);
addSponsorToLedgerEntry(sleCheck, sponsor);
return tesSUCCESS;
}

View File

@@ -848,7 +848,8 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel)
}
// Update owner count.
adjustOwnerCount(sb, sleCreator, 1, viewJ);
auto const sponsor = getTxReserveSponsor(sb, ctx_.tx);
adjustOwnerCount(sb, sleCreator, sponsor, 1, viewJ);
JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.issue())
<< " : " << to_string(saTakerGets.issue())
@@ -908,6 +909,7 @@ CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel)
sleOffer->setFlag(lsfSell);
if (domainID)
sleOffer->setFieldH256(sfDomainID, *domainID);
addSponsorToLedgerEntry(sleOffer, sponsor);
// if it's a hybrid offer, set hybrid flag, and create an open dir
if (bHybrid)

View File

@@ -113,6 +113,7 @@ CreateTicket::doApply()
txSeq != 0 && txSeq != (firstTicketSeq - 1))
return tefINTERNAL;
auto const sponsor = getTxReserveSponsor(view(), ctx_.tx);
for (std::uint32_t i = 0; i < ticketCount; ++i)
{
std::uint32_t const curTicketSeq = firstTicketSeq + i;
@@ -122,12 +123,6 @@ CreateTicket::doApply()
sleTicket->setAccountID(sfAccount, account_);
sleTicket->setFieldU32(sfTicketSequence, curTicketSeq);
if (ctx_.tx.isFieldPresent(sfSponsor) &&
(ctx_.tx.getFieldObject(sfSponsor)[sfFlags] & tfSponsorReserve))
{
sleTicket->setAccountID(
sfSponsorAccount, ctx_.tx.getFieldObject(sfSponsor)[sfAccount]);
}
view().insert(sleTicket);
auto const page = view().dirInsert(
@@ -142,6 +137,7 @@ CreateTicket::doApply()
return tecDIR_FULL;
sleTicket->setFieldU64(sfOwnerNode, *page);
addSponsorToLedgerEntry(sleTicket, sponsor);
}
// Update the record of the number of Tickets this account owns.
@@ -151,22 +147,7 @@ CreateTicket::doApply()
sleAccountRoot->setFieldU32(sfTicketCount, oldTicketCount + ticketCount);
// Every added Ticket counts against the creator's reserve.
adjustOwnerCount(view(), sleAccountRoot, ticketCount, viewJ);
// Check reserve sponsorship
if (ctx_.tx.isFieldPresent(sfSponsor) &&
(ctx_.tx.getFieldObject(sfSponsor)[sfFlags] & tfSponsorReserve))
{
AccountID const& sponsor = ctx_.tx.getFieldObject(sfSponsor)[sfAccount];
auto const& sponsorSle = view().peek(keylet::account(sponsor));
sponsorSle->setFieldU32(
sfSponsoringOwnerCount,
sponsorSle->getFieldU32(sfSponsoringOwnerCount) + ticketCount);
sleAccountRoot->setFieldU32(
sfSponsoredOwnerCount,
sleAccountRoot->getFieldU32(sfSponsoredOwnerCount) + ticketCount);
view().update(sponsorSle);
}
adjustOwnerCount(view(), sleAccountRoot, sponsor, ticketCount, viewJ);
// TicketCreate is the only transaction that can cause an account root's
// Sequence field to increase by more than one. October 2018.

View File

@@ -174,7 +174,9 @@ CredentialCreate::doApply()
return tecDIR_FULL;
sleCred->setFieldU64(sfIssuerNode, *page);
adjustOwnerCount(view(), sleIssuer, 1, j_);
auto const sponsor = getTxReserveSponsor(view(), ctx_.tx);
adjustOwnerCount(view(), sleIssuer, sponsor, 1, j_);
addSponsorToLedgerEntry(sleCred, sponsor);
}
if (subject == account_)
@@ -392,8 +394,11 @@ CredentialAccept::doApply()
sleCred->setFieldU32(sfFlags, lsfAccepted);
view().update(sleCred);
adjustOwnerCount(view(), sleIssuer, -1, j_);
adjustOwnerCount(view(), sleSubject, 1, j_);
auto const currentSponsor = getTxReserveSponsor(view(), ctx_.tx);
adjustOwnerCount(view(), sleIssuer, currentSponsor, -1, j_);
auto const newSponsor = getTxReserveSponsor(view(), ctx_.tx);
adjustOwnerCount(view(), sleSubject, newSponsor, 1, j_);
addSponsorToLedgerEntry(sleCred, newSponsor);
return tesSUCCESS;
}

View File

@@ -108,7 +108,9 @@ addSLE(
return tecDIR_FULL;
(*sle)[sfOwnerNode] = *page;
}
adjustOwnerCount(ctx.view(), sleAccount, 1, ctx.journal);
auto const sponsor = getTxReserveSponsor(ctx.view(), ctx.tx);
adjustOwnerCount(ctx.view(), sleAccount, sponsor, 1, ctx.journal);
addSponsorToLedgerEntry(sle, sponsor);
ctx.view().update(sleAccount);
return tesSUCCESS;
@@ -215,7 +217,8 @@ DIDDelete::deleteSLE(
if (!sleOwner)
return tecINTERNAL;
adjustOwnerCount(view, sleOwner, -1, j);
auto const sponsor = getLedgerEntryReserveSponsor(view, sle);
adjustOwnerCount(view, sleOwner, sponsor, -1, j);
view.update(sleOwner);
// Remove object from ledger

View File

@@ -123,7 +123,9 @@ DelegateSet::doApply()
(*sle)[sfOwnerNode] = *page;
ctx_.view().insert(sle);
adjustOwnerCount(ctx_.view(), sleOwner, 1, ctx_.journal);
auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx);
adjustOwnerCount(ctx_.view(), sleOwner, sponsor, 1, ctx_.journal);
addSponsorToLedgerEntry(sle, sponsor);
}
return tesSUCCESS;
@@ -152,11 +154,12 @@ DelegateSet::deleteDelegate(
if (!sleOwner)
return tecINTERNAL; // LCOV_EXCL_LINE
adjustOwnerCount(view, sleOwner, -1, j);
auto const sponsor = getLedgerEntryReserveSponsor(view, sle);
adjustOwnerCount(view, sleOwner, sponsor, -1, j);
view.erase(sle);
return tesSUCCESS;
}
} // namespace ripple
} // namespace ripple

View File

@@ -94,7 +94,8 @@ DeleteOracle::deleteOracle(
auto const count =
sle->getFieldArray(sfPriceDataSeries).size() > 5 ? -2 : -1;
adjustOwnerCount(view, sleOwner, count, j);
auto const sponsor = getLedgerEntryReserveSponsor(view, sle);
adjustOwnerCount(view, sleOwner, sponsor, count, j);
view.erase(sle);

View File

@@ -206,7 +206,9 @@ DepositPreauth::doApply()
slePreauth->setFieldU64(sfOwnerNode, *page);
// If we succeeded, the new entry counts against the creator's reserve.
adjustOwnerCount(view(), sleOwner, 1, j_);
auto const sponsor = getTxReserveSponsor(view(), ctx_.tx);
adjustOwnerCount(view(), sleOwner, sponsor, 1, j_);
addSponsorToLedgerEntry(slePreauth, sponsor);
}
else if (ctx_.tx.isFieldPresent(sfUnauthorize))
{
@@ -270,7 +272,9 @@ DepositPreauth::doApply()
slePreauth->setFieldU64(sfOwnerNode, *page);
// If we succeeded, the new entry counts against the creator's reserve.
adjustOwnerCount(view(), sleOwner, 1, j_);
auto const sponsor = getTxReserveSponsor(view(), ctx_.tx);
adjustOwnerCount(view(), sleOwner, sponsor, 1, j_);
addSponsorToLedgerEntry(slePreauth, sponsor);
}
else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials))
{
@@ -311,7 +315,8 @@ DepositPreauth::removeFromLedger(
if (!sleOwner)
return tefINTERNAL;
adjustOwnerCount(view, sleOwner, -1, j);
auto const sponsor = getLedgerEntryReserveSponsor(view, slePreauth);
adjustOwnerCount(view, sleOwner, sponsor, -1, j);
// Remove DepositPreauth from ledger.
view.erase(slePreauth);

View File

@@ -599,7 +599,9 @@ EscrowCreate::doApply()
}
// increment owner count
adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal);
auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx);
adjustOwnerCount(ctx_.view(), sle, sponsor, 1, ctx_.journal);
addSponsorToLedgerEntry(slep, sponsor);
ctx_.view().update(sle);
return tesSUCCESS;
}
@@ -796,6 +798,7 @@ template <ValidIssueType T>
static TER
escrowUnlockApplyHelper(
ApplyView& view,
STTx const& tx,
Rate lockedRate,
std::shared_ptr<SLE> const& sleDest,
STAmount const& xrpBalance,
@@ -810,6 +813,7 @@ template <>
TER
escrowUnlockApplyHelper<Issue>(
ApplyView& view,
STTx const& tx,
Rate lockedRate,
std::shared_ptr<SLE> const& sleDest,
STAmount const& xrpBalance,
@@ -945,6 +949,7 @@ template <>
TER
escrowUnlockApplyHelper<MPTIssue>(
ApplyView& view,
STTx const& tx,
Rate lockedRate,
std::shared_ptr<SLE> const& sleDest,
STAmount const& xrpBalance,
@@ -977,7 +982,9 @@ escrowUnlockApplyHelper<MPTIssue>(
}
// update owner count.
adjustOwnerCount(view, sleDest, 1, journal);
auto const sponsor = getTxReserveSponsor(view, tx);
adjustOwnerCount(view, sleDest, sponsor, 1, journal);
addSponsorToLedgerEntry(sleDest, sponsor);
}
if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) &&
@@ -1157,6 +1164,7 @@ EscrowFinish::doApply()
[&]<typename T>(T const&) {
return escrowUnlockApplyHelper<T>(
ctx_.view(),
ctx_.tx,
lockedRate,
sled,
mPriorBalance,
@@ -1187,7 +1195,8 @@ EscrowFinish::doApply()
// Adjust source owner count
auto const sle = ctx_.view().peek(keylet::account(account));
adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal);
auto const sponsor = getLedgerEntryReserveSponsor(ctx_.view(), slep);
adjustOwnerCount(ctx_.view(), sle, sponsor, -1, ctx_.journal);
ctx_.view().update(sle);
// Remove escrow from ledger
@@ -1372,6 +1381,7 @@ EscrowCancel::doApply()
[&]<typename T>(T const&) {
return escrowUnlockApplyHelper<T>(
ctx_.view(),
ctx_.tx,
parityRate,
slep,
mPriorBalance,
@@ -1398,7 +1408,8 @@ EscrowCancel::doApply()
}
}
adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal);
auto const sponsor = getLedgerEntryReserveSponsor(ctx_.view(), slep);
adjustOwnerCount(ctx_.view(), sle, sponsor, -1, ctx_.journal);
ctx_.view().update(sle);
// Remove escrow from ledger

View File

@@ -178,6 +178,7 @@ MPTokenAuthorize::createMPToken(
TER
MPTokenAuthorize::authorize(
ApplyView& view,
STTx const& tx,
beast::Journal journal,
MPTAuthorizeArgs const& args)
{
@@ -208,7 +209,8 @@ MPTokenAuthorize::authorize(
false))
return tecINTERNAL; // LCOV_EXCL_LINE
adjustOwnerCount(view, sleAcct, -1, journal);
auto const sponsor = getLedgerEntryReserveSponsor(view, sleMpt);
adjustOwnerCount(view, sleAcct, sponsor, -1, journal);
view.erase(sleMpt);
return tesSUCCESS;
@@ -243,7 +245,9 @@ MPTokenAuthorize::authorize(
view.insert(mptoken);
// Update owner count.
adjustOwnerCount(view, sleAcct, 1, journal);
auto const sponsor = getTxReserveSponsor(view, tx);
adjustOwnerCount(view, sleAcct, sponsor, 1, journal);
addSponsorToLedgerEntry(mptoken, sponsor);
return tesSUCCESS;
}
@@ -289,6 +293,7 @@ MPTokenAuthorize::doApply()
auto const& tx = ctx_.tx;
return authorize(
ctx_.view(),
tx,
ctx_.journal,
{.priorBalance = mPriorBalance,
.mptIssuanceID = tx[sfMPTokenIssuanceID],

View File

@@ -51,6 +51,7 @@ public:
static TER
authorize(
ApplyView& view,
STTx const& tx,
beast::Journal journal,
MPTAuthorizeArgs const& args);

View File

@@ -85,6 +85,7 @@ MPTokenIssuanceCreate::preflight(PreflightContext const& ctx)
Expected<MPTID, TER>
MPTokenIssuanceCreate::create(
ApplyView& view,
STTx const& tx,
beast::Journal journal,
MPTCreateArgs const& args)
{
@@ -100,6 +101,8 @@ MPTokenIssuanceCreate::create(
auto const mptId = makeMptID(args.sequence, args.account);
auto const mptIssuanceKeylet = keylet::mptIssuance(mptId);
auto const sponsor = getTxReserveSponsor(view, tx);
// create the MPTokenIssuance
{
auto const ownerNode = view.dirInsert(
@@ -132,11 +135,13 @@ MPTokenIssuanceCreate::create(
if (args.domainId)
(*mptIssuance)[sfDomainID] = *args.domainId;
addSponsorToLedgerEntry(mptIssuance, sponsor);
view.insert(mptIssuance);
}
// Update owner count.
adjustOwnerCount(view, acct, 1, journal);
adjustOwnerCount(view, acct, sponsor, 1, journal);
return mptId;
}
@@ -147,6 +152,7 @@ MPTokenIssuanceCreate::doApply()
auto const& tx = ctx_.tx;
auto const result = create(
view(),
tx,
j_,
{
.priorBalance = mPriorBalance,

View File

@@ -56,7 +56,11 @@ public:
doApply() override;
static Expected<MPTID, TER>
create(ApplyView& view, beast::Journal journal, MPTCreateArgs const& args);
create(
ApplyView& view,
STTx const& tx,
beast::Journal journal,
MPTCreateArgs const& args);
};
} // namespace ripple

View File

@@ -78,7 +78,9 @@ MPTokenIssuanceDestroy::doApply()
view().erase(mpt);
adjustOwnerCount(view(), view().peek(keylet::account(account_)), -1, j_);
auto const sponsor = getLedgerEntryReserveSponsor(view(), mpt);
adjustOwnerCount(
view(), view().peek(keylet::account(account_)), sponsor, -1, j_);
return tesSUCCESS;
}

View File

@@ -93,6 +93,7 @@ NFTokenCreateOffer::doApply()
// Use implementation shared with NFTokenMint
return nft::tokenOfferCreateApply(
view(),
ctx_.tx,
ctx_.tx[sfAccount],
ctx_.tx[sfAmount],
ctx_.tx[~sfDestination],

View File

@@ -331,6 +331,7 @@ NFTokenMint::doApply()
// because a Mint is only allowed to create a sell offer.
if (TER const ter = nft::tokenOfferCreateApply(
view(),
ctx_.tx,
ctx_.tx[sfAccount],
ctx_.tx[sfAmount],
ctx_.tx[~sfDestination],

View File

@@ -294,6 +294,7 @@ insertToken(ApplyView& view, AccountID owner, STObject&& nft)
adjustOwnerCount(
view,
view.peek(keylet::account(owner)),
std::nullopt,
1,
beast::Journal{beast::Journal::getNullSink()});
});
@@ -458,11 +459,15 @@ removeToken(
cnt--;
if (cnt != 0)
{
auto const sponsor = getLedgerEntryReserveSponsor(view, curr);
adjustOwnerCount(
view,
view.peek(keylet::account(owner)),
sponsor,
cnt,
beast::Journal{beast::Journal::getNullSink()});
}
return tesSUCCESS;
}
@@ -496,9 +501,11 @@ removeToken(
curr->makeFieldAbsent(sfPreviousPageMin);
}
auto const sponsor = getLedgerEntryReserveSponsor(view, curr);
adjustOwnerCount(
view,
view.peek(keylet::account(owner)),
sponsor,
-1,
beast::Journal{beast::Journal::getNullSink()});
@@ -550,6 +557,7 @@ removeToken(
adjustOwnerCount(
view,
view.peek(keylet::account(owner)),
std::nullopt,
-1 * cnt,
beast::Journal{beast::Journal::getNullSink()});
@@ -703,9 +711,11 @@ deleteTokenOffer(ApplyView& view, std::shared_ptr<SLE> const& offer)
false))
return false;
auto const sponsor = getLedgerEntryReserveSponsor(view, offer);
adjustOwnerCount(
view,
view.peek(keylet::account(owner)),
sponsor,
-1,
beast::Journal{beast::Journal::getNullSink()});
@@ -1022,6 +1032,7 @@ tokenOfferCreatePreclaim(
TER
tokenOfferCreateApply(
ApplyView& view,
STTx const& tx,
AccountID const& acctID,
STAmount const& amount,
std::optional<AccountID> const& dest,
@@ -1038,6 +1049,7 @@ tokenOfferCreateApply(
return tecINSUFFICIENT_RESERVE;
auto const offerID = keylet::nftoffer(acctID, seqProxy.value());
auto const sponsor = getTxReserveSponsor(view, tx);
// Create the offer:
{
@@ -1084,11 +1096,13 @@ tokenOfferCreateApply(
if (dest)
(*offer)[sfDestination] = *dest;
addSponsorToLedgerEntry(offer, sponsor);
view.insert(offer);
}
// Update owner count.
adjustOwnerCount(view, view.peek(acctKeylet), 1, j);
adjustOwnerCount(view, view.peek(acctKeylet), sponsor, 1, j);
return tesSUCCESS;
}

View File

@@ -142,6 +142,7 @@ tokenOfferCreatePreclaim(
TER
tokenOfferCreateApply(
ApplyView& view,
STTx const& tx,
AccountID const& acctID,
STAmount const& amount,
std::optional<AccountID> const& dest,

View File

@@ -155,7 +155,8 @@ closeChannel(
"ripple::closeChannel : minimum channel amount");
(*sle)[sfBalance] =
(*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance];
adjustOwnerCount(view, sle, -1, j);
auto const sponsor = getLedgerEntryReserveSponsor(view, slep);
adjustOwnerCount(view, sle, sponsor, -1, j);
view.update(sle);
// Remove PayChan from ledger
@@ -312,7 +313,10 @@ PayChanCreate::doApply()
// Deduct owner's balance, increment owner count
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal);
auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx);
adjustOwnerCount(ctx_.view(), sle, sponsor, 1, ctx_.journal);
addSponsorToLedgerEntry(slep, sponsor);
ctx_.view().update(sle);
return tesSUCCESS;

View File

@@ -415,6 +415,14 @@ Payment::doApply()
sleDst->setAccountID(sfAccount, dstAccountID);
sleDst->setFieldU32(sfSequence, seqno);
auto const sponsor = getTxReserveSponsor(view(), ctx_.tx);
if (sponsor.has_value())
{
addSponsorToLedgerEntry(sleDst, sponsor);
sponsor.value()->setFieldU32(sfSponsoringAccountCount, 1);
view().update(sponsor.value());
}
view().insert(sleDst);
}
else

View File

@@ -87,7 +87,8 @@ PermissionedDomainDelete::doApply()
XRPL_ASSERT(
ownerSle && ownerSle->getFieldU32(sfOwnerCount) > 0,
"ripple::PermissionedDomainDelete::doApply : nonzero owner count");
adjustOwnerCount(view(), ownerSle, -1, ctx_.journal);
auto const sponsor = getLedgerEntryReserveSponsor(view(), slePd);
adjustOwnerCount(view(), ownerSle, sponsor, -1, ctx_.journal);
view().erase(slePd);
return tesSUCCESS;

View File

@@ -142,7 +142,9 @@ PermissionedDomainSet::doApply()
slePd->setFieldU64(sfOwnerNode, *page);
// If we succeeded, the new entry counts against the creator's reserve.
adjustOwnerCount(view(), ownerSle, 1, ctx_.journal);
auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx);
adjustOwnerCount(view(), ownerSle, sponsor, 1, ctx_.journal);
addSponsorToLedgerEntry(slePd, sponsor);
view().insert(slePd);
}

View File

@@ -188,7 +188,8 @@ adjustOwnerCount(ApplyContext& ctx, int count)
if (auto const sleAccount =
ctx.view().peek(keylet::account(ctx.tx[sfAccount])))
{
adjustOwnerCount(ctx.view(), sleAccount, count, ctx.journal);
adjustOwnerCount(
ctx.view(), sleAccount, std::nullopt, count, ctx.journal);
return true;
}

View File

@@ -231,9 +231,11 @@ removeSignersFromLedger(
return tefBAD_LEDGER;
}
auto const sponsor = getLedgerEntryReserveSponsor(view, signers);
adjustOwnerCount(
view,
view.peek(accountKeylet),
sponsor,
removeFromOwnerCount,
app.journal("View"));
@@ -394,7 +396,9 @@ SetSignerList::replaceSignerList()
// If we succeeded, the new entry counts against the
// creator's reserve.
adjustOwnerCount(view(), sle, addedOwnerCount, viewJ);
auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx);
adjustOwnerCount(view(), sle, sponsor, addedOwnerCount, viewJ);
addSponsorToLedgerEntry(signerList, sponsor);
return tesSUCCESS;
}

View File

@@ -636,7 +636,7 @@ SetTrust::doApply()
if (bLowReserveSet && !bLowReserved)
{
// Set reserve for low account.
adjustOwnerCount(view(), sleLowAccount, 1, viewJ);
adjustOwnerCount(view(), sleLowAccount, std::nullopt, 1, viewJ);
uFlagsOut |= lsfLowReserve;
if (!bHigh)
@@ -646,14 +646,14 @@ SetTrust::doApply()
if (bLowReserveClear && bLowReserved)
{
// Clear reserve for low account.
adjustOwnerCount(view(), sleLowAccount, -1, viewJ);
adjustOwnerCount(view(), sleLowAccount, std::nullopt, -1, viewJ);
uFlagsOut &= ~lsfLowReserve;
}
if (bHighReserveSet && !bHighReserved)
{
// Set reserve for high account.
adjustOwnerCount(view(), sleHighAccount, 1, viewJ);
adjustOwnerCount(view(), sleHighAccount, std::nullopt, 1, viewJ);
uFlagsOut |= lsfHighReserve;
if (bHigh)
@@ -663,7 +663,7 @@ SetTrust::doApply()
if (bHighReserveClear && bHighReserved)
{
// Clear reserve for high account.
adjustOwnerCount(view(), sleHighAccount, -1, viewJ);
adjustOwnerCount(view(), sleHighAccount, std::nullopt, -1, viewJ);
uFlagsOut &= ~lsfHighReserve;
}

View File

@@ -164,19 +164,19 @@ preflight1(PreflightContext const& ctx)
auto const sponsor = ctx.tx.getFieldObject(sfSponsor);
if (sponsor[sfAccount] == ctx.tx[sfAccount])
{
JLOG(ctx.j.debug()) << "preflight1: invalid sponsor account";
JLOG(ctx.j.fatal()) << "preflight1: invalid sponsor account";
return temMALFORMED;
}
if (!(sponsor[sfFlags] & tfSponsorFee) &&
!(sponsor[sfFlags] & tfSponsorReserve))
{
JLOG(ctx.j.debug()) << "preflight1: invalid sponsor flags";
JLOG(ctx.j.fatal()) << "preflight1: invalid sponsor flags";
return temMALFORMED;
}
if (!sponsor.isFieldPresent(sfSignature) &&
if (!sponsor.isFieldPresent(sfTxnSignature) &&
!sponsor.isFieldPresent(sfSigners))
{
JLOG(ctx.j.debug()) << "preflight1: no sfSignature or sfSigners";
JLOG(ctx.j.fatal()) << "preflight1: no sfTxnSignature or sfSigners";
return temMALFORMED;
}
}
@@ -558,7 +558,8 @@ Transactor::ticketDelete(
}
// Update the Ticket owner's reserve.
adjustOwnerCount(view, sleAccount, -1, j);
auto const sponsor = getLedgerEntryReserveSponsor(view, sleTicket);
adjustOwnerCount(view, sleAccount, sponsor, -1, j);
// Remove Ticket from ledger.
view.erase(sleTicket);
@@ -645,7 +646,7 @@ Transactor::checkSign(PreclaimContext const& ctx)
STArray const& txSigners(ctx.tx.getFieldArray(sfSigners));
return checkMultiSign(ctx.view, idAccount, txSigners, ctx.flags, ctx.j);
}
// if (ctx.tx.isFieldPresent(sfSponsor))
// {
// // TODO: check the sponsor signature

View File

@@ -174,7 +174,9 @@ VaultCreate::doApply()
if (auto ter = dirLink(view(), account_, vault))
return ter;
adjustOwnerCount(view(), owner, 1, j_);
auto const sponsor = getTxReserveSponsor(view(), tx);
adjustOwnerCount(view(), owner, sponsor, 1, j_);
addSponsorToLedgerEntry(vault, sponsor);
auto ownerCount = owner->at(sfOwnerCount);
if (mPriorBalance < view().fees().accountReserve(ownerCount))
return tecINSUFFICIENT_RESERVE;
@@ -203,6 +205,7 @@ VaultCreate::doApply()
// in the vault
auto maybeShare = MPTokenIssuanceCreate::create(
view(),
tx,
j_,
{
.priorBalance = std::nullopt,

View File

@@ -112,7 +112,8 @@ VaultDelete::doApply()
// Destroy the asset holding.
auto asset = vault->at(sfAsset);
if (auto ter = removeEmptyHolding(view(), vault->at(sfAccount), asset, j_);
if (auto ter = removeEmptyHolding(
view(), ctx_.tx, vault->at(sfAccount), asset, j_);
!isTesSuccess(ter))
return ter;
@@ -145,7 +146,8 @@ VaultDelete::doApply()
return tefBAD_LEDGER;
// LCOV_EXCL_STOP
}
adjustOwnerCount(view(), pseudoAcct, -1, j_);
auto const mptSponsor = getLedgerEntryReserveSponsor(view(), mpt);
adjustOwnerCount(view(), pseudoAcct, mptSponsor, -1, j_);
view().erase(mpt);
@@ -178,7 +180,8 @@ VaultDelete::doApply()
return tefBAD_LEDGER;
// LCOV_EXCL_STOP
}
adjustOwnerCount(view(), owner, -1, j_);
auto const vaultSponsor = getLedgerEntryReserveSponsor(view(), vault);
adjustOwnerCount(view(), owner, vaultSponsor, -1, j_);
// Destroy the vault.
view().erase(vault);

View File

@@ -200,7 +200,12 @@ VaultDeposit::doApply()
if ((vault->getFlags() & tfVaultPrivate) && account_ != vault->at(sfOwner))
{
if (auto const err = enforceMPTokenAuthorization(
ctx_.view(), mptIssuanceID, account_, mPriorBalance, j_);
ctx_.view(),
ctx_.tx,
mptIssuanceID,
account_,
mPriorBalance,
j_);
!isTesSuccess(err))
return err;
}
@@ -212,6 +217,7 @@ VaultDeposit::doApply()
{
if (auto const err = MPTokenAuthorize::authorize(
view(),
ctx_.tx,
ctx_.journal,
{.priorBalance = mPriorBalance,
.mptIssuanceID = mptIssuanceID->value(),
@@ -225,6 +231,7 @@ VaultDeposit::doApply()
{
if (auto const err = MPTokenAuthorize::authorize(
view(),
ctx_.tx,
ctx_.journal,
{
.priorBalance = mPriorBalance,

View File

@@ -753,7 +753,9 @@ finalizeClaimHelper(
// Remove the claim id from the ledger
outerSb.erase(sleClaimID);
adjustOwnerCount(outerSb, sleOwner, -1, j);
auto const sponsor =
getLedgerEntryReserveSponsor(outerSb, sleClaimID);
adjustOwnerCount(outerSb, sleOwner, sponsor, -1, j);
}
}
@@ -981,6 +983,7 @@ TER
applyCreateAccountAttestations(
ApplyView& view,
RawView& rawView,
STTx const& tx,
TIter attBegin,
TIter attEnd,
AccountID const& doorAccount,
@@ -1178,7 +1181,9 @@ applyCreateAccountAttestations(
return tecINTERNAL;
// Reserve was already checked
adjustOwnerCount(psb, sleDoor, 1, j);
auto const sponsor = getTxReserveSponsor(psb, tx);
adjustOwnerCount(psb, sleDoor, sponsor, 1, j);
addSponsorToLedgerEntry(createdSleClaimID, sponsor);
psb.insert(createdSleClaimID);
psb.update(sleDoor);
}
@@ -1360,6 +1365,7 @@ attestationDoApply(ApplyContext& ctx)
return applyCreateAccountAttestations(
ctx.view(),
ctx.rawView(),
ctx.tx,
&*att,
&*att + 1,
thisDoor,
@@ -1547,7 +1553,9 @@ XChainCreateBridge::doApply()
(*sleBridge)[sfOwnerNode] = *page;
}
adjustOwnerCount(ctx_.view(), sleAcct, 1, ctx_.journal);
auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx);
adjustOwnerCount(ctx_.view(), sleAcct, sponsor, 1, ctx_.journal);
addSponsorToLedgerEntry(sleBridge, sponsor);
ctx_.view().insert(sleBridge);
ctx_.view().update(sleAcct);
@@ -2123,7 +2131,9 @@ XChainCreateClaimID::doApply()
(*sleClaimID)[sfOwnerNode] = *page;
}
adjustOwnerCount(ctx_.view(), sleAcct, 1, ctx_.journal);
auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx);
adjustOwnerCount(ctx_.view(), sleAcct, sponsor, 1, ctx_.journal);
addSponsorToLedgerEntry(sleClaimID, sponsor);
ctx_.view().insert(sleClaimID);
ctx_.view().update(sleBridge);

View File

@@ -452,6 +452,17 @@ areCompatible(
beast::Journal::Stream& s,
char const* reason);
std::optional<std::shared_ptr<SLE>>
getTxReserveSponsor(ApplyView& view, STTx const& tx);
std::optional<std::shared_ptr<SLE>>
getLedgerEntryReserveSponsor(ApplyView& view, SLE::ref sle);
void
addSponsorToLedgerEntry(
std::shared_ptr<SLE> const& sle,
std::optional<std::shared_ptr<SLE>> const& sponsorSle);
//------------------------------------------------------------------------------
//
// Modifiers
@@ -462,7 +473,8 @@ areCompatible(
void
adjustOwnerCount(
ApplyView& view,
std::shared_ptr<SLE> const& sle,
std::shared_ptr<SLE> const& accountSle,
std::optional<std::shared_ptr<SLE>> const& sponsorSle,
std::int32_t amount,
beast::Journal j);
@@ -470,11 +482,17 @@ inline void
adjustOwnerCount(
ApplyView& view,
AccountID const& account,
std::optional<AccountID> const& sponsor,
std::int32_t amount,
beast::Journal j)
{
return adjustOwnerCount(
view, view.peek(keylet::account(account)), amount, j);
view,
view.peek(keylet::account(account)),
sponsor.has_value() ? view.peek(keylet::account(*sponsor))
: std::optional<std::shared_ptr<SLE>>(),
amount,
j);
}
/** @{ */
@@ -590,6 +608,7 @@ addEmptyHolding(
[[nodiscard]] TER
addEmptyHolding(
ApplyView& view,
STTx const& tx,
AccountID const& accountID,
XRPAmount priorBalance,
MPTIssue const& mptIssue,
@@ -641,6 +660,7 @@ trustCreate(
[[nodiscard]] TER
removeEmptyHolding(
ApplyView& view,
STTx const& tx,
AccountID const& accountID,
Issue const& issue,
beast::Journal journal);
@@ -648,6 +668,7 @@ removeEmptyHolding(
[[nodiscard]] TER
removeEmptyHolding(
ApplyView& view,
STTx const& tx,
AccountID const& accountID,
MPTIssue const& mptIssue,
beast::Journal journal);
@@ -655,13 +676,14 @@ removeEmptyHolding(
[[nodiscard]] inline TER
removeEmptyHolding(
ApplyView& view,
STTx const& tx,
AccountID const& accountID,
Asset const& asset,
beast::Journal journal)
{
return std::visit(
[&]<ValidIssueType TIss>(TIss const& issue) -> TER {
return removeEmptyHolding(view, accountID, issue, journal);
return removeEmptyHolding(view, tx, accountID, issue, journal);
},
asset.value());
}
@@ -863,6 +885,7 @@ requireAuth(
[[nodiscard]] TER
enforceMPTokenAuthorization(
ApplyView& view,
STTx const& tx,
MPTID const& mptIssuanceID,
AccountID const& account,
XRPAmount const& priorBalance,

View File

@@ -1021,6 +1021,40 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal)
return std::nullopt;
}
std::optional<std::shared_ptr<SLE>>
getTxReserveSponsor(ApplyView& view, STTx const& tx)
{
if (tx.isFieldPresent(sfSponsor))
{
auto const sponsorObj = tx.getFieldObject(sfSponsor);
auto const flags = sponsorObj.getFlags();
auto const sponsorID = sponsorObj.getAccountID(sfAccount);
if (flags & tfSponsorReserve)
{
return view.peek(keylet::account(sponsorID));
}
}
return std::nullopt;
}
std::optional<std::shared_ptr<SLE>>
getLedgerEntryReserveSponsor(ApplyView& view, SLE::ref sle)
{
if (sle->isFieldPresent(sfSponsorAccount))
return view.peek(keylet::account(sle->getAccountID(sfSponsorAccount)));
return std::nullopt;
}
void
addSponsorToLedgerEntry(
std::shared_ptr<SLE> const& sle,
std::optional<std::shared_ptr<SLE>> const& sponsorSle)
{
if (sponsorSle)
sle->setAccountID(
sfSponsorAccount, sponsorSle.value()->getAccountID(sfAccount));
}
//------------------------------------------------------------------------------
//
// Modifiers
@@ -1030,19 +1064,48 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal)
void
adjustOwnerCount(
ApplyView& view,
std::shared_ptr<SLE> const& sle,
std::shared_ptr<SLE> const& accountSle,
std::optional<std::shared_ptr<SLE>> const& sponsorSle,
std::int32_t amount,
beast::Journal j)
{
if (!sle)
if (!accountSle)
return;
XRPL_ASSERT(amount, "ripple::adjustOwnerCount : nonzero amount input");
std::uint32_t const current{sle->getFieldU32(sfOwnerCount)};
AccountID const id = (*sle)[sfAccount];
if (sponsorSle)
{
XRPL_ASSERT(sponsorSle, "ripple::adjustOwnerCount : sponsor not found");
{
// modify sponsor's SponsoringOwnerCount
std::uint32_t const current{
sponsorSle.value()->getFieldU32(sfSponsoringOwnerCount)};
AccountID const id = sponsorSle.value()->getAccountID(sfAccount);
std::uint32_t const adjusted =
confineOwnerCount(current, amount, id, j);
view.adjustOwnerCountHook(id, current, adjusted);
sponsorSle.value()->setFieldU32(sfSponsoringOwnerCount, adjusted);
view.update(sponsorSle.value());
}
{
// modify account's SponsoredOwnerCount
std::uint32_t const current{
accountSle->getFieldU32(sfSponsoredOwnerCount)};
AccountID const id = (*accountSle)[sfAccount];
std::uint32_t const adjusted =
confineOwnerCount(current, amount, id, j);
view.adjustOwnerCountHook(id, current, adjusted);
accountSle->setFieldU32(sfSponsoredOwnerCount, adjusted);
view.update(accountSle);
}
return;
}
std::uint32_t const current{accountSle->getFieldU32(sfOwnerCount)};
AccountID const id = (*accountSle)[sfAccount];
std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j);
view.adjustOwnerCountHook(id, current, adjusted);
sle->setFieldU32(sfOwnerCount, adjusted);
view.update(sle);
accountSle->setFieldU32(sfOwnerCount, adjusted);
view.update(accountSle);
}
std::function<void(SLE::ref)>
@@ -1201,6 +1264,7 @@ addEmptyHolding(
[[nodiscard]] TER
addEmptyHolding(
ApplyView& view,
STTx const& tx,
AccountID const& accountID,
XRPAmount priorBalance,
MPTIssue const& mptIssue,
@@ -1217,6 +1281,7 @@ addEmptyHolding(
return MPTokenAuthorize::authorize(
view,
tx,
journal,
{.priorBalance = priorBalance,
.mptIssuanceID = mptID,
@@ -1330,7 +1395,7 @@ trustCreate(
}
sleRippleState->setFieldU32(sfFlags, uFlags);
adjustOwnerCount(view, sleAccount, 1, j);
adjustOwnerCount(view, sleAccount, std::nullopt, 1, j);
// ONLY: Create ripple balance.
sleRippleState->setFieldAmount(
@@ -1345,6 +1410,7 @@ trustCreate(
[[nodiscard]] TER
removeEmptyHolding(
ApplyView& view,
STTx const& tx,
AccountID const& accountID,
Issue const& issue,
beast::Journal journal)
@@ -1375,7 +1441,7 @@ removeEmptyHolding(
view.peek(keylet::account(line->at(sfLowLimit)->getIssuer()));
if (!sleLowAccount)
return tecINTERNAL;
adjustOwnerCount(view, sleLowAccount, -1, journal);
adjustOwnerCount(view, sleLowAccount, std::nullopt, -1, journal);
// It's not really necessary to clear the reserve flag, since the line
// is about to be deleted, but this will make the metadata reflect an
// accurate state at the time of deletion.
@@ -1389,7 +1455,7 @@ removeEmptyHolding(
view.peek(keylet::account(line->at(sfHighLimit)->getIssuer()));
if (!sleHighAccount)
return tecINTERNAL;
adjustOwnerCount(view, sleHighAccount, -1, journal);
adjustOwnerCount(view, sleHighAccount, std::nullopt, -1, journal);
// It's not really necessary to clear the reserve flag, since the line
// is about to be deleted, but this will make the metadata reflect an
// accurate state at the time of deletion.
@@ -1407,6 +1473,7 @@ removeEmptyHolding(
[[nodiscard]] TER
removeEmptyHolding(
ApplyView& view,
STTx const& tx,
AccountID const& accountID,
MPTIssue const& mptIssue,
beast::Journal journal)
@@ -1420,6 +1487,7 @@ removeEmptyHolding(
return MPTokenAuthorize::authorize(
view,
tx,
journal,
{.priorBalance = {},
.mptIssuanceID = mptID,
@@ -1517,7 +1585,8 @@ offerDelete(ApplyView& view, std::shared_ptr<SLE> const& sle, beast::Journal j)
}
}
adjustOwnerCount(view, view.peek(keylet::account(owner)), -1, j);
auto const sponsor = getLedgerEntryReserveSponsor(view, sle);
adjustOwnerCount(view, view.peek(keylet::account(owner)), sponsor, -1, j);
view.erase(sle);
@@ -1611,7 +1680,11 @@ rippleCreditIOU(
{
// Clear the reserve of the sender, possibly delete the line!
adjustOwnerCount(
view, view.peek(keylet::account(uSenderID)), -1, j);
view,
view.peek(keylet::account(uSenderID)),
std::nullopt,
-1,
j);
// Clear reserve flag.
sleRippleState->setFieldU32(
@@ -2067,7 +2140,7 @@ updateTrustLine(
{
// VFALCO Where is the line being deleted?
// Clear the reserve of the sender, possibly delete the line!
adjustOwnerCount(view, sle, -1, j);
adjustOwnerCount(view, sle, std::nullopt, -1, j);
// Clear reserve flag.
state->setFieldU32(
@@ -2418,6 +2491,7 @@ requireAuth(
[[nodiscard]] TER
enforceMPTokenAuthorization(
ApplyView& view,
STTx const& tx,
MPTID const& mptIssuanceID,
AccountID const& account,
XRPAmount const& priorBalance, // for MPToken authorization
@@ -2499,6 +2573,7 @@ enforceMPTokenAuthorization(
"ripple::enforceMPTokenAuthorization : new MPToken for domain");
if (auto const err = MPTokenAuthorize::authorize(
view,
tx,
j,
{
.priorBalance = priorBalance,
@@ -2660,7 +2735,7 @@ deleteAMMTrustLine(
if (!(sleState->getFlags() & uFlags))
return tecINTERNAL;
adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, -1, j);
adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, std::nullopt, -1, j);
return tesSUCCESS;
}