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

Proposed 2.3.1-rc1: Reduce the peer charges for well-behaved peers #5243

Merged
merged 2 commits into from
Jan 28, 2025

Conversation

vlntb
Copy link
Collaborator

@vlntb vlntb commented Jan 13, 2025

High Level Overview of Change

This is a hotfix release that includes the following updates:

  • A fix for an erroneous high fee penalty that peers could incur for sending older transactions;
  • Updates to the fees charged for imposing a load on the server;
  • A fix to prevent the relaying of internal pseudo-transactions;
  • Improved logging.

Context of Change

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • 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

codecov bot commented Jan 13, 2025

Codecov Report

Attention: Patch coverage is 24.29379% with 134 lines in your changes missing coverage. Please review.

Project coverage is 77.9%. Comparing base (f64cf91) to head (f3e201f).
Report is 2 commits behind head on release.

Files with missing lines Patch % Lines
src/xrpld/overlay/detail/PeerImp.cpp 1.0% 102 Missing ⚠️
src/xrpld/overlay/detail/OverlayImpl.cpp 60.0% 8 Missing ⚠️
src/xrpld/overlay/detail/PeerImp.h 0.0% 7 Missing ⚠️
src/xrpld/app/ledger/detail/InboundLedger.cpp 0.0% 5 Missing ⚠️
...rc/xrpld/app/ledger/detail/InboundTransactions.cpp 0.0% 4 Missing ⚠️
src/xrpld/app/ledger/detail/LedgerMaster.cpp 0.0% 4 Missing ⚠️
src/libxrpl/resource/Charge.cpp 0.0% 2 Missing ⚠️
include/xrpl/resource/detail/Logic.h 92.3% 1 Missing ⚠️
src/xrpld/rpc/detail/ServerHandler.cpp 88.9% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##           release   #5243     +/-   ##
=========================================
- Coverage     77.9%   77.9%   -0.1%     
=========================================
  Files          784     784             
  Lines        66681   66742     +61     
  Branches      8162    8191     +29     
=========================================
+ Hits         51950   51964     +14     
- Misses       14731   14778     +47     
Files with missing lines Coverage Δ
src/libxrpl/protocol/BuildInfo.cpp 98.2% <ø> (ø)
src/libxrpl/resource/Consumer.cpp 89.8% <100.0%> (ø)
src/xrpld/overlay/Overlay.h 70.0% <ø> (ø)
src/xrpld/overlay/Peer.h 100.0% <ø> (ø)
src/xrpld/overlay/detail/OverlayImpl.h 40.0% <ø> (ø)
src/xrpld/rpc/detail/RPCHelpers.cpp 82.8% <100.0%> (ø)
src/xrpld/rpc/handlers/GatewayBalances.cpp 89.8% <100.0%> (ø)
src/xrpld/rpc/handlers/LedgerHandler.cpp 22.6% <100.0%> (ø)
src/xrpld/rpc/handlers/PathFind.cpp 48.3% <100.0%> (ø)
src/xrpld/rpc/handlers/RipplePathFind.cpp 42.5% <100.0%> (ø)
... and 12 more

... and 2 files with indirect coverage changes

Impacted file tree graph

@vlntb vlntb changed the title fix fee from feeInvalidSignature to feeUnwantedData Proposed version 2.3.1 Jan 14, 2025
@vlntb vlntb requested a review from ximinez January 14, 2025 12:59
@vlntb vlntb marked this pull request as ready for review January 14, 2025 13:00
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.

Go ahead and squash the last two commits (version and release notes) into one, and change the commit message to follow the standard format: "Set version to 2.3.1"

@vlntb vlntb marked this pull request as draft January 16, 2025 18:19
RELEASENOTES.md Outdated Show resolved Hide resolved
include/xrpl/resource/detail/Logic.h Outdated Show resolved Hide resolved
src/xrpld/overlay/detail/OverlayImpl.cpp Outdated Show resolved Hide resolved
@ximinez ximinez mentioned this pull request Jan 23, 2025
5 tasks
Comment on lines 447 to 450
assert(
feeDrop.cost() > feeInvalidSignature.cost() &&
feeInvalidSignature.cost() > feeInvalidRequest.cost() &&
feeInvalidRequest.cost() > 10);
Copy link
Collaborator

Choose a reason for hiding this comment

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

As these fees are const, can you move these to a static initializer that's guaranteed to only be called once? Or otherwise something that isn't called as frequently, like a constructor?

I chatted with Ed and he suggested possibly make_Manager in ResourceManager.cpp? It's called from the Application ctor.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I just had an alternate idea. Instead of re-using "convenient" existing variables, which have changed in this PR, define 3 new Charge values, specifically defined for use here.

