mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-04 01:06:48 +00:00
193 lines
5.5 KiB
C++
193 lines
5.5 KiB
C++
#include <xrpl/tx/transactors/delegate/DelegateSet.h>
|
||
|
||
#include <xrpl/basics/Log.h>
|
||
#include <xrpl/beast/utility/Journal.h>
|
||
#include <xrpl/core/ServiceRegistry.h>
|
||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||
#include <xrpl/protocol/Indexes.h>
|
||
#include <xrpl/protocol/Protocol.h>
|
||
#include <xrpl/protocol/SField.h>
|
||
#include <xrpl/protocol/STAmount.h>
|
||
#include <xrpl/protocol/STLedgerEntry.h>
|
||
#include <xrpl/protocol/STTx.h>
|
||
#include <xrpl/protocol/TER.h>
|
||
#include <xrpl/protocol/XRPAmount.h>
|
||
#include <xrpl/tx/Transactor.h>
|
||
|
||
#include <cstdint>
|
||
#include <memory>
|
||
#include <unordered_set>
|
||
|
||
namespace xrpl {
|
||
|
||
NotTEC
|
||
DelegateSet::preflight(PreflightContext const& ctx)
|
||
{
|
||
auto const& permissions = ctx.tx.getFieldArray(sfPermissions);
|
||
if (permissions.size() > kPermissionMaxSize)
|
||
return temARRAY_TOO_LARGE;
|
||
|
||
// can not authorize self
|
||
if (ctx.tx[sfAccount] == ctx.tx[sfAuthorize])
|
||
return temMALFORMED;
|
||
|
||
std::unordered_set<std::uint32_t> permissionSet;
|
||
|
||
for (auto const& permission : permissions)
|
||
{
|
||
if (!permissionSet.insert(permission[sfPermissionValue]).second)
|
||
return temMALFORMED;
|
||
|
||
if (!Permission::getInstance().isDelegable(permission[sfPermissionValue], ctx.rules))
|
||
return temMALFORMED;
|
||
}
|
||
|
||
return tesSUCCESS;
|
||
}
|
||
|
||
TER
|
||
DelegateSet::preclaim(PreclaimContext const& ctx)
|
||
{
|
||
if (!ctx.view.exists(keylet::account(ctx.tx[sfAccount])))
|
||
return terNO_ACCOUNT; // LCOV_EXCL_LINE
|
||
|
||
if (!ctx.view.exists(keylet::account(ctx.tx[sfAuthorize])))
|
||
return tecNO_TARGET;
|
||
|
||
// Deleting the delegate object is invalid if it doesn’t exist.
|
||
if (ctx.tx.getFieldArray(sfPermissions).empty() &&
|
||
!ctx.view.exists(keylet::delegate(ctx.tx[sfAccount], ctx.tx[sfAuthorize])))
|
||
{
|
||
return tecNO_ENTRY;
|
||
}
|
||
|
||
return tesSUCCESS;
|
||
}
|
||
|
||
TER
|
||
DelegateSet::doApply()
|
||
{
|
||
auto const sleOwner = ctx_.view().peek(keylet::account(accountID_));
|
||
if (!sleOwner)
|
||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||
|
||
auto const& authAccount = ctx_.tx[sfAuthorize];
|
||
auto const delegateKey = keylet::delegate(accountID_, authAccount);
|
||
|
||
auto sle = ctx_.view().peek(delegateKey);
|
||
if (sle)
|
||
{
|
||
auto const& permissions = ctx_.tx.getFieldArray(sfPermissions);
|
||
if (permissions.empty())
|
||
{
|
||
// if permissions array is empty, delete the ledger object.
|
||
return deleteDelegate(view(), sle, j_);
|
||
}
|
||
|
||
sle->setFieldArray(sfPermissions, permissions);
|
||
ctx_.view().update(sle);
|
||
return tesSUCCESS;
|
||
}
|
||
|
||
auto const& permissions = ctx_.tx.getFieldArray(sfPermissions);
|
||
if (permissions.empty())
|
||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||
|
||
STAmount const reserve{
|
||
ctx_.view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)};
|
||
|
||
if (preFeeBalance_ < reserve)
|
||
return tecINSUFFICIENT_RESERVE;
|
||
|
||
sle = std::make_shared<SLE>(delegateKey);
|
||
sle->setAccountID(sfAccount, accountID_);
|
||
sle->setAccountID(sfAuthorize, authAccount);
|
||
|
||
sle->setFieldArray(sfPermissions, permissions);
|
||
|
||
// Add to delegating account's owner directory
|
||
auto const page = ctx_.view().dirInsert(
|
||
keylet::ownerDir(accountID_), delegateKey, describeOwnerDir(accountID_));
|
||
|
||
if (!page)
|
||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||
|
||
(*sle)[sfOwnerNode] = *page;
|
||
|
||
// Add to authorized account's owner directory so AccountDelete can find
|
||
// and clean up inbound delegations when the authorized account is deleted.
|
||
auto const destPage = ctx_.view().dirInsert(
|
||
keylet::ownerDir(authAccount), delegateKey, describeOwnerDir(authAccount));
|
||
|
||
if (!destPage)
|
||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||
|
||
(*sle)[sfDestinationNode] = *destPage;
|
||
|
||
ctx_.view().insert(sle);
|
||
adjustOwnerCount(ctx_.view(), sleOwner, 1, ctx_.journal);
|
||
|
||
return tesSUCCESS;
|
||
}
|
||
|
||
TER
|
||
DelegateSet::deleteDelegate(ApplyView& view, std::shared_ptr<SLE> const& sle, beast::Journal j)
|
||
{
|
||
if (!sle)
|
||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||
|
||
auto const delegator = (*sle)[sfAccount];
|
||
auto const delegatee = (*sle)[sfAuthorize];
|
||
|
||
// Remove from delegating account's owner directory
|
||
if (!view.dirRemove(keylet::ownerDir(delegator), (*sle)[sfOwnerNode], sle->key(), false))
|
||
{
|
||
// LCOV_EXCL_START
|
||
JLOG(j.fatal()) << "Unable to delete Delegate from owner.";
|
||
return tefBAD_LEDGER;
|
||
// LCOV_EXCL_STOP
|
||
}
|
||
|
||
// Remove from authorized account's owner directory, if present
|
||
if (auto const optPage = (*sle)[~sfDestinationNode])
|
||
{
|
||
if (!view.dirRemove(keylet::ownerDir(delegatee), *optPage, sle->key(), false))
|
||
{
|
||
// LCOV_EXCL_START
|
||
JLOG(j.fatal()) << "Unable to delete Delegate from authorized account.";
|
||
return tefBAD_LEDGER;
|
||
// LCOV_EXCL_STOP
|
||
}
|
||
}
|
||
|
||
// Only the delegating account's owner count was incremented on creation
|
||
auto const sleOwner = view.peek(keylet::account(delegator));
|
||
if (!sleOwner)
|
||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||
|
||
adjustOwnerCount(view, sleOwner, -1, j);
|
||
|
||
view.erase(sle);
|
||
|
||
return tesSUCCESS;
|
||
}
|
||
|
||
void
|
||
DelegateSet::visitInvariantEntry(
|
||
bool,
|
||
std::shared_ptr<SLE const> const&,
|
||
std::shared_ptr<SLE const> const&)
|
||
{
|
||
// No transaction-specific invariants yet (future work).
|
||
}
|
||
|
||
bool
|
||
DelegateSet::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||
{
|
||
// No transaction-specific invariants yet (future work).
|
||
return true;
|
||
}
|
||
|
||
} // namespace xrpl
|