Skip to content

Commit

Permalink
Merge pull request #512 from alvasw/use_global_miner_wallet
Browse files Browse the repository at this point in the history
Let Bitcoin Core manage its wallet files
  • Loading branch information
alvasw authored Oct 21, 2022
2 parents d44fbe9 + 9151285 commit 796d5e4
Show file tree
Hide file tree
Showing 22 changed files with 269 additions and 170 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ public enum State {
public DefaultApplicationService(String[] args) {
super("default", args);

bitcoinWalletService = new BitcoinWalletService(persistenceService, config.getBaseDir(), config.isBitcoindRegtest());
liquidWalletService = new LiquidWalletService(persistenceService, config.getBaseDir(), config.isElementsdRegtest());
bitcoinWalletService = new BitcoinWalletService(persistenceService, config.isBitcoindRegtest());
liquidWalletService = new LiquidWalletService(persistenceService, config.isElementsdRegtest());

securityService = new SecurityService(persistenceService);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
Expand All @@ -61,7 +59,7 @@ public static Optional<RpcConfig> getOptionalRegtestConfig(boolean isRegtest, in

private final String currencyCode;
protected final Optional<RpcConfig> optionalRpcConfig;
protected final Path walletsDataDir;
protected final String walletName;
@Getter
protected final Observable<Coin> observableBalanceAsCoin;

Expand All @@ -72,10 +70,10 @@ public static Optional<RpcConfig> getOptionalRegtestConfig(boolean isRegtest, in

public AbstractBitcoindWalletService(String currencyCode,
Optional<RpcConfig> optionalRpcConfig,
Path walletsDataDir) {
String walletName) {
this.currencyCode = currencyCode;
this.optionalRpcConfig = optionalRpcConfig;
this.walletsDataDir = walletsDataDir;
this.walletName = walletName;

observableBalanceAsCoin = new Observable<>(Coin.of(0, currencyCode));
}
Expand All @@ -87,7 +85,6 @@ public AbstractBitcoindWalletService(String currencyCode,
@Override
public CompletableFuture<Boolean> initialize() {
log.info("initialize");
createWalletsDataDirIfNotExisting();

boolean isSuccess = verifyRpcConfigAndCreateWallet(optionalRpcConfig);
// No cmd line arguments passed, so try to use saved configuration
Expand Down Expand Up @@ -135,7 +132,7 @@ public CompletableFuture<Boolean> initializeWallet(RpcConfig rpcConfig, Optional
initializeZmqListeners(zmqConnection, receiveAddresses);

this.zmqConnection = Optional.of(zmqConnection);
log.info("Successfully created/loaded wallet at {}", walletsDataDir);
log.info("Successfully created/loaded wallet at {}", walletName);

updateBalance();

Expand Down Expand Up @@ -230,17 +227,6 @@ protected Wallet getWalletOrThrowException() {

protected abstract T createWallet(RpcConfig rpcConfig);

private void createWalletsDataDirIfNotExisting() {
File dataDir = walletsDataDir.toFile();
if (!dataDir.exists()) {
boolean isSuccess = walletsDataDir.toFile().mkdirs();
if (!isSuccess) {
throw new WalletNotInitializedException("Couldn't create wallets data dir: " +
dataDir.getAbsolutePath());
}
}
}

private boolean verifyRpcConfigAndCreateWallet(Optional<RpcConfig> optionalRpcConfig) {
if (optionalRpcConfig.isEmpty()) return false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import lombok.Getter;

import java.net.MalformedURLException;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;

Expand All @@ -45,7 +44,7 @@ public class BitcoinWallet implements Wallet, ZmqWallet {
@Getter
private final ZmqConnection zmqConnection;

public BitcoinWallet(Path walletPath,
public BitcoinWallet(String walletName,
RpcConfig rpcConfig,
BitcoindDaemon daemon,
ObservableSet<String> receiveAddresses,
Expand All @@ -54,7 +53,7 @@ public BitcoinWallet(Path walletPath,
this.zmqConnection = zmqConnection;

try {
wallet = new BitcoindWallet(daemon, rpcConfig, walletPath);
wallet = new BitcoindWallet(daemon, rpcConfig, walletName);
} catch (MalformedURLException e) {
throw new WalletInitializationFailedException("Couldn't initialize BitcoinWalletService", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.nio.file.Path;
import java.util.Optional;

@Slf4j
Expand All @@ -36,15 +34,14 @@ public class BitcoinWalletService extends AbstractBitcoindWalletService<BitcoinW
private final Persistence<BitcoinWalletStore> persistence;

public BitcoinWalletService(PersistenceService persistenceService,
String baseDir,
boolean isRegtest) {
super("BTC", getOptionalRegtestConfig(isRegtest, 18443), Path.of(baseDir + File.separator + "wallets"));
super("BTC", getOptionalRegtestConfig(isRegtest, 18443), "bisq_bitcoind_default_wallet");
persistence = persistenceService.getOrCreatePersistence(this, persistableStore);
}

@Override
protected BitcoinWallet createWallet(RpcConfig rpcConfig) {
return WalletFactory.createBitcoinWallet(rpcConfig, walletsDataDir, persistableStore);
return WalletFactory.createBitcoinWallet(rpcConfig, walletName, persistableStore);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,11 @@
public class WalletFactory {

public static BitcoinWallet createBitcoinWallet(RpcConfig rpcConfig,
Path walletsDataDir,
String walletName,
BitcoinWalletStore bitcoinWalletStore) {
Path bitcoindDataDir = walletsDataDir.resolve("bitcoind"); // directory name for bitcoind wallet

BitcoindDaemon daemon = createBitcoindDaemon(rpcConfig);
ZmqConnection zmqConnection = initializeBitcoindZeroMq(daemon);
return new BitcoinWallet(bitcoindDataDir, rpcConfig, daemon, bitcoinWalletStore.getReceiveAddresses(), zmqConnection);
return new BitcoinWallet(walletName, rpcConfig, daemon, bitcoinWalletStore.getReceiveAddresses(), zmqConnection);
}

private static BitcoindDaemon createBitcoindDaemon(RpcConfig rpcConfig) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@
import bisq.wallets.bitcoind.rpc.responses.BitcoindGetZmqNotificationsResponse;
import bisq.wallets.core.RpcConfig;
import bisq.wallets.core.exceptions.InvalidRpcCredentialsException;
import bisq.wallets.core.exceptions.RpcCallFailureException;
import bisq.wallets.core.rpc.DaemonRpcClient;
import bisq.wallets.core.rpc.RpcClientFactory;

import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
Expand All @@ -38,21 +38,23 @@ public BitcoindDaemon(DaemonRpcClient rpcClient) {
this.rpcClient = rpcClient;
}

public void createOrLoadWallet(Path walletPath, Optional<String> passphrase) {
createOrLoadWallet(walletPath, passphrase, false, false);
public void createOrLoadWallet(String walletName, Optional<String> passphrase) {
createOrLoadWallet(walletName, passphrase, false, false);
}

public void createOrLoadWatchOnlyWallet(Path walletPath) {
createOrLoadWallet(walletPath, Optional.empty(), true, true);
public void createOrLoadWatchOnlyWallet(String walletName) {
createOrLoadWallet(walletName, Optional.empty(), true, true);
}

private void createOrLoadWallet(Path walletPath, Optional<String> passphrase, boolean disablePrivateKeys, boolean blank) {
if (!doesWalletExist(walletPath)) {
createWallet(walletPath, passphrase.orElse(""), disablePrivateKeys, blank);
} else {
List<String> loadedWallets = listWallets();
if (!loadedWallets.contains(walletPath.toString())) {
loadWallet(walletPath);
private void createOrLoadWallet(String walletName, Optional<String> passphrase, boolean disablePrivateKeys, boolean blank) {
try {
createWallet(walletName, passphrase.orElse(""), disablePrivateKeys, blank);
} catch (RpcCallFailureException e) {
if (doesWalletExist(e)) {
List<String> loadedWallets = listWallets();
if (!loadedWallets.contains(walletName)) {
loadWallet(walletName);
}
}
}
}
Expand Down Expand Up @@ -124,9 +126,8 @@ public void stop() {
rpcClient.invokeAndValidate(rpcCall);
}

public void unloadWallet(Path walletPath) {
String absoluteWalletPath = walletPath.toAbsolutePath().toString();
var request = new BitcoindUnloadWalletRpcCall.Request(absoluteWalletPath);
public void unloadWallet(String walletName) {
var request = new BitcoindUnloadWalletRpcCall.Request(walletName);
var rpcCall = new BitcoindUnloadWalletRpcCall(request);
rpcClient.invokeAndValidate(rpcCall);
}
Expand All @@ -141,13 +142,13 @@ public static boolean verifyRpcConfig(RpcConfig rpcConfig) {
}
}

private boolean doesWalletExist(Path walletPath) {
return walletPath.toFile().exists();
private boolean doesWalletExist(RpcCallFailureException e) {
return e.getCause().getMessage().contains("Database already exists.");
}

private void createWallet(Path walletPath, String passphrase, boolean disablePrivateKeys, boolean blank) {
private void createWallet(String walletName, String passphrase, boolean disablePrivateKeys, boolean blank) {
var request = BitcoindCreateWalletRpcCall.Request.builder()
.walletName(walletPath.toAbsolutePath().toString())
.walletName(walletName)
.disablePrivateKeys(disablePrivateKeys)
.blank(blank)
.passphrase(passphrase)
Expand All @@ -157,9 +158,8 @@ private void createWallet(Path walletPath, String passphrase, boolean disablePri
rpcClient.invokeAndValidate(rpcCall);
}

private void loadWallet(Path walletPath) {
String absoluteWalletPath = walletPath.toAbsolutePath().toString();
var request = new BitcoindLoadWalletRpcCall.Request(absoluteWalletPath);
private void loadWallet(String walletName) {
var request = new BitcoindLoadWalletRpcCall.Request(walletName);
var rpcCall = new BitcoindLoadWalletRpcCall(request);
rpcClient.invokeAndValidate(rpcCall);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import bisq.wallets.core.rpc.WalletRpcClient;

import java.net.MalformedURLException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
Expand All @@ -41,19 +40,19 @@ public class BitcoindWallet {
private final BitcoindDaemon daemon;
private final WalletRpcClient rpcClient;

public BitcoindWallet(BitcoindDaemon daemon, RpcConfig rpcConfig, Path walletPath) throws MalformedURLException {
public BitcoindWallet(BitcoindDaemon daemon, RpcConfig rpcConfig, String walletName) throws MalformedURLException {
this.daemon = daemon;
this.rpcClient = RpcClientFactory.createWalletRpcClient(rpcConfig, walletPath);
this.rpcClient = RpcClientFactory.createWalletRpcClient(rpcConfig, walletName);
}

public void initialize(Optional<String> passphrase) {
Path walletPath = rpcClient.getWalletPath();
daemon.createOrLoadWallet(walletPath, passphrase);
String walletName = rpcClient.getWalletName();
daemon.createOrLoadWallet(walletName, passphrase);
}

public void shutdown() {
Path walletPath = rpcClient.getWalletPath();
daemon.unloadWallet(walletPath);
String walletName = rpcClient.getWalletName();
daemon.unloadWallet(walletName);
}

public BitcoindAddOrCreateMultiSigAddressResponse createMultiSig(int nRequired, List<String> keys) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/

package bisq.wallets.bitcoind;

import bisq.wallets.bitcoind.regtest.BitcoindExtension;
import bisq.wallets.bitcoind.rpc.BitcoindDaemon;
import bisq.wallets.bitcoind.rpc.calls.BitcoindCreateWalletRpcCall;
import bisq.wallets.core.RpcConfig;
import bisq.wallets.core.rpc.DaemonRpcClient;
import bisq.wallets.core.rpc.RpcClientFactory;
import bisq.wallets.regtest.AbstractRegtestSetup;
import bisq.wallets.regtest.bitcoind.BitcoindRegtestSetup;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

import static org.assertj.core.api.Assertions.assertThat;

@ExtendWith(BitcoindExtension.class)
public class BitcoindCreateWalletNameDocumentationIntegrationTests {
private final Path dataDir;
private final DaemonRpcClient rpcClient;
private final BitcoindDaemon daemon;

public BitcoindCreateWalletNameDocumentationIntegrationTests(BitcoindRegtestSetup regtestSetup) {
this.dataDir = regtestSetup.getDataDir();
this.daemon = regtestSetup.getDaemon();
RpcConfig rpcConfig = regtestSetup.getRpcConfig();
this.rpcClient = RpcClientFactory.createDaemonRpcClient(rpcConfig);
}

@Test
@EnabledOnOs({OS.MAC, OS.LINUX})
void createWalletWithAbsolutePath(@TempDir Path walletPath) {
String walletName = walletPath.toAbsolutePath().toString();
var request = BitcoindCreateWalletRpcCall.Request.builder()
.walletName(walletName)
.passphrase(AbstractRegtestSetup.WALLET_PASSPHRASE)
.build();

var rpcCall = new BitcoindCreateWalletRpcCall(request);
rpcClient.invokeAndValidate(rpcCall);

File walletFile = walletPath.resolve("wallet.dat")
.toFile();
assertThat(walletFile).exists();

daemon.unloadWallet(walletName);
}

@Test
@EnabledOnOs({OS.MAC, OS.LINUX})
void createWalletWithRelativePath() throws IOException {
File bitcoindWalletsDir = dataDir.resolve("regtest")
.resolve("wallets")
.toFile();
Path newWalletDir = Files.createTempDirectory(bitcoindWalletsDir.toPath(), "bisq_");

String walletName = newWalletDir.getFileName() + "/b";
var request = BitcoindCreateWalletRpcCall.Request.builder()
.walletName(walletName)
.passphrase(AbstractRegtestSetup.WALLET_PASSPHRASE)
.build();

var rpcCall = new BitcoindCreateWalletRpcCall(request);
rpcClient.invokeAndValidate(rpcCall);

File expectedWalletFile = newWalletDir.resolve("b")
.resolve("wallet.dat")
.toFile();
assertThat(expectedWalletFile).exists();

daemon.unloadWallet(walletName);
}
}
Loading

0 comments on commit 796d5e4

Please sign in to comment.