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

Deep freeze (#5187) #5187

Open
wants to merge 39 commits into
base: develop
Choose a base branch
from
Open

Deep freeze (#5187) #5187

wants to merge 39 commits into from

Conversation

vvysokikh1
Copy link
Collaborator

@vvysokikh1 vvysokikh1 commented Nov 11, 2024

High Level Overview of Change

This PR implements Deep Freeze feature described in this specification: XLS-77d

Context of Change

Added new flags and functionality allowing trust lines to be deep-frozen.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (non-breaking change that only restructures code)
  • Performance (increase or change in throughput and/or latency)
  • Tests (you added tests for code that already exists, or your new feature included in this PR)
  • Documentation update
  • Chore (no impact to binary, e.g. .gitignore, formatting, dropping support for older tooling)
  • Release

API Impact

  • Public API: New feature (new methods and/or new fields)
  • Public API: Breaking change (in general, breaking changes should only impact the next api_version)
  • libxrpl change (any change that may affect libxrpl or dependents of libxrpl)
  • Peer protocol change (must be backward compatible or bump the peer protocol version)

Copy link
Collaborator

@ximinez ximinez left a comment

Choose a reason for hiding this comment

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

Partial review. I didn't go through all the tests.

src/xrpld/app/tx/detail/CreateOffer.cpp Outdated Show resolved Hide resolved
src/xrpld/app/tx/detail/SetTrust.cpp Outdated Show resolved Hide resolved
src/xrpld/ledger/detail/View.cpp Outdated Show resolved Hide resolved
src/xrpld/ledger/detail/View.cpp Outdated Show resolved Hide resolved
src/xrpld/ledger/detail/View.cpp Outdated Show resolved Hide resolved
src/xrpld/app/tx/detail/SetTrust.cpp Outdated Show resolved Hide resolved
Copy link
Collaborator

@ximinez ximinez left a comment

Choose a reason for hiding this comment

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

Partial review

src/xrpld/app/paths/detail/StepChecks.h Outdated Show resolved Hide resolved
src/xrpld/app/tx/detail/SetTrust.cpp Outdated Show resolved Hide resolved
src/xrpld/app/tx/detail/SetTrust.cpp Outdated Show resolved Hide resolved
include/xrpl/protocol/LedgerFormats.h Outdated Show resolved Hide resolved
src/xrpld/app/tx/detail/SetTrust.cpp Outdated Show resolved Hide resolved
include/xrpl/protocol/detail/features.macro Outdated Show resolved Hide resolved
src/xrpld/ledger/detail/View.cpp Outdated Show resolved Hide resolved
src/xrpld/ledger/detail/View.cpp Show resolved Hide resolved
src/test/app/AMMExtended_test.cpp Outdated Show resolved Hide resolved
@@ -4397,6 +4397,48 @@ struct AMM_test : public jtx::AMMTest
0,
std::nullopt,
{features});

// Individually deep frozen account
if (features[featureDeepFreeze])
Copy link
Collaborator

Choose a reason for hiding this comment

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

Don't need to repeat the code. Just add a conditional error:

auto const err = features[featureDeepFreeze] ? ter(tecPATH_DRY) : ter(tesSUCCESS)

And then use it in pay:

env(pay(alice, carol, USD(1)),
            path(~USD),
            sendmax(XRP(10)),
            txflags(tfNoRippleDirect | tfPartialPayment),
            err);

Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually, you shouldn't test here and other updated unit-tests if the feature is disabled because trust is going to fail. You need a separate test for disabled feature in SetTrust and also add to SetTrust tests for invalid combination of the flags. Also need to add OfferCreate and offer crossing, if not added yet, to Offer.

src/test/app/AMMExtended_test.cpp Outdated Show resolved Hide resolved
src/xrpld/app/tx/detail/SetTrust.cpp Show resolved Hide resolved
src/xrpld/app/paths/detail/StepChecks.h Outdated Show resolved Hide resolved
@vvysokikh1 vvysokikh1 changed the title [WIP] Deep freeze Deep freeze (#5187) Dec 10, 2024
@vvysokikh1
Copy link
Collaborator Author

Build fails in Freeze_test.cpp with a number of errors:
src/test/app/Freeze_test.cpp:1462:17: error: reference to ‘path’ is ambiguous 1462 | path(~USD),

Weird, builds and runs on my end. What's your environment?

MAC M1 Sequoia 15.1.1, Apple clang version 16.0.0 (clang-1600.0.26.4) Linux g++ (Ubuntu 13.1.0-8ubuntu1~22.04) 13.1.0

I'm on M3 Sequoia 15.2, same clang version

The issue is related to unity build, working on a fix

Copy link

codecov bot commented Jan 17, 2025

Codecov Report

Attention: Patch coverage is 57.20524% with 98 lines in your changes missing coverage. Please review.

Project coverage is 77.9%. Comparing base (ebd8e63) to head (c1778e5).
Report is 2 commits behind head on develop.

Files with missing lines Patch % Lines
src/xrpld/app/tx/detail/InvariantCheck.cpp 13.9% 93 Missing ⚠️
src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp 84.0% 4 Missing ⚠️
src/xrpld/app/tx/detail/CreateOffer.cpp 87.5% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##           develop   #5187     +/-   ##
=========================================
- Coverage     78.0%   77.9%   -0.1%     
=========================================
  Files          789     789             
  Lines        66955   67164    +209     
  Branches      8132    8166     +34     
=========================================
+ Hits         52223   52325    +102     
- Misses       14732   14839    +107     
Files with missing lines Coverage Δ
include/xrpl/protocol/Feature.h 100.0% <ø> (ø)
src/xrpld/app/paths/TrustLine.h 100.0% <100.0%> (ø)
src/xrpld/app/paths/detail/StepChecks.h 100.0% <100.0%> (ø)
src/xrpld/app/tx/detail/CashCheck.cpp 87.9% <ø> (ø)
src/xrpld/app/tx/detail/InvariantCheck.h 100.0% <ø> (ø)
src/xrpld/app/tx/detail/NFTokenAcceptOffer.h 100.0% <ø> (ø)
src/xrpld/app/tx/detail/OfferStream.cpp 81.6% <100.0%> (+1.3%) ⬆️
src/xrpld/app/tx/detail/SetTrust.cpp 90.9% <100.0%> (+1.3%) ⬆️
src/xrpld/ledger/View.h 100.0% <ø> (ø)
src/xrpld/ledger/detail/View.cpp 91.3% <100.0%> (+0.2%) ⬆️
... and 4 more

... and 4 files with indirect coverage changes

Impacted file tree graph

src/test/app/Freeze_test.cpp Outdated Show resolved Hide resolved
@@ -1778,6 +1781,151 @@ class Freeze_test : public beast::unit_test::suite
}
}

