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

fixDisallowIncomingV1 #4721

Merged
merged 10 commits into from
Oct 5, 2023
Merged

Conversation

dangell7
Copy link
Collaborator

This commit introduces a new fix amendment (fixDisallowIncomingV1) which fixes an issue that occurs with authorized trustlines and the lsfDisallowIncomingTrustline flag.

Currently if a user creates an authorized trustline then sets the lsfDisallowIncomingTrustline flag on their account, the gateway/issuer cannot then approve the trustline.

To recreate the issue:

  1. Issuer sets asfRequireAuth on their account.
  2. User sets asfDisallowIncomingTrustline on their account.
  3. User submits SetTrust tx to Issuer.
  4. Issuer cannot then authorize the trustline.

Once the amendment is activated, the issuer can authorize the trustline after the User sets the asfDisallowIncomingTrustline flag because the trustline already exists.

Comment on lines +144 to +150
if (ctx.view.rules().enabled(fixDisallowIncomingV1) &&
ctx.view.exists(keylet::line(id, uDstAccountID, currency)))
{
// pass
}
else
return tecNO_PERMISSION;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why not just invert the if and return inside it, instead of having an empty if and returning in the else?

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 actually have it like that on my local branch but with clang-format it makes it very "compact". I'm open to doing it like that if its preferred. It was just done like that for readability.

if (ctx.view.rules().enabled(fixDisallowIncomingV1) &&
        !ctx.view.exists(keylet::line(id, uDstAccountID, currency)))
        return tecNO_PERMISSION;

Copy link
Collaborator

Choose a reason for hiding this comment

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

In general positive conditions should be preferred over negative conditions. They are quick and easy to scan through, it's very obvious to the reader what's happening. The more complicated the condition the more likely the next programmer will misunderstand it and insert a mistake.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Personally, I have no trouble reading the current formulation of the if. So I'm good with leaving it the way it is.

@scottschurr
Copy link
Collaborator

I just looked over this pull request. The proposed approach looks like it would work and solve the identified problem.

However, I'm not yet convinced that the identified problem justifies an amendment. Let me walk through why.

