-
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: master
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"
auto const kp1 = randomKeyPair(KeyType::secp256k1); | ||
auto const id1 = calcAccountID(kp1.first); | ||
|
||
auto const kp2 = randomKeyPair(KeyType::secp256k1); | ||
auto const id2 = calcAccountID(kp2.first); | ||
|
||
auto getPayment = [kp1, id1, id2]() { | ||
// Account id1 pays account id2 10,000 XRP. | ||
STObject payment(sfGeneric); | ||
payment.setFieldU16(sfTransactionType, ttPAYMENT); | ||
payment.setAccountID(sfAccount, id1); | ||
payment.setAccountID(sfDestination, id2); | ||
payment.setFieldAmount(sfAmount, STAmount(10000000000ull)); | ||
payment.setFieldAmount(sfFee, STAmount(10ull)); | ||
payment.setFieldU32(sfSequence, 1); | ||
payment.setFieldVL( | ||
sfSigningPubKey, Slice(kp1.first.data(), kp1.first.size())); | ||
return payment; | ||
}; | ||
auto stx = STTx{getPayment()}; | ||
|
||
protocol::TMTransaction m; | ||
m.set_rawtransaction("transaction"); | ||
Serializer s; | ||
stx.add(s); | ||
m.set_rawtransaction(s.data(), s.size()); | ||
m.set_deferred(false); | ||
m.set_status(protocol::TransactionStatus::tsNEW); | ||
env.app().overlay().relay(uint256{0}, m, toSkip); |
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.
Building the transaction like this is overkill. Can I suggest:
auto const jtx = env.jt(noop(env.master));
if (BEAST_EXPECT(jtx.stx))
{
protocol::TMTransaction m;
Serializer s;
jtx.stx->add(s);
m.set_rawtransaction(s.data(), s.size());
m.set_deferred(false);
m.set_status(protocol::TransactionStatus::tsNEW);
env.app().overlay().relay(uint256{0}, m, toSkip);
BEAST_EXPECT(
PeerTest::sendTx_ == expectRelay &&
PeerTest::queueTx_ == expectQueue);
}
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.
Thank you for pointing this out. I copied what other unit-tests are doing. But using env.jt is a lot more compact.
@@ -6,6 +6,55 @@ This document contains the release notes for `rippled`, the reference server imp | |||
|
|||
Have new ideas? Need help with setting up your node? [Please open an issue here](https://github.com/xrplf/rippled/issues/new/choose). | |||
|
|||
# Version 2.3.1 | |||
|
|||
Version 2.3.1 of `rippled`, the reference server implementation of the XRP Ledger protocol, is now available. This release fixes an erroneous high fee penalty that peers can be charged for sending older 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.
This will probably need to be rephrased in light of the additional changes made.
assert( | ||
feeDrop.cost() > feeInvalidSignature.cost() && | ||
feeInvalidSignature.cost() > feeInvalidRequest.cost() && | ||
feeInvalidRequest.cost() > 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.
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.
@@ -22,11 +22,11 @@ | |||
namespace ripple { | |||
namespace Resource { | |||
|
|||
Charge const feeInvalidRequest(100, "malformed request"); | |||
Charge const feeInvalidRequest(200, "malformed request"); |
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.
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?
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.
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.
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.
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.
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.
Updating the variable names sounds good.
void | ||
append(Resource::Charge f, std::string const& add) | ||
{ | ||
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.
Rather than overwriting the fee, why not first check if the fee is higher or lower, and only overwrite if it is higher?
Alternatively, Ed suggested just adding a assert(newFee >= oldFee);
as the updated fee should not be lower.
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 agree with assert(newFee >= oldFee);
proposal. We don't want to do these checks always if this condition is impossible. Once we confirm it with assert assistance in debug builds, it makes sense to skip this comparison in the release for performance reasons.
append(Resource::Charge f, std::string const& add) | ||
{ | ||
fee = f; | ||
context += add; |
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 noticed that the calls to charge
sometimes have the string with a space in front (e.g. string
) and sometimes not (string
). The latter ones currently will be concatenated as is, without separation.
Better would be:
- Update all
add
arguments passed in calls to this function to not have the leading space. - Here, check if context is empty, and if not add a space before adding the passed in string.
Co-authored-by: Ed Hennis <[email protected]>
Co-authored-by: Ed Hennis <[email protected]>
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 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.
@@ -1173,7 +1173,7 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMEndpoints> const& m) | |||
// implication for the protocol. | |||
if (m->endpoints_v2().size() >= 1024) | |||
{ | |||
charge(Resource::feeBadData); | |||
charge(Resource::feeBadData, "endpoints too large"); |
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.
If the function is a message handler (onMessage
) or called directly from a message handler, it should use fee_
, since then the handler returns (OnMessageEnd
) the charge function is called.
If the function is not a message handler, such as a job queue item, it should be charge
.
So in this location, the charge
should not be here, and presumably should use fee_
.
@@ -1188,7 +1188,7 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMEndpoints> const& m) | |||
{ | |||
JLOG(p_journal_.error()) << "failed to parse incoming endpoint: {" | |||
<< tm.endpoint() << "}"; | |||
charge(Resource::feeBadData); | |||
charge(Resource::feeBadData, "endpoints malformed"); |
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.
Same here, regarding fee_
vs charge
.
@@ -1318,7 +1325,7 @@ void | |||
PeerImp::onMessage(std::shared_ptr<protocol::TMGetLedger> const& m) | |||
{ | |||
auto badData = [&](std::string const& msg) { | |||
charge(Resource::feeBadData); | |||
charge(Resource::feeBadData, "get_ledger " + msg); |
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.
Same here, regarding fee_
vs charge
.
@@ -1409,11 +1416,12 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMProofPathRequest> const& m) | |||
JLOG(p_journal_.trace()) << "onMessage, TMProofPathRequest"; | |||
if (!ledgerReplayEnabled_) | |||
{ | |||
charge(Resource::feeInvalidRequest); | |||
charge(Resource::feeInvalidRequest, "proof_path_request disabled"); |
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.
Same here, regarding fee_
vs charge
.
@@ -1458,11 +1468,11 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMReplayDeltaRequest> const& m) | |||
JLOG(p_journal_.trace()) << "onMessage, TMReplayDeltaRequest"; | |||
if (!ledgerReplayEnabled_) | |||
{ | |||
charge(Resource::feeInvalidRequest); | |||
charge(Resource::feeInvalidRequest, "replay_delta_request disabled"); |
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.
Same here, regarding fee_
vs charge
.
return; | ||
} | ||
|
||
if (!ledgerReplayMsgHandler_.processReplayDeltaResponse(m)) | ||
{ | ||
charge(Resource::feeBadData); | ||
charge(Resource::feeBadData, "replay_delta_response"); |
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.
Same here, regarding fee_
vs charge
.
@@ -2572,22 +2600,22 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMSquelch> const& m) | |||
|
|||
if (!m->has_validatorpubkey()) | |||
{ | |||
charge(Resource::feeBadData); | |||
charge(Resource::feeBadData, "squelch no pubkey"); |
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.
Same here, regarding fee_
vs charge
.
return; | ||
} | ||
auto validator = m->validatorpubkey(); | ||
auto const slice{makeSlice(validator)}; | ||
if (!publicKeyType(slice)) | ||
{ | ||
charge(Resource::feeBadData); | ||
charge(Resource::feeBadData, "squelch bad pubkey"); |
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.
Same here, regarding fee_
vs charge
.
return; | ||
} | ||
PublicKey key(slice); | ||
|
||
// Ignore non-validator squelch | ||
if (!app_.validators().listed(key)) | ||
{ | ||
charge(Resource::feeBadData); | ||
charge(Resource::feeBadData, "squelch non-validator"); |
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.
Same here, regarding fee_
vs charge
.
@@ -2607,7 +2635,7 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMSquelch> const& m) | |||
if (!m->squelch()) | |||
squelch_.removeSquelch(key); | |||
else if (!squelch_.addSquelch(key, std::chrono::seconds{duration})) | |||
charge(Resource::feeBadData); | |||
charge(Resource::feeBadData, "squelch duration"); |
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.
Same here, regarding fee_
vs charge
.
High Level Overview of Change
This is a hotfix release that fixes an erroneous high fee penalty that peers can be charged for sending older transactions.
Context of Change
Type of Change
API Impact
libxrpl
change (any change that may affectlibxrpl
or dependents oflibxrpl
)