xrpld
Loading...
Searching...
No Matches
VaultDelete.cpp
1#include <xrpl/tx/transactors/vault/VaultDelete.h>
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/base_uint.h>
5#include <xrpl/beast/utility/Zero.h>
6#include <xrpl/ledger/helpers/AccountRootHelpers.h>
7#include <xrpl/ledger/helpers/MPTokenHelpers.h>
8#include <xrpl/ledger/helpers/TokenHelpers.h>
9#include <xrpl/protocol/AccountID.h>
10#include <xrpl/protocol/Feature.h>
11#include <xrpl/protocol/Indexes.h>
12#include <xrpl/protocol/MPTIssue.h>
13#include <xrpl/protocol/Protocol.h>
14#include <xrpl/protocol/SField.h>
15#include <xrpl/protocol/STLedgerEntry.h>
16#include <xrpl/protocol/STNumber.h> // IWYU pragma: keep
17#include <xrpl/protocol/STTx.h>
18#include <xrpl/protocol/TER.h>
19#include <xrpl/protocol/XRPAmount.h>
20#include <xrpl/tx/Transactor.h>
21
22namespace xrpl {
23
26{
27 if (ctx.tx[sfVaultID] == beast::kZero)
28 {
29 JLOG(ctx.j.debug()) << "VaultDelete: zero/empty vault ID.";
30 return temMALFORMED;
31 }
32
33 if (ctx.tx.isFieldPresent(sfMemoData) && !ctx.rules.enabled(featureLendingProtocolV1_1))
34 return temDISABLED;
35
36 // The sfMemoData field is an optional field used to record the deletion reason.
37 if (!validDataLength(ctx.tx[~sfMemoData], kMaxDataPayloadLength))
38 return temMALFORMED;
39
40 return tesSUCCESS;
41}
42
43TER
45{
46 auto const vault = ctx.view.read(keylet::vault(ctx.tx[sfVaultID]));
47 if (!vault)
48 return tecNO_ENTRY;
49
50 if (vault->at(sfOwner) != ctx.tx[sfAccount])
51 {
52 JLOG(ctx.j.debug()) << "VaultDelete: account is not an owner.";
53 return tecNO_PERMISSION;
54 }
55
56 if (vault->at(sfAssetsAvailable) != 0)
57 {
58 JLOG(ctx.j.debug()) << "VaultDelete: nonzero assets available.";
59 return tecHAS_OBLIGATIONS;
60 }
61
62 if (vault->at(sfAssetsTotal) != 0)
63 {
64 JLOG(ctx.j.debug()) << "VaultDelete: nonzero assets total.";
65 return tecHAS_OBLIGATIONS;
66 }
67
68 // Verify we can destroy MPTokenIssuance
69 auto const sleMPT = ctx.view.read(keylet::mptokenIssuance(vault->at(sfShareMPTID)));
70
71 if (!sleMPT)
72 {
73 // LCOV_EXCL_START
74 JLOG(ctx.j.error()) << "VaultDelete: missing issuance of vault shares.";
76 // LCOV_EXCL_STOP
77 }
78
79 if (sleMPT->at(sfIssuer) != vault->getAccountID(sfAccount))
80 {
81 // LCOV_EXCL_START
82 JLOG(ctx.j.error()) << "VaultDelete: invalid owner of vault shares.";
83 return tecNO_PERMISSION;
84 // LCOV_EXCL_STOP
85 }
86
87 if (sleMPT->at(sfOutstandingAmount) != 0)
88 {
89 JLOG(ctx.j.debug()) << "VaultDelete: nonzero outstanding shares.";
90 return tecHAS_OBLIGATIONS;
91 }
92
93 return tesSUCCESS;
94}
95
96TER
98{
99 auto const vault = view().peek(keylet::vault(ctx_.tx[sfVaultID]));
100 auto applyViewContext = ctx_.getApplyViewContext();
101 if (!vault)
102 return tefINTERNAL; // LCOV_EXCL_LINE
103
104 // Destroy the asset holding.
105 auto asset = vault->at(sfAsset);
106
107 if (auto ter = removeEmptyHolding(applyViewContext, vault->at(sfAccount), asset, j_);
108 !isTesSuccess(ter))
109 return ter;
110
111 auto const& pseudoID = vault->at(sfAccount);
112 auto const pseudoAcct = view().peek(keylet::account(pseudoID));
113 if (!pseudoAcct)
114 {
115 // LCOV_EXCL_START
116 JLOG(j_.error()) << "VaultDelete: missing vault pseudo-account.";
117 return tefBAD_LEDGER;
118 // LCOV_EXCL_STOP
119 }
120
121 // Destroy the share issuance. Do not use MPTokenIssuanceDestroy for this,
122 // no special logic needed. First run few checks, duplicated from preclaim.
123 auto const shareMPTID = *vault->at(sfShareMPTID);
124 auto const mpt = view().peek(keylet::mptokenIssuance(shareMPTID));
125 if (!mpt)
126 {
127 // LCOV_EXCL_START
128 JLOG(j_.error()) << "VaultDelete: missing issuance of vault shares.";
129 return tefINTERNAL;
130 // LCOV_EXCL_STOP
131 }
132
133 // Try to remove MPToken for vault shares for the vault owner if it exists.
134 if (auto const mptoken = view().peek(keylet::mptoken(shareMPTID, accountID_)))
135 {
136 if (auto const ter =
137 removeEmptyHolding(applyViewContext, accountID_, MPTIssue(shareMPTID), j_);
138 !isTesSuccess(ter))
139 {
140 // LCOV_EXCL_START
141 JLOG(j_.error()) //
142 << "VaultDelete: failed to remove vault owner's MPToken"
143 << " MPTID=" << to_string(shareMPTID) //
144 << " account=" << toBase58(accountID_) //
145 << " with result: " << transToken(ter);
146 return ter;
147 // LCOV_EXCL_STOP
148 }
149 }
150
151 if (!view().dirRemove(keylet::ownerDir(pseudoID), (*mpt)[sfOwnerNode], mpt->key(), false))
152 {
153 // LCOV_EXCL_START
154 JLOG(j_.error()) << "VaultDelete: failed to delete issuance object.";
155 return tefBAD_LEDGER;
156 // LCOV_EXCL_STOP
157 }
158 decreaseOwnerCountForObject(view(), pseudoAcct, mpt, 1, j_);
159
160 view().erase(mpt);
161
162 // The pseudo-account's directory should have been deleted already.
163 if (view().peek(keylet::ownerDir(pseudoID)))
164 return tecHAS_OBLIGATIONS; // LCOV_EXCL_LINE
165
166 // Destroy the pseudo-account.
167 auto vaultPseudoSLE = view().peek(keylet::account(pseudoID));
168 if (!vaultPseudoSLE || vaultPseudoSLE->at(~sfVaultID) != vault->key())
169 return tefBAD_LEDGER; // LCOV_EXCL_LINE
170
171 // Making the payment and removing the empty holding should have deleted any
172 // obligations associated with the vault or vault pseudo-account.
173 if (*vaultPseudoSLE->at(sfBalance))
174 {
175 // LCOV_EXCL_START
176 JLOG(j_.error()) << "VaultDelete: pseudo-account has a balance";
177 return tecHAS_OBLIGATIONS;
178 // LCOV_EXCL_STOP
179 }
180 if (vaultPseudoSLE->at(sfOwnerCount) != 0)
181 {
182 // LCOV_EXCL_START
183 JLOG(j_.error()) << "VaultDelete: pseudo-account still owns objects";
184 return tecHAS_OBLIGATIONS;
185 // LCOV_EXCL_STOP
186 }
187 if (view().exists(keylet::ownerDir(pseudoID)))
188 {
189 // LCOV_EXCL_START
190 JLOG(j_.error()) << "VaultDelete: pseudo-account has a directory";
191 return tecHAS_OBLIGATIONS;
192 // LCOV_EXCL_STOP
193 }
194
195 view().erase(vaultPseudoSLE);
196
197 // Remove the vault from its owner's directory.
198 auto const ownerID = vault->at(sfOwner);
199 if (!view().dirRemove(keylet::ownerDir(ownerID), vault->at(sfOwnerNode), vault->key(), false))
200 {
201 // LCOV_EXCL_START
202 JLOG(j_.error()) << "VaultDelete: failed to delete vault object.";
203 return tefBAD_LEDGER;
204 // LCOV_EXCL_STOP
205 }
206
207 auto const owner = view().peek(keylet::account(ownerID));
208 if (!owner)
209 {
210 // LCOV_EXCL_START
211 JLOG(j_.error()) << "VaultDelete: missing vault owner account.";
212 return tefBAD_LEDGER;
213 // LCOV_EXCL_STOP
214 }
215
216 // We are destroying Vault and PseudoAccount, hence decrease by 2
217 decreaseOwnerCountForObject(view(), owner, vault, 2, j_);
218
219 // Destroy the vault.
220 view().erase(vault);
221
222 return tesSUCCESS;
223}
224
225void
227{
228 // No transaction-specific invariants yet (future work).
229}
230
231bool
233{
234 // No transaction-specific invariants yet (future work).
235 return true;
236}
237
238} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
Stream error() const
Definition Journal.h:362
Stream debug() const
Definition Journal.h:344
virtual SLE::pointer peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
virtual void erase(SLE::Ref sle)=0
Remove a peeked SLE.
A view into a ledger.
Definition ReadView.h:41
virtual SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(UInt256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:182
std::shared_ptr< STLedgerEntry const > const & ConstRef
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:464
beast::Journal const j_
Definition Transactor.h:164
ApplyView & view()
Definition Transactor.h:184
AccountID const accountID_
Definition Transactor.h:166
static bool validDataLength(std::optional< Slice > const &slice, std::size_t maxLength)
ApplyContext & ctx_
Definition Transactor.h:162
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
static TER preclaim(PreclaimContext const &ctx)
void visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override
Inspect a single ledger entry modified by this transaction.
static NotTEC preflight(PreflightContext const &ctx)
TER doApply() override
constexpr Zero kZero
Definition Zero.h:30
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:403
Keylet vault(AccountID const &owner, SeqProxy const &seq) noexcept
Definition Indexes.cpp:591
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:573
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:220
Keylet mptokenIssuance(MPTID const &issuanceID) noexcept
Definition Indexes.cpp:567
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
TER removeEmptyHolding(ApplyViewContext ctx, AccountID const &accountID, MPTIssue const &mptIssue, beast::Journal journal)
@ tefBAD_LEDGER
Definition TER.h:165
@ tefINTERNAL
Definition TER.h:168
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:95
std::string transToken(TER code)
Definition TER.cpp:257
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:657
constexpr std::size_t kMaxDataPayloadLength
The maximum length of Data payload.
Definition Protocol.h:303
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:614
void decreaseOwnerCountForObject(ApplyView &view, SLE::Ref accountSle, SLE::Ref objectSle, std::uint32_t count, beast::Journal j)
Decrease owner-count fields for an existing ledger object.
@ temMALFORMED
Definition TER.h:75
@ temDISABLED
Definition TER.h:102
bool isTesSuccess(TER x) noexcept
Definition TER.h:683
TERSubset< CanCvtToTER > TER
Definition TER.h:654
@ tecNO_ENTRY
Definition TER.h:314
@ tecOBJECT_NOT_FOUND
Definition TER.h:334
@ tecNO_PERMISSION
Definition TER.h:313
@ tecHAS_OBLIGATIONS
Definition TER.h:325
@ tesSUCCESS
Definition TER.h:250
UInt256 key
Definition Keylet.h:21
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:92
ReadView const & view
Definition Transactor.h:95
beast::Journal const j
Definition Transactor.h:100
State information when preflighting a tx.
Definition Transactor.h:39
beast::Journal const j
Definition Transactor.h:46