// Only use these for logging in Logic::charge, and keep the values in descending order
Charge const feeLogAsWarn(3000, "log as warn");
Charge const feeLogAsInfo(1000, "log as info");
Charge const feeLogAsDebug(100, "log as debug");

And then we don't need an assert at all, because they won't change independently of their ordering in the logs.

@@ -22,11 +22,11 @@
namespace ripple {
namespace Resource {

Charge const feeInvalidRequest(100, "malformed request");
Charge const feeInvalidRequest(200, "malformed request");
Copy link
Collaborator

Choose a reason for hiding this comment

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

For some of the variables here and below, the name and description are inconsistent.

  • invalid request vs. malformed request => invalid is more general while malformed is more specific.
  • request no reply vs unsatisfiable request => no reply is more specific while unsatisfiable is more general.
  • unwanted vs. useless => similar, but different. unwanted data can still be useful, just not wanted right now.
  • etc.

Can you rename the variables or modify the descriptions to improve consistency?

Copy link
Collaborator

Choose a reason for hiding this comment

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

IMHO, changing the variable names would be better because the descriptions are used in log messages, which, while not a fixed API or anything, are more visible.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That was my worry, too - we are relying on the logs a lot, and we need to be sure that we are not breaking someone's dashboard if we change the message in the area that hasn't been updated for a while. If we all agree, I'll be happy to update the variable names.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Updating the variable names sounds good.

src/xrpld/overlay/detail/PeerImp.h Outdated Show resolved Hide resolved
src/xrpld/overlay/detail/PeerImp.h Show resolved Hide resolved
src/xrpld/overlay/detail/PeerImp.h Show resolved Hide resolved
Copy link
Collaborator

@bthomee bthomee left a comment

Choose a reason for hiding this comment

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

There are a couple of discrepancies when updating the fee for received messages, whereby a mixture of calls to fee_.update and charge happen in the OnMessage.

The first comment provides more details. I only highlighted the charge calls that should be changed to fee_.update. There may be other calls in existing code that GitHub didn't show me, but which then also need to be modified. All this can be done in a follow-up PR, but it's so simple you may as well include it now.

src/xrpld/overlay/detail/PeerImp.cpp Outdated Show resolved Hide resolved
src/xrpld/overlay/detail/PeerImp.cpp Outdated Show resolved Hide resolved
src/xrpld/overlay/detail/PeerImp.cpp Outdated Show resolved Hide resolved
src/xrpld/overlay/detail/PeerImp.cpp Outdated Show resolved Hide resolved
src/xrpld/overlay/detail/PeerImp.cpp Outdated Show resolved Hide resolved
src/xrpld/overlay/detail/PeerImp.cpp Outdated Show resolved Hide resolved
src/xrpld/overlay/detail/PeerImp.cpp Outdated Show resolved Hide resolved
src/xrpld/overlay/detail/PeerImp.cpp Outdated Show resolved Hide resolved
src/xrpld/overlay/detail/PeerImp.cpp Outdated Show resolved Hide resolved
src/xrpld/overlay/detail/PeerImp.cpp Outdated Show resolved Hide resolved
include/xrpl/resource/Charge.h Outdated Show resolved Hide resolved
Comment on lines 447 to 451
// Only use these for logging in Logic::charge, and keep the values in
// descending order
static Charge const feeLogAsWarn(3000, "log as warn");
static Charge const feeLogAsInfo(1000, "log as info");
static Charge const feeLogAsDebug(100, "log as debug");
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would recommend defining these in Fees.h/cpp so the consequences of changing any of the fees are more apparent in terms of how they also will be logged.

Copy link
Collaborator

@ximinez ximinez Jan 24, 2025

Choose a reason for hiding this comment

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

I actually had the opposite thought - now that I see them here, there's no need to define these as Charges. They can just be straight up ints. (Or more specifically, Charge::value_type to ensure the types remain consistent.)
e.g.

static constexpr Charge::value_type feeLogAsWarn = 3000;

Even if we change the values in Fees.cpp in the future, it wouldn't make sense to change them so radically that these cutoffs should ever need to change. e.g. There should always be something that only charges 1. Even the things we changed, we made the values larger, so if anything, they'll log at a higher level, which is probably what we want.

I think a comment in Fees.cpp would be sufficient.

// See also Resource::Logic::charge for log level cutoff values

Now, all that said, if you still disagree, I don't object to moving them to Fees.cpp 😄

Edit: And if you notice there, I used constexpr, so you could add a quick static_assert that won't hurt anything.

static_assert(feeLogAsWarn > feeLogAsInfo && feeLogAsInfo > feeLogAsDebug && feeLogAsDebug > 10);

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'm not sure about this. Putting these definitions into Fees.h/cpp will encourage someone to use them as actual fees mistakenly. Keeping them close to the log-selection logic makes more sense to me.

Comment on lines 447 to 451
// Only use these for logging in Logic::charge, and keep the values in
// descending order
static Charge const feeLogAsWarn(3000, "log as warn");
static Charge const feeLogAsInfo(1000, "log as info");
static Charge const feeLogAsDebug(100, "log as debug");
Copy link
Collaborator

@ximinez ximinez Jan 24, 2025

Choose a reason for hiding this comment

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

I actually had the opposite thought - now that I see them here, there's no need to define these as Charges. They can just be straight up ints. (Or more specifically, Charge::value_type to ensure the types remain consistent.)
e.g.

static constexpr Charge::value_type feeLogAsWarn = 3000;

Even if we change the values in Fees.cpp in the future, it wouldn't make sense to change them so radically that these cutoffs should ever need to change. e.g. There should always be something that only charges 1. Even the things we changed, we made the values larger, so if anything, they'll log at a higher level, which is probably what we want.

I think a comment in Fees.cpp would be sufficient.

// See also Resource::Logic::charge for log level cutoff values

Now, all that said, if you still disagree, I don't object to moving them to Fees.cpp 😄

Edit: And if you notice there, I used constexpr, so you could add a quick static_assert that won't hurt anything.

static_assert(feeLogAsWarn > feeLogAsInfo && feeLogAsInfo > feeLogAsDebug && feeLogAsDebug > 10);

@vlntb vlntb requested review from bthomee and ximinez January 24, 2025 19:21
Copy link
Collaborator

@bthomee bthomee left a comment

Choose a reason for hiding this comment

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

LGTM for everything else.

src/xrpld/overlay/detail/PeerImp.cpp Outdated Show resolved Hide resolved
break;
case ListDisposition::invalid:
// This shouldn't ever happen with a well-behaved peer
fee_ = Resource::feeInvalidSignature;
fee_.update(
Resource::feeInvalidSignature, "invalid list disposition");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is invalid signature here appropriate? If not but we just want to charge the peer a lot, you can create a new const fee.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This error is returned from

// The returned PublicKey value is read from the manifest. Manifests do not
// contain the default-constructed public keys
std::pair<ListDisposition, std::optional<PublicKey>>
ValidatorList::verify

from several places including cryptographic signature check.

I agree that we need to look into this and potentially add more granular error types here, because we may get this error for non-cryptographic reasons too, but I wouldn't suggest doing this as part of the hotfix.

Comment on lines +156 to +157
assert(f >= fee);
fee = f;
Copy link
Collaborator

Choose a reason for hiding this comment

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

reading how we call fee_.update in PeerImp.cpp I do not see anything to provide the guarantee that this assert wants. Should this be fee = std::max(f, fee); instead ?

Copy link
Collaborator

Choose a reason for hiding this comment

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

... or even if (f < fee) return;

Copy link
Collaborator Author

@vlntb vlntb Jan 24, 2025

Choose a reason for hiding this comment

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