void
testNFTOffersWhenFreeze(FeatureBitset features)
Copy link
Collaborator

Choose a reason for hiding this comment

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

these tests only exercise when someone accepts a sell offer from the owner. have we considered adding tests where the NFT owner accepts a buy offer? (where the potential buyer is deep frozen)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

if buyer is deep frozen, he's normally frozen. His accountHolds check will not let this transaction through. Such tests exist in nft tests.

Copy link
Collaborator

Choose a reason for hiding this comment

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

i thought of another scenario:

  1. alice mints NFT
  2. alice creates a sell offer using USD
  3. bob creates a buy offer using USD
  4. carol (broker) accepts the two offers and earns some USD (if buy offer amount is higher than sell offer, broker has the oppportunity to earn buy offer amt - sell offer amt) You can find unit tests by searching for brokerOffers.

we would need to make sure that at step 4, carol cannot broker the offers if she is deep frozen

src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp Outdated Show resolved Hide resolved
src/test/app/Freeze_test.cpp Show resolved Hide resolved
src/test/app/Freeze_test.cpp Outdated Show resolved Hide resolved
src/test/app/Freeze_test.cpp Outdated Show resolved Hide resolved
src/test/app/Freeze_test.cpp Show resolved Hide resolved
src/test/app/Freeze_test.cpp Outdated Show resolved Hide resolved
Comment on lines +1873 to +1876
// test: this should actually fail since the currency holder doesn't
// want to receive this currency.
env(token::acceptSellOffer(A1, sellOfferIndex));
env.close();
Copy link
Collaborator

Choose a reason for hiding this comment

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

This comment is incorrect. A2 has indicated that they want to receive the currency by creating the offer. Any affirmative action by the account receiving funds can override the "limit" set on the trust line, or create a trust line if one doesn't exist. The same is true of offers on the DEX.

Copy link
Collaborator Author

@vvysokikh1 vvysokikh1 Jan 23, 2025

Choose a reason for hiding this comment

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

We had this case discussed during last call. It's a part of 'missing checks if account is allowed to receive currency'. I'm not convinced this is correct behavior, normal offers behave differently. Also I see the use-case where currency holder would want to freeze the trustline to stop inflow of certain currency instead of cancelling all existing nft offers.

Copy link
Collaborator Author

@vvysokikh1 vvysokikh1 Jan 23, 2025

