-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Closes #3110. Ensure user can see error message about absurdly high fees. #3111
Conversation
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.
utACK modulo comment.
qa/rpc-tests/wallet.py
Outdated
outputs = {} | ||
utxo = node0utxos[0] | ||
inputs.append({ "txid" : utxo["txid"], "vout" : utxo["vout"]}) | ||
outputs[self.nodes[2].getnewaddress("")] = Decimal(0.00001000) |
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 should be Decimal("0.00001000")
, otherwise precision is lost in the value passed into the constructor:
>>> from decimal import Decimal
>>> Decimal("0.00001000")
Decimal('0.00001000')
>>> Decimal(0.00001000)
Decimal('0.000010000000000000000818030539140313095458623138256371021270751953125')
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.
Fix force pushed.
Note that I did not test the user interface behaviour; I only read the code. |
4f6110a
to
3e991a9
Compare
src/main.cpp
Outdated
string errmsg = strprintf("absurdly high fees %s, %d > %d", | ||
hash.ToString(), | ||
nFees, ::minRelayTxFee.GetFee(nSize) * 10000); | ||
return state.DoS(0, error(("AcceptToMemoryPool: " + errmsg).c_str()), REJECT_ABSURDLYHIGHFEE, errmsg); |
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.
Use state.Error()
instead, which won't ever propagate over the network (matching the existing behaviour of this code), but can be used to return error messages to the caller. It also matches the expected semantics (the transaction is not inherently invalid, which is what is indicated when using state.DoS()
).
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.
Hmm. REJECT_ABSURDLYHIGHFEE is similar to REJECT_INSUFFICIENTFEE which uses state.DoS():
return state.DoS(0, error("AcceptToMemoryPool: not enough fees %s, %d < %d",
hash.ToString(), nFees, txMinFee),
REJECT_INSUFFICIENTFEE, "insufficient fee");
In both cases, the transactions themselves are valid and could be mined into a block. If local rules (a form of "emergent consensus") allow for insufficient fee, shouldn't we treat an absurdly high fee the same way?
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.
Insufficient fee is a node / standard rule, yes - but one that is communicated over the network precisely because it affects remote users (particularly if the local node has enabled DoS-prevention mechanisms, some of which I think we have, and some of which I know are added in upstream's 0.12).
By contrast, "absurdly high fee" has only ever been a local user protection. There is no rational reason for miners to reject high-fee transactions, and if we do in fact desire a decentralised mining state, no rational reason for any node to reject high-fee transactions. If we want to change this (and explicitly start rejecting high-fee transactions over the network, which we do not currently do), we should do so in its own issue, not as a side-effect of this PR (which is about exposing the error message to the local user).
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.
Changed to state.Error to unblock this PR.
src/consensus/validation.h
Outdated
@@ -17,6 +17,7 @@ static const unsigned char REJECT_NONSTANDARD = 0x40; | |||
static const unsigned char REJECT_DUST = 0x41; | |||
static const unsigned char REJECT_INSUFFICIENTFEE = 0x42; | |||
static const unsigned char REJECT_CHECKPOINT = 0x43; | |||
static const unsigned char REJECT_ABSURDLYHIGHFEE = 0x44; |
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.
Remove this (it becomes unnecessary).
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.
Removed.
qa/rpc-tests/wallet.py
Outdated
outputs = {} | ||
utxo = node0utxos[0] | ||
inputs.append({ "txid" : utxo["txid"], "vout" : utxo["vout"]}) | ||
outputs[self.nodes[2].getnewaddress("")] = Decimal("0.00001000") |
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's not clear here what fee this results in. Maybe add a comment about the size of the input? Or calculate the value output based on the input value being used.
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.
Added comment and updated test so its clearer.
qa/rpc-tests/wallet.py
Outdated
signed_tx = self.nodes[0].signrawtransaction(raw_tx) | ||
self.nodes[0].sendrawtransaction(signed_tx["hex"]) | ||
except JSONRPCException,e: | ||
errorString = e.error['message'] |
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 currently result in the test passing if the error is thrown by any of the called methods, not just sendrawtransaction
, which might not be what we want to test.
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.
Fixed
3e991a9
to
5190a0f
Compare
Tested ACK:
Fails for values less than Decimal("1.0") :
|
@mdr0id As expected. The test picks out an utxo of value 10.0 to spend from so we can assert on the contents of the error message. It might seem inflexible from the point of view of writing tests, but it can help alert us to any changes to utxo generation and absurd/estimated fee calculation. |
5190a0f
to
2d8cf3f
Compare
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.
utACK
src/main.cpp
Outdated
if (fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) { | ||
string errmsg = strprintf("absurdly high fees %s, %d > %d", | ||
hash.ToString(), | ||
nFees, ::minRelayTxFee.GetFee(nSize) * 10000); |
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.
Nit: these two lines didn't need to change their indentation.
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.
Updated: moved over to align neatly under "
src/main.cpp
Outdated
string errmsg = strprintf("absurdly high fees %s, %d > %d", | ||
hash.ToString(), | ||
nFees, ::minRelayTxFee.GetFee(nSize) * 10000); | ||
LogPrint("mempool", errmsg.c_str()); |
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.
Do we usually log without the function name prefix?
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's inconsistent. Code block above does not log function name, but in some other places it is.
for utxo in node2utxos: | ||
if utxo["amount"] == Decimal("10.0"): | ||
break | ||
assert_equal(utxo["amount"], Decimal("10.0")) |
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 somewhat irks me that Python leaves the last iteration of a loop in scope. Handy, yes, but...
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.
Yes! I think this is related to another annoyance of Python:
for i in range(3):
print(i)
print(i)
produces this output:
0
1
2
2
It seems like the final value of i
should be 3 (as in C), not 2. If it were 3, then you'd know you didn't break out of the loop early (not being done in this example, but in general).
2d8cf3f
to
8b15afd
Compare
@zkbot r+ |
📌 Commit 8b15afd has been approved by |
…artel Closes #3110. Ensure user can see error message about absurdly high fees.
I just re-discovered bitcoin/bitcoin#5913 which does the same thing upstream. They get around the P2P error code issue by using the two-byte code |
ZIP 239 preparations 4 Cherry-picked from the following upstream PRs: - bitcoin/bitcoin#5913 - Replaces #3111. - Includes an extra commit included by upstream in the merge outside the PR. - bitcoin/bitcoin#6519 - bitcoin/bitcoin#7179 - bitcoin/bitcoin#7853 - bitcoin/bitcoin#7960
No description provided.