Skip to content

Commit

Permalink
progress
Browse files Browse the repository at this point in the history
  • Loading branch information
thejohnfreeman committed Oct 3, 2024
1 parent 0f445fc commit e589775
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 80 deletions.
8 changes: 0 additions & 8 deletions include/xrpl/protocol/AMMCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,6 @@ class STObject;
class STAmount;
class Rules;

/** Calculate AMM account ID.
*/
AccountID
ammAccountID(
std::uint16_t prefix,
uint256 const& parentHash,
uint256 const& ammID);

/** Calculate Liquidity Provider Token (LPT) Currency.
*/
Currency
Expand Down
9 changes: 9 additions & 0 deletions include/xrpl/protocol/STIssue.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ class STIssue final : public STBase, CountedObject<STIssue>

explicit STIssue(SField const& name);

STIssue&
operator=(STIssue const& rhs) = default;
STIssue&
operator=(Asset const& rhs)
{
asset_ = rhs;
return *this;
}

template <ValidIssueType TIss>
TIss const&
get() const;
Expand Down
2 changes: 2 additions & 0 deletions include/xrpl/protocol/STNumber.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class STNumber final : public STBase,
public Number
{
public:
using value_type = Number;

STNumber(SField const& n, Number const& v = Number());
STNumber(SerialIter& sit, SField const& name);

Expand Down
14 changes: 14 additions & 0 deletions include/xrpl/protocol/STObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,20 @@ class STObject::ValueProxy : private Proxy<T>

operator value_type() const;

template <typename U>
friend bool
operator==(U&& lhs, STObject::ValueProxy<T> const& rhs)
{
return rhs.value() == lhs;
}

template <typename U>
friend bool
operator!=(U&& lhs, STObject::ValueProxy<T> const& rhs)
{
return !(lhs == rhs);
}

private:
friend class STObject;

Expand Down
6 changes: 3 additions & 3 deletions include/xrpl/protocol/detail/ledger_entries.m
Original file line number Diff line number Diff line change
Expand Up @@ -437,14 +437,14 @@ to implement skip lists that allow for efficient backwards (and, in
{sfOwnerNode, soeREQUIRED},
{sfOwner, soeREQUIRED},
{sfAccount, soeREQUIRED},
// no Data ever (YAGNI)
{sfData, soeOPTIONAL},
{sfAsset, soeREQUIRED},
{sfAssetTotal, soeREQUIRED},
{sfAssetAvailable, soeREQUIRED},
{sfAssetMaximum, soeREQUIRED},
{sfAssetMaximum, soeOPTIONAL},
{sfMPTokenIssuanceID, soeREQUIRED}, // sfShare
// no ShareTotal ever (use MPTIssuance.sfOutstandingAmount)
// no WithdrawalPolicy ever (YAGNI)
// no PermissionedDomainID yet
{sfLossUnrealized, soeREQUIRED}, // not in spec
{sfLossUnrealized, soeOPTIONAL}, // not in spec
}))
12 changes: 0 additions & 12 deletions src/libxrpl/protocol/AMMCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,6 @@

