Hi everyone !
I would like to give more details on an issue that we found on the ckBTC minter and that was raised here.
TL;DR
3 transactions ckBTC → BTC (withdrawals) are currently stuck and this is due to 2 bugs:
- An extremely low fee per vbyte was chosen by the minter for those transactions, which prevented them from being mined.
- There is a deterministic panic occurring in the minter when it tries to resubmit those transactions, which means that those transactions are currently stuck.
Note that the funds held by the minter are not at risk and as far as we can tell, excepted for those 3 transactions, the minter works as expected.
Details
Impacted Transactions
All 3 stuck transactions involve withdrawals that happen around 2025.06.21 01:00 - 01:35 UTC
23e46d53929d513cb1dc1b0fa63f9b142f2676958e6e6f0e45653949def954b2
db9e317d38803b83115959ac857e2005855ce572d446351034080864aa8edeb5
- Request 1
- Burn index:
2626426
- Received at
1750468574497835843
== 21.06.2025 01:16:14.497
- Burn index:
- Request 2
- Burn index:
2626428
- Received at
1750468707343288824
== 21.06.2025 01:18:27.343
- Burn index:
- Request 3
- Burn index:
2626430
- Received at
1750468731927248030
== 21.06.2025 01:18:51.927
- Burn index:
- Request 4
- Burn index:
2626432
- Received at
1750468808041112499
== 21.06.2025 01:20:08.041
- Burn index:
- Request 1
422f3115c4f865536f92e94d22cb7b2795b0482e517f7c46561e2234cf03e603
- Request 1
- Burn index:
2626488
- Received at
1750469582867790653
== 21.06.2025 01:33:02.867
- Burn index:
- Request 1
Analysis
The 3 transactions are stuck due the following two bugs.
Bug 1: Low fee per vbyte
An estimated median fee of 142 millistatoshis per vbyte for Bitcoin Mainnet was used during approximatively 30min, from 2025.06.21 01:09:50 UTC until 2025.06.21 01:39:00 UTC.
However, the median fee around that time should have been in the order of 2000 millistatoshis per vbyte. This extremely low fee was used for the 3 stuck transactions, which was way too low to be picked up by miners and explain why those three transactions were rapidly evicted from the mempool.
Solution
We currently don’t have a satisfying explanation for how this low median fee was computed and are also investigating the bitcoin canister. As a stop-gap solution, we will ensure that the median fee computed by the minter is always above some reasonable minimum value.
Bug 2: Panic when resubmitting
The ckBTC minter tries daily to resubmit transactions that were not finalized. However, currently a panic occurring when building the resubmit transaction prevents the minter from processing further those transactions. This is due to the UTXO selection rule that differs between creating a transaction for the first time or resubmitting it.
In more details, let us look at 422f3115c4f865536f92e94d22cb7b2795b0482e517f7c46561e2234cf03e603
. The submitted BTC transaction (can be retrieved from get_events
) is
SubmittedBtcTransaction { requests: [ RetrieveBtcRequest { amount: 157990, address: P2pkh([51, 226, 108, 181, 64, 54, 173, 71, 246, 73, 72, 212, 123, 28, 139, 184, 38, 14, 245, 82]), block_index: 2626488, received_at: 1750469582867790653, kyt_provider: None, reimbursement_account: Some(Account { owner: Principal { len: 10, bytes: [0, 0, 0, 0, 1, 32, 205, 190, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, subaccount: None }) } ], txid: Txid([3, 230, 3, 207, 52, 34, 30, 86, 70, 124, 127, 81, 46, 72, 176, 149, 39, 123, 203, 34, 77, 233, 146, 111, 83, 101, 248, 196, 21, 49, 47, 66]), used_utxos: [ Utxo { outpoint: OutPoint { txid: Txid([141, 123, 45, 7, 79, 177, 177, 145, 143, 77, 206, 60, 201, 97, 232, 50, 79, 38, 200, 87, 91, 46, 70, 135, 80, 3, 217, 21, 39, 160, 255, 148]), vout: 0 }, value: 158000, height: 901322 }, Utxo { outpoint: OutPoint { txid: Txid([117, 219, 203, 2, 46, 215, 129, 132, 15, 150, 208, 33, 176, 123, 214, 116, 231, 210, 30, 81, 179, 57, 81, 203, 18, 14, 34, 119, 120, 102, 208, 89]), vout: 0 }, value: 50000, height: 902114 }], submitted_at: 1750469599678571705, change_output: Some(ChangeOutput { vout: 1, value: 50336 }), fee_per_vbyte: Some(142) }
The important things there are:
- The target is
157_990
- It uses 2 UTXOs
utxo_141
(using the 1st byte of Txid as an identifier) with value158_000
utxo_117
with value50_000
Notice that utxo_141.value >= target
so that the second UTXO is actually not strictly necessary. The first time the transaction was built, greedy
returned utxo_141
only, and utxo_117
was added here to aim at reducing the number of UTXOs managed by the minter (because available_utxos
corresponds to all UTXOs available to the minter when building a transaction for the first time, which is around 47k, well above the threshold of UTXOS_COUNT_THRESHOLD
which has value 1000).
However, when the minter resubmits the transaction and again calls utxos_selection
, this time available_utxos == [utxo_141, utxo_117]
, so that greedy
still returns only utxo_141
and since we only have 2 UTXOs, which is below UTXOS_COUNT_THRESHOLD
(1000), the minter does not try to add additional UTXOs. This results in the panic here because utxo_117
was not used.
Solution
The solution is easy: since the resubmit transaction should use the same UTXOs as the original transaction, there is actually no need to go through utxos_selection
when resubmitting a transaction.