Fifteen OFAC-sanctioned transactions missing from blocks
F2Pool is filtering OFAC transactions again?
Thursday, January 16, 2025My miningpool-observer project aims to detect when Bitcoin mining pools are not mining transactions they could have been mining. Over the past few weeks, it detected fifteen missing transactions spending from OFAC-sanctioned addresses. This post examines whether these transactions were filtered intentionally or if there are other possible explanations for these transactions to be missing from blocks. I conclude that F2Pool might have started to filter OFAC-sanctioned transactions again. However, as F2Pool is currently the only pool that’s possibly filtering, it does not affect Bitcoin’s censorship resistance: The sanctioned transactions confirmed in the following blocks.
An exchange used the recent low-fee environment to consolidate coins from deposit addresses to their hot wallet. These consolidations included more than a hundred transactions each spending at least one UTXO belonging to an OFAC-sanctioned Bitcoin address. These transactions were good candiates for re-evaluating if mining pools filter OFAC-sanctioned transactions.
The miningpool-observer tool provides insights into which transactions mining pools include and which transactions they exclude. This works by regularly building block templates with my node and comparing them to newly mined blocks. The methodology is explained in detail here. While all of the OFAC-sanctioned transactions were mined eventually, fifteen were not mined in the first block they could have been mined in. For these, a closer analysis can help to determine if a mining pool is purposefully excluding these or if they didn’t make it into the block for other reasons: For example, a well-connected mining pool may know high-feerate transactions my node does not yet know about. These higher-feerate can displace lower-feerate transactions my node considered for my block template. On the other hand, it can happen that my node already knows about a transaction, but the pool hasn’t yet sent out a mining job to its miners that includes the transaction. These “young” transactions can’t be used as evidence for transaction filtering.
Blocks with missing sanctioned transactions
In the following, I list all reports my miningpool-observer instance issued via its RSS feed. The open-source tool can be self-hosted to check mining pool blocks against your own block templates.
F2Pool Block 875575 (filtered?)
My miningpool-obsever instance
reports
the transaction
16f0ada6..
as missing from F2Pool’s block 875575. The transaction spends a TXO belonging to
an address sanctioned by
OFAC as part of sanctions against Chinese illicit drug producers and traders.
The transaction had been in my node’s mempool for more than 3 hours at the time
the block was found, which is more than enough time to propagate to other
nodes and mining pools. The transaction has 300 2-of-3 multisig inputs and a
single output resulting in a size of 86.4 kvByte and 345.88 kWU. It pays a
feerate of 3.97 sat/vByte, which is only minimally higher than the 3.92
sat/vByte feerate of the last transaction package in the block. In
mempool.space’s
block audit, the transaction is marked as excluded: “marginal
fee”. This indicates
that “the transaction may have been displaced by an added transaction, or it may
have been displaced by another transaction from the mempool that was also at the
low end of the expected feerate range for the block”.
To determine how close the sanctioned transaction was to not being included, we can place it into its potential position in the block based on its feerate. The package feerate distribution plot above shows that the sanctioned transaction with a weight of 345.88 kWU would have started at roughly the 3.25 MWU mark based on the feerate it pays. The end of the transaction is more than 400 kWU away from the maximum block weight limit of 4 MWU. While the feerate difference between the sanctioned transaction to the last package feerate might be marginal, the transaction should have been included in the block. This indicates that F2Pool could have filtered the transaction.
F2Pool Block 875840 (too young?)
For F2Pool’s block 875840, miningpool-observer
reports
that the transaction
26a3693d..
is missing.
The transaction spends from the same sanctioned address as in block 875575 and
two other sanctioned addresses related to the same Chinese illicit drug
producers and traders mentioned earlier.
The transaction is marked as “young” by miningpool-observer as it was only seen 58 seconds before the block was first seen. My node first saw the transaction at 06:17:10 UTC, while the block header timestamp of F2Pool’s block is 06:17:21 UTC (+11s). The miningpool-observer tool processed the block at 06:18:08 UTC (another +47s later). It’s unclear how accurate the header timestamp is as F2Pool’s and my clock might not be in sync and miners might engage in timestamp rolling. We can’t know if the transaction was filtered by F2Pool, or if it is a false-positive report. It could be that the transaction didn’t make it into F2Pool’s mining job for timing reasons related to transaction propagation or mining job publication. This missing transaction can’t be seen as evidence that F2Pool filters OFAC-sanctioned transactions.
F2Pool Block 875933 (filtered?)
The transaction
5c186378..
is reported
to be missing from F2Pool’s block 875933. One of the outputs being spent
belongs to a sanctioned address related to the same Chinese illicit drug
producers and traders mentioned before.
The transaction had been in my node’s mempool for more than 5 minutes before the
block was found. This is generally enough time for the transaction to
propagate to the pool and to make it into a block template and mining
job. With 300 inputs and one output, the transaction has a size of 85'713 vByte. It pays a fee
of 339'876 sat, which
results in a feerate of 3.965'279 sat/vByte. For this block, the miningpool-observer tool
also reports that F2Pool included the “extra” transaction
a64c4d64..
(among others) which wasn’t present in my node’s block template. This transaction
is a similar 300-input and one-output consolidation with a size of 85'917 vByte (204 vByte
larger than the missing transaction). It pays a fee of 340'632 sat (756 sat more than the missing
transaction), which results in a feerate of 3.964'664 sat/vByte which is minimally lower than the
feerate of the sanctioned transaction. Both transactions didn’t have any
unconfirmed parents or children at the time the block was found. A
fee-maximizing miner should have included the sanctioned transaction
5c186378..
in favor of a64c4d64..
.
While the mempool-space block audit marks the transaction with “marginal fee”, the package feerate distribution chart shows there would have been more than 500 kWU of space in the block after the transaction. This indicates that F2Pool could have filtered the transaction.
F2Pool Block 876028 (filtered?)
For F2Pool’s block 876028, two sanctioned transactions are
reported
missing. Both 4b073053..
and 3d9f2824..
spent
from a sanctioned address related to the Chinese illicit drug producers and
traders mentioned before.
Transaction 4b073053..
had been in my node’s mempool for more than 10 minutes
and transaction 3d9f2824..
for more than 6 minutes. F2Pool ended up including
the 300-input and one-output consolation transaction
906da5b5..
which my node didn’t consider for the block as it has a minimally lower feerate
than the two sanctioned transactions:
The sanctioned transaction 4b073053..
has a size of 84'731 vByte while paying 336'096 sat in fees resulting in a feerate
of 3.966'624 sat/vByte.
With 86'079 vByte and a
fee of 341'388 sat, the
sanctioned transaction 3d9f2824..
pays a feerate of 3.965'985 sat/vByte. The extra transaction
906da5b5..
pays 336'096 sat at a size of 84'749 vByte, which results in a minimally lower feerate of 3.965'781 sat/vByte.
A fee-maximizing miner should have included the extra transaction 906da5b5..
only after the two sanctioned transactions.
While mempool.space tags both transactions as “marginal fee”, both sanctioned transactions could have made it into the block based on their feerate and weight. This indicates that F2Pool could have filtered the two transactions.
F2Pool Block 876255 (filtered?)
The transaction
2d56f2f2..
is
reported
to be missing from F2Pool’s block 876255. It spends from a sanctioned address
related to the Chinese illicit drug producers and traders. The transaction was
in my node’s mempool for over an hour. The
mempool.space
block-audit marks this transaction as “removed” from the expected block too.
This indicates that F2Pool could have filtered the transaction.
SBI Crypto Block 876590 (displaced?)
For SBI Crypto’s block 876590, the transaction a8a2e533.. is reported to be missing. The transaction spends from a sanctioned address related to the Chinese illicit drug producers and traders.
The transaction had been in my mempool for 44 minutes at the time the block was found. With a fee of 252'639 sat and a size of 84'947 vByte it pays a feerate of 2.974'078 sat/vByte. SBI Crypto was aware of 16 transactions (see “Extra Transactions”) that paid a higher higher feerate (between 3 and 15 sat/vByte) totaling up to 4074 vByte. These displaced the sanctioned transaction and 12 other transactions (see “Missing Transactions”) from the block. This allowed the pool to pick the slightly smaller 300-input and one-output non-sanctioned consolidation transaction e6b44288.. paying a fee of 246'969 sat with a size of 83'042 vByte. This results in a feerate of 2.974'025 sat/vByte, which is minimally lower than the feerate of the sanctioned transaction but higher than the feerates of the 12 missing transactions.
As the sanctioned transaction would have been included rather close to the end of the block with the 16 extra transactions, it’s worthwhile to take a closer look if the sanctioned transaction a8a2e533.. would fit into the block after the 16 extra transactions.
In block 876590, the first transaction (e6b44288..) with a feerate lower than the sanctioned transaction starts
after the first 3'659'828 WU of the block and weighs 332'167 WU. The sanctioned transaction
a8a2e533.. weighs
339'785 WU (7618 WU more).
The transaction e6b44288.. ends
at 3'659'828 WU + 332'167 WU =
3'991'995 WU (8005 WU left in block) and the sanctioned transaction
would end at 3'659'828 WU + 339'785 WU =
3'999'613 WU (387 WU left in block). Both don’t overflow
the maximum block weight of 4 MWU, however, there is another threshold to
consider.
When building block templates, Bitcoin Core reserves a bit of block weight for
the block header and the coinbase transaction, which aren’t part of the
template. Due to the bug described in issue #21950: Duplicate coinbase
transaction space reservation in
CreateNewBlock, Bitcoin Core
currently reserves 2x 4000 WU by default. Subtracting the actual coinbase
weight of 716 WU and the header weight of 332 WU from the reserved 8000 WU
leaves us with a reserved but unused weight of 6952 WU. After the sanctioned
transaction a8a2e533.., only
387 WU would have been left in the block, which is less than 6952 WU, and this
explains why the sanctioned transaction was not picked. However, after
transaction e6b44288.., there
were still 8005 WU - 6952 WU = 1053 WU
left to fill. These were filled with a
450 WU and a 437 WU transaction 1. Leaving 1053 WU - 450 WU - 437 WU = 166 WU
of unreserved block weight unused.
Block weight usage of block 876590:
320 WU # header weight
+ 12 WU # tx_count var_int weight
+ 716 WU # coinbase transaction weight
+ 3991834 WU # non-coinbase transaction weight
= 3992882 WU # weight of block 876590
+ 6952 WU # unused, reserved weight
+ 166 WU # unused, unreserved weight
= 4000000 WU # maximum consensus block weight
This assumes that the SBI Crypto pool uses the default value for the block
weight and doesn’t set a custom value with -blockmaxweight
. Based on the
statistics posted by @ismaelsadeeq in
Analyzing Mining Pool Behavior to Address Bitcoin Core’s Double Coinbase
Reservation Issue,
it seems like the majority of pools do use the default. In the two-year
timeframe analyzed by @ismaelsadeeq, SBI
Crypto never mined a block with a weight higher than the non-coinbase
transaction weight Bitcoin Core fills by default. They did however mine a block
with exactly 0 WU of unreserved block weight left.
This indicates that use the default block weight reservation.
This explains why and how the sanctioned transaction was displaced in favor of
higher feerate transactions from SBI Crypto’s block 876590 making it unlikely
that they did filter this transaction. This is supported by the fact that SBI
Crypto did include another sanctioned transaction
1a579eb6..
related to the same sanctioned entity in the same block.
ViaBTC Block 876614 (too young?)
The transaction e26f26d4..
is reported
as missing from ViaBTC’s block 876614. It spends from a sanctioned address
related to the Chinese illicit drug producers and traders. At the time the block
was processed by the miningpool-observer tool, the transaction had only been in
my mempool for 28 seconds. Along with the sanctioned transaction, 55 other
transactions are missing due to being too “young” and range between an age of 25
seconds and an age of 87 seconds. The sanctioned transaction might not have been
propagated to ViaBTC or used in a block template yet. It was likely not filtered
by ViaBTC.
ViaBTC did mine the sanctioned transaction b3a25904..
43 blocks before and the transaction 2951b5bb..
73 blocks after block 876614.
F2Pool Block 876655 (filtered?)
F2Pool’s block 876655 is reported
to be missing the transaction the sanctioned cb35835c..
.
Along with the other transactions, this transaction is sanctioned by OFAC as it
spends from an address related to the Chinese illicit drug producers and traders.
The transaction had been in my mempool for more than 17 minutes at the time F2Pool’s block arrived. With a feerate of 2.98 sat/vByte it pays more than enough fees to be included in the block. The mempool.space block-audit agrees with this and marks the sanctioned transactions as “removed”. This indicates that F2Pool could have filtered the transaction.
F2Pool Block 876732 (filtered?)
The F2Pool block 876732 is reported
to be missing the sanctioned transaction 9522108d..
which is spent from an address related to the Chinese illicit drug producers and
traders. The transaction was in my node’s mempool for nearly 25 minutes before
the F2Pool block arrived and paid a high enough feerate to be included in the
block. The block-audit on mempool.space
marks the sanctioned transactions as “removed” as well. This indicates that F2Pool
could have filtered the transaction.
F2Pool block 876883 (filtered?)
F2Pool’s block 876883 is reported
to be missing the sanctioned transaction 41f4dfbe..
which spends from an address related to the Chinese illicit drug producers and
traders. The transaction had been in my mempool for more than 6 minutes before
the F2Pool found the block and paid a high enough feerate to be included in the
block. The mempool.space
block-audit marks the transaction as “removed”. This indicates that F2Pool could
have filtered the transaction.
F2Pool block 877061 (filtered?)
In F2Pool’s block 877061 the sanctioned transaction 1a97f4c4..
is reported
to be missing. As with the other transactions, it spends from an address related
to the Chinese illicit drug producers and traders. It had been in my node’s
mempool for close to 15 minutes. While the mempool-space block-audit labels
the transaction with “marginal fee”, it paid enough fees and would have fit into
the block. This indicates that F2Pool could have filtered the transaction.
F2Pool block 878458 (filtered?)
F2Pool’s block 878458 is reported
to be missing the sanctioned transaction 5e2c59cb..
which spends from an address related to the Chinese illicit drug producers and
traders. The transaction had been in my mempool for more than an hour. While the
mempool.space
block-audit marks the transaction as “marginal fee”, it does pay enough fees to
be included in the first half of the block. This indicates that F2Pool could have
filtered the transaction.
F2Pool block 878889 (block not full)
For F2Pool’s block 878889, the one-input and one-output transaction
6ad80e22..
is
reported
to be missing from the block. While the previously mentioned transactions were spent
from a sanctioned address, this transaction sent a near-dust amount of 600 sat
to an address related to a different Chinese national belonging to a different
Drug Trafficking Organization sanctioned by
OFAC.
The transaction was in my node’s mempool for more than 16 hours and would have fit into the F2Pool block. However, F2Pool only filled the block with only about 30% of the maximum allowed block weight. While the following F2Pool block 878894 was filled correctly again, the F2Pool blocks 878900 and 878901 were filled with less than 2.5% of the maximum block weight. As @mononautical suggested, these blocks might have been built by a node that was just restarted and didn’t have all transactions loaded into the mempool yet. This reported missing sanctioned transaction can not be used as evidence that F2Pool filters OFAC-sanctioned transactions.
Foundry USA block 878898 (displaced)
In Foundry USA’s block 878898, the sanctioned one-input and one-output transaction e415e518..
is reported
to be missing from Foundry USA’s block 878898. Similar to the transaction before,
it sends a near-dust amount of 600 sat to an address related to a Chinese Drug
Trafficking Organization.
The transaction had been in my node’s mempool for more than 10 days and pays a feerate of 1.05 sat/vByte. However, Foundry USA knew about a large 26548 vByte transaction that pays a feerate of 1.07 sat/vByte and displaced 130 lower-feerate transactions from the block. This includes the sanctioned transaction. This missing sanctioned transaction can not be used as evidence that Foundry filters OFAC-sanctioned transactions.
Conclusion
This blog post analyzes 14 blocks for which OFAC-sanctioned transactions were reported as missing. Out of these, 11 blocks were mined by F2Pool, while SBI Crypto, ViaBTC, and Foundry USA each mined one of the blocks. The missing transactions from SBI Crypto’s block 876590 and Foundry USA’s block 878898 were displaced by higher feerate transactions, and the sanctioned transaction missing from ViaBTC’s block 876614 could have been too young to be included. While F2Pool’s block 875840 is missing a potentially too young transaction and F2Pool’s block 878889 was only 30% full indicating a problem with their block template, the other 9 analyzed F2Pool blocks indicate that F2Pool might be filtering OFAC-sanctioned transactions again.
After I identified four F2Pool blocks with missing sanctioned transactions in my previous analysis (November 2023), the founder of F2Pool confirmed that F2Pool was running a “tx filtering patch”. This patch was disabled for a while. While all pools with more than 5% hashrate mined an OFAC-sanctioned transaction in the past weeks 2, F2Pool mined their most recent OFAC-sanctioned transaction over seven months ago on 2024-06-08.
Note that a mining pool operator has the power and right to choose which transactions to include in their blocks and which transactions to exclude. Personally, I want to know when and if pools filter transactions - not because I agree or disagree with US OFAC sanctions, but because I want to know how well Bitcoin’s censorship resistance holds up against pools trying to filter transactions. As all of the missing sanctioned transactions were picked up in the following blocks, I assume Bitcoin’s censorship resistance has been holding up quite well for now. We’ll see if, for example, stronger mining pool regulations being enforced in the future, will change this. Until then, let’s hope mining protocols like StratumV2 and DATUM, which allow miners to build and use their own templates, gain more adoption.
Not your templates, not your blocks.
See the last two transactions on the bottom of page 86 of block 876590 ↩︎
I purposefully leave this part as an exercise for the reader. Similar to how my miningpool-observer tool does not display when a pool mines an OFAC-sanctioned transaction, I don’t want pictures or tables from this blog post to be a potential source of information for regulators. Nonetheless, the data is publicly visible on the blockchain. If a regulator wants to find out, they will. If a reader wants to verify my claims, either take this as a small exercise (here is a list of OFAC-sanctioned Bitcoin addresses to start with) or feel free to reach out and I can share the methodology and potentially some scripts. ↩︎