mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 19:15:54 +00:00
progress
This commit is contained in:
committed by
Bronek Kozicki
parent
286612cf19
commit
6046fa239c
@@ -236,6 +236,9 @@ public:
|
|||||||
STAmount&
|
STAmount&
|
||||||
operator=(XRPAmount const& amount);
|
operator=(XRPAmount const& amount);
|
||||||
|
|
||||||
|
STAmount&
|
||||||
|
operator=(Number const&);
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// Modification
|
// Modification
|
||||||
@@ -547,6 +550,15 @@ STAmount::operator=(XRPAmount const& amount)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline STAmount&
|
||||||
|
STAmount::operator=(Number const& number)
|
||||||
|
{
|
||||||
|
mIsNegative = number.mantissa() < 0;
|
||||||
|
mValue = mIsNegative ? -number.mantissa() : number.mantissa();
|
||||||
|
mOffset = number.exponent();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
STAmount::negate()
|
STAmount::negate()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -239,6 +239,7 @@ class Vault_test : public beast::unit_test::suite
|
|||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
{
|
{
|
||||||
|
// Deposit non-zero amount.
|
||||||
auto tx = vault.deposit(
|
auto tx = vault.deposit(
|
||||||
{.depositor = depositor,
|
{.depositor = depositor,
|
||||||
.id = keylet.key,
|
.id = keylet.key,
|
||||||
@@ -255,7 +256,7 @@ class Vault_test : public beast::unit_test::suite
|
|||||||
mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
|
mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
|
||||||
Asset asset = mptt.issuanceID();
|
Asset asset = mptt.issuanceID();
|
||||||
// Fund depositor with asset.
|
// Fund depositor with asset.
|
||||||
mptt.authorize({ .account = depositor });
|
mptt.authorize({.account = depositor});
|
||||||
env(pay(issuer, depositor, asset(1000)));
|
env(pay(issuer, depositor, asset(1000)));
|
||||||
// Create vault.
|
// Create vault.
|
||||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||||
@@ -274,11 +275,103 @@ class Vault_test : public beast::unit_test::suite
|
|||||||
BEAST_EXPECT(true);
|
BEAST_EXPECT(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE(Sequence)
|
||||||
|
{
|
||||||
|
using namespace test::jtx;
|
||||||
|
Env env{*this};
|
||||||
|
|
||||||
|
Account issuer{"issuer"};
|
||||||
|
Account owner{"owner"};
|
||||||
|
Account depositor{"depositor"};
|
||||||
|
env.fund(XRP(1000), issuer, owner, depositor);
|
||||||
|
env.close();
|
||||||
|
auto vault = env.vault();
|
||||||
|
|
||||||
|
SUBCASE("IOU")
|
||||||
|
{
|
||||||
|
// Construct asset.
|
||||||
|
Asset asset = issuer["IOU"];
|
||||||
|
// Fund depositor with asset.
|
||||||
|
env.trust(asset(1000), depositor);
|
||||||
|
env(pay(issuer, depositor, asset(1000)));
|
||||||
|
// Create vault.
|
||||||
|
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||||
|
env(tx);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
{
|
||||||
|
// Deposit non-zero amount.
|
||||||
|
auto tx = vault.deposit(
|
||||||
|
{.depositor = depositor,
|
||||||
|
.id = keylet.key,
|
||||||
|
.amount = asset(123)});
|
||||||
|
env(tx);
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Fail to set maximum lower than current amount.
|
||||||
|
auto tx = vault.set({.owner = owner, .id = keylet.key});
|
||||||
|
tx[sfAssetMaximum] = 100;
|
||||||
|
env(tx, ter(tecLIMIT_EXCEEDED));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Set maximum higher than current amount.
|
||||||
|
auto tx = vault.set({.owner = owner, .id = keylet.key});
|
||||||
|
tx[sfAssetMaximum] = 200;
|
||||||
|
env(tx);
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Fail to deposit more than maximum.
|
||||||
|
auto tx = vault.deposit(
|
||||||
|
{.depositor = depositor,
|
||||||
|
.id = keylet.key,
|
||||||
|
.amount = asset(100)});
|
||||||
|
env(tx, ter(tecLIMIT_EXCEEDED));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Fail to delete non-empty vault.
|
||||||
|
auto tx = vault.del({.owner = owner, .id = keylet.key});
|
||||||
|
env(tx, ter(tecHAS_OBLIGATIONS));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Fail to deposit more than assets held.
|
||||||
|
auto tx = vault.deposit(
|
||||||
|
{.depositor = depositor,
|
||||||
|
.id = keylet.key,
|
||||||
|
.amount = asset(1000)});
|
||||||
|
env(tx, ter(tecINSUFFICIENT_FUNDS));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Fail to withdraw more than assets held.
|
||||||
|
auto tx = vault.withdraw(
|
||||||
|
{.depositor = depositor,
|
||||||
|
.id = keylet.key,
|
||||||
|
.amount = asset(1000)});
|
||||||
|
env(tx, ter(tecINSUFFICIENT_FUNDS));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void
|
void
|
||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
EXECUTE(CreateUpdateDelete);
|
// EXECUTE(CreateUpdateDelete);
|
||||||
|
EXECUTE(Sequence);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ Subcase::~Subcase()
|
|||||||
// because only now do we know which subcase was the leaf,
|
// because only now do we know which subcase was the leaf,
|
||||||
// and we only want to print one name line for each subcase.
|
// and we only want to print one name line for each subcase.
|
||||||
_.suite.testcase(_.name());
|
_.suite.testcase(_.name());
|
||||||
|
// Let the runner know that a test executed,
|
||||||
|
// even if `BEAST_EXPECT` was never called.
|
||||||
|
_.suite.pass();
|
||||||
}
|
}
|
||||||
if (_.skipped == 0)
|
if (_.skipped == 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -58,9 +58,13 @@ VaultDeposit::doApply()
|
|||||||
if (!vault)
|
if (!vault)
|
||||||
return tecOBJECT_NOT_FOUND;
|
return tecOBJECT_NOT_FOUND;
|
||||||
|
|
||||||
auto amount = ctx_.tx[sfAmount];
|
// TODO: Check credentials.
|
||||||
|
if (vault->getFlags() & lsfVaultPrivate)
|
||||||
|
return tecNO_AUTH;
|
||||||
|
|
||||||
|
auto assets = ctx_.tx[sfAmount];
|
||||||
Asset const& asset = vault->at(sfAsset);
|
Asset const& asset = vault->at(sfAsset);
|
||||||
if (amount.asset() != asset)
|
if (assets.asset() != asset)
|
||||||
return tecWRONG_ASSET;
|
return tecWRONG_ASSET;
|
||||||
|
|
||||||
if (accountHolds(
|
if (accountHolds(
|
||||||
@@ -69,33 +73,30 @@ VaultDeposit::doApply()
|
|||||||
asset,
|
asset,
|
||||||
FreezeHandling::fhZERO_IF_FROZEN,
|
FreezeHandling::fhZERO_IF_FROZEN,
|
||||||
AuthHandling::ahZERO_IF_UNAUTHORIZED,
|
AuthHandling::ahZERO_IF_UNAUTHORIZED,
|
||||||
j_) < amount)
|
j_) < assets)
|
||||||
{
|
{
|
||||||
return tecINSUFFICIENT_FUNDS;
|
return tecINSUFFICIENT_FUNDS;
|
||||||
}
|
}
|
||||||
|
|
||||||
vault->at(sfAssetTotal) += amount;
|
vault->at(sfAssetTotal) += assets;
|
||||||
vault->at(sfAssetAvailable) += amount;
|
vault->at(sfAssetAvailable) += assets;
|
||||||
|
|
||||||
|
// A deposit must not push the vault over its limit.
|
||||||
auto maximum = *vault->at(sfAssetMaximum);
|
auto maximum = *vault->at(sfAssetMaximum);
|
||||||
if (maximum != 0 && *vault->at(sfAssetTotal) > maximum)
|
if (maximum != 0 && *vault->at(sfAssetTotal) > maximum)
|
||||||
return tecLIMIT_EXCEEDED;
|
return tecLIMIT_EXCEEDED;
|
||||||
|
|
||||||
// TODO: Check credentials.
|
|
||||||
if (vault->getFlags() & lsfVaultPrivate)
|
|
||||||
;
|
|
||||||
|
|
||||||
auto const& vaultAccount = vault->at(sfAccount);
|
auto const& vaultAccount = vault->at(sfAccount);
|
||||||
// Transfer amount from sender to vault.
|
// Transfer assets from depositor to vault.
|
||||||
if (auto ter = accountSend(view(), account_, vaultAccount, amount, j_))
|
if (auto ter = accountSend(view(), account_, vaultAccount, assets, j_))
|
||||||
return ter;
|
return ter;
|
||||||
|
|
||||||
auto shares = assetsToSharesDeposit(view(), vault, amount);
|
// Transfer shares from vault to depositor.
|
||||||
if (!shares)
|
auto shares = assetsToSharesDeposit(view(), vault, assets);
|
||||||
return shares.error();
|
if (auto ter = accountSend(view(), vaultAccount, account_, shares, j_))
|
||||||
if (auto ter = accountSend(view(), vaultAccount, account_, *shares, j_))
|
|
||||||
return ter;
|
return ter;
|
||||||
// TODO: copy mptIssuance.OutstandingAmount to vault.ShareTotal?
|
|
||||||
|
view().update(vault);
|
||||||
|
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,11 @@ VaultSet::doApply()
|
|||||||
if (tx.isFieldPresent(sfData))
|
if (tx.isFieldPresent(sfData))
|
||||||
vault->at(sfData) = tx[sfData];
|
vault->at(sfData) = tx[sfData];
|
||||||
if (tx.isFieldPresent(sfAssetMaximum))
|
if (tx.isFieldPresent(sfAssetMaximum))
|
||||||
|
{
|
||||||
|
if (tx[sfAssetMaximum] < *vault->at(sfAssetTotal))
|
||||||
|
return tecLIMIT_EXCEEDED;
|
||||||
vault->at(sfAssetMaximum) = tx[sfAssetMaximum];
|
vault->at(sfAssetMaximum) = tx[sfAssetMaximum];
|
||||||
|
}
|
||||||
|
|
||||||
view().update(vault);
|
view().update(vault);
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <xrpld/app/tx/detail/VaultWithdraw.h>
|
#include <xrpld/app/tx/detail/VaultWithdraw.h>
|
||||||
|
|
||||||
|
#include <xrpld/ledger/View.h>
|
||||||
#include <xrpl/protocol/Feature.h>
|
#include <xrpl/protocol/Feature.h>
|
||||||
#include <xrpl/protocol/STNumber.h>
|
#include <xrpl/protocol/STNumber.h>
|
||||||
#include <xrpl/protocol/TxFlags.h>
|
#include <xrpl/protocol/TxFlags.h>
|
||||||
@@ -56,6 +57,65 @@ VaultWithdraw::doApply()
|
|||||||
if (!vault)
|
if (!vault)
|
||||||
return tecOBJECT_NOT_FOUND;
|
return tecOBJECT_NOT_FOUND;
|
||||||
|
|
||||||
|
// TODO: Check credentials.
|
||||||
|
if (vault->getFlags() & lsfVaultPrivate)
|
||||||
|
return tecNO_AUTH;
|
||||||
|
|
||||||
|
auto amount = ctx_.tx[sfAmount];
|
||||||
|
|
||||||
|
STAmount shares, assets;
|
||||||
|
if (amount.asset() == vault->at(sfAsset))
|
||||||
|
{
|
||||||
|
// Fixed assets, variable shares.
|
||||||
|
assets = amount;
|
||||||
|
shares = assetsToSharesWithdraw(view(), vault, assets);
|
||||||
|
}
|
||||||
|
else if (amount.asset() == vault->at(sfMPTokenIssuanceID))
|
||||||
|
{
|
||||||
|
// Fixed shares, variable assets.
|
||||||
|
shares = amount;
|
||||||
|
assets = sharesToAssetsWithdraw(view(), vault, shares);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return tecWRONG_ASSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The depositor must have enough shares.
|
||||||
|
if (accountHolds(
|
||||||
|
view(),
|
||||||
|
account_,
|
||||||
|
shares.asset(),
|
||||||
|
FreezeHandling::fhZERO_IF_FROZEN,
|
||||||
|
AuthHandling::ahZERO_IF_UNAUTHORIZED,
|
||||||
|
j_) < shares)
|
||||||
|
{
|
||||||
|
return tecINSUFFICIENT_FUNDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The vault must have enough assets on hand.
|
||||||
|
// The vault may hold assets that it has already pledged.
|
||||||
|
// That is why we look at AssetAvailable instead of the account balance.
|
||||||
|
// TODO: Invariant: vault.AssetAvailable <= vault.Account.balance(vault.Asset)
|
||||||
|
if (*vault->at(sfAssetAvailable) < assets)
|
||||||
|
return tecINSUFFICIENT_FUNDS;
|
||||||
|
|
||||||
|
std::cerr << "total before: " << *vault->at(sfAssetTotal) << std::endl;
|
||||||
|
vault->at(sfAssetTotal) -= assets;
|
||||||
|
std::cerr << "total after: " << *vault->at(sfAssetTotal) << std::endl;
|
||||||
|
vault->at(sfAssetAvailable) -= assets;
|
||||||
|
|
||||||
|
auto const& vaultAccount = vault->at(sfAccount);
|
||||||
|
// Transfer shares from depositor to vault.
|
||||||
|
if (auto ter = accountSend(view(), account_, vaultAccount, shares, j_))
|
||||||
|
return ter;
|
||||||
|
|
||||||
|
// Transfer assets from vault to depositor.
|
||||||
|
if (auto ter = accountSend(view(), vaultAccount, account_, assets, j_))
|
||||||
|
return ter;
|
||||||
|
|
||||||
|
view().update(vault);
|
||||||
|
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -621,7 +621,7 @@ deleteAMMTrustLine(
|
|||||||
// From the perspective of a vault,
|
// From the perspective of a vault,
|
||||||
// return the number of shares to give the depositor
|
// return the number of shares to give the depositor
|
||||||
// when they deposit a fixed amount of assets.
|
// when they deposit a fixed amount of assets.
|
||||||
[[nodiscard]] Expected<STAmount, TER>
|
[[nodiscard]] STAmount
|
||||||
assetsToSharesDeposit(
|
assetsToSharesDeposit(
|
||||||
ReadView const& view,
|
ReadView const& view,
|
||||||
std::shared_ptr<SLE> const& vault,
|
std::shared_ptr<SLE> const& vault,
|
||||||
@@ -630,7 +630,7 @@ assetsToSharesDeposit(
|
|||||||
// From the perspective of a vault,
|
// From the perspective of a vault,
|
||||||
// return the number of shares to demand from the depositor
|
// return the number of shares to demand from the depositor
|
||||||
// when they ask to withdraw a fixed amount of assets.
|
// when they ask to withdraw a fixed amount of assets.
|
||||||
[[nodiscard]] Expected<Number, TER>
|
[[nodiscard]] STAmount
|
||||||
assetsToSharesWithdraw(
|
assetsToSharesWithdraw(
|
||||||
ReadView const& view,
|
ReadView const& view,
|
||||||
std::shared_ptr<SLE> const& vault,
|
std::shared_ptr<SLE> const& vault,
|
||||||
@@ -639,7 +639,7 @@ assetsToSharesWithdraw(
|
|||||||
// From the perspective of a vault,
|
// From the perspective of a vault,
|
||||||
// return the number of assets to give the depositor
|
// return the number of assets to give the depositor
|
||||||
// when they redeem a fixed amount of shares.
|
// when they redeem a fixed amount of shares.
|
||||||
[[nodiscard]] Expected<Number, TER>
|
[[nodiscard]] STAmount
|
||||||
sharesToAssetsWithdraw(
|
sharesToAssetsWithdraw(
|
||||||
ReadView const& view,
|
ReadView const& view,
|
||||||
std::shared_ptr<SLE> const& vault,
|
std::shared_ptr<SLE> const& vault,
|
||||||
|
|||||||
@@ -2235,7 +2235,7 @@ getShareTotal(ReadView const& view, std::shared_ptr<SLE> const& vault)
|
|||||||
return issuance->at(sfOutstandingAmount);
|
return issuance->at(sfOutstandingAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] Expected<STAmount, TER>
|
[[nodiscard]] STAmount
|
||||||
assetsToSharesDeposit(
|
assetsToSharesDeposit(
|
||||||
ReadView const& view,
|
ReadView const& view,
|
||||||
std::shared_ptr<SLE> const& vault,
|
std::shared_ptr<SLE> const& vault,
|
||||||
@@ -2251,7 +2251,7 @@ assetsToSharesDeposit(
|
|||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] Expected<Number, TER>
|
[[nodiscard]] STAmount
|
||||||
assetsToSharesWithdraw(
|
assetsToSharesWithdraw(
|
||||||
ReadView const& view,
|
ReadView const& view,
|
||||||
std::shared_ptr<SLE> const& vault,
|
std::shared_ptr<SLE> const& vault,
|
||||||
@@ -2260,18 +2260,16 @@ assetsToSharesWithdraw(
|
|||||||
assert(assets.asset() == vault->at(sfAsset));
|
assert(assets.asset() == vault->at(sfAsset));
|
||||||
Number assetTotal = vault->at(sfAssetTotal);
|
Number assetTotal = vault->at(sfAssetTotal);
|
||||||
assetTotal -= vault->at(sfLossUnrealized);
|
assetTotal -= vault->at(sfLossUnrealized);
|
||||||
// TODO: What error here?
|
STAmount amount{vault->at(sfMPTokenIssuanceID)};
|
||||||
if (assets > assetTotal)
|
|
||||||
return Unexpected{tecINTERNAL};
|
|
||||||
if (assetTotal == 0)
|
if (assetTotal == 0)
|
||||||
return 0;
|
return amount;
|
||||||
Number shareTotal = getShareTotal(view, vault);
|
Number shareTotal = getShareTotal(view, vault);
|
||||||
auto shares = shareTotal * (assets / assetTotal);
|
amount = shareTotal * (assets / assetTotal);
|
||||||
// TODO: Limit by withdrawal policy?
|
// TODO: Limit by withdrawal policy?
|
||||||
return shares;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] Expected<Number, TER>
|
[[nodiscard]] STAmount
|
||||||
sharesToAssetsWithdraw(
|
sharesToAssetsWithdraw(
|
||||||
ReadView const& view,
|
ReadView const& view,
|
||||||
std::shared_ptr<SLE> const& vault,
|
std::shared_ptr<SLE> const& vault,
|
||||||
@@ -2280,14 +2278,13 @@ sharesToAssetsWithdraw(
|
|||||||
assert(shares.asset() == vault->at(sfMPTokenIssuanceID));
|
assert(shares.asset() == vault->at(sfMPTokenIssuanceID));
|
||||||
Number assetTotal = vault->at(sfAssetTotal);
|
Number assetTotal = vault->at(sfAssetTotal);
|
||||||
assetTotal -= vault->at(sfLossUnrealized);
|
assetTotal -= vault->at(sfLossUnrealized);
|
||||||
|
STAmount amount{vault->at(sfAsset)};
|
||||||
if (assetTotal == 0)
|
if (assetTotal == 0)
|
||||||
return 0;
|
return amount;
|
||||||
Number shareTotal = getShareTotal(view, vault);
|
Number shareTotal = getShareTotal(view, vault);
|
||||||
if (shares > shareTotal)
|
amount = assetTotal * (shares / shareTotal);
|
||||||
return Unexpected{tecINTERNAL};
|
|
||||||
auto assets = assetTotal * (shares / shareTotal);
|
|
||||||
// TODO: Limit by withdrawal policy?
|
// TODO: Limit by withdrawal policy?
|
||||||
return assets;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|||||||
Reference in New Issue
Block a user