If asfDisallowIncomingTrustline were a sticky flag (that couldn't be cleared), then I'd say you have a great justification. But the flag is not sticky. asfDisallowIncomingTrustline can be set and cleared as often as an account wants. So if an account gets into the situation solved by this fix, they can also solve the problem by

  1. Clearing their asfDisallowIncomingTrustline flag briefly,
  2. Letting the tfSetfAuth get set on the trust line, and then
  3. Re-enabling their asfDisallowIncomingTrustline flag.

This unit test fragment demonstrates the approach I'm suggesting.

        {
            Env env{*this, (features | disallowIncoming) - fixDisallowIncomingV1};
            auto const dist = Account("dist");
            auto const gw = Account("gw");
            auto const USD = gw["USD"];
            auto const distUSD = dist["USD"];

            env.fund(XRP(1000), gw, dist);
            env.close();

            env(fset(gw, asfRequireAuth));
            env.close();

            env(fset(dist, asfDisallowIncomingTrustline));
            env.close();

            env(trust(dist, USD(10000)));
            env.close();

            // Can't set trust line because dist has set the
            // asfDisallowIncomingTrustline flag.
            env(trust(gw, distUSD(10000)),
                txflags(tfSetfAuth),
                ter(tecNO_PERMISSION));
            env.close();

            // But dist can clear the asfDisallowIncomingTrustline flag.  Then
            // the trust line can have `tfSetfAuth` set.
            env(fclear(dist, asfDisallowIncomingTrustline));
            env.close();

            env(trust(gw, distUSD(10000)), txflags(tfSetfAuth));
            env.close();

            // Now dist can re-enable asfDisallowIncomingTrustline.
            env(fclear(dist, asfDisallowIncomingTrustline));
            env.close();

            env(pay(gw, dist, USD(1000)));
            env.close();
        }

This approach has the advantage that it works today (without an amendment) and it's pretty easy to explain.

Is it elegant? No. But it solves the problem for what I suspect is a pretty small user population.

I'm also open to being wrong on this. Convince me that this proposed amendment has a good justification.

Thanks for reading.

@dangell7
Copy link
Collaborator Author

I just looked over this pull request. The proposed approach looks like it would work and solve the identified problem.

However, I'm not yet convinced that the identified problem justifies an amendment. Let me walk through why.

If asfDisallowIncomingTrustline were a sticky flag (that couldn't be cleared), then I'd say you have a great justification. But the flag is not sticky. asfDisallowIncomingTrustline can be set and cleared as often as an account wants. So if an account gets into the situation solved by this fix, they can also solve the problem by

  1. Clearing their asfDisallowIncomingTrustline flag briefly,
  2. Letting the tfSetfAuth get set on the trust line, and then
  3. Re-enabling their asfDisallowIncomingTrustline flag.

This unit test fragment demonstrates the approach I'm suggesting.

        {
            Env env{*this, (features | disallowIncoming) - fixDisallowIncomingV1};
            auto const dist = Account("dist");
            auto const gw = Account("gw");
            auto const USD = gw["USD"];
            auto const distUSD = dist["USD"];

            env.fund(XRP(1000), gw, dist);
            env.close();

            env(fset(gw, asfRequireAuth));
            env.close();

            env(fset(dist, asfDisallowIncomingTrustline));
            env.close();

            env(trust(dist, USD(10000)));
            env.close();

            // Can't set trust line because dist has set the
            // asfDisallowIncomingTrustline flag.
            env(trust(gw, distUSD(10000)),
                txflags(tfSetfAuth),
                ter(tecNO_PERMISSION));
            env.close();

            // But dist can clear the asfDisallowIncomingTrustline flag.  Then
            // the trust line can have `tfSetfAuth` set.
            env(fclear(dist, asfDisallowIncomingTrustline));
            env.close();

            env(trust(gw, distUSD(10000)), txflags(tfSetfAuth));
            env.close();

            // Now dist can re-enable asfDisallowIncomingTrustline.
            env(fclear(dist, asfDisallowIncomingTrustline));
            env.close();

            env(pay(gw, dist, USD(1000)));
            env.close();
        }

This approach has the advantage that it works today (without an amendment) and it's pretty easy to explain.

Is it elegant? No. But it solves the problem for what I suspect is a pretty small user population.

I'm also open to being wrong on this. Convince me that this proposed amendment has a good justification.

Thanks for reading.

I would argue a few things.

  1. This means that the issuer needs to communicate to me, for me to clear my flag then reset it.
  2. They/I would need to do this every time, for every issuer that has an authorized trustline.
  3. I already gave permission/trust to the issuer through adding their trustline therefore this process is redundant.

@scottschurr
Copy link
Collaborator

@dangell7 wrote:

I would argue a few things.

  1. This means that the issuer needs to communicate to me, for me to clear my flag then reset it.
  2. They/I would need to do this every time, for every issuer that has an authorized trustline.
  3. I already gave permission/trust to the issuer through adding their trustline therefore this process is redundant.

Thanks for bringing those up. I think all of those are legitimate points. However I'm not completely convinced they are important enough to merit an amendment.

On the other hand I recognize that I'm not a very good representative of ledger usability concerns. So my hope is that someone with more usability focus will weigh in. Possibly @mvadari or @ximinez?

@mvadari
Copy link
Collaborator

mvadari commented Oct 3, 2023

@dangell7 wrote:

I would argue a few things.

  1. This means that the issuer needs to communicate to me, for me to clear my flag then reset it.
  2. They/I would need to do this every time, for every issuer that has an authorized trustline.
  3. I already gave permission/trust to the issuer through adding their trustline therefore this process is redundant.

Thanks for bringing those up. I think all of those are legitimate points. However I'm not completely convinced they are important enough to merit an amendment.

On the other hand I recognize that I'm not a very good representative of ledger usability concerns. So my hope is that someone with more usability focus will weigh in. Possibly @mvadari or @ximinez?

While I agree that the flow @scottschurr described would be an easier solution to the problem (easier in that it doesn't require enabling an amendment), I think this amendment fixes the behavior to be what it should be. IMO this amendment should still be approved, but in the meanwhile we can use other mechanisms to deal with the problem (and the explanations will be a bit simpler when the fix is enabled). I'm of the opinion that if a fix amendment isn't going to change the user API (which would be a pain to deal with in tooling), then there's no reason why it shouldn't be in the codebase.

Copy link
Collaborator

@scottschurr scottschurr left a comment

Choose a reason for hiding this comment

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

👍 Nice job with this pull request. The implementation is straightforward and easy to read. The unit test demonstrates the intention and utility of the change.

I left one note about a comment that might be changed. But I leave that your discretion.

Comment on lines +144 to +150
if (ctx.view.rules().enabled(fixDisallowIncomingV1) &&
ctx.view.exists(keylet::line(id, uDstAccountID, currency)))
{
// pass
}
else
return tecNO_PERMISSION;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Personally, I have no trouble reading the current formulation of the if. So I'm good with leaving it the way it is.

src/test/app/SetTrust_test.cpp Show resolved Hide resolved
src/ripple/app/tx/impl/SetTrust.cpp Show resolved Hide resolved
@intelliot
Copy link
Collaborator

This PR has sufficient approvals. @dangell7 , please:

  1. Bring this branch up-to-date with develop
  2. Confirm that, from your perspective, this PR is ready to merge.

Thanks!

@dangell7
Copy link
Collaborator Author

dangell7 commented Oct 5, 2023

LGTM

@intelliot intelliot merged commit 6ba9450 into XRPLF:develop Oct 5, 2023
15 checks passed
@intelliot intelliot added this to the 2.0 milestone Oct 5, 2023
florent-uzio pushed a commit to florent-uzio/rippled that referenced this pull request Oct 6, 2023
…F#4721)

Context: The `DisallowIncoming` amendment provides an option to block
incoming trust lines from reaching your account. The
asfDisallowIncomingTrustline AccountSet Flag, when enabled, prevents any
incoming trust line from being created. However, it was too restrictive:
it would block an issuer from authorizing a trust line, even if the
trust line already exists. Consider:

1. Issuer sets asfRequireAuth on their account.
2. User sets asfDisallowIncomingTrustline on their account.
3. User submits tx to SetTrust to Issuer.

At this point, without `fixDisallowIncomingV1` active, the issuer would
not be able to authorize the trust line.

The `fixDisallowIncomingV1` amendment, once activated, allows an issuer
to authorize a trust line even after the user sets the
asfDisallowIncomingTrustline flag, as long as the trust line already
exists.
@intelliot intelliot mentioned this pull request Oct 17, 2023
1 task
sophiax851 pushed a commit to sophiax851/rippled that referenced this pull request Jun 12, 2024
…F#4721)

Context: The `DisallowIncoming` amendment provides an option to block
incoming trust lines from reaching your account. The
asfDisallowIncomingTrustline AccountSet Flag, when enabled, prevents any
incoming trust line from being created. However, it was too restrictive:
it would block an issuer from authorizing a trust line, even if the
trust line already exists. Consider:

1. Issuer sets asfRequireAuth on their account.
2. User sets asfDisallowIncomingTrustline on their account.
3. User submits tx to SetTrust to Issuer.

At this point, without `fixDisallowIncomingV1` active, the issuer would
not be able to authorize the trust line.

The `fixDisallowIncomingV1` amendment, once activated, allows an issuer
to authorize a trust line even after the user sets the
asfDisallowIncomingTrustline flag, as long as the trust line already
exists.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

5 participants