mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-24 07:30:30 +00:00
SponsorReserve,SponsorAccount
- not fully tested - need to modify: Reserve check where using sfOwnerCount
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -178,7 +178,8 @@ InnerObjectFormats::InnerObjectFormats()
|
||||
{
|
||||
{sfAccount, soeREQUIRED},
|
||||
{sfFlags, soeREQUIRED},
|
||||
{sfSignature, soeOPTIONAL},
|
||||
{sfSigningPubKey, soeOPTIONAL},
|
||||
{sfTxnSignature, soeOPTIONAL},
|
||||
{sfSigners, soeOPTIONAL},
|
||||
});
|
||||
}
|
||||
|
||||
423
src/test/app/Sponsor_test.cpp
Normal file
423
src/test/app/Sponsor_test.cpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
123
src/test/jtx/impl/sponsor.cpp
Normal file
123
src/test/jtx/impl/sponsor.cpp
Normal 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
|
||||
@@ -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
87
src/test/jtx/sponsor.h
Normal 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
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -51,6 +51,7 @@ public:
|
||||
static TER
|
||||
authorize(
|
||||
ApplyView& view,
|
||||
STTx const& tx,
|
||||
beast::Journal journal,
|
||||
MPTAuthorizeArgs const& args);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -142,6 +142,7 @@ tokenOfferCreatePreclaim(
|
||||
TER
|
||||
tokenOfferCreateApply(
|
||||
ApplyView& view,
|
||||
STTx const& tx,
|
||||
AccountID const& acctID,
|
||||
STAmount const& amount,
|
||||
std::optional<AccountID> const& dest,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user