Choose a reason for hiding this comment

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

The only exception here would actually be acceptance of check, which is really an indication of acceptance. But even this is not currently possible.

src/test/ledger/Invariants_test.cpp Show resolved Hide resolved
src/test/rpc/AccountLines_test.cpp Show resolved Hide resolved
@ximinez ximinez self-requested a review January 23, 2025 18:30
if (zeroIfFrozen == fhZERO_IF_FROZEN)
{
if (isFrozen(view, account, currency, issuer) ||
isDeepFrozen(view, account, currency, issuer))
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm curious, why not keep the original code and just add || isDeepFrozen?

else if (
    (zeroIfFrozen == fhZERO_IF_FROZEN) &&
    (isFrozen(view, account, currency, issuer) ||
     isDeepFrozen(view, account, currency, issuer)))

Unless there is a good reason to change, IMO fewer changes is less risk and easier to review.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I just couldn't help myself to change the previous version.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't see much benefit in this change. It's one line change vs. a bunch of changes. But it's just my opinion.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I find it a bit easier to understand and read, that's the only reason for this change. I can roll back if you insist. I has good test coverage though, so I don't find it risky

src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp Outdated Show resolved Hide resolved
auto const trustLine =
view.read(keylet::line(id, issue.account, issue.currency));

if (!trustLine)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why returning tesSUCCESS? Shouldn't this be similar to a few lines above:

if (!trustLine)
{
    return (flags & tapRETRY) ? TER{terNO_LINE} : TER{tecNO_LINE};
}

If returning an error then the code also has to be amendment gated.

Copy link
Collaborator Author

@vvysokikh1 vvysokikh1 Jan 23, 2025

Choose a reason for hiding this comment

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

Here it's simply checking if trust line exists. If it doesn't exist, there's no freeze/deep freeze flags there so we just exit

env.close();

// test: A1 can send XRP using USD through A2 offer
// TODO: This might be not a correct behavior. A2 doesn't want to
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

When holder freezes trust line, his intention is to avoid receiving this token. So it might be incorrect behavior because in this case he actually receives those.

env.close();

// test: G1 cannot send XRP using USD through A2 offer
// TODO: This might be not a correct behavior. A2 doesn't want to
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why?

src/test/app/Freeze_test.cpp Show resolved Hide resolved
}

void
testLongerPathsWhenFrozen(FeatureBitset features)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why "LongerPaths"? It's the same number of steps, just a payment is used instead of an offer.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Why is it the same number? These tests have book step and xrp step at the very least?

Copy link
Collaborator

Choose a reason for hiding this comment

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

With the offer crossing tests you are exchanging XRP for USD or USD for XRP. Payment does the same thing. The payment engine steps are slightly different. In case of the offer crossing the steps are:

XRPEndpointOfferCrossingStep
BookOfferCrossingStep
DirectIOfferCrossingStep

and in case of the payment the steps are:

XRPEndpointPaymentStep
BookPaymentStep
DirectIPaymentStep

A longer path is something like this: exchange XRP for USD and then exchange USD for GBP as part of the same payment. Or as a unit test something like this:

env(pay(carol, bob, USD(1)),
        test::jtx::path(~USD, ~GBP),
        sendmax(XRP(1)), txflags(tfNoRippleDirect));

src/test/app/Freeze_test.cpp Show resolved Hide resolved
src/test/app/Freeze_test.cpp Outdated Show resolved Hide resolved
src/test/app/Freeze_test.cpp Show resolved Hide resolved
@@ -981,7 +987,11 @@ class AccountLines_test : public beast::unit_test::suite
env.close();

