/**
   * Called when the connection terminates. Notifies the {@link StoredServerChannel} object that we
   * can attempt to resume this channel in the future and stops generating messages for the client.
   *
   * <p>Note that this <b>MUST</b> still be called even after either {@link
   * ServerConnection#destroyConnection(CloseReason)} or {@link PaymentChannelServer#close()} is
   * called to actually handle the connection close logic.
   */
  public void connectionClosed() {
    lock.lock();
    try {
      log.info("Server channel closed.");
      connectionOpen = false;

      try {
        if (state != null && state.getMultisigContract() != null) {
          StoredPaymentChannelServerStates channels =
              (StoredPaymentChannelServerStates)
                  wallet.getExtensions().get(StoredPaymentChannelServerStates.EXTENSION_ID);
          if (channels != null) {
            StoredServerChannel storedServerChannel =
                channels.getChannel(state.getMultisigContract().getHash());
            if (storedServerChannel != null) {
              storedServerChannel.clearConnectedHandler();
            }
          }
        }
      } catch (IllegalStateException e) {
        // Expected when we call getMultisigContract() sometimes
      }
    } finally {
      lock.unlock();
    }
  }
 private synchronized void updateChannelInWallet() {
   if (storedServerChannel != null) {
     storedServerChannel.updateValueToMe(bestValueToMe, bestValueSignature);
     StoredPaymentChannelServerStates channels =
         (StoredPaymentChannelServerStates)
             wallet.getExtensions().get(StoredPaymentChannelServerStates.EXTENSION_ID);
     channels.updatedChannel(storedServerChannel);
   }
 }
  /**
   * Stores this channel's state in the wallet as a part of a {@link
   * StoredPaymentChannelServerStates} wallet extension and keeps it up-to-date each time payment is
   * incremented. This will be automatically removed when a call to {@link
   * PaymentChannelServerState#close()} completes successfully. A channel may only be stored after
   * it has fully opened (ie state == State.READY).
   *
   * @param connectedHandler Optional {@link PaymentChannelServer} object that manages this object.
   *     This will set the appropriate pointer in the newly created {@link StoredServerChannel}
   *     before it is committed to wallet. If set, closing the state object will propagate the close
   *     to the handler which can then do a TCP disconnect.
   */
  public synchronized void storeChannelInWallet(@Nullable PaymentChannelServer connectedHandler) {
    checkState(state == State.READY);
    if (storedServerChannel != null) return;

    log.info("Storing state with contract hash {}.", multisigContract.getHash());
    StoredPaymentChannelServerStates channels =
        (StoredPaymentChannelServerStates)
            wallet.addOrGetExistingExtension(
                new StoredPaymentChannelServerStates(wallet, broadcaster));
    storedServerChannel =
        new StoredServerChannel(
            this,
            multisigContract,
            clientOutput,
            refundTransactionUnlockTimeSecs,
            serverKey,
            bestValueToMe,
            bestValueSignature);
    if (connectedHandler != null)
      checkState(
          storedServerChannel.setConnectedHandler(connectedHandler, false) == connectedHandler);
    channels.putChannel(storedServerChannel);
  }
  /**
   * Closes this channel and broadcasts the highest value payment transaction on the network.
   *
   * <p>This will set the state to {@link State#CLOSED} if the transaction is successfully broadcast
   * on the network. If we fail to broadcast for some reason, the state is set to {@link
   * State#ERROR}.
   *
   * <p>If the current state is before {@link State#READY} (ie we have not finished initializing the
   * channel), we simply set the state to {@link State#CLOSED} and let the client handle getting its
   * refund transaction confirmed.
   *
   * @return a future which completes when the provided multisig contract successfully broadcasts,
   *     or throws if the broadcast fails for some reason. Note that if the network simply rejects
   *     the transaction, this future will never complete, a timeout should be used.
   * @throws InsufficientMoneyException If the payment tx would have cost more in fees to spend than
   *     it is worth.
   */
  public synchronized ListenableFuture<Transaction> close() throws InsufficientMoneyException {
    if (storedServerChannel != null) {
      StoredServerChannel temp = storedServerChannel;
      storedServerChannel = null;
      StoredPaymentChannelServerStates channels =
          (StoredPaymentChannelServerStates)
              wallet.getExtensions().get(StoredPaymentChannelServerStates.EXTENSION_ID);
      channels.closeChannel(
          temp); // May call this method again for us (if it wasn't the original caller)
      if (state.compareTo(State.CLOSING) >= 0) return closedFuture;
    }

    if (state.ordinal() < State.READY.ordinal()) {
      log.error("Attempt to settle channel in state " + state);
      state = State.CLOSED;
      closedFuture.set(null);
      return closedFuture;
    }
    if (state != State.READY) {
      // TODO: What is this codepath for?
      log.warn("Failed attempt to settle a channel in state " + state);
      return closedFuture;
    }
    Transaction tx = null;
    try {
      Wallet.SendRequest req = makeUnsignedChannelContract(bestValueToMe);
      tx = req.tx;
      // Provide a throwaway signature so that completeTx won't complain out about unsigned inputs
      // it doesn't
      // know how to sign. Note that this signature does actually have to be valid, so we can't use
      // a dummy
      // signature to save time, because otherwise completeTx will try to re-sign it to make it
      // valid and then
      // die. We could probably add features to the SendRequest API to make this a bit more
      // efficient.
      signMultisigInput(tx, Transaction.SigHash.NONE, true);
      // Let wallet handle adding additional inputs/fee as necessary.
      req.shuffleOutputs = false;
      req.missingSigsMode = Wallet.MissingSigsMode.USE_DUMMY_SIG;
      wallet.completeTx(req); // TODO: Fix things so shuffling is usable.
      feePaidForPayment = req.tx.getFee();
      log.info("Calculated fee is {}", feePaidForPayment);
      if (feePaidForPayment.compareTo(bestValueToMe) > 0) {
        final String msg =
            String.format(
                Locale.US,
                "Had to pay more in fees (%s) than the channel was worth (%s)",
                feePaidForPayment,
                bestValueToMe);
        throw new InsufficientMoneyException(feePaidForPayment.subtract(bestValueToMe), msg);
      }
      // Now really sign the multisig input.
      signMultisigInput(tx, Transaction.SigHash.ALL, false);
      // Some checks that shouldn't be necessary but it can't hurt to check.
      tx.verify(); // Sanity check syntax.
      for (TransactionInput input : tx.getInputs())
        input.verify(); // Run scripts and ensure it is valid.
    } catch (InsufficientMoneyException e) {
      throw e; // Don't fall through.
    } catch (Exception e) {
      log.error(
          "Could not verify self-built tx\nMULTISIG {}\nCLOSE {}",
          multisigContract,
          tx != null ? tx : "");
      throw new RuntimeException(e); // Should never happen.
    }
    state = State.CLOSING;
    log.info("Closing channel, broadcasting tx {}", tx);
    // The act of broadcasting the transaction will add it to the wallet.
    ListenableFuture<Transaction> future = broadcaster.broadcastTransaction(tx).future();
    Futures.addCallback(
        future,
        new FutureCallback<Transaction>() {
          @Override
          public void onSuccess(Transaction transaction) {
            log.info("TX {} propagated, channel successfully closed.", transaction.getHash());
            state = State.CLOSED;
            closedFuture.set(transaction);
          }

          @Override
          public void onFailure(Throwable throwable) {
            log.error("Failed to settle channel, could not broadcast", throwable);
            state = State.ERROR;
            closedFuture.setException(throwable);
          }
        });
    return closedFuture;
  }
  @GuardedBy("lock")
  private void receiveVersionMessage(Protos.TwoWayChannelMessage msg) throws VerificationException {
    checkState(step == InitStep.WAITING_ON_CLIENT_VERSION && msg.hasClientVersion());
    final Protos.ClientVersion clientVersion = msg.getClientVersion();
    final int major = clientVersion.getMajor();
    if (major != SERVER_MAJOR_VERSION) {
      error(
          "This server needs protocol version "
              + SERVER_MAJOR_VERSION
              + " , client offered "
              + major,
          Protos.Error.ErrorCode.NO_ACCEPTABLE_VERSION,
          CloseReason.NO_ACCEPTABLE_VERSION);
      return;
    }

    Protos.ServerVersion.Builder versionNegotiationBuilder =
        Protos.ServerVersion.newBuilder()
            .setMajor(SERVER_MAJOR_VERSION)
            .setMinor(SERVER_MINOR_VERSION);
    conn.sendToClient(
        Protos.TwoWayChannelMessage.newBuilder()
            .setType(Protos.TwoWayChannelMessage.MessageType.SERVER_VERSION)
            .setServerVersion(versionNegotiationBuilder)
            .build());
    ByteString reopenChannelContractHash = clientVersion.getPreviousChannelContractHash();
    if (reopenChannelContractHash != null && reopenChannelContractHash.size() == 32) {
      Sha256Hash contractHash = new Sha256Hash(reopenChannelContractHash.toByteArray());
      log.info("New client that wants to resume {}", contractHash);
      StoredPaymentChannelServerStates channels =
          (StoredPaymentChannelServerStates)
              wallet.getExtensions().get(StoredPaymentChannelServerStates.EXTENSION_ID);
      if (channels != null) {
        StoredServerChannel storedServerChannel = channels.getChannel(contractHash);
        if (storedServerChannel != null) {
          final PaymentChannelServer existingHandler =
              storedServerChannel.setConnectedHandler(this, false);
          if (existingHandler != this) {
            log.warn("  ... and that channel is already in use, disconnecting other user.");
            existingHandler.close();
            storedServerChannel.setConnectedHandler(this, true);
          }

          log.info("Got resume version message, responding with VERSIONS and CHANNEL_OPEN");
          state = storedServerChannel.getOrCreateState(wallet, broadcaster);
          step = InitStep.CHANNEL_OPEN;
          conn.sendToClient(
              Protos.TwoWayChannelMessage.newBuilder()
                  .setType(Protos.TwoWayChannelMessage.MessageType.CHANNEL_OPEN)
                  .build());
          conn.channelOpen(contractHash);
          return;
        } else {
          log.error(" ... but we do not have any record of that contract! Resume failed.");
        }
      } else {
        log.error(" ... but we do not have any stored channels! Resume failed.");
      }
    }
    log.info(
        "Got initial version message, responding with VERSIONS and INITIATE: min value={}",
        minAcceptedChannelSize.value);

    myKey = new ECKey();
    wallet.freshReceiveKey();

    expireTime = Utils.currentTimeSeconds() + truncateTimeWindow(clientVersion.getTimeWindowSecs());
    step = InitStep.WAITING_ON_UNSIGNED_REFUND;

    Protos.Initiate.Builder initiateBuilder =
        Protos.Initiate.newBuilder()
            .setMultisigKey(ByteString.copyFrom(myKey.getPubKey()))
            .setExpireTimeSecs(expireTime)
            .setMinAcceptedChannelSize(minAcceptedChannelSize.value)
            .setMinPayment(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.value);

    conn.sendToClient(
        Protos.TwoWayChannelMessage.newBuilder()
            .setInitiate(initiateBuilder)
            .setType(Protos.TwoWayChannelMessage.MessageType.INITIATE)
            .build());
  }