namespace ripple {

AccountID
ammAccountID(
std::uint16_t prefix,
uint256 const& parentHash,
uint256 const& ammID)
{
ripesha_hasher rsh;
auto const hash = sha512Half(prefix, parentHash, ammID);
rsh(hash.data(), hash.size());
return AccountID{static_cast<ripesha_hasher::result_type>(rsh)};
}

Currency
ammLPTCurrency(Currency const& cur1, Currency const& cur2)
{
Expand Down
2 changes: 1 addition & 1 deletion src/libxrpl/protocol/Keylet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Keylet::check(STLedgerEntry const& sle) const
if (type == ltCHILD)
return sle.getType() != ltDIR_NODE;

return sle.getType() == type;
return sle.getType() == type && sle.key() == key;
}

} // namespace ripple
38 changes: 3 additions & 35 deletions src/xrpld/app/tx/detail/AMMCreate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,18 +214,7 @@ applyCreate(
auto const ammKeylet = keylet::amm(amount.issue(), amount2.issue());

// Mitigate same account exists possibility
auto const ammAccount = [&]() -> Expected<AccountID, TER> {
std::uint16_t constexpr maxAccountAttempts = 256;
for (auto p = 0; p < maxAccountAttempts; ++p)
{
auto const ammAccount =
ammAccountID(p, sb.info().parentHash, ammKeylet.key);
if (!sb.read(keylet::account(ammAccount)))
return ammAccount;
}
return Unexpected(tecDUPLICATE);
}();

auto const ammAccount = createPseudoAccount(sb, ammKeylet.key);
// AMM account already exists (should not happen)
if (!ammAccount)
{
Expand All @@ -243,28 +232,14 @@ applyCreate(
}

// Create AMM Root Account.
auto sleAMMRoot = std::make_shared<SLE>(keylet::account(*ammAccount));
sleAMMRoot->setAccountID(sfAccount, *ammAccount);
sleAMMRoot->setFieldAmount(sfBalance, STAmount{});
std::uint32_t const seqno{
ctx_.view().rules().enabled(featureDeletableAccounts)
? ctx_.view().seq()
: 1};
sleAMMRoot->setFieldU32(sfSequence, seqno);
// Ignore reserves requirement, disable the master key, allow default
// rippling (AMM LPToken can be used in payments and offer crossing but
// not as a token in another AMM), and enable deposit authorization to
// prevent payments into AMM.
auto sleAMMRoot = sb.peek(keylet::account(*ammAccount));
// Note, that the trustlines created by AMM have 0 credit limit.
// This prevents shifting the balance between accounts via AMM,
// or sending unsolicited LPTokens. This is a desired behavior.
// A user can only receive LPTokens through affirmative action -
// either an AMMDeposit, TrustSet, crossing an offer, etc.
sleAMMRoot->setFieldU32(
sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth);
// Link the root account and AMM object
sleAMMRoot->setFieldH256(sfAMMID, ammKeylet.key);
sb.insert(sleAMMRoot);

// Calculate initial LPT balance.
auto const lpTokens = ammLPTokens(amount, amount2, lptIss);
Expand All @@ -281,14 +256,7 @@ applyCreate(
ctx_.view(), ammSle, account_, lptIss, ctx_.tx[sfTradingFee]);

// Add owner directory to link the root account and AMM object.
if (auto const page = sb.dirInsert(
keylet::ownerDir(*ammAccount),
ammSle->key(),
describeOwnerDir(*ammAccount)))
{
ammSle->setFieldU64(sfOwnerNode, *page);
}
else
if (auto ter = dirLink(sb, *ammAccount, ammSle); ter)
{
JLOG(j_.debug()) << "AMM Instance: failed to insert owner dir";
return {tecDIR_FULL, false};
Expand Down
70 changes: 49 additions & 21 deletions src/xrpld/app/tx/detail/VaultSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <xrpld/app/tx/detail/VaultSet.h>
#include <xrpld/ledger/View.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/STNumber.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/st.h>

Expand All @@ -43,47 +44,74 @@ VaultSet::preclaim(PreclaimContext const& ctx)
return tesSUCCESS;
}

#define TX_ASSERT(condition) condition
#undef TX_ASSERT
#define TX_ASSERT(condition)
#define TX_MATCH(...)
#define TX_OVERWRITE(lhs, rhs) \
if (rhs) \
{ \
lhs = rhs; \
}
#undef TX_OVERWRITE
#define TX_OVERWRITE(lhs, rhs)

TER
VaultSet::doApply()
{
// All return codes in `doApply` must be `tec`, `ter`, or `tes`.
// As we move checks into `preflight` and `preclaim`,
// we can consider downgrading them to `tef` or `tem`.

auto const& tx = ctx_.tx;

auto const& vaultId = tx[~sfVaultID];
auto klVault = vaultId ? Keylet{ltVAULT, *vaultId}
: keylet::vault(tx[sfAccount], tx[sfSequence]);
auto keylet = vaultId ? Keylet{ltVAULT, *vaultId}
: keylet::vault(tx[sfAccount], tx[sfSequence]);

auto vault = view().peek(klVault);
auto vault = view().peek(keylet);

if (vault)
{
TX_ASSERT(vault->getType() == ltVAULT);
TX_ASSERT(vault->key() == klVault.key);
TX_ASSERT((*vault)[sfOwner] == tx[sfAccount]);
TX_MATCH((*vault)[sfAsset], tx[~sfAsset]);
// Update existing object.
// Assert that submitter is the Owner.
if (tx[sfAccount] != (*vault)[sfOwner])
return tecNO_PERMISSION;
// Assert that Asset is the same if given.
if (tx.isFieldPresent(sfAsset) && tx[sfAsset] != (*vault)[sfAsset])
return tecNO_PERMISSION;
view().update(vault);
}
else if (tx.isFieldPresent(sfVaultID))
{
// Update missing object.
return tecOBJECT_NOT_FOUND;
}
else
{
// create new vault
// Create new object.
if (!tx.isFieldPresent(sfAsset))
return tecINCOMPLETE;

vault = std::make_shared<SLE>(keylet);
if (auto ter = dirLink(view(), tx[sfAccount], vault); ter)
return ter;
auto pseudo = createPseudoAccount(view(), vault->key());
if (!pseudo)
return pseudo.error();
(*vault)[sfSequence] = tx[sfSequence];
(*vault)[sfOwner] = tx[sfAccount];
(*vault)[sfAccount] = pseudo.value();
(*vault)[~sfData] = tx[~sfData];
(*vault)[sfAsset] = tx[sfAsset];
(*vault)[sfAssetTotal] = 0;
(*vault)[sfAssetAvailable] = 0;
(*vault)[~sfAssetMaximum] = tx[~sfAssetMaximum];
// TODO: create MPT for share
// No `LossUnrealized`.
view().insert(vault);
}

TX_OVERWRITE((*vault)[sfAssetMaximum], tx[~sfAssetMaximum]);
// Set or clear field AssetMaximum.
if (tx.isFieldPresent(sfAssetMaximum))
{
if (tx[sfAssetMaximum] == 0)
{
vault->delField(sfAssetMaximum);
}
else
{
(*vault)[sfAssetMaximum] = tx[sfAssetMaximum];
}
}

return tesSUCCESS;
}
Expand Down
6 changes: 6 additions & 0 deletions src/xrpld/ledger/View.h
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,12 @@ dirNext(
[[nodiscard]] std::function<void(SLE::ref)>
describeOwnerDir(AccountID const& account);

[[nodiscard]] TER
dirLink(ApplyView& view, AccountID const& owner, std::shared_ptr<SLE>& object);

[[nodiscard]] Expected<AccountID, TER>
createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey);

// VFALCO NOTE Both STAmount parameters should just
// be "Amount", a unit-less number.
//
Expand Down
47 changes: 47 additions & 0 deletions src/xrpld/ledger/detail/View.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/Quality.h>
#include <xrpl/protocol/digest.h>
#include <xrpl/protocol/st.h>
#include <cassert>
#include <optional>
Expand Down Expand Up @@ -832,6 +833,52 @@ describeOwnerDir(AccountID const& account)
};
}

