Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions include/xrpl/ledger/View.h
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,17 @@ areCompatible(
beast::Journal::Stream& s,
char const* reason);

uint32_t
ownerCount(std::shared_ptr<SLE const> const& accountSle);

TER
checkInsufficientReserve(
ReadView const& view,
std::shared_ptr<SLE const> const& accSle,
STAmount const& accBalance,
std::int32_t ownerCountDelta,
std::int32_t accountCountDelta = 0);

//------------------------------------------------------------------------------
//
// Modifiers
Expand Down
38 changes: 31 additions & 7 deletions src/libxrpl/ledger/View.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,30 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal)
return std::nullopt;
}

uint32_t
ownerCount(std::shared_ptr<SLE const> const& accountSle)
{
auto const ownerCount = accountSle->getFieldU32(sfOwnerCount);
return ownerCount;
Comment thread
tequdev marked this conversation as resolved.
Outdated
}

TER
checkInsufficientReserve(
ReadView const& view,
std::shared_ptr<SLE const> const& accSle,
STAmount const& accBalance,
std::int32_t ownerCountDelta,
std::int32_t accountCountDelta)
Comment thread
vvysokikh1 marked this conversation as resolved.
Outdated
{
STAmount const reserve{view.fees().accountReserve(
Comment thread
tequdev marked this conversation as resolved.
Outdated
accSle->getFieldU32(sfOwnerCount) + ownerCountDelta)};

if (accBalance < reserve)
return tecINSUFFICIENT_RESERVE;

return tesSUCCESS;
}

//------------------------------------------------------------------------------
//
// Modifiers
Expand Down Expand Up @@ -1328,13 +1352,13 @@ authorizeMPToken(
// an account owns, in the case of MPTokens we only
// *enforce* a reserve if the user owns more than two
// items. This is similar to the reserve requirements of trust lines.
std::uint32_t const uOwnerCount = sleAcct->getFieldU32(sfOwnerCount);
XRPAmount const reserveCreate(
(uOwnerCount < 2) ? XRPAmount(beast::zero)
: view.fees().accountReserve(uOwnerCount + 1));

if (priorBalance < reserveCreate)
return tecINSUFFICIENT_RESERVE;
if (ownerCount(sleAcct) >= 2)
{
if (auto const ret =
checkInsufficientReserve(view, sleAcct, priorBalance, 1);
!isTesSuccess(ret))
return ret;
}

auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
auto mptoken = std::make_shared<SLE>(mptokenKey);
Expand Down
16 changes: 8 additions & 8 deletions src/xrpld/app/tx/detail/AMMWithdraw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -590,15 +590,15 @@ AMMWithdraw::withdraw(
if (!sleAccount)
return tecINTERNAL; // LCOV_EXCL_LINE
auto const balance = (*sleAccount)[sfBalance].xrp();
std::uint32_t const ownerCount = sleAccount->at(sfOwnerCount);

// See also SetTrust::doApply()
XRPAmount const reserve(
(ownerCount < 2) ? XRPAmount(beast::zero)
: view.fees().accountReserve(ownerCount + 1));

if (std::max(priorBalance, balance) < reserve)
return tecINSUFFICIENT_RESERVE;
std::uint32_t const count = ownerCount(sleAccount);
if (count >= 2)
{
if (auto const ret = checkInsufficientReserve(
view, sleAccount, std::max(priorBalance, balance), 1);
!isTesSuccess(ret))
return ret;
}
}
return tesSUCCESS;
};
Expand Down
11 changes: 4 additions & 7 deletions src/xrpld/app/tx/detail/CreateCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,10 @@ CreateCheck::doApply()
// A check counts against the reserve of the issuing account, but we
// check the starting balance because we want to allow dipping into the
// reserve to pay fees.
{
STAmount const reserve{
view().fees().accountReserve(sle->getFieldU32(sfOwnerCount) + 1)};

if (mPriorBalance < reserve)
return tecINSUFFICIENT_RESERVE;
}
if (auto const ret =
checkInsufficientReserve(view(), sle, mPriorBalance, 1);
!isTesSuccess(ret))
return ret;

