-
Notifications
You must be signed in to change notification settings - Fork 38.7k
Fix: respend relay must examine more inputs #4484
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
Conversation
|
@gavinandresen Hope you have time to review. |
|
Code changes look good, I am writing a regression test to help test. |
|
First cut regression test that just tests the 'ordinary' double-spend case: Simulating attack scenarios addressed by this pull as additional tests would be spiffy... |
|
I'm adding 3 more tests, then will commit doublespendrelay.py. The tests are: no relay same input triple-spend, yes relay with triple spend before, and after, double-spend in input list. No test for not adding invalid tx to bloom filter. This would require shipping a tool that transmits invalid txes. |
|
The util.py function stop_node, which is used repeatedly in this test, sometimes hangs due to #4502 (stuck GetExternalIP). A successful test can take up to 30 seconds due to propagation waits. |
|
The changes themselves look OK at quick glance. However: Pattern matched :) In general, waiting a bit for tests to complete should be OK. However, anything longer than X seconds or minutes tends to impact programmer productivity, by slowing down the compile+test cycle, sometimes leading to the skipping of tests. The usual solution is a switch between quick tests and comprehensive tests. Long term, "make check" should really be running pull tester tests locally. We should not have to push to github to get comprehensive testing, IMO. My dev boxes are probably more beefy than our pull tester box. |
|
@jgarzik Compile with --with-comparison-tool=file.jar |
|
Looks good. |
|
Edited: removed accusation. The code should be: Please look at my comments in #4450. Don't merge this patch, it's too difficult to make it right. |
|
@SergioDemianLerner No, please re-read as committed. And please do provide a concrete example of at least one bug when making such a broad accusation. |
|
Dear @dgenr8, |
|
@dgenr8 I still see the problem in the code. |
|
@SergioDemianLerner That is still @petertodd's attack. Small first spend and huge respend. Rate limiter. |
|
Yup. @SergioDemianLerner If you think there's an attack, write up a quick script that demonstrates it. For instance here's one I did for the invalid-due-to-too-many-sigops attack: https://github.com/petertodd/tx-flood-attack It's really useful to have attack code available to do regression testing, as well as test new implementations. |
|
Ok. The reason my "attack" does not work is because RelayTransaction() does not relay the same transaction twice. And this is because setInventoryKnown is unbounded. But setInventoryKnown should be bounded (and this will be probably corrected in a future version), and so the attack is still possible, but it will require overflowing the setInventoryKnown to force it delete the tx hash. If elements are evicted randomly from setInventoryKnown, then there should be not problem. So from many perspectives I see this patch dangerous. The Bitcoin network is a dynamic feedback system that can oscillate and broadcasting is amplification. Have you seen bridges oscillate? |
|
For example, using SIGHASH_NONE the attacker can easily create thousands of variants of a single transaction without even computing signatures. Just 2000 double-spend inputs, and a single changing output. So the attacker consumes X bandwidth and each link of the whole network consumes X bandwidth. Can't we rise the bloom filter reset value to something like an average 100K hashes? |
|
What is the limit Kb/sec of the rate limiter? |
|
Last I checked the code before merge, it was 50 kB per 10 minutes. Seems it |
|
Yes, the RR rate limit is currently 100 thousand bytes/min = 1.6Kb/s. It was increased due to feedback from @sipa and @petertodd. All of the numerical constants are open to better-reasoned values of course. @SergioDemianLerner Was raising the bloom filter size related to your malleability point? The bloom filter contains outputs, not respend txes. When attacker controls the outputs we can just assume he has many ways to construct big respend txes. But you hit on something there with signature hash type. If tx1 (FIRST spend) has any hash type but SIGHASH_ALL, alerts are going to be triggered by third parties just doing what they're allowed to. So IsEquivalent needs to get smarter and take signature hash type into account, always ignoring all the parts a third party could change. Or, the easy way out is only alert for SIGHASH_ALL. If this is a bug, is it evidence of tacoma narrows style instability? Or can we just call it progress, collaboration, and enough-eyeballs? Looking for input on hash type. |
|
1.6Kb/s is very low, so the network will never be in danger for normal transactions/blocks, but is also means that the double-spend alert system will be easy to saturate if an attacker wants to. |
|
@SergioDemianLerner Targeted saturation with the goal of executing a particular double-spend in the dark is a risk. But they are all in the dark today. We would probably see this if unwise merchants accept 0-conf payments for valuable items. I completely disagree that you are isolated -- and you have contributed a ton to this. Do you still NACK it? If so, are there specific changes that would change your mind? |
|
@dgenr8 Yes, I still think it has problems. If we keep the reset constant too low (1000 outputs), then I see that the system could be attacked in this other way:
Let me first explain 1 with a targeted attack. I've have already explained how two transactions can pass though the network by being sent iteratively, but I will explain again if I was not clear: If the target node is an SPV node, also battery will be drained by full bandwidth usage. My Conclusion:
|
Why did you go back to assuming that all prevouts re-spent by tx1 or tx2 are simultaneously added to the bloom filter? That won't happen until MAX_DOUBLESPEND_BLOOM respend txes are relayed (on average). Your idea to dispense with the bloom filter in favor of a boolean respendRelayedForMe associated with mempool prevouts could eliminate fears about the bloom filter reset though. A conflict tx needs to be added to the wallet, to persistently document the state of the txes it conflicts with. This approach made alerting on respends in a block work too. But the bloom filter protects the wallet as well as the network. You are right that after each bloom filter reset (or wallet restart), one more conflicting tx can get added to the wallet. |
|
@SergioDemianLerner Just to note, overall this attack requires attacker to pay his victim, and the network to accept, but fail to execute, the transaction. MAX_DOUBLESPEND_BLOOM increased to 100000. |
|
@dgenr8 Yes I did erroneously assumed again simultaneously additions of prevouts. The problem is still g_signals.SyncTransaction(tx, NULL). Have you carefully followed this call when a double-spend is received? I didn't. The thing that you should research is what do nodes do when they receive multiple double-spends. If they store anything in memory, then an attack is possible. Even if the attack takes a week, it's a remote, anonymous attack. And you have to research this for every other Bitcoin implementation. For example BitcoinJ will probably store the double-spend within the wallet, and so it can be made to crash. Rising MAX_DOUBLESPEND_BLOOM seems good, but you should also analyze the bloom filter size to see how many false positives it will generate when the number of added prevouts reaches MAX_DOUBLESPEND_BLOOM. Regarding paying to the victim: my understanding is that the attacker is not. Because all of the tx created by the attacker are double-spends, none of them will be included in a block. In the worse case, just one of them will (and anyway the amount could be only a few satoshis). |
|
@SergioDemianLerner Got it, you are concerned about a wallet resource exhaustion attack on double-spend payees. Well, those are dropping right on into the wallet. Aaaand Sergio scores ;) As you say, I think the only thing we can do is not allow a respend relay tx into the wallet at all, unless it conflicts with a tx already in the wallet. That latter case is needed for the user alert, and limited by the bloom filter. Regarding non-core wallets, your attack is already possible today, but if relay results in more double spends on the network, it will be more of a threat. |
Restructure double-spend detection to fix three vulnerabilities in respend relay. When a transaction is found to be a relayable respend, stop checking its inputs for more respent prevouts. When a respend is found NOT to be relayable due to previous relay in connection with one input, keep searching its inputs. Do not update the respend bloom filter until a respend is actually relayed. Since all the checks in RelayableRespend have been parceled out to other locations (except for the rate limit) rename the function.
Use a 4-node network to test relay scenarios.
|
Lerner attack addressed in 389f3ee. |
Add flag fRespend to SyncTransaction interface. Do not add double-spends to the local wallet, even if they pay us, unless they conflict with an existing wallet transaction. This is to fix a resource exhaustion attack discovered by Sergio Demian Lerner.
|
Automatic sanity-testing: FAILED BUILD/TEST, see http://jenkins.bluematt.me/pull-tester/p4484_389f3ee45528dc0c88507b5a6abd78c71942eef4/ for binaries and test log. This could happen for one of several reasons:
If you believe this to be in error, please ping BlueMatt on freenode or TheBlueMatt here. 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/ |
|
Closing, since the now depends on reverted changes. I'll submit a new cleaned-up pull request. |
Restructure double-spend detection to fix three vulnerabilities in respend relay.
First, when a transaction is found to be a relayable respend, stop checking its inputs for more respent prevouts. Attacker should not be able to suppress relay by including a later input that has already been double-spent.
Second, when a respend is found NOT to be relayable due to previous relay in connection with one input, keep searching its inputs. Attacker should not be able to suppress relay by including an earlier input that has already been double-spent.
Third, do not update the respend bloom filter until a respend is actually relayed. Otherwise, attacker can neutralize relay by sending an invalid respend, followed by a valid one.