void undo(BlockImpl block) throws TransactionType.UndoNotSupportedException {
   block.undo();
   List<Transaction> addedUnconfirmedTransactions = new ArrayList<>();
   for (TransactionImpl transaction : block.getTransactions()) {
     TransactionHashInfo transactionHashInfo = transactionHashes.get(transaction.getHash());
     if (transactionHashInfo != null
         && transactionHashInfo.transactionId.equals(transaction.getId())) {
       transactionHashes.remove(transaction.getHash());
     }
     unconfirmedTransactions.put(transaction.getId(), transaction);
     unconfirmedTransactionHashes.put(transaction.getHash(), transaction);
     transaction.undo();
     addedUnconfirmedTransactions.add(transaction);
   }
   if (addedUnconfirmedTransactions.size() > 0) {
     transactionListeners.notify(
         addedUnconfirmedTransactions, TransactionProcessor.Event.ADDED_UNCONFIRMED_TRANSACTIONS);
   }
 }
 void removeUnconfirmedTransactions(Collection<TransactionImpl> transactions) {
   List<Transaction> removedList = new ArrayList<>();
   for (TransactionImpl transaction : transactions) {
     if (unconfirmedTransactions.remove(transaction.getId()) != null) {
       transaction.undoUnconfirmed();
       unconfirmedTransactionHashes.remove(transaction.getHash());
       removedList.add(transaction);
     }
   }
   transactionListeners.notify(removedList, Event.REMOVED_UNCONFIRMED_TRANSACTIONS);
 }
 void apply(BlockImpl block) {
   block.apply();
   for (TransactionImpl transaction : block.getTransactions()) {
     if (!unconfirmedTransactions.containsKey(transaction.getId())) {
       transaction.applyUnconfirmed();
     }
     // TODO: Phaser not yet implemented
     // Phaser.processTransaction(transaction);
     transaction.apply();
     transactionHashes.put(transaction.getHash(), new TransactionHashInfo(transaction));
   }
   purgeExpiredHashes(block.getTimestamp());
 }
  TransactionImpl checkTransactionHashes(BlockImpl block) {
    TransactionImpl duplicateTransaction = null;
    for (TransactionImpl transaction : block.getTransactions()) {
      if (transactionHashes.putIfAbsent(transaction.getHash(), new TransactionHashInfo(transaction))
              != null
          && block.getHeight() != 58294) {
        duplicateTransaction = transaction;
        break;
      }
    }

    if (duplicateTransaction != null) {
      for (TransactionImpl transaction : block.getTransactions()) {
        if (!transaction.equals(duplicateTransaction)) {
          TransactionHashInfo transactionHashInfo = transactionHashes.get(transaction.getHash());
          if (transactionHashInfo != null
              && transactionHashInfo.transactionId.equals(transaction.getId())) {
            transactionHashes.remove(transaction.getHash());
          }
        }
      }
    }
    return duplicateTransaction;
  }
        @Override
        public void run() {

          try {
            try {

              int curTime = Convert.getEpochTime();
              List<Transaction> removedUnconfirmedTransactions = new ArrayList<>();

              synchronized (BlockchainImpl.getInstance()) {
                Iterator<TransactionImpl> iterator = unconfirmedTransactions.values().iterator();
                while (iterator.hasNext()) {
                  TransactionImpl transaction = iterator.next();
                  if (transaction.getExpiration() < curTime) {
                    iterator.remove();
                    unconfirmedTransactionHashes.remove(transaction.getHash());
                    transaction.undoUnconfirmed();
                    removedUnconfirmedTransactions.add(transaction);
                  }
                }
              }

              if (removedUnconfirmedTransactions.size() > 0) {
                transactionListeners.notify(
                    removedUnconfirmedTransactions, Event.REMOVED_UNCONFIRMED_TRANSACTIONS);
              }

            } catch (Exception e) {
              Logger.logDebugMessage("Error removing unconfirmed transactions", e);
            }
          } catch (Throwable t) {
            Logger.logMessage("CRITICAL ERROR. PLEASE REPORT TO THE DEVELOPERS.\n" + t.toString());
            t.printStackTrace();
            System.exit(1);
          }
        }
  private List<Transaction> processTransactions(
      List<TransactionImpl> transactions, final boolean sendToPeers) {
    List<Transaction> sendToPeersTransactions = new ArrayList<>();
    List<Transaction> addedUnconfirmedTransactions = new ArrayList<>();
    List<Transaction> addedDoubleSpendingTransactions = new ArrayList<>();

    for (TransactionImpl transaction : transactions) {

      try {

        int curTime = Convert.getEpochTime();
        if (transaction.getTimestamp() > curTime + 15
            || transaction.getExpiration() < curTime
            || transaction.getDeadline() > 1440) {
          continue;
        }

        synchronized (BlockchainImpl.getInstance()) {
          Long id = transaction.getId();
          if (TransactionDb.hasTransaction(id)
              || unconfirmedTransactions.containsKey(id)
              || !transaction.verify()) {
            continue;
          }

          if (transactionHashes.containsKey(transaction.getHash())
              || unconfirmedTransactionHashes.containsKey(transaction.getHash())) {
            continue;
          }

          if (transaction.applyUnconfirmed()) {
            if (sendToPeers) {
              if (nonBroadcastedTransactions.containsKey(id)) {
                Logger.logDebugMessage(
                    "Received back transaction "
                        + transaction.getStringId()
                        + " that we generated, will not forward to peers");
                nonBroadcastedTransactions.remove(id);
              } else {
                sendToPeersTransactions.add(transaction);
              }
            }
            unconfirmedTransactions.put(id, transaction);
            unconfirmedTransactionHashes.put(transaction.getHash(), transaction);
            addedUnconfirmedTransactions.add(transaction);
          } else {
            addedDoubleSpendingTransactions.add(transaction);
          }
        }

      } catch (RuntimeException e) {
        Logger.logMessage("Error processing transaction", e);
      }
    }

    if (sendToPeersTransactions.size() > 0) {
      Peers.sendToSomePeers(sendToPeersTransactions);
    }

    if (addedUnconfirmedTransactions.size() > 0) {
      transactionListeners.notify(
          addedUnconfirmedTransactions, Event.ADDED_UNCONFIRMED_TRANSACTIONS);
    }
    if (addedDoubleSpendingTransactions.size() > 0) {
      transactionListeners.notify(
          addedDoubleSpendingTransactions, Event.ADDED_DOUBLESPENDING_TRANSACTIONS);
    }
    return addedUnconfirmedTransactions;
  }