    handler.onMessageBegin(
        header.message_type,
        m,
        header.payload_wire_size,
        header.uncompressed_size,
        header.algorithm != Algorithm::None);
    handler.onMessage(m);
    handler.onMessageEnd(header.message_type, m);

We always start from onMessageBegin, which sets the fee to the lowest level (feeTrivialPeer = 1).
We only process one message in this run.
This guarantees that fee update always goes from 1 to any other fee that is higher.

@ximinez ximinez changed the base branch from master to release January 24, 2025 21:36
src/xrpld/overlay/detail/PeerImp.cpp Outdated Show resolved Hide resolved
src/libxrpl/protocol/BuildInfo.cpp Outdated Show resolved Hide resolved
src/xrpld/overlay/detail/OverlayImpl.cpp Outdated Show resolved Hide resolved
@vlntb vlntb requested review from bthomee, ximinez and Bronek January 25, 2025 07:03
Comment on lines 2575 to 2576
// Something new: Dynamic fee
fee_.fee = Resource::Charge(m->transactions_size(), "transactions");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Given that the existing code does not have a corresponding fee for one or more transactions, what is the practical effect of this change?

The handleTransaction function is called for each transaction and already applies charges. Does charging this dynamic fee here cause double-charging or does it serve to fill a gap that currently exists?

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 agree. Introducing dynamic fees needs to be better thought through. We are introducing the potential of a fee going down, something that ChargeWithContext::update currently does not support. I propose to remove this dynamic fee change from the hotfix and return later once we have better unit-test coverage for this part of the logic.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This function calls handleTransaction (along with onMessage(std::shared_ptr<protocol::TMTransaction>)).

handleTransaction could update the fee to feeUselessData, which has a value of 150. A TMTransactions message could potentially be a lot bigger than that.

Not only that, though, if the message is big enough (which is currently impossible with a well-behaved peer, but possible in the future), the dynamic fee could take the total pretty close to the dynamic fee level. Probably best to remove this and rethink it for the future.

Comment on lines 2781 to 2783
JLOG(p_journal_.debug())
<< "Caching " << (batch ? "batch" : "unsolicited")
<< " pseudo-transaction tx " << tx->getID();
Copy link
Collaborator

Choose a reason for hiding this comment

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

This log message doesn't seem to match what is happening here. There's canonicalization and possibly relaying going on, while the log message suggests that both batch and non-batch (unsolicited) tx are being cached.

I'd also recommend just having three separate and appropriately phrased log messages, depending on the situation below (skipped, relayed, charged).

Also, to confirm:

