[bisq-network/bisq] Add local Bitcoin node configuration detection (#3982)

Christoph Atteneder notifications at github.com
Wed Feb 26 10:42:31 UTC 2020


ripcurlx requested changes on this pull request.

NACK

Please see my remarks in the code.

I tested the functionality on Regtest with a local Bitcoin Core and I don't get the informational popup anymore when running the client with a properly configured client
![Bildschirmfoto 2020-02-26 um 11 29 59](https://user-images.githubusercontent.com/170962/75336389-5b14a700-588b-11ea-927e-3b71ee4d763c.png)
Having peer bloom filters not configured and I also don't get the warning. Doesn't it work with the local Regtest setup?

>              nextStep.run();
             return;
         }
 
-        localBitcoinNode.detectAndRun(nextStep);
+        // Here we only want to provide the user with a choice (in a popup) in case a
+        // local node is detected, but badly configured.
+        var detectedButMisconfigured = localBitcoinNode.isDetectedButMisconfigured();
+        if (detectedButMisconfigured) {
+            displayLocalNodeMisconfigurationHandler.accept(nextStep);

```suggestion
            if (displayLocalNodeMisconfigurationHandler != null) {
                displayLocalNodeMisconfigurationHandler.accept(nextStep);
            }
```

Add a check to prevent possible NullPointerException.

> @@ -559,7 +576,7 @@ else if (displayTorNetworkSettingsHandler != null)
 
         // We only init wallet service here if not using Tor for bitcoinj.
         // When using Tor, wallet init must be deferred until Tor is ready.
-        if (!preferences.getUseTorForBitcoinJ() || localBitcoinNode.isDetected()) {

If `localBitcoinNode.willUse() == true` and `config.useTorForBtcOptionSetExplicitly == true` the new code will behave differently than before. So I don't think we should remove this from here.

> +    }
+
+    /* Performs a blocking Bitcoin protocol handshake, which includes exchanging version
+     * messages and acks. Its purpose is to check if a local Bitcoin node is running,
+     * and, if it is, check its advertised configuration. The returned Optional is empty,
+     * if a local peer wasn't found, or if handshake failed for some reason. This method
+     * could be noticably simplified, by turning connection failure callback into a
+     * future and using a first-future-to-complete type of construct, but I couldn't find
+     * a ready-made implementation.
+     */
+    private Optional<VersionMessage> attemptHandshakeForVersionMessage() {
+        Peer peer;
+        try {
+            peer = createLocalPeer(port);
+        } catch (UnknownHostException ex) {
+            log.error("Local bitcoin node handshake attempt unexpectedly threw: {}", ex);

Using it like that the placeholder will never be filled. Maybe something like:
```suggestion
            log.error("Local bitcoin node handshake attempt was unexpectedly interrupted", ex);
```



> +        /* We temporarily silence BitcoinJ NioClient's and NioClientManager's loggers,
+         * because when a local Bitcoin node is not found they pollute console output
+         * with "connection refused" error messages.
+         */
+        var originalNioClientLoggerLevel = silence(NioClient.class);
+        var originalNioClientManagerLoggerLevel = silence(NioClientManager.class);
+
+        NioClient client;
+
+        try {
+            log.info("Initiating attempt to connect to and handshake with a local "
+                    + "Bitcoin node (which may or may not be running) on port {}.",
+                    port);
+            client = createClient(peer, port, CONNECTION_TIMEOUT);
+        } catch (IOException ex) {
+            log.error("Local bitcoin node handshake attempt unexpectedly threw: {}", ex);

Using it like that the placeholder will never be filled. Maybe something like:
```suggestion
            log.error("Local bitcoin node handshake attempt was unexpectedly interrupted", ex);
```

> +        var peer = new Peer(networkParameters, ourVersionMessage, localPeerAddress, blockchain);
+        return peer;

Not sure if that makes it so much more readable than simply:
```
return new Peer(networkParameters, ourVersionMessage, localPeerAddress, blockchain);
```

> +        NioClient client = new NioClient(serverAddress, peer, connectionTimeout);
+
+        return client;

Same here:
```
return new NioClient(serverAddress, peer, connectionTimeout);
```

> +        AbstractBlockChain blockchain = null;
+
+        var peer = new Peer(networkParameters, ourVersionMessage, localPeerAddress, blockchain);

I know that you do this because you use Vim, but everyone with a more sophisticated editor has this parameter information next to the null value in the constructor instantiation

> +
+    /* Creates an NioClient that is expected to only be used to coerce a VersionMessage
+     * out of a local Bitcoin node and be closed right after.
+     */
+    private static NioClient createClient(Peer peer, int port, int connectionTimeout) throws IOException {
+        InetSocketAddress serverAddress =
+                new InetSocketAddress(InetAddress.getLocalHost(), port);
+
+        // This initiates the handshake procedure, which, if successful, will complete
+        // the peerVersionMessageFuture, or be cancelled, in case of failure.
+        NioClient client = new NioClient(serverAddress, peer, connectionTimeout);
+
+        return client;
+    }
+
+    private static Level silence(Class klass) {

```suggestion
    private static Level silence(Class<?> klass) {
```

> +
+        // This initiates the handshake procedure, which, if successful, will complete
+        // the peerVersionMessageFuture, or be cancelled, in case of failure.
+        NioClient client = new NioClient(serverAddress, peer, connectionTimeout);
+
+        return client;
+    }
+
+    private static Level silence(Class klass) {
+        var logger = getLogger(klass);
+        var originalLevel = logger.getLevel();
+        logger.setLevel(Level.OFF);
+        return originalLevel;
+    }
+
+    private static void restoreLoggerLevel(Class klass, Level originalLevel) {

```suggestion
    private static void restoreLoggerLevel(Class<?> klass, Level originalLevel) {
```

> +
+        return client;
+    }
+
+    private static Level silence(Class klass) {
+        var logger = getLogger(klass);
+        var originalLevel = logger.getLevel();
+        logger.setLevel(Level.OFF);
+        return originalLevel;
+    }
+
+    private static void restoreLoggerLevel(Class klass, Level originalLevel) {
+        getLogger(klass).setLevel(originalLevel);
+    }
+
+    private static ch.qos.logback.classic.Logger getLogger(Class klass) {

```suggestion
    private static ch.qos.logback.classic.Logger getLogger(Class<?> klass) {
```

> +        return originalLevel;
+    }
+
+    private static void restoreLoggerLevel(Class klass, Level originalLevel) {
+        getLogger(klass).setLevel(originalLevel);
+    }
+
+    private static ch.qos.logback.classic.Logger getLogger(Class klass) {
+        return (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(klass);
+    }
+
+    private ListenableFuture<VersionMessage> getVersionMessage(Peer peer) {
+        SettableFuture<VersionMessage> peerVersionMessageFuture = SettableFuture.create();
+
+        var versionHandshakeDone = peer.getVersionHandshakeFuture();
+        FutureCallback<Peer> fetchPeerVersionMessage = new FutureCallback<Peer>() {

```suggestion
        FutureCallback<Peer> fetchPeerVersionMessage = new FutureCallback<>() {
```

> +        getLogger(klass).setLevel(originalLevel);
+    }
+
+    private static ch.qos.logback.classic.Logger getLogger(Class klass) {
+        return (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(klass);
+    }
+
+    private ListenableFuture<VersionMessage> getVersionMessage(Peer peer) {
+        SettableFuture<VersionMessage> peerVersionMessageFuture = SettableFuture.create();
+
+        var versionHandshakeDone = peer.getVersionHandshakeFuture();
+        FutureCallback<Peer> fetchPeerVersionMessage = new FutureCallback<Peer>() {
+            public void onSuccess(Peer peer) {
+                peerVersionMessageFuture.set(peer.getPeerVersionMessage());
+            }
+            public void onFailure(Throwable thr) {

```suggestion
            public void onFailure(@NotNull Throwable thr) {
```

> +                new PeerDisconnectedEventListener() {
+                    public void onPeerDisconnected(Peer peer, int peerCount) {

Can be replaced with:
```
(disconnectedPeer, peerCount) -> {
```

> +
+        PeerDisconnectedEventListener cancelIfConnectionFails =
+                new PeerDisconnectedEventListener() {
+                    public void onPeerDisconnected(Peer peer, int peerCount) {
+                        var peerVersionMessageAlreadyReceived =
+                                peerVersionMessageFuture.isDone();
+                        if (peerVersionMessageAlreadyReceived) {
+                            // This method is called whether or not the handshake was
+                            // successful. In case it was successful, we don't want to do
+                            // anything here.
+                            return;
+                        }
+                        // In some cases Peer will self-disconnect after receiving
+                        // node's VersionMessage, but before completing the handshake.
+                        // In such a case, we want to retrieve the VersionMessage.
+                        var peerVersionMessage = peer.getPeerVersionMessage();

That would need also to change this to:
```suggestion
                        var peerVersionMessage = disconnectedPeer.getPeerVersionMessage();
```
to prevent scope issues with the other peer passed as parameter. This makes it also easier to distinguish that they are different peers.

> +                            var mayInterruptWhileRunning = true;
+                            peerVersionMessageFuture.cancel(mayInterruptWhileRunning);

Here again: This is something that helps only someone not using code editors like IntelliJ.

> +    public boolean willUse() {
+        return !willIgnore() && isUsable();
+    }
+
+    /**
+     * Returns whether Bisq will ignore a local Bitcoin node even if it is usable.
+     */
+    public boolean willIgnore() {
+        BaseCurrencyNetwork baseCurrencyNetwork = config.baseCurrencyNetwork;
+
+        // For dao testnet (server side regtest) we disable the use of local bitcoin node to
+        // avoid confusion if local btc node is not synced with our dao testnet master node.
+        // Note: above comment was previously in WalletConfig::createPeerGroup.
+
+        return config.ignoreLocalBtcNode
+            || baseCurrencyNetwork.isDaoRegTest()

```suggestion
                || baseCurrencyNetwork.isDaoRegTest()
```

> +        return !willIgnore() && isUsable();
+    }
+
+    /**
+     * Returns whether Bisq will ignore a local Bitcoin node even if it is usable.
+     */
+    public boolean willIgnore() {
+        BaseCurrencyNetwork baseCurrencyNetwork = config.baseCurrencyNetwork;
+
+        // For dao testnet (server side regtest) we disable the use of local bitcoin node to
+        // avoid confusion if local btc node is not synced with our dao testnet master node.
+        // Note: above comment was previously in WalletConfig::createPeerGroup.
+
+        return config.ignoreLocalBtcNode
+            || baseCurrencyNetwork.isDaoRegTest()
+            || baseCurrencyNetwork.isDaoTestNet();

```suggestion
                || baseCurrencyNetwork.isDaoTestNet();
```

> +    }
+
+    /* Initiates detection and configuration checks. The results are cached so that the
+     * public methods isUsable, isDetected, etc. don't trigger a recheck.
+     */
+    private void checkUsable() {
+        var optionalVersionMessage = attemptHandshakeForVersionMessage();
+        handleHandshakeAttempt(optionalVersionMessage);
+    }
+
+    private void handleHandshakeAttempt(Optional<VersionMessage> optionalVersionMessage) {
+        if (!optionalVersionMessage.isPresent()) {
+            detected = false;
+            wellConfigured = false;
+            log.info("No local Bitcoin node detected on port {},"
+                    + " or the connection was prematurely closed"

```suggestion
                            + " or the connection was prematurely closed"
```

> +
+    /* Initiates detection and configuration checks. The results are cached so that the
+     * public methods isUsable, isDetected, etc. don't trigger a recheck.
+     */
+    private void checkUsable() {
+        var optionalVersionMessage = attemptHandshakeForVersionMessage();
+        handleHandshakeAttempt(optionalVersionMessage);
+    }
+
+    private void handleHandshakeAttempt(Optional<VersionMessage> optionalVersionMessage) {
+        if (!optionalVersionMessage.isPresent()) {
+            detected = false;
+            wellConfigured = false;
+            log.info("No local Bitcoin node detected on port {},"
+                    + " or the connection was prematurely closed"
+                    + " (before a version messages could be coerced)",

```suggestion
                            + " (before a version messages could be coerced)",
```

> +            log.error("Local bitcoin node handshake attempt unexpectedly threw: {}", ex);
+            return Optional.empty();
+        }
+
+        /* We temporarily silence BitcoinJ NioClient's and NioClientManager's loggers,
+         * because when a local Bitcoin node is not found they pollute console output
+         * with "connection refused" error messages.
+         */
+        var originalNioClientLoggerLevel = silence(NioClient.class);
+        var originalNioClientManagerLoggerLevel = silence(NioClientManager.class);
+
+        NioClient client;
+
+        try {
+            log.info("Initiating attempt to connect to and handshake with a local "
+                    + "Bitcoin node (which may or may not be running) on port {}.",

```suggestion
                            + "Bitcoin node (which may or may not be running) on port {}.",
```

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/bisq-network/bisq/pull/3982#pullrequestreview-364736228
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.bisq.network/pipermail/bisq-github/attachments/20200226/08a47bc2/attachment-0001.html>


More information about the bisq-github mailing list