mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-25 04:57:11 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8b66c8961 | ||
|
|
d0a54d1159 |
No files matched your search
@@ -0,0 +1,81 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <xrpl/basics/Number.h>
|
||||||
|
#include <xrpl/basics/base_uint.h>
|
||||||
|
#include <xrpl/protocol/AccountID.h>
|
||||||
|
#include <xrpl/protocol/Asset.h>
|
||||||
|
#include <xrpl/protocol/MPTIssue.h>
|
||||||
|
#include <xrpl/protocol/STLedgerEntry.h>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace xrpl {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Collects vault and share-issuance snapshots from ledger entry visits.
|
||||||
|
*
|
||||||
|
* Used by per-transaction invariant checks (e.g. VaultCreate) that need
|
||||||
|
* vault and MPTokenIssuance state without the full balance-delta tracking
|
||||||
|
* that ValidVault maintains.
|
||||||
|
*/
|
||||||
|
class VaultInvariantData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Vault
|
||||||
|
{
|
||||||
|
uint256 key = beast::kZero;
|
||||||
|
Asset asset;
|
||||||
|
AccountID pseudoId;
|
||||||
|
AccountID owner;
|
||||||
|
uint192 shareMPTID = beast::kZero;
|
||||||
|
Number assetsTotal = 0;
|
||||||
|
Number assetsAvailable = 0;
|
||||||
|
Number assetsMaximum = 0;
|
||||||
|
Number lossUnrealized = 0;
|
||||||
|
|
||||||
|
static Vault
|
||||||
|
make(SLE const&);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Shares
|
||||||
|
{
|
||||||
|
MPTIssue share;
|
||||||
|
std::uint64_t sharesTotal = 0;
|
||||||
|
std::uint64_t sharesMaximum = 0;
|
||||||
|
|
||||||
|
static Shares
|
||||||
|
make(SLE const&);
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after);
|
||||||
|
|
||||||
|
[[nodiscard]] std::vector<Vault> const&
|
||||||
|
afterVaults() const
|
||||||
|
{
|
||||||
|
return afterVault_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::vector<Vault> const&
|
||||||
|
beforeVaults() const
|
||||||
|
{
|
||||||
|
return beforeVault_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Find shares in afterMPTs_ whose mptID matches. */
|
||||||
|
[[nodiscard]] std::optional<Shares>
|
||||||
|
findShares(uint192 const& mptID) const;
|
||||||
|
|
||||||
|
/** Find deleted shares in beforeMPTs_ whose mptID matches. */
|
||||||
|
[[nodiscard]] std::optional<Shares>
|
||||||
|
findDeletedShares(uint192 const& mptID) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Vault> afterVault_;
|
||||||
|
std::vector<Vault> beforeVault_;
|
||||||
|
std::vector<Shares> afterMPTs_;
|
||||||
|
std::vector<Shares> beforeMPTs_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xrpl
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <xrpl/tx/Transactor.h>
|
#include <xrpl/tx/Transactor.h>
|
||||||
|
#include <xrpl/tx/invariants/VaultInvariantData.h>
|
||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
@@ -38,6 +39,9 @@ public:
|
|||||||
XRPAmount fee,
|
XRPAmount fee,
|
||||||
ReadView const& view,
|
ReadView const& view,
|
||||||
beast::Journal const& j) override;
|
beast::Journal const& j) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
VaultInvariantData data_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace xrpl
|
} // namespace xrpl
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <xrpl/tx/Transactor.h>
|
#include <xrpl/tx/Transactor.h>
|
||||||
|
#include <xrpl/tx/invariants/VaultInvariantData.h>
|
||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
@@ -32,6 +33,9 @@ public:
|
|||||||
XRPAmount fee,
|
XRPAmount fee,
|
||||||
ReadView const& view,
|
ReadView const& view,
|
||||||
beast::Journal const& j) override;
|
beast::Journal const& j) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
VaultInvariantData data_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace xrpl
|
} // namespace xrpl
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <xrpl/beast/utility/Journal.h>
|
#include <xrpl/beast/utility/Journal.h>
|
||||||
#include <xrpl/beast/utility/instrumentation.h>
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
|
||||||
#include <xrpl/protocol/Feature.h>
|
#include <xrpl/protocol/Feature.h>
|
||||||
#include <xrpl/protocol/Indexes.h>
|
#include <xrpl/protocol/Indexes.h>
|
||||||
#include <xrpl/protocol/Issue.h>
|
#include <xrpl/protocol/Issue.h>
|
||||||
@@ -339,51 +338,8 @@ ValidVault::finalize(
|
|||||||
return !enforce; // That's all we can do here
|
return !enforce; // That's all we can do here
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note, if afterVault_ is empty then we know that beforeVault_ is not
|
// Delete-specific checks now live in VaultDelete::finalizeInvariants.
|
||||||
// empty, as enforced at the top of this function
|
return true;
|
||||||
auto const& beforeVault = beforeVault_[0];
|
|
||||||
|
|
||||||
// At this moment we only know a vault is being deleted and there
|
|
||||||
// might be some MPTokenIssuance objects which are deleted in the
|
|
||||||
// same transaction. Find the one matching this vault.
|
|
||||||
auto const deletedShares = [&]() -> std::optional<Shares> {
|
|
||||||
for (auto const& e : beforeMPTs_)
|
|
||||||
{
|
|
||||||
if (e.share.getMptID() == beforeVault.shareMPTID)
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}();
|
|
||||||
|
|
||||||
if (!deletedShares)
|
|
||||||
{
|
|
||||||
JLOG(j.fatal()) << "Invariant failed: deleted vault must also "
|
|
||||||
"delete shares";
|
|
||||||
XRPL_ASSERT(enforce, "xrpl::ValidVault::finalize : shares deletion invariant");
|
|
||||||
return !enforce; // That's all we can do here
|
|
||||||
}
|
|
||||||
|
|
||||||
bool result = true;
|
|
||||||
if (deletedShares->sharesTotal != 0)
|
|
||||||
{
|
|
||||||
JLOG(j.fatal()) << "Invariant failed: deleted vault must have no "
|
|
||||||
"shares outstanding";
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
if (beforeVault.assetsTotal != kZero)
|
|
||||||
{
|
|
||||||
JLOG(j.fatal()) << "Invariant failed: deleted vault must have no "
|
|
||||||
"assets outstanding";
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
if (beforeVault.assetsAvailable != kZero)
|
|
||||||
{
|
|
||||||
JLOG(j.fatal()) << "Invariant failed: deleted vault must have no "
|
|
||||||
"assets available";
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
if (txnType == ttVAULT_DELETE)
|
if (txnType == ttVAULT_DELETE)
|
||||||
{
|
{
|
||||||
@@ -544,61 +500,13 @@ ValidVault::finalize(
|
|||||||
result &= [&]() {
|
result &= [&]() {
|
||||||
switch (txnType)
|
switch (txnType)
|
||||||
{
|
{
|
||||||
case ttVAULT_CREATE: {
|
case ttVAULT_CREATE:
|
||||||
bool result = true;
|
case ttLOAN_SET:
|
||||||
|
case ttLOAN_MANAGE:
|
||||||
if (!beforeVault_.empty())
|
case ttLOAN_PAY:
|
||||||
{
|
// Create-specific checks live in VaultCreate::finalizeInvariants.
|
||||||
JLOG(j.fatal()) //
|
// Loan checks are TBD.
|
||||||
<< "Invariant failed: create operation must not have "
|
return true;
|
||||||
"updated a vault";
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (afterVault.assetsAvailable != kZero || afterVault.assetsTotal != kZero ||
|
|
||||||
afterVault.lossUnrealized != kZero || updatedShares->sharesTotal != 0)
|
|
||||||
{
|
|
||||||
JLOG(j.fatal()) //
|
|
||||||
<< "Invariant failed: created vault must be empty";
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (afterVault.pseudoId != updatedShares->share.getIssuer())
|
|
||||||
{
|
|
||||||
JLOG(j.fatal()) //
|
|
||||||
<< "Invariant failed: shares issuer and vault "
|
|
||||||
"pseudo-account must be the same";
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto const sleSharesIssuer =
|
|
||||||
view.read(keylet::account(updatedShares->share.getIssuer()));
|
|
||||||
if (!sleSharesIssuer)
|
|
||||||
{
|
|
||||||
JLOG(j.fatal()) //
|
|
||||||
<< "Invariant failed: shares issuer must exist";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isPseudoAccount(sleSharesIssuer))
|
|
||||||
{
|
|
||||||
JLOG(j.fatal()) //
|
|
||||||
<< "Invariant failed: shares issuer must be a "
|
|
||||||
"pseudo-account";
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto const vaultId = (*sleSharesIssuer)[~sfVaultID];
|
|
||||||
!vaultId || *vaultId != afterVault.key)
|
|
||||||
{
|
|
||||||
JLOG(j.fatal()) //
|
|
||||||
<< "Invariant failed: shares issuer pseudo-account "
|
|
||||||
"must point back to the vault";
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
case ttVAULT_SET: {
|
case ttVAULT_SET: {
|
||||||
bool result = true;
|
bool result = true;
|
||||||
|
|
||||||
@@ -1042,13 +950,6 @@ ValidVault::finalize(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
case ttLOAN_SET:
|
|
||||||
case ttLOAN_MANAGE:
|
|
||||||
case ttLOAN_PAY: {
|
|
||||||
// TBD
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
UNREACHABLE("xrpl::ValidVault::finalize : unknown transaction type");
|
UNREACHABLE("xrpl::ValidVault::finalize : unknown transaction type");
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
#include <xrpl/tx/invariants/VaultInvariantData.h>
|
||||||
|
|
||||||
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
|
#include <xrpl/protocol/Indexes.h>
|
||||||
|
#include <xrpl/protocol/LedgerFormats.h>
|
||||||
|
#include <xrpl/protocol/Protocol.h>
|
||||||
|
#include <xrpl/protocol/SField.h>
|
||||||
|
#include <xrpl/protocol/STLedgerEntry.h>
|
||||||
|
#include <xrpl/protocol/STNumber.h> // IWYU pragma: keep
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace xrpl {
|
||||||
|
|
||||||
|
VaultInvariantData::Vault
|
||||||
|
VaultInvariantData::Vault::make(SLE const& from)
|
||||||
|
{
|
||||||
|
XRPL_ASSERT(from.getType() == ltVAULT, "VaultInvariantData::Vault::make : from Vault object");
|
||||||
|
|
||||||
|
Vault self;
|
||||||
|
self.key = from.key();
|
||||||
|
self.asset = from.at(sfAsset);
|
||||||
|
self.pseudoId = from.getAccountID(sfAccount);
|
||||||
|
self.owner = from.at(sfOwner);
|
||||||
|
self.shareMPTID = from.getFieldH192(sfShareMPTID);
|
||||||
|
self.assetsTotal = from.at(sfAssetsTotal);
|
||||||
|
self.assetsAvailable = from.at(sfAssetsAvailable);
|
||||||
|
self.assetsMaximum = from.at(sfAssetsMaximum);
|
||||||
|
self.lossUnrealized = from.at(sfLossUnrealized);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
VaultInvariantData::Shares
|
||||||
|
VaultInvariantData::Shares::make(SLE const& from)
|
||||||
|
{
|
||||||
|
XRPL_ASSERT(
|
||||||
|
from.getType() == ltMPTOKEN_ISSUANCE,
|
||||||
|
"VaultInvariantData::Shares::make : from MPTokenIssuance object");
|
||||||
|
|
||||||
|
Shares self;
|
||||||
|
self.share = MPTIssue(makeMptID(from.getFieldU32(sfSequence), from.getAccountID(sfIssuer)));
|
||||||
|
self.sharesTotal = from.getFieldU64(sfOutstandingAmount);
|
||||||
|
self.sharesMaximum = from[~sfMaximumAmount].value_or(kMaxMpTokenAmount);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
VaultInvariantData::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after)
|
||||||
|
{
|
||||||
|
XRPL_ASSERT(
|
||||||
|
after != nullptr && (before != nullptr || !isDelete),
|
||||||
|
"xrpl::VaultInvariantData::visitEntry : some object is available");
|
||||||
|
|
||||||
|
if (before && before->getType() == ltVAULT)
|
||||||
|
beforeVault_.push_back(Vault::make(*before));
|
||||||
|
|
||||||
|
if (before && before->getType() == ltMPTOKEN_ISSUANCE)
|
||||||
|
beforeMPTs_.push_back(Shares::make(*before));
|
||||||
|
|
||||||
|
if (!isDelete && after)
|
||||||
|
{
|
||||||
|
switch (after->getType())
|
||||||
|
{
|
||||||
|
case ltVAULT:
|
||||||
|
afterVault_.push_back(Vault::make(*after));
|
||||||
|
break;
|
||||||
|
case ltMPTOKEN_ISSUANCE:
|
||||||
|
afterMPTs_.push_back(Shares::make(*after));
|
||||||
|
break;
|
||||||
|
default:;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<VaultInvariantData::Shares>
|
||||||
|
VaultInvariantData::findShares(uint192 const& mptID) const
|
||||||
|
{
|
||||||
|
for (auto const& s : afterMPTs_)
|
||||||
|
{
|
||||||
|
if (s.share.getMptID() == mptID)
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<VaultInvariantData::Shares>
|
||||||
|
VaultInvariantData::findDeletedShares(uint192 const& mptID) const
|
||||||
|
{
|
||||||
|
for (auto const& s : beforeMPTs_)
|
||||||
|
{
|
||||||
|
if (s.share.getMptID() == mptID)
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xrpl
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
#include <xrpl/tx/transactors/vault/VaultCreate.h>
|
#include <xrpl/tx/transactors/vault/VaultCreate.h>
|
||||||
|
|
||||||
|
#include <xrpl/basics/Log.h>
|
||||||
#include <xrpl/basics/Number.h>
|
#include <xrpl/basics/Number.h>
|
||||||
#include <xrpl/basics/base_uint.h>
|
#include <xrpl/basics/base_uint.h>
|
||||||
#include <xrpl/beast/utility/Zero.h>
|
#include <xrpl/beast/utility/Zero.h>
|
||||||
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
#include <xrpl/core/ServiceRegistry.h>
|
#include <xrpl/core/ServiceRegistry.h>
|
||||||
#include <xrpl/ledger/View.h>
|
#include <xrpl/ledger/View.h>
|
||||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||||
@@ -263,15 +265,107 @@ VaultCreate::doApply()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
VaultCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
VaultCreate::visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after)
|
||||||
{
|
{
|
||||||
// No transaction-specific invariants yet (future work).
|
data_.visitEntry(isDelete, before, after);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
VaultCreate::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
VaultCreate::finalizeInvariants(
|
||||||
|
STTx const&,
|
||||||
|
TER result,
|
||||||
|
XRPAmount,
|
||||||
|
ReadView const& view,
|
||||||
|
beast::Journal const& j)
|
||||||
{
|
{
|
||||||
// No transaction-specific invariants yet (future work).
|
bool const enforce = view.rules().enabled(featureSingleAssetVault);
|
||||||
|
|
||||||
|
if (!isTesSuccess(result))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
auto const& afterVaults = data_.afterVaults();
|
||||||
|
if (afterVaults.empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
auto const& afterVault = afterVaults[0];
|
||||||
|
bool checkResult = true;
|
||||||
|
|
||||||
|
if (!data_.beforeVaults().empty())
|
||||||
|
{
|
||||||
|
JLOG(j.fatal()) << "Invariant failed: create operation must not have updated a vault";
|
||||||
|
checkResult = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The MPTokenIssuance may not be in the modified set (e.g. only the vault
|
||||||
|
// was touched in the test), so fall back to a view read if needed.
|
||||||
|
auto const updatedShares = [&]() -> std::optional<VaultInvariantData::Shares> {
|
||||||
|
if (auto found = data_.findShares(afterVault.shareMPTID))
|
||||||
|
return found;
|
||||||
|
auto const sleShares = view.read(keylet::mptIssuance(afterVault.shareMPTID));
|
||||||
|
return sleShares ? std::optional<VaultInvariantData::Shares>(
|
||||||
|
VaultInvariantData::Shares::make(*sleShares))
|
||||||
|
: std::nullopt;
|
||||||
|
}();
|
||||||
|
|
||||||
|
static constexpr Number kZero{};
|
||||||
|
|
||||||
|
if (afterVault.assetsAvailable != kZero || afterVault.assetsTotal != kZero ||
|
||||||
|
afterVault.lossUnrealized != kZero || (updatedShares && updatedShares->sharesTotal != 0))
|
||||||
|
{
|
||||||
|
JLOG(j.fatal()) << "Invariant failed: created vault must be empty";
|
||||||
|
checkResult = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!updatedShares)
|
||||||
|
{
|
||||||
|
JLOG(j.fatal()) << "Invariant failed: updated vault must have shares";
|
||||||
|
XRPL_ASSERT(enforce, "xrpl::VaultCreate::finalizeInvariants : vault has shares invariant");
|
||||||
|
if (!checkResult)
|
||||||
|
{
|
||||||
|
XRPL_ASSERT(enforce, "xrpl::VaultCreate::finalizeInvariants : vault create invariants");
|
||||||
|
}
|
||||||
|
return !enforce;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (afterVault.pseudoId != updatedShares->share.getIssuer())
|
||||||
|
{
|
||||||
|
JLOG(j.fatal())
|
||||||
|
<< "Invariant failed: shares issuer and vault pseudo-account must be the same";
|
||||||
|
checkResult = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const sleSharesIssuer = view.read(keylet::account(updatedShares->share.getIssuer()));
|
||||||
|
if (!sleSharesIssuer)
|
||||||
|
{
|
||||||
|
JLOG(j.fatal()) << "Invariant failed: shares issuer must exist";
|
||||||
|
XRPL_ASSERT(
|
||||||
|
enforce, "xrpl::VaultCreate::finalizeInvariants : shares issuer exists invariant");
|
||||||
|
if (!checkResult)
|
||||||
|
{
|
||||||
|
XRPL_ASSERT(enforce, "xrpl::VaultCreate::finalizeInvariants : vault create invariants");
|
||||||
|
}
|
||||||
|
return !enforce;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isPseudoAccount(sleSharesIssuer))
|
||||||
|
{
|
||||||
|
JLOG(j.fatal()) << "Invariant failed: shares issuer must be a pseudo-account";
|
||||||
|
checkResult = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto const vaultId = (*sleSharesIssuer)[~sfVaultID]; !vaultId || *vaultId != afterVault.key)
|
||||||
|
{
|
||||||
|
JLOG(j.fatal())
|
||||||
|
<< "Invariant failed: shares issuer pseudo-account must point back to the vault";
|
||||||
|
checkResult = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkResult)
|
||||||
|
{
|
||||||
|
XRPL_ASSERT(enforce, "xrpl::VaultCreate::finalizeInvariants : vault create invariants");
|
||||||
|
return !enforce;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
#include <xrpl/tx/transactors/vault/VaultDelete.h>
|
#include <xrpl/tx/transactors/vault/VaultDelete.h>
|
||||||
|
|
||||||
#include <xrpl/basics/Log.h>
|
#include <xrpl/basics/Log.h>
|
||||||
|
#include <xrpl/basics/Number.h>
|
||||||
#include <xrpl/basics/base_uint.h>
|
#include <xrpl/basics/base_uint.h>
|
||||||
#include <xrpl/beast/utility/Zero.h>
|
#include <xrpl/beast/utility/Zero.h>
|
||||||
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
|
#include <xrpl/ledger/ReadView.h>
|
||||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||||
#include <xrpl/protocol/AccountID.h>
|
#include <xrpl/protocol/AccountID.h>
|
||||||
|
#include <xrpl/protocol/Feature.h>
|
||||||
#include <xrpl/protocol/Indexes.h>
|
#include <xrpl/protocol/Indexes.h>
|
||||||
#include <xrpl/protocol/MPTIssue.h>
|
#include <xrpl/protocol/MPTIssue.h>
|
||||||
#include <xrpl/protocol/SField.h>
|
#include <xrpl/protocol/SField.h>
|
||||||
@@ -211,15 +215,76 @@ VaultDelete::doApply()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
VaultDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
VaultDelete::visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after)
|
||||||
{
|
{
|
||||||
// No transaction-specific invariants yet (future work).
|
data_.visitEntry(isDelete, before, after);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
VaultDelete::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
VaultDelete::finalizeInvariants(
|
||||||
|
STTx const&,
|
||||||
|
TER result,
|
||||||
|
XRPAmount,
|
||||||
|
ReadView const& view,
|
||||||
|
beast::Journal const& j)
|
||||||
{
|
{
|
||||||
// No transaction-specific invariants yet (future work).
|
bool const enforce = view.rules().enabled(featureSingleAssetVault);
|
||||||
|
|
||||||
|
if (!isTesSuccess(result))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
static constexpr Number kZero{};
|
||||||
|
|
||||||
|
// VaultDelete must have deleted the vault; if it still exists, something
|
||||||
|
// went wrong.
|
||||||
|
if (!data_.afterVaults().empty())
|
||||||
|
{
|
||||||
|
JLOG(j.fatal()) << "Invariant failed: vault not deleted by VaultDelete";
|
||||||
|
XRPL_ASSERT(enforce, "xrpl::VaultDelete::finalizeInvariants : vault not deleted invariant");
|
||||||
|
return !enforce;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing to check if the vault was never in the before-set (e.g. a
|
||||||
|
// test that omits the vault entry entirely).
|
||||||
|
if (data_.beforeVaults().empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
auto const& beforeVault = data_.beforeVaults()[0];
|
||||||
|
|
||||||
|
auto const deletedShares = data_.findDeletedShares(beforeVault.shareMPTID);
|
||||||
|
if (!deletedShares)
|
||||||
|
{
|
||||||
|
JLOG(j.fatal()) << "Invariant failed: deleted vault must also delete shares";
|
||||||
|
XRPL_ASSERT(enforce, "xrpl::VaultDelete::finalizeInvariants : shares deletion invariant");
|
||||||
|
return !enforce;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result2 = true;
|
||||||
|
|
||||||
|
if (deletedShares->sharesTotal != 0)
|
||||||
|
{
|
||||||
|
JLOG(j.fatal()) << "Invariant failed: deleted vault must have no shares outstanding";
|
||||||
|
result2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (beforeVault.assetsTotal != kZero)
|
||||||
|
{
|
||||||
|
JLOG(j.fatal()) << "Invariant failed: deleted vault must have no assets outstanding";
|
||||||
|
result2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (beforeVault.assetsAvailable != kZero)
|
||||||
|
{
|
||||||
|
JLOG(j.fatal()) << "Invariant failed: deleted vault must have no assets available";
|
||||||
|
result2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result2)
|
||||||
|
{
|
||||||
|
XRPL_ASSERT(enforce, "xrpl::VaultDelete::finalizeInvariants : vault delete invariants");
|
||||||
|
return !enforce;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user