  • One of the fixes introduced in this hotfix was to not relay pseudo-tx, but isn't that what actually can happen here anyway?
  • If a pseudo-tx is non-batch it is unwanted - is there any point to canonicalize and possibly relay it then, before charging the peer for useless data? If possible, moving the if (!batch) check up and then returning early would seem sensible.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

One of the fixes introduced in this hotfix was to not relay pseudo-tx, but isn't that what actually can happen here anyway?

Yes, ideally, we should be doing this check in a single place, but unfortunately, there are several other code paths that can lead to a relay call. So, the final decision about relaying is made inside of the relay call itself.

If a pseudo-tx is non-batch it is unwanted - is there any point to canonicalize and possibly relay it then, before charging the peer for useless data? If possible, moving the if (!batch) check up and then returning early would seem sensible.

I agree in principle. However,
canonicalize - not only streamlines the format but also adds to the cache, which we need.
relay - not only relays transactions but also adds transactions to the queue, which we also want to keep.

I would create a separate ticket to refactor relay, canonicalize and other method names that do not represent what is currently happening.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added extra debug logs

@vlntb vlntb requested a review from bthomee January 27, 2025 12:11
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.

None of the things I've pointed out here are showstoppers. We're really close to ready on this.

src/xrpld/overlay/detail/OverlayImpl.cpp Outdated Show resolved Hide resolved
src/xrpld/overlay/detail/OverlayImpl.cpp Outdated Show resolved Hide resolved
src/xrpld/overlay/detail/OverlayImpl.cpp Outdated Show resolved Hide resolved
include/xrpl/resource/detail/Logic.h Outdated Show resolved Hide resolved
@vlntb vlntb marked this pull request as ready for review January 27, 2025 16:15
@vlntb vlntb requested a review from ximinez January 27, 2025 16:30
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.

It looks like everything has been addressed, and this will be ready to merge once the commits are cleaned up.

@vlntb vlntb force-pushed the fix/signaturefee-to-unwanteddata branch from 7dcfec9 to 00edaea Compare January 27, 2025 20:53
@vlntb vlntb added the Ready to merge *PR author* thinks it's ready to merge. Has passed code review. Perf sign-off may still be required. label Jan 27, 2025
@vlntb vlntb changed the title Proposed version 2.3.1 Proposed 2.3.1-rc1 Jan 27, 2025
@vlntb vlntb force-pushed the fix/signaturefee-to-unwanteddata branch 2 times, most recently from c0ff860 to b755b01 Compare January 27, 2025 23:14
vlntb and others added 2 commits January 27, 2025 19:41
- Fix an erroneous high fee penalty that peers could incur for sending
  older transactions.
- Update to the fees charged for imposing a load on the server.
- Prevent the relaying of internal pseudo-transactions.
  - Before: Pseudo-transactions received from a peer will fail the signature
    check, even if they were requested (using TMGetObjectByHash), because
    they have no signature. This causes the peer to be charge for an
    invalid signature.
  - After: Pseudo-transactions, are put into the global cache
    (TransactionMaster) only. If the transaction is not part of
    a TMTransactions batch, the peer is charged an unwanted data fee.
    These fees will not be a problem in the normal course of operations,
    but should dissuade peers from behaving badly by sending a bunch of
    junk.
- Improve logging: include the reason for fees charged to a peer.

Co-authored-by: Ed Hennis <[email protected]>
@ximinez ximinez force-pushed the fix/signaturefee-to-unwanteddata branch from b755b01 to f3e201f Compare January 28, 2025 00:44
@ximinez ximinez merged commit f3e201f into XRPLF:release Jan 28, 2025
20 of 21 checks passed
@ximinez ximinez changed the title Proposed 2.3.1-rc1 Proposed 2.3.1-rc1: Reduce the peer charges for well-behaved peers Jan 29, 2025
This was referenced Jan 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Ready to merge *PR author* thinks it's ready to merge. Has passed code review. Perf sign-off may still be required.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants