remove btc fee service

This commit is contained in:
woodser 2022-12-09 13:20:23 +00:00
parent 3314eac881
commit 31dfdd7710
49 changed files with 66 additions and 1797 deletions

View File

@ -1,93 +0,0 @@
package bisq.apitest.method.wallet;
import bisq.core.api.model.TxFeeRateInfo;
import io.grpc.StatusRuntimeException;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.TestMethodOrder;
import static bisq.apitest.Scaffold.BitcoinCoreApp.bitcoind;
import static bisq.apitest.config.HavenoAppConfig.alicedaemon;
import static bisq.apitest.config.HavenoAppConfig.seednode;
import static java.lang.String.format;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import bisq.apitest.method.MethodTest;
@Disabled
@Slf4j
@TestMethodOrder(OrderAnnotation.class)
public class BtcTxFeeRateTest extends MethodTest {
@BeforeAll
public static void setUp() {
startSupportingApps(false,
true,
bitcoind,
seednode,
alicedaemon);
}
@Test
@Order(1)
public void testGetTxFeeRate(final TestInfo testInfo) {
var txFeeRateInfo = TxFeeRateInfo.fromProto(aliceClient.getTxFeeRate());
log.debug("{} -> Fee rate with no preference: {}", testName(testInfo), txFeeRateInfo);
assertFalse(txFeeRateInfo.isUseCustomTxFeeRate());
assertTrue(txFeeRateInfo.getFeeServiceRate() > 0);
}
@Test
@Order(2)
public void testSetInvalidTxFeeRateShouldThrowException(final TestInfo testInfo) {
var currentTxFeeRateInfo = TxFeeRateInfo.fromProto(aliceClient.getTxFeeRate());
Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.setTxFeeRate(1));
String expectedExceptionMessage =
format("INVALID_ARGUMENT: tx fee rate preference must be >= %d sats/byte",
currentTxFeeRateInfo.getMinFeeServiceRate());
assertEquals(expectedExceptionMessage, exception.getMessage());
}
@Test
@Order(3)
public void testSetValidTxFeeRate(final TestInfo testInfo) {
var currentTxFeeRateInfo = TxFeeRateInfo.fromProto(aliceClient.getTxFeeRate());
var customFeeRate = currentTxFeeRateInfo.getMinFeeServiceRate() + 5;
var txFeeRateInfo = TxFeeRateInfo.fromProto(aliceClient.setTxFeeRate(customFeeRate));
log.debug("{} -> Fee rates with custom preference: {}", testName(testInfo), txFeeRateInfo);
assertTrue(txFeeRateInfo.isUseCustomTxFeeRate());
assertEquals(customFeeRate, txFeeRateInfo.getCustomTxFeeRate());
assertTrue(txFeeRateInfo.getFeeServiceRate() > 0);
}
@Test
@Order(4)
public void testUnsetTxFeeRate(final TestInfo testInfo) {
var txFeeRateInfo = TxFeeRateInfo.fromProto(aliceClient.unsetTxFeeRate());
log.debug("{} -> Fee rate with no preference: {}", testName(testInfo), txFeeRateInfo);
assertFalse(txFeeRateInfo.isUseCustomTxFeeRate());
assertTrue(txFeeRateInfo.getFeeServiceRate() > 0);
}
@AfterAll
public static void tearDown() {
tearDownScaffold();
}
}

View File

@ -1,7 +1,6 @@
package bisq.apitest.method.wallet;
import bisq.proto.grpc.BtcBalanceInfo;
import bisq.proto.grpc.TxInfo;
import lombok.extern.slf4j.Slf4j;
@ -96,49 +95,6 @@ public class BtcWalletTest extends MethodTest {
new TableBuilder(BTC_BALANCE_TBL, btcBalanceInfo).build());
}
@Test
@Order(3)
public void testAliceSendBTCToBob(TestInfo testInfo) {
String bobsBtcAddress = bobClient.getUnusedBtcAddress();
log.debug("Sending 5.5 BTC From Alice to Bob @ {}", bobsBtcAddress);
TxInfo txInfo = aliceClient.sendBtc(bobsBtcAddress,
"5.50",
"100",
TX_MEMO);
assertTrue(txInfo.getIsPending());
// Note that the memo is not set on the tx yet.
assertTrue(txInfo.getMemo().isEmpty());
genBtcBlocksThenWait(1, 1000);
// Fetch the tx and check for confirmation and memo.
txInfo = aliceClient.getTransaction(txInfo.getTxId());
assertFalse(txInfo.getIsPending());
assertEquals(TX_MEMO, txInfo.getMemo());
BtcBalanceInfo alicesBalances = aliceClient.getBtcBalances();
log.debug("{} Alice's BTC Balances:\n{}",
testName(testInfo),
new TableBuilder(BTC_BALANCE_TBL, alicesBalances).build());
bisq.core.api.model.BtcBalanceInfo alicesExpectedBalances =
bisq.core.api.model.BtcBalanceInfo.valueOf(700000000,
0,
700000000,
0);
verifyBtcBalances(alicesExpectedBalances, alicesBalances);
BtcBalanceInfo bobsBalances = bobClient.getBtcBalances();
log.debug("{} Bob's BTC Balances:\n{}",
testName(testInfo),
new TableBuilder(BTC_BALANCE_TBL, bobsBalances).build());
// The sendbtc tx weight and size randomly varies between two distinct values
// (876 wu, 219 bytes, OR 880 wu, 220 bytes) from test run to test run, hence
// the assertion of an available balance range [1549978000, 1549978100].
assertTrue(bobsBalances.getAvailableBalance() >= 1549978000);
assertTrue(bobsBalances.getAvailableBalance() <= 1549978100);
}
@AfterAll
public static void tearDown() {
tearDownScaffold();

View File

@ -36,7 +36,6 @@ import static bisq.apitest.config.HavenoAppConfig.seednode;
import bisq.apitest.method.MethodTest;
import bisq.apitest.method.wallet.BtcTxFeeRateTest;
import bisq.apitest.method.wallet.BtcWalletTest;
import bisq.apitest.method.wallet.WalletProtectionTest;
@ -66,7 +65,6 @@ public class WalletTest extends MethodTest {
btcWalletTest.testInitialBtcBalances(testInfo);
btcWalletTest.testFundAlicesBtcWallet(testInfo);
btcWalletTest.testAliceSendBTCToBob(testInfo);
}
@Test
@ -86,17 +84,6 @@ public class WalletTest extends MethodTest {
walletProtectionTest.testRemoveNewWalletPassword();
}
@Test
@Order(4)
public void testTxFeeRateMethods(final TestInfo testInfo) {
BtcTxFeeRateTest test = new BtcTxFeeRateTest();
test.testGetTxFeeRate(testInfo);
test.testSetInvalidTxFeeRateShouldThrowException(testInfo);
test.testSetValidTxFeeRate(testInfo);
test.testUnsetTxFeeRate(testInfo);
}
@AfterAll
public static void tearDown() {
tearDownScaffold();

View File

@ -41,7 +41,6 @@ import java.util.List;
import lombok.extern.slf4j.Slf4j;
import static bisq.cli.CurrencyFormat.formatInternalFiatPrice;
import static bisq.cli.CurrencyFormat.formatTxFeeRateInfo;
import static bisq.cli.CurrencyFormat.toSatoshis;
import static bisq.cli.Method.*;
import static bisq.cli.opts.OptLabel.*;
@ -228,51 +227,7 @@ public class CliMain {
var memo = opts.getMemo();
var txInfo = client.sendBtc(address, amount, txFeeRate, memo);
out.printf("%s btc sent to %s in tx %s%n",
amount,
address,
txInfo.getTxId());
return;
}
case gettxfeerate: {
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
out.println(client.getMethodHelp(method));
return;
}
var txFeeRate = client.getTxFeeRate();
out.println(formatTxFeeRateInfo(txFeeRate));
return;
}
case settxfeerate: {
var opts = new SetTxFeeRateOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(client.getMethodHelp(method));
return;
}
var txFeeRate = client.setTxFeeRate(toLong(opts.getFeeRate()));
out.println(formatTxFeeRateInfo(txFeeRate));
return;
}
case unsettxfeerate: {
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
out.println(client.getMethodHelp(method));
return;
}
var txFeeRate = client.unsetTxFeeRate();
out.println(formatTxFeeRateInfo(txFeeRate));
return;
}
case gettransaction: {
var opts = new GetTransactionOptionParser(args).parse();
if (opts.isForHelp()) {
out.println(client.getMethodHelp(method));
return;
}
var txId = opts.getTxId();
var tx = client.getTransaction(txId);
new TableBuilder(TRANSACTION_TBL, tx).build().print(out);
return;
throw new RuntimeException("Send BTC not implemented");
}
case createoffer: {
var opts = new CreateOfferOptionParser(args).parse();

View File

@ -17,8 +17,6 @@
package bisq.cli;
import bisq.proto.grpc.TxFeeRateInfo;
import com.google.common.annotations.VisibleForTesting;
import java.text.DecimalFormat;
@ -76,18 +74,6 @@ public class CurrencyFormat {
return BSQ_FORMAT.format(new BigDecimal(sats).divide(BSQ_SATOSHI_DIVISOR));
}
public static String formatTxFeeRateInfo(TxFeeRateInfo txFeeRateInfo) {
if (txFeeRateInfo.getUseCustomTxFeeRate())
return format("custom tx fee rate: %s sats/byte, network rate: %s sats/byte, min network rate: %s sats/byte",
formatFeeSatoshis(txFeeRateInfo.getCustomTxFeeRate()),
formatFeeSatoshis(txFeeRateInfo.getFeeServiceRate()),
formatFeeSatoshis(txFeeRateInfo.getMinFeeServiceRate()));
else
return format("tx fee rate: %s sats/byte, min tx fee rate: %s sats/byte",
formatFeeSatoshis(txFeeRateInfo.getFeeServiceRate()),
formatFeeSatoshis(txFeeRateInfo.getMinFeeServiceRate()));
}
public static String formatInternalFiatPrice(BigDecimal price) {
INTERNAL_FIAT_DECIMAL_FORMAT.setMinimumFractionDigits(4);
INTERNAL_FIAT_DECIMAL_FORMAT.setMaximumFractionDigits(4);

View File

@ -27,8 +27,6 @@ import bisq.proto.grpc.OfferInfo;
import bisq.proto.grpc.RegisterDisputeAgentRequest;
import bisq.proto.grpc.StopRequest;
import bisq.proto.grpc.TradeInfo;
import bisq.proto.grpc.TxFeeRateInfo;
import bisq.proto.grpc.TxInfo;
import protobuf.PaymentAccount;
import protobuf.PaymentAccountForm;
@ -98,26 +96,6 @@ public final class GrpcClient {
return walletsServiceRequest.getUnusedBtcAddress();
}
public TxInfo sendBtc(String address, String amount, String txFeeRate, String memo) {
return walletsServiceRequest.sendBtc(address, amount, txFeeRate, memo);
}
public TxFeeRateInfo getTxFeeRate() {
return walletsServiceRequest.getTxFeeRate();
}
public TxFeeRateInfo setTxFeeRate(long txFeeRate) {
return walletsServiceRequest.setTxFeeRate(txFeeRate);
}
public TxFeeRateInfo unsetTxFeeRate() {
return walletsServiceRequest.unsetTxFeeRate();
}
public TxInfo getTransaction(String txId) {
return walletsServiceRequest.getTransaction(txId);
}
public OfferInfo createFixedPricedOffer(String direction,
String currencyCode,
long amount,

View File

@ -17,43 +17,10 @@
package bisq.cli;
import bisq.proto.grpc.TxInfo;
import com.google.common.annotations.VisibleForTesting;
import static bisq.cli.ColumnHeaderConstants.*;
import static bisq.cli.CurrencyFormat.formatSatoshis;
import static com.google.common.base.Strings.padEnd;
@VisibleForTesting
public class TransactionFormat {
public static String format(TxInfo txInfo) {
String headerLine = padEnd(COL_HEADER_TX_ID, txInfo.getTxId().length(), ' ') + COL_HEADER_DELIMITER
+ COL_HEADER_TX_IS_CONFIRMED + COL_HEADER_DELIMITER
+ COL_HEADER_TX_INPUT_SUM + COL_HEADER_DELIMITER
+ COL_HEADER_TX_OUTPUT_SUM + COL_HEADER_DELIMITER
+ COL_HEADER_TX_FEE + COL_HEADER_DELIMITER
+ COL_HEADER_TX_SIZE + COL_HEADER_DELIMITER
+ (txInfo.getMemo().isEmpty() ? "" : COL_HEADER_TX_MEMO + COL_HEADER_DELIMITER)
+ "\n";
String colDataFormat = "%-" + txInfo.getTxId().length() + "s"
+ " %" + COL_HEADER_TX_IS_CONFIRMED.length() + "s"
+ " %" + COL_HEADER_TX_INPUT_SUM.length() + "s"
+ " %" + COL_HEADER_TX_OUTPUT_SUM.length() + "s"
+ " %" + COL_HEADER_TX_FEE.length() + "s"
+ " %" + COL_HEADER_TX_SIZE.length() + "s"
+ " %s";
return headerLine
+ String.format(colDataFormat,
txInfo.getTxId(),
txInfo.getIsPending() ? "NO" : "YES", // pending=true means not confirmed
formatSatoshis(txInfo.getInputSum()),
formatSatoshis(txInfo.getOutputSum()),
formatSatoshis(txInfo.getFee()),
txInfo.getSize(),
txInfo.getMemo().isEmpty() ? "" : txInfo.getMemo());
}
}

View File

@ -23,18 +23,12 @@ import bisq.proto.grpc.BtcBalanceInfo;
import bisq.proto.grpc.GetAddressBalanceRequest;
import bisq.proto.grpc.GetBalancesRequest;
import bisq.proto.grpc.GetFundingAddressesRequest;
import bisq.proto.grpc.GetTransactionRequest;
import bisq.proto.grpc.GetTxFeeRateRequest;
import bisq.proto.grpc.LockWalletRequest;
import bisq.proto.grpc.MarketPriceRequest;
import bisq.proto.grpc.RemoveWalletPasswordRequest;
import bisq.proto.grpc.SendBtcRequest;
import bisq.proto.grpc.SetTxFeeRatePreferenceRequest;
import bisq.proto.grpc.SetWalletPasswordRequest;
import bisq.proto.grpc.TxFeeRateInfo;
import bisq.proto.grpc.TxInfo;
import bisq.proto.grpc.UnlockWalletRequest;
import bisq.proto.grpc.UnsetTxFeeRatePreferenceRequest;
import java.util.List;
@ -95,40 +89,6 @@ public class WalletsServiceRequest {
.getAddress();
}
public TxInfo sendBtc(String address, String amount, String txFeeRate, String memo) {
var request = SendBtcRequest.newBuilder()
.setAddress(address)
.setAmount(amount)
.setTxFeeRate(txFeeRate)
.setMemo(memo)
.build();
return grpcStubs.walletsService.sendBtc(request).getTxInfo();
}
public TxFeeRateInfo getTxFeeRate() {
var request = GetTxFeeRateRequest.newBuilder().build();
return grpcStubs.walletsService.getTxFeeRate(request).getTxFeeRateInfo();
}
public TxFeeRateInfo setTxFeeRate(long txFeeRate) {
var request = SetTxFeeRatePreferenceRequest.newBuilder()
.setTxFeeRatePreference(txFeeRate)
.build();
return grpcStubs.walletsService.setTxFeeRatePreference(request).getTxFeeRateInfo();
}
public TxFeeRateInfo unsetTxFeeRate() {
var request = UnsetTxFeeRatePreferenceRequest.newBuilder().build();
return grpcStubs.walletsService.unsetTxFeeRatePreference(request).getTxFeeRateInfo();
}
public TxInfo getTransaction(String txId) {
var request = GetTransactionRequest.newBuilder()
.setTxId(txId)
.build();
return grpcStubs.walletsService.getTransaction(request).getTxInfo();
}
public void lockWallet() {
var request = LockWalletRequest.newBuilder().build();
//noinspection ResultOfMethodCallIgnored

View File

@ -17,8 +17,6 @@
package bisq.cli.table.builder;
import bisq.proto.grpc.TxInfo;
import java.util.List;
import javax.annotation.Nullable;
@ -61,25 +59,22 @@ class TransactionTableBuilder extends AbstractTableBuilder {
public Table build() {
// TODO Add 'gettransactions' api method & show multiple tx in the console.
// For now, a tx tbl is only one row.
TxInfo tx = (TxInfo) protos.get(0);
// Declare the columns derived from tx info.
@Nullable
Column<String> colMemo = tx.getMemo().isEmpty()
? null
: new StringColumn(COL_HEADER_TX_MEMO);
Column<String> colMemo = null;
// Populate columns with tx info.
colTxId.addRow(tx.getTxId());
colIsConfirmed.addRow(!tx.getIsPending());
colInputSum.addRow(tx.getInputSum());
colOutputSum.addRow(tx.getOutputSum());
colTxFee.addRow(tx.getFee());
colTxSize.addRow((long) tx.getSize());
colTxId.addRow(null);
colIsConfirmed.addRow(null);
colInputSum.addRow(null);
colOutputSum.addRow(null);
colTxFee.addRow(null);
colTxSize.addRow(null);
if (colMemo != null)
colMemo.addRow(tx.getMemo());
colMemo.addRow(null);
// Define and return the table instance with populated columns.

View File

@ -18,7 +18,6 @@ public class GetTransactionCliOutputDiffTest extends AbstractCliTest {
throw new IllegalStateException("Need a single transaction-id program argument.");
GetTransactionCliOutputDiffTest test = new GetTransactionCliOutputDiffTest(args[0]);
test.getTransaction();
}
private final String transactionId;
@ -27,15 +26,4 @@ public class GetTransactionCliOutputDiffTest extends AbstractCliTest {
super();
this.transactionId = transactionId;
}
private void getTransaction() {
var tx = aliceClient.getTransaction(transactionId);
// TransactionFormat class had been deprecated, then deleted on 17-Feb-2022, but
// these diff tests can be useful for testing changes to the current tbl formatting api.
// var oldTbl = TransactionFormat.format(tx);
var newTbl = new TableBuilder(TRANSACTION_TBL, tx).build().toString();
// printOldTbl(oldTbl);
printNewTbl(newTbl);
// checkDiffsIgnoreWhitespace(oldTbl, newTbl);
}
}

View File

@ -23,7 +23,6 @@ import bisq.core.api.model.MarketDepthInfo;
import bisq.core.api.model.MarketPriceInfo;
import bisq.core.api.model.PaymentAccountForm;
import bisq.core.api.model.PaymentAccountFormField;
import bisq.core.api.model.TxFeeRateInfo;
import bisq.core.app.AppStartupState;
import bisq.core.monetary.Price;
import bisq.core.offer.Offer;
@ -315,24 +314,6 @@ public class CoreApi {
walletsService.sendBtc(address, amount, txFeeRate, memo, callback);
}
public void getTxFeeRate(ResultHandler resultHandler) {
walletsService.getTxFeeRate(resultHandler);
}
public void setTxFeeRatePreference(long txFeeRate,
ResultHandler resultHandler) {
walletsService.setTxFeeRatePreference(txFeeRate, resultHandler);
}
public void unsetTxFeeRatePreference(ResultHandler resultHandler) {
walletsService.unsetTxFeeRatePreference(resultHandler);
}
public TxFeeRateInfo getMostRecentTxFeeRateInfo() {
return walletsService.getMostRecentTxFeeRateInfo();
}
public Transaction getTransaction(String txId) {
return walletsService.getTransaction(txId);
}

View File

@ -103,12 +103,10 @@ class CoreTradesService {
var useSavingsWallet = true;
// synchronize access to take offer model // TODO (woodser): to avoid synchronizing, don't use stateful model
Coin txFeeFromFeeService; // TODO (woodser): remove this and other unused fields
Coin takerFee;
Coin fundsNeededForTrade;
synchronized (takeOfferModel) {
takeOfferModel.initModel(offer, paymentAccount, useSavingsWallet);
txFeeFromFeeService = takeOfferModel.getTxFeeFromFeeService();
takerFee = takeOfferModel.getTakerFee();
fundsNeededForTrade = takeOfferModel.getFundsNeededForTrade();
log.info("Initiating take {} offer, {}", offer.isBuyOffer() ? "buy" : "sell", takeOfferModel);
@ -116,7 +114,6 @@ class CoreTradesService {
// take offer
tradeManager.onTakeOffer(offer.getAmount(),
txFeeFromFeeService,
takerFee,
fundsNeededForTrade,
offer,
@ -127,6 +124,7 @@ class CoreTradesService {
errorMessageHandler
);
} catch (Exception e) {
e.printStackTrace();
errorMessageHandler.handleErrorMessage(e.getMessage());
}
}

View File

@ -20,7 +20,6 @@ package bisq.core.api;
import bisq.core.api.model.AddressBalanceInfo;
import bisq.core.api.model.BalancesInfo;
import bisq.core.api.model.BtcBalanceInfo;
import bisq.core.api.model.TxFeeRateInfo;
import bisq.core.api.model.XmrBalanceInfo;
import bisq.core.app.AppStartupState;
import bisq.core.btc.Balances;
@ -31,15 +30,12 @@ import bisq.core.btc.setup.WalletsSetup;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.btc.wallet.WalletsManager;
import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.provider.fee.FeeService;
import bisq.core.user.Preferences;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.CoinFormatter;
import bisq.common.Timer;
import bisq.common.UserThread;
import bisq.common.handlers.ResultHandler;
import bisq.common.util.Utilities;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
@ -57,10 +53,6 @@ import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import org.bouncycastle.crypto.params.KeyParameter;
@ -97,8 +89,6 @@ class CoreWalletsService {
private final BtcWalletService btcWalletService;
private final XmrWalletService xmrWalletService;
private final CoinFormatter btcFormatter;
private final FeeService feeService;
private final Preferences preferences;
@Nullable
private Timer lockTimer;
@ -106,8 +96,6 @@ class CoreWalletsService {
@Nullable
private KeyParameter tempAesKey;
private final ListeningExecutorService executor = Utilities.getSingleThreadListeningExecutor("CoreWalletsService");
@Inject
public CoreWalletsService(AppStartupState appStartupState,
CoreContext coreContext,
@ -118,7 +106,6 @@ class CoreWalletsService {
BtcWalletService btcWalletService,
XmrWalletService xmrWalletService,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
FeeService feeService,
Preferences preferences) {
this.appStartupState = appStartupState;
this.coreContext = coreContext;
@ -129,8 +116,6 @@ class CoreWalletsService {
this.btcWalletService = btcWalletService;
this.xmrWalletService = xmrWalletService;
this.btcFormatter = btcFormatter;
this.feeService = feeService;
this.preferences = preferences;
}
@Nullable
@ -311,58 +296,6 @@ class CoreWalletsService {
}
}
void getTxFeeRate(ResultHandler resultHandler) {
try {
@SuppressWarnings({"unchecked", "Convert2MethodRef"})
ListenableFuture<Void> future =
(ListenableFuture<Void>) executor.submit(() -> feeService.requestFees());
Futures.addCallback(future, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable Void ignored) {
resultHandler.handleResult();
}
@Override
public void onFailure(Throwable t) {
log.error("", t);
throw new IllegalStateException("could not request fees from fee service", t);
}
}, MoreExecutors.directExecutor());
} catch (Exception ex) {
log.error("", ex);
throw new IllegalStateException("could not request fees from fee service", ex);
}
}
void setTxFeeRatePreference(long txFeeRate,
ResultHandler resultHandler) {
long minFeePerVbyte = feeService.getMinFeePerVByte();
if (txFeeRate < minFeePerVbyte)
throw new IllegalStateException(
format("tx fee rate preference must be >= %d sats/byte", minFeePerVbyte));
preferences.setUseCustomWithdrawalTxFee(true);
Coin satsPerByte = Coin.valueOf(txFeeRate);
preferences.setWithdrawalTxFeeInVbytes(satsPerByte.value);
getTxFeeRate(resultHandler);
}
void unsetTxFeeRatePreference(ResultHandler resultHandler) {
preferences.setUseCustomWithdrawalTxFee(false);
getTxFeeRate(resultHandler);
}
TxFeeRateInfo getMostRecentTxFeeRateInfo() {
return new TxFeeRateInfo(
preferences.isUseCustomWithdrawalTxFee(),
preferences.getWithdrawalTxFeeInVbytes(),
feeService.getMinFeePerVByte(),
feeService.getTxFeePerVbyte().value,
feeService.getLastRequest());
}
Transaction getTransaction(String txId) {
if (txId.length() != 64)
throw new IllegalArgumentException(format("%s is not a transaction id", txId));

View File

@ -1,81 +0,0 @@
/*
* This file is part of Haveno.
*
* Haveno is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Haveno is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.api.model;
import bisq.common.Payload;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@EqualsAndHashCode
@Getter
public class TxFeeRateInfo implements Payload {
private final boolean useCustomTxFeeRate;
private final long customTxFeeRate;
private final long minFeeServiceRate;
private final long feeServiceRate;
private final long lastFeeServiceRequestTs;
public TxFeeRateInfo(boolean useCustomTxFeeRate,
long customTxFeeRate,
long minFeeServiceRate,
long feeServiceRate,
long lastFeeServiceRequestTs) {
this.useCustomTxFeeRate = useCustomTxFeeRate;
this.customTxFeeRate = customTxFeeRate;
this.minFeeServiceRate = minFeeServiceRate;
this.feeServiceRate = feeServiceRate;
this.lastFeeServiceRequestTs = lastFeeServiceRequestTs;
}
//////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
//////////////////////////////////////////////////////////////////////////////////////
@Override
public bisq.proto.grpc.TxFeeRateInfo toProtoMessage() {
return bisq.proto.grpc.TxFeeRateInfo.newBuilder()
.setUseCustomTxFeeRate(useCustomTxFeeRate)
.setCustomTxFeeRate(customTxFeeRate)
.setMinFeeServiceRate(minFeeServiceRate)
.setFeeServiceRate(feeServiceRate)
.setLastFeeServiceRequestTs(lastFeeServiceRequestTs)
.build();
}
@SuppressWarnings("unused")
public static TxFeeRateInfo fromProto(bisq.proto.grpc.TxFeeRateInfo proto) {
return new TxFeeRateInfo(proto.getUseCustomTxFeeRate(),
proto.getCustomTxFeeRate(),
proto.getMinFeeServiceRate(),
proto.getFeeServiceRate(),
proto.getLastFeeServiceRequestTs());
}
@Override
public String toString() {
return "TxFeeRateInfo{" + "\n" +
" useCustomTxFeeRate=" + useCustomTxFeeRate + "\n" +
", customTxFeeRate=" + customTxFeeRate + " sats/byte" + "\n" +
", minFeeServiceRate=" + minFeeServiceRate + " sats/byte" + "\n" +
", feeServiceRate=" + feeServiceRate + " sats/byte" + "\n" +
", lastFeeServiceRequestTs=" + lastFeeServiceRequestTs + "\n" +
'}';
}
}

View File

@ -1,212 +0,0 @@
/*
* This file is part of Haveno.
*
* Haveno is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Haveno is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.api.model;
import bisq.common.Payload;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.Transaction;
import java.util.Map;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import static java.util.Objects.requireNonNull;
@EqualsAndHashCode
@Getter
public class TxInfo implements Payload {
// The client cannot see an instance of an org.bitcoinj.core.Transaction. We use the
// lighter weight TxInfo proto wrapper instead, containing just enough fields to
// view some transaction details. A block explorer or bitcoin-core client can be
// used to see more detail.
private final String txId;
private final long inputSum;
private final long outputSum;
private final long fee;
private final int size;
private final boolean isPending;
private final String memo;
public TxInfo(TxInfoBuilder builder) {
this.txId = builder.txId;
this.inputSum = builder.inputSum;
this.outputSum = builder.outputSum;
this.fee = builder.fee;
this.size = builder.size;
this.isPending = builder.isPending;
this.memo = builder.memo;
}
public static TxInfo toTxInfo(Transaction transaction) {
if (transaction == null)
throw new IllegalStateException("server created a null transaction");
if (transaction.getFee() != null)
return new TxInfoBuilder()
.withTxId(transaction.getTxId().toString())
.withInputSum(transaction.getInputSum().value)
.withOutputSum(transaction.getOutputSum().value)
.withFee(transaction.getFee().value)
.withSize(transaction.getMessageSize())
.withIsPending(transaction.isPending())
.withMemo(transaction.getMemo())
.build();
else
return new TxInfoBuilder()
.withTxId(transaction.getTxId().toString())
.withInputSum(transaction.getInputSum().value)
.withOutputSum(transaction.getOutputSum().value)
// Do not set fee == null.
.withSize(transaction.getMessageSize())
.withIsPending(transaction.isPending())
.withMemo(transaction.getMemo())
.build();
}
//////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
//////////////////////////////////////////////////////////////////////////////////////
@Override
public bisq.proto.grpc.TxInfo toProtoMessage() {
return bisq.proto.grpc.TxInfo.newBuilder()
.setTxId(txId)
.setInputSum(inputSum)
.setOutputSum(outputSum)
.setFee(fee)
.setSize(size)
.setIsPending(isPending)
.setMemo(memo == null ? "" : memo)
.build();
}
@SuppressWarnings("unused")
public static TxInfo fromProto(bisq.proto.grpc.TxInfo proto) {
return new TxInfoBuilder()
.withTxId(proto.getTxId())
.withInputSum(proto.getInputSum())
.withOutputSum(proto.getOutputSum())
.withFee(proto.getFee())
.withSize(proto.getSize())
.withIsPending(proto.getIsPending())
.withMemo(proto.getMemo())
.build();
}
public static class TxInfoBuilder {
private String txId;
private long inputSum;
private long outputSum;
private long fee;
private int size;
private boolean isPending;
private String memo;
public TxInfoBuilder withTxId(String txId) {
this.txId = txId;
return this;
}
public TxInfoBuilder withInputSum(long inputSum) {
this.inputSum = inputSum;
return this;
}
public TxInfoBuilder withOutputSum(long outputSum) {
this.outputSum = outputSum;
return this;
}
public TxInfoBuilder withFee(long fee) {
this.fee = fee;
return this;
}
public TxInfoBuilder withSize(int size) {
this.size = size;
return this;
}
public TxInfoBuilder withIsPending(boolean isPending) {
this.isPending = isPending;
return this;
}
public TxInfoBuilder withMemo(String memo) {
this.memo = memo;
return this;
}
public TxInfo build() {
return new TxInfo(this);
}
}
@Override
public String toString() {
return "TxInfo{" + "\n" +
" txId='" + txId + '\'' + "\n" +
", inputSum=" + inputSum + "\n" +
", outputSum=" + outputSum + "\n" +
", fee=" + fee + "\n" +
", size=" + size + "\n" +
", isPending=" + isPending + "\n" +
", memo='" + memo + '\'' + "\n" +
'}';
}
public static String getTransactionDetailString(Transaction tx) {
if (tx == null)
throw new IllegalArgumentException("Cannot print details for null transaction");
StringBuilder builder = new StringBuilder("Transaction " + requireNonNull(tx).getTxId() + ":").append("\n");
builder.append("\tisPending: ").append(tx.isPending()).append("\n");
builder.append("\tfee: ").append(tx.getFee()).append("\n");
builder.append("\tweight: ").append(tx.getWeight()).append("\n");
builder.append("\tVsize: ").append(tx.getVsize()).append("\n");
builder.append("\tinputSum: ").append(tx.getInputSum()).append("\n");
builder.append("\toutputSum: ").append(tx.getOutputSum()).append("\n");
Map<Sha256Hash, Integer> appearsInHashes = tx.getAppearsInHashes();
if (appearsInHashes != null)
builder.append("\tappearsInHashes: yes, count: ").append(appearsInHashes.size()).append("\n");
else
builder.append("\tappearsInHashes: ").append("no").append("\n");
builder.append("\tanyOutputSpent: ").append(tx.isAnyOutputSpent()).append("\n");
builder.append("\tupdateTime: ").append(tx.getUpdateTime()).append("\n");
builder.append("\tincludedInBestChainAt: ").append(tx.getIncludedInBestChainAt()).append("\n");
builder.append("\thasWitnesses: ").append(tx.hasWitnesses()).append("\n");
builder.append("\tlockTime: ").append(tx.getLockTime()).append("\n");
builder.append("\tversion: ").append(tx.getVersion()).append("\n");
builder.append("\thasConfidence: ").append(tx.hasConfidence()).append("\n");
builder.append("\tsigOpCount: ").append(tx.getSigOpCount()).append("\n");
builder.append("\tisTimeLocked: ").append(tx.isTimeLocked()).append("\n");
builder.append("\thasRelativeLockTime: ").append(tx.hasRelativeLockTime()).append("\n");
builder.append("\tisOptInFullRBF: ").append(tx.isOptInFullRBF()).append("\n");
builder.append("\tpurpose: ").append(tx.getPurpose()).append("\n");
builder.append("\texchangeRate: ").append(tx.getExchangeRate()).append("\n");
builder.append("\tmemo: ").append(tx.getMemo()).append("\n");
return builder.toString();
}
}

View File

@ -33,7 +33,6 @@ import bisq.core.offer.OpenOfferManager;
import bisq.core.offer.TriggerPriceService;
import bisq.core.payment.AmazonGiftCardAccount;
import bisq.core.payment.RevolutAccount;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.mempool.MempoolService;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.support.dispute.arbitration.ArbitrationManager;
@ -88,7 +87,6 @@ public class DomainInitialisation {
private final RefundAgentManager refundAgentManager;
private final PrivateNotificationManager privateNotificationManager;
private final P2PService p2PService;
private final FeeService feeService;
private final TradeStatisticsManager tradeStatisticsManager;
private final AccountAgeWitnessService accountAgeWitnessService;
private final SignedWitnessService signedWitnessService;
@ -122,7 +120,6 @@ public class DomainInitialisation {
RefundAgentManager refundAgentManager,
PrivateNotificationManager privateNotificationManager,
P2PService p2PService,
FeeService feeService,
TradeStatisticsManager tradeStatisticsManager,
AccountAgeWitnessService accountAgeWitnessService,
SignedWitnessService signedWitnessService,
@ -154,7 +151,6 @@ public class DomainInitialisation {
this.refundAgentManager = refundAgentManager;
this.privateNotificationManager = privateNotificationManager;
this.p2PService = p2PService;
this.feeService = feeService;
this.tradeStatisticsManager = tradeStatisticsManager;
this.accountAgeWitnessService = accountAgeWitnessService;
this.signedWitnessService = signedWitnessService;
@ -207,9 +203,6 @@ public class DomainInitialisation {
p2PService.onAllServicesInitialized();
feeService.onAllServicesInitialized();
tradeStatisticsManager.onAllServicesInitialized();
accountAgeWitnessService.onAllServicesInitialized();

View File

@ -25,7 +25,6 @@ import bisq.core.btc.setup.WalletsSetup;
import bisq.core.btc.wallet.WalletsManager;
import bisq.core.locale.Res;
import bisq.core.offer.OpenOfferManager;
import bisq.core.provider.fee.FeeService;
import bisq.core.trade.TradeManager;
import bisq.core.user.Preferences;
import bisq.core.util.FormattingUtils;
@ -69,7 +68,6 @@ public class WalletAppSetup {
private final WalletsManager walletsManager;
private final WalletsSetup walletsSetup;
private final CoreMoneroConnectionsService connectionService;
private final FeeService feeService;
private final Config config;
private final Preferences preferences;
@ -94,14 +92,12 @@ public class WalletAppSetup {
WalletsManager walletsManager,
WalletsSetup walletsSetup,
CoreMoneroConnectionsService connectionService,
FeeService feeService,
Config config,
Preferences preferences) {
this.coreContext = coreContext;
this.walletsManager = walletsManager;
this.walletsSetup = walletsSetup;
this.connectionService = connectionService;
this.feeService = feeService;
this.config = config;
this.preferences = preferences;
this.useTorForBTC.set(preferences.getUseTorForBitcoinJ());
@ -121,9 +117,8 @@ public class WalletAppSetup {
ObjectProperty<Throwable> walletServiceException = new SimpleObjectProperty<>();
btcInfoBinding = EasyBind.combine(connectionService.downloadPercentageProperty(), // TODO (woodser): update to XMR
connectionService.chainHeightProperty(),
feeService.feeUpdateCounterProperty(),
walletServiceException,
(downloadPercentage, chainHeight, feeUpdate, exception) -> {
(downloadPercentage, chainHeight, exception) -> {
String result;
if (exception == null) {
double percentage = (double) downloadPercentage;

View File

@ -29,7 +29,6 @@ import bisq.core.btc.wallet.TradeWalletService;
import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.provider.ProvidersRepository;
import bisq.core.provider.fee.FeeProvider;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.PriceFeedService;
import bisq.common.app.AppModule;
@ -99,8 +98,6 @@ public class BitcoinModule extends AppModule {
bind(ProvidersRepository.class).in(Singleton.class);
bind(FeeProvider.class).in(Singleton.class);
bind(PriceFeedService.class).in(Singleton.class);
bind(FeeService.class).in(Singleton.class);
bind(TxFeeEstimationService.class).in(Singleton.class);
}
}

View File

@ -1,205 +0,0 @@
/*
* This file is part of Haveno.
*
* Haveno is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Haveno is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.btc;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.provider.fee.FeeService;
import bisq.core.user.Preferences;
import bisq.common.util.Tuple2;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException;
import javax.inject.Inject;
import com.google.common.annotations.VisibleForTesting;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Util class for getting the estimated tx fee for maker or taker fee tx.
*/
@Slf4j
public class TxFeeEstimationService {
// 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_VSIZE = 175;
private static int DEPOSIT_TX_VSIZE = 233;
private static int MAX_ITERATIONS = 10;
private final FeeService feeService;
private final BtcWalletService btcWalletService;
private final Preferences preferences;
@Inject
public TxFeeEstimationService(FeeService feeService,
BtcWalletService btcWalletService,
Preferences preferences) {
this.feeService = feeService;
this.btcWalletService = btcWalletService;
this.preferences = preferences;
}
public Tuple2<Coin, Integer> getEstimatedFeeAndTxVsizeForTaker(Coin fundsNeededForTrade, Coin tradeFee) {
return getEstimatedFeeAndTxVsize(true,
fundsNeededForTrade,
tradeFee,
feeService,
btcWalletService,
preferences);
}
public Tuple2<Coin, Integer> getEstimatedFeeAndTxVsizeForMaker(Coin reservedFundsForOffer,
Coin tradeFee) {
return getEstimatedFeeAndTxVsize(false,
reservedFundsForOffer,
tradeFee,
feeService,
btcWalletService,
preferences);
}
private Tuple2<Coin, Integer> getEstimatedFeeAndTxVsize(boolean isTaker,
Coin amount,
Coin tradeFee,
FeeService feeService,
BtcWalletService btcWalletService,
Preferences preferences) {
Coin txFeePerVbyte = feeService.getTxFeePerVbyte();
// We start with min taker fee vsize of 175
int estimatedTxVsize = TYPICAL_TX_WITH_1_INPUT_VSIZE;
try {
estimatedTxVsize = getEstimatedTxVsize(List.of(tradeFee, amount), estimatedTxVsize, txFeePerVbyte, btcWalletService);
} catch (InsufficientMoneyException e) {
if (isTaker) {
// If we cannot do the estimation, we use the vsize o the largest of our txs which is the deposit tx.
estimatedTxVsize = DEPOSIT_TX_VSIZE;
}
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 vsize of {} vbytes.", estimatedTxVsize);
}
Coin txFee;
int vsize;
if (isTaker) {
int averageVsize = (estimatedTxVsize + DEPOSIT_TX_VSIZE) / 2; // deposit tx has about 233 vbytes
// We use at least the vsize of the deposit tx to not underpay it.
vsize = Math.max(DEPOSIT_TX_VSIZE, averageVsize);
txFee = txFeePerVbyte.multiply(vsize);
log.info("Fee estimation resulted in a tx vsize of {} vbytes.\n" +
"We use an average between the taker fee tx and the deposit tx (233 vbytes) which results in {} vbytes.\n" +
"The deposit tx has 233 vbytes, we use that as our min value. Vsize for fee calculation is {} vbytes.\n" +
"The tx fee of {} Sat", estimatedTxVsize, averageVsize, vsize, txFee.value);
} else {
vsize = estimatedTxVsize;
txFee = txFeePerVbyte.multiply(vsize);
log.info("Fee estimation resulted in a tx vsize of {} vbytes and a tx fee of {} Sat.", vsize, txFee.value);
}
return new Tuple2<>(txFee, vsize);
}
public Tuple2<Coin, Integer> getEstimatedFeeAndTxVsize(Coin amount,
BtcWalletService btcWalletService) {
Coin txFeePerVbyte = btcWalletService.getTxFeeForWithdrawalPerVbyte();
// We start with min taker fee vsize of 175
int estimatedTxVsize = TYPICAL_TX_WITH_1_INPUT_VSIZE;
try {
estimatedTxVsize = getEstimatedTxVsize(List.of(amount), estimatedTxVsize, txFeePerVbyte, btcWalletService);
} catch (InsufficientMoneyException e) {
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 vsize of {} vbytes.", estimatedTxVsize);
}
Coin txFee = txFeePerVbyte.multiply(estimatedTxVsize);
log.info("Fee estimation resulted in a tx vsize of {} vbytes and a tx fee of {} Sat.", estimatedTxVsize, txFee.value);
return new Tuple2<>(txFee, estimatedTxVsize);
}
// We start with the initialEstimatedTxVsize for a tx with 1 input (175) vbytes 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 vsize from the tx BitcoinJ has created
// with the required inputs we compare if the vsize is not more then 20% different to our assumed tx vsize. If we are inside
// that tolerance we use that tx vsize for our fee estimation, if not (if there has been more then 1 inputs) we
// apply the new fee based on the reported tx vsize and request again from BitcoinJ to fill that tx with the inputs
// to be sufficiently funded. The algorithm how BitcoinJ selects utxos is complex and contains several aspects
// (minimize fee, don't create too many tiny utxos,...). We treat that algorithm as an unknown and it is not
// guaranteed that there are more inputs required if we increase the fee (it could be that there is a better
// selection of inputs chosen if we have increased the fee and therefore less inputs and smaller tx vsize). As the increased fee might
// change the number of inputs we need to repeat that process until we are inside of a certain tolerance. To avoid
// potential endless loops we add a counter (we use 10, usually it takes just very few iterations).
// Worst case would be that the last vsize we got reported is > 20% off to
// the real tx vsize but as fee estimation is anyway a educated guess in the best case we don't worry too much.
// If we have underpaid the tx might take longer to get confirmed.
@VisibleForTesting
static int getEstimatedTxVsize(List<Coin> outputValues,
int initialEstimatedTxVsize,
Coin txFeePerVbyte,
BtcWalletService btcWalletService)
throws InsufficientMoneyException {
boolean isInTolerance;
int estimatedTxVsize = initialEstimatedTxVsize;
int realTxVsize;
int counter = 0;
do {
Coin txFee = txFeePerVbyte.multiply(estimatedTxVsize);
realTxVsize = btcWalletService.getEstimatedFeeTxVsize(outputValues, txFee);
isInTolerance = isInTolerance(estimatedTxVsize, realTxVsize, 0.2);
if (!isInTolerance) {
estimatedTxVsize = realTxVsize;
}
counter++;
}
while (!isInTolerance && counter < MAX_ITERATIONS);
if (!isInTolerance) {
log.warn("We could not find a tx which satisfies our tolerance requirement of 20%. " +
"realTxVsize={}, estimatedTxVsize={}",
realTxVsize, estimatedTxVsize);
}
return estimatedTxVsize;
}
@VisibleForTesting
static boolean isInTolerance(int estimatedVsize, int txVsize, double tolerance) {
checkArgument(estimatedVsize > 0, "estimatedVsize must be positive");
checkArgument(txVsize > 0, "txVsize must be positive");
checkArgument(tolerance > 0, "tolerance must be positive");
double deviation = Math.abs(1 - ((double) estimatedVsize / (double) txVsize));
return deviation <= tolerance;
}
}

View File

@ -25,7 +25,6 @@ import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.model.AddressEntryList;
import bisq.core.btc.setup.WalletsSetup;
import bisq.core.btc.wallet.http.MemPoolSpaceTxBroadcaster;
import bisq.core.provider.fee.FeeService;
import bisq.core.user.Preferences;
import bisq.common.handlers.ErrorMessageHandler;
@ -88,11 +87,9 @@ public class BtcWalletService extends WalletService {
@Inject
public BtcWalletService(WalletsSetup walletsSetup,
AddressEntryList addressEntryList,
Preferences preferences,
FeeService feeService) {
Preferences preferences) {
super(walletsSetup,
preferences,
feeService);
preferences);
this.addressEntryList = addressEntryList;
@ -574,6 +571,10 @@ public class BtcWalletService extends WalletService {
// Withdrawal Fee calculation
///////////////////////////////////////////////////////////////////////////////////////////
public Coin getTxFeeForWithdrawalPerVbyte() {
throw new RuntimeException("BTC fee estimation removed");
}
public Transaction getFeeEstimationTransaction(String fromAddress,
String toAddress,
Coin amount,

View File

@ -24,7 +24,6 @@ import bisq.core.btc.listeners.BalanceListener;
import bisq.core.btc.listeners.TxConfidenceListener;
import bisq.core.btc.setup.WalletsSetup;
import bisq.core.btc.wallet.http.MemPoolSpaceTxBroadcaster;
import bisq.core.provider.fee.FeeService;
import bisq.core.user.Preferences;
import bisq.common.config.Config;
@ -117,7 +116,6 @@ import monero.wallet.model.MoneroTxWallet;
public abstract class WalletService {
protected final WalletsSetup walletsSetup;
protected final Preferences preferences;
protected final FeeService feeService;
protected final NetworkParameters params;
private final BisqWalletListener walletEventListener = new BisqWalletListener();
private final CopyOnWriteArraySet<AddressConfidenceListener> addressConfidenceListeners = new CopyOnWriteArraySet<>();
@ -140,11 +138,9 @@ public abstract class WalletService {
@Inject
WalletService(WalletsSetup walletsSetup,
Preferences preferences,
FeeService feeService) {
Preferences preferences) {
this.walletsSetup = walletsSetup;
this.preferences = preferences;
this.feeService = feeService;
params = walletsSetup.getParams();
@ -519,14 +515,6 @@ public abstract class WalletService {
return getBalanceForAddress(getAddressFromOutput(output));
}
public Coin getTxFeeForWithdrawalPerVbyte() {
Coin fee = (preferences.isUseCustomWithdrawalTxFee()) ?
Coin.valueOf(preferences.getWithdrawalTxFeeInVbytes()) :
feeService.getTxFeePerVbyte();
log.info("tx fee = " + fee.toFriendlyString());
return fee;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Tx outputs
@ -578,7 +566,6 @@ public abstract class WalletService {
throws InsufficientMoneyException, AddressFormatException {
SendRequest sendRequest = SendRequest.emptyWallet(Address.fromString(params, toAddress));
sendRequest.fee = Coin.ZERO;
sendRequest.feePerKb = getTxFeeForWithdrawalPerVbyte().multiply(1000);
sendRequest.aesKey = aesKey;
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
printTx("empty btc wallet", sendResult.tx);

View File

@ -17,7 +17,6 @@
package bisq.core.offer;
import bisq.core.btc.TxFeeEstimationService;
import bisq.core.btc.wallet.Restrictions;
import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.locale.CurrencyUtil;
@ -38,7 +37,6 @@ import bisq.network.p2p.P2PService;
import bisq.common.app.Version;
import bisq.common.crypto.PubKeyRingProvider;
import bisq.common.util.Tuple2;
import bisq.common.util.Utilities;
import org.bitcoinj.core.Coin;
@ -59,7 +57,6 @@ import static bisq.core.payment.payload.PaymentMethod.HAL_CASH_ID;
@Singleton
public class CreateOfferService {
private final OfferUtil offerUtil;
private final TxFeeEstimationService txFeeEstimationService;
private final PriceFeedService priceFeedService;
private final P2PService p2PService;
private final PubKeyRingProvider pubKeyRingProvider;
@ -75,7 +72,6 @@ public class CreateOfferService {
@Inject
public CreateOfferService(OfferUtil offerUtil,
TxFeeEstimationService txFeeEstimationService,
PriceFeedService priceFeedService,
P2PService p2PService,
PubKeyRingProvider pubKeyRingProvider,
@ -84,7 +80,6 @@ public class CreateOfferService {
TradeStatisticsManager tradeStatisticsManager,
ArbitratorManager arbitratorManager) {
this.offerUtil = offerUtil;
this.txFeeEstimationService = txFeeEstimationService;
this.priceFeedService = priceFeedService;
this.p2PService = p2PService;
this.pubKeyRingProvider = pubKeyRingProvider;
@ -227,18 +222,6 @@ public class CreateOfferService {
return offer;
}
public Tuple2<Coin, Integer> getEstimatedFeeAndTxVsize(Coin amount,
OfferDirection direction,
double buyerSecurityDeposit,
double sellerSecurityDeposit) {
Coin reservedFundsForOffer = getReservedFundsForOffer(direction,
amount,
buyerSecurityDeposit,
sellerSecurityDeposit);
return txFeeEstimationService.getEstimatedFeeAndTxVsizeForMaker(reservedFundsForOffer,
offerUtil.getMakerFee(amount));
}
public Coin getReservedFundsForOffer(OfferDirection direction,
Coin amount,
double buyerSecurityDeposit,

View File

@ -27,9 +27,9 @@ import bisq.core.monetary.Volume;
import bisq.core.payment.CashByMailAccount;
import bisq.core.payment.F2FAccount;
import bisq.core.payment.PaymentAccount;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.MarketPrice;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.HavenoUtils;
import bisq.core.trade.statistics.ReferralIdService;
import bisq.core.user.AutoConfirmSettings;
import bisq.core.user.Preferences;
@ -191,8 +191,8 @@ public class OfferUtil {
@Nullable
public Coin getTakerFee(@Nullable Coin amount) {
if (amount != null) {
Coin feePerBtc = CoinUtil.getFeePerBtc(FeeService.getTakerFeePerBtc(), amount);
return CoinUtil.maxCoin(feePerBtc, FeeService.getMinTakerFee());
Coin feePerBtc = CoinUtil.getFeePerBtc(HavenoUtils.getTakerFeePerBtc(), amount);
return CoinUtil.maxCoin(feePerBtc, HavenoUtils.getMinTakerFee());
} else {
return null;
}

View File

@ -27,7 +27,6 @@ import bisq.core.offer.Offer;
import bisq.core.offer.OfferUtil;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.PriceFeedService;
import bisq.common.taskrunner.Model;
@ -37,8 +36,6 @@ import org.bitcoinj.core.Coin;
import javax.inject.Inject;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@ -60,7 +57,6 @@ public class TakeOfferModel implements Model {
// Immutable
private final AccountAgeWitnessService accountAgeWitnessService;
private final XmrWalletService xmrWalletService;
private final FeeService feeService;
private final OfferUtil offerUtil;
private final PriceFeedService priceFeedService;
@ -75,11 +71,6 @@ public class TakeOfferModel implements Model {
private Coin securityDeposit;
private boolean useSavingsWallet;
// Use an average of a typical trade fee tx with 1 input, deposit tx and payout tx.
private final int feeTxVsize = 192; // (175+233+169)/3
private Coin txFeePerVbyteFromFeeService;
@Getter
private Coin txFeeFromFeeService;
@Getter
private Coin takerFee;
@Getter
@ -98,12 +89,10 @@ public class TakeOfferModel implements Model {
@Inject
public TakeOfferModel(AccountAgeWitnessService accountAgeWitnessService,
XmrWalletService xmrWalletService,
FeeService feeService,
OfferUtil offerUtil,
PriceFeedService priceFeedService) {
this.accountAgeWitnessService = accountAgeWitnessService;
this.xmrWalletService = xmrWalletService;
this.feeService = feeService;
this.offerUtil = offerUtil;
this.priceFeedService = priceFeedService;
}
@ -124,7 +113,6 @@ public class TakeOfferModel implements Model {
: offer.getSellerSecurityDeposit();
this.takerFee = offerUtil.getTakerFee(amount);
calculateTxFees();
calculateVolume();
calculateTotalToPay();
offer.resetState();
@ -137,46 +125,12 @@ public class TakeOfferModel implements Model {
// empty
}
private void calculateTxFees() {
// Taker pays 3 times the tx fee (taker fee, deposit, payout) because the mining
// fee might be different when maker created the offer and reserved his funds.
// Taker creates at least taker fee and deposit tx at nearly the same moment.
// Just the payout will be later and still could lead to issues if the required
// fee changed a lot in the meantime. using RBF and/or 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 175 vbytes (if one input). The trade txs has
// about 169-263 vbytes. We use 192 as a average value.
// Fee calculations:
// Trade fee tx: 175 vbytes (1 input)
// Deposit tx: 233 vbytes (1 MS output+ OP_RETURN) - 263 vbytes
// (1 MS output + OP_RETURN + change in case of smaller trade amount)
// Payout tx: 169 vbytes
// Disputed payout tx: 139 vbytes
txFeePerVbyteFromFeeService = getTxFeePerVbyte();
txFeeFromFeeService = offerUtil.getTxFeeByVsize(txFeePerVbyteFromFeeService, feeTxVsize);
log.info("{} txFeePerVbyte = {}", feeService.getClass().getSimpleName(), txFeePerVbyteFromFeeService);
}
private Coin getTxFeePerVbyte() {
try {
CompletableFuture<Void> feeRequestFuture = CompletableFuture.runAsync(feeService::requestFees);
feeRequestFuture.get(); // Block until async fee request is complete.
return feeService.getTxFeePerVbyte();
} catch (InterruptedException | ExecutionException e) {
throw new IllegalStateException("Could not request fees from fee service.", e);
}
}
private void calculateTotalToPay() {
// Taker pays 2 times the tx fee because the mining fee might be different when
// maker created the offer and reserved his funds, so that would not work well
// with dynamic fees. The mining fee for the takeOfferFee tx is deducted from
// the createOfferFee and not visible to the trader.
Coin feeAndSecDeposit = getTotalTxFee().add(securityDeposit).add(takerFee);
Coin feeAndSecDeposit = securityDeposit.add(takerFee);
totalToPayAsCoin = offer.isBuyOffer()
? feeAndSecDeposit.add(amount)
@ -212,35 +166,10 @@ public class TakeOfferModel implements Model {
offer.getMirroredDirection());
}
public Coin getTotalTxFee() {
return txFeeFromFeeService.add(getTxFeeForDepositTx()).add(getTxFeeForPayoutTx());
}
@NotNull
public Coin getFundsNeededForTrade() {
// If taking a buy offer, taker needs to reserve the offer.amt too.
return securityDeposit
.add(getTxFeeForDepositTx())
.add(getTxFeeForPayoutTx())
.add(offer.isBuyOffer() ? amount : ZERO);
}
private Coin getTxFeeForDepositTx() {
// TODO fix with new trade protocol!
// 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;
}
private Coin getTxFeeForPayoutTx() {
// TODO fix with new trade protocol!
// 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;
return securityDeposit.add(offer.isBuyOffer() ? amount : ZERO);
}
private void validateModelInputs() {
@ -264,8 +193,6 @@ public class TakeOfferModel implements Model {
this.takerFee = null;
this.totalAvailableBalance = null;
this.totalToPayAsCoin = null;
this.txFeeFromFeeService = null;
this.txFeePerVbyteFromFeeService = null;
this.useSavingsWallet = true;
this.volume = null;
}
@ -281,9 +208,6 @@ public class TakeOfferModel implements Model {
", addressEntry=" + addressEntry + "\n" +
", amount=" + amount + "\n" +
", securityDeposit=" + securityDeposit + "\n" +
", feeTxVsize=" + feeTxVsize + "\n" +
", txFeePerVbyteFromFeeService=" + txFeePerVbyteFromFeeService + "\n" +
", txFeeFromFeeService=" + txFeeFromFeeService + "\n" +
", takerFee=" + takerFee + "\n" +
", totalToPayAsCoin=" + totalToPayAsCoin + "\n" +
", missingCoin=" + missingCoin + "\n" +

View File

@ -1,191 +0,0 @@
/*
* This file is part of Haveno.
*
* Haveno is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Haveno is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.provider.fee;
import bisq.common.UserThread;
import bisq.common.config.Config;
import bisq.common.handlers.FaultHandler;
import bisq.common.util.Tuple2;
import org.bitcoinj.utils.MonetaryFormat;
import org.bitcoinj.core.Coin;
import com.google.inject.Inject;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import java.time.Instant;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import bisq.core.util.ParsingUtils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
@Slf4j
public class FeeService {
///////////////////////////////////////////////////////////////////////////////////////////
// Static
///////////////////////////////////////////////////////////////////////////////////////////
// Miner fees are between 1-600 sat/vbyte. We try to stay on the safe side. BTC_DEFAULT_TX_FEE is only used if our
// fee service would not deliver data.
private static final long BTC_DEFAULT_TX_FEE = 50;
private static final long MIN_PAUSE_BETWEEN_REQUESTS_IN_MIN = 2;
private static final MonetaryFormat btcCoinFormat = Config.baseCurrencyNetworkParameters().getMonetaryFormat();
public static Coin getMakerFeePerBtc() {
return ParsingUtils.parseToCoin("0.001", btcCoinFormat);
}
public static Coin getMinMakerFee() {
return ParsingUtils.parseToCoin("0.00005", btcCoinFormat);
}
public static Coin getTakerFeePerBtc() {
return ParsingUtils.parseToCoin("0.003", btcCoinFormat);
}
public static Coin getMinTakerFee() {
return ParsingUtils.parseToCoin("0.00005", btcCoinFormat);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Class fields
///////////////////////////////////////////////////////////////////////////////////////////
private final FeeProvider feeProvider;
private final IntegerProperty feeUpdateCounter = new SimpleIntegerProperty(0);
private long txFeePerVbyte = BTC_DEFAULT_TX_FEE;
private Map<String, Long> timeStampMap;
@Getter
private long lastRequest;
@Getter
private long minFeePerVByte;
private long epochInSecondAtLastRequest;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public FeeService(FeeProvider feeProvider) {
this.feeProvider = feeProvider;
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
public void onAllServicesInitialized() {
minFeePerVByte = Config.baseCurrencyNetwork().getDefaultMinFeePerVbyte();
requestFees();
// We update all 5 min.
UserThread.runPeriodically(this::requestFees, 5, TimeUnit.MINUTES);
}
public void requestFees() {
requestFees(null, null);
}
public void requestFees(Runnable resultHandler) {
requestFees(resultHandler, null);
}
public void requestFees(@Nullable Runnable resultHandler, @Nullable FaultHandler faultHandler) {
long now = Instant.now().getEpochSecond();
// We all requests only each 2 minutes
if (now - lastRequest > MIN_PAUSE_BETWEEN_REQUESTS_IN_MIN * 60) {
lastRequest = now;
FeeRequest feeRequest = new FeeRequest();
SettableFuture<Tuple2<Map<String, Long>, Map<String, Long>>> future = feeRequest.getFees(feeProvider);
Futures.addCallback(future, new FutureCallback<Tuple2<Map<String, Long>, Map<String, Long>>>() {
@Override
public void onSuccess(@Nullable Tuple2<Map<String, Long>, Map<String, Long>> result) {
UserThread.execute(() -> {
checkNotNull(result, "Result must not be null at getFees");
timeStampMap = result.first;
epochInSecondAtLastRequest = timeStampMap.get(Config.BTC_FEES_TS);
final Map<String, Long> map = result.second;
txFeePerVbyte = map.get(Config.BTC_TX_FEE);
minFeePerVByte = map.get(Config.BTC_MIN_TX_FEE);
if (txFeePerVbyte < minFeePerVByte) {
log.warn("The delivered fee of {} sat/vbyte is smaller than the min. default fee of {} sat/vbyte", txFeePerVbyte, minFeePerVByte);
txFeePerVbyte = minFeePerVByte;
}
feeUpdateCounter.set(feeUpdateCounter.get() + 1);
log.info("BTC tx fee: txFeePerVbyte={} minFeePerVbyte={}", txFeePerVbyte, minFeePerVByte);
if (resultHandler != null)
resultHandler.run();
});
}
@Override
public void onFailure(@NotNull Throwable throwable) {
log.warn("Could not load fees. feeProvider={}, error={}", feeProvider.toString(), throwable.toString());
if (faultHandler != null)
UserThread.execute(() -> faultHandler.handleFault("Could not load fees", throwable));
}
}, MoreExecutors.directExecutor());
} else {
log.debug("We got a requestFees called again before min pause of {} minutes has passed.", MIN_PAUSE_BETWEEN_REQUESTS_IN_MIN);
UserThread.execute(() -> {
if (resultHandler != null)
resultHandler.run();
});
}
}
public Coin getTxFee(int vsizeInVbytes) {
return getTxFeePerVbyte().multiply(vsizeInVbytes);
}
public Coin getTxFeePerVbyte() {
return Coin.valueOf(txFeePerVbyte);
}
public ReadOnlyIntegerProperty feeUpdateCounterProperty() {
return feeUpdateCounter;
}
public boolean isFeeAvailable() {
return feeUpdateCounter.get() > 0;
}
}

View File

@ -27,6 +27,7 @@ import bisq.core.trade.messages.InitTradeRequest;
import bisq.core.trade.messages.PaymentReceivedMessage;
import bisq.core.trade.messages.PaymentSentMessage;
import bisq.core.util.JsonUtil;
import bisq.core.util.ParsingUtils;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
@ -39,7 +40,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.MonetaryFormat;
import com.google.common.base.CaseFormat;
import com.google.common.base.Charsets;
@ -95,12 +96,29 @@ public class HavenoUtils {
public static long xmrToCentineros(double xmr) {
return atomicUnitsToCentineros(xmrToAtomicUnits(xmr));
}
private static final MonetaryFormat xmrCoinFormat = Config.baseCurrencyNetworkParameters().getMonetaryFormat();
public static Coin getMakerFeePerBtc() {
return ParsingUtils.parseToCoin("0.001", xmrCoinFormat);
}
public static Coin getMinMakerFee() {
return ParsingUtils.parseToCoin("0.00005", xmrCoinFormat);
}
public static Coin getTakerFeePerBtc() {
return ParsingUtils.parseToCoin("0.003", xmrCoinFormat);
}
public static Coin getMinTakerFee() {
return ParsingUtils.parseToCoin("0.00005", xmrCoinFormat);
}
/**
* Get address to collect trade fees.
*
* TODO: move to config constants?
*
* @return the address which collects trade fees
*/
public static String getTradeFeeAddress() {

View File

@ -29,7 +29,6 @@ import bisq.core.offer.OpenOffer;
import bisq.core.offer.OpenOfferManager;
import bisq.core.offer.SignedOffer;
import bisq.core.offer.availability.OfferAvailabilityModel;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
@ -458,8 +457,8 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
}
// compute expected taker fee
Coin feePerBtc = CoinUtil.getFeePerBtc(FeeService.getTakerFeePerBtc(), Coin.valueOf(offer.getOfferPayload().getAmount()));
Coin takerFee = CoinUtil.maxCoin(feePerBtc, FeeService.getMinTakerFee());
Coin feePerBtc = CoinUtil.getFeePerBtc(HavenoUtils.getTakerFeePerBtc(), Coin.valueOf(offer.getOfferPayload().getAmount()));
Coin takerFee = CoinUtil.maxCoin(feePerBtc, HavenoUtils.getMinTakerFee());
// create arbitrator trade
trade = new ArbitratorTrade(offer,
@ -691,7 +690,6 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
// First we check if offer is still available then we create the trade with the protocol
public void onTakeOffer(Coin amount,
Coin txFee,
Coin takerFee,
Coin fundsNeededForTrade,
Offer offer,

View File

@ -29,7 +29,6 @@ import bisq.core.locale.GlobalSettings;
import bisq.core.locale.TradeCurrency;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.PaymentAccountUtil;
import bisq.core.provider.fee.FeeService;
import bisq.core.xmr.MoneroNodeSettings;
import bisq.network.p2p.network.BridgeAddressProvider;
@ -162,7 +161,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
private final PersistenceManager<PreferencesPayload> persistenceManager;
private final Config config;
private final FeeService feeService;
private final LocalBitcoinNode localBitcoinNode;
private final String btcNodesFromOptions;
@Getter
@ -175,13 +173,11 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
@Inject
public Preferences(PersistenceManager<PreferencesPayload> persistenceManager,
Config config,
FeeService feeService,
LocalBitcoinNode localBitcoinNode,
@Named(Config.BTC_NODES) String btcNodesFromOptions) {
this.persistenceManager = persistenceManager;
this.config = config;
this.feeService = feeService;
this.localBitcoinNode = localBitcoinNode;
this.btcNodesFromOptions = btcNodesFromOptions;
@ -837,11 +833,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
return prefPayload.getBridgeAddresses();
}
public long getWithdrawalTxFeeInVbytes() {
return Math.max(prefPayload.getWithdrawalTxFeeInVbytes(),
feeService.getMinFeePerVByte());
}
public List<String> getDefaultXmrTxProofServices() {
if (config.useLocalhostForP2P) {
return XMR_TX_PROOF_SERVICES_CLEAR_NET;

View File

@ -20,8 +20,7 @@ package bisq.core.util.coin;
import bisq.core.btc.wallet.Restrictions;
import bisq.core.monetary.Price;
import bisq.core.monetary.Volume;
import bisq.core.provider.fee.FeeService;
import bisq.core.trade.HavenoUtils;
import bisq.common.util.MathUtils;
import org.bitcoinj.core.Coin;
@ -98,8 +97,8 @@ public class CoinUtil {
@Nullable
public static Coin getMakerFee(@Nullable Coin amount) {
if (amount != null) {
Coin feePerBtc = getFeePerBtc(FeeService.getMakerFeePerBtc(), amount);
return maxCoin(feePerBtc, FeeService.getMinMakerFee());
Coin feePerBtc = getFeePerBtc(HavenoUtils.getMakerFeePerBtc(), amount);
return maxCoin(feePerBtc, HavenoUtils.getMinMakerFee());
} else {
return null;
}

View File

@ -1,141 +0,0 @@
/*
* This file is part of Haveno.
*
* Haveno is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Haveno is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.btc;
import bisq.core.btc.wallet.BtcWalletService;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException;
import java.util.List;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class TxFeeEstimationServiceTest {
@Test
public void testGetEstimatedTxVsize_withDefaultTxVsize() throws InsufficientMoneyException {
List<Coin> outputValues = List.of(Coin.valueOf(2000), Coin.valueOf(3000));
int initialEstimatedTxVsize;
Coin txFeePerVbyte;
BtcWalletService btcWalletService = mock(BtcWalletService.class);
int result;
int realTxVsize;
Coin txFee;
initialEstimatedTxVsize = 175;
txFeePerVbyte = Coin.valueOf(10);
realTxVsize = 175;
txFee = txFeePerVbyte.multiply(initialEstimatedTxVsize);
when(btcWalletService.getEstimatedFeeTxVsize(outputValues, txFee)).thenReturn(realTxVsize);
result = TxFeeEstimationService.getEstimatedTxVsize(outputValues, initialEstimatedTxVsize, txFeePerVbyte, btcWalletService);
assertEquals(175, result);
}
// FIXME @Bernard could you have a look?
@Test
@Ignore
public void testGetEstimatedTxVsize_withLargeTx() throws InsufficientMoneyException {
List<Coin> outputValues = List.of(Coin.valueOf(2000), Coin.valueOf(3000));
int initialEstimatedTxVsize;
Coin txFeePerVbyte;
BtcWalletService btcWalletService = mock(BtcWalletService.class);
int result;
int realTxVsize;
Coin txFee;
initialEstimatedTxVsize = 175;
txFeePerVbyte = Coin.valueOf(10);
realTxVsize = 1750;
txFee = txFeePerVbyte.multiply(initialEstimatedTxVsize);
when(btcWalletService.getEstimatedFeeTxVsize(outputValues, txFee)).thenReturn(realTxVsize);
// repeated calls to getEstimatedFeeTxVsize do not work (returns 0 at second call in loop which cause test to fail)
result = TxFeeEstimationService.getEstimatedTxVsize(outputValues, initialEstimatedTxVsize, txFeePerVbyte, btcWalletService);
assertEquals(1750, result);
}
// FIXME @Bernard could you have a look?
@Test
@Ignore
public void testGetEstimatedTxVsize_withSmallTx() throws InsufficientMoneyException {
List<Coin> outputValues = List.of(Coin.valueOf(2000), Coin.valueOf(3000));
int initialEstimatedTxVsize;
Coin txFeePerVbyte;
BtcWalletService btcWalletService = mock(BtcWalletService.class);
int result;
int realTxVsize;
Coin txFee;
initialEstimatedTxVsize = 1750;
txFeePerVbyte = Coin.valueOf(10);
realTxVsize = 175;
txFee = txFeePerVbyte.multiply(initialEstimatedTxVsize);
when(btcWalletService.getEstimatedFeeTxVsize(outputValues, txFee)).thenReturn(realTxVsize);
result = TxFeeEstimationService.getEstimatedTxVsize(outputValues, initialEstimatedTxVsize, txFeePerVbyte, btcWalletService);
assertEquals(175, result);
}
@Test
public void testIsInTolerance() {
int estimatedSize;
int txVsize;
double tolerance;
boolean result;
estimatedSize = 100;
txVsize = 100;
tolerance = 0.0001;
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
assertTrue(result);
estimatedSize = 100;
txVsize = 200;
tolerance = 0.2;
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
assertFalse(result);
estimatedSize = 120;
txVsize = 100;
tolerance = 0.2;
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
assertTrue(result);
estimatedSize = 200;
txVsize = 100;
tolerance = 1;
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
assertTrue(result);
estimatedSize = 201;
txVsize = 100;
tolerance = 1;
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
assertFalse(result);
}
}

View File

@ -61,7 +61,7 @@ public class PreferencesTest {
Config config = new Config();
LocalBitcoinNode localBitcoinNode = new LocalBitcoinNode(config);
preferences = new Preferences(
persistenceManager, config, null, localBitcoinNode, null);
persistenceManager, config, localBitcoinNode, null);
}
@Test

View File

@ -20,7 +20,6 @@ package bisq.daemon.grpc;
import bisq.common.UserThread;
import bisq.core.api.CoreApi;
import bisq.core.api.model.AddressBalanceInfo;
import bisq.core.api.model.TxFeeRateInfo;
import bisq.proto.grpc.GetAddressBalanceReply;
import bisq.proto.grpc.GetAddressBalanceRequest;
@ -38,26 +37,17 @@ import bisq.proto.grpc.CreateXmrTxRequest;
import bisq.proto.grpc.CreateXmrTxReply;
import bisq.proto.grpc.RelayXmrTxRequest;
import bisq.proto.grpc.RelayXmrTxReply;
import bisq.proto.grpc.GetTransactionReply;
import bisq.proto.grpc.GetTransactionRequest;
import bisq.proto.grpc.GetTxFeeRateReply;
import bisq.proto.grpc.GetTxFeeRateRequest;
import bisq.proto.grpc.GetXmrSeedReply;
import bisq.proto.grpc.GetXmrSeedRequest;
import bisq.proto.grpc.LockWalletReply;
import bisq.proto.grpc.LockWalletRequest;
import bisq.proto.grpc.RemoveWalletPasswordReply;
import bisq.proto.grpc.RemoveWalletPasswordRequest;
import bisq.proto.grpc.SendBtcReply;
import bisq.proto.grpc.SendBtcRequest;
import bisq.proto.grpc.SetTxFeeRatePreferenceReply;
import bisq.proto.grpc.SetTxFeeRatePreferenceRequest;
import bisq.proto.grpc.SetWalletPasswordReply;
import bisq.proto.grpc.SetWalletPasswordRequest;
import bisq.proto.grpc.UnlockWalletReply;
import bisq.proto.grpc.UnlockWalletRequest;
import bisq.proto.grpc.UnsetTxFeeRatePreferenceReply;
import bisq.proto.grpc.UnsetTxFeeRatePreferenceRequest;
import io.grpc.ServerInterceptor;
import io.grpc.stub.StreamObserver;
@ -79,7 +69,6 @@ import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import static bisq.core.api.model.TxInfo.toTxInfo;
import static bisq.core.api.model.XmrTx.toXmrTx;
import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
import static bisq.proto.grpc.WalletsGrpc.*;
@ -247,110 +236,6 @@ class GrpcWalletsService extends WalletsImplBase {
}
}
@Override
public void sendBtc(SendBtcRequest req,
StreamObserver<SendBtcReply> responseObserver) {
try {
coreApi.sendBtc(req.getAddress(),
req.getAmount(),
req.getTxFeeRate(),
req.getMemo(),
new FutureCallback<>() {
@Override
public void onSuccess(Transaction tx) {
if (tx != null) {
log.info("Successfully published BTC tx: id {}, output sum {} sats, fee {} sats, size {} bytes",
tx.getTxId().toString(),
tx.getOutputSum(),
tx.getFee(),
tx.getMessageSize());
var reply = SendBtcReply.newBuilder()
.setTxInfo(toTxInfo(tx).toProtoMessage())
.build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
} else {
throw new IllegalStateException("btc transaction is null");
}
}
@Override
public void onFailure(@NotNull Throwable t) {
log.error("", t);
throw new IllegalStateException(t);
}
});
} catch (Throwable cause) {
exceptionHandler.handleException(log, cause, responseObserver);
}
}
@Override
public void getTxFeeRate(GetTxFeeRateRequest req,
StreamObserver<GetTxFeeRateReply> responseObserver) {
try {
coreApi.getTxFeeRate(() -> {
TxFeeRateInfo txFeeRateInfo = coreApi.getMostRecentTxFeeRateInfo();
var reply = GetTxFeeRateReply.newBuilder()
.setTxFeeRateInfo(txFeeRateInfo.toProtoMessage())
.build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
});
} catch (Throwable cause) {
exceptionHandler.handleException(log, cause, responseObserver);
}
}
@Override
public void setTxFeeRatePreference(SetTxFeeRatePreferenceRequest req,
StreamObserver<SetTxFeeRatePreferenceReply> responseObserver) {
try {
coreApi.setTxFeeRatePreference(req.getTxFeeRatePreference(), () -> {
TxFeeRateInfo txFeeRateInfo = coreApi.getMostRecentTxFeeRateInfo();
var reply = SetTxFeeRatePreferenceReply.newBuilder()
.setTxFeeRateInfo(txFeeRateInfo.toProtoMessage())
.build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
});
} catch (Throwable cause) {
exceptionHandler.handleException(log, cause, responseObserver);
}
}
@Override
public void unsetTxFeeRatePreference(UnsetTxFeeRatePreferenceRequest req,
StreamObserver<UnsetTxFeeRatePreferenceReply> responseObserver) {
try {
coreApi.unsetTxFeeRatePreference(() -> {
TxFeeRateInfo txFeeRateInfo = coreApi.getMostRecentTxFeeRateInfo();
var reply = UnsetTxFeeRatePreferenceReply.newBuilder()
.setTxFeeRateInfo(txFeeRateInfo.toProtoMessage())
.build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
});
} catch (Throwable cause) {
exceptionHandler.handleException(log, cause, responseObserver);
}
}
@Override
public void getTransaction(GetTransactionRequest req,
StreamObserver<GetTransactionReply> responseObserver) {
try {
Transaction tx = coreApi.getTransaction(req.getTxId());
var reply = GetTransactionReply.newBuilder()
.setTxInfo(toTxInfo(tx).toProtoMessage())
.build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
} catch (Throwable cause) {
exceptionHandler.handleException(log, cause, responseObserver);
}
}
@Override
public void setWalletPassword(SetWalletPasswordRequest req,
StreamObserver<SetWalletPasswordReply> responseObserver) {
@ -416,11 +301,6 @@ class GrpcWalletsService extends WalletsImplBase {
put(getGetBalancesMethod().getFullMethodName(), new GrpcCallRateMeter(100, SECONDS)); // TODO: why do tests make so many calls to get balances?
put(getGetAddressBalanceMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
put(getGetFundingAddressesMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
put(getSendBtcMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES));
put(getGetTxFeeRateMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
put(getSetTxFeeRatePreferenceMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
put(getUnsetTxFeeRatePreferenceMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
put(getGetTransactionMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
// Trying to set or remove a wallet password several times before the 1st attempt has time to
// persist the change to disk may corrupt the wallet, so allow only 1 attempt per 5 seconds.

View File

@ -59,7 +59,6 @@ import bisq.core.payment.RevolutAccount;
import bisq.core.presentation.BalancePresentation;
import bisq.core.presentation.SupportTicketsPresentation;
import bisq.core.presentation.TradePresentation;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.TradeManager;
import bisq.core.user.DontShowAgainLookup;
@ -173,7 +172,6 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
WalletPasswordWindow walletPasswordWindow,
NotificationCenter notificationCenter,
TacWindow tacWindow,
FeeService feeService,
PriceFeedService priceFeedService,
Config config,
LocalBitcoinNode localBitcoinNode,
@ -210,7 +208,6 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
TxIdTextField.setXmrWalletService(xmrWalletService);
GUIUtil.setFeeService(feeService);
GUIUtil.setPreferences(preferences);
setupHandlers();

View File

@ -31,7 +31,6 @@ import bisq.core.btc.model.XmrAddressEntry;
import bisq.core.btc.setup.WalletsSetup;
import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.locale.Res;
import bisq.core.provider.fee.FeeService;
import bisq.core.trade.HavenoUtils;
import bisq.core.trade.Trade;
import bisq.core.trade.TradeManager;
@ -97,8 +96,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
WalletsSetup walletsSetup,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
BtcAddressValidator btcAddressValidator,
WalletPasswordWindow walletPasswordWindow,
FeeService feeService) {
WalletPasswordWindow walletPasswordWindow) {
this.xmrWalletService = xmrWalletService;
this.tradeManager = tradeManager;
this.p2PService = p2PService;

View File

@ -22,7 +22,6 @@ import bisq.desktop.util.DisplayUtils;
import bisq.desktop.util.GUIUtil;
import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.btc.TxFeeEstimationService;
import bisq.core.btc.listeners.XmrBalanceListener;
import bisq.core.btc.model.XmrAddressEntry;
import bisq.core.btc.wallet.Restrictions;
@ -37,7 +36,6 @@ import bisq.core.offer.OfferDirection;
import bisq.core.offer.OfferUtil;
import bisq.core.offer.OpenOfferManager;
import bisq.core.payment.PaymentAccount;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.handlers.TransactionResultHandler;
import bisq.core.trade.statistics.TradeStatistics3;
@ -104,7 +102,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
protected final PriceFeedService priceFeedService;
final String shortOfferId;
private final AccountAgeWitnessService accountAgeWitnessService;
private final FeeService feeService;
private final CoinFormatter btcFormatter;
private final Navigation navigation;
private final String offerId;
@ -132,7 +129,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
private Coin txFeeFromFeeService = Coin.ZERO;
@Getter
private boolean marketPriceAvailable;
private int feeTxVsize = TxFeeEstimationService.TYPICAL_TX_WITH_1_INPUT_VSIZE;
protected boolean allowAmountUpdate = true;
private final TradeStatisticsManager tradeStatisticsManager;
@ -157,7 +153,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
P2PService p2PService,
PriceFeedService priceFeedService,
AccountAgeWitnessService accountAgeWitnessService,
FeeService feeService,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
TradeStatisticsManager tradeStatisticsManager,
Navigation navigation) {
@ -171,7 +166,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
this.p2PService = p2PService;
this.priceFeedService = priceFeedService;
this.accountAgeWitnessService = accountAgeWitnessService;
this.feeService = feeService;
this.btcFormatter = btcFormatter;
this.navigation = navigation;
this.tradeStatisticsManager = tradeStatisticsManager;

View File

@ -44,9 +44,9 @@ import bisq.core.payment.payload.PaymentMethod;
import bisq.core.payment.validation.BtcValidator;
import bisq.core.payment.validation.FiatVolumeValidator;
import bisq.core.payment.validation.SecurityDepositValidator;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.MarketPrice;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.HavenoUtils;
import bisq.core.user.Preferences;
import bisq.core.util.FormattingUtils;
import bisq.core.util.ParsingUtils;
@ -999,7 +999,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
dataModel.getMakerFeeInBtc(),
dataModel.getAmount().get(),
btcFormatter,
FeeService.getMinMakerFee());
HavenoUtils.getMinMakerFee());
}
public String getMakerFeePercentage() {

View File

@ -25,7 +25,6 @@ import bisq.core.offer.CreateOfferService;
import bisq.core.offer.OfferUtil;
import bisq.core.offer.OpenOfferManager;
import bisq.core.payment.PaymentAccount;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.statistics.TradeStatisticsManager;
import bisq.core.user.Preferences;
@ -58,7 +57,6 @@ class CreateOfferDataModel extends MutableOfferDataModel {
P2PService p2PService,
PriceFeedService priceFeedService,
AccountAgeWitnessService accountAgeWitnessService,
FeeService feeService,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
TradeStatisticsManager tradeStatisticsManager,
Navigation navigation) {
@ -71,7 +69,6 @@ class CreateOfferDataModel extends MutableOfferDataModel {
p2PService,
priceFeedService,
accountAgeWitnessService,
feeService,
btcFormatter,
tradeStatisticsManager,
navigation);

View File

@ -39,9 +39,9 @@ import bisq.core.offer.OfferUtil;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.PaymentAccountUtil;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.mempool.MempoolService;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.HavenoUtils;
import bisq.core.trade.TradeManager;
import bisq.core.trade.handlers.TradeResultHandler;
import bisq.core.user.Preferences;
@ -86,7 +86,6 @@ class TakeOfferDataModel extends OfferDataModel {
private final TradeManager tradeManager;
private final OfferBook offerBook;
private final User user;
private final FeeService feeService;
private final MempoolService mempoolService;
private final FilterManager filterManager;
final Preferences preferences;
@ -95,9 +94,7 @@ class TakeOfferDataModel extends OfferDataModel {
private final Navigation navigation;
private final P2PService p2PService;
private Coin txFeeFromFeeService;
private Coin securityDeposit;
// Coin feeFromFundingTx = Coin.NEGATIVE_SATOSHI;
private Offer offer;
@ -110,10 +107,6 @@ class TakeOfferDataModel extends OfferDataModel {
private PaymentAccount paymentAccount;
private boolean isTabSelected;
Price tradePrice;
// Use an average of a typical trade fee tx with 1 input, deposit tx and payout tx.
private int feeTxVsize = 192; // (175+233+169)/3
private boolean freezeFee;
private Coin txFeePerVbyteFromFeeService;
@Getter
protected final IntegerProperty mempoolStatus = new SimpleIntegerProperty();
@Getter
@ -130,7 +123,7 @@ class TakeOfferDataModel extends OfferDataModel {
OfferBook offerBook,
OfferUtil offerUtil,
XmrWalletService xmrWalletService,
User user, FeeService feeService,
User user,
MempoolService mempoolService,
FilterManager filterManager,
Preferences preferences,
@ -144,7 +137,6 @@ class TakeOfferDataModel extends OfferDataModel {
this.tradeManager = tradeManager;
this.offerBook = offerBook;
this.user = user;
this.feeService = feeService;
this.mempoolService = mempoolService;
this.filterManager = filterManager;
this.preferences = preferences;
@ -212,39 +204,6 @@ class TakeOfferDataModel extends OfferDataModel {
getBuyerSecurityDeposit() :
getSellerSecurityDeposit();
// Taker pays 3 times the tx fee (taker fee, deposit, payout) because the mining fee might be different when maker created the offer
// and reserved his funds. Taker creates at least taker fee and deposit tx at nearly the same moment. Just the payout will
// be later and still could lead to issues if the required fee changed a lot in the meantime. using RBF and/or
// 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 175 vbytes (if one input). The trade txs has about 169-263 vbytes.
// We use 192 as average value.
// trade fee tx: 175 vbytes (1 input)
// deposit tx: 233 vbytes (1 MS output+ OP_RETURN) - 263 vbytes (1 MS output + OP_RETURN + change in case of smaller trade amount)
// payout tx: 169 vbytes
// disputed payout tx: 139 vbytes
// 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.
txFeePerVbyteFromFeeService = feeService.getTxFeePerVbyte();
txFeeFromFeeService = getTxFeeByVsize(feeTxVsize);
// We request to get the actual estimated fee
log.info("Start requestTxFee: txFeeFromFeeService={}", txFeeFromFeeService);
feeService.requestFees(() -> {
if (!freezeFee) {
txFeePerVbyteFromFeeService = feeService.getTxFeePerVbyte();
txFeeFromFeeService = getTxFeeByVsize(feeTxVsize);
calculateTotalToPay();
log.info("Completed requestTxFee: txFeeFromFeeService={}", txFeeFromFeeService);
} else {
log.debug("We received the tx fee response after we have shown the funding screen and ignore that " +
"to avoid that the total funds to pay changes due changed tx fees.");
}
});
mempoolStatus.setValue(-1);
mempoolService.validateOfferMakerTx(offer.getOfferPayload(), (txValidator -> {
mempoolStatus.setValue(txValidator.isFail() ? 0 : 1);
@ -271,7 +230,6 @@ class TakeOfferDataModel extends OfferDataModel {
// We don't want that the fee gets updated anymore after we show the funding screen.
void onShowPayFundsScreen() {
freezeFee = true;
calculateTotalToPay();
}
@ -303,7 +261,6 @@ class TakeOfferDataModel extends OfferDataModel {
// errorMessageHandler is used only in the check availability phase. As soon we have a trade we write the error msg in the trade object as we want to
// have it persisted as well.
void onTakeOffer(TradeResultHandler tradeResultHandler, ErrorMessageHandler errorMessageHandler) {
checkNotNull(txFeeFromFeeService, "txFeeFromFeeService must not be null");
checkNotNull(getTakerFee(), "takerFee must not be null");
Coin fundsNeededForTrade = getFundsNeededForTrade();
@ -324,7 +281,6 @@ class TakeOfferDataModel extends OfferDataModel {
new Popup().warning(Res.get("offerbook.warning.offerWasAlreadyUsedInTrade")).show();
} else {
tradeManager.onTakeOffer(amount.get(),
txFeeFromFeeService,
getTakerFee(),
fundsNeededForTrade,
offer,
@ -481,8 +437,8 @@ class TakeOfferDataModel extends OfferDataModel {
Coin amount = this.amount.get();
if (amount != null) {
// TODO write unit test for that
Coin feePerBtc = CoinUtil.getFeePerBtc(FeeService.getTakerFeePerBtc(), amount);
return CoinUtil.maxCoin(feePerBtc, FeeService.getMinTakerFee());
Coin feePerBtc = CoinUtil.getFeePerBtc(HavenoUtils.getTakerFeePerBtc(), amount);
return CoinUtil.maxCoin(feePerBtc, HavenoUtils.getMinTakerFee());
} else {
return null;
}
@ -493,18 +449,6 @@ class TakeOfferDataModel extends OfferDataModel {
xmrWalletService.resetAddressEntriesForOpenOffer(offer.getId());
}
// We use the sum of the vsize of the trade fee and the deposit tx to get an average.
// Miners will take the trade fee tx if the total fee of both dependent txs are good enough.
// 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 getAverageVsize(int txVsize) {
return (txVsize + 233) / 2;
}
private Coin getTxFeeByVsize(int vsizeInVbytes) {
return txFeePerVbyteFromFeeService.multiply(getAverageVsize(vsizeInVbytes));
}
/* private void setFeeFromFundingTx(Coin fee) {
feeFromFundingTx = fee;
isFeeFromFundingTxSufficient.set(feeFromFundingTx.compareTo(FeePolicy.getMinRequiredFeeForFundingTx()) >= 0);
@ -557,23 +501,7 @@ class TakeOfferDataModel extends OfferDataModel {
@NotNull
private Coin getFundsNeededForTrade() {
return getSecurityDeposit().add(getTxFeeForDepositTx()).add(getTxFeeForPayoutTx());
}
private Coin getTxFeeForDepositTx() {
//TODO fix with new trade protocol!
// 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(233);
}
private Coin getTxFeeForPayoutTx() {
//TODO fix with new trade protocol!
// 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(169);
return getSecurityDeposit();
}
public XmrAddressEntry getAddressEntry() {

View File

@ -38,7 +38,7 @@ import bisq.core.offer.OfferUtil;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.payment.validation.BtcValidator;
import bisq.core.provider.fee.FeeService;
import bisq.core.trade.HavenoUtils;
import bisq.core.trade.Trade;
import bisq.core.util.FormattingUtils;
import bisq.core.util.VolumeUtil;
@ -692,7 +692,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
dataModel.getTakerFeeInBtc(),
dataModel.getAmount().get(),
btcFormatter,
FeeService.getMinMakerFee());
HavenoUtils.getMinMakerFee());
}
public String getTakerFeePercentage() {

View File

@ -31,7 +31,6 @@ import bisq.core.offer.OfferUtil;
import bisq.core.offer.OpenOfferManager;
import bisq.core.offer.CreateOfferService;
import bisq.core.payment.PaymentAccount;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.statistics.TradeStatisticsManager;
import bisq.core.user.Preferences;
@ -65,7 +64,6 @@ class DuplicateOfferDataModel extends MutableOfferDataModel {
P2PService p2PService,
PriceFeedService priceFeedService,
AccountAgeWitnessService accountAgeWitnessService,
FeeService feeService,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
TradeStatisticsManager tradeStatisticsManager,
Navigation navigation) {
@ -79,7 +77,6 @@ class DuplicateOfferDataModel extends MutableOfferDataModel {
p2PService,
priceFeedService,
accountAgeWitnessService,
feeService,
btcFormatter,
tradeStatisticsManager,
navigation);

View File

@ -35,7 +35,6 @@ import bisq.core.offer.OpenOffer;
import bisq.core.offer.OpenOfferManager;
import bisq.core.payment.PaymentAccount;
import bisq.core.proto.persistable.CorePersistenceProtoResolver;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.statistics.TradeStatisticsManager;
import bisq.core.user.Preferences;
@ -72,7 +71,6 @@ class EditOfferDataModel extends MutableOfferDataModel {
P2PService p2PService,
PriceFeedService priceFeedService,
AccountAgeWitnessService accountAgeWitnessService,
FeeService feeService,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
CorePersistenceProtoResolver corePersistenceProtoResolver,
TradeStatisticsManager tradeStatisticsManager,
@ -87,7 +85,6 @@ class EditOfferDataModel extends MutableOfferDataModel {
p2PService,
priceFeedService,
accountAgeWitnessService,
feeService,
btcFormatter,
tradeStatisticsManager,
navigation);

View File

@ -28,12 +28,12 @@ import bisq.core.btc.wallet.Restrictions;
import bisq.core.network.MessageState;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferUtil;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.mempool.MempoolService;
import bisq.core.trade.ArbitratorTrade;
import bisq.core.trade.BuyerTrade;
import bisq.core.trade.ClosedTradableManager;
import bisq.core.trade.Contract;
import bisq.core.trade.HavenoUtils;
import bisq.core.trade.SellerTrade;
import bisq.core.trade.Trade;
import bisq.core.trade.TradeUtil;
@ -338,8 +338,8 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
Coin tradeFeeInBTC = dataModel.getTradeFeeInBTC();
Coin minTradeFee = dataModel.isMaker() ?
FeeService.getMinMakerFee() :
FeeService.getMinTakerFee();
HavenoUtils.getMinMakerFee() :
HavenoUtils.getMinTakerFee();
String percentage = GUIUtil.getPercentageOfTradeAmount(tradeFeeInBTC, trade.getAmount(),
minTradeFee);

View File

@ -25,7 +25,6 @@ import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.locale.TradeCurrency;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.MarketPrice;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.user.Preferences;
@ -88,17 +87,13 @@ public class MarketPricePresentation {
@Inject
public MarketPricePresentation(XmrWalletService xmrWalletService,
PriceFeedService priceFeedService,
Preferences preferences,
FeeService feeService) {
Preferences preferences) {
this.priceFeedService = priceFeedService;
this.preferences = preferences;
TxIdTextField.setPreferences(preferences);
// TODO
TxIdTextField.setXmrWalletService(xmrWalletService);
GUIUtil.setFeeService(feeService);
}
public void setup() {

View File

@ -17,7 +17,6 @@
package bisq.desktop.main.settings.preferences;
import bisq.desktop.app.HavenoApp;
import bisq.desktop.common.view.ActivatableViewAndModel;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.AutoTooltipButton;
@ -44,7 +43,6 @@ import bisq.core.locale.TradeCurrency;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.payment.validation.BtcValidator;
import bisq.core.provider.fee.FeeService;
import bisq.core.user.Preferences;
import bisq.core.user.User;
import bisq.core.util.FormattingUtils;
@ -121,14 +119,13 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
notifyOnPreReleaseToggle;
private int gridRow = 0;
private int displayCurrenciesGridRowIndex = 0;
private InputTextField transactionFeeInputTextField, ignoreTradersListInputTextField, ignoreDustThresholdInputTextField,
private InputTextField ignoreTradersListInputTextField, ignoreDustThresholdInputTextField,
autoConfRequiredConfirmationsTf, autoConfServiceAddressTf, autoConfTradeLimitTf, /*referralIdInputTextField,*/
rpcUserTextField, blockNotifyPortTextField;
private PasswordTextField rpcPwTextField;
private ChangeListener<Boolean> transactionFeeFocusedListener, autoConfServiceAddressFocusOutListener, autoConfRequiredConfirmationsFocusOutListener;
private ChangeListener<Boolean> autoConfServiceAddressFocusOutListener, autoConfRequiredConfirmationsFocusOutListener;
private final Preferences preferences;
private final FeeService feeService;
//private final ReferralIdService referralIdService;
private final FilterManager filterManager;
private final File storageDir;
@ -150,8 +147,6 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
rpcUserListener, rpcPwListener, blockNotifyPortListener,
autoConfTradeLimitListener, autoConfServiceAddressListener;
private ChangeListener<Boolean> deviationFocusedListener;
private ChangeListener<Boolean> useCustomFeeCheckboxListener;
private ChangeListener<Number> transactionFeeChangeListener;
private final boolean displayStandbyModeFeature;
private ChangeListener<Filter> filterChangeListener;
@ -163,7 +158,6 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
@Inject
public PreferencesView(PreferencesViewModel model,
Preferences preferences,
FeeService feeService,
FilterManager filterManager,
Config config,
User user,
@ -173,7 +167,6 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
this.user = user;
this.formatter = formatter;
this.preferences = preferences;
this.feeService = feeService;
this.filterManager = filterManager;
this.storageDir = storageDir;
this.displayStandbyModeFeature = Utilities.isLinux() || Utilities.isOSX() || Utilities.isWindows();
@ -238,56 +231,6 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
btcExplorerTextField = btcExp.first;
editCustomBtcExplorer = btcExp.second;
Tuple3<Label, InputTextField, ToggleButton> tuple = addTopLabelInputTextFieldSlideToggleButton(root, ++gridRow,
Res.get("setting.preferences.txFee"), Res.get("setting.preferences.useCustomValue"));
transactionFeeInputTextField = tuple.second;
useCustomFee = tuple.third;
useCustomFeeCheckboxListener = (observable, oldValue, newValue) -> {
preferences.setUseCustomWithdrawalTxFee(newValue);
transactionFeeInputTextField.setEditable(newValue);
if (!newValue) {
transactionFeeInputTextField.setText(String.valueOf(feeService.getTxFeePerVbyte().value));
try {
preferences.setWithdrawalTxFeeInVbytes(feeService.getTxFeePerVbyte().value);
} catch (Exception e) {
e.printStackTrace();
}
}
preferences.setUseCustomWithdrawalTxFee(newValue);
};
transactionFeeFocusedListener = (o, oldValue, newValue) -> {
if (oldValue && !newValue) {
String estimatedFee = String.valueOf(feeService.getTxFeePerVbyte().value);
try {
int withdrawalTxFeePerVbyte = Integer.parseInt(transactionFeeInputTextField.getText());
final long minFeePerVbyte = feeService.getMinFeePerVByte();
if (withdrawalTxFeePerVbyte < minFeePerVbyte) {
new Popup().warning(Res.get("setting.preferences.txFeeMin", minFeePerVbyte)).show();
transactionFeeInputTextField.setText(estimatedFee);
} else if (withdrawalTxFeePerVbyte > 5000) {
new Popup().warning(Res.get("setting.preferences.txFeeTooLarge")).show();
transactionFeeInputTextField.setText(estimatedFee);
} else {
preferences.setWithdrawalTxFeeInVbytes(withdrawalTxFeePerVbyte);
}
} catch (NumberFormatException t) {
log.error(t.toString());
t.printStackTrace();
new Popup().warning(Res.get("validation.integerOnly")).show();
transactionFeeInputTextField.setText(estimatedFee);
} catch (Throwable t) {
log.error(t.toString());
t.printStackTrace();
new Popup().warning(Res.get("validation.inputError", t.getMessage())).show();
transactionFeeInputTextField.setText(estimatedFee);
}
}
};
transactionFeeChangeListener = (observable, oldValue, newValue) -> transactionFeeInputTextField.setText(String.valueOf(feeService.getTxFeePerVbyte().value));
// deviation
deviationInputTextField = addInputTextField(root, ++gridRow,
Res.get("setting.preferences.deviation"));
@ -693,15 +636,6 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
///////////////////////////////////////////////////////////////////////////////////////////
private void activateGeneralOptions() {
boolean useCustomWithdrawalTxFee = preferences.isUseCustomWithdrawalTxFee();
useCustomFee.setSelected(useCustomWithdrawalTxFee);
transactionFeeInputTextField.setEditable(useCustomWithdrawalTxFee);
if (!useCustomWithdrawalTxFee) {
transactionFeeInputTextField.setText(String.valueOf(feeService.getTxFeePerVbyte().value));
feeService.feeUpdateCounterProperty().addListener(transactionFeeChangeListener);
}
transactionFeeInputTextField.setText(String.valueOf(getTxFeeForWithdrawalPerVbyte()));
ignoreTradersListInputTextField.setText(String.join(", ", preferences.getIgnoreTradersList()));
/* referralIdService.getOptionalReferralId().ifPresent(referralId -> referralIdInputTextField.setText(referralId));
referralIdInputTextField.setPromptText(Res.get("setting.preferences.refererId.prompt"));*/
@ -764,21 +698,11 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
deviationInputTextField.textProperty().addListener(deviationListener);
deviationInputTextField.focusedProperty().addListener(deviationFocusedListener);
transactionFeeInputTextField.focusedProperty().addListener(transactionFeeFocusedListener);
ignoreTradersListInputTextField.textProperty().addListener(ignoreTradersListListener);
useCustomFee.selectedProperty().addListener(useCustomFeeCheckboxListener);
//referralIdInputTextField.textProperty().addListener(referralIdListener);
ignoreDustThresholdInputTextField.textProperty().addListener(ignoreDustThresholdListener);
}
private Coin getTxFeeForWithdrawalPerVbyte() {
Coin fee = (preferences.isUseCustomWithdrawalTxFee()) ?
Coin.valueOf(preferences.getWithdrawalTxFeeInVbytes()) :
feeService.getTxFeePerVbyte();
log.info("tx fee = " + fee.toFriendlyString());
return fee;
}
private void activateDisplayCurrencies() {
preferredTradeCurrencyComboBox.setItems(tradeCurrencies);
preferredTradeCurrencyComboBox.getSelectionModel().select(preferences.getPreferredTradeCurrency());
@ -897,11 +821,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
editCustomBtcExplorer.setOnAction(null);
deviationInputTextField.textProperty().removeListener(deviationListener);
deviationInputTextField.focusedProperty().removeListener(deviationFocusedListener);
transactionFeeInputTextField.focusedProperty().removeListener(transactionFeeFocusedListener);
if (transactionFeeChangeListener != null)
feeService.feeUpdateCounterProperty().removeListener(transactionFeeChangeListener);
ignoreTradersListInputTextField.textProperty().removeListener(ignoreTradersListListener);
useCustomFee.selectedProperty().removeListener(useCustomFeeCheckboxListener);
//referralIdInputTextField.textProperty().removeListener(referralIdListener);
ignoreDustThresholdInputTextField.textProperty().removeListener(ignoreDustThresholdListener);
}

View File

@ -41,7 +41,6 @@ import bisq.core.locale.TradeCurrency;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.PaymentAccountList;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.provider.fee.FeeService;
import bisq.core.trade.HavenoUtils;
import bisq.core.trade.txproof.AssetTxProofResult;
import bisq.core.user.DontShowAgainLookup;
@ -160,15 +159,10 @@ public class GUIUtil {
public final static int AMOUNT_DECIMALS_WITH_ZEROS = 3;
public final static int AMOUNT_DECIMALS = 4;
private static FeeService feeService;
private static Preferences preferences;
public static TradeCurrency TOP_ALTCOIN = CurrencyUtil.getTradeCurrency("ETH").get();
public static void setFeeService(FeeService feeService) {
GUIUtil.feeService = feeService;
}
public static void setPreferences(Preferences preferences) {
GUIUtil.preferences = preferences;
}

View File

@ -12,7 +12,6 @@ import bisq.core.offer.OfferUtil;
import bisq.core.payment.ClearXchangeAccount;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.RevolutAccount;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.statistics.TradeStatisticsManager;
import bisq.core.user.Preferences;
@ -50,7 +49,6 @@ public class CreateOfferDataModelTest {
XmrAddressEntry addressEntry = mock(XmrAddressEntry.class);
XmrWalletService btcWalletService = mock(XmrWalletService.class);
PriceFeedService priceFeedService = mock(PriceFeedService.class);
FeeService feeService = mock(FeeService.class);
CreateOfferService createOfferService = mock(CreateOfferService.class);
preferences = mock(Preferences.class);
offerUtil = mock(OfferUtil.class);
@ -72,7 +70,6 @@ public class CreateOfferDataModelTest {
null,
priceFeedService,
null,
feeService,
null,
tradeStats,
null);

View File

@ -32,7 +32,6 @@ import bisq.core.payment.PaymentAccount;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.payment.validation.BtcValidator;
import bisq.core.payment.validation.SecurityDepositValidator;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.MarketPrice;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.statistics.TradeStatisticsManager;
@ -84,7 +83,6 @@ public class CreateOfferViewModelTest {
final AltcoinValidator altcoinValidator = new AltcoinValidator();
final FiatPriceValidator fiatPriceValidator = new FiatPriceValidator();
FeeService feeService = mock(FeeService.class);
XmrAddressEntry addressEntry = mock(XmrAddressEntry.class);
XmrWalletService xmrWalletService = mock(XmrWalletService.class);
PriceFeedService priceFeedService = mock(PriceFeedService.class);
@ -105,7 +103,6 @@ public class CreateOfferViewModelTest {
12684.0450,
Instant.now().getEpochSecond(),
true));
when(feeService.getTxFee(anyInt())).thenReturn(Coin.valueOf(1000L));
when(user.findFirstPaymentAccountWithCurrency(any())).thenReturn(paymentAccount);
when(paymentAccount.getPaymentMethod()).thenReturn(mock(PaymentMethod.class));
when(user.getPaymentAccountsAsObservable()).thenReturn(FXCollections.observableSet());
@ -124,7 +121,6 @@ public class CreateOfferViewModelTest {
null,
priceFeedService,
accountAgeWitnessService,
feeService,
coinFormatter,
tradeStats,
null);

View File

@ -18,7 +18,6 @@
package bisq.desktop.maker;
import bisq.core.btc.nodes.LocalBitcoinNode;
import bisq.core.provider.fee.FeeService;
import bisq.core.user.Preferences;
import bisq.common.config.Config;
@ -35,7 +34,6 @@ public class PreferenceMakers {
public static final Property<Preferences, PersistenceManager> storage = new Property<>();
public static final Property<Preferences, Config> config = new Property<>();
public static final Property<Preferences, FeeService> feeService = new Property<>();
public static final Property<Preferences, LocalBitcoinNode> localBitcoinNode = new Property<>();
public static final Property<Preferences, String> useTorFlagFromOptions = new Property<>();
public static final Property<Preferences, String> referralID = new Property<>();
@ -43,7 +41,6 @@ public class PreferenceMakers {
public static final Instantiator<Preferences> Preferences = lookup -> new Preferences(
lookup.valueOf(storage, new SameValueDonor<PersistenceManager>(null)),
lookup.valueOf(config, new SameValueDonor<Config>(null)),
lookup.valueOf(feeService, new SameValueDonor<FeeService>(null)),
lookup.valueOf(localBitcoinNode, new SameValueDonor<LocalBitcoinNode>(null)),
lookup.valueOf(useTorFlagFromOptions, new SameValueDonor<String>(null))
);

View File

@ -874,27 +874,6 @@ message ContractInfo {
string arbitrator_node_address = 100;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Transactions
///////////////////////////////////////////////////////////////////////////////////////////
message TxFeeRateInfo {
bool use_custom_tx_fee_rate = 1;
uint64 custom_tx_fee_rate = 2;
uint64 fee_service_rate = 3;
uint64 last_fee_service_request_ts = 4;
uint64 min_fee_service_rate = 5;
}
message TxInfo {
string tx_id = 1;
uint64 input_sum = 2;
uint64 output_sum = 3;
uint64 fee = 4;
int32 size = 5;
bool is_pending = 6;
string memo = 7;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Wallets
@ -917,16 +896,6 @@ service Wallets {
}
rpc GetAddressBalance (GetAddressBalanceRequest) returns (GetAddressBalanceReply) {
}
rpc SendBtc (SendBtcRequest) returns (SendBtcReply) {
}
rpc GetTxFeeRate (GetTxFeeRateRequest) returns (GetTxFeeRateReply) {
}
rpc SetTxFeeRatePreference (SetTxFeeRatePreferenceRequest) returns (SetTxFeeRatePreferenceReply) {
}
rpc UnsetTxFeeRatePreference (UnsetTxFeeRatePreferenceRequest) returns (UnsetTxFeeRatePreferenceReply) {
}
rpc GetTransaction (GetTransactionRequest) returns (GetTransactionReply) {
}
rpc GetFundingAddresses (GetFundingAddressesRequest) returns (GetFundingAddressesReply) {
}
rpc SetWalletPassword (SetWalletPasswordRequest) returns (SetWalletPasswordReply) {
@ -1038,40 +1007,6 @@ message SendBtcRequest {
string memo = 4;
}
message SendBtcReply {
TxInfo tx_info = 1;
}
message GetTxFeeRateRequest {
}
message GetTxFeeRateReply {
TxFeeRateInfo tx_fee_rate_info = 1;
}
message SetTxFeeRatePreferenceRequest {
uint64 tx_fee_rate_preference = 1;
}
message SetTxFeeRatePreferenceReply {
TxFeeRateInfo tx_fee_rate_info = 1;
}
message UnsetTxFeeRatePreferenceRequest {
}
message UnsetTxFeeRatePreferenceReply {
TxFeeRateInfo tx_fee_rate_info = 1;
}
message GetTransactionRequest {
string tx_id = 1;
}
message GetTransactionReply {
TxInfo tx_info = 1;
}
message GetFundingAddressesRequest {
}