Skip to content

Make anti-leech choker really punish underreporters#8037

Open
Artoria2e5 wants to merge 1 commit intoarvidn:RC_2_0from
Artoria2e5:leech2
Open

Make anti-leech choker really punish underreporters#8037
Artoria2e5 wants to merge 1 commit intoarvidn:RC_2_0from
Artoria2e5:leech2

Conversation

@Artoria2e5
Copy link
Contributor

We previously only prevented underreporting from working. But since we can know some clients are underreporting, many by a factor of +Infinity, why not punish them?

The new change does just that. It divides the anti-leech score by the estimated under-reporting ratio. Of course we never estimate +Infinity because we do +1 on the divisor. Very light float32 math is used.

The comments are updated to explain the ways in which our solution differs from Chow et. al.

@Artoria2e5 Artoria2e5 changed the title Make anti-leech choker punish underreporters Make anti-leech choker really punish underreporters Sep 16, 2025
We previously only prevented underreporting from working. But since we know they are underreporting, many by a factor of +Infinity, why not punish them?
@arvidn
Copy link
Owner

arvidn commented Jan 3, 2026

../src/choker.cpp:206:52: error: implicit conversion from 'long' to 'float' may lose precision [-Werror,-Wimplicit-int-float-conversion]
                return int(std::abs(has - total_size / 2) * 2000 / total_size

Copy link
Owner

@arvidn arvidn left a comment

Choose a reason for hiding this comment

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

sorry for the delay. I finally put some thought into this. Any improvement is welcome, and maybe this is still an improvement, even if it's not perfect.

// 2. The ramp-up for liars remains too slow. Hence we also calculate a
// "honesty" ratio as (sent_blks / (claimed_blks + 1)) to
// quickly increase the penalty for no-report and divide Chow's
// progress-score with it.
Copy link
Owner

@arvidn arvidn Jan 4, 2026

Choose a reason for hiding this comment

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

one important consideration with (2) is the inherent latency in receiving HAVE messages. The complete bandwidth-delay product will always be considered lied about, by this logic. The delay in this case is worse than just the network round-trip. The other end needs to:

  • put it in the disk cache
  • wait for the other blocks for the same piece to be downloaded
  • hash the piece (and worst-case, have to read some blocks back from disk)
  • queue a HAVE message to be sent (which may end up behind other PIECE messages)

At high rates, this bandwidth delay product can easily be megabytes.

Another, perhaps less important, consideration is that this logic doesn't take into account which blocks were sent. A "cheater" that only downloads a single block per piece, but from 128 different peers, will be able to withhold 127/128 HAVE messages (99.2%) without triggering any suspicion (by this logic). This is assuming a piece size of 128 blocks, i.e. 2 MiB.

Comment on lines +199 to +200
float const dishonesty = std::min(1.f
, 1.f * float(given) / float(piece_length) / float(says_has + 1));
Copy link
Owner

Choose a reason for hiding this comment

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

I would prefer integer math here, only partly because of embedded systems but also to have deterministic results.

Since you divide by honesty below, couldn't you invert this, call it honesty and multiply it below? Then it might be reasonable to use an integer. e.g. std::int64 const honesty = std::max(1, piece_length * says_hash / (given + 1));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants