Sending a getheaders message with an empty locator and a stop hash is a request for a single header by hash. The node will respond with headers for blocks not in the main chain as well as those in the main chain. To avoid fingerprinting, the node should, however, ignore requests for headers on side branches that are too old. This replicates the logic that currently exists for getdata requests for blocks.
[net] Ignore getheaders requests for very old side blocks #11113
pull jimpo wants to merge 2 commits into bitcoin:master from jimpo:net-getheaders-fingerprint changing 4 files +189 −11-
jimpo commented at 9:15 PM on August 22, 2017: contributor
- jimpo force-pushed on Aug 22, 2017
-
TheBlueMatt commented at 9:44 PM on August 22, 2017: member
Hmm, we should also handle the case where they have orphans in their locator similarly (ie parse their locator further back to find the first locator entry that is on our main chain).
-
jimpo commented at 10:03 PM on August 22, 2017: contributor
@TheBlueMatt This is the case where the locator is empty and only the stopHash is set. It's a special case request for a particular header by hash.
-
TheBlueMatt commented at 10:13 PM on August 22, 2017: member
@jimpo yes, sorry, I had since edited my comment to note that we should do something similar for locator-present requests.
-
in src/net_processing.cpp:731 in 4f559f1d88 outdated
724 | @@ -725,6 +725,17 @@ void Misbehaving(NodeId pnode, int howmuch) 725 | // blockchain -> download logic notification 726 | // 727 | 728 | +// To prevent fingerprinting attacks, only send blocks/headers outside of the 729 | +// active chain if they are no more than a month older (both in time, and in 730 | +// best equivalent proof of work) than the best header chain we know about. 731 | +static bool SideBlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams)
TheBlueMatt commented at 10:24 PM on August 22, 2017:Generally referred to as "Orphans" not "SideBlocks".
TheBlueMatt commented at 10:27 PM on August 22, 2017: memberOops, I misread FindForkInGlobalIndex, sorry, code looks fine, though note nomenclature is wrong.
jimpo force-pushed on Aug 22, 2017laanwj added the label P2P on Aug 23, 2017sdaftuar commented at 8:49 PM on August 24, 2017: memberConcept ACK, good catch. Would be great to have a p2p test case for this too.
jimpo force-pushed on Aug 26, 2017jimpo force-pushed on Aug 26, 2017sipa commented at 7:30 PM on August 27, 2017: memberutACK fed7483751cc94a7ce27fc6057a2080af8178223. Only had a casual look at the tests.
sdaftuar commented at 7:10 PM on August 30, 2017: memberACK 3d61d72
in src/net_processing.cpp:733 in 3d61d728d1 outdated
724 | @@ -725,6 +725,17 @@ void Misbehaving(NodeId pnode, int howmuch) 725 | // blockchain -> download logic notification 726 | // 727 | 728 | +// To prevent fingerprinting attacks, only send blocks/headers outside of the 729 | +// active chain if they are no more than a month older (both in time, and in 730 | +// best equivalent proof of work) than the best header chain we know about. 731 | +static bool OrphanBlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams) 732 | +{ 733 | + static const int nOneMonth = 30 * 24 * 60 * 60;
meshcollider commented at 10:13 PM on September 4, 2017:Constants should be caps with snake case
in src/net_processing.cpp:720 in 3d61d728d1 outdated
724 | @@ -725,6 +725,17 @@ void Misbehaving(NodeId pnode, int howmuch) 725 | // blockchain -> download logic notification 726 | // 727 | 728 | +// To prevent fingerprinting attacks, only send blocks/headers outside of the 729 | +// active chain if they are no more than a month older (both in time, and in 730 | +// best equivalent proof of work) than the best header chain we know about. 731 | +static bool OrphanBlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams)
meshcollider commented at 10:15 PM on September 4, 2017:snake_case argument names
jimpo commented at 2:09 AM on September 7, 2017:consensusParamsis camelCase everywhere else in the file, so I think it's better to leave as is. Feel free to replace everywhere in a separate PR.
meshcollider commented at 9:19 PM on September 8, 2017:Style-only PRs are highly discouraged, developer guide says to use the style in all new code regardless of style around it, because eventually it will all be replaced
promag commented at 10:20 PM on September 18, 2017:Just
params?
meshcollider commented at 10:36 PM on September 4, 2017: contributorutACK
jimpo force-pushed on Sep 7, 2017jimpo force-pushed on Sep 7, 2017jimpo force-pushed on Sep 7, 2017jimpo force-pushed on Sep 7, 2017jimpo force-pushed on Sep 8, 2017gmaxwell commented at 6:35 PM on September 8, 2017: contributorutACK.
Perhaps a small test nit, if I'm not misreading the test, I think the test would pass if it started failing for all really old headers even if they're on the active chain. We should have a check that that is still successful. We should also include a note in the tests that we don't test both criteria (both time and PET) as those tests would be good to add later.
obNitpickSummoning: @sipa you're okay with calling this function 'orphanblock' rather than something like 'staleblock'? :)
jimpo force-pushed on Sep 8, 2017jimpo commented at 8:21 PM on September 8, 2017: contributor@gmaxwell Thanks, added another test for that case. What do you mean by PET?
Also, yeah, I don't like the term orphan block (because it's ambiguous). Is stale block the preferred term now? I originally named it "side block" but I hear no one else calls it that.
EDIT: Nevermind, PET = Proof (of work) Equivalent Time. Will add the comment.
jimpo force-pushed on Sep 8, 2017in src/net_processing.cpp:65 in d07a788f25 outdated
60 | @@ -61,6 +61,14 @@ static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUAR 61 | 62 | static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8] 63 | 64 | +/// Age after which an orphan block will no longer be served if requested as 65 | +/// protection against fingerprinting.
promag commented at 10:19 PM on September 18, 2017:Specify unit.
in src/net_processing.cpp:69 in d07a788f25 outdated
60 | @@ -61,6 +61,14 @@ static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUAR 61 | 62 | static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8] 63 | 64 | +/// Age after which an orphan block will no longer be served if requested as 65 | +/// protection against fingerprinting. 66 | +static const int ORPHAN_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60; 67 | + 68 | +/// Age after which a block is considered historical for purposes of rate 69 | +/// limiting block relay. Set to one week.
promag commented at 10:19 PM on September 18, 2017:Specify unit.
in test/functional/test_framework/mininode.py:1314 in d07a788f25 outdated
1309 | @@ -1310,8 +1310,8 @@ def __repr__(self): 1310 | class msg_headers(object): 1311 | command = b"headers" 1312 | 1313 | - def __init__(self): 1314 | - self.headers = [] 1315 | + def __init__(self, headers=None): 1316 | + self.headers = headers if headers is not None else []
promag commented at 10:30 PM on September 18, 2017:Just?
def __init__(self, headers=[]): self.headers = headers
jimpo commented at 5:34 PM on September 19, 2017:That is not safe in Python: http://docs.python-guide.org/en/latest/writing/gotchas/#mutable-default-arguments.
promag commented at 8:34 PM on September 25, 2017:Oh! nice, ty!
in test/functional/p2p-fingerprint.py:143 in d07a788f25 outdated
138 | + # Request for very old orphan block header should now fail 139 | + self.send_header_request(orphan_hash, node0) 140 | + time.sleep(3) 141 | + assert not self.last_header_equals(orphan_hash, node0) 142 | + 143 | + # Verify we can fetch very old blocks and headers on the active chain.
promag commented at 10:31 PM on September 18, 2017:Nit, I also prefer periods, but at least be consistent, remove?
in test/functional/p2p-fingerprint.py:11 in d07a788f25 outdated
6 | + 7 | +If an orphan block more than a month old or its header are requested by a peer, 8 | +the node should pretend that it does not have it to avoid fingerprinting. 9 | +""" 10 | + 11 | +import time
promag commented at 10:32 PM on September 18, 2017:Nit
from time import (sleep, time)
jimpo commented at 5:40 PM on September 19, 2017:If other people feel strongly, I'm willing to change it, but I prefer to only import packages and modules, not values. This makes it a bit more clear where the function is coming from.
in src/net_processing.cpp:723 in d07a788f25 outdated
713 | @@ -706,6 +714,16 @@ void Misbehaving(NodeId pnode, int howmuch) 714 | // blockchain -> download logic notification 715 | // 716 | 717 | +// To prevent fingerprinting attacks, only send blocks/headers outside of the 718 | +// active chain if they are no more than a month older (both in time, and in 719 | +// best equivalent proof of work) than the best header chain we know about. 720 | +static bool OrphanBlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams) 721 | +{ 722 | + return (pindexBestHeader != nullptr) &&
promag commented at 10:36 PM on September 18, 2017:Missing
AssertLockHeldbecause ofpindexBestHeader?
theuni commented at 6:40 PM on September 19, 2017:+1
jimpo force-pushed on Sep 19, 2017TheBlueMatt commented at 1:41 AM on September 21, 2017: memberutACK non-test parts of 8e57e82942c6ccb6ac905bd44d0d476bcf1651ec
gmaxwell approvedgmaxwell commented at 7:40 PM on September 25, 2017: contributorre-ACK
promag commented at 8:37 PM on September 25, 2017: memberutACK modulus periods in commit messages and PR title 😛.
jimpo renamed this:[net] Ignore getheaders requests for very old side blocks.
[net] Ignore getheaders requests for very old side blocks
on Sep 26, 2017jonasschnelli commented at 5:53 PM on September 27, 2017: contributor@promag: can you retest/re-ack if the requested changes are ok?
jimpo force-pushed on Sep 27, 2017in test/functional/p2p-fingerprint.py:114 in b74b0b788f outdated
109 | + 110 | + orphan_hash = int(block_hashes[-1], 16) 111 | + 112 | + # Check that getdata request for orphan block succeeds 113 | + self.send_block_request(orphan_hash, node0) 114 | + test_function = lambda: self.last_block_equals(orphan_hash, node0)
promag commented at 10:52 PM on September 27, 2017:Inline lambdas in
wait_until?
jimpo commented at 11:36 PM on September 27, 2017:I'd rather not.
in test/functional/p2p-fingerprint.py:135 in b74b0b788f outdated
130 | + self.send_header_request(block_hash, node0) 131 | + node0.sync_with_ping() 132 | + 133 | + # Request for very old orphan block should now fail 134 | + self.send_block_request(orphan_hash, node0) 135 | + time.sleep(3)
promag commented at 11:00 PM on September 27, 2017:Also use
wait_untilinstead of fixed sleep?
jimpo commented at 11:35 PM on September 27, 2017:That doesn't work. We're waiting to make sure a change does not happen.
in test/functional/p2p-fingerprint.py:140 in b74b0b788f outdated
135 | + time.sleep(3) 136 | + assert not self.last_block_equals(orphan_hash, node0) 137 | + 138 | + # Request for very old orphan block header should now fail 139 | + self.send_header_request(orphan_hash, node0) 140 | + time.sleep(3)
promag commented at 11:04 PM on September 27, 2017:Same as above.
in test/functional/p2p-fingerprint.py:97 in b74b0b788f outdated
92 | + # Generating a chain of 10 blocks 93 | + block_hashes = self.nodes[0].generate(nblocks=10) 94 | + 95 | + # Create longer chain starting 2 blocks before current tip 96 | + height = len(block_hashes) - 2 97 | + block_hash = block_hashes[height - 1]
promag commented at 11:06 PM on September 27, 2017:Nit, remove above
heightand:block_hash = block_hashes[-3]
jimpo commented at 11:37 PM on September 27, 2017:I think this better represents my intent as is. Also, height is used two lines below.
in test/functional/p2p-fingerprint.py:37 in b74b0b788f outdated
32 | + def set_test_params(self): 33 | + self.setup_clean_chain = True 34 | + self.num_nodes = 1 35 | + 36 | + # Build a chain of blocks on top of given one 37 | + def build_chain(self, nblocks, prev_hash, prev_height, prev_median_time):
promag commented at 11:23 PM on September 27, 2017:Determine
prev_heightandprev_median_timehere and simplify code below.
jimpo commented at 11:36 PM on September 27, 2017:I don't understand. Example code?
promag commented at 8:32 AM on September 28, 2017:I mean something like:
def build_chain(self, nblocks, prev_hash): header = self.nodes[0].getblockheader(prev_hash) height = header["height"] cur_time = header["mediantime"] blocks = [] for _ in range(nblocks): height += 1 cur_time += 1 coinbase = create_coinbase(height) block = create_block(int(prev_hash, 16), coinbase, cur_time) block.solve() blocks.append(block) prev_hash = block.hash return blocks ... new_blocks = self.build_chain(5, block_hashes[-3])This way height below is not needed.
promag commented at 11:28 PM on September 27, 2017: memberCommit [test] P2P functional test for certain fingerprinting protections has changes that belong to 1st commit.
jimpo force-pushed on Sep 27, 2017laanwj commented at 11:15 AM on September 28, 2017: memberobNitpickSummoning: @sipa you're okay with calling this function 'orphanblock' rather than something like 'staleblock'? :)
Yes, a small nit regarding language: there is some ambiguity with regard to "orphan". Sometimes this is used for disconnected blocks/transactions (of which the parent is not known), sometimes for blocks/transactions outside the active chain. "stale" would be more specific, maybe.
utACK otherwise.
promag commented at 1:19 PM on September 28, 2017: memberutACK 00ec5a3. Nits and other possible improvements can be worked out later.
jimpo force-pushed on Oct 3, 2017a2be3b66b5[net] Ignore getheaders requests for very old side blocks
Sending a getheaders message with an empty locator and a stop hash is a request for a single header by hash. The node will respond with headers for blocks not in the main chain as well as those in the main chain. To avoid fingerprinting, the node should, however, ignore requests for headers on side branches that are too old.
[test] P2P functional test for certain fingerprinting protections eff4bd8ab2jimpo force-pushed on Oct 3, 2017laanwj merged this on Oct 11, 2017laanwj closed this on Oct 11, 2017laanwj referenced this in commit fef65c4f5e on Oct 11, 2017jimpo deleted the branch on Oct 11, 2017codablock referenced this in commit 953d74bcea on Mar 12, 2019codablock referenced this in commit d1a6022605 on Mar 12, 2019DrahtBot locked this on Sep 8, 2021
github-metadata-mirror
This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-21 12:15 UTC
This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me