// Note that we use the value from the sequence or ticket as the
// Check sequence. For more explanation see comments in SeqProxy.h.
Expand Down
11 changes: 4 additions & 7 deletions src/xrpld/app/tx/detail/CreateTicket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,10 @@ CreateTicket::doApply()
// check the starting balance because we want to allow dipping into the
// reserve to pay fees.
std::uint32_t const ticketCount = ctx_.tx[sfTicketCount];
{
XRPAmount const reserve = view().fees().accountReserve(
sleAccountRoot->getFieldU32(sfOwnerCount) + ticketCount);

if (mPriorBalance < reserve)
return tecINSUFFICIENT_RESERVE;
}
if (auto const ret = checkInsufficientReserve(
view(), sleAccountRoot, mPriorBalance, ticketCount);
!isTesSuccess(ret))
return ret;

beast::Journal viewJ{ctx_.app.journal("View")};

Expand Down
20 changes: 8 additions & 12 deletions src/xrpld/app/tx/detail/Credentials.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,10 @@ CredentialCreate::doApply()
if (!sleIssuer)
return tefINTERNAL;

{
STAmount const reserve{view().fees().accountReserve(
sleIssuer->getFieldU32(sfOwnerCount) + 1)};
if (mPriorBalance < reserve)
return tecINSUFFICIENT_RESERVE;
}
if (auto const ret =
checkInsufficientReserve(view(), sleIssuer, mPriorBalance, 1);
!isTesSuccess(ret))
return ret;

sleCred->setAccountID(sfSubject, subject);
sleCred->setAccountID(sfIssuer, account_);
Expand Down Expand Up @@ -344,12 +342,10 @@ CredentialAccept::doApply()
if (!sleSubject || !sleIssuer)
return tefINTERNAL;

{
STAmount const reserve{view().fees().accountReserve(
sleSubject->getFieldU32(sfOwnerCount) + 1)};
if (mPriorBalance < reserve)
return tecINSUFFICIENT_RESERVE;
}
if (auto const ret =
checkInsufficientReserve(view(), sleSubject, mPriorBalance, 1);
!isTesSuccess(ret))
return ret;

auto const credType(ctx_.tx[sfCredentialType]);
Keylet const credentialKey = keylet::credential(account_, issuer, credType);
Expand Down
13 changes: 5 additions & 8 deletions src/xrpld/app/tx/detail/DID.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,11 @@ addSLE(
return tefINTERNAL;

// Check reserve availability for new object creation
{
auto const balance = STAmount((*sleAccount)[sfBalance]).xrp();
auto const reserve =
ctx.view().fees().accountReserve((*sleAccount)[sfOwnerCount] + 1);

if (balance < reserve)
return tecINSUFFICIENT_RESERVE;
}
auto const balance = STAmount((*sleAccount)[sfBalance]).xrp();
if (auto const ret =
checkInsufficientReserve(ctx.view(), sleAccount, balance, 1);
!isTesSuccess(ret))
return ret;

// Add ledger object to ledger
ctx.view().insert(sle);
Expand Down
9 changes: 4 additions & 5 deletions src/xrpld/app/tx/detail/DelegateSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,10 @@ DelegateSet::doApply()
return tesSUCCESS;
}

STAmount const reserve{ctx_.view().fees().accountReserve(
sleOwner->getFieldU32(sfOwnerCount) + 1)};

if (mPriorBalance < reserve)
return tecINSUFFICIENT_RESERVE;
if (auto const ret =
checkInsufficientReserve(view(), sleOwner, mPriorBalance, 1);
!isTesSuccess(ret))
return ret;

