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

feat: LPT freeze #1840

Open
wants to merge 22 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Clio(ConanFile):
'protobuf/3.21.9',
'grpc/1.50.1',
'openssl/1.1.1u',
'xrpl/2.4.0-b1',
'xrpl/2.4.0-b1@shawn/lptfreeze',
'zlib/1.3.1',
'libbacktrace/cci.20210118'
]
Expand Down
6 changes: 4 additions & 2 deletions src/app/ClioApplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ ClioApplication::run(bool const useNgWebServer)
// Interface to the database
auto backend = data::makeBackend(config_);

auto const amendmentCenter = std::make_shared<data::AmendmentCenter const>(backend);

{
auto const migrationInspector = migration::makeMigrationInspector(config_, backend);
// Check if any migration is blocking Clio server starting.
Expand All @@ -117,7 +119,7 @@ ClioApplication::run(bool const useNgWebServer)
}

// Manages clients subscribed to streams
auto subscriptions = feed::SubscriptionManager::makeSubscriptionManager(config_, backend);
auto subscriptions = feed::SubscriptionManager::makeSubscriptionManager(config_, backend, amendmentCenter);

// Tracks which ledgers have been validated by the network
auto ledgers = etl::NetworkValidatedLedgers::makeValidatedLedgers();
Expand All @@ -133,7 +135,7 @@ ClioApplication::run(bool const useNgWebServer)

auto workQueue = rpc::WorkQueue::makeWorkQueue(config_);
auto counters = rpc::Counters::makeCounters(workQueue);
auto const amendmentCenter = std::make_shared<data::AmendmentCenter const>(backend);

