4

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.

Darius
  • 779
  • 4
  • 16

1 Answers1

3

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 vbytes for user interface but also for mempool logic and the DEFAULT_MIN_RELAY_TX_FEE constant is set in vbytes :

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 250sat 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.

Antoine Poinsot
  • 5,881
  • 2
  • 11
  • 28
  • 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! – Darius May 14 '20 at 23:21
  • 1
    The round down is actually a *consequence* of the round up but yes that's the point. You are welcome :) – Antoine Poinsot May 15 '20 at 09:51