Skip to content

Proposal for dynamic network difficulty adjustment #2309

Description

@PeterSurda

There is a need to raise the difficulty in order to better protect against SPAM. Unfortunately this is at least partially not backwards compatible, because there is no mechanism for updating the network difficulty, the recipient metadata, or chan difficulty.

The main constraint is that I want this to not require a central instance, but at the same time not reduce anonymity.

The core of the proposal is a new network object, a difficulty vote. It will not contain any recipient, sender, encryption or signatures, only some random padding. It could have a fixed size. In metadata, it would contain

  • either a creation time or a duration, to allow calculating the original difficulty
  • current difficulty
  • proposed difficulty

The client will verify the PoW as it does now, and then use a sliding window to calculate the current difficulty. There needs to be some wiggle room to avoid under/overshoots as the inventory contents are transient.

What this will allow is to have people who want to become "miners" and "bump" the network difficulty, making it more costly for the spammers. It would auto-adjust based on how much proof of work the miners contribute.

Deployment would happen in phases:

  • Each of your addresses should track last announced difficulty. We already track last time the pubkey was sent. This is needed so that we know whether it's needed to announce an update or not.
  • Allow pubkey objects to update metadata (difficulty). I.e. if you receive an updated pubkey from someone, you update the demanded difficulty. This currently doesn't work, after you receive a pubkey, the difficulty is never updated.
  • Have pubkey objects to be auto generated when difficulty changes even if there is no request for them and the old ones haven't expired, but leave a bit of wiggle room (delay, range), to counter deanonymisation and DoS attempts.
  • Add an option in keys.dat to enable generating difficulty vote and configure the target difficulty. Default off. Simultaneously, accept difficulty vote objects and calculate difficulty, just to see how it works. The resulting difficulty can be shown inside the network stats tab, and/or exposed via API.
  • Allow tracking of objects that don't have sufficient difficulty for network-wide difficulty. Currently, they would keep getting redownloaded. We also need to be able to track them without having to store them fully in the inventory, so that we also reduce our storage of spam. For example, we'd track the inventory vector and expiration. We can use a new table in messages.dat for that.
  • There should be new options in keys.dat to
    • Use the difficulty vote to calculate which difficulty to announce for your own addresses (i.e. your own spam filter). Default off.
    • Use the difficulty vote to calculate which objects to accept in inventory (i.e. a network-wide spam filter). Default off.
  • Deprecate user-configurable difficulty and have the global one calculated from the difficulty votes to be used instead. This will make your own spam filter mandatory.
  • Force migrate chans, because chans don't have a difficulty metadata and they are most affected by spam
  • Force using difficulty for inventory filter (i.e. network-wide spam filter)

The difficulty check for accepting inventory objects should be more relaxed than for the local spam filter, to make network-wide transitions smoother.

