/**
   * Called when the client provides the multi-sig contract. Checks that the previously-provided
   * refund transaction spends this transaction (because we will use it as a base to create payment
   * transactions) as well as output value and form (ie it is a 2-of-2 multisig to the correct
   * keys).
   *
   * @param multisigContract The provided multisig contract. Do not mutate this object after this
   *     call.
   * @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 VerificationException If the provided multisig contract is not well-formed or does not
   *     meet previously-specified parameters
   */
  public synchronized ListenableFuture<PaymentChannelServerState> provideMultiSigContract(
      final Transaction multisigContract) throws VerificationException {
    checkNotNull(multisigContract);
    checkState(state == State.WAITING_FOR_MULTISIG_CONTRACT);
    try {
      multisigContract.verify();
      this.multisigContract = multisigContract;
      this.multisigScript = multisigContract.getOutput(0).getScriptPubKey();

      // Check that multisigContract's first output is a 2-of-2 multisig to the correct pubkeys in
      // the correct order
      final Script expectedScript =
          ScriptBuilder.createMultiSigOutputScript(2, Lists.newArrayList(clientKey, serverKey));
      if (!Arrays.equals(multisigScript.getProgram(), expectedScript.getProgram()))
        throw new VerificationException(
            "Multisig contract's first output was not a standard 2-of-2 multisig to client and server in that order.");

      this.totalValue = multisigContract.getOutput(0).getValue();
      if (this.totalValue.signum() <= 0)
        throw new VerificationException(
            "Not accepting an attempt to open a contract with zero value.");
    } catch (VerificationException e) {
      // We couldn't parse the multisig transaction or its output.
      log.error("Provided multisig contract did not verify: {}", multisigContract.toString());
      throw e;
    }
    log.info("Broadcasting multisig contract: {}", multisigContract);
    state = State.WAITING_FOR_MULTISIG_ACCEPTANCE;
    final SettableFuture<PaymentChannelServerState> future = SettableFuture.create();
    Futures.addCallback(
        broadcaster.broadcastTransaction(multisigContract).future(),
        new FutureCallback<Transaction>() {
          @Override
          public void onSuccess(Transaction transaction) {
            log.info(
                "Successfully broadcast multisig contract {}. Channel now open.",
                transaction.getHashAsString());
            try {
              // Manually add the multisigContract to the wallet, overriding the isRelevant checks
              // so we can track
              // it and check for double-spends later
              wallet.receivePending(multisigContract, null, true);
            } catch (VerificationException e) {
              throw new RuntimeException(
                  e); // Cannot happen, we already called multisigContract.verify()
            }
            state = State.READY;
            future.set(PaymentChannelServerState.this);
          }

          @Override
          public void onFailure(Throwable throwable) {
            // Couldn't broadcast the transaction for some reason.
            log.error("Broadcast multisig contract failed", throwable);
            state = State.ERROR;
            future.setException(throwable);
          }
        });
    return future;
  }
 @Override
 public String toString() {
   final String newline = String.format(Locale.US, "%n");
   final String closeStr =
       close == null ? "still open" : close.toString().replaceAll(newline, newline + "   ");
   return String.format(
       Locale.US,
       "Stored client channel for server ID %s (%s)%n"
           + "    Key:         %s%n"
           + "    Value left:  %s%n"
           + "    Refund fees: %s%n"
           + "    Contract:  %s"
           + "Refund:    %s"
           + "Close:     %s",
       id,
       active ? "active" : "inactive",
       myKey,
       valueToMe,
       refundFees,
       contract.toString().replaceAll(newline, newline + "    "),
       refund.toString().replaceAll(newline, newline + "    "),
       closeStr);
 }
 @Override
 public synchronized String toString() {
   final String newline = String.format(Locale.US, "%n");
   return String.format(
       Locale.US,
       "Stored server channel (%s)%n"
           + "    Version:       %d%n"
           + "    Key:           %s%n"
           + "    Value to me:   %s%n"
           + "    Client output: %s%n"
           + "    Refund unlock: %s (%d unix time)%n"
           + "    Contract:    %s%n",
       connectedHandler != null ? "connected" : "disconnected",
       majorVersion,
       myKey,
       bestValueToMe,
       clientOutput,
       new Date(refundTransactionUnlockTimeSecs * 1000),
       refundTransactionUnlockTimeSecs,
       contract.toString().replaceAll(newline, newline + "    "));
 }