auto const& permissions = ctx_.tx.getFieldArray(sfPermissions);
if (!permissions.empty())
Expand Down
22 changes: 8 additions & 14 deletions src/xrpld/app/tx/detail/DepositPreauth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,13 +170,10 @@ DepositPreauth::doApply()
// A preauth counts against the reserve of the issuing account, but we
// check the starting balance because we want to allow dipping into the
// reserve to pay fees.
{
STAmount const reserve{view().fees().accountReserve(
sleOwner->getFieldU32(sfOwnerCount) + 1)};

if (mPriorBalance < reserve)
return tecINSUFFICIENT_RESERVE;
}
if (auto const ret =
checkInsufficientReserve(view(), sleOwner, mPriorBalance, 1);
!isTesSuccess(ret))
return ret;

// Preclaim already verified that the Preauth entry does not yet exist.
// Create and populate the Preauth entry.
Expand Down Expand Up @@ -221,13 +218,10 @@ DepositPreauth::doApply()
// A preauth counts against the reserve of the issuing account, but we
// check the starting balance because we want to allow dipping into the
// reserve to pay fees.
{
STAmount const reserve{view().fees().accountReserve(
sleOwner->getFieldU32(sfOwnerCount) + 1)};

if (mPriorBalance < reserve)
return tecINSUFFICIENT_RESERVE;
}
if (auto const ret =
checkInsufficientReserve(view(), sleOwner, mPriorBalance, 1);
!isTesSuccess(ret))
return ret;

// Preclaim already verified that the Preauth entry does not yet exist.
// Create and populate the Preauth entry.
Expand Down
22 changes: 11 additions & 11 deletions src/xrpld/app/tx/detail/Escrow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -496,16 +496,17 @@ EscrowCreate::doApply()
// Check reserve and funds availability
STAmount const amount{ctx_.tx[sfAmount]};

auto const reserve =
ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + 1);

if (mSourceBalance < reserve)
return tecINSUFFICIENT_RESERVE;
if (auto const ret =
checkInsufficientReserve(ctx_.view(), sle, mSourceBalance, 1);
!isTesSuccess(ret))
return ret;

// Check reserve and funds availability
if (isXRP(amount))
{
if (mSourceBalance < reserve + STAmount(amount).xrp())
if (auto const ret = checkInsufficientReserve(
ctx_.view(), sle, mSourceBalance - STAmount(amount).xrp(), 1);
!isTesSuccess(ret))
return tecUNFUNDED;
}

Expand Down Expand Up @@ -974,11 +975,10 @@ escrowUnlockApplyHelper<MPTIssue>(
if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) &&
createAsset && !receiverIssuer)
{
if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)};
xrpBalance < view.fees().accountReserve(ownerCount + 1))
{
return tecINSUFFICIENT_RESERVE;
}
if (auto const ret =
checkInsufficientReserve(view, sleDest, xrpBalance, 1);
!isTesSuccess(ret))
return ret;