// Set flags on gw2 trust lines so we can look for them.
env(trust(alice, gw2Currency(0), gw2, tfSetNoRipple | tfSetFreeze));
env(trust(
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is the difference between this test and the one above? (Both of them are prefaced by the same comment // Set flags on gw2 trust lines so we can look for them.)
Are any of the pre-conditions different in these tests?

Copy link
Collaborator Author

@vvysokikh1 vvysokikh1 Jan 24, 2025

Choose a reason for hiding this comment

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

different RPC formats seem to be the difference

src/test/rpc/AccountLines_test.cpp Show resolved Hide resolved
src/xrpld/app/tx/detail/InvariantCheck.h Show resolved Hide resolved
};

/**
* @brief Invariant: frozen trust line balance change is not allowed.
Copy link
Collaborator

Choose a reason for hiding this comment

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

A normal frozen trust line could increase its balance (another counter-party could send IOUs to increase the frozen account's balance). Is this a different case?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Are there unit tests which exercise this invariant?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

working on them at the moment.

@@ -62,6 +62,10 @@ addLine(Json::Value& jsonLines, RPCTrustLine const& line)
jPeer[jss::freeze] = true;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does the ledger_entry RPC command return deep_freeze flags (provided, it is set appropriately on the trust line) ? Can you add unit tests for the same?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I am referring to retrieval of trust-line ledger objects using the ledger_entry RPC

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ledger_entry doesn't return anything related to trustline flags, so in a nutshell, it doesn't return information about either normal freeze or deep freeze.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

@vvysokikh1 vvysokikh1 Jan 24, 2025

Choose a reason for hiding this comment

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

Oh I see. I probably looked into wrong implementation.

But anyway, since raw flags are provided, there's no reason to add deep_freeze flag specific test in my opinion.

@XRPLF XRPLF deleted a comment from ckeshava Jan 24, 2025
Comment on lines +605 to +606
XRPL_ASSERT(
after, "ripple::BalanceChangeNotFrozen::visitEntry : invalid after.");
Copy link
Collaborator

Choose a reason for hiding this comment

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

The name is supposed to indicate what you're testing, not what failed.

Suggested change
XRPL_ASSERT(
after, "ripple::BalanceChangeNotFrozen::visitEntry : invalid after.");
XRPL_ASSERT(
after, "ripple::BalanceChangeNotFrozen::visitEntry : valid after.");

Comment on lines +687 to +688
STAmount amt =
line ? line->at(sfBalance) : line->at(sfBalance).zeroed();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
STAmount amt =
line ? line->at(sfBalance) : line->at(sfBalance).zeroed();
STAmount amt =
line ? line->at(sfBalance) : other->at(sfBalance).zeroed();

Comment on lines +703 to +706
XRPL_ASSERT(
change.balanceChangeSign,
"ripple::BalanceChangeNotFrozen::recordBalance : invalid trust "
"line balance sign.");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
XRPL_ASSERT(
change.balanceChangeSign,
"ripple::BalanceChangeNotFrozen::recordBalance : invalid trust "
"line balance sign.");
XRPL_ASSERT(
change.balanceChangeSign,
"ripple::BalanceChangeNotFrozen::recordBalance : valid trust "
"line balance sign.");

Comment on lines +722 to +730
recordBalance(
after->at(sfHighLimit).getIssuer(),
currency,
{after, balanceChangeSign});

recordBalance(
after->at(sfLowLimit).getIssuer(),
currency,
{after, -balanceChangeSign});
Copy link
Collaborator

Choose a reason for hiding this comment

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

High and low sign handling is a consistent source of confusion. Some comments here would be helpful. Something like:

Suggested change
recordBalance(
after->at(sfHighLimit).getIssuer(),
currency,
{after, balanceChangeSign});
recordBalance(
after->at(sfLowLimit).getIssuer(),
currency,
{after, -balanceChangeSign});
// Change from low account's perspective, which is trust line default
recordBalance(
after->at(sfHighLimit).getIssuer(),
currency,
{after, balanceChangeSign});
// Change from high account's perspective, which reverses the sign.
recordBalance(
after->at(sfLowLimit).getIssuer(),
currency,
{after, -balanceChangeSign});

Comment on lines +842 to +845
XRPL_ASSERT(
enforce,
"ripple::BalanceChangeNotFrozen::validateFrozenState : Attempting to "
"move frozen funds.");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
XRPL_ASSERT(
enforce,
"ripple::BalanceChangeNotFrozen::validateFrozenState : Attempting to "
"move frozen funds.");
XRPL_ASSERT(
enforce,
"ripple::BalanceChangeNotFrozen::validateFrozenState : enforce invariant.");

Comment on lines +641 to +643
XRPL_ASSERT(
enforce,
"ripple::BalanceChangeNotFrozen::finalize : Issuer not found.");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
XRPL_ASSERT(
enforce,
"ripple::BalanceChangeNotFrozen::finalize : Issuer not found.");
XRPL_ASSERT(
enforce,
"ripple::BalanceChangeNotFrozen::finalize : enforce invariant.");

Comment on lines +537 to +539
XRPL_ASSERT(
!isXRP(issue.currency),
"NFTokenAcceptOffer::checkAcceptAsset : input is not XRP");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
XRPL_ASSERT(
!isXRP(issue.currency),
"NFTokenAcceptOffer::checkAcceptAsset : input is not XRP");
XRPL_ASSERT(
!isXRP(issue.currency) && view.rules().enabled(featureDeepFreeze) ,
"NFTokenAcceptOffer::checkAcceptAsset : valid to check");

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants