mjg-foundation / bitcoin-ordislow

Bitcoin Ordislow
https://bitcoincore.org/en/download
MIT License
2 stars 0 forks source link

Implement Ordislow spam mitigation #1

Open 1ma opened 1 year ago

1ma commented 1 year ago

Please describe the feature you'd like to see added.

The Ordislow spam mitigation patch slows down the propagation of new blocks which contain inscriptions, thus penalizing the miners who mine these transactions.

Node operators must be able to tune their nodes to slow down the propagation of these blocks by withholding them by a certain amount of time of their choosing.

Is your feature related to a problem, if so please describe it.

Inscriptions are a malicious kind of Bitcoin transaction that exploit a particular sequence of Bitcoin Script instructions that allow the attacker to attach up to 4MB of arbitrary bytes into these transactions.

These transactions are designed to, over time, cost-effectively raise the requirements of running a full node way above schedule and therefore subvert Bitcoin's decentralization.

Keeping an eye on these kind of malicious transactions and filtering them out is something that the Bitcoin Core dev team has always worked on. Bitcoin Core has included spam filters for different kinds of malicious transactions since 2010, and even Satoshi spoke against "using" the network this way^1.

Describe the solution you'd like

At a high level, these are the steps to implement Ordislow:

  1. Encapsulate LukeDashJr's filter-ordinals.patch (affectionately known as Ordisrespector) into it's own function so it can be called from elsewhere in the code base. This function still needs to be called at the same place in src/script/interpreter.cpp (so Ordislow is really a superset of Ordisrespector).

  2. Identify the section of the codebase that receives new mined blocks and validates them. In here, the code has to iterate over each transaction included in the block and raise a boolean flag in case any of these transactions is an inscription (using the function from step 1).

  3. In the section of the code that broadcasts new valid blocks, the code has to check the boolean flag. If it wasn't raised the block must be broadcasted as usual. If it was raised, the code has to withhold it for some amount of time before broadcasting it. This amount of time must be tunable by the source builder, ideally it should be set in an integer constant. Also, the code must understand the concept of delay = -1. In that case, the block must not be broadcasted at all.

Additionally, Bitcoin Core must log these events to the debug.log:

I think these would be helpful to debug Ordislow, as well as informative.

See the updated comment.

Describe any alternatives you've considered

Not caring about Bitcoin. But as long as it still works and can be defended it's not really an alternative.

Please leave any additional context

The original Ordislow thread on Twitter: https://twitter.com/oomahq/status/1642993852654182400

1ma commented 1 year ago

The whole thing can be done also for STAMP transactions^1 for an additional 5 million sat reward^2.

In this case the work to identify this kind of transactions (what Luke did for inscriptions) has not been done yet, as far as I know.

mjg-foundation commented 1 year ago

I found that the filter-ordinals patch is quite difficult to decouple from the function it's in, so I'll try to use that function as a whole to mark ordinal blocks instead. I also found that it's difficult to treat blocks differently when relaying, but could be possible by reordering the blocks to be relayed, with the ordinal blocks last, and starting the delay when reaching the first ordinal block to be relayed.

mjg-foundation commented 1 year ago

Just so I understand correctly, does the filter-ordinals patch just reject ordinals transactions in the mempool, or does it also reject blocks that include ordinals?

@1ma

Retropex commented 1 year ago

It only rejects transactions in the mempool, otherwise the chain would fork.

1ma commented 1 year ago

@Retropex is right. Read my Ordisrespector threads from February, if it helps. Ordisrespector (filter-ordinals.patch) and Ordislow are meant to complement each other.

https://twitter.com/oomahq/status/1621899175079051264 https://twitter.com/oomahq/status/1623052780280885253

mjg-foundation commented 1 year ago

I hit a new roadblock. The filter-ordinals patch made by LukeDashJr doesn't seem to be getting any hits on complete blocks. Instead, here's the trace I'm getting after adding a couple checks, in the most recent code: ordislow_checks

mjg-foundation commented 1 year ago

Also note that I added is_ordinal_included (is not nullptr) as an OR condition for entering the check for ordinals, as SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS isn't part of the flags checked in CheckInputScripts when it's called for new blocks.

mjg-foundation commented 1 year ago

I also noticed another case here: ordislow_errors2

dzyphr commented 1 year ago

does lukes patch filter just ordinals or all inscriptions?

Retropex commented 1 year ago

Ordinals are part of the inscriptions and Luke's filter filters all inscriptions.

dzyphr commented 1 year ago

Thanks @Retropex . Im trying to grasp an understanding of ordislow compared to Lukes ordisrespector patch. Is ordislow just attempting to implement the stuff requested above on top of Luke's patch?

Retropex commented 1 year ago

To roughly summarize Ordislow is there to force minors to execute Ordisrespector and therefore to stop mining inscriptions.

dzyphr commented 1 year ago

Ive been looking at Bitcoin core to see how the interpreter patch works and how it could be applied to other parts of the code. I see two potential paths for it, one is anywhere that we have access to a tx object we can run it through EvalScript which will implicitly check for inscription. However this is probably bloat, we should likely extract the parts of EvalScript that just check for inscriptions and use it in a similar way. But technically anywhere you have a tx object we should be able to run the filter. Thoughts on this?

dzyphr commented 1 year ago

heres an example on my branch: https://github.com/dzyphr/spam-resistant-Bitcoin/commit/e6502395800ba710a431949da370e0ed3e4ca38c

Retropex commented 1 year ago

Unfortunately I don't have the skills to understand at all but that, we will have to see with @mjg-foundation but he no longer has the area to be active for now.

dzyphr commented 1 year ago

No worries, I will just be implementing it experimentally for now in that repo.

Retropex commented 1 year ago

Can’t wait to see what it will give I’m waiting for ordislow with great impatience

dzyphr commented 1 year ago

question regarding step3:

In the section of the code that broadcasts new valid blocks, the code has to check the boolean flag. If it wasn't raised the block must be broadcasted as usual. If it was raised, the code has to withhold it for some amount of time before broadcasting it. This amount of time must be tunable by the source builder, ideally it should be set in an integer constant. Also, the code must understand the concept of delay = -1. In that case, the block must not be broadcasted at all.

I'm confused which part of the code we are talking about, and how this would work. For example if we receive a valid block we cannot just withhold it for another block unless a theoretical second spam-free block comes with a reorg of the previous one or many spammy blocks.

1ma commented 10 months ago

Update 2023-11-07

Turns out my original instructions are too simplistic because I was not aware of compact block relaying.

In reality Bitcoin Core only relays the new block header and its transaction IDs. When Bitcoin Core receives a compact block it fetches its transactions from the mempool, and if it was missing some it asks its peers for them.

Once it has all the block's transactions at hand it then relays the compact block immediately, and after that it starts the actual validation process (checking for double spends, etc). So in reality Bitcoin Core does things in a very non-intuitive order to optimize for relaying speed (what a shame...)

In light of this new information a better description of Ordislow would be:

  1. When Bitcoin Core receives a compact block, detect when it had to ask its peers for missing transactions from the mempool. These transactions are sus! Set them apart for further inspection. No need to check the rest, we can assume the node is also running Ordisrespector.
  2. For each of these transactions that had to be fetched, check if any of them contain the OP_0 OP_IF data envelope in their script.
  3. If there's any positive, simply do not broadcast the block but proceed to full block validation as usual.

For now we can ignore the "custom sleep" requirement, it adds too much unnecessary complexity. Max Pain Mode only.

Useful logs I'd like to see (should be added to the cmpctblock group of logs):

The receival of a new compact block and how many transactions were not found in the mempool is also useful, but already available (debug=cmpctblock has to be added to the node config to see this one):

Successfully reconstructed block 000000000000000000001c46dda7e778f4fa7598bee48dd94e898d119fa7484e with 1 txn prefilled, 1423 txn from mempool (incl at least 55 from extra pool) and 1115 txn requested
cculianu commented 10 months ago

After being in communication with @1ma .. I have developed a patch here (against latest core v26.0rc2 tag): https://github.com/cculianu/bitcoin/pull/1 (it's a PR just so you can see it as 1 big diff -- actual branch name on my repo is gleanable from the PR).

Here it is in action, with -debug=ordislow -debug=cmpctblock:

284212360-f8b3d97b-604a-4331-b05e-351ab2fecce5
mjg-foundation commented 9 months ago

@1ma @cculianu any status on the efficacy of the patch? Has the bounty been paid?

cculianu commented 9 months ago

@1ma @cculianu any status on the efficacy of the patch? Has the bounty been paid?

Not yet.. they are still testing it. I ragequit a few days ago due to my feeling frustrated with the situation BTC finds itself in, but came back today. It brought back old memories to work on BTC again (and also worry about how I feel it is not being well managed). But I came back today and @1ma and I had a chat and we plan on working together again to do more testing.

I have been running it for a week at least an it works for me just fine but the testers had some concerns and suggestions that maybe need ironing out...

Fudmottin commented 4 months ago

Is there any further news on the patch? Did any of the code make it into v27.0 which I am now running? I build from GitHub source, but I'm not super competent with git. I also don't know the code base although some of the header files in the src directory look like they implement features in C++20 or earlier. eg reverse_iterator.h, span.h, and random.h based on the file names.

Does anyone have any tips on getting up to speed with the bitcoin core codebase? I'm still catching up with modern C++, but I can read a fair bit of it from C++11. I've used C++17/20 features in tiny programs as well as a bit of boost, specifically the multiprecision cpp_int and mpfr_float.

Mastering Bitcoin 2nd Edition by Andreas M. Antonopoulos seems to be getting rather dated. I'm also not a python guy for some of the code examples. I prefer to do things in C++. I would like to drop inscriptions on the floor rather than relaying them. As for dealing with new blocks containing inscriptions, I gather that is more complicated due to a potential forking issue.

1ma commented 4 months ago

I called off the bounty, unfortunately. Realized this approach is more hurtful to well-behaved miners than badly-behaving ones.

The reason is that large and spamming pools can set up fast relaying networks between them so they don't incur any penalty (they already do in practice), while small independent miners who are not directly peered to them would see the blockchain tip with a lot of delay, therefore would be mining on top of a stale tip for longer, putting them at an increased disadvantage compared to those who do not.

Fudmottin commented 3 months ago

Sorry to hear that.

I have wondered how much latency there is in my full node getting new tip notifications. It's too bad that devices like Bitaxe aren't more plentiful. It would be nice if node runners could contribute to decentralized mining. I am aware that one data center has orders of magnitude more hash than all the combined nodes, even if they had an outboard ASIC or two for hashing.