mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Merge remote-tracking branch 'origin/develop' into a1q123456/optimise-xxhash
This commit is contained in:
28
docs/build/environment.md
vendored
28
docs/build/environment.md
vendored
@@ -53,6 +53,34 @@ minimum required (see [BUILD.md][]).
|
|||||||
clang --version
|
clang --version
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Install Xcode Specific Version (Optional)
|
||||||
|
|
||||||
|
If you develop other applications using XCode you might be consistently updating to the newest version of Apple Clang.
|
||||||
|
This will likely cause issues building rippled. You may want to install a specific version of Xcode:
|
||||||
|
|
||||||
|
1. **Download Xcode**
|
||||||
|
|
||||||
|
- Visit [Apple Developer Downloads](https://developer.apple.com/download/more/)
|
||||||
|
- Sign in with your Apple Developer account
|
||||||
|
- Search for an Xcode version that includes **Apple Clang (Expected Version)**
|
||||||
|
- Download the `.xip` file
|
||||||
|
|
||||||
|
2. **Install and Configure Xcode**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Extract the .xip file and rename for version management
|
||||||
|
# Example: Xcode_16.2.app
|
||||||
|
|
||||||
|
# Move to Applications directory
|
||||||
|
sudo mv Xcode_16.2.app /Applications/
|
||||||
|
|
||||||
|
# Set as default toolchain (persistent)
|
||||||
|
sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
|
||||||
|
|
||||||
|
# Set as environment variable (temporary)
|
||||||
|
export DEVELOPER_DIR=/Applications/Xcode_16.2.app/Contents/Developer
|
||||||
|
```
|
||||||
|
|
||||||
The command line developer tools should include Git too:
|
The command line developer tools should include Git too:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
// If you add an amendment here, then do not forget to increment `numFeatures`
|
// If you add an amendment here, then do not forget to increment `numFeatures`
|
||||||
// in include/xrpl/protocol/Feature.h.
|
// in include/xrpl/protocol/Feature.h.
|
||||||
|
|
||||||
|
XRPL_FIX (MPTDeliveredAmount, Supported::no, VoteBehavior::DefaultNo)
|
||||||
XRPL_FIX (AMMClawbackRounding, Supported::no, VoteBehavior::DefaultNo)
|
XRPL_FIX (AMMClawbackRounding, Supported::no, VoteBehavior::DefaultNo)
|
||||||
XRPL_FEATURE(TokenEscrow, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FEATURE(TokenEscrow, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
XRPL_FIX (EnforceNFTokenTrustlineV2, Supported::yes, VoteBehavior::DefaultNo)
|
XRPL_FIX (EnforceNFTokenTrustlineV2, Supported::yes, VoteBehavior::DefaultNo)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include <test/jtx/WSClient.h>
|
#include <test/jtx/WSClient.h>
|
||||||
|
|
||||||
#include <xrpl/beast/unit_test.h>
|
#include <xrpl/beast/unit_test.h>
|
||||||
|
#include <xrpl/beast/unit_test/suite.h>
|
||||||
#include <xrpl/protocol/jss.h>
|
#include <xrpl/protocol/jss.h>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
@@ -329,12 +330,95 @@ class DeliveredAmount_test : public beast::unit_test::suite
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testMPTDeliveredAmountRPC(FeatureBitset features)
|
||||||
|
{
|
||||||
|
testcase("MPT DeliveredAmount");
|
||||||
|
|
||||||
|
using namespace jtx;
|
||||||
|
Account const alice("alice");
|
||||||
|
Account const carol("carol");
|
||||||
|
Account const bob("bob");
|
||||||
|
Env env{*this, features};
|
||||||
|
|
||||||
|
MPTTester mptAlice(
|
||||||
|
env, alice, {.holders = {bob, carol}, .close = false});
|
||||||
|
|
||||||
|
mptAlice.create(
|
||||||
|
{.transferFee = 25000,
|
||||||
|
.ownerCount = 1,
|
||||||
|
.holderCount = 0,
|
||||||
|
.flags = tfMPTCanTransfer});
|
||||||
|
auto const MPT = mptAlice["MPT"];
|
||||||
|
|
||||||
|
mptAlice.authorize({.account = bob});
|
||||||
|
mptAlice.authorize({.account = carol});
|
||||||
|
|
||||||
|
// issuer to holder
|
||||||
|
mptAlice.pay(alice, bob, 10000);
|
||||||
|
|
||||||
|
// holder to holder
|
||||||
|
env(pay(bob, carol, mptAlice.mpt(1000)), txflags(tfPartialPayment));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// Get the hash for the most recent transaction.
|
||||||
|
std::string txHash{
|
||||||
|
env.tx()->getJson(JsonOptions::none)[jss::hash].asString()};
|
||||||
|
Json::Value meta = env.rpc("tx", txHash)[jss::result][jss::meta];
|
||||||
|
|
||||||
|
if (features[fixMPTDeliveredAmount])
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(
|
||||||
|
meta[sfDeliveredAmount.jsonName] ==
|
||||||
|
STAmount{MPT(800)}.getJson(JsonOptions::none));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
meta[jss::delivered_amount] ==
|
||||||
|
STAmount{MPT(800)}.getJson(JsonOptions::none));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(!meta.isMember(sfDeliveredAmount.jsonName));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
meta[jss::delivered_amount] = Json::Value("unavailable"));
|
||||||
|
}
|
||||||
|
|
||||||
|
env(pay(bob, carol, MPT(1000)),
|
||||||
|
sendmax(MPT(1200)),
|
||||||
|
txflags(tfPartialPayment));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
txHash = env.tx()->getJson(JsonOptions::none)[jss::hash].asString();
|
||||||
|
meta = env.rpc("tx", txHash)[jss::result][jss::meta];
|
||||||
|
|
||||||
|
if (features[fixMPTDeliveredAmount])
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(
|
||||||
|
meta[sfDeliveredAmount.jsonName] ==
|
||||||
|
STAmount{MPT(960)}.getJson(JsonOptions::none));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
meta[jss::delivered_amount] ==
|
||||||
|
STAmount{MPT(960)}.getJson(JsonOptions::none));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(!meta.isMember(sfDeliveredAmount.jsonName));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
meta[jss::delivered_amount] = Json::Value("unavailable"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void
|
void
|
||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
|
using namespace test::jtx;
|
||||||
|
FeatureBitset const all{testable_amendments()};
|
||||||
|
|
||||||
testTxDeliveredAmountRPC();
|
testTxDeliveredAmountRPC();
|
||||||
testAccountDeliveredAmountSubscribe();
|
testAccountDeliveredAmountSubscribe();
|
||||||
|
|
||||||
|
testMPTDeliveredAmountRPC(all - fixMPTDeliveredAmount);
|
||||||
|
testMPTDeliveredAmountRPC(all);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -175,126 +175,18 @@ MPTokenAuthorize::createMPToken(
|
|||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
TER
|
|
||||||
MPTokenAuthorize::authorize(
|
|
||||||
ApplyView& view,
|
|
||||||
beast::Journal journal,
|
|
||||||
MPTAuthorizeArgs const& args)
|
|
||||||
{
|
|
||||||
auto const sleAcct = view.peek(keylet::account(args.account));
|
|
||||||
if (!sleAcct)
|
|
||||||
return tecINTERNAL;
|
|
||||||
|
|
||||||
// If the account that submitted the tx is a holder
|
|
||||||
// Note: `account_` is holder's account
|
|
||||||
// `holderID` is NOT used
|
|
||||||
if (!args.holderID)
|
|
||||||
{
|
|
||||||
// When a holder wants to unauthorize/delete a MPT, the ledger must
|
|
||||||
// - delete mptokenKey from owner directory
|
|
||||||
// - delete the MPToken
|
|
||||||
if (args.flags & tfMPTUnauthorize)
|
|
||||||
{
|
|
||||||
auto const mptokenKey =
|
|
||||||
keylet::mptoken(args.mptIssuanceID, args.account);
|
|
||||||
auto const sleMpt = view.peek(mptokenKey);
|
|
||||||
if (!sleMpt || (*sleMpt)[sfMPTAmount] != 0)
|
|
||||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
|
||||||
|
|
||||||
if (!view.dirRemove(
|
|
||||||
keylet::ownerDir(args.account),
|
|
||||||
(*sleMpt)[sfOwnerNode],
|
|
||||||
sleMpt->key(),
|
|
||||||
false))
|
|
||||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
|
||||||
|
|
||||||
adjustOwnerCount(view, sleAcct, -1, journal);
|
|
||||||
|
|
||||||
view.erase(sleMpt);
|
|
||||||
return tesSUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A potential holder wants to authorize/hold a mpt, the ledger must:
|
|
||||||
// - add the new mptokenKey to the owner directory
|
|
||||||
// - create the MPToken object for the holder
|
|
||||||
|
|
||||||
// The reserve that is required to create the MPToken. Note
|
|
||||||
// that although the reserve increases with every item
|
|
||||||
// an account owns, in the case of MPTokens we only
|
|
||||||
// *enforce* a reserve if the user owns more than two
|
|
||||||
// items. This is similar to the reserve requirements of trust lines.
|
|
||||||
std::uint32_t const uOwnerCount = sleAcct->getFieldU32(sfOwnerCount);
|
|
||||||
XRPAmount const reserveCreate(
|
|
||||||
(uOwnerCount < 2) ? XRPAmount(beast::zero)
|
|
||||||
: view.fees().accountReserve(uOwnerCount + 1));
|
|
||||||
|
|
||||||
if (args.priorBalance < reserveCreate)
|
|
||||||
return tecINSUFFICIENT_RESERVE;
|
|
||||||
|
|
||||||
auto const mptokenKey =
|
|
||||||
keylet::mptoken(args.mptIssuanceID, args.account);
|
|
||||||
auto mptoken = std::make_shared<SLE>(mptokenKey);
|
|
||||||
if (auto ter = dirLink(view, args.account, mptoken))
|
|
||||||
return ter; // LCOV_EXCL_LINE
|
|
||||||
|
|
||||||
(*mptoken)[sfAccount] = args.account;
|
|
||||||
(*mptoken)[sfMPTokenIssuanceID] = args.mptIssuanceID;
|
|
||||||
(*mptoken)[sfFlags] = 0;
|
|
||||||
view.insert(mptoken);
|
|
||||||
|
|
||||||
// Update owner count.
|
|
||||||
adjustOwnerCount(view, sleAcct, 1, journal);
|
|
||||||
|
|
||||||
return tesSUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto const sleMptIssuance =
|
|
||||||
view.read(keylet::mptIssuance(args.mptIssuanceID));
|
|
||||||
if (!sleMptIssuance)
|
|
||||||
return tecINTERNAL;
|
|
||||||
|
|
||||||
// If the account that submitted this tx is the issuer of the MPT
|
|
||||||
// Note: `account_` is issuer's account
|
|
||||||
// `holderID` is holder's account
|
|
||||||
if (args.account != (*sleMptIssuance)[sfIssuer])
|
|
||||||
return tecINTERNAL;
|
|
||||||
|
|
||||||
auto const sleMpt =
|
|
||||||
view.peek(keylet::mptoken(args.mptIssuanceID, *args.holderID));
|
|
||||||
if (!sleMpt)
|
|
||||||
return tecINTERNAL;
|
|
||||||
|
|
||||||
std::uint32_t const flagsIn = sleMpt->getFieldU32(sfFlags);
|
|
||||||
std::uint32_t flagsOut = flagsIn;
|
|
||||||
|
|
||||||
// Issuer wants to unauthorize the holder, unset lsfMPTAuthorized on
|
|
||||||
// their MPToken
|
|
||||||
if (args.flags & tfMPTUnauthorize)
|
|
||||||
flagsOut &= ~lsfMPTAuthorized;
|
|
||||||
// Issuer wants to authorize a holder, set lsfMPTAuthorized on their
|
|
||||||
// MPToken
|
|
||||||
else
|
|
||||||
flagsOut |= lsfMPTAuthorized;
|
|
||||||
|
|
||||||
if (flagsIn != flagsOut)
|
|
||||||
sleMpt->setFieldU32(sfFlags, flagsOut);
|
|
||||||
|
|
||||||
view.update(sleMpt);
|
|
||||||
return tesSUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
TER
|
TER
|
||||||
MPTokenAuthorize::doApply()
|
MPTokenAuthorize::doApply()
|
||||||
{
|
{
|
||||||
auto const& tx = ctx_.tx;
|
auto const& tx = ctx_.tx;
|
||||||
return authorize(
|
return authorizeMPToken(
|
||||||
ctx_.view(),
|
ctx_.view(),
|
||||||
|
mPriorBalance,
|
||||||
|
tx[sfMPTokenIssuanceID],
|
||||||
|
account_,
|
||||||
ctx_.journal,
|
ctx_.journal,
|
||||||
{.priorBalance = mPriorBalance,
|
tx.getFlags(),
|
||||||
.mptIssuanceID = tx[sfMPTokenIssuanceID],
|
tx[~sfHolder]);
|
||||||
.account = account_,
|
|
||||||
.flags = tx.getFlags(),
|
|
||||||
.holderID = tx[~sfHolder]});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|||||||
@@ -48,12 +48,6 @@ public:
|
|||||||
static TER
|
static TER
|
||||||
preclaim(PreclaimContext const& ctx);
|
preclaim(PreclaimContext const& ctx);
|
||||||
|
|
||||||
static TER
|
|
||||||
authorize(
|
|
||||||
ApplyView& view,
|
|
||||||
beast::Journal journal,
|
|
||||||
MPTAuthorizeArgs const& args);
|
|
||||||
|
|
||||||
static TER
|
static TER
|
||||||
createMPToken(
|
createMPToken(
|
||||||
ApplyView& view,
|
ApplyView& view,
|
||||||
|
|||||||
@@ -580,7 +580,16 @@ Payment::doApply()
|
|||||||
auto res = accountSend(
|
auto res = accountSend(
|
||||||
pv, account_, dstAccountID, amountDeliver, ctx_.journal);
|
pv, account_, dstAccountID, amountDeliver, ctx_.journal);
|
||||||
if (res == tesSUCCESS)
|
if (res == tesSUCCESS)
|
||||||
|
{
|
||||||
pv.apply(ctx_.rawView());
|
pv.apply(ctx_.rawView());
|
||||||
|
|
||||||
|
// If the actual amount delivered is different from the original
|
||||||
|
// amount due to partial payment or transfer fee, we need to update
|
||||||
|
// DelieveredAmount using the actual delivered amount
|
||||||
|
if (view().rules().enabled(fixMPTDeliveredAmount) &&
|
||||||
|
amountDeliver != dstAmount)
|
||||||
|
ctx_.deliver(amountDeliver);
|
||||||
|
}
|
||||||
else if (res == tecINSUFFICIENT_FUNDS || res == tecPATH_DRY)
|
else if (res == tecINSUFFICIENT_FUNDS || res == tecPATH_DRY)
|
||||||
res = tecPATH_PARTIAL;
|
res = tecPATH_PARTIAL;
|
||||||
|
|
||||||
|
|||||||
@@ -210,12 +210,12 @@ VaultDeposit::doApply()
|
|||||||
auto sleMpt = view().read(keylet::mptoken(mptIssuanceID, account_));
|
auto sleMpt = view().read(keylet::mptoken(mptIssuanceID, account_));
|
||||||
if (!sleMpt)
|
if (!sleMpt)
|
||||||
{
|
{
|
||||||
if (auto const err = MPTokenAuthorize::authorize(
|
if (auto const err = authorizeMPToken(
|
||||||
view(),
|
view(),
|
||||||
ctx_.journal,
|
mPriorBalance,
|
||||||
{.priorBalance = mPriorBalance,
|
mptIssuanceID->value(),
|
||||||
.mptIssuanceID = mptIssuanceID->value(),
|
account_,
|
||||||
.account = account_});
|
ctx_.journal);
|
||||||
!isTesSuccess(err))
|
!isTesSuccess(err))
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -223,15 +223,15 @@ VaultDeposit::doApply()
|
|||||||
// If the vault is private, set the authorized flag for the vault owner
|
// If the vault is private, set the authorized flag for the vault owner
|
||||||
if (vault->isFlag(tfVaultPrivate))
|
if (vault->isFlag(tfVaultPrivate))
|
||||||
{
|
{
|
||||||
if (auto const err = MPTokenAuthorize::authorize(
|
if (auto const err = authorizeMPToken(
|
||||||
view(),
|
view(),
|
||||||
|
mPriorBalance, // priorBalance
|
||||||
|
mptIssuanceID->value(), // mptIssuanceID
|
||||||
|
sleIssuance->at(sfIssuer), // account
|
||||||
ctx_.journal,
|
ctx_.journal,
|
||||||
{
|
{}, // flags
|
||||||
.priorBalance = mPriorBalance,
|
account_ // holderID
|
||||||
.mptIssuanceID = mptIssuanceID->value(),
|
);
|
||||||
.account = sleIssuance->at(sfIssuer),
|
|
||||||
.holderID = account_,
|
|
||||||
});
|
|
||||||
!isTesSuccess(err))
|
!isTesSuccess(err))
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -600,6 +600,16 @@ addEmptyHolding(
|
|||||||
asset.value());
|
asset.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] TER
|
||||||
|
authorizeMPToken(
|
||||||
|
ApplyView& view,
|
||||||
|
XRPAmount const& priorBalance,
|
||||||
|
MPTID const& mptIssuanceID,
|
||||||
|
AccountID const& account,
|
||||||
|
beast::Journal journal,
|
||||||
|
std::uint32_t flags = 0,
|
||||||
|
std::optional<AccountID> holderID = std::nullopt);
|
||||||
|
|
||||||
// VFALCO NOTE Both STAmount parameters should just
|
// VFALCO NOTE Both STAmount parameters should just
|
||||||
// be "Amount", a unit-less number.
|
// be "Amount", a unit-less number.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <xrpld/app/misc/CredentialHelpers.h>
|
#include <xrpld/app/misc/CredentialHelpers.h>
|
||||||
#include <xrpld/app/tx/detail/MPTokenAuthorize.h>
|
|
||||||
#include <xrpld/ledger/ReadView.h>
|
#include <xrpld/ledger/ReadView.h>
|
||||||
#include <xrpld/ledger/View.h>
|
#include <xrpld/ledger/View.h>
|
||||||
|
|
||||||
@@ -1215,12 +1214,115 @@ addEmptyHolding(
|
|||||||
if (view.peek(keylet::mptoken(mptID, accountID)))
|
if (view.peek(keylet::mptoken(mptID, accountID)))
|
||||||
return tecDUPLICATE;
|
return tecDUPLICATE;
|
||||||
|
|
||||||
return MPTokenAuthorize::authorize(
|
return authorizeMPToken(view, priorBalance, mptID, accountID, journal);
|
||||||
view,
|
}
|
||||||
journal,
|
|
||||||
{.priorBalance = priorBalance,
|
[[nodiscard]] TER
|
||||||
.mptIssuanceID = mptID,
|
authorizeMPToken(
|
||||||
.account = accountID});
|
ApplyView& view,
|
||||||
|
XRPAmount const& priorBalance,
|
||||||
|
MPTID const& mptIssuanceID,
|
||||||
|
AccountID const& account,
|
||||||
|
beast::Journal journal,
|
||||||
|
std::uint32_t flags,
|
||||||
|
std::optional<AccountID> holderID)
|
||||||
|
{
|
||||||
|
auto const sleAcct = view.peek(keylet::account(account));
|
||||||
|
if (!sleAcct)
|
||||||
|
return tecINTERNAL;
|
||||||
|
|
||||||
|
// If the account that submitted the tx is a holder
|
||||||
|
// Note: `account_` is holder's account
|
||||||
|
// `holderID` is NOT used
|
||||||
|
if (!holderID)
|
||||||
|
{
|
||||||
|
// When a holder wants to unauthorize/delete a MPT, the ledger must
|
||||||
|
// - delete mptokenKey from owner directory
|
||||||
|
// - delete the MPToken
|
||||||
|
if (flags & tfMPTUnauthorize)
|
||||||
|
{
|
||||||
|
auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
|
||||||
|
auto const sleMpt = view.peek(mptokenKey);
|
||||||
|
if (!sleMpt || (*sleMpt)[sfMPTAmount] != 0)
|
||||||
|
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||||
|
|
||||||
|
if (!view.dirRemove(
|
||||||
|
keylet::ownerDir(account),
|
||||||
|
(*sleMpt)[sfOwnerNode],
|
||||||
|
sleMpt->key(),
|
||||||
|
false))
|
||||||
|
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||||
|
|
||||||
|
adjustOwnerCount(view, sleAcct, -1, journal);
|
||||||
|
|
||||||
|
view.erase(sleMpt);
|
||||||
|
return tesSUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A potential holder wants to authorize/hold a mpt, the ledger must:
|
||||||
|
// - add the new mptokenKey to the owner directory
|
||||||
|
// - create the MPToken object for the holder
|
||||||
|
|
||||||
|
// The reserve that is required to create the MPToken. Note
|
||||||
|
// that although the reserve increases with every item
|
||||||
|
// an account owns, in the case of MPTokens we only
|
||||||
|
// *enforce* a reserve if the user owns more than two
|
||||||
|
// items. This is similar to the reserve requirements of trust lines.
|
||||||
|
std::uint32_t const uOwnerCount = sleAcct->getFieldU32(sfOwnerCount);
|
||||||
|
XRPAmount const reserveCreate(
|
||||||
|
(uOwnerCount < 2) ? XRPAmount(beast::zero)
|
||||||
|
: view.fees().accountReserve(uOwnerCount + 1));
|
||||||
|
|
||||||
|
if (priorBalance < reserveCreate)
|
||||||
|
return tecINSUFFICIENT_RESERVE;
|
||||||
|
|
||||||
|
auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
|
||||||
|
auto mptoken = std::make_shared<SLE>(mptokenKey);
|
||||||
|
if (auto ter = dirLink(view, account, mptoken))
|
||||||
|
return ter; // LCOV_EXCL_LINE
|
||||||
|
|
||||||
|
(*mptoken)[sfAccount] = account;
|
||||||
|
(*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID;
|
||||||
|
(*mptoken)[sfFlags] = 0;
|
||||||
|
view.insert(mptoken);
|
||||||
|
|
||||||
|
// Update owner count.
|
||||||
|
adjustOwnerCount(view, sleAcct, 1, journal);
|
||||||
|
|
||||||
|
return tesSUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const sleMptIssuance = view.read(keylet::mptIssuance(mptIssuanceID));
|
||||||
|
if (!sleMptIssuance)
|
||||||
|
return tecINTERNAL;
|
||||||
|
|
||||||
|
// If the account that submitted this tx is the issuer of the MPT
|
||||||
|
// Note: `account_` is issuer's account
|
||||||
|
// `holderID` is holder's account
|
||||||
|
if (account != (*sleMptIssuance)[sfIssuer])
|
||||||
|
return tecINTERNAL;
|
||||||
|
|
||||||
|
auto const sleMpt = view.peek(keylet::mptoken(mptIssuanceID, *holderID));
|
||||||
|
if (!sleMpt)
|
||||||
|
return tecINTERNAL;
|
||||||
|
|
||||||
|
std::uint32_t const flagsIn = sleMpt->getFieldU32(sfFlags);
|
||||||
|
std::uint32_t flagsOut = flagsIn;
|
||||||
|
|
||||||
|
// Issuer wants to unauthorize the holder, unset lsfMPTAuthorized on
|
||||||
|
// their MPToken
|
||||||
|
if (flags & tfMPTUnauthorize)
|
||||||
|
flagsOut &= ~lsfMPTAuthorized;
|
||||||
|
// Issuer wants to authorize a holder, set lsfMPTAuthorized on their
|
||||||
|
// MPToken
|
||||||
|
else
|
||||||
|
flagsOut |= lsfMPTAuthorized;
|
||||||
|
|
||||||
|
if (flagsIn != flagsOut)
|
||||||
|
sleMpt->setFieldU32(sfFlags, flagsOut);
|
||||||
|
|
||||||
|
view.update(sleMpt);
|
||||||
|
return tesSUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
TER
|
TER
|
||||||
@@ -1418,13 +1520,14 @@ removeEmptyHolding(
|
|||||||
if (mptoken->at(sfMPTAmount) != 0)
|
if (mptoken->at(sfMPTAmount) != 0)
|
||||||
return tecHAS_OBLIGATIONS;
|
return tecHAS_OBLIGATIONS;
|
||||||
|
|
||||||
return MPTokenAuthorize::authorize(
|
return authorizeMPToken(
|
||||||
view,
|
view,
|
||||||
|
{}, // priorBalance
|
||||||
|
mptID,
|
||||||
|
accountID,
|
||||||
journal,
|
journal,
|
||||||
{.priorBalance = {},
|
tfMPTUnauthorize // flags
|
||||||
.mptIssuanceID = mptID,
|
);
|
||||||
.account = accountID,
|
|
||||||
.flags = tfMPTUnauthorize});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TER
|
TER
|
||||||
@@ -2497,15 +2600,12 @@ enforceMPTokenAuthorization(
|
|||||||
XRPL_ASSERT(
|
XRPL_ASSERT(
|
||||||
maybeDomainID.has_value() && sleToken == nullptr,
|
maybeDomainID.has_value() && sleToken == nullptr,
|
||||||
"ripple::enforceMPTokenAuthorization : new MPToken for domain");
|
"ripple::enforceMPTokenAuthorization : new MPToken for domain");
|
||||||
if (auto const err = MPTokenAuthorize::authorize(
|
if (auto const err = authorizeMPToken(
|
||||||
view,
|
view,
|
||||||
j,
|
priorBalance, // priorBalance
|
||||||
{
|
mptIssuanceID, // mptIssuanceID
|
||||||
.priorBalance = priorBalance,
|
account, // account
|
||||||
.mptIssuanceID = mptIssuanceID,
|
j);
|
||||||
.account = account,
|
|
||||||
.flags = 0,
|
|
||||||
});
|
|
||||||
!isTesSuccess(err))
|
!isTesSuccess(err))
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
|||||||
@@ -446,6 +446,8 @@ Slot<clock_type>::deletePeer(PublicKey const& validator, id_t id, bool erase)
|
|||||||
auto it = peers_.find(id);
|
auto it = peers_.find(id);
|
||||||
if (it != peers_.end())
|
if (it != peers_.end())
|
||||||
{
|
{
|
||||||
|
std::vector<Peer::id_t> toUnsquelch;
|
||||||
|
|
||||||
JLOG(journal_.trace())
|
JLOG(journal_.trace())
|
||||||
<< "deletePeer: " << Slice(validator) << " " << id << " selected "
|
<< "deletePeer: " << Slice(validator) << " " << id << " selected "
|
||||||
<< (it->second.state == PeerState::Selected) << " considered "
|
<< (it->second.state == PeerState::Selected) << " considered "
|
||||||
@@ -457,7 +459,7 @@ Slot<clock_type>::deletePeer(PublicKey const& validator, id_t id, bool erase)
|
|||||||
for (auto& [k, v] : peers_)
|
for (auto& [k, v] : peers_)
|
||||||
{
|
{
|
||||||
if (v.state == PeerState::Squelched)
|
if (v.state == PeerState::Squelched)
|
||||||
handler_.unsquelch(validator, k);
|
toUnsquelch.push_back(k);
|
||||||
v.state = PeerState::Counting;
|
v.state = PeerState::Counting;
|
||||||
v.count = 0;
|
v.count = 0;
|
||||||
v.expire = now;
|
v.expire = now;
|
||||||
@@ -479,6 +481,10 @@ Slot<clock_type>::deletePeer(PublicKey const& validator, id_t id, bool erase)
|
|||||||
|
|
||||||
if (erase)
|
if (erase)
|
||||||
peers_.erase(it);
|
peers_.erase(it);
|
||||||
|
|
||||||
|
// Must be after peers_.erase(it)
|
||||||
|
for (auto const& k : toUnsquelch)
|
||||||
|
handler_.unsquelch(validator, k);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1423,7 +1423,12 @@ OverlayImpl::updateSlotAndSquelch(
|
|||||||
if (!strand_.running_in_this_thread())
|
if (!strand_.running_in_this_thread())
|
||||||
return post(
|
return post(
|
||||||
strand_,
|
strand_,
|
||||||
[this, key, validator, peers = std::move(peers), type]() mutable {
|
// Must capture copies of reference parameters (i.e. key, validator)
|
||||||
|
[this,
|
||||||
|
key = key,
|
||||||
|
validator = validator,
|
||||||
|
peers = std::move(peers),
|
||||||
|
type]() mutable {
|
||||||
updateSlotAndSquelch(key, validator, std::move(peers), type);
|
updateSlotAndSquelch(key, validator, std::move(peers), type);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1444,9 +1449,12 @@ OverlayImpl::updateSlotAndSquelch(
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (!strand_.running_in_this_thread())
|
if (!strand_.running_in_this_thread())
|
||||||
return post(strand_, [this, key, validator, peer, type]() {
|
return post(
|
||||||
updateSlotAndSquelch(key, validator, peer, type);
|
strand_,
|
||||||
});
|
// Must capture copies of reference parameters (i.e. key, validator)
|
||||||
|
[this, key = key, validator = validator, peer, type]() {
|
||||||
|
updateSlotAndSquelch(key, validator, peer, type);
|
||||||
|
});
|
||||||
|
|
||||||
slots_.updateSlotAndSquelch(key, validator, peer, type, [&]() {
|
slots_.updateSlotAndSquelch(key, validator, peer, type, [&]() {
|
||||||
reportInboundTraffic(TrafficCount::squelch_ignored, 0);
|
reportInboundTraffic(TrafficCount::squelch_ignored, 0);
|
||||||
|
|||||||
Reference in New Issue
Block a user