diff --git a/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java b/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java index 14622a291d..b15aa5312a 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java @@ -1,7 +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 io.bisq.core.btc.wallet; import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; import io.bisq.core.btc.BitcoinNodes.BtcNode; +import io.bisq.network.DnsLookupException; import io.bisq.network.DnsLookupTor; import org.bitcoinj.core.PeerAddress; import org.bitcoinj.net.OnionCat; @@ -17,6 +35,16 @@ import java.util.Objects; class BtcNodeConverter { private static final Logger log = LoggerFactory.getLogger(BtcNodeConverter.class); + private final Facade facade; + + BtcNodeConverter() { + this.facade = new Facade(); + } + + BtcNodeConverter(Facade facade) { + this.facade = facade; + } + @Nullable PeerAddress convertOnionHost(BtcNode node) { // no DNS lookup for onion addresses @@ -24,7 +52,7 @@ class BtcNodeConverter { try { // OnionCat.onionHostToInetAddress converts onion to ipv6 representation // inetAddress is not used but required for wallet persistence. Throws nullPointer if not set. - InetAddress inetAddress = OnionCat.onionHostToInetAddress(onionAddress); + InetAddress inetAddress = facade.onionHostToInetAddress(onionAddress); PeerAddress result = new PeerAddress(onionAddress, node.getPort()); result.setAddr(inetAddress); return result; @@ -67,11 +95,11 @@ class BtcNodeConverter { } @Nullable - private static PeerAddress create(Socks5Proxy proxy, String host, int port) { + private PeerAddress create(Socks5Proxy proxy, String host, int port) { try { // We use DnsLookupTor to not leak with DNS lookup // Blocking call. takes about 600 ms ;-( - InetAddress lookupAddress = DnsLookupTor.lookup(proxy, host); + InetAddress lookupAddress = facade.torLookup(proxy, host); InetSocketAddress address = new InetSocketAddress(lookupAddress, port); return new PeerAddress(address.getAddress(), address.getPort()); } catch (Exception e) { @@ -90,5 +118,15 @@ class BtcNodeConverter { return null; } } + + static class Facade { + InetAddress onionHostToInetAddress(String onionAddress) throws UnknownHostException { + return OnionCat.onionHostToInetAddress(onionAddress); + } + + InetAddress torLookup(Socks5Proxy proxy, String host) throws DnsLookupException { + return DnsLookupTor.lookup(proxy, host); + } + } } diff --git a/core/src/test/java/io/bisq/core/btc/wallet/BtcNodeConverterTest.java b/core/src/test/java/io/bisq/core/btc/wallet/BtcNodeConverterTest.java new file mode 100644 index 0000000000..84feafd9f4 --- /dev/null +++ b/core/src/test/java/io/bisq/core/btc/wallet/BtcNodeConverterTest.java @@ -0,0 +1,76 @@ +package io.bisq.core.btc.wallet; + +import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; +import io.bisq.core.btc.BitcoinNodes.BtcNode; +import io.bisq.core.btc.wallet.BtcNodeConverter.Facade; +import io.bisq.network.DnsLookupException; +import org.bitcoinj.core.PeerAddress; +import org.junit.Test; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BtcNodeConverterTest { + @Test + public void testConvertOnionHost() throws UnknownHostException { + BtcNode node = mock(BtcNode.class); + when(node.getOnionAddress()).thenReturn("aaa.onion"); + + InetAddress inetAddress = mock(InetAddress.class); + + Facade facade = mock(Facade.class); + when(facade.onionHostToInetAddress(any())).thenReturn(inetAddress); + + PeerAddress peerAddress = new BtcNodeConverter(facade).convertOnionHost(node); + // noinspection ConstantConditions + assertEquals(inetAddress, peerAddress.getAddr()); + } + + @Test + public void testConvertOnionHostOnFailure() throws UnknownHostException { + BtcNode node = mock(BtcNode.class); + when(node.getOnionAddress()).thenReturn("aaa.onion"); + + Facade facade = mock(Facade.class); + when(facade.onionHostToInetAddress(any())).thenThrow(UnknownHostException.class); + + PeerAddress peerAddress = new BtcNodeConverter(facade).convertOnionHost(node); + assertNull(peerAddress); + } + + @Test + public void testConvertClearNode() { + final String ip = "192.168.0.1"; + + BtcNode node = mock(BtcNode.class); + when(node.getHostNameOrAddress()).thenReturn(ip); + + PeerAddress peerAddress = new BtcNodeConverter().convertClearNode(node); + // noinspection ConstantConditions + InetAddress inetAddress = peerAddress.getAddr(); + assertEquals(ip, inetAddress.getHostName()); + } + + @Test + public void testConvertWithTor() throws DnsLookupException { + InetAddress expected = mock(InetAddress.class); + + Facade facade = mock(Facade.class); + when(facade.torLookup(any(), anyString())).thenReturn(expected); + + BtcNode node = mock(BtcNode.class); + when(node.getHostNameOrAddress()).thenReturn("aaa.onion"); + + PeerAddress peerAddress = new BtcNodeConverter(facade).convertWithTor(node, mock(Socks5Proxy.class)); + + // noinspection ConstantConditions + assertEquals(expected, peerAddress.getAddr()); + } +}