auto const handlerProvider = std::make_shared<rpc::impl::ProductionHandlerProvider const>(
config_, backend, subscriptions, balancer, etl, amendmentCenter, counters
);
Expand Down
1 change: 1 addition & 0 deletions src/data/AmendmentCenter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ struct Amendments {
REGISTER(fixAMMv1_2);
REGISTER(AMMClawback);
REGISTER(Credentials);
REGISTER(fixFrozenLPTokenTransfer);

// Obsolete but supported by libxrpl
REGISTER(CryptoConditionsSuite);
Expand Down
2 changes: 1 addition & 1 deletion src/feed/SubscriptionManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ SubscriptionManager::unsubBook(ripple::Book const& book, SubscriberSharedPtr con
void
SubscriptionManager::pubTransaction(data::TransactionAndMetadata const& txMeta, ripple::LedgerHeader const& lgrInfo)
{
transactionFeed_.pub(txMeta, lgrInfo, backend_);
transactionFeed_.pub(txMeta, lgrInfo, backend_, amendmentCenter_);
}

boost::json::object
Expand Down
15 changes: 12 additions & 3 deletions src/feed/SubscriptionManager.hpp
shawnxie999 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#pragma once

#include "data/AmendmentCenterInterface.hpp"
#include "data/BackendInterface.hpp"
#include "data/Types.hpp"
#include "feed/SubscriptionManagerInterface.hpp"
Expand Down Expand Up @@ -60,6 +61,7 @@ namespace feed {
*/
class SubscriptionManager : public SubscriptionManagerInterface {
std::shared_ptr<data::BackendInterface const> backend_;
std::shared_ptr<data::AmendmentCenterInterface const> amendmentCenter_;
util::async::AnyExecutionContext ctx_;
impl::ForwardFeed manifestFeed_;
impl::ForwardFeed validationsFeed_;
Expand All @@ -74,33 +76,40 @@ class SubscriptionManager : public SubscriptionManagerInterface {
*
* @param config The configuration to use
* @param backend The backend to use
* @param amendmentCenter The amendmentCenter to use
* @return A shared pointer to a new instance of SubscriptionManager
*/
static std::shared_ptr<SubscriptionManager>
makeSubscriptionManager(
util::config::ClioConfigDefinition const& config,
std::shared_ptr<data::BackendInterface const> const& backend
std::shared_ptr<data::BackendInterface const> const& backend,
std::shared_ptr<data::AmendmentCenterInterface const> const& amendmentCenter
)
{
auto const workersNum = config.get<uint64_t>("subscription_workers");

util::Logger const logger{"Subscriptions"};
LOG(logger.info()) << "Starting subscription manager with " << workersNum << " workers";

return std::make_shared<feed::SubscriptionManager>(util::async::PoolExecutionContext(workersNum), backend);
return std::make_shared<feed::SubscriptionManager>(
util::async::PoolExecutionContext(workersNum), backend, amendmentCenter
);
}

/**
* @brief Construct a new Subscription Manager object
*
* @param executor The executor to use to publish the feeds
* @param backend The backend to use
* @param amendmentCenter The amendmentCenter to use
*/
SubscriptionManager(
util::async::AnyExecutionContext&& executor,
std::shared_ptr<data::BackendInterface const> const& backend
std::shared_ptr<data::BackendInterface const> const& backend,
std::shared_ptr<data::AmendmentCenterInterface const> const& amendmentCenter
)
: backend_(backend)
, amendmentCenter_(amendmentCenter)
, ctx_(std::move(executor))
, manifestFeed_(ctx_, "manifest")
, validationsFeed_(ctx_, "validations")
Expand Down
6 changes: 4 additions & 2 deletions src/feed/impl/TransactionFeed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "feed/impl/TransactionFeed.hpp"

#include "data/AmendmentCenterInterface.hpp"
#include "data/BackendInterface.hpp"
#include "data/Types.hpp"
#include "feed/Types.hpp"
Expand Down Expand Up @@ -174,7 +175,8 @@ void
TransactionFeed::pub(
data::TransactionAndMetadata const& txMeta,
ripple::LedgerHeader const& lgrInfo,
std::shared_ptr<data::BackendInterface const> const& backend
std::shared_ptr<data::BackendInterface const> const& backend,
std::shared_ptr<data::AmendmentCenterInterface const> const& amendmentCenter
)
{
auto [tx, meta] = rpc::deserializeTxPlusMeta(txMeta, lgrInfo.seq);
Expand All @@ -187,7 +189,7 @@ TransactionFeed::pub(
if (account != amount.issue().account) {
auto fetchFundsSynchronous = [&]() {
data::synchronous([&](boost::asio::yield_context yield) {
ownerFunds = rpc::accountFunds(*backend, lgrInfo.seq, amount, account, yield);
ownerFunds = rpc::accountFunds(*backend, *amendmentCenter, lgrInfo.seq, amount, account, yield);
});
};
data::retryOnTimeout(fetchFundsSynchronous);
Expand Down
4 changes: 3 additions & 1 deletion src/feed/impl/TransactionFeed.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#pragma once

#include "data/AmendmentCenterInterface.hpp"
#include "data/BackendInterface.hpp"
#include "data/Types.hpp"
#include "feed/Types.hpp"
Expand Down Expand Up @@ -180,7 +181,8 @@ class TransactionFeed {
void
pub(data::TransactionAndMetadata const& txMeta,
ripple::LedgerHeader const& lgrInfo,
std::shared_ptr<data::BackendInterface const> const& backend);
std::shared_ptr<data::BackendInterface const> const& backend,
std::shared_ptr<data::AmendmentCenterInterface const> const& amendmentCenter);

/**
* @brief Get the number of subscribers of the transaction feed.
Expand Down
15 changes: 10 additions & 5 deletions src/rpc/AMMHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ namespace rpc {
std::pair<ripple::STAmount, ripple::STAmount>
getAmmPoolHolds(
BackendInterface const& backend,
data::AmendmentCenterInterface const& amendmentCenter,
std::uint32_t sequence,
ripple::AccountID const& ammAccountID,
ripple::Issue const& issue1,
Expand All @@ -46,10 +47,12 @@ getAmmPoolHolds(
boost::asio::yield_context yield
)
{
auto const assetInBalance =
accountHolds(backend, sequence, ammAccountID, issue1.currency, issue1.account, freezeHandling, yield);
auto const assetOutBalance =
accountHolds(backend, sequence, ammAccountID, issue2.currency, issue2.account, freezeHandling, yield);
auto const assetInBalance = accountHolds(
backend, amendmentCenter, sequence, ammAccountID, issue1.currency, issue1.account, freezeHandling, yield
);
auto const assetOutBalance = accountHolds(
backend, amendmentCenter, sequence, ammAccountID, issue2.currency, issue2.account, freezeHandling, yield
);
return std::make_pair(assetInBalance, assetOutBalance);
}

Expand All @@ -65,7 +68,9 @@ getAmmLpHolds(
)
{
auto const lptCurrency = ammLPTCurrency(cur1, cur2);
return accountHolds(backend, sequence, lpAccount, lptCurrency, ammAccount, true, yield);

cindyyan317 marked this conversation as resolved.
Show resolved Hide resolved
// not using accountHolds because we don't need to check if the associated tokens of the LP are frozen
return ammAccountHolds(backend, sequence, lpAccount, lptCurrency, ammAccount, true, yield);
}

ripple::STAmount
Expand Down
3 changes: 3 additions & 0 deletions src/rpc/AMMHelpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#pragma once

#include "data/AmendmentCenterInterface.hpp"
#include "data/BackendInterface.hpp"

#include <boost/asio/spawn.hpp>
Expand All @@ -37,6 +38,7 @@ namespace rpc {
* @brief getAmmPoolHolds returns the balances of the amm asset pair
*
* @param backend The backend to use
* @param amendmentCenter The amendmentCenter to use
* @param sequence The sequence number to use
* @param ammAccountID The amm account
* @param issue1 The first issue
Expand All @@ -48,6 +50,7 @@ namespace rpc {
std::pair<ripple::STAmount, ripple::STAmount>
getAmmPoolHolds(
BackendInterface const& backend,
data::AmendmentCenterInterface const& amendmentCenter,
std::uint32_t sequence,
ripple::AccountID const& ammAccountID,
ripple::Issue const& issue1,
Expand Down
118 changes: 115 additions & 3 deletions src/rpc/RPCHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

#include "rpc/RPCHelpers.hpp"

#include "data/AmendmentCenter.hpp"
#include "data/AmendmentCenterInterface.hpp"
#include "data/BackendInterface.hpp"
#include "data/Types.hpp"
#include "rpc/Errors.hpp"
Expand Down Expand Up @@ -943,6 +945,20 @@ isFrozen(
return false;
}

bool
isLPTokenFrozen(
BackendInterface const& backend,
std::uint32_t sequence,
ripple::AccountID const& account,
ripple::Issue const& asset,
ripple::Issue const& asset2,
boost::asio::yield_context yield
)
{
return isFrozen(backend, sequence, account, asset.currency, asset.account, yield) ||
isFrozen(backend, sequence, account, asset2.currency, asset2.account, yield);
}

ripple::XRPAmount
xrpLiquid(
BackendInterface const& backend,
Expand Down Expand Up @@ -981,6 +997,7 @@ xrpLiquid(
ripple::STAmount
accountFunds(
BackendInterface const& backend,
data::AmendmentCenterInterface const& amendmentCenter,
std::uint32_t const sequence,
ripple::STAmount const& amount,
ripple::AccountID const& id,
Expand All @@ -991,11 +1008,11 @@ accountFunds(
return amount;
}

return accountHolds(backend, sequence, id, amount.getCurrency(), amount.getIssuer(), true, yield);
return accountHolds(backend, amendmentCenter, sequence, id, amount.getCurrency(), amount.getIssuer(), true, yield);
}

ripple::STAmount
accountHolds(
ammAccountHolds(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ammAccountHolds and accountHolds seem share some common code , can we refactor them a bit?

Copy link
Collaborator Author

@shawnxie999 shawnxie999 Jan 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think we benefit much from refactoring to share common code. these two functions should be independent from each other anyways

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My main thought was that pulling out the common code could make things easier to maintain and avoid duplication. If we ever need to update that logic, we'd only have to do it in one place.
Screenshot 2025-01-31 at 10 37 36
For example, the green part is the shared code, if there is bug in it, we need to change two places.
Are these two functions really independent? I saw accountHolds just does one more check comparing with ammAcountHolds. So the former can call latter like this:

ripple::STAmount
accountHolds(
    BackendInterface const& backend,
    data::AmendmentCenterInterface const& amendmentCenter,
    std::uint32_t sequence,
    ripple::AccountID const& account,
    ripple::Currency const& currency,
    ripple::AccountID const& issuer,
    bool const zeroIfFrozen,
    boost::asio::yield_context yield
)
{
    auto amountBeforeCheckingLP = ammAccountHolds(backend, sequence, account, currency, issuer, zeroIfFrozen, yield);

    if (!zeroIfFrozen || amountBeforeCheckingLP == amountBeforeCheckingLP.zeroed())
        return amountBeforeCheckingLP;

    auto const isLptFrozen = [&]() {
        if (amendmentCenter.isEnabled(yield, data::Amendments::fixFrozenLPTokenTransfer, sequence)) {
            auto const issuerBlob = backend.fetchLedgerObject(ripple::keylet::account(issuer).key, sequence, yield);

            if (!issuerBlob)
                return true;

            ripple::SLE const issuerSle{
                ripple::SerialIter{issuerBlob->data(), issuerBlob->size()}, ripple::keylet::account(issuer).key
            };

            // if the issuer is an amm account, then currency is lptoken, so we will need to check if the
            // assets in the pool are frozen as well
            if (issuerSle.isFieldPresent(ripple::sfAMMID)) {
                auto const ammKeylet = ripple::keylet::amm(issuerSle[ripple::sfAMMID]);
                auto const ammBlob = backend.fetchLedgerObject(ammKeylet.key, sequence, yield);

                if (!ammBlob)
                    return true;

                ripple::SLE const ammSle{ripple::SerialIter{ammBlob->data(), ammBlob->size()}, ammKeylet.key};

                return isLPTokenFrozen(
                    backend,
                    sequence,
                    account,
                    ammSle[ripple::sfAsset].get<ripple::Issue>(),
                    ammSle[ripple::sfAsset2].get<ripple::Issue>(),
                    yield
                );
            }
        }
        return false;
    };

    return isLptFrozen() ? amountBeforeCheckingLP.zeroed() : amountBeforeCheckingLP;
}

`

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

truthfully, the code in ammAccountHolds:

    if (ripple::isXRP(currency))
        return {xrpLiquid(backend, sequence, account, yield)};

should be replaced with an assertion ASSERT(!ripple::isXRP(currency)), which is why I think these two functions are independent

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you genuinely feel certain modifications are necessary, please go ahead and adjust the code accordingly. IMO, refactoring common logic doesn’t mean you can’t make case-specific tweaks; it just helps keep the codebase cleaner and easier to maintain.

Copy link
Collaborator Author

@shawnxie999 shawnxie999 Feb 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they may share some common piece of code now, but that may not be the case soon. In fact i believe the implementation ammAccountHolds will soon diverge from accountHolds due to the DeepFreeze feature. For the incoming deepfreeze feature,accountHolds will need to add isDeepFreeze check (that's not gated by an amendment).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accountHolds will need to add isDeepFreeze check

Would it make it impossible for accountHolds calls ammAccountHolds?

Since you prefer to keep it as is, I'll respect your decision.

BackendInterface const& backend,
std::uint32_t sequence,
ripple::AccountID const& account,
Expand Down Expand Up @@ -1036,6 +1053,93 @@ accountHolds(
return amount;
}

ripple::STAmount
accountHolds(
BackendInterface const& backend,
data::AmendmentCenterInterface const& amendmentCenter,
std::uint32_t sequence,
ripple::AccountID const& account,
ripple::Currency const& currency,
ripple::AccountID const& issuer,
bool const zeroIfFrozen,
boost::asio::yield_context yield
)
{
ripple::STAmount amount;
if (ripple::isXRP(currency))
return {xrpLiquid(backend, sequence, account, yield)};

auto const key = ripple::keylet::line(account, issuer, currency).key;
auto const blob = backend.fetchLedgerObject(key, sequence, yield);

if (!blob) {
amount.setIssue(ripple::Issue(currency, issuer));
amount.clear();
return amount;
}

ripple::SerialIter it{blob->data(), blob->size()};
ripple::SLE const sle{it, key};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need it if allowBalance is false.


auto const allowBalance = [&]() {
if (!zeroIfFrozen)
return true;

if (isFrozen(backend, sequence, account, currency, issuer, yield))
return false;

if (amendmentCenter.isEnabled(yield, data::Amendments::fixFrozenLPTokenTransfer, sequence)) {
auto const issuerBlob = backend.fetchLedgerObject(ripple::keylet::account(issuer).key, sequence, yield);

if (!issuerBlob)
return false;

ripple::SLE const issuerSle{
ripple::SerialIter{issuerBlob->data(), issuerBlob->size()}, ripple::keylet::account(issuer).key
};

// if the issuer is an amm account, then currency is lptoken, so we will need to check if the
// assets in the pool are frozen as well
if (issuerSle.isFieldPresent(ripple::sfAMMID)) {
auto const ammKeylet = ripple::keylet::amm(issuerSle[ripple::sfAMMID]);
auto const ammBlob = backend.fetchLedgerObject(ammKeylet.key, sequence, yield);

if (!ammBlob)
return false;

ripple::SLE const ammSle{ripple::SerialIter{ammBlob->data(), ammBlob->size()}, ammKeylet.key};

if (isLPTokenFrozen(
cindyyan317 marked this conversation as resolved.
Show resolved Hide resolved
backend,
sequence,
account,
ammSle[ripple::sfAsset].get<ripple::Issue>(),
ammSle[ripple::sfAsset2].get<ripple::Issue>(),
yield
)) {
return false;
}
}
}

return true;
}();

if (allowBalance) {
amount = sle.getFieldAmount(ripple::sfBalance);
if (account > issuer) {
// Put balance in account terms.
amount.negate();
}
amount.setIssuer(issuer);
} else {
amount.setIssue(ripple::Issue(currency, issuer));
amount.clear();
}

return amount;
}

ripple::Rate
transferRate(
BackendInterface const& backend,
Expand Down Expand Up @@ -1064,6 +1168,7 @@ postProcessOrderBook(
ripple::Book const& book,
ripple::AccountID const& takerID,
data::BackendInterface const& backend,
data::AmendmentCenterInterface const& amendmentCenter,
std::uint32_t const ledgerSequence,
boost::asio::yield_context yield
)
Expand Down Expand Up @@ -1106,7 +1211,14 @@ postProcessOrderBook(
firstOwnerOffer = false;
} else {
saOwnerFunds = accountHolds(
backend, ledgerSequence, uOfferOwnerID, book.out.currency, book.out.account, true, yield
backend,
amendmentCenter,
ledgerSequence,
uOfferOwnerID,
book.out.currency,
book.out.account,
true,
yield
);

if (saOwnerFunds < beast::zero)
Expand Down
Loading
Loading