private WalletTransaction connectTransactionOutputs(
      org.bitcoinj.wallet.Protos.Transaction txProto) throws UnreadableWalletException {
    Transaction tx = txMap.get(txProto.getHash());
    final WalletTransaction.Pool pool;
    switch (txProto.getPool()) {
      case DEAD:
        pool = WalletTransaction.Pool.DEAD;
        break;
      case PENDING:
        pool = WalletTransaction.Pool.PENDING;
        break;
      case SPENT:
        pool = WalletTransaction.Pool.SPENT;
        break;
      case UNSPENT:
        pool = WalletTransaction.Pool.UNSPENT;
        break;
        // Upgrade old wallets: inactive pool has been merged with the pending pool.
        // Remove this some time after 0.9 is old and everyone has upgraded.
        // There should not be any spent outputs in this tx as old wallets would not allow them to
        // be spent
        // in this state.
      case INACTIVE:
      case PENDING_INACTIVE:
        pool = WalletTransaction.Pool.PENDING;
        break;
      default:
        throw new UnreadableWalletException("Unknown transaction pool: " + txProto.getPool());
    }
    for (int i = 0; i < tx.getOutputs().size(); i++) {
      TransactionOutput output = tx.getOutputs().get(i);
      final Protos.TransactionOutput transactionOutput = txProto.getTransactionOutput(i);
      if (transactionOutput.hasSpentByTransactionHash()) {
        final ByteString spentByTransactionHash = transactionOutput.getSpentByTransactionHash();
        Transaction spendingTx = txMap.get(spentByTransactionHash);
        if (spendingTx == null) {
          throw new UnreadableWalletException(
              String.format(
                  "Could not connect %s to %s",
                  tx.getHashAsString(), byteStringToHash(spentByTransactionHash)));
        }
        final int spendingIndex = transactionOutput.getSpentByTransactionIndex();
        TransactionInput input = checkNotNull(spendingTx.getInput(spendingIndex));
        input.connect(output);
      }
    }

    if (txProto.hasConfidence()) {
      Protos.TransactionConfidence confidenceProto = txProto.getConfidence();
      TransactionConfidence confidence = tx.getConfidence();
      readConfidence(tx, confidenceProto, confidence);
    }

    return new WalletTransaction(pool, tx);
  }
  @Test
  public void skipScripts() throws Exception {
    store = createStore(params, 10);
    chain = new FullPrunedBlockChain(params, store);

    // Check that we aren't accidentally leaving any references
    // to the full StoredUndoableBlock's lying around (ie memory leaks)

    ECKey outKey = new ECKey();
    int height = 1;

    // Build some blocks on genesis block to create a spendable output
    Block rollingBlock =
        params
            .getGenesisBlock()
            .createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
    chain.add(rollingBlock);
    TransactionOutput spendableOutput = rollingBlock.getTransactions().get(0).getOutput(0);
    for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) {
      rollingBlock =
          rollingBlock.createNextBlockWithCoinbase(
              Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
      chain.add(rollingBlock);
    }

    rollingBlock = rollingBlock.createNextBlock(null);
    Transaction t = new Transaction(params);
    t.addOutput(new TransactionOutput(params, t, FIFTY_COINS, new byte[] {}));
    TransactionInput input = t.addInput(spendableOutput);
    // Invalid script.
    input.clearScriptBytes();
    rollingBlock.addTransaction(t);
    rollingBlock.solve();
    chain.setRunScripts(false);
    try {
      chain.add(rollingBlock);
    } catch (VerificationException e) {
      fail();
    }
    try {
      store.close();
    } catch (Exception e) {
    }
  }
  /**
   * 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;
  }
  /**
   * This is required for signatures which use a sigHashType which cannot be represented using
   * SigHash and anyoneCanPay See transaction
   * c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73, which has sigHashType 0
   */
  public static synchronized byte[] serializeForSignature(
      Transaction spendingTx, int inputIndex, byte[] connectedScript, byte sigHashType) {
    NetworkParameters params = TestNet3Params.get();
    // The SIGHASH flags are used in the design of contracts, please see this page for a further
    // understanding of
    // the purposes of the code in this method:
    //
    //   https://en.bitcoin.it/wiki/Contracts

    try {

      Transaction tx = new Transaction(params, spendingTx.bitcoinSerialize());
      // Store all the input scripts and clear them in preparation for signing. If we're signing a
      // fresh
      // transaction that step isn't very helpful, but it doesn't add much cost relative to the
      // actual
      // EC math so we'll do it anyway.
      //
      // Also store the input sequence numbers in case we are clearing them with SigHash.NONE/SINGLE

      byte[][] inputScripts = new byte[tx.getInputs().size()][];
      long[] inputSequenceNumbers = new long[tx.getInputs().size()];
      for (int i = 0; i < tx.getInputs().size(); i++) {
        inputScripts[i] = tx.getInputs().get(i).getScriptBytes();
        inputSequenceNumbers[i] = tx.getInputs().get(i).getSequenceNumber();
        tx.getInput(i).setScriptSig(new Script(new byte[0]));
      }

      // This step has no purpose beyond being synchronized with the reference clients bugs.
      // OP_CODESEPARATOR
      // is a legacy holdover from a previous, broken design of executing scripts that shipped in
      // Bitcoin 0.1.
      // It was seriously flawed and would have let anyone take anyone elses money. Later versions
      // switched to
      // the design we use today where scripts are executed independently but share a stack. This
      // left the
      // OP_CODESEPARATOR instruction having no purpose as it was only meant to be used internally,
      // not actually
      // ever put into scripts. Deleting OP_CODESEPARATOR is a step that should never be required
      // but if we don't
      // do it, we could split off the main chain.

      connectedScript =
          Script.removeAllInstancesOfOp(connectedScript, ScriptOpCodes.OP_CODESEPARATOR);
      // Set the input to the script of its output. Satoshi does this but the step has no obvious
      // purpose as
      // the signature covers the hash of the prevout transaction which obviously includes the
      // output script
      // already. Perhaps it felt safer to him in some way, or is another leftover from how the code
      // was written.
      TransactionInput input = tx.getInputs().get(inputIndex);
      input.setScriptSig(new Script(connectedScript));
      List<TransactionOutput> outputs = tx.getOutputs();

      if ((sigHashType & 0x1f) == (Transaction.SigHash.NONE.ordinal() + 1)) {
        // SIGHASH_NONE means no outputs are signed at all - the signature is effectively for a
        // "blank cheque".
        // this.outputs = new ArrayList<TransactionOutput>(0);
        tx.clearOutputs();
        // The signature isn't broken by new versions of the transaction issued by other parties.
        for (int i = 0; i < tx.getInputs().size(); i++)
          if (i != inputIndex) tx.getInputs().get(i).setSequenceNumber(0);
      } else if ((sigHashType & 0x1f) == (Transaction.SigHash.SINGLE.ordinal() + 1)) {
        // SIGHASH_SINGLE means only sign the output at the same index as the input (ie, my output).
        if (inputIndex >= tx.getOutputs().size()) {
          // The input index is beyond the number of outputs, it's a buggy signature made by a
          // broken
          // Bitcoin implementation. The reference client also contains a bug in handling this case:
          // any transaction output that is signed in this case will result in both the signed
          // output
          // and any future outputs to this public key being steal-able by anyone who has
          // the resulting signature and the public key (both of which are part of the signed tx
          // input).
          // Put the transaction back to how we found it.
          //
          // TODO: Only allow this to happen if we are checking a signature, not signing a
          // transactions
          for (int i = 0; i < tx.getInputs().size(); i++) {
            // tx.getInputs().get(i).setScriptSig(inputScripts[i]);
            /*                        tx.getInputs().get(i).setScriptSig(ScriptBuilder.createMultiSigInputScriptBytes(
            Arrays.asList(inputScripts[i])));*/
            tx.getInput(i).setScriptSig(new Script(inputScripts[i]));
            tx.getInputs().get(i).setSequenceNumber(inputSequenceNumbers[i]);
          }
          // this.outputs = outputs;

          // Satoshis bug is that SignatureHash was supposed to return a hash and on this codepath
          // it
          // actually returns the constant "1" to indicate an error, which is never checked for.
          // Oops.
          return Utils.HEX.decode(
              "0100000000000000000000000000000000000000000000000000000000000000");
        }

        // In SIGHASH_SINGLE the outputs after the matching input index are deleted, and the outputs
        // before
        // that position are "nulled out". Unintuitively, the value in a "null" transaction is set
        // to -1.
        /*                this.outputs = new ArrayList<TransactionOutput>(this.outputs.subList(0, inputIndex + 1));
        for (int i = 0; i < inputIndex; i++)
            this.outputs.set(i, new TransactionOutput(params, this, Coin.NEGATIVE_SATOSHI, new byte[] {}));
        // The signature isn't broken by new versions of the transaction issued by other parties.
        for (int i = 0; i < inputs.size(); i++)
            if (i != inputIndex)
                inputs.get(i).setSequenceNumber(0);*/
        // In SIGHASH_SINGLE the outputs after the matching input index are deleted, and the outputs
        // before
        // that position are "nulled out". Unintuitively, the value in a "null" transaction is set
        // to -1.
        // tx.outputs = new ArrayList<TransactionOutput>(tx.getOutputs().subList(0, inputIndex +
        // 1));
        tx.clearOutputs();
        for (int i = 0; i <= inputIndex; i++)
          if (i == inputIndex) {
            // need to make sure the output at inputIndex stays the same
            tx.addOutput(spendingTx.getOutput(inputIndex));
          } else {
            // this.outputs.set(i, new TransactionOutput(params, this, Coin.NEGATIVE_SATOSHI, new
            // byte[] {}));
            tx.addOutput(new TransactionOutput(params, tx, Coin.NEGATIVE_SATOSHI, new byte[] {}));
          }

        // The signature isn't broken by new versions of the transaction issued by other parties.
        for (int i = 0; i < tx.getInputs().size(); i++)
          if (i != inputIndex) tx.getInputs().get(i).setSequenceNumber(0);
      }

      List<TransactionInput> inputs = tx.getInputs();
      if ((sigHashType & (byte) 0x80) == 0x80) {
        // SIGHASH_ANYONECANPAY means the signature in the input is not broken by
        // changes/additions/removals
        // of other inputs. For example, this is useful for building assurance contracts.
        tx.clearInputs();
        tx.getInputs().add(input);
      }

      ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(256);
      tx.bitcoinSerialize(bos);
      // We also have to write a hash type (sigHashType is actually an unsigned char)
      uint32ToByteStreamLE(0x000000ff & sigHashType, bos);
      // Note that this is NOT reversed to ensure it will be signed correctly. If it were to be
      // printed out
      // however then we would expect that it is IS reversed.
      byte[] txSignatureBytes = bos.toByteArray();
      bos.close();

      // Put the transaction back to how we found it.
      // tx.inputs = inputs;
      tx.clearInputs();
      for (int i = 0; i < inputs.size(); i++) {
        tx.addInput(inputs.get(i));
      }
      for (int i = 0; i < inputs.size(); i++) {
        inputs.get(i).setScriptSig(new Script(inputScripts[i]));
        inputs.get(i).setSequenceNumber(inputSequenceNumbers[i]);
      }
      // this.outputs = outputs;
      tx.clearOutputs();
      for (int i = 0; i < outputs.size(); i++) {
        tx.addOutput(outputs.get(i));
      }
      return txSignatureBytes;
    } catch (IOException e) {
      throw new RuntimeException(e); // Cannot happen.
    }
  }
  private void readTransaction(Protos.Transaction txProto, NetworkParameters params)
      throws UnreadableWalletException {
    Transaction tx = new Transaction(params);
    if (txProto.hasUpdatedAt()) {
      tx.setUpdateTime(new Date(txProto.getUpdatedAt()));
    }

    for (Protos.TransactionOutput outputProto : txProto.getTransactionOutputList()) {
      Coin value = Coin.valueOf(outputProto.getValue());
      byte[] scriptBytes = outputProto.getScriptBytes().toByteArray();
      TransactionOutput output = new TransactionOutput(params, tx, value, scriptBytes);
      tx.addOutput(output);
    }

    for (Protos.TransactionInput inputProto : txProto.getTransactionInputList()) {
      byte[] scriptBytes = inputProto.getScriptBytes().toByteArray();
      TransactionOutPoint outpoint =
          new TransactionOutPoint(
              params,
              inputProto.getTransactionOutPointIndex() & 0xFFFFFFFFL,
              byteStringToHash(inputProto.getTransactionOutPointHash()));
      Coin value = inputProto.hasValue() ? Coin.valueOf(inputProto.getValue()) : null;
      TransactionInput input = new TransactionInput(params, tx, scriptBytes, outpoint, value);
      if (inputProto.hasSequence()) {
        input.setSequenceNumber(inputProto.getSequence());
      }
      tx.addInput(input);
    }

    for (int i = 0; i < txProto.getBlockHashCount(); i++) {
      ByteString blockHash = txProto.getBlockHash(i);
      int relativityOffset = 0;
      if (txProto.getBlockRelativityOffsetsCount() > 0)
        relativityOffset = txProto.getBlockRelativityOffsets(i);
      tx.addBlockAppearance(byteStringToHash(blockHash), relativityOffset);
    }

    if (txProto.hasLockTime()) {
      tx.setLockTime(0xffffffffL & txProto.getLockTime());
    }

    if (txProto.hasPurpose()) {
      switch (txProto.getPurpose()) {
        case UNKNOWN:
          tx.setPurpose(Transaction.Purpose.UNKNOWN);
          break;
        case USER_PAYMENT:
          tx.setPurpose(Transaction.Purpose.USER_PAYMENT);
          break;
        case KEY_ROTATION:
          tx.setPurpose(Transaction.Purpose.KEY_ROTATION);
          break;
        case ASSURANCE_CONTRACT_CLAIM:
          tx.setPurpose(Transaction.Purpose.ASSURANCE_CONTRACT_CLAIM);
          break;
        case ASSURANCE_CONTRACT_PLEDGE:
          tx.setPurpose(Transaction.Purpose.ASSURANCE_CONTRACT_PLEDGE);
          break;
        case ASSURANCE_CONTRACT_STUB:
          tx.setPurpose(Transaction.Purpose.ASSURANCE_CONTRACT_STUB);
          break;
        default:
          throw new RuntimeException("New purpose serialization not implemented");
      }
    } else {
      // Old wallet: assume a user payment as that's the only reason a new tx would have been
      // created back then.
      tx.setPurpose(Transaction.Purpose.USER_PAYMENT);
    }

    if (txProto.hasExchangeRate()) {
      Protos.ExchangeRate exchangeRateProto = txProto.getExchangeRate();
      tx.setExchangeRate(
          new ExchangeRate(
              Coin.valueOf(exchangeRateProto.getCoinValue()),
              Fiat.valueOf(
                  exchangeRateProto.getFiatCurrencyCode(), exchangeRateProto.getFiatValue())));
    }

    if (txProto.hasMemo()) tx.setMemo(txProto.getMemo());

    // Transaction should now be complete.
    Sha256Hash protoHash = byteStringToHash(txProto.getHash());
    if (!tx.getHash().equals(protoHash))
      throw new UnreadableWalletException(
          String.format(
              "Transaction did not deserialize completely: %s vs %s", tx.getHash(), protoHash));
    if (txMap.containsKey(txProto.getHash()))
      throw new UnreadableWalletException(
          "Wallet contained duplicate transaction " + byteStringToHash(txProto.getHash()));
    txMap.put(txProto.getHash(), tx);
  }
  private static Protos.Transaction makeTxProto(WalletTransaction wtx) {
    Transaction tx = wtx.getTransaction();
    Protos.Transaction.Builder txBuilder = Protos.Transaction.newBuilder();

    txBuilder
        .setPool(getProtoPool(wtx))
        .setHash(hashToByteString(tx.getHash()))
        .setVersion((int) tx.getVersion());

    if (tx.getUpdateTime() != null) {
      txBuilder.setUpdatedAt(tx.getUpdateTime().getTime());
    }

    if (tx.getLockTime() > 0) {
      txBuilder.setLockTime((int) tx.getLockTime());
    }

    // Handle inputs.
    for (TransactionInput input : tx.getInputs()) {
      Protos.TransactionInput.Builder inputBuilder =
          Protos.TransactionInput.newBuilder()
              .setScriptBytes(ByteString.copyFrom(input.getScriptBytes()))
              .setTransactionOutPointHash(hashToByteString(input.getOutpoint().getHash()))
              .setTransactionOutPointIndex((int) input.getOutpoint().getIndex());
      if (input.hasSequence()) inputBuilder.setSequence((int) input.getSequenceNumber());
      if (input.getValue() != null) inputBuilder.setValue(input.getValue().value);
      txBuilder.addTransactionInput(inputBuilder);
    }

    // Handle outputs.
    for (TransactionOutput output : tx.getOutputs()) {
      Protos.TransactionOutput.Builder outputBuilder =
          Protos.TransactionOutput.newBuilder()
              .setScriptBytes(ByteString.copyFrom(output.getScriptBytes()))
              .setValue(output.getValue().value);
      final TransactionInput spentBy = output.getSpentBy();
      if (spentBy != null) {
        Sha256Hash spendingHash = spentBy.getParentTransaction().getHash();
        int spentByTransactionIndex = spentBy.getParentTransaction().getInputs().indexOf(spentBy);
        outputBuilder
            .setSpentByTransactionHash(hashToByteString(spendingHash))
            .setSpentByTransactionIndex(spentByTransactionIndex);
      }
      txBuilder.addTransactionOutput(outputBuilder);
    }

    // Handle which blocks tx was seen in.
    final Map<Sha256Hash, Integer> appearsInHashes = tx.getAppearsInHashes();
    if (appearsInHashes != null) {
      for (Map.Entry<Sha256Hash, Integer> entry : appearsInHashes.entrySet()) {
        txBuilder.addBlockHash(hashToByteString(entry.getKey()));
        txBuilder.addBlockRelativityOffsets(entry.getValue());
      }
    }

    if (tx.hasConfidence()) {
      TransactionConfidence confidence = tx.getConfidence();
      Protos.TransactionConfidence.Builder confidenceBuilder =
          Protos.TransactionConfidence.newBuilder();
      writeConfidence(txBuilder, confidence, confidenceBuilder);
    }

    Protos.Transaction.Purpose purpose;
    switch (tx.getPurpose()) {
      case UNKNOWN:
        purpose = Protos.Transaction.Purpose.UNKNOWN;
        break;
      case USER_PAYMENT:
        purpose = Protos.Transaction.Purpose.USER_PAYMENT;
        break;
      case KEY_ROTATION:
        purpose = Protos.Transaction.Purpose.KEY_ROTATION;
        break;
      case ASSURANCE_CONTRACT_CLAIM:
        purpose = Protos.Transaction.Purpose.ASSURANCE_CONTRACT_CLAIM;
        break;
      case ASSURANCE_CONTRACT_PLEDGE:
        purpose = Protos.Transaction.Purpose.ASSURANCE_CONTRACT_PLEDGE;
        break;
      case ASSURANCE_CONTRACT_STUB:
        purpose = Protos.Transaction.Purpose.ASSURANCE_CONTRACT_STUB;
        break;
      default:
        throw new RuntimeException("New tx purpose serialization not implemented.");
    }
    txBuilder.setPurpose(purpose);

    ExchangeRate exchangeRate = tx.getExchangeRate();
    if (exchangeRate != null) {
      Protos.ExchangeRate.Builder exchangeRateBuilder =
          Protos.ExchangeRate.newBuilder()
              .setCoinValue(exchangeRate.coin.value)
              .setFiatValue(exchangeRate.fiat.value)
              .setFiatCurrencyCode(exchangeRate.fiat.currencyCode);
      txBuilder.setExchangeRate(exchangeRateBuilder);
    }

    if (tx.getMemo() != null) txBuilder.setMemo(tx.getMemo());

    return txBuilder.build();
  }
  @Override
  /** Used during reorgs to connect a block previously on a fork */
  protected synchronized TransactionOutputChanges connectTransactions(StoredBlock newBlock)
      throws VerificationException, BlockStoreException, PrunedException {
    checkState(lock.isHeldByCurrentThread());
    if (!params.passesCheckpoint(newBlock.getHeight(), newBlock.getHeader().getHash()))
      throw new VerificationException("Block failed checkpoint lockin at " + newBlock.getHeight());

    blockStore.beginDatabaseBatchWrite();
    StoredUndoableBlock block = blockStore.getUndoBlock(newBlock.getHeader().getHash());
    if (block == null) {
      // We're trying to re-org too deep and the data needed has been deleted.
      blockStore.abortDatabaseBatchWrite();
      throw new PrunedException(newBlock.getHeader().getHash());
    }
    TransactionOutputChanges txOutChanges;
    try {
      List<Transaction> transactions = block.getTransactions();
      if (transactions != null) {
        LinkedList<UTXO> txOutsSpent = new LinkedList<UTXO>();
        LinkedList<UTXO> txOutsCreated = new LinkedList<UTXO>();
        long sigOps = 0;
        final Set<VerifyFlag> verifyFlags = EnumSet.noneOf(VerifyFlag.class);
        if (newBlock.getHeader().getTimeSeconds() >= NetworkParameters.BIP16_ENFORCE_TIME)
          verifyFlags.add(VerifyFlag.P2SH);
        if (!params.isCheckpoint(newBlock.getHeight())) {
          for (Transaction tx : transactions) {
            Sha256Hash hash = tx.getHash();
            if (blockStore.hasUnspentOutputs(hash, tx.getOutputs().size()))
              throw new VerificationException("Block failed BIP30 test!");
          }
        }
        Coin totalFees = Coin.ZERO;
        Coin coinbaseValue = null;

        if (scriptVerificationExecutor.isShutdown())
          scriptVerificationExecutor =
              Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
        List<Future<VerificationException>> listScriptVerificationResults =
            new ArrayList<Future<VerificationException>>(transactions.size());
        for (final Transaction tx : transactions) {
          boolean isCoinBase = tx.isCoinBase();
          Coin valueIn = Coin.ZERO;
          Coin valueOut = Coin.ZERO;
          final List<Script> prevOutScripts = new LinkedList<Script>();
          if (!isCoinBase) {
            for (int index = 0; index < tx.getInputs().size(); index++) {
              final TransactionInput in = tx.getInputs().get(index);
              final UTXO prevOut =
                  blockStore.getTransactionOutput(
                      in.getOutpoint().getHash(), in.getOutpoint().getIndex());
              if (prevOut == null)
                throw new VerificationException(
                    "Attempted spend of a non-existent or already spent output!");
              if (prevOut.isCoinbase()
                  && newBlock.getHeight() - prevOut.getHeight()
                      < params.getSpendableCoinbaseDepth())
                throw new VerificationException(
                    "Tried to spend coinbase at depth "
                        + (newBlock.getHeight() - prevOut.getHeight()));
              valueIn = valueIn.add(prevOut.getValue());
              if (verifyFlags.contains(VerifyFlag.P2SH)) {
                if (prevOut.getScript().isPayToScriptHash())
                  sigOps += Script.getP2SHSigOpCount(in.getScriptBytes());
                if (sigOps > Block.MAX_BLOCK_SIGOPS)
                  throw new VerificationException("Too many P2SH SigOps in block");
              }

              prevOutScripts.add(prevOut.getScript());

              blockStore.removeUnspentTransactionOutput(prevOut);
              txOutsSpent.add(prevOut);
            }
          }
          Sha256Hash hash = tx.getHash();
          for (TransactionOutput out : tx.getOutputs()) {
            valueOut = valueOut.add(out.getValue());
            Script script = getScript(out.getScriptBytes());
            UTXO newOut =
                new UTXO(
                    hash,
                    out.getIndex(),
                    out.getValue(),
                    newBlock.getHeight(),
                    isCoinBase,
                    script,
                    getScriptAddress(script));
            blockStore.addUnspentTransactionOutput(newOut);
            txOutsCreated.add(newOut);
          }
          // All values were already checked for being non-negative (as it is verified in
          // Transaction.verify())
          // but we check again here just for defence in depth. Transactions with zero output value
          // are OK.
          if (valueOut.signum() < 0 || valueOut.compareTo(params.getMaxMoney()) > 0)
            throw new VerificationException("Transaction output value out of range");
          if (isCoinBase) {
            coinbaseValue = valueOut;
          } else {
            if (valueIn.compareTo(valueOut) < 0 || valueIn.compareTo(params.getMaxMoney()) > 0)
              throw new VerificationException("Transaction input value out of range");
            totalFees = totalFees.add(valueIn.subtract(valueOut));
          }

          if (!isCoinBase) {
            // Because correctlySpends modifies transactions, this must come after we are done with
            // tx
            FutureTask<VerificationException> future =
                new FutureTask<VerificationException>(
                    new Verifier(tx, prevOutScripts, verifyFlags));
            scriptVerificationExecutor.execute(future);
            listScriptVerificationResults.add(future);
          }
        }
        if (totalFees.compareTo(params.getMaxMoney()) > 0
            || newBlock
                    .getHeader()
                    .getBlockInflation(newBlock.getHeight())
                    .add(totalFees)
                    .compareTo(coinbaseValue)
                < 0) throw new VerificationException("Transaction fees out of range");
        txOutChanges = new TransactionOutputChanges(txOutsCreated, txOutsSpent);
        for (Future<VerificationException> future : listScriptVerificationResults) {
          VerificationException e;
          try {
            e = future.get();
          } catch (InterruptedException thrownE) {
            throw new RuntimeException(thrownE); // Shouldn't happen
          } catch (ExecutionException thrownE) {
            log.error("Script.correctlySpends threw a non-normal exception: " + thrownE.getCause());
            throw new VerificationException(
                "Bug in Script.correctlySpends, likely script malformed in some new and interesting way.",
                thrownE);
          }
          if (e != null) throw e;
        }
      } else {
        txOutChanges = block.getTxOutChanges();
        if (!params.isCheckpoint(newBlock.getHeight()))
          for (UTXO out : txOutChanges.txOutsCreated) {
            Sha256Hash hash = out.getHash();
            if (blockStore.getTransactionOutput(hash, out.getIndex()) != null)
              throw new VerificationException("Block failed BIP30 test!");
          }
        for (UTXO out : txOutChanges.txOutsCreated) blockStore.addUnspentTransactionOutput(out);
        for (UTXO out : txOutChanges.txOutsSpent) blockStore.removeUnspentTransactionOutput(out);
      }
    } catch (VerificationException e) {
      scriptVerificationExecutor.shutdownNow();
      blockStore.abortDatabaseBatchWrite();
      throw e;
    } catch (BlockStoreException e) {
      scriptVerificationExecutor.shutdownNow();
      blockStore.abortDatabaseBatchWrite();
      throw e;
    }
    return txOutChanges;
  }
  @Override
  protected TransactionOutputChanges connectTransactions(int height, Block block)
      throws VerificationException, BlockStoreException {
    checkState(lock.isHeldByCurrentThread());
    if (block.transactions == null)
      throw new RuntimeException(
          "connectTransactions called with Block that didn't have transactions!");
    if (!params.passesCheckpoint(height, block.getHash()))
      throw new VerificationException("Block failed checkpoint lockin at " + height);

    blockStore.beginDatabaseBatchWrite();

    LinkedList<UTXO> txOutsSpent = new LinkedList<UTXO>();
    LinkedList<UTXO> txOutsCreated = new LinkedList<UTXO>();
    long sigOps = 0;
    final Set<VerifyFlag> verifyFlags = EnumSet.noneOf(VerifyFlag.class);
    if (block.getTimeSeconds() >= NetworkParameters.BIP16_ENFORCE_TIME)
      verifyFlags.add(VerifyFlag.P2SH);

    if (scriptVerificationExecutor.isShutdown())
      scriptVerificationExecutor =
          Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    List<Future<VerificationException>> listScriptVerificationResults =
        new ArrayList<Future<VerificationException>>(block.transactions.size());
    try {
      if (!params.isCheckpoint(height)) {
        // BIP30 violator blocks are ones that contain a duplicated transaction. They are all in the
        // checkpoints list and we therefore only check non-checkpoints for duplicated transactions
        // here. See the
        // BIP30 document for more details on this:
        // https://github.com/bitcoin/bips/blob/master/bip-0030.mediawiki
        for (Transaction tx : block.transactions) {
          Sha256Hash hash = tx.getHash();
          // If we already have unspent outputs for this hash, we saw the tx already. Either the
          // block is
          // being added twice (bug) or the block is a BIP30 violator.
          if (blockStore.hasUnspentOutputs(hash, tx.getOutputs().size()))
            throw new VerificationException("Block failed BIP30 test!");
          if (verifyFlags.contains(
              VerifyFlag
                  .P2SH)) // We already check non-BIP16 sigops in Block.verifyTransactions(true)
          sigOps += tx.getSigOpCount();
        }
      }
      Coin totalFees = Coin.ZERO;
      Coin coinbaseValue = null;
      for (final Transaction tx : block.transactions) {
        boolean isCoinBase = tx.isCoinBase();
        Coin valueIn = Coin.ZERO;
        Coin valueOut = Coin.ZERO;
        final List<Script> prevOutScripts = new LinkedList<Script>();
        if (!isCoinBase) {
          // For each input of the transaction remove the corresponding output from the set of
          // unspent
          // outputs.
          for (int index = 0; index < tx.getInputs().size(); index++) {
            TransactionInput in = tx.getInputs().get(index);
            UTXO prevOut =
                blockStore.getTransactionOutput(
                    in.getOutpoint().getHash(), in.getOutpoint().getIndex());
            if (prevOut == null)
              throw new VerificationException(
                  "Attempted to spend a non-existent or already spent output!");
            // Coinbases can't be spent until they mature, to avoid re-orgs destroying entire
            // transaction
            // chains. The assumption is there will ~never be re-orgs deeper than the spendable
            // coinbase
            // chain depth.
            if (prevOut.isCoinbase()) {
              if (height - prevOut.getHeight() < params.getSpendableCoinbaseDepth()) {
                throw new VerificationException(
                    "Tried to spend coinbase at depth " + (height - prevOut.getHeight()));
              }
            }
            // TODO: Check we're not spending the genesis transaction here. Satoshis code won't
            // allow it.
            valueIn = valueIn.add(prevOut.getValue());
            if (verifyFlags.contains(VerifyFlag.P2SH)) {
              if (prevOut.getScript().isPayToScriptHash())
                sigOps += Script.getP2SHSigOpCount(in.getScriptBytes());
              if (sigOps > Block.MAX_BLOCK_SIGOPS)
                throw new VerificationException("Too many P2SH SigOps in block");
            }

            prevOutScripts.add(prevOut.getScript());
            blockStore.removeUnspentTransactionOutput(prevOut);
            txOutsSpent.add(prevOut);
          }
        }
        Sha256Hash hash = tx.getHash();
        for (TransactionOutput out : tx.getOutputs()) {
          valueOut = valueOut.add(out.getValue());
          // For each output, add it to the set of unspent outputs so it can be consumed in future.
          Script script = getScript(out.getScriptBytes());
          UTXO newOut =
              new UTXO(
                  hash,
                  out.getIndex(),
                  out.getValue(),
                  height,
                  isCoinBase,
                  script,
                  getScriptAddress(script));
          blockStore.addUnspentTransactionOutput(newOut);
          txOutsCreated.add(newOut);
        }
        // All values were already checked for being non-negative (as it is verified in
        // Transaction.verify())
        // but we check again here just for defence in depth. Transactions with zero output value
        // are OK.
        if (valueOut.signum() < 0 || valueOut.compareTo(params.getMaxMoney()) > 0)
          throw new VerificationException("Transaction output value out of range");
        if (isCoinBase) {
          coinbaseValue = valueOut;
        } else {
          if (valueIn.compareTo(valueOut) < 0 || valueIn.compareTo(params.getMaxMoney()) > 0)
            throw new VerificationException("Transaction input value out of range");
          totalFees = totalFees.add(valueIn.subtract(valueOut));
        }

        if (!isCoinBase && runScripts) {
          // Because correctlySpends modifies transactions, this must come after we are done with tx
          FutureTask<VerificationException> future =
              new FutureTask<VerificationException>(new Verifier(tx, prevOutScripts, verifyFlags));
          scriptVerificationExecutor.execute(future);
          listScriptVerificationResults.add(future);
        }
      }
      if (totalFees.compareTo(params.getMaxMoney()) > 0
          || block.getBlockInflation(height).add(totalFees).compareTo(coinbaseValue) < 0)
        throw new VerificationException("Transaction fees out of range");
      for (Future<VerificationException> future : listScriptVerificationResults) {
        VerificationException e;
        try {
          e = future.get();
        } catch (InterruptedException thrownE) {
          throw new RuntimeException(thrownE); // Shouldn't happen
        } catch (ExecutionException thrownE) {
          log.error("Script.correctlySpends threw a non-normal exception: " + thrownE.getCause());
          throw new VerificationException(
              "Bug in Script.correctlySpends, likely script malformed in some new and interesting way.",
              thrownE);
        }
        if (e != null) throw e;
      }
    } catch (VerificationException e) {
      scriptVerificationExecutor.shutdownNow();
      blockStore.abortDatabaseBatchWrite();
      throw e;
    } catch (BlockStoreException e) {
      scriptVerificationExecutor.shutdownNow();
      blockStore.abortDatabaseBatchWrite();
      throw e;
    }
    return new TransactionOutputChanges(txOutsCreated, txOutsSpent);
  }