-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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 version 2.3.1 #5243
base: release
Are you sure you want to change the base?
Proposed version 2.3.1 #5243
Conversation
* 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 part of a TMTransactions batch, the peer is charged the equivalent of one trivial request per transaction. If not, 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.
* If reduce relay is enabled, it will queue up the tx id for peers that also have it enabled so they can ask for it later if they need it.
There was a problem hiding this 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"
include/xrpl/resource/detail/Logic.h
Outdated
// 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"); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 Charge
s. They can just be straight up int
s. (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);
There was a problem hiding this comment.
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.
include/xrpl/resource/detail/Logic.h
Outdated
// 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"); |
There was a problem hiding this comment.
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 Charge
s. They can just be straight up int
s. (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);
if (relay) | ||
{ | ||
auto& txn = tx->get(); | ||
sm = std::make_shared<Message>(txn, protocol::mtTRANSACTION); | ||
} | ||
|
||
if (!relay && !app_.config().TX_REDUCE_RELAY_ENABLE) | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this can be simplified to:
if (!relay) {
auto peers = getActivePeers(toSkip, 0, 0, 0)
JLOG(journal_.trace()) << "not relaying tx, total peers " << peers.size();
for (auto const& p : peers)
p->addTxQueue(hash);
return;
}
auto& txn = tx->get();
sm = std::make_shared<Message>(txn, protocol::mtTRANSACTION);
And then the statements below no longer need to check whether relay is set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Message allocation is more expensive than couple of additional boolean checks. The current approach should have performance advantage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be no performance disadvantage to my proposal, since in the current code the message is allocated when relay
is set, which is also the case in my proposal - the code returns early when relay
is not set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the relay initial is set in here:
bool relay = tx.has_value();
which guarantees that transaction is initialised.
If we remove the check, then later when we try to unbox transaction from optional
auto& txn = tx->get();
we may get a runtime error.
There was a problem hiding this 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.
break; | ||
case ListDisposition::invalid: | ||
// This shouldn't ever happen with a well-behaved peer | ||
fee_ = Resource::feeInvalidSignature; | ||
fee_.update( | ||
Resource::feeInvalidSignature, "invalid list disposition"); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Co-authored-by: Bart <[email protected]>
assert(f >= fee); | ||
fee = f; |
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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;
There was a problem hiding this comment.
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.
|
||
### Bug Fixes and Performance Improvements | ||
|
||
- Change the charged fee for sending older transactions from feeInvalidSignature to feeUnwantedData. [#5243](https://github.com/XRPLF/rippled/pull/5243) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change this line; preferably into multiple items (change fees overall, do not charge repeated fees on batch transactions, improve logging of fees, not sure how to describe changes to relay
)
src/xrpld/overlay/detail/PeerImp.cpp
Outdated
if (tx->getStatus() == NEW) | ||
{ | ||
JLOG(p_journal_.debug()) | ||
<< "Cacheing " << (batch ? "batch" : "unsolicited") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo ?
Co-authored-by: Ed Hennis <[email protected]>
Co-authored-by: Ed Hennis <[email protected]>
…ippled into fix/signaturefee-to-unwanteddata
Co-authored-by: Ed Hennis <[email protected]>
// Something new: Dynamic fee | ||
fee_.fee = Resource::Charge(m->transactions_size(), "transactions"); |
There was a problem hiding this comment.
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?
JLOG(p_journal_.debug()) | ||
<< "Caching " << (batch ? "batch" : "unsolicited") | ||
<< " pseudo-transaction tx " << tx->getID(); |
There was a problem hiding this comment.
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.
High Level Overview of Change
This is a hotfix release that includes the following updates:
Context of Change
Type of Change
API Impact
libxrpl
change (any change that may affectlibxrpl
or dependents oflibxrpl
)