Skip to content

Conversation

@kazcw
Copy link
Contributor

@kazcw kazcw commented Jul 16, 2014

AlreadyHave only prevents rerequesting transactions accepted to the mempool or identified as orphans; if a transaction failed validation, it keeps requesting it and validating it every time it receives an inv. This change adds a small mruset to remember some failed transactions.

This requires a new distinction between permanent failures (that are independent of chain state) and temporary failures; the few potentially temporary rejections are not cached.

I wrote this because my node keeps banning old peers affected by #3190, when they're only resending the failed transactions because we keep getdataing them. A small cache of invalid transactions has low overhead in code complexity and runtime complexity, and prevents a lot of unnecessary banning for #3190 and any possible future bugs with similar effects.

src/main.cpp Outdated
Copy link
Member

Choose a reason for hiding this comment

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

Don't use an assignment. Just:

mruset<uint256> setRejectedTx(200);

@kazcw
Copy link
Contributor Author

kazcw commented Jul 16, 2014

My concern with remembering some rejections that currently may change is that it offers an easy avenue to blind a particular node to a double-spend: just send it the tx to be hidden at a time when it will reject-cache that tx, and that tx will effectively be invisible to the node until/unless it's included in a block.

If this is not considered an important risk, I think we could also reject premature coinbase spends permanently, and remove the whole rejection IsPermanent mechanism (treating all rejections as permanent would simplify things a bit).

@laanwj
Copy link
Member

laanwj commented Jul 17, 2014

I like the idea to not re-request transactions that are seen as misbehaviour.

Previously AlreadyHave would only prevent rerequesting transactions accepted to
the mempool or identified as orphans; if a transaction failed validation, it
would keep requesting it and validating it every time it received an inv. This
change adds a small mruset to remember some failed transactions.

The main benefit of this change is that if a bug like bitcoin#3190 causes a peer to
periodically send invs for bogus transactions, we don't repeatedly getdata and
validate the same transactions until we DoS-ban the peer.

This requires a new distinction between permanent failures (that are independent
of chain state) and temporary failures; the few potentially temporary rejections
are not cached.
@kazcw
Copy link
Contributor Author

kazcw commented Jul 20, 2014

If we do start persisting rejections that would otherwise not be permanent under certain circumstances, it seems like we'd ought to implement a way to still keep track of them for purposes of double-spend monitoring.

I'm going to include just the minimal behavioral changes to fix the original issue in this PR, caching only rejections that definitely would have been rejected again if they were received again. Any additional changes using the rejection cache could be addressed separately.

@BitcoinPullTester
Copy link

Automatic sanity-testing: PASSED, see http://jenkins.bluematt.me/pull-tester/p4542_edde2dda82320e4660bc09cefd285310b8f1318e/ for binaries and test log.
This test script verifies pulls every time they are updated. It, however, dies sometimes and fails to test properly. If you are waiting on a test, please check timestamps to verify that the test.log is moving at http://jenkins.bluematt.me/pull-tester/current/
Contact BlueMatt on freenode if something looks broken.

Copy link
Contributor

Choose a reason for hiding this comment

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

What does the 200 do here? Limit it to remembering 200 TXs? Why 200? Would it make more sense to remember for a certain period rather than a fixed number?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

200 is arbitrary. I figured it would be large enough to handle the rate at which invalid txes would occur under "normal" circumstances like the wallet tx empty-vin bug. I don't think reasonable behavior is very dependent on the set size/expiration policy so long as it's in the right ballpark; it seems like returns diminish quickly beyond the cache being big enough to handle the bulk of unnecessary transaction-rerequesting. An expiry time would be slightly more complex to deal with than a maximum number of entries, and since I don't see the choice of policies making any important functional difference I just went with the simplest.

Copy link
Member

Choose a reason for hiding this comment

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

Having a hard upper limit is a good idea for any data structure, this avoids DoS possibilities.

@TheBlueMatt
Copy link
Contributor

I dont think the network needs to concern itself with double spends. They've proven to be a PITA to try to relay and probably shouldnt be relayed as standard transactions, so I'd say ignore them for this pull.

@rebroad
Copy link
Contributor

rebroad commented Sep 29, 2014

@kazcw needs rebase.

Copy link

Choose a reason for hiding this comment

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

Nit: Should just pass a copy of a bool IMHO, I guess a pointer is bigger on some system archs even.

Copy link
Member

Choose a reason for hiding this comment

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

That's an output parameter, passing a copy would defeat the purpose

Copy link

Choose a reason for hiding this comment

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

Then leave out the const, it could be a reference ;)?

Copy link
Member

Choose a reason for hiding this comment

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

What const? And no, it can't be a reference, because it can be 0 if you're not interested in the output (see the function).

Copy link

Choose a reason for hiding this comment

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

I was at a totally different place in my brain while posting this. You are completely right.

@laanwj
Copy link
Member

laanwj commented Mar 24, 2015

Needs rebase (if we still want this)

@laanwj
Copy link
Member

laanwj commented Jun 10, 2015

Closing this due to inactivity. Let me know if you start work on this again, then I'll reopen.

@laanwj laanwj closed this Jun 10, 2015
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Sep 8, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants