From 06eded55378e91252c138c79b18b88cddf5cca6f Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 2 Jan 2023 19:16:13 -0500 Subject: [PATCH 01/12] Add listaddresses rpc call --- .../wallets/electrum/rpc/ElectrumDaemon.java | 6 +++ .../calls/ElectrumListAddressesRpcCall.java | 42 +++++++++++++++++++ .../ElectrumListAddressesResponse.java | 27 ++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/calls/ElectrumListAddressesRpcCall.java create mode 100644 wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/responses/ElectrumListAddressesResponse.java diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/ElectrumDaemon.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/ElectrumDaemon.java index c12ec63647..c69a646000 100644 --- a/wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/ElectrumDaemon.java +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/ElectrumDaemon.java @@ -154,4 +154,10 @@ public boolean verifyMessage(String address, String signature, String message) { var rpcCall = new ElectrumVerifyMessageRpcCall(request); return rpcClient.call(rpcCall).getResult(); } + + public List listAddresses() { + var rpcCall = new ElectrumListAddressesRpcCall(); + ElectrumListAddressesResponse call = rpcClient.call(rpcCall); + return call.getResult(); + } } diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/calls/ElectrumListAddressesRpcCall.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/calls/ElectrumListAddressesRpcCall.java new file mode 100644 index 0000000000..5e60d9de62 --- /dev/null +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/calls/ElectrumListAddressesRpcCall.java @@ -0,0 +1,42 @@ +/* + * This file is part of Bisq. + * + * Bisq 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. + * + * Bisq 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 Bisq. If not, see . + */ + +package bisq.wallets.electrum.rpc.calls; + +import bisq.wallets.electrum.rpc.responses.ElectrumListAddressesResponse; +import bisq.wallets.json_rpc.DaemonRpcCall; + +public class ElectrumListAddressesRpcCall extends DaemonRpcCall { + public ElectrumListAddressesRpcCall() { + super(null); + } + + @Override + public String getRpcMethodName() { + return "listaddresses"; + } + + @Override + public boolean isResponseValid(ElectrumListAddressesResponse response) { + return true; + } + + @Override + public Class getRpcResponseClass() { + return ElectrumListAddressesResponse.class; + } +} diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/responses/ElectrumListAddressesResponse.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/responses/ElectrumListAddressesResponse.java new file mode 100644 index 0000000000..f8cc42cbed --- /dev/null +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/responses/ElectrumListAddressesResponse.java @@ -0,0 +1,27 @@ +/* + * This file is part of Bisq. + * + * Bisq 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. + * + * Bisq 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 Bisq. If not, see . + */ + +package bisq.wallets.electrum.rpc.responses; + +import bisq.wallets.json_rpc.JsonRpcResponse; +import lombok.Getter; + +import java.util.List; + +@Getter +public class ElectrumListAddressesResponse extends JsonRpcResponse> { +} From 31d5b1cb5df217ef16be48e768aae80997a8d814 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 2 Jan 2023 21:45:16 -0500 Subject: [PATCH 02/12] Add listaddressgroupings rpc call --- .../BitcoindListAddressGroupingsRpcCall.java | 43 +++++++++++++++++++ .../BitcoindListAddressGroupingsResponse.java | 25 +++++++++++ 2 files changed, 68 insertions(+) create mode 100644 wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/calls/BitcoindListAddressGroupingsRpcCall.java create mode 100644 wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/responses/BitcoindListAddressGroupingsResponse.java diff --git a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/calls/BitcoindListAddressGroupingsRpcCall.java b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/calls/BitcoindListAddressGroupingsRpcCall.java new file mode 100644 index 0000000000..c0127d6bf0 --- /dev/null +++ b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/calls/BitcoindListAddressGroupingsRpcCall.java @@ -0,0 +1,43 @@ +/* + * This file is part of Bisq. + * + * Bisq 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. + * + * Bisq 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 Bisq. If not, see . + */ + +package bisq.wallets.bitcoind.rpc.calls; + +import bisq.wallets.bitcoind.rpc.responses.BitcoindListAddressGroupingsResponse; +import bisq.wallets.json_rpc.DaemonRpcCall; + +public class BitcoindListAddressGroupingsRpcCall extends DaemonRpcCall { + + public BitcoindListAddressGroupingsRpcCall() { + super(null); + } + + @Override + public String getRpcMethodName() { + return "listaddressgroupings"; + } + + @Override + public boolean isResponseValid(BitcoindListAddressGroupingsResponse response) { + return true; + } + + @Override + public Class getRpcResponseClass() { + return BitcoindListAddressGroupingsResponse.class; + } +} diff --git a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/responses/BitcoindListAddressGroupingsResponse.java b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/responses/BitcoindListAddressGroupingsResponse.java new file mode 100644 index 0000000000..0aa232a2f1 --- /dev/null +++ b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/responses/BitcoindListAddressGroupingsResponse.java @@ -0,0 +1,25 @@ +/* + * This file is part of Bisq. + * + * Bisq 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. + * + * Bisq 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 Bisq. If not, see . + */ + +package bisq.wallets.bitcoind.rpc.responses; + +import bisq.wallets.json_rpc.JsonRpcResponse; + +import java.util.List; + +public class BitcoindListAddressGroupingsResponse extends JsonRpcResponse> { +} From e959f6a4e1676a62b5b023019c0f76af4ec697d9 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 2 Jan 2023 21:58:27 -0500 Subject: [PATCH 03/12] Use walletAddresses for monitoring to also listen on old addresses. Let ElectrumWalletService implement WalletService. Add methods to WalletService. Rename receiveAddresses to walletAddresses. Add Transaction model classes (not used yet). Various refactorings --- .../dashboard/WalletDashboardController.java | 4 +- .../wallet/txs/WalletTransactionListItem.java | 4 +- .../primary/main/top/TopPanelController.java | 2 +- .../AbstractBitcoindWalletService.java | 34 +++-- .../bisq/wallets/bitcoind/BitcoinWallet.java | 9 +- .../bitcoind/BitcoinWalletService.java | 20 ++- .../wallets/bitcoind/rpc/BitcoindWallet.java | 5 + .../BitcoindListTransactionsResponse.java | 4 +- .../main/java/bisq/wallets/core/Wallet.java | 7 +- .../java/bisq/wallets/core/WalletService.java | 18 ++- .../bisq/wallets/core/model/Transaction.java | 37 ++++- .../wallets/core/model/TransactionInfo.java | 32 ++++ .../wallets/core/model/TransactionInput.java | 39 +++++ .../wallets/core/model/TransactionOutput.java | 35 +++++ .../java/bisq/wallets/core/model/Utxo.java | 1 - .../bisq/wallets/electrum/ElectrumWallet.java | 14 +- .../electrum/ElectrumWalletService.java | 141 +++++++++++------- .../ElectrumOnChainTransactionResponse.java | 4 +- .../bisq/wallets/elementsd/LiquidWallet.java | 13 +- .../elementsd/LiquidWalletService.java | 11 ++ .../wallets/elementsd/LiquidWalletStore.java | 22 +-- .../src/main/proto/wallets.elementsd.proto | 2 +- 22 files changed, 331 insertions(+), 127 deletions(-) create mode 100644 wallets/core/src/main/java/bisq/wallets/core/model/TransactionInfo.java create mode 100644 wallets/core/src/main/java/bisq/wallets/core/model/TransactionInput.java create mode 100644 wallets/core/src/main/java/bisq/wallets/core/model/TransactionOutput.java diff --git a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/dashboard/WalletDashboardController.java b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/dashboard/WalletDashboardController.java index 9510d8ff2a..d34f627599 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/dashboard/WalletDashboardController.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/dashboard/WalletDashboardController.java @@ -45,9 +45,9 @@ public WalletDashboardController(DefaultApplicationService applicationService) { @Override public void onActivate() { balancePin = FxBindings.bind(model.getBalanceAsCoinProperty()) - .to(electrumWalletService.getObservableBalanceAsCoin()); + .to(electrumWalletService.getBalance()); - electrumWalletService.getBalance().whenComplete((balance, throwable) -> { + electrumWalletService.requestBalance().whenComplete((balance, throwable) -> { if (throwable == null) { UIThread.run(() -> model.getBalanceAsCoinProperty().set(balance)); } diff --git a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTransactionListItem.java b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTransactionListItem.java index d0319696e7..2744c90f48 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTransactionListItem.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTransactionListItem.java @@ -20,7 +20,7 @@ import bisq.desktop.components.table.TableItem; import bisq.presentation.formatters.AmountFormatter; import bisq.presentation.formatters.DateFormatter; -import bisq.wallets.core.model.Transaction; +import bisq.wallets.core.model.TransactionInfo; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -32,7 +32,7 @@ public class WalletTransactionListItem implements TableItem { private final String amount; private final String confirmations; - public WalletTransactionListItem(Transaction transaction) { + public WalletTransactionListItem(TransactionInfo transaction) { date = DateFormatter.formatDateTime(transaction.getDate()); txId = transaction.getTxId(); amount = AmountFormatter.formatAmount(transaction.getAmount()); diff --git a/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelController.java b/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelController.java index c4be4786b7..b68bfe9b4f 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelController.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelController.java @@ -45,7 +45,7 @@ public TopPanelController(DefaultApplicationService applicationService) { @Override public void onActivate() { balancePin = FxBindings.bind(model.getBalanceAsCoinProperty()) - .to(electrumWalletService.getObservableBalanceAsCoin()); + .to(electrumWalletService.getBalance()); } @Override diff --git a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/AbstractBitcoindWalletService.java b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/AbstractBitcoindWalletService.java index 2c325bcfa7..41729a568a 100644 --- a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/AbstractBitcoindWalletService.java +++ b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/AbstractBitcoindWalletService.java @@ -19,7 +19,7 @@ import bisq.common.monetary.Coin; import bisq.common.observable.Observable; -import bisq.common.observable.ObservableSet; +import bisq.common.observable.ObservableArray; import bisq.persistence.PersistableStore; import bisq.persistence.PersistenceClient; import bisq.wallets.bitcoind.rpc.BitcoindDaemon; @@ -29,7 +29,7 @@ import bisq.wallets.core.Wallet; import bisq.wallets.core.WalletService; import bisq.wallets.core.exceptions.WalletNotInitializedException; -import bisq.wallets.core.model.Transaction; +import bisq.wallets.core.model.TransactionInfo; import bisq.wallets.core.model.Utxo; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -67,6 +67,8 @@ public static Optional getOptionalRegtestConfig(boolean isRegtest, in protected Optional wallet = Optional.empty(); protected Optional zmqConnection = Optional.empty(); protected final Set utxoTxIds = new HashSet<>(); + @Getter + protected final ObservableArray walletAddresses = new ObservableArray<>(); public AbstractBitcoindWalletService(String currencyCode, Optional optionalRpcConfig, @@ -128,8 +130,8 @@ public CompletableFuture initializeWallet(RpcConfig rpcConfig, Optional wallet.initialize(walletPassphrase); ZmqConnection zmqConnection = wallet.getZmqConnection(); - ObservableSet receiveAddresses = wallet.getReceiveAddresses(); - initializeZmqListeners(zmqConnection, receiveAddresses); + walletAddresses.addAll(wallet.getWalletAddresses()); + initializeZmqListeners(zmqConnection, walletAddresses); this.zmqConnection = Optional.of(zmqConnection); log.info("Successfully created/loaded wallet at {}", walletName); @@ -158,15 +160,19 @@ public CompletableFuture getNewAddress() { } @Override - public ObservableSet getReceiveAddresses() { + public CompletableFuture> requestWalletAddresses() { if (wallet.isEmpty()) { - return new ObservableSet<>(); + return CompletableFuture.completedFuture(walletAddresses); + } else { + return CompletableFuture.supplyAsync(() -> { + walletAddresses.addAll(wallet.get().getWalletAddresses()); + return walletAddresses; + }); } - return wallet.get().getReceiveAddresses(); } @Override - public CompletableFuture> listTransactions() { + public CompletableFuture> listTransactions() { return CompletableFuture.supplyAsync(() -> { Wallet wallet = getWalletOrThrowException(); return wallet.listTransactions(); @@ -190,14 +196,12 @@ public CompletableFuture sendToAddress(Optional passphrase, Stri } @Override - public CompletableFuture signMessage(Optional passphrase, String address, String message) { - return CompletableFuture.supplyAsync(() -> { - Wallet wallet = getWalletOrThrowException(); - return wallet.signMessage(passphrase, address, message); - }); + public CompletableFuture isWalletEncrypted() { + //todo implement + return CompletableFuture.supplyAsync(() -> true); } - protected void initializeZmqListeners(ZmqConnection zmqConnection, ObservableSet receiveAddresses) { + protected void initializeZmqListeners(ZmqConnection zmqConnection, List walletAddresses) { // Update balance when new block gets mined zmqConnection.getListeners().registerNewBlockMinedListener(unused -> updateBalance()); @@ -210,7 +214,7 @@ protected void initializeZmqListeners(ZmqConnection zmqConnection, ObservableSet // Update balance if a receive address is in tx output zmqConnection.getListeners().registerTxOutputAddressesListener(addresses -> { - boolean receiveAddressInTxOutput = addresses.stream().anyMatch(receiveAddresses::contains); + boolean receiveAddressInTxOutput = addresses.stream().anyMatch(walletAddresses::contains); if (receiveAddressInTxOutput) { updateBalance(); } diff --git a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWallet.java b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWallet.java index bac5bf7670..4be062fe2f 100644 --- a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWallet.java +++ b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWallet.java @@ -25,7 +25,7 @@ import bisq.wallets.core.RpcConfig; import bisq.wallets.core.Wallet; import bisq.wallets.core.model.AddressType; -import bisq.wallets.core.model.Transaction; +import bisq.wallets.core.model.TransactionInfo; import bisq.wallets.core.model.Utxo; import lombok.Getter; @@ -74,13 +74,18 @@ public String getNewAddress() { return newAddress; } + @Override + public List getWalletAddresses() { + return wallet.listAddressGroupings(); + } + @Override public String signMessage(Optional passphrase, String address, String message) { return wallet.signMessage(passphrase, address, message); } @Override - public List listTransactions() { + public List listTransactions() { return wallet.listTransactions(1000); } diff --git a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWalletService.java b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWalletService.java index 7a1c8d9542..ebf3567e62 100644 --- a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWalletService.java +++ b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWalletService.java @@ -17,6 +17,8 @@ package bisq.wallets.bitcoind; +import bisq.common.monetary.Coin; +import bisq.common.observable.Observable; import bisq.persistence.Persistence; import bisq.persistence.PersistenceService; import bisq.wallets.core.RpcConfig; @@ -24,6 +26,7 @@ import lombok.extern.slf4j.Slf4j; import java.util.Optional; +import java.util.concurrent.CompletableFuture; @Slf4j public class BitcoinWalletService extends AbstractBitcoindWalletService { @@ -32,6 +35,8 @@ public class BitcoinWalletService extends AbstractBitcoindWalletService persistence; + @Getter + private final Observable balance = new Observable<>(Coin.asBtc(0)); public BitcoinWalletService(PersistenceService persistenceService, boolean isRegtest) { @@ -44,7 +49,6 @@ protected BitcoinWallet createWallet(RpcConfig rpcConfig) { return WalletFactory.createBitcoinWallet(rpcConfig, walletName, persistableStore); } - @Override protected void persistRpcConfig(RpcConfig rpcConfig) { persistableStore.setRpcConfig(Optional.of(rpcConfig)); @@ -55,4 +59,18 @@ protected void persistRpcConfig(RpcConfig rpcConfig) { protected Optional getRpcConfigFromPersistableStore() { return persistableStore.getRpcConfig(); } + + @Override + public CompletableFuture requestBalance() { + if (wallet.isEmpty()) { + return CompletableFuture.completedFuture(Coin.asBtc(0)); + } else { + return CompletableFuture.supplyAsync(() -> { + double balance = wallet.get().getBalance(); + Coin balanceAsCoin = Coin.asBtc(balance); + this.balance.set(balanceAsCoin); + return balanceAsCoin; + }); + } + } } diff --git a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/BitcoindWallet.java b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/BitcoindWallet.java index 54bdbfabd3..5c7f2950e0 100644 --- a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/BitcoindWallet.java +++ b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/BitcoindWallet.java @@ -99,6 +99,11 @@ public String getNewAddress(AddressType addressType, String label) { return rpcClient.call(rpcCall).getResult(); } + public List listAddressGroupings() { + var call = new BitcoindListAddressGroupingsRpcCall(); + return rpcClient.call(call).getResult(); + } + public List importDescriptors( List requests ) { diff --git a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/responses/BitcoindListTransactionsResponse.java b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/responses/BitcoindListTransactionsResponse.java index bbb1f198df..831685b3a8 100644 --- a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/responses/BitcoindListTransactionsResponse.java +++ b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/responses/BitcoindListTransactionsResponse.java @@ -18,7 +18,7 @@ package bisq.wallets.bitcoind.rpc.responses; import bisq.common.monetary.Coin; -import bisq.wallets.core.model.Transaction; +import bisq.wallets.core.model.TransactionInfo; import bisq.wallets.json_rpc.JsonRpcResponse; import com.squareup.moshi.Json; import lombok.Getter; @@ -28,7 +28,7 @@ public class BitcoindListTransactionsResponse extends JsonRpcResponse> { @Getter - public static class Entry implements Transaction { + public static class Entry implements TransactionInfo { private boolean involvesWatchonly; private String address; private String category; diff --git a/wallets/core/src/main/java/bisq/wallets/core/Wallet.java b/wallets/core/src/main/java/bisq/wallets/core/Wallet.java index be6975155f..ecbe00814b 100644 --- a/wallets/core/src/main/java/bisq/wallets/core/Wallet.java +++ b/wallets/core/src/main/java/bisq/wallets/core/Wallet.java @@ -17,8 +17,7 @@ package bisq.wallets.core; -import bisq.common.observable.ObservableSet; -import bisq.wallets.core.model.Transaction; +import bisq.wallets.core.model.TransactionInfo; import bisq.wallets.core.model.Utxo; import java.util.List; @@ -33,9 +32,9 @@ public interface Wallet { String getNewAddress(); - ObservableSet getReceiveAddresses(); + List getWalletAddresses(); - List listTransactions(); + List listTransactions(); List listUnspent(); diff --git a/wallets/core/src/main/java/bisq/wallets/core/WalletService.java b/wallets/core/src/main/java/bisq/wallets/core/WalletService.java index b3c1b57a6b..06c908e973 100644 --- a/wallets/core/src/main/java/bisq/wallets/core/WalletService.java +++ b/wallets/core/src/main/java/bisq/wallets/core/WalletService.java @@ -18,8 +18,10 @@ package bisq.wallets.core; import bisq.common.application.Service; -import bisq.common.observable.ObservableSet; -import bisq.wallets.core.model.Transaction; +import bisq.common.monetary.Coin; +import bisq.common.observable.Observable; +import bisq.common.observable.ObservableArray; +import bisq.wallets.core.model.TransactionInfo; import bisq.wallets.core.model.Utxo; import java.util.List; @@ -34,13 +36,19 @@ public interface WalletService extends Service { CompletableFuture getNewAddress(); - ObservableSet getReceiveAddresses(); + ObservableArray getWalletAddresses(); - CompletableFuture signMessage(Optional passphrase, String address, String message); + CompletableFuture> requestWalletAddresses(); - CompletableFuture> listTransactions(); + CompletableFuture> listTransactions(); CompletableFuture> listUnspent(); CompletableFuture sendToAddress(Optional passphrase, String address, double amount); + + CompletableFuture isWalletEncrypted(); + + CompletableFuture requestBalance(); + + Observable getBalance(); } diff --git a/wallets/core/src/main/java/bisq/wallets/core/model/Transaction.java b/wallets/core/src/main/java/bisq/wallets/core/model/Transaction.java index 105823a9d4..5328adefe3 100644 --- a/wallets/core/src/main/java/bisq/wallets/core/model/Transaction.java +++ b/wallets/core/src/main/java/bisq/wallets/core/model/Transaction.java @@ -17,16 +17,37 @@ package bisq.wallets.core.model; -import bisq.common.monetary.Coin; +import lombok.EqualsAndHashCode; +import lombok.Getter; import java.util.Date; +import java.util.List; -public interface Transaction { - String getTxId(); +@Getter +@EqualsAndHashCode +public class Transaction { + private final String txId; + private final List inputs; + private final List outputs; + private final int lockTime; + private final int height; + private final Date date; + private final int confirmations; - Coin getAmount(); + public Transaction(String txId, + List inputs, + List outputs, + int lockTime, + int height, + Date date, + int confirmations) { - int getConfirmations(); - - Date getDate(); -} + this.txId = txId; + this.inputs = inputs; + this.outputs = outputs; + this.lockTime = lockTime; + this.height = height; + this.date = date; + this.confirmations = confirmations; + } +} \ No newline at end of file diff --git a/wallets/core/src/main/java/bisq/wallets/core/model/TransactionInfo.java b/wallets/core/src/main/java/bisq/wallets/core/model/TransactionInfo.java new file mode 100644 index 0000000000..f299c70c8a --- /dev/null +++ b/wallets/core/src/main/java/bisq/wallets/core/model/TransactionInfo.java @@ -0,0 +1,32 @@ +/* + * This file is part of Bisq. + * + * Bisq 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. + * + * Bisq 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 Bisq. If not, see . + */ + +package bisq.wallets.core.model; + +import bisq.common.monetary.Coin; + +import java.util.Date; + +public interface TransactionInfo { + String getTxId(); + + Coin getAmount(); + + int getConfirmations(); + + Date getDate(); +} diff --git a/wallets/core/src/main/java/bisq/wallets/core/model/TransactionInput.java b/wallets/core/src/main/java/bisq/wallets/core/model/TransactionInput.java new file mode 100644 index 0000000000..c2c3635eb2 --- /dev/null +++ b/wallets/core/src/main/java/bisq/wallets/core/model/TransactionInput.java @@ -0,0 +1,39 @@ +/* + * This file is part of Bisq. + * + * Bisq 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. + * + * Bisq 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 Bisq. If not, see . + */ + +package bisq.wallets.core.model; + +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Getter +@EqualsAndHashCode +public class TransactionInput { + private final String prevOutTxId; + private final int prevOutIndex; + private final long sequence; + private final String scriptSig; + private final String witness; + + public TransactionInput(String prevOutTxId, int prevOutIndex, long sequence, String scriptSig, String witness) { + this.prevOutTxId = prevOutTxId; + this.prevOutIndex = prevOutIndex; + this.sequence = sequence; + this.scriptSig = scriptSig; + this.witness = witness; + } +} \ No newline at end of file diff --git a/wallets/core/src/main/java/bisq/wallets/core/model/TransactionOutput.java b/wallets/core/src/main/java/bisq/wallets/core/model/TransactionOutput.java new file mode 100644 index 0000000000..85fdc0ce98 --- /dev/null +++ b/wallets/core/src/main/java/bisq/wallets/core/model/TransactionOutput.java @@ -0,0 +1,35 @@ +/* + * This file is part of Bisq. + * + * Bisq 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. + * + * Bisq 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 Bisq. If not, see . + */ + +package bisq.wallets.core.model; + +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Getter +@EqualsAndHashCode +public class TransactionOutput { + private final long value; + private final String address; + private final String scriptPubKey; + + public TransactionOutput(long value, String address, String scriptPubKey) { + this.value = value; + this.address = address; + this.scriptPubKey = scriptPubKey; + } +} \ No newline at end of file diff --git a/wallets/core/src/main/java/bisq/wallets/core/model/Utxo.java b/wallets/core/src/main/java/bisq/wallets/core/model/Utxo.java index df63cc2a7e..04379de229 100644 --- a/wallets/core/src/main/java/bisq/wallets/core/model/Utxo.java +++ b/wallets/core/src/main/java/bisq/wallets/core/model/Utxo.java @@ -23,5 +23,4 @@ public interface Utxo { String getAddress(); double getAmount(); - } diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWallet.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWallet.java index c1c3a6dda7..a834cb6046 100644 --- a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWallet.java +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWallet.java @@ -19,12 +19,11 @@ import bisq.common.observable.ObservableSet; import bisq.wallets.core.Wallet; -import bisq.wallets.core.model.Transaction; +import bisq.wallets.core.model.TransactionInfo; import bisq.wallets.core.model.Utxo; import bisq.wallets.electrum.rpc.ElectrumDaemon; import bisq.wallets.electrum.rpc.responses.ElectrumOnChainHistoryResponse; import bisq.wallets.json_rpc.JsonRpcResponse; -import lombok.Getter; import java.nio.file.Path; import java.util.List; @@ -35,13 +34,9 @@ public class ElectrumWallet implements Wallet { private final Path walletPath; private final ElectrumDaemon daemon; - @Getter - private final ObservableSet receiveAddresses; - public ElectrumWallet(Path walletPath, ElectrumDaemon daemon, ObservableSet receiveAddresses) { this.walletPath = walletPath; this.daemon = daemon; - this.receiveAddresses = receiveAddresses; } @Override @@ -69,7 +64,7 @@ public String getNewAddress() { } @Override - public List listTransactions() { + public List listTransactions() { ElectrumOnChainHistoryResponse onChainHistoryResponse = daemon.onChainHistory(); return onChainHistoryResponse.getResult().getTransactions(); } @@ -102,4 +97,9 @@ public String signMessage(Optional passphrase, String address, String me private boolean doesWalletExist(Path walletPath) { return walletPath.toFile().exists(); } + + @Override + public List getWalletAddresses() { + return daemon.listAddresses(); + } } diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java index 1caca7a0a7..fd86995962 100644 --- a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java @@ -17,12 +17,14 @@ package bisq.wallets.electrum; -import bisq.common.application.Service; import bisq.common.monetary.Coin; import bisq.common.observable.Observable; +import bisq.common.observable.ObservableArray; import bisq.common.observable.ObservableSet; import bisq.common.util.NetworkUtils; -import bisq.wallets.core.model.Transaction; +import bisq.wallets.core.RpcConfig; +import bisq.wallets.core.WalletService; +import bisq.wallets.core.model.TransactionInfo; import bisq.wallets.core.model.Utxo; import bisq.wallets.electrum.notifications.ElectrumNotifyApi; import bisq.wallets.electrum.notifications.ElectrumNotifyWebServer; @@ -36,7 +38,9 @@ import java.util.concurrent.CompletableFuture; @Slf4j -public class ElectrumWalletService implements Service { +public class ElectrumWalletService implements WalletService { + + private ElectrumProcessConfig processConfig; @Getter public static class Config { @@ -55,22 +59,35 @@ public static Config from(com.typesafe.config.Config config) { private final Path electrumRootDataDir; private final ElectrumProcess electrumProcess; private final ElectrumNotifyWebServer electrumNotifyWebServer = new ElectrumNotifyWebServer(NetworkUtils.findFreeSystemPort()); - @Getter - private final Observable observableBalanceAsCoin = new Observable<>(Coin.asBtc(0)); + private final ObservableArray walletAddresses = new ObservableArray<>(); @Getter - private final ObservableSet receiveAddresses = new ObservableSet<>(); + private final Observable balance = new Observable<>(Coin.asBtc(0)); + @Getter + private boolean isWalletReady; - private ElectrumWallet electrumWallet; + private ElectrumWallet wallet; public ElectrumWalletService(Config config, Path bisqDataDir) { this.config = config; electrumRootDataDir = bisqDataDir.resolve("wallets") .resolve("electrum"); - this.electrumProcess = createElectrumProcess(); + + processConfig = ElectrumProcessConfig.builder() + .dataDir(electrumRootDataDir.resolve("wallet")) + .electrumXServerHost("127.0.0.1") + .electrumXServerPort(50001) + .electrumConfig(ElectrumConfig.Generator.generate()) + .build(); + electrumProcess = new ElectrumProcess(electrumRootDataDir, processConfig); } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // Service + /////////////////////////////////////////////////////////////////////////////////////////////////// + @Override public CompletableFuture initialize() { if (!config.isEnabled()) { @@ -78,11 +95,36 @@ public CompletableFuture initialize() { } log.info("initialize"); + return initializeWallet(processConfig.getElectrumConfig().toRpcConfig(), Optional.empty()); + } + + @Override + public CompletableFuture shutdown() { + if (!config.isEnabled()) { + return CompletableFuture.completedFuture(true); + } + + log.info("shutdown"); + return CompletableFuture.supplyAsync(() -> { + wallet.shutdown(); + electrumProcess.shutdown(); + electrumNotifyWebServer.stopServer(); + return true; + }); + } + + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // WalletService + /////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public CompletableFuture initializeWallet(RpcConfig rpcConfig, Optional walletPassphrase) { return CompletableFuture.supplyAsync(() -> { long ts = System.currentTimeMillis(); electrumProcess.start(); - electrumWallet = new ElectrumWallet( + wallet = new ElectrumWallet( electrumRootDataDir.resolve("wallet") .resolve("regtest") .resolve("wallets") @@ -91,111 +133,98 @@ public CompletableFuture initialize() { new ObservableSet<>() ); - // TODO pw support - electrumWallet.initialize(Optional.empty()); + wallet.initialize(Optional.empty()); log.info("Electrum wallet initialized after {} ms.", System.currentTimeMillis() - ts); initializeReceiveAddressMonitor(); updateBalance(); - + isWalletReady = true; return true; }); } @Override - public CompletableFuture shutdown() { - if (!config.isEnabled()) { - return CompletableFuture.completedFuture(true); - } - - log.info("shutdown"); + public CompletableFuture getNewAddress() { return CompletableFuture.supplyAsync(() -> { - electrumWallet.shutdown(); - electrumProcess.shutdown(); - electrumNotifyWebServer.stopServer(); - return true; + String receiveAddress = wallet.getNewAddress(); + monitorAddress(receiveAddress); + return receiveAddress; }); } - public CompletableFuture getNewAddress() { + @Override + public CompletableFuture> requestWalletAddresses() { return CompletableFuture.supplyAsync(() -> { - String receiveAddress = electrumWallet.getNewAddress(); - monitorAddress(receiveAddress); - - // Do we need persistence? - receiveAddresses.add(receiveAddress); - return receiveAddress; + walletAddresses.addAll(wallet.getWalletAddresses()); + return walletAddresses; }); } - public CompletableFuture> listTransactions() { - return CompletableFuture.supplyAsync(() -> electrumWallet.listTransactions()); + @Override + public CompletableFuture> listTransactions() { + return CompletableFuture.supplyAsync(() -> wallet.listTransactions()); } + @Override public CompletableFuture> listUnspent() { - return CompletableFuture.supplyAsync(() -> electrumWallet.listUnspent()); + return CompletableFuture.supplyAsync(() -> wallet.listUnspent()); } + @Override public CompletableFuture sendToAddress(Optional passphrase, String address, double amount) { return CompletableFuture.supplyAsync(() -> { - String txId = electrumWallet.sendToAddress(passphrase, address, amount); + String txId = wallet.sendToAddress(passphrase, address, amount); updateBalance(); return txId; }); } + @Override public CompletableFuture isWalletEncrypted() { //todo implement return CompletableFuture.supplyAsync(() -> true); } - public CompletableFuture getBalance() { + @Override + public CompletableFuture requestBalance() { return CompletableFuture.supplyAsync(() -> { - double balance = electrumWallet.getBalance(); + double balance = wallet.getBalance(); Coin balanceAsCoin = Coin.asBtc(balance); - observableBalanceAsCoin.set(balanceAsCoin); + this.balance.set(balanceAsCoin); return balanceAsCoin; }); } - private ElectrumProcess createElectrumProcess() { - var processConfig = ElectrumProcessConfig.builder() - .dataDir(electrumRootDataDir.resolve("wallet")) - .electrumXServerHost("127.0.0.1") - .electrumXServerPort(50001) - .electrumConfig(ElectrumConfig.Generator.generate()) - .build(); - return new ElectrumProcess(electrumRootDataDir, processConfig); - } + /////////////////////////////////////////////////////////////////////////////////////////////////// + // Private + /////////////////////////////////////////////////////////////////////////////////////////////////// private void initializeReceiveAddressMonitor() { ElectrumNotifyApi.registerListener((address, status) -> { if (status != null) { - receiveAddresses.remove(address); updateBalance(); } }); electrumNotifyWebServer.startServer(); - receiveAddresses.forEach(address -> - electrumWallet.notify(address, electrumNotifyWebServer.getNotifyEndpointUrl()) - ); + + requestWalletAddresses().whenComplete((addresses, throwable) -> { + if (throwable == null) { + addresses.forEach(this::monitorAddress); + } + }); } private void monitorAddress(String address) { - electrumWallet.notify(address, electrumNotifyWebServer.getNotifyEndpointUrl()); + wallet.notify(address, electrumNotifyWebServer.getNotifyEndpointUrl()); } private void updateBalance() { CompletableFuture.runAsync(() -> { - double balance = electrumWallet.getBalance(); + double balance = wallet.getBalance(); Coin coin = Coin.asBtc(balance); - - // Balance changed? - if (!observableBalanceAsCoin.get().equals(coin)) { - observableBalanceAsCoin.set(coin); - } + this.balance.set(coin); }); } } diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/responses/ElectrumOnChainTransactionResponse.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/responses/ElectrumOnChainTransactionResponse.java index e9cea12cd8..4a6ab68b51 100644 --- a/wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/responses/ElectrumOnChainTransactionResponse.java +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/responses/ElectrumOnChainTransactionResponse.java @@ -18,7 +18,7 @@ package bisq.wallets.electrum.rpc.responses; import bisq.common.monetary.Coin; -import bisq.wallets.core.model.Transaction; +import bisq.wallets.core.model.TransactionInfo; import com.squareup.moshi.Json; import lombok.Getter; import lombok.ToString; @@ -27,7 +27,7 @@ @ToString @Getter -public class ElectrumOnChainTransactionResponse implements Transaction { +public class ElectrumOnChainTransactionResponse implements TransactionInfo { @Json(name = "bc_balance") private String bcBalance; @Json(name = "bc_value") diff --git a/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWallet.java b/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWallet.java index af91564c3c..b78c001ba1 100644 --- a/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWallet.java +++ b/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWallet.java @@ -17,18 +17,17 @@ package bisq.wallets.elementsd; -import bisq.common.observable.ObservableSet; +import bisq.common.observable.ObservableArray; import bisq.wallets.bitcoind.zmq.ZmqConnection; import bisq.wallets.bitcoind.zmq.ZmqWallet; import bisq.wallets.core.Wallet; import bisq.wallets.core.model.AddressType; -import bisq.wallets.core.model.Transaction; +import bisq.wallets.core.model.TransactionInfo; import bisq.wallets.core.model.Utxo; import bisq.wallets.elementsd.rpc.ElementsdDaemon; import bisq.wallets.elementsd.rpc.ElementsdWallet; import lombok.Getter; -import java.nio.file.Path; import java.util.List; import java.util.Optional; @@ -74,13 +73,13 @@ public double getBalance() { @Override public String getNewAddress() { String newAddress = wallet.getNewAddress(AddressType.BECH32, ""); - liquidWalletStore.getReceiveAddresses().add(newAddress); + liquidWalletStore.getWalletAddresses().add(newAddress); return newAddress; } @Override - public ObservableSet getReceiveAddresses() { - return liquidWalletStore.getReceiveAddresses(); + public ObservableArray getWalletAddresses() { + return liquidWalletStore.getWalletAddresses(); } @Override @@ -89,7 +88,7 @@ public String signMessage(Optional passphrase, String address, String me } @Override - public List listTransactions() { + public List listTransactions() { return wallet.listTransactions(1000); } diff --git a/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWalletService.java b/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWalletService.java index 266dd2387c..4ed7dedd59 100644 --- a/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWalletService.java +++ b/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWalletService.java @@ -17,6 +17,8 @@ package bisq.wallets.elementsd; +import bisq.common.monetary.Coin; +import bisq.common.observable.Observable; import bisq.persistence.Persistence; import bisq.persistence.PersistenceService; import bisq.wallets.bitcoind.AbstractBitcoindWalletService; @@ -25,6 +27,7 @@ import lombok.extern.slf4j.Slf4j; import java.util.Optional; +import java.util.concurrent.CompletableFuture; @Slf4j public class LiquidWalletService extends AbstractBitcoindWalletService { @@ -33,6 +36,8 @@ public class LiquidWalletService extends AbstractBitcoindWalletService persistence; + @Getter + private final Observable balance = new Observable<>(Coin.of(0, "L-BTC")); public LiquidWalletService(PersistenceService persistenceService, boolean isRegtest) { @@ -55,4 +60,10 @@ protected void persistRpcConfig(RpcConfig rpcConfig) { protected Optional getRpcConfigFromPersistableStore() { return persistableStore.getRpcConfig(); } + + @Override + public CompletableFuture requestBalance() { + //todo impl wallet.getBalance request + return CompletableFuture.completedFuture(balance.get()); + } } diff --git a/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWalletStore.java b/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWalletStore.java index a35719b354..2e1de909be 100644 --- a/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWalletStore.java +++ b/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWalletStore.java @@ -17,7 +17,7 @@ package bisq.wallets.elementsd; -import bisq.common.observable.ObservableSet; +import bisq.common.observable.ObservableArray; import bisq.common.proto.ProtoResolver; import bisq.common.proto.UnresolvableProtobufMessageException; import bisq.persistence.PersistableStore; @@ -26,30 +26,30 @@ import lombok.Getter; import lombok.Setter; -import java.util.HashSet; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; -import java.util.Set; public final class LiquidWalletStore implements PersistableStore { @Getter @Setter private Optional rpcConfig = Optional.empty(); @Getter - private final ObservableSet receiveAddresses = new ObservableSet<>(); + private final ObservableArray walletAddresses = new ObservableArray<>(); public LiquidWalletStore() { } - private LiquidWalletStore(Optional rpcConfig, Set receiveAddresses) { + private LiquidWalletStore(Optional rpcConfig, List walletAddresses) { this.rpcConfig = rpcConfig; - this.receiveAddresses.addAll(receiveAddresses); + this.walletAddresses.addAll(walletAddresses); } @Override public bisq.wallets.protobuf.LiquidWalletStore toProto() { bisq.wallets.protobuf.LiquidWalletStore.Builder builder = bisq.wallets.protobuf.LiquidWalletStore.newBuilder() - .addAllReceiveAddresses(receiveAddresses); + .addAllWalletAddresses(walletAddresses); rpcConfig.ifPresent(config -> builder.setRpcConfig(config.toProto())); return builder.build(); @@ -58,7 +58,7 @@ public bisq.wallets.protobuf.LiquidWalletStore toProto() { public static LiquidWalletStore fromProto(bisq.wallets.protobuf.LiquidWalletStore proto) { Optional rpcConfig = proto.hasRpcConfig() ? Optional.of(RpcConfig.fromProto(proto.getRpcConfig())) : Optional.empty(); - return new LiquidWalletStore(rpcConfig, new HashSet<>(proto.getReceiveAddressesList())); + return new LiquidWalletStore(rpcConfig, new ArrayList<>(proto.getWalletAddressesList())); } @Override @@ -74,13 +74,13 @@ public ProtoResolver> getResolver() { @Override public LiquidWalletStore getClone() { - return new LiquidWalletStore(rpcConfig, receiveAddresses); + return new LiquidWalletStore(rpcConfig, walletAddresses); } @Override public void applyPersisted(LiquidWalletStore persisted) { rpcConfig = persisted.rpcConfig; - receiveAddresses.clear(); - receiveAddresses.addAll(persisted.getReceiveAddresses()); + walletAddresses.clear(); + walletAddresses.addAll(persisted.getWalletAddresses()); } } diff --git a/wallets/elementsd/src/main/proto/wallets.elementsd.proto b/wallets/elementsd/src/main/proto/wallets.elementsd.proto index 23d2e6b671..fe12686791 100644 --- a/wallets/elementsd/src/main/proto/wallets.elementsd.proto +++ b/wallets/elementsd/src/main/proto/wallets.elementsd.proto @@ -24,5 +24,5 @@ import "wallets.core.proto"; message LiquidWalletStore { optional wallets.RpcConfig rpcConfig = 1; - repeated string receiveAddresses = 2; + repeated string walletAddresses = 2; } \ No newline at end of file From 70d61ba3c3b1922a31393ed6b54fb9b2d9c3a218 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 2 Jan 2023 22:05:33 -0500 Subject: [PATCH 04/12] Rename getNewAddress to getUnusedAddress. --- .../receive/WalletReceiveController.java | 2 +- .../AbstractBitcoindWalletService.java | 4 ++-- .../bisq/wallets/bitcoind/BitcoinWallet.java | 2 +- .../main/java/bisq/wallets/core/Wallet.java | 2 +- .../java/bisq/wallets/core/WalletService.java | 2 +- .../bisq/wallets/electrum/ElectrumWallet.java | 19 ++++++++++--------- .../electrum/ElectrumWalletService.java | 4 ++-- .../bisq/wallets/elementsd/LiquidWallet.java | 2 +- 8 files changed, 19 insertions(+), 18 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/receive/WalletReceiveController.java b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/receive/WalletReceiveController.java index 9e13280da3..ff29f8a41f 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/receive/WalletReceiveController.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/receive/WalletReceiveController.java @@ -40,7 +40,7 @@ public WalletReceiveController(DefaultApplicationService applicationService) { @Override public void onActivate() { - electrumWalletService.getNewAddress(). + electrumWalletService.getUnusedAddress(). thenAccept(receiveAddress -> UIThread.run(() -> model.getReceiveAddress().setValue(receiveAddress))); } diff --git a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/AbstractBitcoindWalletService.java b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/AbstractBitcoindWalletService.java index 41729a568a..61b2931228 100644 --- a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/AbstractBitcoindWalletService.java +++ b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/AbstractBitcoindWalletService.java @@ -147,10 +147,10 @@ public boolean isWalletReady() { } @Override - public CompletableFuture getNewAddress() { + public CompletableFuture getUnusedAddress() { return CompletableFuture.supplyAsync(() -> { Wallet wallet = getWalletOrThrowException(); - String receiveAddress = wallet.getNewAddress(); + String receiveAddress = wallet.getUnusedAddress(); // getNewAddress updates the receive adresses set persist(); diff --git a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWallet.java b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWallet.java index 4be062fe2f..6974af6b5a 100644 --- a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWallet.java +++ b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWallet.java @@ -68,7 +68,7 @@ public double getBalance() { } @Override - public String getNewAddress() { + public String getUnusedAddress() { String newAddress = wallet.getNewAddress(AddressType.BECH32, ""); receiveAddresses.add(newAddress); return newAddress; diff --git a/wallets/core/src/main/java/bisq/wallets/core/Wallet.java b/wallets/core/src/main/java/bisq/wallets/core/Wallet.java index ecbe00814b..d118f328f5 100644 --- a/wallets/core/src/main/java/bisq/wallets/core/Wallet.java +++ b/wallets/core/src/main/java/bisq/wallets/core/Wallet.java @@ -30,7 +30,7 @@ public interface Wallet { double getBalance(); - String getNewAddress(); + String getUnusedAddress(); List getWalletAddresses(); diff --git a/wallets/core/src/main/java/bisq/wallets/core/WalletService.java b/wallets/core/src/main/java/bisq/wallets/core/WalletService.java index 06c908e973..6ea84743e5 100644 --- a/wallets/core/src/main/java/bisq/wallets/core/WalletService.java +++ b/wallets/core/src/main/java/bisq/wallets/core/WalletService.java @@ -34,7 +34,7 @@ public interface WalletService extends Service { boolean isWalletReady(); - CompletableFuture getNewAddress(); + CompletableFuture getUnusedAddress(); ObservableArray getWalletAddresses(); diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWallet.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWallet.java index a834cb6046..67955f9b7b 100644 --- a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWallet.java +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWallet.java @@ -50,6 +50,7 @@ public void initialize(Optional walletPassphrase) { @Override public void shutdown() { + daemon.stop(); // Electrum does not provide an unload wallet rpc call. } @@ -59,7 +60,7 @@ public double getBalance() { } @Override - public String getNewAddress() { + public String getUnusedAddress() { return daemon.getUnusedAddress(); } @@ -78,10 +79,6 @@ public List listUnspent() { .collect(Collectors.toList()); } - public void notify(String address, String endpointUrl) { - daemon.notify(address, endpointUrl); - } - @Override public String sendToAddress(Optional passphrase, String address, double amount) { String unsignedTx = daemon.payTo(passphrase, address, amount); @@ -94,12 +91,16 @@ public String signMessage(Optional passphrase, String address, String me return daemon.signMessage(passphrase, address, message); } - private boolean doesWalletExist(Path walletPath) { - return walletPath.toFile().exists(); - } - @Override public List getWalletAddresses() { return daemon.listAddresses(); } + + void notify(String address, String endpointUrl) { + daemon.notify(address, endpointUrl); + } + + private boolean doesWalletExist(Path walletPath) { + return walletPath.toFile().exists(); + } } diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java index fd86995962..0280b63213 100644 --- a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java @@ -144,9 +144,9 @@ public CompletableFuture initializeWallet(RpcConfig rpcConfig, Optional } @Override - public CompletableFuture getNewAddress() { + public CompletableFuture getUnusedAddress() { return CompletableFuture.supplyAsync(() -> { - String receiveAddress = wallet.getNewAddress(); + String receiveAddress = wallet.getUnusedAddress(); monitorAddress(receiveAddress); return receiveAddress; }); diff --git a/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWallet.java b/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWallet.java index b78c001ba1..5fcff870b1 100644 --- a/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWallet.java +++ b/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWallet.java @@ -71,7 +71,7 @@ public double getBalance() { } @Override - public String getNewAddress() { + public String getUnusedAddress() { String newAddress = wallet.getNewAddress(AddressType.BECH32, ""); liquidWalletStore.getWalletAddresses().add(newAddress); return newAddress; From cacb42eb88bf8bf8de0cbcd78fda0da1b6be9ca5 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 2 Jan 2023 22:21:29 -0500 Subject: [PATCH 05/12] Use ObservableSet for walletAddresses --- .../AbstractBitcoindWalletService.java | 8 ++-- .../java/bisq/wallets/core/WalletService.java | 6 +-- .../ElectrumSendIntegrationTests.java | 2 +- ...ElectrumTxAndPasswordIntegrationTests.java | 2 +- ...ectrumUnconfirmedSendIntegrationTests.java | 2 +- .../electrum/ElectrumWalletService.java | 39 +++++++++++-------- .../notifications/ElectrumNotifyApi.java | 5 +-- .../ElectrumNotifyWebServer.java | 1 - 8 files changed, 33 insertions(+), 32 deletions(-) diff --git a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/AbstractBitcoindWalletService.java b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/AbstractBitcoindWalletService.java index 61b2931228..34cd56f436 100644 --- a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/AbstractBitcoindWalletService.java +++ b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/AbstractBitcoindWalletService.java @@ -19,7 +19,7 @@ import bisq.common.monetary.Coin; import bisq.common.observable.Observable; -import bisq.common.observable.ObservableArray; +import bisq.common.observable.ObservableSet; import bisq.persistence.PersistableStore; import bisq.persistence.PersistenceClient; import bisq.wallets.bitcoind.rpc.BitcoindDaemon; @@ -68,7 +68,7 @@ public static Optional getOptionalRegtestConfig(boolean isRegtest, in protected Optional zmqConnection = Optional.empty(); protected final Set utxoTxIds = new HashSet<>(); @Getter - protected final ObservableArray walletAddresses = new ObservableArray<>(); + protected final ObservableSet walletAddresses = new ObservableSet<>(); public AbstractBitcoindWalletService(String currencyCode, Optional optionalRpcConfig, @@ -160,7 +160,7 @@ public CompletableFuture getUnusedAddress() { } @Override - public CompletableFuture> requestWalletAddresses() { + public CompletableFuture> requestWalletAddresses() { if (wallet.isEmpty()) { return CompletableFuture.completedFuture(walletAddresses); } else { @@ -201,7 +201,7 @@ public CompletableFuture isWalletEncrypted() { return CompletableFuture.supplyAsync(() -> true); } - protected void initializeZmqListeners(ZmqConnection zmqConnection, List walletAddresses) { + protected void initializeZmqListeners(ZmqConnection zmqConnection, Set walletAddresses) { // Update balance when new block gets mined zmqConnection.getListeners().registerNewBlockMinedListener(unused -> updateBalance()); diff --git a/wallets/core/src/main/java/bisq/wallets/core/WalletService.java b/wallets/core/src/main/java/bisq/wallets/core/WalletService.java index 6ea84743e5..8864ba1e5b 100644 --- a/wallets/core/src/main/java/bisq/wallets/core/WalletService.java +++ b/wallets/core/src/main/java/bisq/wallets/core/WalletService.java @@ -20,7 +20,7 @@ import bisq.common.application.Service; import bisq.common.monetary.Coin; import bisq.common.observable.Observable; -import bisq.common.observable.ObservableArray; +import bisq.common.observable.ObservableSet; import bisq.wallets.core.model.TransactionInfo; import bisq.wallets.core.model.Utxo; @@ -36,9 +36,9 @@ public interface WalletService extends Service { CompletableFuture getUnusedAddress(); - ObservableArray getWalletAddresses(); + ObservableSet getWalletAddresses(); - CompletableFuture> requestWalletAddresses(); + CompletableFuture> requestWalletAddresses(); CompletableFuture> listTransactions(); diff --git a/wallets/electrum/src/integrationTest/java/bisq/wallets/electrum/ElectrumSendIntegrationTests.java b/wallets/electrum/src/integrationTest/java/bisq/wallets/electrum/ElectrumSendIntegrationTests.java index 96bfec3dc6..d640fc1a9a 100644 --- a/wallets/electrum/src/integrationTest/java/bisq/wallets/electrum/ElectrumSendIntegrationTests.java +++ b/wallets/electrum/src/integrationTest/java/bisq/wallets/electrum/ElectrumSendIntegrationTests.java @@ -60,7 +60,7 @@ void sendBtcTest() throws InterruptedException { double balanceBeforeTest = electrumDaemon.getBalance(); var electrumProcessedTxLatch = new CountDownLatch(1); - ElectrumNotifyApi.registerListener((address, status) -> { + ElectrumNotifyApi.addListener((address, status) -> { if (status != null) { electrumProcessedTxLatch.countDown(); } diff --git a/wallets/electrum/src/integrationTest/java/bisq/wallets/electrum/ElectrumTxAndPasswordIntegrationTests.java b/wallets/electrum/src/integrationTest/java/bisq/wallets/electrum/ElectrumTxAndPasswordIntegrationTests.java index 4e3fadc2bd..24b29fd8cd 100644 --- a/wallets/electrum/src/integrationTest/java/bisq/wallets/electrum/ElectrumTxAndPasswordIntegrationTests.java +++ b/wallets/electrum/src/integrationTest/java/bisq/wallets/electrum/ElectrumTxAndPasswordIntegrationTests.java @@ -126,7 +126,7 @@ void listUnspentGetTxAndHistoryTest() throws InterruptedException { private void fundElectrumWallet() throws InterruptedException { var electrumProcessedTxLatch = new CountDownLatch(1); - ElectrumNotifyApi.registerListener((address, status) -> { + ElectrumNotifyApi.addListener((address, status) -> { if (status != null) { electrumProcessedTxLatch.countDown(); } diff --git a/wallets/electrum/src/integrationTest/java/bisq/wallets/electrum/ElectrumUnconfirmedSendIntegrationTests.java b/wallets/electrum/src/integrationTest/java/bisq/wallets/electrum/ElectrumUnconfirmedSendIntegrationTests.java index 27790cd810..67a12da8d5 100644 --- a/wallets/electrum/src/integrationTest/java/bisq/wallets/electrum/ElectrumUnconfirmedSendIntegrationTests.java +++ b/wallets/electrum/src/integrationTest/java/bisq/wallets/electrum/ElectrumUnconfirmedSendIntegrationTests.java @@ -60,7 +60,7 @@ void setUp() { void unconfirmedBalanceTest() throws InterruptedException { double balanceBeforeTest = electrumDaemon.getBalance(); Map addressToLatchMap = new ConcurrentHashMap<>(); - ElectrumNotifyApi.registerListener((address, status) -> { + ElectrumNotifyApi.addListener((address, status) -> { CountDownLatch latch = addressToLatchMap.get(address); if (status != null) { latch.countDown(); diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java index 0280b63213..af7a95fa97 100644 --- a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java @@ -19,7 +19,6 @@ import bisq.common.monetary.Coin; import bisq.common.observable.Observable; -import bisq.common.observable.ObservableArray; import bisq.common.observable.ObservableSet; import bisq.common.util.NetworkUtils; import bisq.wallets.core.RpcConfig; @@ -36,11 +35,11 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; @Slf4j -public class ElectrumWalletService implements WalletService { +public class ElectrumWalletService implements WalletService, ElectrumNotifyApi.Listener { - private ElectrumProcessConfig processConfig; @Getter public static class Config { @@ -57,10 +56,11 @@ public static Config from(com.typesafe.config.Config config) { private final Config config; private final Path electrumRootDataDir; + private final ElectrumProcessConfig processConfig; private final ElectrumProcess electrumProcess; private final ElectrumNotifyWebServer electrumNotifyWebServer = new ElectrumNotifyWebServer(NetworkUtils.findFreeSystemPort()); @Getter - private final ObservableArray walletAddresses = new ObservableArray<>(); + private final ObservableSet walletAddresses = new ObservableSet<>(); @Getter private final Observable balance = new Observable<>(Coin.asBtc(0)); @Getter @@ -153,7 +153,7 @@ public CompletableFuture getUnusedAddress() { } @Override - public CompletableFuture> requestWalletAddresses() { + public CompletableFuture> requestWalletAddresses() { return CompletableFuture.supplyAsync(() -> { walletAddresses.addAll(wallet.getWalletAddresses()); return walletAddresses; @@ -195,25 +195,30 @@ public CompletableFuture requestBalance() { }); } + /////////////////////////////////////////////////////////////////////////////////////////////////// + // ElectrumNotifyApi.Listener + /////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void onAddressStatusChanged(String address, String status) { + if (status != null) { + updateBalance(); + } + } + /////////////////////////////////////////////////////////////////////////////////////////////////// // Private /////////////////////////////////////////////////////////////////////////////////////////////////// private void initializeReceiveAddressMonitor() { - ElectrumNotifyApi.registerListener((address, status) -> { - if (status != null) { - updateBalance(); - } - }); - + ElectrumNotifyApi.addListener(this); electrumNotifyWebServer.startServer(); - - requestWalletAddresses().whenComplete((addresses, throwable) -> { - if (throwable == null) { - addresses.forEach(this::monitorAddress); - } - }); + try { + requestWalletAddresses().get().forEach(this::monitorAddress); + } catch (InterruptedException | ExecutionException e) { + log.error("requestWalletAddresses failed. ", e); + } } private void monitorAddress(String address) { diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/notifications/ElectrumNotifyApi.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/notifications/ElectrumNotifyApi.java index 597bc5d545..c8a18ef904 100644 --- a/wallets/electrum/src/main/java/bisq/wallets/electrum/notifications/ElectrumNotifyApi.java +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/notifications/ElectrumNotifyApi.java @@ -36,9 +36,7 @@ public class ElectrumNotifyApi { private static final List listeners = new CopyOnWriteArrayList<>(); public interface Listener { - void onAddressStatusChanged(String address, String status); - } @POST @@ -49,12 +47,11 @@ public String notifyEndpoint(ElectrumNotifyRequest request) { return "SUCCESS"; } - public static void registerListener(Listener listener) { + public static void addListener(Listener listener) { listeners.add(listener); } public static void removeListener(Listener listener) { listeners.remove(listener); } - } diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/notifications/ElectrumNotifyWebServer.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/notifications/ElectrumNotifyWebServer.java index 1222238d31..57466de243 100644 --- a/wallets/electrum/src/main/java/bisq/wallets/electrum/notifications/ElectrumNotifyWebServer.java +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/notifications/ElectrumNotifyWebServer.java @@ -35,7 +35,6 @@ public ElectrumNotifyWebServer(int port) { this.baseUrl = "http://localhost:" + port + "/"; } - public void startServer() { ResourceConfig app = register(ElectrumNotifyApi.class); URI uri = URI.create(baseUrl); From 45f8f750cf79b9774d253bb28d9e7bbeff59f45c Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 2 Jan 2023 22:55:48 -0500 Subject: [PATCH 06/12] Fix binaryPath --- .../bisq/wallets/electrum/ElectrumProcess.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumProcess.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumProcess.java index 95d2e0d1a4..dd7cbc9280 100644 --- a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumProcess.java +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumProcess.java @@ -114,10 +114,20 @@ public ElectrumDaemon getElectrumDaemon() { private Path resolveBinaryPath() { Path destDirPath = electrumRootDataDir.resolve("bin"); String binarySuffix = getBinarySuffix(); - Path extractedFilePath = destDirPath.resolve("Electrum." + binarySuffix); - if (OsUtils.isMac()) { - extractedFilePath = extractedFilePath.resolve("Contents/MacOS/run_electrum"); + + //todo defined in gradle, we could let gradle write the version to a file which we read + String version = "4.2.2"; + // File name: electrum-4.2.2.dmg / electrum-4.2.2.exe / electrum-4.2.2-x86_64.AppImage + switch (OsUtils.getOperatingSystem()) { + case LINUX: + return destDirPath.resolve("electrum-" + version + "-x86_64." + binarySuffix); + case MAC: + return destDirPath.resolve("Electrum." + binarySuffix) + .resolve("Contents/MacOS/run_electrum"); + case WIN: + return destDirPath.resolve("electrum-" + version + "." + binarySuffix); + default: + throw new UnsupportedOperationException("Bisq is running on an unsupported OS: " + OsUtils.getOSName()); } - return extractedFilePath; } } From 0495bea68bf3ef388cdab5b625b4b8ff8980154a Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 2 Jan 2023 22:56:20 -0500 Subject: [PATCH 07/12] Add params to config --- application/src/main/resources/default.conf | 3 +++ .../electrum/ElectrumWalletService.java | 24 ++++++++++++------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/application/src/main/resources/default.conf b/application/src/main/resources/default.conf index e5311d001d..4e2360fc95 100644 --- a/application/src/main/resources/default.conf +++ b/application/src/main/resources/default.conf @@ -124,5 +124,8 @@ application { electrumWallet = { enabled = false + network = regtest + electrumXServerHost = 127.0.0.1 + electrumXServerPort = 50001 } } \ No newline at end of file diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java index af7a95fa97..480bdc9f7d 100644 --- a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java @@ -39,18 +39,25 @@ @Slf4j public class ElectrumWalletService implements WalletService, ElectrumNotifyApi.Listener { - - @Getter public static class Config { private final boolean enabled; + private final String network; + private final String electrumXServerHost; + private final int electrumXServerPort; - public Config(boolean enabled) { + public Config(boolean enabled, String network, String electrumXServerHost, int electrumXServerPort) { this.enabled = enabled; + this.network = network; + this.electrumXServerHost = electrumXServerHost; + this.electrumXServerPort = electrumXServerPort; } public static Config from(com.typesafe.config.Config config) { - return new Config(config.getBoolean("enabled")); + return new Config(config.getBoolean("enabled"), + config.getString("network"), + config.getString("electrumXServerHost"), + config.getInt("electrumXServerPort")); } } @@ -59,25 +66,24 @@ public static Config from(com.typesafe.config.Config config) { private final ElectrumProcessConfig processConfig; private final ElectrumProcess electrumProcess; private final ElectrumNotifyWebServer electrumNotifyWebServer = new ElectrumNotifyWebServer(NetworkUtils.findFreeSystemPort()); + @Getter private final ObservableSet walletAddresses = new ObservableSet<>(); @Getter private final Observable balance = new Observable<>(Coin.asBtc(0)); @Getter private boolean isWalletReady; - private ElectrumWallet wallet; public ElectrumWalletService(Config config, Path bisqDataDir) { this.config = config; - electrumRootDataDir = bisqDataDir.resolve("wallets") .resolve("electrum"); processConfig = ElectrumProcessConfig.builder() .dataDir(electrumRootDataDir.resolve("wallet")) - .electrumXServerHost("127.0.0.1") - .electrumXServerPort(50001) + .electrumXServerHost(config.getElectrumXServerHost()) + .electrumXServerPort(config.getElectrumXServerPort()) .electrumConfig(ElectrumConfig.Generator.generate()) .build(); electrumProcess = new ElectrumProcess(electrumRootDataDir, processConfig); @@ -126,7 +132,7 @@ public CompletableFuture initializeWallet(RpcConfig rpcConfig, Optional wallet = new ElectrumWallet( electrumRootDataDir.resolve("wallet") - .resolve("regtest") + .resolve(config.getNetwork()) .resolve("wallets") .resolve("default_wallet"), electrumProcess.getElectrumDaemon(), From eb5df979582087b80987e37c5f011e76051c3bb9 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 2 Jan 2023 23:21:05 -0500 Subject: [PATCH 08/12] Add requestTransactions method --- .../AbstractBitcoindWalletService.java | 12 +++++++ .../bisq/wallets/bitcoind/BitcoinWallet.java | 27 ++++++++++++-- .../main/java/bisq/wallets/core/Wallet.java | 3 ++ .../java/bisq/wallets/core/WalletService.java | 5 +++ .../bisq/wallets/electrum/ElectrumWallet.java | 35 +++++++++++++++++-- .../electrum/ElectrumWalletService.java | 11 ++++++ .../bisq/wallets/elementsd/LiquidWallet.java | 8 +++++ 7 files changed, 96 insertions(+), 5 deletions(-) diff --git a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/AbstractBitcoindWalletService.java b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/AbstractBitcoindWalletService.java index 34cd56f436..46402621f4 100644 --- a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/AbstractBitcoindWalletService.java +++ b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/AbstractBitcoindWalletService.java @@ -29,6 +29,7 @@ import bisq.wallets.core.Wallet; import bisq.wallets.core.WalletService; import bisq.wallets.core.exceptions.WalletNotInitializedException; +import bisq.wallets.core.model.Transaction; import bisq.wallets.core.model.TransactionInfo; import bisq.wallets.core.model.Utxo; import lombok.Getter; @@ -69,6 +70,8 @@ public static Optional getOptionalRegtestConfig(boolean isRegtest, in protected final Set utxoTxIds = new HashSet<>(); @Getter protected final ObservableSet walletAddresses = new ObservableSet<>(); + @Getter + private final ObservableSet transactions = new ObservableSet<>(); public AbstractBitcoindWalletService(String currencyCode, Optional optionalRpcConfig, @@ -195,6 +198,15 @@ public CompletableFuture sendToAddress(Optional passphrase, Stri }); } + @Override + public CompletableFuture> requestTransactions() { + return CompletableFuture.supplyAsync(() -> { + //todo implement + // transactions.addAll(getWalletOrThrowException().getTransactions()); + return transactions; + }); + } + @Override public CompletableFuture isWalletEncrypted() { //todo implement diff --git a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWallet.java b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWallet.java index 6974af6b5a..845f8a53be 100644 --- a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWallet.java +++ b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWallet.java @@ -24,13 +24,14 @@ import bisq.wallets.bitcoind.zmq.ZmqWallet; import bisq.wallets.core.RpcConfig; import bisq.wallets.core.Wallet; -import bisq.wallets.core.model.AddressType; -import bisq.wallets.core.model.TransactionInfo; -import bisq.wallets.core.model.Utxo; +import bisq.wallets.core.model.*; import lombok.Getter; +import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; public class BitcoinWallet implements Wallet, ZmqWallet { @@ -89,6 +90,26 @@ public List listTransactions() { return wallet.listTransactions(1000); } + @Override + public List getTransactions() { + return wallet.listTransactions(1000).stream() + .map(tx -> { + //todo add getrawtransaction with verbose = 1 flag to get full tx data + // BitcoindGetRawTransactionRpcCall does not support verbose + List inputs = new ArrayList<>(); + List outputs = new ArrayList<>(); + int lockTime = 0; + return new Transaction(tx.getTxId(), + inputs, + outputs, + lockTime, + tx.getBlockheight(), + new Date(tx.getTime() * 1000L), + tx.getConfirmations()); + }) + .collect(Collectors.toList()); + } + @Override public List listUnspent() { return wallet.listUnspent(); diff --git a/wallets/core/src/main/java/bisq/wallets/core/Wallet.java b/wallets/core/src/main/java/bisq/wallets/core/Wallet.java index d118f328f5..cbd602e052 100644 --- a/wallets/core/src/main/java/bisq/wallets/core/Wallet.java +++ b/wallets/core/src/main/java/bisq/wallets/core/Wallet.java @@ -17,6 +17,7 @@ package bisq.wallets.core; +import bisq.wallets.core.model.Transaction; import bisq.wallets.core.model.TransactionInfo; import bisq.wallets.core.model.Utxo; @@ -36,6 +37,8 @@ public interface Wallet { List listTransactions(); + List getTransactions(); + List listUnspent(); String sendToAddress(Optional passphrase, String address, double amount); diff --git a/wallets/core/src/main/java/bisq/wallets/core/WalletService.java b/wallets/core/src/main/java/bisq/wallets/core/WalletService.java index 8864ba1e5b..5016858576 100644 --- a/wallets/core/src/main/java/bisq/wallets/core/WalletService.java +++ b/wallets/core/src/main/java/bisq/wallets/core/WalletService.java @@ -21,6 +21,7 @@ import bisq.common.monetary.Coin; import bisq.common.observable.Observable; import bisq.common.observable.ObservableSet; +import bisq.wallets.core.model.Transaction; import bisq.wallets.core.model.TransactionInfo; import bisq.wallets.core.model.Utxo; @@ -51,4 +52,8 @@ public interface WalletService extends Service { CompletableFuture requestBalance(); Observable getBalance(); + + ObservableSet getTransactions(); + + CompletableFuture> requestTransactions(); } diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWallet.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWallet.java index 67955f9b7b..7dc17d6491 100644 --- a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWallet.java +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWallet.java @@ -19,10 +19,11 @@ import bisq.common.observable.ObservableSet; import bisq.wallets.core.Wallet; -import bisq.wallets.core.model.TransactionInfo; -import bisq.wallets.core.model.Utxo; +import bisq.wallets.core.model.*; import bisq.wallets.electrum.rpc.ElectrumDaemon; +import bisq.wallets.electrum.rpc.responses.ElectrumDeserializeResponse; import bisq.wallets.electrum.rpc.responses.ElectrumOnChainHistoryResponse; +import bisq.wallets.electrum.rpc.responses.ElectrumOnChainTransactionResponse; import bisq.wallets.json_rpc.JsonRpcResponse; import java.nio.file.Path; @@ -96,6 +97,36 @@ public List getWalletAddresses() { return daemon.listAddresses(); } + + public ElectrumDeserializeResponse.Result getElectrumTransaction(String txId) { + String txAsHex = daemon.getTransaction(txId); + return daemon.deserialize(txAsHex).getResult(); + } + + @Override + public List getTransactions() { + ElectrumOnChainHistoryResponse onChainHistoryResponse = daemon.onChainHistory(); + List responses = onChainHistoryResponse.getResult().getTransactions(); + return responses.stream().map(response -> { + String txId = response.getTxId(); + ElectrumDeserializeResponse.Result deserializedTx = getElectrumTransaction(txId); + List inputs = deserializedTx.getInputs().stream() + .map(inputResponse -> new TransactionInput(inputResponse.getPrevOutHash(), inputResponse.getPrevOutN(), inputResponse.getNSequence(), inputResponse.getScriptSig(), inputResponse.getWitness())) + .collect(Collectors.toList()); + List outputs = deserializedTx.getOutputs().stream() + .map(outputResponse -> new TransactionOutput(outputResponse.getValueSats(), outputResponse.getAddress(), outputResponse.getScriptPubKey())) + .collect(Collectors.toList()); + return new Transaction(txId, + inputs, + outputs, + deserializedTx.getLockTime(), + response.getHeight(), + response.getDate(), + response.getConfirmations()) + ; + }).collect(Collectors.toList()); + } + void notify(String address, String endpointUrl) { daemon.notify(address, endpointUrl); } diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java index 480bdc9f7d..f8d2e7450a 100644 --- a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java @@ -23,6 +23,7 @@ import bisq.common.util.NetworkUtils; import bisq.wallets.core.RpcConfig; import bisq.wallets.core.WalletService; +import bisq.wallets.core.model.Transaction; import bisq.wallets.core.model.TransactionInfo; import bisq.wallets.core.model.Utxo; import bisq.wallets.electrum.notifications.ElectrumNotifyApi; @@ -72,6 +73,8 @@ public static Config from(com.typesafe.config.Config config) { @Getter private final Observable balance = new Observable<>(Coin.asBtc(0)); @Getter + private final ObservableSet transactions = new ObservableSet<>(); + @Getter private boolean isWalletReady; private ElectrumWallet wallet; @@ -171,6 +174,14 @@ public CompletableFuture> listTransactions() { return CompletableFuture.supplyAsync(() -> wallet.listTransactions()); } + @Override + public CompletableFuture> requestTransactions() { + return CompletableFuture.supplyAsync(() -> { + transactions.addAll(wallet.getTransactions()); + return transactions; + }); + } + @Override public CompletableFuture> listUnspent() { return CompletableFuture.supplyAsync(() -> wallet.listUnspent()); diff --git a/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWallet.java b/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWallet.java index 5fcff870b1..bb3fd6a442 100644 --- a/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWallet.java +++ b/wallets/elementsd/src/main/java/bisq/wallets/elementsd/LiquidWallet.java @@ -22,6 +22,7 @@ import bisq.wallets.bitcoind.zmq.ZmqWallet; import bisq.wallets.core.Wallet; import bisq.wallets.core.model.AddressType; +import bisq.wallets.core.model.Transaction; import bisq.wallets.core.model.TransactionInfo; import bisq.wallets.core.model.Utxo; import bisq.wallets.elementsd.rpc.ElementsdDaemon; @@ -31,6 +32,7 @@ import java.util.List; import java.util.Optional; +// todo should we use the Wallet interface here? It is not a common wallet for the app but a trade protocol specific one public class LiquidWallet implements Wallet, ZmqWallet { private final String walletName; @@ -92,6 +94,12 @@ public List listTransactions() { return wallet.listTransactions(1000); } + @Override + public List getTransactions() { + //todo impl + return null; + } + @Override public List listUnspent() { return wallet.listUnspent(); From 9562e6789755b1ce0a09eecd926a04aa8913285c Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 2 Jan 2023 23:52:33 -0500 Subject: [PATCH 09/12] Use Transaction in WalletTransactionListItem Make Date optional as it is null if unconfirmed tx. Add binding for Transactions --- .../wallet/txs/WalletTransactionListItem.java | 11 +++++++---- .../wallet/txs/WalletTxsController.java | 19 ++++++++++--------- .../bisq/wallets/bitcoind/BitcoinWallet.java | 9 +++++++-- .../BitcoindListTransactionsResponse.java | 5 +++-- .../bisq/wallets/core/model/Transaction.java | 14 +++++++++++--- .../wallets/core/model/TransactionInfo.java | 3 ++- .../bisq/wallets/electrum/ElectrumWallet.java | 8 ++++++-- .../electrum/ElectrumWalletService.java | 17 +++++++---------- .../ElectrumOnChainTransactionResponse.java | 5 +++-- 9 files changed, 56 insertions(+), 35 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTransactionListItem.java b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTransactionListItem.java index 2744c90f48..7c06ab6d55 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTransactionListItem.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTransactionListItem.java @@ -20,23 +20,26 @@ import bisq.desktop.components.table.TableItem; import bisq.presentation.formatters.AmountFormatter; import bisq.presentation.formatters.DateFormatter; -import bisq.wallets.core.model.TransactionInfo; +import bisq.wallets.core.model.Transaction; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import java.util.Date; + @Slf4j @Getter +@EqualsAndHashCode public class WalletTransactionListItem implements TableItem { private final String date; private final String txId; private final String amount; private final String confirmations; - public WalletTransactionListItem(TransactionInfo transaction) { - date = DateFormatter.formatDateTime(transaction.getDate()); + public WalletTransactionListItem(Transaction transaction) { + date = DateFormatter.formatDateTime(transaction.getDate().orElse(new Date())); txId = transaction.getTxId(); amount = AmountFormatter.formatAmount(transaction.getAmount()); confirmations = String.valueOf(transaction.getConfirmations()); } - } diff --git a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTxsController.java b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTxsController.java index 7111b86388..c5b0216d9e 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTxsController.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTxsController.java @@ -18,20 +18,21 @@ package bisq.desktop.primary.main.content.wallet.txs; import bisq.application.DefaultApplicationService; -import bisq.desktop.common.threading.UIThread; +import bisq.common.observable.Pin; +import bisq.desktop.common.observable.FxBindings; import bisq.desktop.common.view.Controller; +import bisq.wallets.core.model.Transaction; import bisq.wallets.electrum.ElectrumWalletService; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import java.util.stream.Collectors; - @Slf4j public class WalletTxsController implements Controller { @Getter private final WalletTxsView view; private final WalletTxsModel model; private final ElectrumWalletService electrumWalletService; + private Pin transactionsPin; public WalletTxsController(DefaultApplicationService applicationService) { electrumWalletService = applicationService.getElectrumWalletService(); @@ -41,15 +42,15 @@ public WalletTxsController(DefaultApplicationService applicationService) { @Override public void onActivate() { - electrumWalletService.listTransactions() - .thenAccept(transactions -> UIThread.run(() -> { - model.getListItems().setAll(transactions.stream() - .map(WalletTransactionListItem::new) - .collect(Collectors.toList())); - })); + transactionsPin = FxBindings.bind(model.getListItems()) + .map(WalletTransactionListItem::new) + .to(electrumWalletService.getTransactions()); + + electrumWalletService.requestTransactions(); } @Override public void onDeactivate() { + transactionsPin.unbind(); } } diff --git a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWallet.java b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWallet.java index 845f8a53be..ac0ce7ad06 100644 --- a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWallet.java +++ b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWallet.java @@ -17,6 +17,7 @@ package bisq.wallets.bitcoind; +import bisq.common.monetary.Coin; import bisq.common.observable.ObservableSet; import bisq.wallets.bitcoind.rpc.BitcoindDaemon; import bisq.wallets.bitcoind.rpc.BitcoindWallet; @@ -99,13 +100,17 @@ public List getTransactions() { List inputs = new ArrayList<>(); List outputs = new ArrayList<>(); int lockTime = 0; + Coin amount = Coin.asBtc(0); + boolean incoming = true; return new Transaction(tx.getTxId(), inputs, outputs, lockTime, tx.getBlockheight(), - new Date(tx.getTime() * 1000L), - tx.getConfirmations()); + Optional.of(new Date(tx.getTime() * 1000L)), + tx.getConfirmations(), + amount, + incoming); }) .collect(Collectors.toList()); } diff --git a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/responses/BitcoindListTransactionsResponse.java b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/responses/BitcoindListTransactionsResponse.java index 831685b3a8..3625da2372 100644 --- a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/responses/BitcoindListTransactionsResponse.java +++ b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/rpc/responses/BitcoindListTransactionsResponse.java @@ -25,6 +25,7 @@ import java.util.Date; import java.util.List; +import java.util.Optional; public class BitcoindListTransactionsResponse extends JsonRpcResponse> { @Getter @@ -59,8 +60,8 @@ public Coin getAmount() { } @Override - public Date getDate() { - return new Date(time * 1000L); + public Optional getDate() { + return Optional.of(new Date(time * 1000L)); } } } diff --git a/wallets/core/src/main/java/bisq/wallets/core/model/Transaction.java b/wallets/core/src/main/java/bisq/wallets/core/model/Transaction.java index 5328adefe3..7748aba150 100644 --- a/wallets/core/src/main/java/bisq/wallets/core/model/Transaction.java +++ b/wallets/core/src/main/java/bisq/wallets/core/model/Transaction.java @@ -17,11 +17,13 @@ package bisq.wallets.core.model; +import bisq.common.monetary.Coin; import lombok.EqualsAndHashCode; import lombok.Getter; import java.util.Date; import java.util.List; +import java.util.Optional; @Getter @EqualsAndHashCode @@ -31,16 +33,20 @@ public class Transaction { private final List outputs; private final int lockTime; private final int height; - private final Date date; + private final Optional date; private final int confirmations; + private final Coin amount; + private final boolean incoming; public Transaction(String txId, List inputs, List outputs, int lockTime, int height, - Date date, - int confirmations) { + Optional date, + int confirmations, + Coin amount, + boolean incoming) { this.txId = txId; this.inputs = inputs; @@ -49,5 +55,7 @@ public Transaction(String txId, this.height = height; this.date = date; this.confirmations = confirmations; + this.amount = amount; + this.incoming = incoming; } } \ No newline at end of file diff --git a/wallets/core/src/main/java/bisq/wallets/core/model/TransactionInfo.java b/wallets/core/src/main/java/bisq/wallets/core/model/TransactionInfo.java index f299c70c8a..b6384002fc 100644 --- a/wallets/core/src/main/java/bisq/wallets/core/model/TransactionInfo.java +++ b/wallets/core/src/main/java/bisq/wallets/core/model/TransactionInfo.java @@ -20,6 +20,7 @@ import bisq.common.monetary.Coin; import java.util.Date; +import java.util.Optional; public interface TransactionInfo { String getTxId(); @@ -28,5 +29,5 @@ public interface TransactionInfo { int getConfirmations(); - Date getDate(); + Optional getDate(); } diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWallet.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWallet.java index 7dc17d6491..0e07abdc5a 100644 --- a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWallet.java +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWallet.java @@ -25,12 +25,14 @@ import bisq.wallets.electrum.rpc.responses.ElectrumOnChainHistoryResponse; import bisq.wallets.electrum.rpc.responses.ElectrumOnChainTransactionResponse; import bisq.wallets.json_rpc.JsonRpcResponse; +import lombok.extern.slf4j.Slf4j; import java.nio.file.Path; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +@Slf4j public class ElectrumWallet implements Wallet { private final Path walletPath; private final ElectrumDaemon daemon; @@ -122,8 +124,10 @@ public List getTransactions() { deserializedTx.getLockTime(), response.getHeight(), response.getDate(), - response.getConfirmations()) - ; + response.getConfirmations(), + response.getAmount(), + response.isIncoming() + ); }).collect(Collectors.toList()); } diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java index f8d2e7450a..6bb8691c73 100644 --- a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java @@ -146,7 +146,8 @@ public CompletableFuture initializeWallet(RpcConfig rpcConfig, Optional log.info("Electrum wallet initialized after {} ms.", System.currentTimeMillis() - ts); initializeReceiveAddressMonitor(); - updateBalance(); + requestBalance(); + requestTransactions(); isWalletReady = true; return true; }); @@ -191,7 +192,7 @@ public CompletableFuture> listUnspent() { public CompletableFuture sendToAddress(Optional passphrase, String address, double amount) { return CompletableFuture.supplyAsync(() -> { String txId = wallet.sendToAddress(passphrase, address, amount); - updateBalance(); + // requestBalance(); return txId; }); } @@ -212,6 +213,7 @@ public CompletableFuture requestBalance() { }); } + /////////////////////////////////////////////////////////////////////////////////////////////////// // ElectrumNotifyApi.Listener /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -219,7 +221,8 @@ public CompletableFuture requestBalance() { @Override public void onAddressStatusChanged(String address, String status) { if (status != null) { - updateBalance(); + requestBalance(); + requestTransactions(); } } @@ -242,11 +245,5 @@ private void monitorAddress(String address) { wallet.notify(address, electrumNotifyWebServer.getNotifyEndpointUrl()); } - private void updateBalance() { - CompletableFuture.runAsync(() -> { - double balance = wallet.getBalance(); - Coin coin = Coin.asBtc(balance); - this.balance.set(coin); - }); - } + } diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/responses/ElectrumOnChainTransactionResponse.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/responses/ElectrumOnChainTransactionResponse.java index 4a6ab68b51..4fe60f6890 100644 --- a/wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/responses/ElectrumOnChainTransactionResponse.java +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/rpc/responses/ElectrumOnChainTransactionResponse.java @@ -24,6 +24,7 @@ import lombok.ToString; import java.util.Date; +import java.util.Optional; @ToString @Getter @@ -60,7 +61,7 @@ public Coin getAmount() { } @Override - public Date getDate() { - return timestamp != null ? new Date(timestamp * 1000L) : new Date(0); + public Optional getDate() { + return timestamp != null ? Optional.of(new Date(timestamp * 1000L)) : Optional.empty(); } } From 10a83b0a23bfcc41658a4b16ab9f9dfb9277624a Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 20 Feb 2023 13:26:02 +0700 Subject: [PATCH 10/12] Add sorting for tx table --- .../wallet/txs/WalletTransactionListItem.java | 19 +++++++---- .../content/wallet/txs/WalletTxsView.java | 34 ++++++++++++++----- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTransactionListItem.java b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTransactionListItem.java index 7c06ab6d55..c25b524891 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTransactionListItem.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTransactionListItem.java @@ -17,6 +17,7 @@ package bisq.desktop.primary.main.content.wallet.txs; +import bisq.common.monetary.Coin; import bisq.desktop.components.table.TableItem; import bisq.presentation.formatters.AmountFormatter; import bisq.presentation.formatters.DateFormatter; @@ -31,15 +32,21 @@ @Getter @EqualsAndHashCode public class WalletTransactionListItem implements TableItem { - private final String date; + private final Date date; + private final String dateAsString; private final String txId; - private final String amount; - private final String confirmations; + private final String amountAsString; + private final String confirmationsAsString; + private final Coin amount; + private final int confirmations; public WalletTransactionListItem(Transaction transaction) { - date = DateFormatter.formatDateTime(transaction.getDate().orElse(new Date())); + date = transaction.getDate().orElse(new Date()); + dateAsString = DateFormatter.formatDateTime(date); txId = transaction.getTxId(); - amount = AmountFormatter.formatAmount(transaction.getAmount()); - confirmations = String.valueOf(transaction.getConfirmations()); + amount = transaction.getAmount(); + amountAsString = AmountFormatter.formatAmount(amount); + confirmations = transaction.getConfirmations(); + confirmationsAsString = String.valueOf(confirmations); } } diff --git a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTxsView.java b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTxsView.java index d6d0be814a..f6eb7e5fe9 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTxsView.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTxsView.java @@ -22,9 +22,12 @@ import bisq.desktop.components.table.BisqTableView; import bisq.i18n.Res; import javafx.geometry.Insets; +import javafx.scene.control.TableColumn; import javafx.scene.layout.VBox; import lombok.extern.slf4j.Slf4j; +import java.util.Comparator; + @Slf4j public class WalletTxsView extends View { private final BisqTableView tableView; @@ -52,31 +55,44 @@ protected void onViewDetached() { } private void configTableView() { - tableView.getColumns().add(new BisqTableColumn.Builder() + BisqTableColumn column = new BisqTableColumn.Builder() .title(Res.get("date")) .minWidth(200) - .valueSupplier(WalletTransactionListItem::getDate) + .valueSupplier(WalletTransactionListItem::getDateAsString) .isSortable(true) .isFirst() - .build()); + .build(); + column.setComparator(Comparator.comparing(WalletTransactionListItem::getDateAsString)); + column.setSortType(TableColumn.SortType.DESCENDING); + tableView.getSortOrder().add(column); + tableView.getColumns().add(column); + tableView.getColumns().add(new BisqTableColumn.Builder() .title(Res.get("wallet.txs.txId")) .minWidth(200) .valueSupplier(WalletTransactionListItem::getTxId) .isSortable(true) .build()); - tableView.getColumns().add(new BisqTableColumn.Builder() + + column = new BisqTableColumn.Builder() .title(Res.get("wallet.txs.amount")) .minWidth(120) - .valueSupplier(WalletTransactionListItem::getAmount) + .valueSupplier(WalletTransactionListItem::getAmountAsString) .isSortable(true) - .build()); - tableView.getColumns().add(new BisqTableColumn.Builder() + .build(); + column.setComparator(Comparator.comparing(WalletTransactionListItem::getAmount)); + column.setSortType(TableColumn.SortType.DESCENDING); + tableView.getColumns().add(column); + + column = new BisqTableColumn.Builder() .title(Res.get("wallet.txs.confirmations")) .minWidth(120) - .valueSupplier(WalletTransactionListItem::getConfirmations) + .valueSupplier(WalletTransactionListItem::getConfirmationsAsString) .isSortable(true) .isLast() - .build()); + .build(); + column.setComparator(Comparator.comparing(WalletTransactionListItem::getConfirmations)); + column.setSortType(TableColumn.SortType.DESCENDING); + tableView.getColumns().add(column); } } From 0f6d0b390baaaff039b98f9cec9a58818cb01a40 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 20 Feb 2023 13:42:58 +0700 Subject: [PATCH 11/12] Add isWalletEnabled getter and show wallet and balance in UI only if enabled --- .../java/bisq/desktop/primary/main/left/LeftNavModel.java | 2 ++ .../java/bisq/desktop/primary/main/left/LeftNavView.java | 6 +++++- .../bisq/desktop/primary/main/top/TopPanelController.java | 2 +- .../java/bisq/desktop/primary/main/top/TopPanelModel.java | 6 ++++++ .../java/bisq/desktop/primary/main/top/TopPanelView.java | 6 +++++- .../java/bisq/wallets/electrum/ElectrumWalletService.java | 4 ++++ 6 files changed, 23 insertions(+), 3 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/primary/main/left/LeftNavModel.java b/desktop/src/main/java/bisq/desktop/primary/main/left/LeftNavModel.java index 45397ed1cc..79a5d6f5e3 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/left/LeftNavModel.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/left/LeftNavModel.java @@ -40,6 +40,7 @@ @Slf4j @Getter public class LeftNavModel implements Model { + private final boolean isWalletEnabled; private final NetworkService networkService; private final Set navigationTargets = new HashSet<>(); private final List leftNavButtons = new ArrayList<>(); @@ -56,6 +57,7 @@ public class LeftNavModel implements Model { public LeftNavModel(DefaultApplicationService applicationService) { + isWalletEnabled = applicationService.getElectrumWalletService().isWalletEnabled(); networkService = applicationService.getNetworkService(); torEnabled.set(networkService.isTransportTypeSupported(Transport.Type.TOR)); diff --git a/desktop/src/main/java/bisq/desktop/primary/main/left/LeftNavView.java b/desktop/src/main/java/bisq/desktop/primary/main/left/LeftNavView.java index 728507983e..99cef38501 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/left/LeftNavView.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/left/LeftNavView.java @@ -162,10 +162,14 @@ public LeftNavView(LeftNavModel model, LeftNavController controller) { selectionMarker.setPrefWidth(3); selectionMarker.setPrefHeight(LeftNavButton.HEIGHT); - mainMenuItems.getChildren().addAll(dashBoard, trade, tradeSubMenuItems, wallet, + mainMenuItems.getChildren().addAll(dashBoard, trade, tradeSubMenuItems, learn, learnSubMenuItems, chat, events, support, settings); + if (model.isWalletEnabled()) { + mainMenuItems.getChildren().add(3, wallet); + } + mainMenuItems.setLayoutY(menuTop); root.getChildren().addAll(logoExpanded, logoCollapsed, selectionMarker, mainMenuItems, expandIcon, collapseIcon, networkInfoBox); } diff --git a/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelController.java b/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelController.java index b68bfe9b4f..783cc1d5d1 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelController.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelController.java @@ -35,7 +35,7 @@ public class TopPanelController implements Controller { public TopPanelController(DefaultApplicationService applicationService) { electrumWalletService = applicationService.getElectrumWalletService(); - model = new TopPanelModel(); + model = new TopPanelModel(applicationService); UserProfileSelection userProfileSelection = new UserProfileSelection(applicationService.getUserService().getUserIdentityService()); MarketSelection marketSelection = new MarketSelection(applicationService.getOracleService().getMarketPriceService()); view = new TopPanelView(model, this, userProfileSelection, marketSelection.getRoot()); diff --git a/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelModel.java b/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelModel.java index eea34ac446..9876dfc81d 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelModel.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelModel.java @@ -18,6 +18,7 @@ package bisq.desktop.primary.main.top; +import bisq.application.DefaultApplicationService; import bisq.common.monetary.Coin; import bisq.desktop.common.view.Model; import bisq.presentation.formatters.AmountFormatter; @@ -29,9 +30,14 @@ @Getter public class TopPanelModel implements Model { + private final boolean isWalletEnabled; private final ObjectProperty balanceAsCoinProperty = new SimpleObjectProperty<>(Coin.of(0, "BTC")); private final ObservableValue formattedBalanceProperty = Bindings.createStringBinding( () -> AmountFormatter.formatAmount(balanceAsCoinProperty.get(), true), balanceAsCoinProperty ); + + public TopPanelModel(DefaultApplicationService applicationService) { + isWalletEnabled = applicationService.getElectrumWalletService().isWalletEnabled(); + } } diff --git a/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelView.java b/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelView.java index b017f1e9c8..fc27cc0092 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelView.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelView.java @@ -54,7 +54,11 @@ public TopPanelView(TopPanelModel model, balanceLabel = balanceTriple.getSecond(); HBox.setMargin(balanceBox, new Insets(0, 0, 0, 30)); - root.getChildren().addAll(balanceBox, Spacer.fillHBox(), marketPriceBox, userProfileSelectionRoot); + root.getChildren().addAll(Spacer.fillHBox(), marketPriceBox, userProfileSelectionRoot); + + if (model.isWalletEnabled()) { + root.getChildren().add(0, balanceBox); + } } @Override diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java index 6bb8691c73..307936440f 100644 --- a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java @@ -122,6 +122,10 @@ public CompletableFuture shutdown() { }); } + public boolean isWalletEnabled() { + return config.isEnabled(); + } + /////////////////////////////////////////////////////////////////////////////////////////////////// // WalletService From 46b1b9fd0633abe29fcb27a5d5d84bbfe5b3a00b Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 20 Feb 2023 14:58:40 +0700 Subject: [PATCH 12/12] Refactor wallet config Use enum for selected btc wallet. Use optional for WalletService. Use WalletService interface in client code. If WalletService is not present navigate to Dashboard if any wallet UI was selected previously. --- application/build.gradle | 1 + .../application/BitcoinWalletSelection.java | 24 +++++++++++++++ .../DefaultApplicationService.java | 30 +++++++++++++++---- application/src/main/resources/default.conf | 17 +++++++---- desktop/build.gradle | 1 + .../main/content/ContentController.java | 5 +++- .../primary/main/content/ContentModel.java | 8 +++-- .../main/content/wallet/WalletModel.java | 1 - .../dashboard/WalletDashboardController.java | 10 +++---- .../receive/WalletReceiveController.java | 8 ++--- .../wallet/send/WalletSendController.java | 10 +++---- .../settings/WalletSettingsController.java | 6 ++-- .../wallet/txs/WalletTxsController.java | 10 +++---- .../primary/main/left/LeftNavModel.java | 2 +- .../primary/main/top/TopPanelController.java | 22 +++++++++----- .../primary/main/top/TopPanelModel.java | 5 ++-- wallets/bitcoind/build.gradle | 1 + .../bitcoind/BitcoinWalletService.java | 28 +++++++++++++---- .../electrum/ElectrumWalletService.java | 19 ++---------- 19 files changed, 138 insertions(+), 70 deletions(-) create mode 100644 application/src/main/java/bisq/application/BitcoinWalletSelection.java diff --git a/application/build.gradle b/application/build.gradle index 9260df9e42..9695c1966c 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -20,6 +20,7 @@ dependencies { implementation('network:network') implementation("wallets:electrum") + implementation("wallets:bitcoind") implementation libs.google.guava implementation libs.typesafe.config diff --git a/application/src/main/java/bisq/application/BitcoinWalletSelection.java b/application/src/main/java/bisq/application/BitcoinWalletSelection.java new file mode 100644 index 0000000000..8db93e9d73 --- /dev/null +++ b/application/src/main/java/bisq/application/BitcoinWalletSelection.java @@ -0,0 +1,24 @@ +/* + * This file is part of Bisq. + * + * Bisq 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. + * + * Bisq 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 Bisq. If not, see . + */ + +package bisq.application; + +enum BitcoinWalletSelection { + BITCOIND, + ELECTRUM, + NONE +} \ No newline at end of file diff --git a/application/src/main/java/bisq/application/DefaultApplicationService.java b/application/src/main/java/bisq/application/DefaultApplicationService.java index 291daf3958..37afed5385 100644 --- a/application/src/main/java/bisq/application/DefaultApplicationService.java +++ b/application/src/main/java/bisq/application/DefaultApplicationService.java @@ -19,6 +19,7 @@ import bisq.account.AccountService; import bisq.chat.ChatService; +import bisq.common.application.Service; import bisq.common.observable.Observable; import bisq.common.threading.ExecutorFactory; import bisq.common.util.CompletableFutureUtils; @@ -33,11 +34,14 @@ import bisq.settings.SettingsService; import bisq.support.SupportService; import bisq.user.UserService; +import bisq.wallets.bitcoind.BitcoinWalletService; +import bisq.wallets.core.WalletService; import bisq.wallets.electrum.ElectrumWalletService; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import java.nio.file.Path; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; @@ -66,7 +70,7 @@ public enum State { } private final SecurityService securityService; - private final ElectrumWalletService electrumWalletService; + private final Optional walletService; private final NetworkService networkService; private final IdentityService identityService; private final OracleService oracleService; @@ -83,8 +87,20 @@ public enum State { public DefaultApplicationService(String[] args) { super("default", args); securityService = new SecurityService(persistenceService); - electrumWalletService = new ElectrumWalletService(ElectrumWalletService.Config.from(getConfig("electrumWallet")), - Path.of(config.getBaseDir())); + com.typesafe.config.Config bitcoinWalletConfig = getConfig("bitcoinWallet"); + BitcoinWalletSelection bitcoinWalletSelection = bitcoinWalletConfig.getEnum(BitcoinWalletSelection.class, "bitcoinWalletSelection"); + switch (bitcoinWalletSelection) { + case BITCOIND: + walletService = Optional.of(new BitcoinWalletService(BitcoinWalletService.Config.from(bitcoinWalletConfig.getConfig("bitcoind")), getPersistenceService())); + break; + case ELECTRUM: + walletService = Optional.of(new ElectrumWalletService(ElectrumWalletService.Config.from(bitcoinWalletConfig.getConfig("electrum")), Path.of(config.getBaseDir()))); + break; + case NONE: + default: + walletService = Optional.empty(); + break; + } networkService = new NetworkService(NetworkServiceConfig.from(config.getBaseDir(), getConfig("network")), persistenceService, @@ -129,7 +145,8 @@ public CompletableFuture initialize() { setState(State.INITIALIZE_NETWORK); CompletableFuture networkFuture = networkService.initialize(); - CompletableFuture walletFuture = electrumWalletService.initialize(); + CompletableFuture walletFuture = walletService.map(Service::initialize) + .orElse(CompletableFuture.completedFuture(true)); networkFuture.whenComplete((r, throwable) -> { if (throwable != null) { @@ -189,7 +206,10 @@ public CompletableFuture shutdown() { .thenCompose(result -> oracleService.shutdown()) .thenCompose(result -> identityService.shutdown()) .thenCompose(result -> networkService.shutdown()) - .thenCompose(result -> electrumWalletService.shutdown()) + .thenCompose(result -> { + return walletService.map(Service::shutdown) + .orElse(CompletableFuture.completedFuture(true)); + }) .thenCompose(result -> securityService.shutdown()) .orTimeout(10, TimeUnit.SECONDS) .handle((result, throwable) -> throwable == null) diff --git a/application/src/main/resources/default.conf b/application/src/main/resources/default.conf index 4e2360fc95..dafa546fb4 100644 --- a/application/src/main/resources/default.conf +++ b/application/src/main/resources/default.conf @@ -122,10 +122,17 @@ application { } } - electrumWallet = { - enabled = false - network = regtest - electrumXServerHost = 127.0.0.1 - electrumXServerPort = 50001 + bitcoinWallet = { + // BitcoinWalletSelection enum values: BITCOIND, ELECTRUM, NONE + // BITCOIND currently not supported + bitcoinWalletSelection = NONE + bitcoind = { + network = regtest + } + electrum = { + network = regtest + electrumXServerHost = 127.0.0.1 + electrumXServerPort = 50001 + } } } \ No newline at end of file diff --git a/desktop/build.gradle b/desktop/build.gradle index 11e5744cb0..c25d7f6efe 100644 --- a/desktop/build.gradle +++ b/desktop/build.gradle @@ -31,6 +31,7 @@ dependencies { implementation("network:network") implementation("wallets:electrum") + implementation("wallets:bitcoind") implementation libs.google.guava implementation libs.bundles.fontawesomefx diff --git a/desktop/src/main/java/bisq/desktop/primary/main/content/ContentController.java b/desktop/src/main/java/bisq/desktop/primary/main/content/ContentController.java index 0a4c327dcf..eeda2aee0b 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/content/ContentController.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/content/ContentController.java @@ -56,7 +56,7 @@ public ContentController(DefaultApplicationService applicationService) { super(NavigationTarget.CONTENT); this.applicationService = applicationService; - model = new ContentModel(); + model = new ContentModel(applicationService.getWalletService().isPresent()); view = new ContentView(model, this); } @@ -70,6 +70,9 @@ public void onDeactivate() { @Override protected Optional createController(NavigationTarget navigationTarget) { + if (navigationTarget == NavigationTarget.WALLET && !model.isWalletEnabled()) { + navigationTarget = NavigationTarget.DASHBOARD; + } switch (navigationTarget) { case DASHBOARD: { return Optional.of(new DashboardController(applicationService)); diff --git a/desktop/src/main/java/bisq/desktop/primary/main/content/ContentModel.java b/desktop/src/main/java/bisq/desktop/primary/main/content/ContentModel.java index 97634dc9b2..0667ac9f04 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/content/ContentModel.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/content/ContentModel.java @@ -19,12 +19,16 @@ import bisq.desktop.common.view.NavigationModel; import bisq.desktop.common.view.NavigationTarget; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; @Slf4j - +@Getter public class ContentModel extends NavigationModel { - public ContentModel() { + private final boolean isWalletEnabled; + + public ContentModel(boolean isWalletEnabled) { + this.isWalletEnabled = isWalletEnabled; } @Override diff --git a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/WalletModel.java b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/WalletModel.java index da5e0daffd..f4ab68ea47 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/WalletModel.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/WalletModel.java @@ -21,7 +21,6 @@ import bisq.desktop.common.view.TabModel; public class WalletModel extends TabModel { - public WalletModel() { } diff --git a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/dashboard/WalletDashboardController.java b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/dashboard/WalletDashboardController.java index d34f627599..1f90a0ca48 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/dashboard/WalletDashboardController.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/dashboard/WalletDashboardController.java @@ -24,7 +24,7 @@ import bisq.desktop.common.view.Controller; import bisq.desktop.common.view.Navigation; import bisq.desktop.common.view.NavigationTarget; -import bisq.wallets.electrum.ElectrumWalletService; +import bisq.wallets.core.WalletService; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -33,11 +33,11 @@ public class WalletDashboardController implements Controller { @Getter private final WalletDashboardView view; private final WalletDashboardModel model; - private final ElectrumWalletService electrumWalletService; + private final WalletService walletService; private Pin balancePin; public WalletDashboardController(DefaultApplicationService applicationService) { - electrumWalletService = applicationService.getElectrumWalletService(); + walletService = applicationService.getWalletService().orElseThrow(); model = new WalletDashboardModel(); view = new WalletDashboardView(model, this); } @@ -45,9 +45,9 @@ public WalletDashboardController(DefaultApplicationService applicationService) { @Override public void onActivate() { balancePin = FxBindings.bind(model.getBalanceAsCoinProperty()) - .to(electrumWalletService.getBalance()); + .to(walletService.getBalance()); - electrumWalletService.requestBalance().whenComplete((balance, throwable) -> { + walletService.requestBalance().whenComplete((balance, throwable) -> { if (throwable == null) { UIThread.run(() -> model.getBalanceAsCoinProperty().set(balance)); } diff --git a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/receive/WalletReceiveController.java b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/receive/WalletReceiveController.java index ff29f8a41f..1edffc2df7 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/receive/WalletReceiveController.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/receive/WalletReceiveController.java @@ -21,7 +21,7 @@ import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.utils.ClipboardUtil; import bisq.desktop.common.view.Controller; -import bisq.wallets.electrum.ElectrumWalletService; +import bisq.wallets.core.WalletService; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -30,17 +30,17 @@ public class WalletReceiveController implements Controller { @Getter private final WalletReceiveView view; private final WalletReceiveModel model; - private final ElectrumWalletService electrumWalletService; + private final WalletService walletService; public WalletReceiveController(DefaultApplicationService applicationService) { - electrumWalletService = applicationService.getElectrumWalletService(); + walletService = applicationService.getWalletService().orElseThrow(); model = new WalletReceiveModel(); view = new WalletReceiveView(model, this); } @Override public void onActivate() { - electrumWalletService.getUnusedAddress(). + walletService.getUnusedAddress(). thenAccept(receiveAddress -> UIThread.run(() -> model.getReceiveAddress().setValue(receiveAddress))); } diff --git a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/send/WalletSendController.java b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/send/WalletSendController.java index 1d681253d5..eec84f5dc0 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/send/WalletSendController.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/send/WalletSendController.java @@ -22,7 +22,7 @@ import bisq.desktop.common.utils.validation.MonetaryValidator; import bisq.desktop.common.view.Controller; import bisq.desktop.components.overlay.Popup; -import bisq.wallets.electrum.ElectrumWalletService; +import bisq.wallets.core.WalletService; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.fxmisc.easybind.EasyBind; @@ -35,13 +35,13 @@ public class WalletSendController implements Controller { @Getter private final WalletSendView view; private final WalletSendModel model; - private final ElectrumWalletService electrumWalletService; + private final WalletService walletService; private final MonetaryValidator amountValidator = new MonetaryValidator(); private Subscription addressPin; private Subscription amountPin; public WalletSendController(DefaultApplicationService applicationService) { - electrumWalletService = applicationService.getElectrumWalletService(); + walletService = applicationService.getWalletService().orElseThrow(); model = new WalletSendModel(); view = new WalletSendView(model, this, amountValidator); } @@ -56,7 +56,7 @@ public void onActivate() { }); //todo check if wallet is encrypted - electrumWalletService.isWalletEncrypted() + walletService.isWalletEncrypted() .thenAccept(isWalletEncrypted -> UIThread.run(() -> model.getIsPasswordVisible().set(isWalletEncrypted))); } @@ -70,7 +70,7 @@ void onSend() { //todo double amount = Double.parseDouble(model.getAmount().get()); String address = model.getAddress().get(); - electrumWalletService.sendToAddress(Optional.ofNullable(model.getPassword().get()), address, amount) + walletService.sendToAddress(Optional.ofNullable(model.getPassword().get()), address, amount) .whenComplete((response, throwable) -> { if (throwable != null) { UIThread.run(() -> new Popup().error(throwable.getMessage()).show()); diff --git a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/settings/WalletSettingsController.java b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/settings/WalletSettingsController.java index 671c8b9952..e5cb62ee4c 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/settings/WalletSettingsController.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/settings/WalletSettingsController.java @@ -19,7 +19,7 @@ import bisq.application.DefaultApplicationService; import bisq.desktop.common.view.Controller; -import bisq.wallets.electrum.ElectrumWalletService; +import bisq.wallets.core.WalletService; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -28,10 +28,10 @@ public class WalletSettingsController implements Controller { @Getter private final WalletSettingsView view; private final WalletSettingsModel model; - private final ElectrumWalletService electrumWalletService; + private final WalletService walletService; public WalletSettingsController(DefaultApplicationService applicationService) { - electrumWalletService = applicationService.getElectrumWalletService(); + walletService = applicationService.getWalletService().orElseThrow(); model = new WalletSettingsModel(); view = new WalletSettingsView(model, this); } diff --git a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTxsController.java b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTxsController.java index c5b0216d9e..a695accb86 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTxsController.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/content/wallet/txs/WalletTxsController.java @@ -21,8 +21,8 @@ import bisq.common.observable.Pin; import bisq.desktop.common.observable.FxBindings; import bisq.desktop.common.view.Controller; +import bisq.wallets.core.WalletService; import bisq.wallets.core.model.Transaction; -import bisq.wallets.electrum.ElectrumWalletService; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -31,11 +31,11 @@ public class WalletTxsController implements Controller { @Getter private final WalletTxsView view; private final WalletTxsModel model; - private final ElectrumWalletService electrumWalletService; + private final WalletService walletService; private Pin transactionsPin; public WalletTxsController(DefaultApplicationService applicationService) { - electrumWalletService = applicationService.getElectrumWalletService(); + walletService = applicationService.getWalletService().orElseThrow(); model = new WalletTxsModel(); view = new WalletTxsView(model, this); } @@ -44,9 +44,9 @@ public WalletTxsController(DefaultApplicationService applicationService) { public void onActivate() { transactionsPin = FxBindings.bind(model.getListItems()) .map(WalletTransactionListItem::new) - .to(electrumWalletService.getTransactions()); + .to(walletService.getTransactions()); - electrumWalletService.requestTransactions(); + walletService.requestTransactions(); } @Override diff --git a/desktop/src/main/java/bisq/desktop/primary/main/left/LeftNavModel.java b/desktop/src/main/java/bisq/desktop/primary/main/left/LeftNavModel.java index 79a5d6f5e3..dc98a6aac9 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/left/LeftNavModel.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/left/LeftNavModel.java @@ -57,7 +57,7 @@ public class LeftNavModel implements Model { public LeftNavModel(DefaultApplicationService applicationService) { - isWalletEnabled = applicationService.getElectrumWalletService().isWalletEnabled(); + isWalletEnabled = applicationService.getWalletService().isPresent(); networkService = applicationService.getNetworkService(); torEnabled.set(networkService.isTransportTypeSupported(Transport.Type.TOR)); diff --git a/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelController.java b/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelController.java index 783cc1d5d1..fabef1406d 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelController.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelController.java @@ -22,20 +22,24 @@ import bisq.desktop.common.observable.FxBindings; import bisq.desktop.common.view.Controller; import bisq.desktop.primary.main.content.components.UserProfileSelection; -import bisq.wallets.electrum.ElectrumWalletService; +import bisq.wallets.core.WalletService; import lombok.Getter; +import javax.annotation.Nullable; +import java.util.Optional; + public class TopPanelController implements Controller { - private final ElectrumWalletService electrumWalletService; @Getter private final TopPanelView view; private final TopPanelModel model; + private final Optional walletService; + @Nullable private Pin balancePin; public TopPanelController(DefaultApplicationService applicationService) { - electrumWalletService = applicationService.getElectrumWalletService(); + walletService = applicationService.getWalletService(); - model = new TopPanelModel(applicationService); + model = new TopPanelModel(applicationService.getWalletService().isPresent()); UserProfileSelection userProfileSelection = new UserProfileSelection(applicationService.getUserService().getUserIdentityService()); MarketSelection marketSelection = new MarketSelection(applicationService.getOracleService().getMarketPriceService()); view = new TopPanelView(model, this, userProfileSelection, marketSelection.getRoot()); @@ -44,12 +48,16 @@ public TopPanelController(DefaultApplicationService applicationService) { @Override public void onActivate() { - balancePin = FxBindings.bind(model.getBalanceAsCoinProperty()) - .to(electrumWalletService.getBalance()); + walletService.ifPresent(walletService -> { + balancePin = FxBindings.bind(model.getBalanceAsCoinProperty()) + .to(walletService.getBalance()); + }); } @Override public void onDeactivate() { - balancePin.unbind(); + if (balancePin != null) { + balancePin.unbind(); + } } } diff --git a/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelModel.java b/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelModel.java index 9876dfc81d..ac8fa9cd29 100644 --- a/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelModel.java +++ b/desktop/src/main/java/bisq/desktop/primary/main/top/TopPanelModel.java @@ -18,7 +18,6 @@ package bisq.desktop.primary.main.top; -import bisq.application.DefaultApplicationService; import bisq.common.monetary.Coin; import bisq.desktop.common.view.Model; import bisq.presentation.formatters.AmountFormatter; @@ -37,7 +36,7 @@ public class TopPanelModel implements Model { balanceAsCoinProperty ); - public TopPanelModel(DefaultApplicationService applicationService) { - isWalletEnabled = applicationService.getElectrumWalletService().isWalletEnabled(); + public TopPanelModel(boolean isWalletEnabled) { + this.isWalletEnabled = isWalletEnabled; } } diff --git a/wallets/bitcoind/build.gradle b/wallets/bitcoind/build.gradle index f8352e5924..51ad2e10ed 100644 --- a/wallets/bitcoind/build.gradle +++ b/wallets/bitcoind/build.gradle @@ -10,6 +10,7 @@ dependencies { implementation project(':core') implementation project(':json-rpc') + implementation libs.typesafe.config implementation libs.google.guava implementation libs.jeromq diff --git a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWalletService.java b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWalletService.java index ebf3567e62..f2616e03c2 100644 --- a/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWalletService.java +++ b/wallets/bitcoind/src/main/java/bisq/wallets/bitcoind/BitcoinWalletService.java @@ -29,18 +29,34 @@ import java.util.concurrent.CompletableFuture; @Slf4j +@Getter public class BitcoinWalletService extends AbstractBitcoindWalletService { - @Getter + public static class Config { + private final String network; + + public Config(String network) { + this.network = network; + } + + public static Config from(com.typesafe.config.Config config) { + return new Config(config.getString("network")); + } + + public boolean isRegtest() { + return network.equals("regtest"); + } + } + + private final Config config; private final BitcoinWalletStore persistableStore = new BitcoinWalletStore(); - @Getter private final Persistence persistence; - @Getter private final Observable balance = new Observable<>(Coin.asBtc(0)); - public BitcoinWalletService(PersistenceService persistenceService, - boolean isRegtest) { - super("BTC", getOptionalRegtestConfig(isRegtest, 18443), "bisq_bitcoind_default_wallet"); + public BitcoinWalletService(Config config, + PersistenceService persistenceService) { + super("BTC", getOptionalRegtestConfig(config.isRegtest(), 18443), "bisq_bitcoind_default_wallet"); + this.config = config; persistence = persistenceService.getOrCreatePersistence(this, persistableStore); } diff --git a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java index 307936440f..15ce8e6018 100644 --- a/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java +++ b/wallets/electrum/src/main/java/bisq/wallets/electrum/ElectrumWalletService.java @@ -42,21 +42,18 @@ public class ElectrumWalletService implements WalletService, ElectrumNotifyApi.Listener { @Getter public static class Config { - private final boolean enabled; private final String network; private final String electrumXServerHost; private final int electrumXServerPort; - public Config(boolean enabled, String network, String electrumXServerHost, int electrumXServerPort) { - this.enabled = enabled; + public Config(String network, String electrumXServerHost, int electrumXServerPort) { this.network = network; this.electrumXServerHost = electrumXServerHost; this.electrumXServerPort = electrumXServerPort; } public static Config from(com.typesafe.config.Config config) { - return new Config(config.getBoolean("enabled"), - config.getString("network"), + return new Config(config.getString("network"), config.getString("electrumXServerHost"), config.getInt("electrumXServerPort")); } @@ -99,20 +96,12 @@ public ElectrumWalletService(Config config, Path bisqDataDir) { @Override public CompletableFuture initialize() { - if (!config.isEnabled()) { - return CompletableFuture.completedFuture(true); - } - log.info("initialize"); return initializeWallet(processConfig.getElectrumConfig().toRpcConfig(), Optional.empty()); } @Override public CompletableFuture shutdown() { - if (!config.isEnabled()) { - return CompletableFuture.completedFuture(true); - } - log.info("shutdown"); return CompletableFuture.supplyAsync(() -> { wallet.shutdown(); @@ -122,10 +111,6 @@ public CompletableFuture shutdown() { }); } - public boolean isWalletEnabled() { - return config.isEnabled(); - } - /////////////////////////////////////////////////////////////////////////////////////////////////// // WalletService