if (auto const ter =
MPTokenAuthorize::createMPToken(view, mptID, receiver, 0);
Expand Down
11 changes: 7 additions & 4 deletions src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,13 @@ MPTokenIssuanceCreate::create(
if (!acct)
return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE

if (args.priorBalance &&
*(args.priorBalance) <
view.fees().accountReserve((*acct)[sfOwnerCount] + 1))
return Unexpected(tecINSUFFICIENT_RESERVE);
if (args.priorBalance)
{
if (auto const ret =
checkInsufficientReserve(view, acct, *(args.priorBalance), 1);
!isTesSuccess(ret))
return Unexpected(ret); // tecINSUFFICIENT_RESERVE
}

auto const mptId = makeMptID(args.sequence, args.account);
auto const mptIssuanceKeylet = keylet::mptIssuance(mptId);
Expand Down
13 changes: 9 additions & 4 deletions src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,10 +463,15 @@ NFTokenAcceptOffer::transferNFToken(
auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount);
if (buyerOwnerCountAfter > buyerOwnerCountBefore)
{
if (auto const reserve =
view().fees().accountReserve(buyerOwnerCountAfter);
buyerBalance < reserve)
return tecINSUFFICIENT_RESERVE;
if (auto const ret = checkInsufficientReserve(
ctx_.view(),
sleBuyer,
buyerBalance,
// Specify 0 here since ownerCount has already been
// incremented
0);
!isTesSuccess(ret))
return ret;
}
}

Expand Down
16 changes: 11 additions & 5 deletions src/xrpld/app/tx/detail/NFTokenMint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,13 +351,19 @@ NFTokenMint::doApply()
// allows NFTs to be added to the page (and burn fees) without
// requiring the reserve to be met each time. The reserve is
// only managed when a new NFT page or sell offer is added.
if (auto const ownerCountAfter =
view().read(keylet::account(account_))->getFieldU32(sfOwnerCount);
auto const sle = view().read(keylet::account(account_));
if (auto const ownerCountAfter = sle->getFieldU32(sfOwnerCount);
ownerCountAfter > ownerCountBefore)
{
if (auto const reserve = view().fees().accountReserve(ownerCountAfter);
mPriorBalance < reserve)
return tecINSUFFICIENT_RESERVE;
if (auto const ret = checkInsufficientReserve(
ctx_.view(),
sle,
mPriorBalance,
// Specify 0 here since ownerCount has already been
// incremented
0);
!isTesSuccess(ret))
return ret;
}
return tesSUCCESS;
}
Expand Down
7 changes: 4 additions & 3 deletions src/xrpld/app/tx/detail/NFTokenUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1033,9 +1033,10 @@ tokenOfferCreateApply(
std::uint32_t txFlags)
{
Keylet const acctKeylet = keylet::account(acctID);
if (auto const acct = view.read(acctKeylet);
priorBalance < view.fees().accountReserve((*acct)[sfOwnerCount] + 1))
return tecINSUFFICIENT_RESERVE;
if (auto const ret = checkInsufficientReserve(
view, view.read(acctKeylet), priorBalance, 1);
!isTesSuccess(ret))
return ret;

auto const offerID = keylet::nftoffer(acctID, seqProxy.value());

Expand Down
30 changes: 16 additions & 14 deletions src/xrpld/app/tx/detail/PayChan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,13 +204,14 @@
// Check reserve and funds availability
{
auto const balance = (*sle)[sfBalance];
auto const reserve =
ctx.view.fees().accountReserve((*sle)[sfOwnerCount] + 1);

if (balance < reserve)
return tecINSUFFICIENT_RESERVE;

if (balance < reserve + ctx.tx[sfAmount])
if (auto const ret =
checkInsufficientReserve(ctx.view, sle, balance, 1);
!isTesSuccess(ret))
return ret;

Check warning on line 210 in src/xrpld/app/tx/detail/PayChan.cpp

View check run for this annotation

Codecov / codecov/patch

src/xrpld/app/tx/detail/PayChan.cpp#L210

Added line #L210 was not covered by tests

if (auto const ret = checkInsufficientReserve(
ctx.view, sle, balance - ctx.tx[sfAmount], 1);
!isTesSuccess(ret))
return tecUNFUNDED;
}

Expand Down Expand Up @@ -394,13 +395,14 @@
{
// Check reserve and funds availability
auto const balance = (*sle)[sfBalance];
auto const reserve =
ctx_.view().fees().accountReserve((*sle)[sfOwnerCount]);

if (balance < reserve)
return tecINSUFFICIENT_RESERVE;

if (balance < reserve + ctx_.tx[sfAmount])
if (auto const ret =
checkInsufficientReserve(ctx_.view(), sle, balance, 0);
!isTesSuccess(ret))
return ret;

Check warning on line 401 in src/xrpld/app/tx/detail/PayChan.cpp

View check run for this annotation

Codecov / codecov/patch

src/xrpld/app/tx/detail/PayChan.cpp#L401

Added line #L401 was not covered by tests

if (auto const ret = checkInsufficientReserve(
ctx_.view(), sle, balance - ctx_.tx[sfAmount], 0);
!isTesSuccess(ret))
return tecUNFUNDED;
}

Expand Down
Loading