/**
  * Closes the channel with the client (will generate a {@link
  * ServerConnectionEventHandler#channelClosed(PaymentChannelCloseException.CloseReason)} event)
  *
  * <p>Note that this does <i>NOT</i> actually broadcast the most recent payment transaction, which
  * will be triggered automatically when the channel times out by the {@link
  * StoredPaymentChannelServerStates}, or manually by calling {@link
  * StoredPaymentChannelServerStates#closeChannel(StoredServerChannel)} with the channel returned
  * by {@link StoredPaymentChannelServerStates#getChannel(com.google.bitcoin.core.Sha256Hash)} with
  * the id provided in {@link
  * ServerConnectionEventHandler#channelOpen(com.google.bitcoin.core.Sha256Hash)}
  */
 @SuppressWarnings("unchecked")
 // The warning 'unchecked call to write(MessageType)' being suppressed here comes from the build()
 // formally returning MessageLite-derived class that cannot be statically guaranteed to be the
 // same MessageType
 // that is used in connectionChannel.
 protected final synchronized void closeChannel() {
   if (connectionChannel == null)
     throw new IllegalStateException("Channel is not fully initialized/has already been closed");
   connectionChannel.write(
       Protos.TwoWayChannelMessage.newBuilder()
           .setType(Protos.TwoWayChannelMessage.MessageType.CLOSE)
           .build());
   connectionChannel.closeConnection();
 }
  @Test
  public void basicClientServerTest() throws Exception {
    // Tests creating a basic server, opening a client connection and sending a few messages

    final SettableFuture<Void> serverConnectionOpen = SettableFuture.create();
    final SettableFuture<Void> clientConnectionOpen = SettableFuture.create();
    final SettableFuture<Void> serverConnectionClosed = SettableFuture.create();
    final SettableFuture<Void> clientConnectionClosed = SettableFuture.create();
    final SettableFuture<Protos.TwoWayChannelMessage> clientMessage1Received =
        SettableFuture.create();
    final SettableFuture<Protos.TwoWayChannelMessage> clientMessage2Received =
        SettableFuture.create();
    NioServer server =
        new NioServer(
            new StreamParserFactory() {
              @Override
              public ProtobufParser getNewParser(InetAddress inetAddress, int port) {
                return new ProtobufParser<Protos.TwoWayChannelMessage>(
                    new ProtobufParser.Listener<Protos.TwoWayChannelMessage>() {
                      @Override
                      public void messageReceived(
                          ProtobufParser<Protos.TwoWayChannelMessage> handler,
                          Protos.TwoWayChannelMessage msg) {
                        handler.write(msg);
                        handler.write(msg);
                      }

                      @Override
                      public void connectionOpen(ProtobufParser handler) {
                        serverConnectionOpen.set(null);
                      }

                      @Override
                      public void connectionClosed(ProtobufParser handler) {
                        serverConnectionClosed.set(null);
                      }
                    },
                    Protos.TwoWayChannelMessage.getDefaultInstance(),
                    1000,
                    0);
              }
            },
            new InetSocketAddress("localhost", 4243));
    server.startAndWait();

    ProtobufParser<Protos.TwoWayChannelMessage> clientHandler =
        new ProtobufParser<Protos.TwoWayChannelMessage>(
            new ProtobufParser.Listener<Protos.TwoWayChannelMessage>() {
              @Override
              public synchronized void messageReceived(
                  ProtobufParser handler, Protos.TwoWayChannelMessage msg) {
                if (clientMessage1Received.isDone()) clientMessage2Received.set(msg);
                else clientMessage1Received.set(msg);
              }

              @Override
              public void connectionOpen(ProtobufParser handler) {
                clientConnectionOpen.set(null);
              }

              @Override
              public void connectionClosed(ProtobufParser handler) {
                clientConnectionClosed.set(null);
              }
            },
            Protos.TwoWayChannelMessage.getDefaultInstance(),
            1000,
            0);

    MessageWriteTarget client =
        openConnection(new InetSocketAddress("localhost", 4243), clientHandler);

    clientConnectionOpen.get();
    serverConnectionOpen.get();

    Protos.TwoWayChannelMessage msg =
        Protos.TwoWayChannelMessage.newBuilder()
            .setType(Protos.TwoWayChannelMessage.MessageType.CHANNEL_OPEN)
            .build();
    clientHandler.write(msg);
    assertEquals(msg, clientMessage1Received.get());
    assertEquals(msg, clientMessage2Received.get());

    client.closeConnection();
    serverConnectionClosed.get();
    clientConnectionClosed.get();

    server.stopAndWait();
    assertFalse(server.isRunning());
  }
  @Test
  public void testConnectionEventHandlers() throws Exception {
    final SettableFuture<Void> serverConnection1Open = SettableFuture.create();
    final SettableFuture<Void> serverConnection2Open = SettableFuture.create();
    final SettableFuture<Void> serverConnection3Open = SettableFuture.create();
    final SettableFuture<Void> client1ConnectionOpen = SettableFuture.create();
    final SettableFuture<Void> client2ConnectionOpen = SettableFuture.create();
    final SettableFuture<Void> client3ConnectionOpen = SettableFuture.create();
    final SettableFuture<Void> serverConnectionClosed1 = SettableFuture.create();
    final SettableFuture<Void> serverConnectionClosed2 = SettableFuture.create();
    final SettableFuture<Void> serverConnectionClosed3 = SettableFuture.create();
    final SettableFuture<Void> client1ConnectionClosed = SettableFuture.create();
    final SettableFuture<Void> client2ConnectionClosed = SettableFuture.create();
    final SettableFuture<Void> client3ConnectionClosed = SettableFuture.create();
    final SettableFuture<Protos.TwoWayChannelMessage> client1MessageReceived =
        SettableFuture.create();
    final SettableFuture<Protos.TwoWayChannelMessage> client2MessageReceived =
        SettableFuture.create();
    final SettableFuture<Protos.TwoWayChannelMessage> client3MessageReceived =
        SettableFuture.create();
    NioServer server =
        new NioServer(
            new StreamParserFactory() {
              @Override
              public ProtobufParser getNewParser(InetAddress inetAddress, int port) {
                return new ProtobufParser<Protos.TwoWayChannelMessage>(
                    new ProtobufParser.Listener<Protos.TwoWayChannelMessage>() {
                      @Override
                      public void messageReceived(
                          ProtobufParser<Protos.TwoWayChannelMessage> handler,
                          Protos.TwoWayChannelMessage msg) {
                        handler.write(msg);
                      }

                      @Override
                      public synchronized void connectionOpen(ProtobufParser handler) {
                        if (serverConnection1Open.isDone()) {
                          if (serverConnection2Open.isDone()) serverConnection3Open.set(null);
                          else serverConnection2Open.set(null);
                        } else serverConnection1Open.set(null);
                      }

                      @Override
                      public synchronized void connectionClosed(ProtobufParser handler) {
                        if (serverConnectionClosed1.isDone()) {
                          if (serverConnectionClosed2.isDone()) {
                            checkState(!serverConnectionClosed3.isDone());
                            serverConnectionClosed3.set(null);
                          } else serverConnectionClosed2.set(null);
                        } else serverConnectionClosed1.set(null);
                      }
                    },
                    Protos.TwoWayChannelMessage.getDefaultInstance(),
                    1000,
                    0);
              }
            },
            new InetSocketAddress("localhost", 4243));
    server.startAndWait();

    ProtobufParser<Protos.TwoWayChannelMessage> client1Handler =
        new ProtobufParser<Protos.TwoWayChannelMessage>(
            new ProtobufParser.Listener<Protos.TwoWayChannelMessage>() {
              @Override
              public void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) {
                client1MessageReceived.set(msg);
              }

              @Override
              public void connectionOpen(ProtobufParser handler) {
                client1ConnectionOpen.set(null);
              }

              @Override
              public void connectionClosed(ProtobufParser handler) {
                client1ConnectionClosed.set(null);
              }
            },
            Protos.TwoWayChannelMessage.getDefaultInstance(),
            1000,
            0);
    MessageWriteTarget client1 =
        openConnection(new InetSocketAddress("localhost", 4243), client1Handler);

    client1ConnectionOpen.get();
    serverConnection1Open.get();

    ProtobufParser<Protos.TwoWayChannelMessage> client2Handler =
        new ProtobufParser<Protos.TwoWayChannelMessage>(
            new ProtobufParser.Listener<Protos.TwoWayChannelMessage>() {
              @Override
              public void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) {
                client2MessageReceived.set(msg);
              }

              @Override
              public void connectionOpen(ProtobufParser handler) {
                client2ConnectionOpen.set(null);
              }

              @Override
              public void connectionClosed(ProtobufParser handler) {
                client2ConnectionClosed.set(null);
              }
            },
            Protos.TwoWayChannelMessage.getDefaultInstance(),
            1000,
            0);
    openConnection(new InetSocketAddress("localhost", 4243), client2Handler);

    client2ConnectionOpen.get();
    serverConnection2Open.get();

    ProtobufParser<Protos.TwoWayChannelMessage> client3Handler =
        new ProtobufParser<Protos.TwoWayChannelMessage>(
            new ProtobufParser.Listener<Protos.TwoWayChannelMessage>() {
              @Override
              public void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) {
                client3MessageReceived.set(msg);
              }

              @Override
              public void connectionOpen(ProtobufParser handler) {
                client3ConnectionOpen.set(null);
              }

              @Override
              public synchronized void connectionClosed(ProtobufParser handler) {
                checkState(!client3ConnectionClosed.isDone());
                client3ConnectionClosed.set(null);
              }
            },
            Protos.TwoWayChannelMessage.getDefaultInstance(),
            1000,
            0);
    NioClient client3 = new NioClient(new InetSocketAddress("localhost", 4243), client3Handler, 0);

    client3ConnectionOpen.get();
    serverConnection3Open.get();

    Protos.TwoWayChannelMessage msg =
        Protos.TwoWayChannelMessage.newBuilder()
            .setType(Protos.TwoWayChannelMessage.MessageType.CHANNEL_OPEN)
            .build();
    client1Handler.write(msg);
    assertEquals(msg, client1MessageReceived.get());

    Protos.TwoWayChannelMessage msg2 =
        Protos.TwoWayChannelMessage.newBuilder()
            .setType(Protos.TwoWayChannelMessage.MessageType.INITIATE)
            .build();
    client2Handler.write(msg2);
    assertEquals(msg2, client2MessageReceived.get());

    client1.closeConnection();
    serverConnectionClosed1.get();
    client1ConnectionClosed.get();

    Protos.TwoWayChannelMessage msg3 =
        Protos.TwoWayChannelMessage.newBuilder()
            .setType(Protos.TwoWayChannelMessage.MessageType.CLOSE)
            .build();
    client3Handler.write(msg3);
    assertEquals(msg3, client3MessageReceived.get());

    // Try to create a race condition by triggering handlerThread closing and client3 closing at the
    // same time
    // This often triggers ClosedByInterruptException in handleKey
    server.stop();
    server.selector.wakeup();
    client3.closeConnection();
    client3ConnectionClosed.get();
    serverConnectionClosed3.get();

    server.stopAndWait();
    client2ConnectionClosed.get();
    serverConnectionClosed2.get();

    server.stopAndWait();
  }
  @Test
  public void largeDataTest() throws Exception {
    /**
     * Test various large-data handling, essentially testing {@link
     * ProtobufParser#receiveBytes(java.nio.ByteBuffer)}
     */
    final SettableFuture<Void> serverConnectionOpen = SettableFuture.create();
    final SettableFuture<Void> clientConnectionOpen = SettableFuture.create();
    final SettableFuture<Void> serverConnectionClosed = SettableFuture.create();
    final SettableFuture<Void> clientConnectionClosed = SettableFuture.create();
    final SettableFuture<Protos.TwoWayChannelMessage> clientMessage1Received =
        SettableFuture.create();
    final SettableFuture<Protos.TwoWayChannelMessage> clientMessage2Received =
        SettableFuture.create();
    final SettableFuture<Protos.TwoWayChannelMessage> clientMessage3Received =
        SettableFuture.create();
    final SettableFuture<Protos.TwoWayChannelMessage> clientMessage4Received =
        SettableFuture.create();
    NioServer server =
        new NioServer(
            new StreamParserFactory() {
              @Override
              public ProtobufParser getNewParser(InetAddress inetAddress, int port) {
                return new ProtobufParser<Protos.TwoWayChannelMessage>(
                    new ProtobufParser.Listener<Protos.TwoWayChannelMessage>() {
                      @Override
                      public void messageReceived(
                          ProtobufParser<Protos.TwoWayChannelMessage> handler,
                          Protos.TwoWayChannelMessage msg) {
                        handler.write(msg);
                      }

                      @Override
                      public void connectionOpen(ProtobufParser handler) {
                        serverConnectionOpen.set(null);
                      }

                      @Override
                      public void connectionClosed(ProtobufParser handler) {
                        serverConnectionClosed.set(null);
                      }
                    },
                    Protos.TwoWayChannelMessage.getDefaultInstance(),
                    0x10000,
                    0);
              }
            },
            new InetSocketAddress("localhost", 4243));
    server.startAndWait();

    ProtobufParser<Protos.TwoWayChannelMessage> clientHandler =
        new ProtobufParser<Protos.TwoWayChannelMessage>(
            new ProtobufParser.Listener<Protos.TwoWayChannelMessage>() {
              @Override
              public synchronized void messageReceived(
                  ProtobufParser handler, Protos.TwoWayChannelMessage msg) {
                if (clientMessage1Received.isDone()) {
                  if (clientMessage2Received.isDone()) {
                    if (clientMessage3Received.isDone()) {
                      if (clientMessage4Received.isDone()) fail.set(true);
                      clientMessage4Received.set(msg);
                    } else clientMessage3Received.set(msg);
                  } else clientMessage2Received.set(msg);
                } else clientMessage1Received.set(msg);
              }

              @Override
              public void connectionOpen(ProtobufParser handler) {
                clientConnectionOpen.set(null);
              }

              @Override
              public void connectionClosed(ProtobufParser handler) {
                clientConnectionClosed.set(null);
              }
            },
            Protos.TwoWayChannelMessage.getDefaultInstance(),
            0x10000,
            0);

    MessageWriteTarget client =
        openConnection(new InetSocketAddress("localhost", 4243), clientHandler);

    clientConnectionOpen.get();
    serverConnectionOpen.get();

    // Large message that is larger than buffer and equal to maximum message size
    Protos.TwoWayChannelMessage msg =
        Protos.TwoWayChannelMessage.newBuilder()
            .setType(Protos.TwoWayChannelMessage.MessageType.CHANNEL_OPEN)
            .setClientVersion(
                Protos.ClientVersion.newBuilder()
                    .setMajor(1)
                    .setPreviousChannelContractHash(ByteString.copyFrom(new byte[0x10000 - 12])))
            .build();
    // Small message that fits in the buffer
    Protos.TwoWayChannelMessage msg2 =
        Protos.TwoWayChannelMessage.newBuilder()
            .setType(Protos.TwoWayChannelMessage.MessageType.CHANNEL_OPEN)
            .setClientVersion(
                Protos.ClientVersion.newBuilder()
                    .setMajor(1)
                    .setPreviousChannelContractHash(ByteString.copyFrom(new byte[1])))
            .build();
    // Break up the message into chunks to simulate packet network (with strange MTUs...)
    byte[] messageBytes = msg.toByteArray();
    byte[] messageLength = new byte[4];
    Utils.uint32ToByteArrayBE(messageBytes.length, messageLength, 0);
    client.writeBytes(new byte[] {messageLength[0], messageLength[1]});
    Thread.sleep(10);
    client.writeBytes(new byte[] {messageLength[2], messageLength[3]});
    Thread.sleep(10);
    client.writeBytes(new byte[] {messageBytes[0], messageBytes[1]});
    Thread.sleep(10);
    client.writeBytes(Arrays.copyOfRange(messageBytes, 2, messageBytes.length - 1));
    Thread.sleep(10);

    // Now send the end of msg + msg2 + msg3 all at once
    byte[] messageBytes2 = msg2.toByteArray();
    byte[] messageLength2 = new byte[4];
    Utils.uint32ToByteArrayBE(messageBytes2.length, messageLength2, 0);
    byte[] sendBytes =
        Arrays.copyOf(
            new byte[] {messageBytes[messageBytes.length - 1]},
            1 + messageBytes2.length * 2 + messageLength2.length * 2);
    System.arraycopy(messageLength2, 0, sendBytes, 1, 4);
    System.arraycopy(messageBytes2, 0, sendBytes, 5, messageBytes2.length);
    System.arraycopy(messageLength2, 0, sendBytes, 5 + messageBytes2.length, 4);
    System.arraycopy(messageBytes2, 0, sendBytes, 9 + messageBytes2.length, messageBytes2.length);
    client.writeBytes(sendBytes);
    assertEquals(msg, clientMessage1Received.get());
    assertEquals(msg2, clientMessage2Received.get());
    assertEquals(msg2, clientMessage3Received.get());

    // Now resent msg2 in chunks, by itself
    Utils.uint32ToByteArrayBE(messageBytes2.length, messageLength2, 0);
    client.writeBytes(new byte[] {messageLength2[0], messageLength2[1]});
    Thread.sleep(10);
    client.writeBytes(new byte[] {messageLength2[2], messageLength2[3]});
    Thread.sleep(10);
    client.writeBytes(new byte[] {messageBytes2[0], messageBytes2[1]});
    Thread.sleep(10);
    client.writeBytes(new byte[] {messageBytes2[2], messageBytes2[3]});
    Thread.sleep(10);
    client.writeBytes(Arrays.copyOfRange(messageBytes2, 4, messageBytes2.length));
    assertEquals(msg2, clientMessage4Received.get());

    Protos.TwoWayChannelMessage msg5 =
        Protos.TwoWayChannelMessage.newBuilder()
            .setType(Protos.TwoWayChannelMessage.MessageType.CHANNEL_OPEN)
            .setClientVersion(
                Protos.ClientVersion.newBuilder()
                    .setMajor(1)
                    .setPreviousChannelContractHash(ByteString.copyFrom(new byte[0x10000 - 11])))
            .build();
    try {
      clientHandler.write(msg5);
    } catch (IllegalStateException e) {
    }

    // Override max size and make sure the server drops our connection
    byte[] messageLength5 = new byte[4];
    Utils.uint32ToByteArrayBE(msg5.toByteArray().length, messageLength5, 0);
    client.writeBytes(messageLength5);

    serverConnectionClosed.get();
    clientConnectionClosed.get();

    server.stopAndWait();
  }
  @Test
  public void basicTimeoutTest() throws Exception {
    // Tests various timeout scenarios

    final SettableFuture<Void> serverConnection1Open = SettableFuture.create();
    final SettableFuture<Void> clientConnection1Open = SettableFuture.create();
    final SettableFuture<Void> serverConnection1Closed = SettableFuture.create();
    final SettableFuture<Void> clientConnection1Closed = SettableFuture.create();

    final SettableFuture<Void> serverConnection2Open = SettableFuture.create();
    final SettableFuture<Void> clientConnection2Open = SettableFuture.create();
    final SettableFuture<Void> serverConnection2Closed = SettableFuture.create();
    final SettableFuture<Void> clientConnection2Closed = SettableFuture.create();
    NioServer server =
        new NioServer(
            new StreamParserFactory() {
              @Override
              public ProtobufParser getNewParser(InetAddress inetAddress, int port) {
                return new ProtobufParser<Protos.TwoWayChannelMessage>(
                    new ProtobufParser.Listener<Protos.TwoWayChannelMessage>() {
                      @Override
                      public void messageReceived(
                          ProtobufParser handler, Protos.TwoWayChannelMessage msg) {
                        fail.set(true);
                      }

                      @Override
                      public synchronized void connectionOpen(ProtobufParser handler) {
                        if (serverConnection1Open.isDone()) {
                          handler.setSocketTimeout(0);
                          serverConnection2Open.set(null);
                        } else serverConnection1Open.set(null);
                      }

                      @Override
                      public synchronized void connectionClosed(ProtobufParser handler) {
                        if (serverConnection1Closed.isDone()) {
                          serverConnection2Closed.set(null);
                        } else serverConnection1Closed.set(null);
                      }
                    },
                    Protos.TwoWayChannelMessage.getDefaultInstance(),
                    1000,
                    10);
              }
            },
            new InetSocketAddress("localhost", 4243));
    server.startAndWait();

    openConnection(
        new InetSocketAddress("localhost", 4243),
        new ProtobufParser<Protos.TwoWayChannelMessage>(
            new ProtobufParser.Listener<Protos.TwoWayChannelMessage>() {
              @Override
              public void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) {
                fail.set(true);
              }

              @Override
              public void connectionOpen(ProtobufParser handler) {
                clientConnection1Open.set(null);
              }

              @Override
              public void connectionClosed(ProtobufParser handler) {
                clientConnection1Closed.set(null);
              }
            },
            Protos.TwoWayChannelMessage.getDefaultInstance(),
            1000,
            0));

    clientConnection1Open.get();
    serverConnection1Open.get();
    long closeDelayStart = System.currentTimeMillis();
    clientConnection1Closed.get();
    serverConnection1Closed.get();
    long closeDelayFinish = System.currentTimeMillis();

    ProtobufParser<Protos.TwoWayChannelMessage> client2Handler =
        new ProtobufParser<Protos.TwoWayChannelMessage>(
            new ProtobufParser.Listener<Protos.TwoWayChannelMessage>() {
              @Override
              public void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) {
                fail.set(true);
              }

              @Override
              public void connectionOpen(ProtobufParser handler) {
                clientConnection2Open.set(null);
              }

              @Override
              public void connectionClosed(ProtobufParser handler) {
                clientConnection2Closed.set(null);
              }
            },
            Protos.TwoWayChannelMessage.getDefaultInstance(),
            1000,
            0);
    openConnection(new InetSocketAddress("localhost", 4243), client2Handler);

    clientConnection2Open.get();
    serverConnection2Open.get();
    Thread.sleep((closeDelayFinish - closeDelayStart) * 10);
    assertFalse(clientConnection2Closed.isDone() || serverConnection2Closed.isDone());

    client2Handler.setSocketTimeout(10);
    clientConnection2Closed.get();
    serverConnection2Closed.get();

    server.stopAndWait();
  }
  /**
   * Attempts to open a new connection to and open a payment channel with the given host and port,
   * blocking until the connection is open
   *
   * @param server The host/port pair where the server is listening.
   * @param timeoutSeconds The connection timeout and read timeout during initialization. This
   *     should be large enough to accommodate ECDSA signature operations and network latency.
   * @param wallet The wallet which will be paid from, and where completed transactions will be
   *     committed. Must already have a {@link StoredPaymentChannelClientStates} object in its
   *     extensions set.
   * @param myKey A freshly generated keypair used for the multisig contract and refund output.
   * @param maxValue The maximum value this channel is allowed to request
   * @param serverId A unique ID which is used to attempt reopening of an existing channel. This
   *     must be unique to the server, and, if your application is exposing payment channels to some
   *     API, this should also probably encompass some caller UID to avoid applications opening
   *     channels which were created by others.
   * @throws IOException if there's an issue using the network.
   * @throws ValueOutOfRangeException if the balance of wallet is lower than maxValue.
   */
  public PaymentChannelClientConnection(
      InetSocketAddress server,
      int timeoutSeconds,
      Wallet wallet,
      ECKey myKey,
      BigInteger maxValue,
      String serverId)
      throws IOException, ValueOutOfRangeException {
    // Glue the object which vends/ingests protobuf messages in order to manage state to the network
    // object which
    // reads/writes them to the wire in length prefixed form.
    channelClient =
        new PaymentChannelClient(
            wallet,
            myKey,
            maxValue,
            Sha256Hash.create(serverId.getBytes()),
            new PaymentChannelClient.ClientConnection() {
              @Override
              public void sendToServer(Protos.TwoWayChannelMessage msg) {
                wireParser.write(msg);
              }

              @Override
              public void destroyConnection(PaymentChannelCloseException.CloseReason reason) {
                channelOpenFuture.setException(
                    new PaymentChannelCloseException(
                        "Payment channel client requested that the connection be closed: " + reason,
                        reason));
                wireParser.closeConnection();
              }

              @Override
              public void channelOpen() {
                wireParser.setSocketTimeout(0);
                // Inform the API user that we're done and ready to roll.
                channelOpenFuture.set(PaymentChannelClientConnection.this);
              }
            });

    // And glue back in the opposite direction - network to the channelClient.
    wireParser =
        new ProtobufParser<Protos.TwoWayChannelMessage>(
            new ProtobufParser.Listener<Protos.TwoWayChannelMessage>() {
              @Override
              public void messageReceived(ProtobufParser handler, Protos.TwoWayChannelMessage msg) {
                try {
                  channelClient.receiveMessage(msg);
                } catch (ValueOutOfRangeException e) {
                  // We should only get this exception during INITIATE, so channelOpen wasn't called
                  // yet.
                  channelOpenFuture.setException(e);
                }
              }

              @Override
              public void connectionOpen(ProtobufParser handler) {
                channelClient.connectionOpen();
              }

              @Override
              public void connectionClosed(ProtobufParser handler) {
                channelClient.connectionClosed();
                channelOpenFuture.setException(
                    new PaymentChannelCloseException(
                        "The TCP socket died",
                        PaymentChannelCloseException.CloseReason.CONNECTION_CLOSED));
              }
            },
            Protos.TwoWayChannelMessage.getDefaultInstance(),
            Short.MAX_VALUE,
            timeoutSeconds * 1000);

    // Initiate the outbound network connection. We don't need to keep this around. The wireParser
    // object will handle
    // things from here on out.
    new NioClient(server, wireParser, timeoutSeconds * 1000);
  }