Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixInnerObjTemplate: Add STObject constructor to explicitly set inner object template #4906

Merged
merged 12 commits into from
Feb 7, 2024
Merged
6 changes: 4 additions & 2 deletions src/ripple/app/misc/impl/AMMUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,17 +287,18 @@ initializeFeeAuctionVote(
Issue const& lptIssue,
std::uint16_t tfee)
{
auto const& rules = view.rules();
// AMM creator gets the voting slot.
STArray voteSlots;
STObject voteEntry{sfVoteEntry};
STObject voteEntry = STObject::makeInnerObject(sfVoteEntry, rules);
if (tfee != 0)
voteEntry.setFieldU16(sfTradingFee, tfee);
voteEntry.setFieldU32(sfVoteWeight, VOTE_WEIGHT_SCALE_FACTOR);
voteEntry.setAccountID(sfAccount, account);
voteSlots.push_back(voteEntry);
ammSle->setFieldArray(sfVoteSlots, voteSlots);
// AMM creator gets the auction slot for free.
auto& auctionSlot = ammSle->peekFieldObject(sfAuctionSlot);
STObject auctionSlot = STObject::makeInnerObject(sfAuctionSlot, rules);
auctionSlot.setAccountID(sfAccount, account);
// current + sec in 24h
auto const expiration = std::chrono::duration_cast<std::chrono::seconds>(
Expand All @@ -315,6 +316,7 @@ initializeFeeAuctionVote(
auctionSlot.setFieldU16(sfDiscountedFee, dfee);
else if (auctionSlot.isFieldPresent(sfDiscountedFee))
auctionSlot.makeFieldAbsent(sfDiscountedFee);
ammSle->set(&auctionSlot);
ximinez marked this conversation as resolved.
Show resolved Hide resolved
}

} // namespace ripple
5 changes: 3 additions & 2 deletions src/ripple/app/tx/impl/AMMVote.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ applyVote(
Number den{0};
// Account already has vote entry
bool foundAccount = false;
auto const& rules = ctx_.view().rules();
// Iterate over the current vote entries and update each entry
// per current total tokens balance and each LP tokens balance.
// Find the entry with the least tokens and whether the account
Expand All @@ -119,7 +120,7 @@ applyVote(
continue;
}
auto feeVal = entry[sfTradingFee];
STObject newEntry{sfVoteEntry};
STObject newEntry = STObject::makeInnerObject(sfVoteEntry, rules);
// The account already has the vote entry.
if (account == account_)
{
Expand Down Expand Up @@ -158,7 +159,7 @@ applyVote(
{
auto update = [&](std::optional<std::uint8_t> const& minPos =
std::nullopt) {
STObject newEntry{sfVoteEntry};
STObject newEntry = STObject::makeInnerObject(sfVoteEntry, rules);
if (feeNew != 0)
newEntry.setFieldU16(sfTradingFee, feeNew);
newEntry.setFieldU32(
Expand Down
3 changes: 2 additions & 1 deletion src/ripple/protocol/Feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ namespace detail {
// Feature.cpp. Because it's only used to reserve storage, and determine how
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
// the actual number of amendments. A LogicError on startup will verify this.
static constexpr std::size_t numFeatures = 65;
static constexpr std::size_t numFeatures = 66;

/** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated
Expand Down Expand Up @@ -352,6 +352,7 @@ extern uint256 const featureXChainBridge;
extern uint256 const fixDisallowIncomingV1;
extern uint256 const featureDID;
extern uint256 const fixFillOrKill;
extern uint256 const fixInnerObjTemplate;

} // namespace ripple

Expand Down
2 changes: 2 additions & 0 deletions src/ripple/protocol/SOTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ enum SOEStyle {
soeREQUIRED = 0, // required
soeOPTIONAL = 1, // optional, may be present with default value
soeDEFAULT = 2, // optional, if present, must not have default value
// inner object with the default fields has to be
// constructed with STObject::makeInnerObject()
ximinez marked this conversation as resolved.
Show resolved Hide resolved
};

//------------------------------------------------------------------------------
Expand Down
4 changes: 4 additions & 0 deletions src/ripple/protocol/STObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
namespace ripple {

class STArray;
class Rules;

inline void
throwFieldNotFound(SField const& field)
Expand Down Expand Up @@ -102,6 +103,9 @@ class STObject : public STBase, public CountedObject<STObject>
STObject(SerialIter&& sit, SField const& name);
explicit STObject(SField const& name);

static STObject
makeInnerObject(SField const& name, Rules const& rules);

iterator
begin() const;

Expand Down
1 change: 1 addition & 0 deletions src/ripple/protocol/impl/Feature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ REGISTER_FEATURE(XChainBridge, Supported::yes, VoteBehavior::De
REGISTER_FIX (fixDisallowIncomingV1, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FEATURE(DID, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FIX(fixFillOrKill, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FIX(fixInnerObjTemplate, Supported::yes, VoteBehavior::DefaultNo);

// The following amendments are obsolete, but must remain supported
// because they could potentially get enabled.
Expand Down
3 changes: 3 additions & 0 deletions src/ripple/protocol/impl/InnerObjectFormats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ namespace ripple {

InnerObjectFormats::InnerObjectFormats()
{
// inner objects with the default fields have to be
// constructed with STObject::makeInnerObject()

add(sfSignerEntry.jsonName.c_str(),
sfSignerEntry.getCode(),
{
Expand Down
22 changes: 22 additions & 0 deletions src/ripple/protocol/impl/STObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
//==============================================================================

#include <ripple/basics/Log.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/InnerObjectFormats.h>
#include <ripple/protocol/Rules.h>
#include <ripple/protocol/STAccount.h>
#include <ripple/protocol/STArray.h>
#include <ripple/protocol/STBlob.h>
Expand Down Expand Up @@ -57,6 +59,19 @@ STObject::STObject(SerialIter& sit, SField const& name, int depth) noexcept(
set(sit, depth);
}

STObject
STObject::makeInnerObject(SField const& name, Rules const& rules)
{
STObject obj{name};
if (rules.enabled(fixInnerObjTemplate))
{
if (SOTemplate const* elements =
InnerObjectFormats::getInstance().findSOTemplateBySField(name))
obj.set(*elements);
}
return obj;
}

STBase*
STObject::copy(std::size_t n, void* buf) const
{
Expand Down Expand Up @@ -630,6 +645,13 @@ STObject::getFieldArray(SField const& field) const
void
STObject::set(std::unique_ptr<STBase> v)
{
set(v.get());
}

void
STObject::set(STBase* v)
gregtatcam marked this conversation as resolved.
Show resolved Hide resolved
{
ximinez marked this conversation as resolved.
Show resolved Hide resolved
assert(v);
auto const i = getFieldIndex(v->getFName());
if (i != -1)
{
Expand Down
105 changes: 105 additions & 0 deletions src/test/app/AMM_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4789,6 +4789,110 @@ struct AMM_test : public jtx::AMMTest
}
}

void
testFixDefaultInnerObj()
{
using namespace jtx;
FeatureBitset const all{supported_amendments()};

auto test = [&](FeatureBitset features,
TER const& err1,
TER const& err2,
TER const& err3,
TER const& err4,
std::uint16_t tfee,
bool closeLedger,
std::optional<std::uint16_t> extra = std::nullopt) {
Env env(*this, features);
fund(env, gw, {alice}, XRP(1'000), {USD(10)});
AMM amm(
env,
gw,
XRP(10),
USD(10),
{.tfee = tfee, .close = closeLedger});
amm.deposit(alice, USD(10), XRP(10));
amm.vote(
alice,
tfee,
std::nullopt,
std::nullopt,
std::nullopt,
ter(err1));
amm.withdraw(gw, USD(1), std::nullopt, std::nullopt, ter(err2));
// with the amendment disabled and ledger not closed,
// second vote succeeds if the first vote sets the trading fee
// to non-zero; if the first vote sets the trading fee to >0 && <9
// then the second withdraw succeeds if the second vote sets
// the trading fee so that the discounted fee is non-zero
amm.vote(
alice, 20, std::nullopt, std::nullopt, std::nullopt, ter(err3));
amm.withdraw(gw, USD(2), std::nullopt, std::nullopt, ter(err4));
};

// ledger is closed after each transaction, vote/withdraw don't fail
// regardless whether the amendment is enabled or not
test(all, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 0, true);
test(
all - fixInnerObjTemplate,
tesSUCCESS,
tesSUCCESS,
tesSUCCESS,
tesSUCCESS,
0,
true);
// ledger is not closed after each transaction
// vote/withdraw don't fail if the amendment is enabled
test(all, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 0, false);
// vote/withdraw fail if the amendment is not enabled
// second vote/withdraw still fail: second vote fails because
// the initial trading fee is 0, consequently second withdraw fails
// because the second vote fails
test(
all - fixInnerObjTemplate,
tefEXCEPTION,
tefEXCEPTION,
tefEXCEPTION,
tefEXCEPTION,
0,
false);
// if non-zero trading/discounted fee then vote/withdraw
// don't fail whether the ledger is closed or not and
// the amendment is enabled or not
test(all, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 10, true);
test(
all - fixInnerObjTemplate,
tesSUCCESS,
tesSUCCESS,
tesSUCCESS,
tesSUCCESS,
10,
true);
test(all, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 10, false);
test(
all - fixInnerObjTemplate,
tesSUCCESS,
tesSUCCESS,
tesSUCCESS,
tesSUCCESS,
10,
false);
// non-zero trading fee but discounted fee is 0, vote doesn't fail
// but withdraw fails
test(all, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 9, false);
// second vote sets the trading fee to non-zero, consequently
// second withdraw doesn't fail even if the amendment is not
// enabled and the ledger is not closed
test(
all - fixInnerObjTemplate,
tesSUCCESS,
tefEXCEPTION,
tesSUCCESS,
tesSUCCESS,
9,
false);
}

void
testCore()
{
Expand All @@ -4815,6 +4919,7 @@ struct AMM_test : public jtx::AMMTest
testClawback();
testAMMID();
testSelection();
testFixDefaultInnerObj();
}

void
Expand Down
28 changes: 24 additions & 4 deletions src/test/jtx/AMM.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ class LPToken
}
};

struct CreateArg
{
bool log = false;
std::uint16_t tfee = 0;
std::uint32_t fee = 0;
std::optional<std::uint32_t> flags = std::nullopt;
std::optional<jtx::seq> seq = std::nullopt;
std::optional<jtx::msig> ms = std::nullopt;
std::optional<ter> err = std::nullopt;
bool close = true;
};

/** Convenience class to test AMM functionality.
*/
class AMM
Expand Down Expand Up @@ -91,13 +103,20 @@ class AMM
std::optional<std::uint32_t> flags = std::nullopt,
std::optional<jtx::seq> seq = std::nullopt,
std::optional<jtx::msig> ms = std::nullopt,
std::optional<ter> const& ter = std::nullopt);
std::optional<ter> const& ter = std::nullopt,
bool close = true);
AMM(Env& env,
Account const& account,
STAmount const& asset1,
STAmount const& asset2,
ter const& ter,
bool log = false);
bool log = false,
bool close = true);
AMM(Env& env,
Account const& account,
STAmount const& asset1,
STAmount const& asset2,
CreateArg const& arg);

/** Send amm_info RPC command
*/
Expand Down Expand Up @@ -200,14 +219,15 @@ class AMM
IOUAmount
withdrawAll(
std::optional<Account> const& account,
std::optional<STAmount> const& asset1OutDetails = std::nullopt)
std::optional<STAmount> const& asset1OutDetails = std::nullopt,
std::optional<ter> const& ter = std::nullopt)
{
return withdraw(
account,
std::nullopt,
asset1OutDetails,
asset1OutDetails ? tfOneAssetWithdrawAll : tfWithdrawAll,
std::nullopt);
ter);
}

IOUAmount
Expand Down
32 changes: 28 additions & 4 deletions src/test/jtx/impl/AMM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,16 @@ AMM::AMM(
std::optional<std::uint32_t> flags,
std::optional<jtx::seq> seq,
std::optional<jtx::msig> ms,
std::optional<ter> const& ter)
std::optional<ter> const& ter,
bool close)
: env_(env)
, creatorAccount_(account)
, asset1_(asset1)
, asset2_(asset2)
, ammID_(keylet::amm(asset1_.issue(), asset2_.issue()).key)
, initialLPTokens_(initialTokens(asset1, asset2))
, log_(log)
, doClose_(true)
, doClose_(close)
, lastPurchasePrice_(0)
, bidMin_()
, bidMax_()
Expand All @@ -85,7 +86,8 @@ AMM::AMM(
STAmount const& asset1,
STAmount const& asset2,
ter const& ter,
bool log)
bool log,
bool close)
: AMM(env,
account,
asset1,
Expand All @@ -96,7 +98,29 @@ AMM::AMM(
std::nullopt,
std::nullopt,
std::nullopt,
ter)
ter,
close)
{
}

AMM::AMM(
Env& env,
Account const& account,
STAmount const& asset1,
STAmount const& asset2,
CreateArg const& arg)
: AMM(env,
account,
asset1,
asset2,
arg.log,
arg.tfee,
arg.fee,
arg.flags,
arg.seq,
arg.ms,
arg.err,
arg.close)
{
}

Expand Down
Loading