Activity

  1. self-assigned this
    on Mar 22, 2026
  2. smallzhong commented on Mar 28, 2026

    @smallzhong
    Contributor

    Peter, thanks for putting this proposal together. The spam problem is undeniably the most pressing issue Bitmessage faces right now, and I understand the intent behind it. However, I have some fundamental concerns about this approach, and I'd also like to take this opportunity to discuss the bigger picture.

    On the Difficulty Vote Mechanism

    Who Will Mine?

    The core assumption of this proposal is that people will voluntarily spend computing power to generate vote objects. But unlike Bitcoin miners who earn coins for their work, Bitmessage "miners" would burn electricity purely to help the network filter spam — with zero economic return. If not enough people participate, the system is dead on arrival. If only a few participate, they effectively gain the power to manipulate network-wide difficulty — which is more dangerous than a fixed difficulty.

    Difficulty Can Be Maliciously Manipulated

    An attacker who rents cloud GPUs can flood the network with vote objects in a short period, pushing difficulty so high that ordinary users can no longer send messages. This would effectively be a DoS attack using the very mechanism we designed to protect the network.

    No Chain, No Consensus

    Different nodes inherently have inconsistent inventory contents — objects have a TTL (up to 28 days + 3 hours), and network propagation introduces delays. Computing a sliding window over inconsistent inputs will produce different difficulty values on different nodes. The proposal mentions the need for "wiggle room," but without a chain or any consensus mechanism, there's no fundamental solution to this problem. The end result could be messages accepted by some parts of the network and rejected by others — a de facto network split.

    The Bigger Picture

    Before continuing to layer complex mechanisms on top of the current protocol and codebase, I think we need to face some more fundamental issues:

    The Address Model Copies Bitcoin Without Bitcoin's Prerequisites

    Bitcoin makes addresses a hash of the public key because it has a chain — public keys are naturally revealed on-chain when spending UTXOs, and anyone can retrieve them. Bitmessage has no chain, so it needs a getpubkey mechanism to request public keys. This creates two serious problems:

    First, getpubkey breaks anonymity. A getpubkey request contains the target address's RIPE hash or tag in plaintext. Any network observer can see that "someone wants to send a message to this address." For a protocol whose core selling point is anonymity, this is a fundamental design flaw.

    Second, public key exposure enables targeted spam. In the current model, anyone can obtain any address's public key — either from pubkey objects broadcast on the network, or by actively sending getpubkey requests. Once an attacker has a public key, they can craft messages that the target is guaranteed to receive and decrypt. This means an attacker can systematically collect public keys and send spam to every reachable address on the network, with certainty that each message will land in the recipient's inbox.

    A cleaner design would have addresses contain the public key directly, so senders can construct encrypted messages without "requesting" anything from the network. To network observers, all objects should look indistinguishable from random data — only the actual recipient should be able to identify and decrypt messages intended for them. This would eliminate both the anonymity leak from getpubkey and result in a fundamentally cleaner protocol design.

    Object Type Is Visible in Plaintext Headers

    The objectType of every network object (getpubkey/pubkey/msg/broadcast) is unencrypted. Combined with the size differences between object types, a passive observer can easily classify all traffic.

    SHA-1 Is Still in the Signature Path

    pyelliptic's sign() defaults to SHA-1 as its digest algorithm, and highlevelcrypto's verify() tries SHA-1 first when no algorithm is specified. SHA-1 collision attacks have been practically demonstrated since 2017 (SHAttered).

    Python 2.7

    It's 2026. Python 2.7 has been end-of-life for over 6 years. A security-focused communication tool running on a runtime with no security updates is a risk in itself.

    My Take

    Some of these issues are at the protocol level (address model, public key exposure, plaintext headers) and cannot be fixed without changing the protocol. Others are at the engineering level (SHA-1, Python 2) but fixing them requires breaking compatibility. Continuing to add new features on top of this foundation — new object types, inventory restructuring, forced chan migration, multi-phase deployment — carries enormous engineering cost with questionable returns.

    Bitmessage's core vision — decentralized, anonymous, censorship-resistant communication — is still valuable, arguably more valuable than it was in 2012. But I believe the more pragmatic path forward is to start a new project with a from-scratch protocol design, using modern cryptographic primitives and modern engineering practices, rather than continuing to patch what we have.

  3. PeterSurda commented on Mar 28, 2026

    @PeterSurda
    MemberAuthor

    Thank you for your feedback. These are reasonable arguments, but debateable.

    1. Mining incentives: I agree that this isn't perfect, but we don't need perfect. We just need the ability for the network difficulty to adjust, without too much risk of centralisation. We can either have a completely separate protocol for network difficulty adjustment, but then the question arises whether Bitmessage itself even makes sense. For starters I'll do the "mining", others don't have to participate. For the future, this can be connected with my old proposal for using blind signatures as an alternative to PoW. The TLDR; is that especially mobile users could purchase tokens, which would be certified signatures that they blind during creation, and people with enough mining power could register with me and then receive money for the mining. This does have an element of centralisation, me, but it's an optional functionality. Blind signatures preserve user anonymity.
    2. Difficulty manipulation: This should be possible to compensate by competition and that it only is a problem if it is a bigger problem than the SPAM that the attackers would otherwise send. At least with competition there is a counter-force, but without difficulty adjustment there is almost nothing that you can do.
    3. Difficulty consensus absence: This is also only a partial problem, even if the wiggle room fails. We can limit the difficulty adjustments to only affect the calculation of pubkey objects. The inventory would still grow but SPAM in your inbox would be reduced and the authoritative difficulty for sending messages would still be only the one from the pubkey object. Also if you receive a message with too low difficulty, you could respond by providing the updated pubkey (after the old one expires and after some anti-deanonymisation delay). This way the sender would be notified that the message needs to be resent. There needs to be some additional code for this but it looks feasible to me. But overall, we do have to balance between coordination and decentralisation.
    4. Address versus pubkey: Earlier versions of Bitmessage did have the pubkey more exposed and it resulted it SPAM which was then further exploited for deanonymisation. So the tradeoff may be counterintuitive. Also if there is no pubkey object, there also cannot be any pubkey metadata, or the metadata can't change. You can't signal which features you support and you can't adjust your difficulty.
    5. Object type visible: This is also only a partial problem, it can be addressed by onion routing (you encapsulate the pubkey objects into one or more layers of "remailers". This technically is possible now, there just isn't a good way of coordinating who can be a "remailer" and who not, because if the "remailer" is offline, the object gets stuck.
    6. SHA-1 I think this can be deprecated on the next release, I don't think there's much point in keeping SHA-1 signatures.
    7. Python 2.7 Yes, this has been bugging me for a long time, but I recently found a way to speed up the python 3 porting. I think the backend (without GUI) can be ported in a reasonable future. I'm not sure about the GUI status. I haven't found anyone so far who can do this with acceptable quality, but now this can be AI assisted, once I get the workflow working correctly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions