mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 18:03:12 +01:00
Use segwit tx sizes
This commit is contained in:
parent
7a58bfbafa
commit
29f23fe50c
@ -41,9 +41,24 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
*/
|
||||
@Slf4j
|
||||
public class TxFeeEstimationService {
|
||||
public static int TYPICAL_TX_WITH_1_INPUT_SIZE = 260;
|
||||
private static int DEPOSIT_TX_SIZE = 320;
|
||||
private static int PAYOUT_TX_SIZE = 380;
|
||||
|
||||
// Size/vsize of typical trade txs
|
||||
// Real txs size/vsize may vary in 1 or 2 bytes from the estimated values.
|
||||
// Values calculated with https://gist.github.com/oscarguindzberg/3d1349cb65d9fd9af9de0feaa3fd27ac
|
||||
// legacy fee tx with 1 input, maker/taker fee paid in btc size/vsize = 258
|
||||
// legacy deposit tx without change size/vsize = 381
|
||||
// legacy deposit tx with change size/vsize = 414
|
||||
// legacy payout tx size/vsize = 337
|
||||
// legacy delayed payout tx size/vsize = 302
|
||||
// segwit fee tx with 1 input, maker/taker fee paid in btc vsize = 173
|
||||
// segwit deposit tx without change vsize = 232
|
||||
// segwit deposit tx with change vsize = 263
|
||||
// segwit payout tx vsize = 169
|
||||
// segwit delayed payout tx vsize = 139
|
||||
public static int TYPICAL_TX_WITH_1_INPUT_SIZE = 175;
|
||||
private static int DEPOSIT_TX_SIZE = 233;
|
||||
private static int PAYOUT_TX_SIZE = 169;
|
||||
|
||||
private static int BSQ_INPUT_INCREASE = 150;
|
||||
private static int MAX_ITERATIONS = 10;
|
||||
|
||||
@ -87,14 +102,14 @@ public class TxFeeEstimationService {
|
||||
BtcWalletService btcWalletService,
|
||||
Preferences preferences) {
|
||||
Coin txFeePerByte = feeService.getTxFeePerByte();
|
||||
// We start with min taker fee size of 260
|
||||
// We start with min taker fee size of 175
|
||||
int estimatedTxSize = TYPICAL_TX_WITH_1_INPUT_SIZE;
|
||||
try {
|
||||
estimatedTxSize = getEstimatedTxSize(List.of(tradeFee, amount), estimatedTxSize, txFeePerByte, btcWalletService);
|
||||
} catch (InsufficientMoneyException e) {
|
||||
if (isTaker) {
|
||||
// if we cannot do the estimation we use the payout tx size
|
||||
estimatedTxSize = PAYOUT_TX_SIZE;
|
||||
// if we cannot do the estimation we use the deposit tx size
|
||||
estimatedTxSize = DEPOSIT_TX_SIZE;
|
||||
}
|
||||
log.info("We cannot do the fee estimation because there are not enough funds in the wallet. This is expected " +
|
||||
"if the user pays from an external wallet. In that case we use an estimated tx size of {} bytes.", estimatedTxSize);
|
||||
@ -109,13 +124,13 @@ public class TxFeeEstimationService {
|
||||
Coin txFee;
|
||||
int size;
|
||||
if (isTaker) {
|
||||
int averageSize = (estimatedTxSize + DEPOSIT_TX_SIZE) / 2; // deposit tx has about 320 bytes
|
||||
// We use at least the size of the payout tx to not underpay at payout.
|
||||
size = Math.max(PAYOUT_TX_SIZE, averageSize);
|
||||
int averageSize = (estimatedTxSize + DEPOSIT_TX_SIZE) / 2; // deposit tx has about 233 bytes
|
||||
// We use at least the size of the deposit tx to not underpay it.
|
||||
size = Math.max(DEPOSIT_TX_SIZE, averageSize);
|
||||
txFee = txFeePerByte.multiply(size);
|
||||
log.info("Fee estimation resulted in a tx size of {} bytes.\n" +
|
||||
"We use an average between the taker fee tx and the deposit tx (320 bytes) which results in {} bytes.\n" +
|
||||
"The payout tx has 380 bytes, we use that as our min value. Size for fee calculation is {} bytes.\n" +
|
||||
"We use an average between the taker fee tx and the deposit tx (233 bytes) which results in {} bytes.\n" +
|
||||
"The deposit tx has 233 bytes, we use that as our min value. Size for fee calculation is {} bytes.\n" +
|
||||
"The tx fee of {} Sat", estimatedTxSize, averageSize, size, txFee.value);
|
||||
} else {
|
||||
size = estimatedTxSize;
|
||||
@ -130,7 +145,7 @@ public class TxFeeEstimationService {
|
||||
FeeService feeService,
|
||||
BtcWalletService btcWalletService) {
|
||||
Coin txFeePerByte = feeService.getTxFeePerByte();
|
||||
// We start with min taker fee size of 260
|
||||
// We start with min taker fee size of 175
|
||||
int estimatedTxSize = TYPICAL_TX_WITH_1_INPUT_SIZE;
|
||||
try {
|
||||
estimatedTxSize = getEstimatedTxSize(List.of(amount), estimatedTxSize, txFeePerByte, btcWalletService);
|
||||
@ -145,7 +160,7 @@ public class TxFeeEstimationService {
|
||||
return new Tuple2<>(txFee, estimatedTxSize);
|
||||
}
|
||||
|
||||
// We start with the initialEstimatedTxSize for a tx with 1 input (260) bytes and get from BitcoinJ a tx back which
|
||||
// We start with the initialEstimatedTxSize for a tx with 1 input (175) bytes and get from BitcoinJ a tx back which
|
||||
// contains the required inputs to fund that tx (outputs + miner fee). The miner fee in that case is based on
|
||||
// the assumption that we only need 1 input. Once we receive back the real tx size from the tx BitcoinJ has created
|
||||
// with the required inputs we compare if the size is not more then 20% different to our assumed tx size. If we are inside
|
||||
|
@ -45,14 +45,14 @@ public class TxFeeEstimationServiceTest {
|
||||
int realTxSize;
|
||||
Coin txFee;
|
||||
|
||||
initialEstimatedTxSize = 260;
|
||||
initialEstimatedTxSize = 175;
|
||||
txFeePerByte = Coin.valueOf(10);
|
||||
realTxSize = 260;
|
||||
realTxSize = 175;
|
||||
|
||||
txFee = txFeePerByte.multiply(initialEstimatedTxSize);
|
||||
when(btcWalletService.getEstimatedFeeTxSize(outputValues, txFee)).thenReturn(realTxSize);
|
||||
result = TxFeeEstimationService.getEstimatedTxSize(outputValues, initialEstimatedTxSize, txFeePerByte, btcWalletService);
|
||||
assertEquals(260, result);
|
||||
assertEquals(175, result);
|
||||
}
|
||||
|
||||
// FIXME @Bernard could you have a look?
|
||||
@ -67,16 +67,16 @@ public class TxFeeEstimationServiceTest {
|
||||
int realTxSize;
|
||||
Coin txFee;
|
||||
|
||||
initialEstimatedTxSize = 260;
|
||||
initialEstimatedTxSize = 175;
|
||||
txFeePerByte = Coin.valueOf(10);
|
||||
realTxSize = 2600;
|
||||
realTxSize = 1750;
|
||||
|
||||
txFee = txFeePerByte.multiply(initialEstimatedTxSize);
|
||||
when(btcWalletService.getEstimatedFeeTxSize(outputValues, txFee)).thenReturn(realTxSize);
|
||||
|
||||
// repeated calls to getEstimatedFeeTxSize do not work (returns 0 at second call in loop which cause test to fail)
|
||||
result = TxFeeEstimationService.getEstimatedTxSize(outputValues, initialEstimatedTxSize, txFeePerByte, btcWalletService);
|
||||
assertEquals(2600, result);
|
||||
assertEquals(1750, result);
|
||||
}
|
||||
|
||||
// FIXME @Bernard could you have a look?
|
||||
@ -91,14 +91,14 @@ public class TxFeeEstimationServiceTest {
|
||||
int realTxSize;
|
||||
Coin txFee;
|
||||
|
||||
initialEstimatedTxSize = 2600;
|
||||
initialEstimatedTxSize = 1750;
|
||||
txFeePerByte = Coin.valueOf(10);
|
||||
realTxSize = 260;
|
||||
realTxSize = 175;
|
||||
|
||||
txFee = txFeePerByte.multiply(initialEstimatedTxSize);
|
||||
when(btcWalletService.getEstimatedFeeTxSize(outputValues, txFee)).thenReturn(realTxSize);
|
||||
result = TxFeeEstimationService.getEstimatedTxSize(outputValues, initialEstimatedTxSize, txFeePerByte, btcWalletService);
|
||||
assertEquals(260, result);
|
||||
assertEquals(175, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -109,8 +109,8 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||
private PaymentAccount paymentAccount;
|
||||
private boolean isTabSelected;
|
||||
Price tradePrice;
|
||||
// 260 kb is size of typical trade fee tx with 1 input but trade tx (deposit and payout) are larger so we adjust to 320
|
||||
private int feeTxSize = 320;
|
||||
// Use an average of a typical trade fee tx with 1 input, deposit tx and payout tx.
|
||||
private int feeTxSize = 192; // (175+233+169)/3
|
||||
private boolean freezeFee;
|
||||
private Coin txFeePerByteFromFeeService;
|
||||
|
||||
@ -213,13 +213,13 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||
// multiple batch-signed payout tx with different fees might be an option but RBF is not supported yet in BitcoinJ
|
||||
// and batched txs would add more complexity to the trade protocol.
|
||||
|
||||
// A typical trade fee tx has about 260 bytes (if one input). The trade txs has about 336-414 bytes.
|
||||
// We use 320 as a average value.
|
||||
// A typical trade fee tx has about 175 bytes (if one input). The trade txs has about 169-263 bytes.
|
||||
// We use 192 as average value.
|
||||
|
||||
// trade fee tx: 260 bytes (1 input)
|
||||
// deposit tx: 336 bytes (1 MS output+ OP_RETURN) - 414 bytes (1 MS output + OP_RETURN + change in case of smaller trade amount)
|
||||
// payout tx: 371 bytes
|
||||
// disputed payout tx: 408 bytes
|
||||
// trade fee tx: 175 bytes (1 input)
|
||||
// deposit tx: 233 bytes (1 MS output+ OP_RETURN) - 263 bytes (1 MS output + OP_RETURN + change in case of smaller trade amount)
|
||||
// payout tx: 169 bytes
|
||||
// disputed payout tx: 139 bytes
|
||||
|
||||
// Set the default values (in rare cases if the fee request was not done yet we get the hard coded default values)
|
||||
// But the "take offer" happens usually after that so we should have already the value from the estimation service.
|
||||
@ -351,22 +351,22 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||
// there more deterministic.
|
||||
// The trade fee tx can be in the worst case very large if there are many inputs so if we take that tx alone
|
||||
// for the fee estimation we would overpay a lot.
|
||||
// On the other side if we have the best case of a 1 input tx fee tx then it is only 260 bytes but the
|
||||
// other 2 txs are larger (320 and 380 bytes) and would get a lower fee/byte as intended.
|
||||
// On the other side if we have the best case of a 1 input tx fee tx then it is only 175 bytes but the
|
||||
// other 2 txs are different (233 and 169 bytes) and may get a lower fee/byte as intended.
|
||||
// We apply following model to not overpay too much but be on the safe side as well.
|
||||
// We sum the taker fee tx and the deposit tx together as it can be assumed that both be in the same block and
|
||||
// as they are dependent txs the miner will pick both if the fee in total is good enough.
|
||||
// We make sure that the fee is sufficient to meet our intended fee/byte for the larger payout tx with 380 bytes.
|
||||
// We make sure that the fee is sufficient to meet our intended fee/byte for the larger deposit tx with 233 bytes.
|
||||
Tuple2<Coin, Integer> estimatedFeeAndTxSize = txFeeEstimationService.getEstimatedFeeAndTxSizeForTaker(fundsNeededForTrade,
|
||||
getTakerFee());
|
||||
txFeeFromFeeService = estimatedFeeAndTxSize.first;
|
||||
feeTxSize = estimatedFeeAndTxSize.second;
|
||||
} else {
|
||||
feeTxSize = 380;
|
||||
feeTxSize = 233;
|
||||
txFeeFromFeeService = txFeePerByteFromFeeService.multiply(feeTxSize);
|
||||
log.info("We cannot do the fee estimation because there are no funds in the wallet.\nThis is expected " +
|
||||
"if the user has not funded their wallet yet.\n" +
|
||||
"In that case we use an estimated tx size of 380 bytes.\n" +
|
||||
"In that case we use an estimated tx size of 233 bytes.\n" +
|
||||
"txFee based on estimated size of {} bytes. feeTxSize = {} bytes. Actual tx size = {} bytes. TxFee is {} ({} sat/byte)",
|
||||
feeTxSize, feeTxSize, txSize, txFeeFromFeeService.toFriendlyString(), feeService.getTxFeePerByte());
|
||||
}
|
||||
@ -531,7 +531,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||
// With that we avoid that we overpay in case that the trade fee has many inputs and we would apply that fee for the
|
||||
// other 2 txs as well. We still might overpay a bit for the payout tx.
|
||||
private int getAverageSize(int txSize) {
|
||||
return (txSize + 320) / 2;
|
||||
return (txSize + 233) / 2;
|
||||
}
|
||||
|
||||
private Coin getTxFeeBySize(int sizeInBytes) {
|
||||
@ -606,7 +606,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||
// Unfortunately we cannot change that to the correct fees as it would break backward compatibility
|
||||
// We still might find a way with offer version or app version checks so lets keep that commented out
|
||||
// code as that shows how it should be.
|
||||
return txFeeFromFeeService; //feeService.getTxFee(320);
|
||||
return txFeeFromFeeService; //feeService.getTxFee(233);
|
||||
}
|
||||
|
||||
private Coin getTxFeeForPayoutTx() {
|
||||
@ -614,7 +614,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||
// Unfortunately we cannot change that to the correct fees as it would break backward compatibility
|
||||
// We still might find a way with offer version or app version checks so lets keep that commented out
|
||||
// code as that shows how it should be.
|
||||
return txFeeFromFeeService; //feeService.getTxFee(380);
|
||||
return txFeeFromFeeService; //feeService.getTxFee(169);
|
||||
}
|
||||
|
||||
public AddressEntry getAddressEntry() {
|
||||
|
Loading…
Reference in New Issue
Block a user