288 bool const enforce = view.
rules().
enabled(featureSingleAssetVault);
298 "Invariant failed: vault operation succeeded without modifying "
300 XRPL_ASSERT(enforce,
"xrpl::ValidVault::finalize : vault noop invariant");
309 "Invariant failed: vault updated by a wrong transaction type";
312 "xrpl::ValidVault::finalize : illegal vault transaction "
320 "Invariant failed: vault operation updated more than single vault";
321 XRPL_ASSERT(enforce,
"xrpl::ValidVault::finalize : single vault invariant");
331 if (txnType != ttVAULT_DELETE)
334 "Invariant failed: vault deleted by a wrong transaction type";
337 "xrpl::ValidVault::finalize : illegal vault deletion "
352 if (e.share.getMptID() == beforeVault.shareMPTID)
360 JLOG(j.
fatal()) <<
"Invariant failed: deleted vault must also "
362 XRPL_ASSERT(enforce,
"xrpl::ValidVault::finalize : shares deletion invariant");
367 if (deletedShares->sharesTotal != 0)
369 JLOG(j.
fatal()) <<
"Invariant failed: deleted vault must have no "
370 "shares outstanding";
373 if (beforeVault.assetsTotal !=
kZero)
375 JLOG(j.
fatal()) <<
"Invariant failed: deleted vault must have no "
376 "assets outstanding";
379 if (beforeVault.assetsAvailable !=
kZero)
381 JLOG(j.
fatal()) <<
"Invariant failed: deleted vault must have no "
388 if (txnType == ttVAULT_DELETE)
390 JLOG(j.
fatal()) <<
"Invariant failed: vault deletion succeeded without "
392 XRPL_ASSERT(enforce,
"xrpl::ValidVault::finalize : vault deletion invariant");
400 "xrpl::ValidVault::finalize : single vault operation");
410 if (e.share.getMptID() == afterVault.shareMPTID)
425 if (afterVault.asset != beforeVault.asset || afterVault.pseudoId != beforeVault.pseudoId ||
426 afterVault.shareMPTID != beforeVault.shareMPTID)
428 JLOG(j.
fatal()) <<
"Invariant failed: violation of vault immutable data";
435 JLOG(j.
fatal()) <<
"Invariant failed: updated vault must have shares";
436 XRPL_ASSERT(enforce,
"xrpl::ValidVault::finalize : vault has shares invariant");
440 if (updatedShares->sharesTotal == 0)
442 if (afterVault.assetsTotal !=
kZero)
444 JLOG(j.
fatal()) <<
"Invariant failed: updated zero sized "
445 "vault must have no assets outstanding";
448 if (afterVault.assetsAvailable !=
kZero)
450 JLOG(j.
fatal()) <<
"Invariant failed: updated zero sized "
451 "vault must have no assets available";
455 else if (updatedShares->sharesTotal > updatedShares->sharesMaximum)
458 <<
"Invariant failed: updated shares must not exceed maximum "
459 << updatedShares->sharesMaximum;
463 if (afterVault.assetsAvailable <
kZero)
465 JLOG(j.
fatal()) <<
"Invariant failed: assets available must be positive";
469 if (afterVault.assetsAvailable > afterVault.assetsTotal)
471 JLOG(j.
fatal()) <<
"Invariant failed: assets available must "
472 "not be greater than assets outstanding";
475 else if (afterVault.lossUnrealized > afterVault.assetsTotal - afterVault.assetsAvailable)
478 <<
"Invariant failed: loss unrealized must not exceed "
479 "the difference between assets outstanding and available";
483 if (afterVault.assetsTotal <
kZero)
485 JLOG(j.
fatal()) <<
"Invariant failed: assets outstanding must be positive";
489 if (afterVault.assetsMaximum <
kZero)
491 JLOG(j.
fatal()) <<
"Invariant failed: assets maximum must be positive";
500 "Invariant failed: vault created by a wrong transaction type";
501 XRPL_ASSERT(enforce,
"xrpl::ValidVault::finalize : vault creation invariant");
506 txnType != ttLOAN_MANAGE && txnType != ttLOAN_PAY)
509 "Invariant failed: vault transaction must not change loss "
521 if (e.share.getMptID() == beforeVault.shareMPTID)
532 JLOG(j.
fatal()) <<
"Invariant failed: vault operation succeeded "
533 "without updating shares";
534 XRPL_ASSERT(enforce,
"xrpl::ValidVault::finalize : shares noop invariant");
538 auto const& vaultAsset = afterVault.asset;
547 case ttVAULT_CREATE: {
553 <<
"Invariant failed: create operation must not have "
558 if (afterVault.assetsAvailable !=
kZero || afterVault.assetsTotal !=
kZero ||
559 afterVault.lossUnrealized !=
kZero || updatedShares->sharesTotal != 0)
562 <<
"Invariant failed: created vault must be empty";
566 if (afterVault.pseudoId != updatedShares->share.getIssuer())
569 <<
"Invariant failed: shares issuer and vault "
570 "pseudo-account must be the same";
574 auto const sleSharesIssuer =
576 if (!sleSharesIssuer)
579 <<
"Invariant failed: shares issuer must exist";
586 <<
"Invariant failed: shares issuer must be a "
591 if (
auto const vaultId = (*sleSharesIssuer)[~sfVaultID];
592 !vaultId || *vaultId != afterVault.key)
595 <<
"Invariant failed: shares issuer pseudo-account "
596 "must point back to the vault";
606 !
beforeVault_.empty(),
"xrpl::ValidVault::finalize : set updated a vault");
609 auto const vaultDeltaAssets =
deltaAssets(afterVault.pseudoId);
610 if (vaultDeltaAssets)
613 "Invariant failed: set must not change vault balance";
617 if (beforeVault.assetsTotal != afterVault.assetsTotal)
620 "Invariant failed: set must not change assets "
625 if (afterVault.assetsMaximum >
kZero &&
626 afterVault.assetsTotal > afterVault.assetsMaximum)
629 "Invariant failed: set assets outstanding must not "
630 "exceed assets maximum";
634 if (beforeVault.assetsAvailable != afterVault.assetsAvailable)
637 "Invariant failed: set must not change assets "
642 if (beforeShares && updatedShares &&
643 beforeShares->sharesTotal != updatedShares->sharesTotal)
646 "Invariant failed: set must not change shares "
653 case ttVAULT_DEPOSIT: {
657 !
beforeVault_.empty(),
"xrpl::ValidVault::finalize : deposit updated a vault");
660 auto const maybeVaultDeltaAssets =
deltaAssets(afterVault.pseudoId);
661 if (!maybeVaultDeltaAssets)
664 "Invariant failed: deposit must change vault balance";
671 auto const vaultDeltaAssets =
672 roundToAsset(vaultAsset, maybeVaultDeltaAssets->delta, minScale);
673 auto const txAmount =
roundToAsset(vaultAsset, tx[sfAmount], minScale);
675 if (vaultDeltaAssets > txAmount)
678 "Invariant failed: deposit must not change vault "
679 "balance by more than deposited amount";
683 if (vaultDeltaAssets <=
kZero)
686 "Invariant failed: deposit must increase vault balance";
692 bool const issuerDeposit = [&]() ->
bool {
693 if (vaultAsset.native())
695 return tx[sfAccount] == vaultAsset.getIssuer();
701 if (!maybeAccDeltaAssets)
704 <<
"Invariant failed: deposit must change depositor balance";
707 auto const localMinScale =
710 auto const accountDeltaAssets =
711 roundToAsset(vaultAsset, maybeAccDeltaAssets->delta, localMinScale);
712 auto const localVaultDeltaAssets =
713 roundToAsset(vaultAsset, vaultDeltaAssets, localMinScale);
718 if (accountDeltaAssets >=
kZero)
721 <<
"Invariant failed: deposit must decrease depositor balance";
725 if (localVaultDeltaAssets * -1 != accountDeltaAssets)
727 JLOG(j.
fatal()) <<
"Invariant failed: " <<
728 "deposit must change vault and depositor balance by equal amount";
733 if (afterVault.assetsMaximum >
kZero &&
734 afterVault.assetsTotal > afterVault.assetsMaximum)
736 JLOG(j.
fatal()) <<
"Invariant failed: " <<
737 "deposit assets outstanding must not exceed assets maximum";
741 auto const maybeAccDeltaShares =
deltaShares(tx[sfAccount]);
742 if (!maybeAccDeltaShares)
744 JLOG(j.
fatal()) <<
"Invariant failed: deposit must change depositor shares";
748 auto const& accountDeltaShares = *maybeAccDeltaShares;
749 if (accountDeltaShares.delta <=
kZero)
751 JLOG(j.
fatal()) <<
"Invariant failed: deposit must increase depositor shares";
755 auto const maybeVaultDeltaShares =
deltaShares(afterVault.pseudoId);
756 if (!maybeVaultDeltaShares || maybeVaultDeltaShares->delta ==
kZero)
758 JLOG(j.
fatal()) <<
"Invariant failed: deposit must change vault shares";
763 auto const& vaultDeltaShares = *maybeVaultDeltaShares;
764 if (vaultDeltaShares.delta * -1 != accountDeltaShares.delta)
766 JLOG(j.
fatal()) <<
"Invariant failed: " <<
767 "deposit must change depositor and vault shares by equal amount";
772 vaultAsset, afterVault.assetsTotal - beforeVault.assetsTotal, minScale);
773 if (assetTotalDelta != vaultDeltaAssets)
776 <<
"Invariant failed: deposit and assets outstanding must add up";
781 vaultAsset, afterVault.assetsAvailable - beforeVault.assetsAvailable, minScale);
782 if (assetAvailableDelta != vaultDeltaAssets)
784 JLOG(j.
fatal()) <<
"Invariant failed: deposit and assets available must add up";
790 case ttVAULT_WITHDRAW: {
795 "xrpl::ValidVault::finalize : withdrawal updated a vault");
798 auto const maybeVaultDeltaAssets =
deltaAssets(afterVault.pseudoId);
799 if (!maybeVaultDeltaAssets)
801 JLOG(j.
fatal()) <<
"Invariant failed: withdrawal must change vault balance";
808 auto const vaultPseudoDeltaAssets =
809 roundToAsset(vaultAsset, maybeVaultDeltaAssets->delta, minScale);
811 if (vaultPseudoDeltaAssets >=
kZero)
813 JLOG(j.
fatal()) <<
"Invariant failed: withdrawal must decrease vault balance";
819 bool const issuerWithdrawal = [&]() ->
bool {
820 if (vaultAsset.native())
822 auto const destination = tx[~sfDestination].value_or(tx[sfAccount]);
823 return destination == vaultAsset.getIssuer();
826 if (!issuerWithdrawal)
830 if (
auto const destination = tx[~sfDestination];
831 destination && *destination != tx[sfAccount])
836 if (maybeAccDelta.has_value() == maybeOtherAccDelta.has_value())
839 "Invariant failed: withdrawal must change one destination balance";
843 auto const destinationDelta =
844 maybeAccDelta ? *maybeAccDelta : *maybeOtherAccDelta;
849 auto const localMinScale =
std::max(minScale, destinationScale);
851 auto const roundedDestinationDelta =
852 roundToAsset(vaultAsset, destinationDelta.delta, localMinScale);
860 bool const tolerateZeroDelta =
861 view.
rules().
enabled(fixCleanup3_2_0) && !vaultAsset.integral();
862 auto const invalidBalanceChange = tolerateZeroDelta
863 ? roundedDestinationDelta <
kZero
864 : roundedDestinationDelta <=
kZero;
865 if (invalidBalanceChange)
868 "Invariant failed: withdrawal must increase destination balance";
872 auto const localPseudoDeltaAssets =
873 roundToAsset(vaultAsset, vaultPseudoDeltaAssets, localMinScale);
882 auto const destroyedIsSubUlp = tolerateZeroDelta &&
885 maybeVaultDeltaAssets->delta * -1 - destinationDelta.delta,
888 if (!destroyedIsSubUlp &&
889 localPseudoDeltaAssets * -1 != roundedDestinationDelta)
891 JLOG(j.
fatal()) <<
"Invariant failed: " <<
892 "withdrawal must change vault and destination balance by equal "
899 auto const accountDeltaShares =
deltaShares(tx[sfAccount]);
900 if (!accountDeltaShares)
902 JLOG(j.
fatal()) <<
"Invariant failed: withdrawal must change depositor shares";
906 if (accountDeltaShares->delta >=
kZero)
909 <<
"Invariant failed: withdrawal must decrease depositor shares";
914 auto const vaultDeltaShares =
deltaShares(afterVault.pseudoId);
915 if (!vaultDeltaShares || vaultDeltaShares->delta ==
kZero)
917 JLOG(j.
fatal()) <<
"Invariant failed: withdrawal must change vault shares";
921 if (vaultDeltaShares->delta * -1 != accountDeltaShares->delta)
923 JLOG(j.
fatal()) <<
"Invariant failed: " <<
924 "withdrawal must change depositor and vault shares by equal amount";
929 vaultAsset, afterVault.assetsTotal - beforeVault.assetsTotal, minScale);
931 if (assetTotalDelta != vaultPseudoDeltaAssets)
934 <<
"Invariant failed: withdrawal and assets outstanding must add up";
939 vaultAsset, afterVault.assetsAvailable - beforeVault.assetsAvailable, minScale);
941 if (assetAvailableDelta != vaultPseudoDeltaAssets)
944 <<
"Invariant failed: withdrawal and assets available must add up";
950 case ttVAULT_CLAWBACK: {
954 !
beforeVault_.empty(),
"xrpl::ValidVault::finalize : clawback updated a vault");
957 if (vaultAsset.native() || vaultAsset.getIssuer() != tx[sfAccount])
961 if (!(beforeShares && beforeShares->sharesTotal > 0 &&
962 isVaultEmpty(beforeVault) && beforeVault.owner == tx[sfAccount]))
964 JLOG(j.
fatal()) <<
"Invariant failed: " <<
965 "clawback may only be performed by the asset issuer, or by the vault "
966 "owner of an empty vault";
971 auto const maybeVaultDeltaAssets =
deltaAssets(afterVault.pseudoId);
972 if (maybeVaultDeltaAssets)
974 auto const minScale =
976 auto const vaultDeltaAssets =
977 roundToAsset(vaultAsset, maybeVaultDeltaAssets->delta, minScale);
978 if (vaultDeltaAssets >=
kZero)
980 JLOG(j.
fatal()) <<
"Invariant failed: clawback must decrease vault balance";
985 vaultAsset, afterVault.assetsTotal - beforeVault.assetsTotal, minScale);
986 if (assetsTotalDelta != vaultDeltaAssets)
989 "Invariant failed: clawback and assets outstanding must add up";
995 afterVault.assetsAvailable - beforeVault.assetsAvailable,
997 if (assetAvailableDelta != vaultDeltaAssets)
1000 "Invariant failed: clawback and assets available must add up";
1007 "Invariant failed: clawback must change vault balance";
1012 auto const maybeAccountDeltaShares =
deltaShares(tx[sfHolder]);
1013 if (!maybeAccountDeltaShares)
1016 "Invariant failed: clawback must change holder shares";
1019 if (maybeAccountDeltaShares->delta >=
kZero)
1022 "Invariant failed: clawback must decrease holder shares";
1027 auto const vaultDeltaShares =
deltaShares(afterVault.pseudoId);
1028 if (!vaultDeltaShares || vaultDeltaShares->delta ==
kZero)
1031 "Invariant failed: clawback must change vault shares";
1035 if (vaultDeltaShares->delta * -1 != maybeAccountDeltaShares->delta)
1037 JLOG(j.
fatal()) <<
"Invariant failed: " <<
1038 "clawback must change holder and vault shares by equal amount";
1054 UNREACHABLE(
"xrpl::ValidVault::finalize : unknown transaction type");
1064 XRPL_ASSERT(enforce,
"xrpl::ValidVault::finalize : vault invariants");