Compare commits

..

1 Commits

Author SHA1 Message Date
tequ
9e5f76dbf9 Reduce MagicEnum usage in server definitions
Use existing type, ledger, transaction, and transaction-result registries to build server definitions. This removes custom enum ranges and duplicated name translation logic.
2026-08-04 00:02:45 +09:00
9 changed files with 12 additions and 797 deletions

View File

@@ -242,7 +242,6 @@
#define sfLockingChainDoor ((8U << 16U) + 22U)
#define sfIssuingChainDoor ((8U << 16U) + 23U)
#define sfSubject ((8U << 16U) + 24U)
#define sfBrokerAccount ((8U << 16U) + 98U)
#define sfInform ((8U << 16U) + 99U)
#define sfIndexes ((19U << 16U) + 1U)
#define sfHashes ((19U << 16U) + 2U)

View File

@@ -34,7 +34,6 @@
// If you add an amendment here, then do not forget to increment `numFeatures`
// in include/xrpl/protocol/Feature.h.
XRPL_FEATURE(URITokenBroker, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (HookMap, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FIX (GuardDepth32, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(NamedHooks, Supported::yes, VoteBehavior::DefaultNo)

View File

@@ -319,7 +319,6 @@ TYPED_SFIELD(sfAttestationRewardAccount, ACCOUNT, 21)
TYPED_SFIELD(sfLockingChainDoor, ACCOUNT, 22)
TYPED_SFIELD(sfIssuingChainDoor, ACCOUNT, 23)
TYPED_SFIELD(sfSubject, ACCOUNT, 24)
TYPED_SFIELD(sfBrokerAccount, ACCOUNT, 98)
TYPED_SFIELD(sfInform, ACCOUNT, 99)
// vector of 256-bit

View File

@@ -314,7 +314,6 @@ TRANSACTION(ttURITOKEN_BURN, 46, URITokenBurn, ({
TRANSACTION(ttURITOKEN_BUY, 47, URITokenBuy, ({
{sfURITokenID, soeREQUIRED},
{sfAmount, soeREQUIRED},
{sfBrokerAccount, soeOPTIONAL},
}))
TRANSACTION(ttURITOKEN_CREATE_SELL_OFFER, 48, URITokenCreateSellOffer, ({

View File

@@ -7033,130 +7033,6 @@ private:
testTSHStrongWeak(env, tshNONE, __LINE__);
}
}
// A broker is an explicitly discovered weak TSH. This is required for
// XAH sales because native balance changes are not found by the generic
// balance-based weak-TSH discovery.
if (features[featureURITokenBroker] && features[fixXahauV1])
{
for (bool const collectEnabled : {false, true})
{
test::jtx::Env env{
*this,
network::makeNetworkConfig(
21337, "10", "1000000", "200000"),
features};
auto const issuer = Account("alice");
auto const owner = Account("bob");
auto const buyer = Account("carol");
auto const broker = Account("dave");
env.fund(XRP(1000), issuer, owner, buyer, broker);
env.close();
std::string const uri =
collectEnabled ? "broker-collect" : "broker-no-collect";
auto const tid = uritoken::tokenid(issuer, uri);
std::string const hexid{strHex(tid)};
env(uritoken::mint(issuer, uri),
uritoken::dest(owner),
uritoken::amt(XRP(1)),
ter(tesSUCCESS));
env.close();
env(uritoken::buy(owner, hexid),
uritoken::amt(XRP(1)),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
env(uritoken::sell(owner, hexid),
uritoken::dest(buyer),
uritoken::amt(XRP(1)),
ter(tesSUCCESS));
env.close();
if (collectEnabled)
addWeakTSH(env, broker);
setTSHHook(env, broker, false);
auto buy = uritoken::buy(buyer, hexid);
buy[sfAmount.jsonName] =
XRP(2).value().getJson(JsonOptions::none);
buy[sfBrokerAccount.jsonName] = broker.human();
env(buy, fee(XRP(1)), ter(tesSUCCESS));
env.close();
testTSHStrongWeak(
env, collectEnabled ? tshWEAK : tshNONE, __LINE__);
}
}
// If the token issuer is also the broker, TSH strengths are ORed and
// the account executes exactly once: weak for a non-burnable token and
// strong for a burnable token.
if (features[featureURITokenBroker] && features[fixXahauV1])
{
for (bool const burnable : {false, true})
{
test::jtx::Env env{
*this,
network::makeNetworkConfig(
21337, "10", "1000000", "200000"),
features};
auto const issuer = Account("alice");
auto const owner = Account("bob");
auto const buyer = Account("carol");
env.fund(XRP(1000), issuer, owner, buyer);
env.close();
std::string const uri =
burnable ? "broker-issuer-burnable" : "broker-issuer";
auto const tid = uritoken::tokenid(issuer, uri);
std::string const hexid{strHex(tid)};
env(uritoken::mint(issuer, uri),
uritoken::dest(owner),
uritoken::amt(XRP(1)),
txflags(burnable ? tfBurnable : 0),
ter(tesSUCCESS));
env.close();
env(uritoken::buy(owner, hexid),
uritoken::amt(XRP(1)),
fee(XRP(1)),
ter(tesSUCCESS));
env.close();
env(uritoken::sell(owner, hexid),
uritoken::dest(buyer),
uritoken::amt(XRP(1)),
ter(tesSUCCESS));
env.close();
if (!burnable)
addWeakTSH(env, issuer);
setTSHHook(env, issuer, burnable);
auto buy = uritoken::buy(buyer, hexid);
buy[sfAmount.jsonName] =
XRP(2).value().getJson(JsonOptions::none);
buy[sfBrokerAccount.jsonName] = issuer.human();
env(buy, fee(XRP(1)), ter(tesSUCCESS));
env.close();
testTSHStrongWeak(
env, burnable ? tshSTRONG : tshWEAK, __LINE__);
Json::Value params;
params[jss::transaction] =
env.tx()->getJson(JsonOptions::none)[jss::hash];
auto const result = env.rpc("json", "tx", to_string(params));
auto const executions =
result[jss::result][jss::meta][sfHookExecutions.jsonName];
BEAST_EXPECT(executions.size() == 1);
}
}
}
void

View File

@@ -1,465 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2026 XRPL-Labs
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <test/jtx.h>
#include <test/jtx/AMM.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/TxFlags.h>
namespace ripple {
namespace test {
struct URITokenBroker_test : public beast::unit_test::suite
{
static Json::Value
brokeredBuy(
jtx::Account const& buyer,
std::string const& tokenID,
STAmount const& amount,
jtx::Account const& broker)
{
auto tx = jtx::uritoken::buy(buyer, tokenID);
tx[sfAmount.jsonName] = amount.getJson(JsonOptions::none);
tx[sfBrokerAccount.jsonName] = broker.human();
return tx;
}
static std::string
mintAndSell(
jtx::Env& env,
jtx::Account const& seller,
std::string const& uri,
STAmount const& amount,
std::optional<jtx::Account> const& destination = std::nullopt)
{
auto const id = strHex(jtx::uritoken::tokenid(seller, uri));
env(jtx::uritoken::mint(seller, uri));
if (destination)
env(jtx::uritoken::sell(seller, id),
jtx::uritoken::amt(amount),
jtx::uritoken::dest(*destination));
else
env(jtx::uritoken::sell(seller, id), jtx::uritoken::amt(amount));
env.close();
return id;
}
static std::shared_ptr<SLE const>
token(jtx::Env const& env, uint256 const& id)
{
return env.le(Keylet{ltURI_TOKEN, id});
}
static std::shared_ptr<SLE const>
token(jtx::Env const& env, std::string const& id)
{
uint256 tokenID;
if (!tokenID.parseHex(id))
return nullptr;
return token(env, tokenID);
}
void
testAmendment(FeatureBitset const& features)
{
testcase("amendment");
using namespace jtx;
Account const seller{"seller"};
Account const buyer{"buyer"};
Account const broker{"broker"};
// A broker-less buy remains valid when the broker amendment is off.
{
Env env{*this, features - featureURITokenBroker};
env.fund(XRP(1000), seller, buyer, broker);
auto const id = mintAndSell(env, seller, "no-broker", XRP(10));
env(uritoken::buy(buyer, id), uritoken::amt(XRP(10)));
}
// BrokerAccount requires both URITokenBroker and fixXahauV1.
for (auto const disabled : {featureURITokenBroker, fixXahauV1})
{
Env env{*this, features - disabled};
env.fund(XRP(1000), seller, buyer, broker);
auto const id =
mintAndSell(env, seller, to_string(disabled), XRP(10));
env(brokeredBuy(buyer, id, XRP(11), broker), ter(temDISABLED));
}
}
void
testMalformed(FeatureBitset const& features)
{
testcase("preflight");
using namespace jtx;
Env env{*this, features};
Account const seller{"seller"};
Account const buyer{"buyer"};
env.fund(XRP(1000), seller, buyer);
auto const id = mintAndSell(env, seller, "preflight", XRP(10));
env(brokeredBuy(buyer, id, XRP(11), buyer), ter(temREDUNDANT));
auto zero = uritoken::buy(buyer, id);
zero[sfAmount.jsonName] = XRP(11).value().getJson(JsonOptions::none);
zero[sfBrokerAccount.jsonName] = "rrrrrrrrrrrrrrrrrrrrrhoLvTp";
env(zero, ter(temMALFORMED));
// BrokerAccount is only part of the URITokenBuy template.
auto mint = uritoken::mint(seller, "broker-on-mint");
mint[sfBrokerAccount.jsonName] = buyer.human();
env(mint, ter(temMALFORMED));
// BrokerAccount belongs only to URITokenBuy's serialization template.
auto payment = pay(buyer, seller, XRP(1));
payment[sfBrokerAccount.jsonName] = seller.human();
env(payment, ter(temMALFORMED));
}
void
testPreclaim(FeatureBitset const& features)
{
testcase("preclaim");
using namespace jtx;
Account const seller{"seller"};
Account const buyer{"buyer"};
Account const broker{"broker"};
Account const missing{"missing"};
// Missing broker account.
{
Env env{*this, features};
env.fund(XRP(1000), seller, buyer);
env.memoize(missing);
auto const id = mintAndSell(env, seller, "missing", XRP(10));
env(brokeredBuy(buyer, id, XRP(11), missing), ter(tecNO_TARGET));
}
// The seller cannot also receive the broker spread.
{
Env env{*this, features};
env.fund(XRP(1000), seller, buyer);
auto const id = mintAndSell(env, seller, "seller", XRP(10));
env(brokeredBuy(buyer, id, XRP(11), seller), ter(tecNO_PERMISSION));
}
// A brokered sale requires both a positive price and positive spread.
{
Env env{*this, features};
env.fund(XRP(1000), seller, buyer, broker);
auto const zeroID = mintAndSell(env, seller, "zero", XRP(0), buyer);
env(brokeredBuy(buyer, zeroID, XRP(1), broker),
ter(tecNO_PERMISSION));
// The legacy destination-restricted zero-price buy is unchanged.
env(uritoken::buy(buyer, zeroID), uritoken::amt(XRP(0)));
auto const id = mintAndSell(env, seller, "spread", XRP(10));
env(brokeredBuy(buyer, id, XRP(9), broker),
ter(tecINSUFFICIENT_PAYMENT));
env(brokeredBuy(buyer, id, XRP(10), broker), ter(tecNO_PERMISSION));
}
// An IOU broker must already have a trust line.
{
Env env{*this, features};
Account const gw{"gateway"};
auto const USD = gw["USD"];
env.fund(XRP(1000), seller, buyer, broker, gw);
env.trust(USD(1000), seller, buyer);
env.close();
env(pay(gw, buyer, USD(100)));
auto const id = mintAndSell(env, seller, "no-line", USD(10));
env(brokeredBuy(buyer, id, USD(11), broker), ter(tecNO_LINE));
}
// AMM pseudo-accounts cannot be named as brokers.
{
Env env{*this, features | featureAMM};
Account const gw{"gateway"};
auto const USD = gw["USD"];
env.fund(XRP(30'000), seller, buyer, gw);
env.trust(USD(30'000), seller, buyer);
env.close();
env(pay(gw, seller, USD(20'000)));
AMM amm{env, seller, XRP(10'000), USD(10'000)};
auto const id = mintAndSell(env, seller, "amm", XRP(10));
auto tx = uritoken::buy(buyer, id);
tx[sfAmount.jsonName] = XRP(11).value().getJson(JsonOptions::none);
tx[sfBrokerAccount.jsonName] = toBase58(amm.ammAccount());
env(tx, ter(tecNO_PERMISSION));
}
}
void
testXAH(FeatureBitset const& features)
{
testcase("XAH settlement");
using namespace jtx;
Env env{*this, features};
Account const seller{"seller"};
Account const buyer{"buyer"};
Account const broker{"broker"};
env.fund(XRP(1000), seller, buyer, broker);
env(fset(seller, asfDepositAuth));
env(fset(broker, asfDepositAuth));
env.close();
auto const id = mintAndSell(env, seller, "xah", XRP(100));
auto const sellerBefore = env.balance(seller);
auto const buyerBefore = env.balance(buyer);
auto const brokerBefore = env.balance(broker);
auto const buyerOwners = env.ownerCount(buyer);
auto const brokerOwners = env.ownerCount(broker);
auto const fee = env.current()->fees().base;
env(brokeredBuy(buyer, id, XRP(110), broker));
env.close();
BEAST_EXPECT(env.balance(seller) == sellerBefore + XRP(100));
BEAST_EXPECT(env.balance(broker) == brokerBefore + XRP(10));
BEAST_EXPECT(env.balance(buyer) == buyerBefore - XRP(110) - fee);
BEAST_EXPECT(env.ownerCount(buyer) == buyerOwners + 1);
BEAST_EXPECT(env.ownerCount(broker) == brokerOwners);
auto const sle = token(env, id);
if (!BEAST_EXPECT(sle))
return;
BEAST_EXPECT((*sle)[sfOwner] == buyer.id());
BEAST_EXPECT(!sle->isFieldPresent(sfAmount));
BEAST_EXPECT(!sle->isFieldPresent(sfDestination));
}
void
testXAHReserve(FeatureBitset const& features)
{
testcase("XAH reserve boundary");
using namespace jtx;
Env env{*this, features};
Account const seller{"seller"};
Account const buyer{"buyer"};
Account const broker{"broker"};
env.fund(XRP(1000), seller, broker);
env.fund(XRP(300), buyer);
env.close();
auto const id = mintAndSell(env, seller, "reserve", XRP(1));
auto const sellerBefore = env.balance(seller);
auto const brokerBefore = env.balance(broker);
// The buyer needs the next owner reserve plus the full signed amount,
// not merely the seller's one-XAH listing amount.
env(brokeredBuy(buyer, id, XRP(51), broker),
ter(tecINSUFFICIENT_FUNDS));
BEAST_EXPECT(env.balance(seller) == sellerBefore);
BEAST_EXPECT(env.balance(broker) == brokerBefore);
auto const sle = token(env, id);
BEAST_EXPECT(sle && (*sle)[sfOwner] == seller.id());
}
void
testIOU(FeatureBitset const& features)
{
testcase("IOU settlement");
using namespace jtx;
Account const seller{"seller"};
Account const buyer{"buyer"};
Account const broker{"broker"};
Account const gw{"gateway"};
auto const USD = gw["USD"];
// Both recipients bear the transfer rate; buyer debit remains exact.
{
Env env{*this, features};
env.fund(XRP(1000), seller, buyer, broker, gw);
env(rate(gw, 1.25));
env.trust(USD(1000), seller, buyer, broker);
env.close();
env(trust(gw, seller["USD"](1000), tfClearNoRipple));
env(trust(gw, buyer["USD"](1000), tfClearNoRipple));
env(trust(gw, broker["USD"](1000), tfClearNoRipple));
env.close();
env(pay(gw, buyer, USD(1000)));
auto const id = mintAndSell(env, seller, "rate", USD(100));
auto const buyerBefore = env.balance(buyer, USD.issue());
auto const brokerOwners = env.ownerCount(broker);
env(brokeredBuy(buyer, id, USD(110), broker));
env.close();
BEAST_EXPECT(env.balance(seller, USD.issue()) == USD(80));
BEAST_EXPECT(env.balance(broker, USD.issue()) == USD(8));
BEAST_EXPECT(
env.balance(buyer, USD.issue()) == buyerBefore - USD(110));
BEAST_EXPECT(env.ownerCount(broker) == brokerOwners);
}
// The issuer may be the broker and needs no trust line.
{
Env env{*this, features};
env.fund(XRP(1000), seller, buyer, gw);
env(rate(gw, 1.25));
env.trust(USD(1000), seller, buyer);
env.close();
env(trust(gw, seller["USD"](1000), tfClearNoRipple));
env(trust(gw, buyer["USD"](1000), tfClearNoRipple));
env.close();
env(pay(gw, buyer, USD(1000)));
auto const id = mintAndSell(env, seller, "issuer", USD(100));
auto const buyerBefore = env.balance(buyer, USD.issue());
env(brokeredBuy(buyer, id, USD(110), gw));
env.close();
BEAST_EXPECT(env.balance(seller, USD.issue()) == USD(80));
BEAST_EXPECT(
env.balance(buyer, USD.issue()) == buyerBefore - USD(110));
}
// Existing line limits are not consulted and no owner object is added.
{
Env env{*this, features};
env.fund(XRP(1000), seller, buyer, broker, gw);
env.trust(USD(1000), seller, buyer);
env.trust(USD(1), broker);
env.close();
env(trust(gw, seller["USD"](1000), tfClearNoRipple));
env(trust(gw, buyer["USD"](1000), tfClearNoRipple));
env(trust(gw, broker["USD"](1), tfClearNoRipple));
env.close();
env(pay(gw, buyer, USD(100)));
env(pay(gw, broker, USD(1)));
auto const id = mintAndSell(env, seller, "limit", USD(10));
auto const brokerOwners = env.ownerCount(broker);
env(brokeredBuy(buyer, id, USD(20), broker));
env.close();
BEAST_EXPECT(env.balance(broker, USD.issue()) == USD(11));
BEAST_EXPECT(env.ownerCount(broker) == brokerOwners);
}
}
void
testIOUPermissions(FeatureBitset const& features)
{
testcase("IOU permissions");
using namespace jtx;
Account const seller{"seller"};
Account const buyer{"buyer"};
Account const broker{"broker"};
Account const gw{"gateway"};
auto const USD = gw["USD"];
// A frozen broker line rejects the broker leg atomically.
{
Env env{*this, features};
env.fund(XRP(1000), seller, buyer, broker, gw);
env.trust(USD(1000), seller, buyer, broker);
env.close();
env(trust(gw, seller["USD"](1000), tfClearNoRipple));
env(trust(gw, buyer["USD"](1000), tfClearNoRipple));
env(pay(gw, buyer, USD(100)));
env(trust(gw, broker["USD"](1000), tfSetFreeze | tfClearNoRipple));
env.close();
auto const id = mintAndSell(env, seller, "freeze", USD(10));
auto const buyerBefore = env.balance(buyer, USD.issue());
env(brokeredBuy(buyer, id, USD(11), broker), ter(tecFROZEN));
BEAST_EXPECT(env.balance(seller, USD.issue()) == USD(0));
BEAST_EXPECT(env.balance(broker, USD.issue()) == USD(0));
BEAST_EXPECT(env.balance(buyer, USD.issue()) == buyerBefore);
}
// Under RequireAuth, an existing but unauthorized broker line fails.
{
Env env{*this, features};
auto const sellerUSD = seller["USD"];
auto const buyerUSD = buyer["USD"];
env.fund(XRP(1000), seller, buyer, broker, gw);
env(fset(gw, asfRequireAuth));
env.close();
env.trust(USD(1000), seller, buyer, broker);
env(trust(gw, sellerUSD(1000)),
txflags(tfSetfAuth | tfClearNoRipple));
env(trust(gw, buyerUSD(1000)),
txflags(tfSetfAuth | tfClearNoRipple));
env(trust(gw, broker["USD"](1000)), txflags(tfClearNoRipple));
env.close();
env(pay(gw, buyer, USD(100)));
auto const id = mintAndSell(env, seller, "auth", USD(10));
env(brokeredBuy(buyer, id, USD(11), broker), ter(tecNO_AUTH));
}
// NoRipple on the broker leg rejects the whole transaction; the
// already-computed seller leg must also be rolled back.
{
Env env{*this, features};
env.fund(XRP(1000), seller, buyer, broker, gw);
env.trust(USD(1000), seller, buyer, broker);
env.close();
env(trust(gw, seller["USD"](1000), tfClearNoRipple));
env(trust(gw, buyer["USD"](1000), tfClearNoRipple));
env(pay(gw, buyer, USD(100)));
env(trust(gw, broker["USD"](1000), tfSetNoRipple));
env.close();
auto const id = mintAndSell(env, seller, "no-ripple", USD(10));
auto const sellerBefore = env.balance(seller, USD.issue());
auto const buyerBefore = env.balance(buyer, USD.issue());
auto const brokerBefore = env.balance(broker, USD.issue());
env(brokeredBuy(buyer, id, USD(11), broker), ter(tecPATH_DRY));
BEAST_EXPECT(env.balance(seller, USD.issue()) == sellerBefore);
BEAST_EXPECT(env.balance(buyer, USD.issue()) == buyerBefore);
BEAST_EXPECT(env.balance(broker, USD.issue()) == brokerBefore);
auto const sle = token(env, id);
BEAST_EXPECT(sle && (*sle)[sfOwner] == seller.id());
}
}
public:
void
run() override
{
auto const features = jtx::supported_amendments();
testAmendment(features);
testMalformed(features);
testPreclaim(features);
testXAH(features);
testXAHReserve(features);
testIOU(features);
testIOUPermissions(features);
}
};
BEAST_DEFINE_TESTSUITE(URITokenBroker, app, ripple);
} // namespace test
} // namespace ripple

View File

@@ -200,10 +200,6 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
issuer,
(ut->getFlags() & lsfBurnable) ? tshSTRONG : tshWEAK);
if (rv.rules().enabled(featureURITokenBroker) &&
tx.isFieldPresent(sfBrokerAccount))
ADD_TSH(tx.getAccountID(sfBrokerAccount), tshWEAK);
break;
}

View File

@@ -29,33 +29,6 @@
namespace ripple {
TER
checkBroker(
ReadView const& view,
AccountID const& broker,
AccountID const& owner,
STAmount const& saleAmount,
STAmount const& purchaseAmount)
{
auto const sleBroker = view.read(keylet::account(broker));
if (!sleBroker)
return tecNO_TARGET;
if (sleBroker->isFieldPresent(sfAMMID))
return tecNO_PERMISSION;
if (broker == owner || saleAmount == beast::zero ||
purchaseAmount == saleAmount)
return tecNO_PERMISSION;
if (!purchaseAmount.native() && broker != purchaseAmount.getIssuer() &&
!view.exists(keylet::line(
broker, purchaseAmount.getIssuer(), purchaseAmount.getCurrency())))
return tecNO_LINE;
return tesSUCCESS;
}
NotTEC
URIToken::preflight(PreflightContext const& ctx)
{
@@ -165,23 +138,6 @@ URIToken::preflight(PreflightContext const& ctx)
ctx.tx.getAccountID(sfAccount) == ctx.tx.getAccountID(sfDestination))
return temREDUNDANT;
if (ctx.tx.isFieldPresent(sfBrokerAccount))
{
if (tt != ttURITOKEN_BUY)
return temMALFORMED;
if (!ctx.rules.enabled(featureURITokenBroker) ||
!ctx.rules.enabled(fixXahauV1))
return temDISABLED;
auto const broker = ctx.tx.getAccountID(sfBrokerAccount);
if (broker == beast::zero)
return temMALFORMED;
if (broker == ctx.tx.getAccountID(sfAccount))
return temREDUNDANT;
}
return preflight2(ctx);
}
@@ -279,21 +235,6 @@ URIToken::preclaim(PreclaimContext const& ctx)
if (purchaseAmount < saleAmount)
return tecINSUFFICIENT_PAYMENT;
if (ctx.tx.isFieldPresent(sfBrokerAccount))
{
if (!ctx.view.rules().enabled(featureURITokenBroker) || !fixV1)
return temDISABLED;
if (TER const result = checkBroker(
ctx.view,
ctx.tx.getAccountID(sfBrokerAccount),
*owner,
*saleAmount,
purchaseAmount);
!isTesSuccess(result))
return result;
}
if (fixV1)
{
if (purchaseAmount.native() && saleAmount->native())
@@ -542,11 +483,6 @@ URIToken::doApply()
if (purchaseAmount.issue() != saleAmount->issue())
return temBAD_CURRENCY;
std::optional<AccountID> const broker = ctx_.tx[~sfBrokerAccount];
if (broker &&
(!sb.rules().enabled(featureURITokenBroker) || !fixV1))
return temDISABLED;
if (fixV1)
{
// this is the reworked version of the buy routine
@@ -554,14 +490,6 @@ URIToken::doApply()
if (purchaseAmount < saleAmount)
return tecINSUFFICIENT_PAYMENT;
if (broker)
{
if (TER const result = checkBroker(
sb, *broker, *owner, *saleAmount, purchaseAmount);
!isTesSuccess(result))
return result;
}
// if it's an xrp sale/purchase then no trustline needed
if (purchaseAmount.native())
{
@@ -597,23 +525,6 @@ URIToken::doApply()
return result;
}
if (broker)
{
if (TER result = trustTransferAllowed(
sb,
{account_, *broker},
purchaseAmount.issue(),
j);
!isTesSuccess(result))
{
JLOG(j.trace()) << "URIToken::doApply broker "
"trustTransferAllowed result="
<< result;
return result;
}
}
if (STAmount availableFunds{accountFunds(
sb, account_, purchaseAmount, fhZERO_IF_FROZEN, j)};
purchaseAmount > availableFunds)
@@ -625,31 +536,13 @@ URIToken::doApply()
sb,
account_,
*owner,
broker ? *saleAmount : purchaseAmount,
purchaseAmount,
j,
WaiveTransferFee::No,
false);
!isTesSuccess(result))
return result;
if (broker)
{
STAmount const spread = purchaseAmount - *saleAmount;
if (spread <= beast::zero)
return tecINTERNAL; // LCOV_EXCL_LINE
if (TER result = accountSend(
sb,
account_,
*broker,
spread,
j,
WaiveTransferFee::No,
false);
!isTesSuccess(result))
return result;
}
// add token to new owner dir
auto const newPage = sb.dirInsert(
keylet::ownerDir(account_),

View File

@@ -22,12 +22,8 @@
#include <xrpld/app/main/Application.h>
#include <xrpld/app/misc/AmendmentTable.h>
#include <xrpld/app/misc/NetworkOPs.h>
#include <xrpld/rpc/detail/TransactionSign.h>
#include <xrpl/json/json_value.h>
#include <xrpl/json/json_writer.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/RPCErr.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/digest.h>
#include <xrpl/protocol/jss.h>
@@ -35,14 +31,6 @@
#include <magic_enum.hpp>
#include <sstream>
#define MAGIC_ENUM(x, _min, _max) \
template <> \
struct magic_enum::customize::enum_range<x> \
{ \
static constexpr int min = _min; \
static constexpr int max = _max; \
};
#define MAGIC_ENUM_16(x) \
template <> \
struct magic_enum::customize::enum_range<x> \
@@ -58,15 +46,6 @@
static constexpr bool is_flags = true; \
};
MAGIC_ENUM(ripple::SerializedTypeID, -2, 10004);
MAGIC_ENUM(ripple::LedgerEntryType, 0, 255);
MAGIC_ENUM(ripple::TELcodes, -399, 300);
MAGIC_ENUM(ripple::TEMcodes, -299, -200);
MAGIC_ENUM(ripple::TEFcodes, -199, -100);
MAGIC_ENUM(ripple::TERcodes, -99, -1);
MAGIC_ENUM(ripple::TEScodes, 0, 1);
MAGIC_ENUM(ripple::TECcodes, 100, 255);
MAGIC_ENUM_16(ripple::TxType);
MAGIC_ENUM_FLAG(ripple::UniversalFlags);
MAGIC_ENUM_FLAG(ripple::AccountSetFlags);
MAGIC_ENUM_FLAG(ripple::OfferCreateFlags);
@@ -192,24 +171,19 @@ private:
ret[jss::TYPES]["Done"] = -1;
std::map<int32_t, std::string> type_map{{-1, "Done"}};
for (auto const& entry : magic_enum::enum_entries<SerializedTypeID>())
for (auto const& [rawName, typeValue] : sTypeMap)
{
const auto name = entry.second;
std::string type_name =
translate(name.data() + 4 /* remove STI_ */);
int32_t type_value = static_cast<int32_t>(entry.first);
ret[jss::TYPES][type_name] = type_value;
type_map[type_value] = type_name;
std::string typeName =
translate(std::string(rawName).substr(4) /* remove STI_ */);
ret[jss::TYPES][typeName] = typeValue;
type_map[typeValue] = typeName;
}
ret[jss::LEDGER_ENTRY_TYPES] = Json::objectValue;
ret[jss::LEDGER_ENTRY_TYPES][jss::Invalid] = -1;
for (auto const& entry : magic_enum::enum_entries<LedgerEntryType>())
for (auto const& f : LedgerFormats::getInstance())
{
const auto name = entry.second;
std::string type_name = translate(name.data() + 2 /* remove lt_ */);
int32_t type_value = static_cast<int32_t>(entry.first);
ret[jss::LEDGER_ENTRY_TYPES][type_name] = type_value;
ret[jss::LEDGER_ENTRY_TYPES][f.getName()] = f.getType();
}
ret[jss::FIELDS] = Json::arrayValue;
@@ -326,71 +300,16 @@ private:
}
ret[jss::TRANSACTION_RESULTS] = Json::objectValue;
for (auto const& entry : magic_enum::enum_entries<TELcodes>())
for (auto const& [code, terInfo] : transResults())
{
const auto name = entry.second;
ret[jss::TRANSACTION_RESULTS][STR(name)] =
static_cast<int32_t>(entry.first);
ret[jss::TRANSACTION_RESULTS][terInfo.first] = code;
}
for (auto const& entry : magic_enum::enum_entries<TEMcodes>())
{
const auto name = entry.second;
ret[jss::TRANSACTION_RESULTS][STR(name)] =
static_cast<int32_t>(entry.first);
}
for (auto const& entry : magic_enum::enum_entries<TEFcodes>())
{
const auto name = entry.second;
ret[jss::TRANSACTION_RESULTS][STR(name)] =
static_cast<int32_t>(entry.first);
}
for (auto const& entry : magic_enum::enum_entries<TERcodes>())
{
const auto name = entry.second;
ret[jss::TRANSACTION_RESULTS][STR(name)] =
static_cast<int32_t>(entry.first);
}
for (auto const& entry : magic_enum::enum_entries<TEScodes>())
{
const auto name = entry.second;
ret[jss::TRANSACTION_RESULTS][STR(name)] =
static_cast<int32_t>(entry.first);
}
for (auto const& entry : magic_enum::enum_entries<TECcodes>())
{
const auto name = entry.second;
ret[jss::TRANSACTION_RESULTS][STR(name)] =
static_cast<int32_t>(entry.first);
}
auto const translate_tt = [](std::string inp) -> std::string {
if (inp == "Amendment")
return "EnableAmendment";
if (inp == "Fee")
return "SetFee";
if (inp == "PaychanClaim")
return "PaymentChannelClaim";
if (inp == "PaychanCreate")
return "PaymentChannelCreate";
if (inp == "PaychanFund")
return "PaymentChannelFund";
if (inp == "RegularKeySet")
return "SetRegularKey";
if (inp == "HookSet")
return "SetHook";
if (inp == "RemarksSet")
return "SetRemarks";
return inp;
};
ret[jss::TRANSACTION_TYPES] = Json::objectValue;
ret[jss::TRANSACTION_TYPES][jss::Invalid] = -1;
for (auto const& entry : magic_enum::enum_entries<TxType>())
for (auto const& f : TxFormats::getInstance())
{
const auto name = entry.second;
std::string type_name = translate_tt(translate(name.data() + 2));
int32_t type_value = static_cast<int32_t>(entry.first);
ret[jss::TRANSACTION_TYPES][type_name] = type_value;
ret[jss::TRANSACTION_TYPES][f.getName()] = f.getType();
}
// Transaction Flags: