Why is a minRelayTxFee of 1000 sat/kb not equivalent to 250 sat/kw?

Naively, we would expect a minRelayTxFee of 1000 sat/kb to correspond to 250 sat/kw. However, apparently this is not so according to this issue (ACINQ), which also references this issue (c-lightning).

``````  /**
* why 253 and not 250 since feerate-per-kw is feerate-per-kb / 250 and the minimum relay fee rate is 1000 satoshi/Kb ?
*
* because bitcoin core uses neither the actual tx size in bytes or the tx weight to check fees, but a "virtual size"
* which is (3 * weight) / 4 ...
* so we want :
* fee > 1000 * virtual size
* feerate-per-kw * weight > 1000 * (3 * weight / 4)
* feerate_per-kw > 250 + 3000 / (4 * weight)
* with a conservative minimum weight of 400, we get a minimum feerate_per-kw of 253
*
* see https://github.com/ElementsProject/lightning/pull/1251
**/
val MinimumFeeratePerKw = 253
``````

I thought vbytes were equal to weight units divided by 4 and rounded up, so I don't understand where `(3 * weight) / 4` comes from.

• I think this is a typo. It's `(3 + weight) / 4`, not `(3 * weight) / 4`. Commented May 14, 2020 at 18:41

I thought vbytes were equal to weight units divided by 4

It is [1], but rounded up to the next integer, so the implementation in `bitcoind` is :

``````int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost, unsigned int bytes_per_sigop)
{
return (std::max(nWeight, nSigOpCost * bytes_per_sigop) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
}
``````

(Where `WITNESS_SCALE_FACTOR = 4`).

In addition, `bitcoind` not only uses `vbyte`s for user interface but also for mempool logic and the `DEFAULT_MIN_RELAY_TX_FEE` constant is set in `vbyte`s :

``````static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
``````

In order to check it for standardness `bitcoind` will compute your transaction size as rounded up (if not a multiple of `4`), and will record your transaction feerate as rounded down.
It will then compare this to the minimum relay feerate (`1000`) : a feerate of `250`sat per KW will pass only if the size of your transaction (in weight units) is a multiple of `4`.

It might be clearer with code, here is a Python function which illustrates this behaviour :

``````>>> def bitcoind_fun(tx_weight, feerate_perkw):
...     fees = tx_weight * feerate_perkw // 1000
...     print("Your transaction will pay {} sats of fees, is {} WU large (a feerate of {}sat/Kw)".format(fees, tx_weight, feerate_perkw))
...     tx_vbytes_for_bitcoind = (tx_weight + 3) // 4
...     tx_feerate_vbytes_for_bitcoind = fees * 1000 // tx_vbytes_for_bitcoind
...     print("bitcoind reads your transaction as paying {} sats of fees for a transaction of {} vbytes, so a {}sat/perKvb feerate".format(fees, tx_vbytes_for_bitcoind, tx_feerate_vbytes_for_bitcoind))
``````

Which if you run with a `tx_weight` which is a multiple of `4` will pass, otherwise it won't :

``````>>> bitcoind_fun(1600, 250)
Your transaction will pay 400 sats of fees, is 1600 WU large (a feerate of 250sat/Kw)
bitcoind reads your transaction as paying 400 sats of fees for a transaction of 400 vbytes, so a 1000sat/perKvb feerate
>>> bitcoind_fun(1601, 250)
Your transaction will pay 400 sats of fees, is 1601 WU large (a feerate of 250sat/Kw)
bitcoind reads your transaction as paying 400 sats of fees for a transaction of 401 vbytes, so a 997sat/perKvb feerate
>>> bitcoind_fun(1602, 250)
Your transaction will pay 400 sats of fees, is 1602 WU large (a feerate of 250sat/Kw)
bitcoind reads your transaction as paying 400 sats of fees for a transaction of 401 vbytes, so a 997sat/perKvb feerate
``````

So we use 253 as the feerate floor to always be safe :

``````>>> bitcoind_fun(1600, 253)
Your transaction will pay 404 sats of fees, is 1600 WU large (a feerate of 253sat/Kw)
bitcoind reads your transaction as paying 404 sats of fees for a transaction of 400 vbytes, so a 1010sat/perKvb feerate
>>> bitcoind_fun(1601, 253)
Your transaction will pay 405 sats of fees, is 1601 WU large (a feerate of 253sat/Kw)
bitcoind reads your transaction as paying 405 sats of fees for a transaction of 401 vbytes, so a 1009sat/perKvb feerate
>>> bitcoind_fun(1602, 253)
Your transaction will pay 405 sats of fees, is 1602 WU large (a feerate of 253sat/Kw)
bitcoind reads your transaction as paying 405 sats of fees for a transaction of 401 vbytes, so a 1009sat/perKvb feerate
``````

By the way the comment in the code you put is wrong : it's not `(3 * weight) / 4 ` but `(3 + weight) / 4`.

[1] Excluding sig OPs limits that artificially increases the virtual size to further prevent DOS.

PS: In the same vein, see https://github.com/bitcoin/bitcoin/issues/13283.

• I see, so the core issue is rounding up when calculating the tx size, and rounding down when calculating the fee rate. That is very tricky. Thank you for such a clear explanation! 🙏 🙏 Commented May 14, 2020 at 23:21
• The round down is actually a consequence of the round up but yes that's the point. You are welcome :) Commented May 15, 2020 at 9:51