diff --git a/wallets/src/main/java/bisq/wallets/bitcoind/BitcoindWallet.java b/wallets/src/main/java/bisq/wallets/bitcoind/BitcoindWallet.java index f836756eb1..54247a26ef 100644 --- a/wallets/src/main/java/bisq/wallets/bitcoind/BitcoindWallet.java +++ b/wallets/src/main/java/bisq/wallets/bitcoind/BitcoindWallet.java @@ -17,12 +17,13 @@ package bisq.wallets.bitcoind; -import bisq.wallets.*; +import bisq.wallets.AddressType; +import bisq.wallets.Wallet; import bisq.wallets.bitcoind.responses.ListTransactionsResponseEntry; import bisq.wallets.bitcoind.responses.ListUnspentResponseEntry; -import bisq.wallets.bitcoind.rpc.*; -import bisq.wallets.exceptions.InvalidRpcCredentialsException; -import bisq.wallets.exceptions.RpcCallFailureException; +import bisq.wallets.bitcoind.rpc.RpcClient; +import bisq.wallets.bitcoind.rpc.RpcConfig; +import bisq.wallets.bitcoind.rpc.WalletRpcConfig; import bisq.wallets.exceptions.WalletInitializationFailedException; import bisq.wallets.model.Transaction; import bisq.wallets.model.Utxo; @@ -55,12 +56,8 @@ public BitcoindWallet(Path walletPath, RpcConfig rpcConfig) { @Override public void initialize(String walletPassphrase) { - try { - chainBackend.createOrLoadWallet(walletPath, walletPassphrase, false, false); - walletBackend.walletPassphrase(walletPassphrase, BitcoindWalletBackend.DEFAULT_WALLET_TIMEOUT); - } catch (RpcCallFailureException e) { - throw new InvalidRpcCredentialsException(e); - } + chainBackend.createOrLoadWallet(walletPath, walletPassphrase, false, false); + walletBackend.walletPassphrase(walletPassphrase, BitcoindWalletBackend.DEFAULT_WALLET_TIMEOUT); } @Override diff --git a/wallets/src/main/java/bisq/wallets/bitcoind/rpc/RpcClient.java b/wallets/src/main/java/bisq/wallets/bitcoind/rpc/RpcClient.java index 0e309729e5..62aea452c5 100644 --- a/wallets/src/main/java/bisq/wallets/bitcoind/rpc/RpcClient.java +++ b/wallets/src/main/java/bisq/wallets/bitcoind/rpc/RpcClient.java @@ -19,9 +19,12 @@ import bisq.common.encoding.Base64; import bisq.wallets.bitcoind.BitcoindRpcEndpoint; +import bisq.wallets.exceptions.CannotConnectToWalletException; +import bisq.wallets.exceptions.InvalidRpcCredentialsException; import bisq.wallets.exceptions.RpcCallFailureException; import com.googlecode.jsonrpc4j.JsonRpcHttpClient; +import java.net.ConnectException; import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.StandardCharsets; @@ -44,7 +47,13 @@ public RpcClient(RpcConfig config) throws MalformedURLException { public T invoke(BitcoindRpcEndpoint rpcEndpoint, Object argument, Class clazz) { try { return jsonRpcClient.invoke(rpcEndpoint.getMethodName(), argument, clazz); - } catch (Throwable t) { + } catch (ConnectException e) { + throw new CannotConnectToWalletException(e); + } + catch (Throwable t) { + if (rpcAuthenticationFailed(t)) { + throw new InvalidRpcCredentialsException("Invalid RPC credentials", t); + } throw new RpcCallFailureException("RPC call to " + rpcEndpoint.getMethodName() + " failed.", t); } } @@ -64,6 +73,10 @@ private JsonRpcHttpClient createRpcClientWithUrlSuffix(RpcConfig rpcConfig, Opti return new JsonRpcHttpClient(new URL(url), createAuthHeader(rpcConfig)); } + private boolean rpcAuthenticationFailed(Throwable t) { + return t.getCause().toString().contains("401"); + } + private Map createAuthHeader(RpcConfig rpcConfig) { String auth = rpcConfig.user() + ":" + rpcConfig.password(); String base64Auth = Base64.encode(auth.getBytes(StandardCharsets.UTF_8)); diff --git a/wallets/src/main/java/bisq/wallets/exceptions/CannotConnectToWalletException.java b/wallets/src/main/java/bisq/wallets/exceptions/CannotConnectToWalletException.java new file mode 100644 index 0000000000..9a7a717056 --- /dev/null +++ b/wallets/src/main/java/bisq/wallets/exceptions/CannotConnectToWalletException.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.wallets.exceptions; + +public class CannotConnectToWalletException extends RuntimeException { + public CannotConnectToWalletException(Throwable cause) { + super(cause); + } +} diff --git a/wallets/src/main/java/bisq/wallets/exceptions/InvalidRpcCredentialsException.java b/wallets/src/main/java/bisq/wallets/exceptions/InvalidRpcCredentialsException.java index a82adfc126..bde5cbc14b 100644 --- a/wallets/src/main/java/bisq/wallets/exceptions/InvalidRpcCredentialsException.java +++ b/wallets/src/main/java/bisq/wallets/exceptions/InvalidRpcCredentialsException.java @@ -18,7 +18,7 @@ package bisq.wallets.exceptions; public class InvalidRpcCredentialsException extends RuntimeException { - public InvalidRpcCredentialsException(Throwable cause) { - super(cause); + public InvalidRpcCredentialsException(String message, Throwable cause) { + super(message, cause); } } diff --git a/wallets/src/test/java/bisq/wallets/bitcoind/BitcoindConnectionFailureTests.java b/wallets/src/test/java/bisq/wallets/bitcoind/BitcoindConnectionFailureTests.java new file mode 100644 index 0000000000..5f2199e3bc --- /dev/null +++ b/wallets/src/test/java/bisq/wallets/bitcoind/BitcoindConnectionFailureTests.java @@ -0,0 +1,62 @@ +/* + * 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; + +import bisq.wallets.NetworkType; +import bisq.wallets.bitcoind.rpc.RpcClient; +import bisq.wallets.bitcoind.rpc.RpcConfig; +import bisq.wallets.exceptions.CannotConnectToWalletException; +import bisq.wallets.exceptions.InvalidRpcCredentialsException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.MalformedURLException; + +public class BitcoindConnectionFailureTests { + @Test + void bitcoindNotRunningTest() throws MalformedURLException { + var rpcClient = new RpcClient(BitcoindRegtestSetup.RPC_CONFIG); + var minerChainBackend = new BitcoindChainBackend(rpcClient); + + CannotConnectToWalletException exception = Assertions + .assertThrows(CannotConnectToWalletException.class, minerChainBackend::listWallets); + + Assertions.assertTrue(exception.getCause() instanceof ConnectException); + } + + @Test + void wrongRpcCredentialsTest() throws IOException { + BitcoindProcess bitcoindProcess = BitcoindRegtestSetup.createAndStartBitcoind(); + + RpcConfig rpcConfig = new RpcConfig.Builder() + .networkType(NetworkType.REGTEST) + .hostname("127.0.0.1") + .user("bisq") + .password("WRONG_PASSWORD") + .build(); + + var rpcClient = new RpcClient(rpcConfig); + var minerChainBackend = new BitcoindChainBackend(rpcClient); + + Assertions.assertThrows(InvalidRpcCredentialsException.class, minerChainBackend::listWallets); + + bitcoindProcess.stopAndWaitUntilStopped(); + } +}