Compare commits

...

3 Commits

Author SHA1 Message Date
Bronek Kozicki
b4388f3341 Add invariants for LoanManage 2025-10-15 14:07:41 +01:00
Bronek Kozicki
3f1f37f323 Add invariants for LoanPay 2025-10-15 13:30:39 +01:00
Bronek Kozicki
2d37254504 Initial implementation of Vault invariants for Loan operations 2025-10-10 15:14:32 +01:00
2 changed files with 194 additions and 11 deletions

View File

@@ -2857,7 +2857,7 @@ ValidVault::finalize(
if (!beforeVault_.empty() &&
afterVault.lossUnrealized != beforeVault_[0].lossUnrealized &&
tx.getTxnType() != ttLOAN_MANAGE)
txnType != ttLOAN_MANAGE && txnType != ttLOAN_PAY)
{
JLOG(j.fatal()) << //
"Invariant failed: vault transaction must not change loss "
@@ -2879,9 +2879,9 @@ ValidVault::finalize(
}();
if (!beforeShares &&
(tx.getTxnType() == ttVAULT_DEPOSIT || //
tx.getTxnType() == ttVAULT_WITHDRAW || //
tx.getTxnType() == ttVAULT_CLAWBACK))
(txnType == ttVAULT_DEPOSIT || //
txnType == ttVAULT_WITHDRAW || //
txnType == ttVAULT_CLAWBACK))
{
JLOG(j.fatal()) << "Invariant failed: vault operation succeeded "
"without updating shares";
@@ -3414,17 +3414,195 @@ ValidVault::finalize(
return result;
}
case ttLOAN_SET:
case ttLOAN_MANAGE:
case ttLOAN_PAY: {
// TBD
return true;
case ttLOAN_SET: {
bool result = true;
if (beforeShares)
{
JLOG(j.fatal()) //
<< "Invariant failed: loan must not have updated vault "
"shares";
result = false;
}
XRPL_ASSERT(
!beforeVault_.empty(),
"ripple::ValidVault::finalize : loan updated a vault");
auto const& beforeVault = beforeVault_[0];
auto const vaultDeltaAssets = deltaAssets(afterVault.pseudoId);
if (!vaultDeltaAssets)
{
JLOG(j.fatal())
<< "Invariant failed: loan must change vault balance";
return false; // That's all we can do
}
if (*vaultDeltaAssets >= zero)
{
JLOG(j.fatal()) << "Invariant failed: loan must "
"decrease vault balance";
result = false;
}
auto const loan =
beforeVault.assetsAvailable - afterVault.assetsAvailable;
if (loan <= zero)
{
JLOG(j.fatal())
<< "Invariant failed: loan must be greater than zero";
result = false;
}
if (*vaultDeltaAssets * -1 != loan)
{
JLOG(j.fatal()) << "Invariant failed: loan must agree with "
"change of assets available";
result = false;
}
if (afterVault.assetsTotal < beforeVault.assetsTotal)
{
JLOG(j.fatal()) << "Invariant failed: loan must not "
"decrease assets outstanding";
result = false;
}
return result;
}
case ttLOAN_PAY: {
bool result = true;
if (beforeShares)
{
JLOG(j.fatal()) //
<< "Invariant failed: loan must not have updated vault "
"shares";
result = false;
}
XRPL_ASSERT(
!beforeVault_.empty(),
"ripple::ValidVault::finalize : payment updated a vault");
// auto const& beforeVault = beforeVault_[0];
auto const vaultDeltaAssets = deltaAssets(afterVault.pseudoId);
if (!vaultDeltaAssets)
{
JLOG(j.fatal()) << "Invariant failed: payment must change "
"vault balance";
return false; // That's all we can do
}
if (*vaultDeltaAssets <= zero)
{
JLOG(j.fatal()) << "Invariant failed: payment must "
"increase vault balance";
result = false;
}
XRPL_ASSERT(
!beforeVault_.empty(),
"ripple::ValidVault::finalize : payment updated a vault");
auto const& beforeVault = beforeVault_[0];
// TODO: this is failing Loan tests, why ?
// if (afterVault.assetsTotal < beforeVault.assetsTotal)
// {
// JLOG(j.fatal()) << "Invariant failed: payment must not "
// "decrease assets outstanding";
// result = false;
// }
if (beforeVault.assetsAvailable + *vaultDeltaAssets !=
afterVault.assetsAvailable)
{
JLOG(j.fatal())
<< "Invariant failed: payment must agree with "
"change of assets available";
result = false;
}
return result;
}
case ttLOAN_MANAGE: {
bool result = true;
if (beforeShares)
{
JLOG(j.fatal()) //
<< "Invariant failed: loan management must not have "
"updated vault shares";
result = false;
}
XRPL_ASSERT(
!beforeVault_.empty(),
"ripple::ValidVault::finalize : loan manage updated a "
"vault");
auto const& beforeVault = beforeVault_[0];
if (auto const vaultDeltaAssets =
deltaAssets(afterVault.pseudoId);
vaultDeltaAssets)
{
if (*vaultDeltaAssets < zero)
{
JLOG(j.fatal())
<< "Invariant failed: loan manage must not "
"decrease vault balance";
result = false;
}
if (beforeVault.assetsAvailable + *vaultDeltaAssets !=
afterVault.assetsAvailable)
{
JLOG(j.fatal())
<< "Invariant failed: loan manage must agree "
"with change in assets avalable";
result = false;
}
if (beforeVault.assetsTotal < afterVault.assetsTotal)
{
JLOG(j.fatal())
<< "Invariant failed: loan manage must not "
"increase assets outstanding";
result = false;
}
}
else
{
if (beforeVault.assetsAvailable !=
afterVault.assetsAvailable)
{
JLOG(j.fatal())
<< "Invariant failed: loan manage must not "
"change assets available without altering "
"vault balance";
result = false;
}
if (beforeVault.assetsTotal != afterVault.assetsTotal)
{
JLOG(j.fatal())
<< "Invariant failed: loan manage must not "
"change assets outstanding without altering "
"vault balance";
result = false;
}
}
return result;
}
default:
// LCOV_EXCL_START
default:
UNREACHABLE(
"ripple::ValidVault::finalize : unknown transaction type");
"ripple::ValidVault::finalize : unknown transaction "
"type");
return false;
// LCOV_EXCL_STOP
}

View File

@@ -257,6 +257,11 @@ LoanPay::doApply()
paymentParts->valueChange,
managementFeeRate,
originalPrincipalRequested);
// TODO: this is crashing Loan tests, why ?
// XRPL_ASSERT(
// vaultValueChange >= 0,
// "ripple::LoanPay::doApply : positive loan change");
// debtDecrease may be negative, increasing the debt
auto const debtDecrease = totalPaidToVault - vaultValueChange;
XRPL_ASSERT_PARTS(