TER
dirLink(ApplyView& view, AccountID const& owner, std::shared_ptr<SLE>& object)
{
auto const page = view.dirInsert(
keylet::ownerDir(owner), object->key(), describeOwnerDir(owner));
if (!page)
return tecDIR_FULL;
object->setFieldU64(sfOwnerNode, *page);
return tesSUCCESS;
}

Expected<AccountID, TER>
createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey)
{
AccountID accountId;
for (auto i = 0;; ++i)
{
if (i >= 256)
return Unexpected(tecDUPLICATE);
ripesha_hasher rsh;
auto const hash = sha512Half(i, view.info().parentHash, pseudoOwnerKey);
rsh(hash.data(), hash.size());
accountId = static_cast<ripesha_hasher::result_type>(rsh);
if (!view.read(keylet::account(accountId)))
break;
}

// Create pseudo-account.
auto account = std::make_shared<SLE>(keylet::account(accountId));
account->setAccountID(sfAccount, accountId);
account->setFieldAmount(sfBalance, STAmount{});
std::uint32_t const seqno{
view.rules().enabled(featureDeletableAccounts) ? view.seq() : 1};
account->setFieldU32(sfSequence, seqno);
// Ignore reserves requirement, disable the master key, allow default
// rippling, and enable deposit authorization to prevent payments into
// pseudo-account.
account->setFieldU32(
sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth);
// Link the pseudo-account with its owner object.
// account->setFieldH256(sfPseudoOwner, pseudoOwnerKey);
view.insert(account);

return accountId;
}

TER
trustCreate(
ApplyView& view,
Expand Down

0 comments on commit e589775

Please sign in to comment.