private void setUpMockitoRules() throws Exception {

    when(mockDatabase.getDatabaseFactory()).thenReturn(mockDatabaseFactory);
    when(mockDatabaseTable.getEmptyRecord()).thenReturn(mockDatabaseTableRecord);
    when(mockDatabase.getTable(
            AssetIssuingTransactionDatabaseConstants.DIGITAL_ASSET_TRANSACTION_TABLE_NAME))
        .thenReturn(mockDatabaseTable);
    when(mockDatabase.getTable(
            AssetIssuingTransactionDatabaseConstants
                .DIGITAL_ASSET_TRANSACTION_ASSET_ISSUING_TABLE_NAME))
        .thenReturn(mockDatabaseTable2);
    when(mockDatabaseTable2.getEmptyRecord()).thenReturn(mockDatabaseTableRecord);
    when(mockDatabaseTable2.getRecords()).thenReturn(records);
    when(pluginDatabaseSystem.openDatabase(
            pluginId, AssetIssuingTransactionDatabaseConstants.DIGITAL_ASSET_TRANSACTION_DATABASE))
        .thenReturn(mockDatabase);

    when(deviceUser.getPublicKey()).thenReturn("myPublicKey");
    when(deviceUserManager.getLoggedInDeviceUser()).thenReturn(deviceUser);

    when(actorAssetIssuerManager.getActorAssetIssuer()).thenReturn(actorAssetIssuer);

    when(eventManager.getNewListener(
            EventType.INCOMING_ASSET_ON_CRYPTO_NETWORK_WAITING_TRANSFERENCE_ASSET_ISSUER))
        .thenReturn(fermatEventListener1);
    when(eventManager.getNewListener(
            EventType.INCOMING_ASSET_ON_BLOCKCHAIN_WAITING_TRANSFERENCE_ASSET_ISSUER))
        .thenReturn(fermatEventListener2);
    when(eventManager.getNewListener(
            EventType.INCOMING_ASSET_REVERSED_ON_BLOCKCHAIN_WAITING_TRANSFERENCE_ASSET_ISSUER))
        .thenReturn(fermatEventListener3);
    when(eventManager.getNewListener(
            EventType.INCOMING_ASSET_REVERSED_ON_CRYPTO_NETWORK_WAITING_TRANSFERENCE_ASSET_ISSUER))
        .thenReturn(fermatEventListener4);

    when(pluginFileSystem.createTextFile(
            this.pluginId,
            "digital-asset-issuing/publicKey",
            "name.xml",
            FilePrivacy.PUBLIC,
            FileLifeSpan.PERMANENT))
        .thenReturn(pluginTextFile);
    when(bitcoinWalletManager.loadWallet(this.walletPublicKey)).thenReturn(bitcoinWalletWallet);
    when(bitcoinWalletWallet.getBalance(BalanceType.AVAILABLE)).thenReturn(bitcoinWalletBalance);
    when(bitcoinWalletBalance.getBalance()).thenReturn(bitcoinWalletAvailableBalance);
    when(assetVaultManager.getNewAssetVaultCryptoAddress(this.blockchainNetworkType))
        .thenReturn(cryptoAddress);
    //        doNothing().when(assetIssuingPluginRoot).issueAssets(digitalAsset, 1, walletPublicKey,
    // blockchainNetworkType);
  }
  private void doTheMainTask() {
    /*
     * TODO: The first thing to do is to ask the crypto vault
     *       the source address, transaction hash and then the
     *       timestamp of the event ON_CRYPTO_NETWORK
     */

    /* TODO: Reemplazar por el que se lee de la transacción
     *       Esto se va a poder hacer cuando nos pasen todos los parámetros
     *       necesarios.
     */
    List<
            com.bitdubai.fermat_ccp_plugin.layer.crypto_transaction.outgoing_extra_user.developer
                .bitdubai.version_1.util.TransactionWrapper>
        transactionList;

    // We first check for the new transactions to apply
    try {
      transactionList = dao.getNewTransactions();
    } catch (Exception e) {
      this.reportUnexpectedError(e);
      return;
    }

    /* For each transaction:
     1. We check that we can apply it
     2. We apply it in the bitcoin wallet available balance
    */

    long funds;
    for (com.bitdubai.fermat_ccp_plugin.layer.crypto_transaction.outgoing_extra_user.developer
            .bitdubai.version_1.util.TransactionWrapper
        transaction : transactionList) {

      try {
        BitcoinWalletWallet bitcoinWalletWallet =
            bitcoinWalletManager.loadWallet(transaction.getWalletPublicKey());
        funds = bitcoinWalletWallet.getBalance(BalanceType.AVAILABLE).getBalance();
        if (funds < transaction.getAmount()) {
          dao.cancelTransaction(transaction, "Insufficient founds.");
          // TODO: Lanzar un evento de fondos insuficientes
        } else {
          // If we have enough funds we debit them from the available balance
          bitcoinWalletWallet.getBalance(BalanceType.AVAILABLE).debit(transaction);
          // The we set that we register that we have executed the debit
          dao.setToPIA(transaction);
        }

      } catch (Exception e) {

        this.reportUnexpectedError(e);
      }
    }

    // Now we check for all the transactions that have been discounted from the available amount
    // but bot applied to vault
    try {
      transactionList = dao.getPersistedInAvailable();
    } catch (Exception e) {
      this.reportUnexpectedError(e);
      return;
    }

    for (com.bitdubai.fermat_ccp_plugin.layer.crypto_transaction.outgoing_extra_user.developer
            .bitdubai.version_1.util.TransactionWrapper
        transaction : transactionList) {
      // Now we apply it in the vault
      try {
        String hash =
            this.cryptoVaultManager.sendBitcoins(
                transaction.getWalletPublicKey(),
                transaction.getTransactionId(),
                transaction.getAddressTo(),
                transaction.getAmount());
        dao.setTransactionHash(transaction, hash);
        dao.setToSTCV(transaction);

      } catch (InsufficientCryptoFundsException e) {

        // TODO: THEN RAISE EVENT TO INFORM THE SITUATION

        try {
          dao.cancelTransaction(transaction, "Insufficient founds.");
        } catch (CantUpdateRecordException
            | InconsistentTableStateException
            | CantLoadTableToMemoryException e2) {
          reportUnexpectedError(e2);
          continue;
        } catch (Exception exception) {
          reportUnexpectedError(exception);
          continue;
        }
        // And we finally report the error
        Exception ez =
            new InconsistentFundsException(
                "Basic wallet balance and crypto vault funds are inconsistent", e, "", "");
        reportUnexpectedError(ez);

      } catch (InvalidSendToAddressException | CouldNotSendMoneyException e) {

        try {

          dao.cancelTransaction(transaction, "There was a problem and the money was not sent.");

        } catch (Exception exception) {
          reportUnexpectedError(exception);
          continue;
        }

        reportUnexpectedError(e);

      } catch (Exception exception) {

        reportUnexpectedError(exception);
      }
    }

    /*
     * Now we proceed to apply the transactions sent to the bitcoin network to the wallet book
     * balance. We need to check the state of the transaction in the crypto vault before
     * discounting it
     */
    try {
      transactionList = dao.getSentToCryptoVaultTransactions();

    } catch (Exception exception) {
      reportUnexpectedError(exception);
      return;
    }

    for (com.bitdubai.fermat_ccp_plugin.layer.crypto_transaction.outgoing_extra_user.developer
            .bitdubai.version_1.util.TransactionWrapper
        transaction : transactionList) {

      try {
        BitcoinWalletWallet bitcoinWalletWallet =
            bitcoinWalletManager.loadWallet(transaction.getWalletPublicKey());
        CryptoStatus cryptoStatus =
            this.bitcoinNetworkManager.getCryptoStatus(transaction.getTransactionId());
        com.bitdubai.fermat_ccp_plugin.layer.crypto_transaction.outgoing_extra_user.developer
            .bitdubai.version_1.util.TransactionHandler.handleTransaction(
            transaction,
            bitcoinNetworkManager,
            cryptoStatus,
            bitcoinWalletWallet,
            this.dao,
            this.errorManager);

      } catch (Exception exception) {
        reportUnexpectedError(exception);
      }
    }
  }
  // Acordate que la transaccion tiene que tener un tipo Intra Wallets y la transaccion que se
  // guarda en la loss lleva el Exchange rate en cero
  // hay q ver si no puede completar la transaccion de avisar con una notificacion
  @Override
  public void sendToWallet(
      String txHash, // ESTE TX HASH PARA QUE SERIA?
      long cryptoAmount,
      String notes,
      Actors actortype,
      ReferenceWallet reference_wallet_sending,
      ReferenceWallet reference_wallet_receiving,
      String wallet_public_key_sending,
      String wallet_public_key_receiving,
      BlockchainNetworkType blockchainNetworkType)
      throws CantSendTransactionException, TransferIntraWalletUsersNotEnoughFundsException {

    Long initialBalance = null;

    UUID id = UUID.randomUUID();

    try {

      // Register the new transaction
      dao.registerNewTransaction(
          id,
          txHash,
          cryptoAmount,
          notes,
          actortype,
          reference_wallet_sending,
          reference_wallet_receiving,
          wallet_public_key_sending,
          wallet_public_key_receiving,
          TransactionState.NEW,
          blockchainNetworkType);

      TransferIntraWalletUsersWrapper transferIntraWalletUsersWrapper = dao.getTransaction(id);

      // DEBIT TO WALLET SENDING

      switch (reference_wallet_sending) {
        case BASIC_WALLET_BITCOIN_WALLET:

          // Consult current balance of the sending wallet

          BitcoinWalletWallet bitcoinWalletWallet =
              this.bitcoinWalletManager.loadWallet(wallet_public_key_sending);
          initialBalance =
              bitcoinWalletWallet
                  .getBalance(BalanceType.AVAILABLE)
                  .getBalance(blockchainNetworkType);

          // Checks the balance of the debiting wallet in order to decide if to continue or not.

          if (initialBalance != null && initialBalance >= cryptoAmount) {

            // Prepares the record to be used within transactions

            // TODO: los actors de estas transacciones van a ser la wallets, hay que distinguir
            // esto con el actor type para poder mostrar despues bien el que seria el contacto
            // asociado
            BitcoinWalletTransactionRecord bitcoinWalletTransactionWalletRecord =
                buildBitcoinWalletRecord(
                    id,
                    new CryptoAddress("", CryptoCurrency.BITCOIN),
                    null,
                    cryptoAmount,
                    new CryptoAddress("", CryptoCurrency.BITCOIN),
                    notes,
                    System.currentTimeMillis(),
                    "",
                    wallet_public_key_sending,
                    wallet_public_key_receiving,
                    actortype,
                    actortype,
                    blockchainNetworkType);

            bitcoinWalletWallet
                .getBalance(BalanceType.BOOK)
                .debit(bitcoinWalletTransactionWalletRecord);
            bitcoinWalletWallet
                .getBalance(BalanceType.AVAILABLE)
                .debit(bitcoinWalletTransactionWalletRecord);

            // Proceeds to credit in the destination wallet
            try {
              receivedToWallet(
                  id,
                  txHash,
                  cryptoAmount,
                  notes,
                  actortype,
                  reference_wallet_sending,
                  reference_wallet_receiving,
                  wallet_public_key_sending,
                  wallet_public_key_receiving,
                  blockchainNetworkType);
            } catch (CantReceiveWalletTransactionException e) {
              // change transaction state to reversed and update balance to revert
              bitcoinWalletWallet.revertTransaction(bitcoinWalletTransactionWalletRecord, false);
              dao.setToError(transferIntraWalletUsersWrapper);
              broadcaster.publish(
                  BroadcasterType.NOTIFICATION_SERVICE,
                  wallet_public_key_sending,
                  "TRANSACTIONWALLETREVERSE");

              throw new CantSendTransactionException(
                  "I could not send the transaction",
                  e,
                  "TransferIntraWalletUsersModuleManager",
                  "Recived Wallet process error");
            }

            // process oK
            dao.setToCompleted(transferIntraWalletUsersWrapper);

          } else {
            broadcaster.publish(
                BroadcasterType.NOTIFICATION_SERVICE,
                wallet_public_key_sending,
                "TRANSACTIONWALLETREVERSE");
            // change transaction state to error
            dao.setToError(transferIntraWalletUsersWrapper);
            // There are not enough funds to perform this transaction
            throw new TransferIntraWalletUsersNotEnoughFundsException(
                "There are not enough funds to perform this transaction",
                null,
                "",
                "NotEnoughFunds");
          }

          break;
        case BASIC_WALLET_DISCOUNT_WALLET:
          break;
        case BASIC_WALLET_FIAT_WALLET:
          break;
        case BASIC_WALLET_LOSS_PROTECTED_WALLET:
          BitcoinLossProtectedWallet bitcoinLossProtectedWallet =
              this.bitcoinLossWalletManager.loadWallet(wallet_public_key_sending);
          // consult current balance of the sending wallet
          initialBalance =
              bitcoinLossProtectedWallet
                  .getBalance(BalanceType.AVAILABLE)
                  .getBalance(blockchainNetworkType);

          // Checks the balance of the debiting wallet in order to decide if to continue or not.

          if (initialBalance != null && initialBalance >= cryptoAmount) {

            // Prepares the record to be used within transactions

            BitcoinLossProtectedWalletTransactionRecord
                bitcoinLossProtectedWalletTransactionWalletRecord2 =
                    buildLossWalletRecord(
                        id,
                        new CryptoAddress("", CryptoCurrency.BITCOIN),
                        null,
                        cryptoAmount,
                        new CryptoAddress("", CryptoCurrency.BITCOIN),
                        notes,
                        System.currentTimeMillis(),
                        "",
                        "",
                        "",
                        actortype,
                        actortype,
                        blockchainNetworkType);

            bitcoinLossProtectedWallet
                .getBalance(BalanceType.BOOK)
                .debit(bitcoinLossProtectedWalletTransactionWalletRecord2);
            bitcoinLossProtectedWallet
                .getBalance(BalanceType.AVAILABLE)
                .debit(bitcoinLossProtectedWalletTransactionWalletRecord2);

            try {
              receivedToWallet(
                  id,
                  txHash,
                  cryptoAmount,
                  notes,
                  actortype,
                  reference_wallet_sending,
                  reference_wallet_receiving,
                  wallet_public_key_sending,
                  wallet_public_key_receiving,
                  blockchainNetworkType);

            } catch (CantReceiveWalletTransactionException e) {

              bitcoinLossProtectedWallet.revertTransaction(
                  bitcoinLossProtectedWalletTransactionWalletRecord2, false);
              broadcaster.publish(
                  BroadcasterType.NOTIFICATION_SERVICE,
                  wallet_public_key_sending,
                  "TRANSACTIONWALLETREVERSE");

              dao.setToError(transferIntraWalletUsersWrapper);
              throw new CantSendTransactionException(
                  "I could not send the transaction",
                  e,
                  "TransferIntraWalletUsersModuleManager",
                  "Recived Wallet process error");
            }

            // process oK
            dao.setToCompleted(transferIntraWalletUsersWrapper);

          } else {
            dao.setToError(transferIntraWalletUsersWrapper);
            // There are not enough funds to perform this transaction
            broadcaster.publish(
                BroadcasterType.NOTIFICATION_SERVICE,
                wallet_public_key_sending,
                "TRANSACTIONWALLETREVERSE");

            throw new TransferIntraWalletUsersNotEnoughFundsException(
                "There are not enough funds to perform this transaction",
                null,
                "",
                "NotEnoughFunds");
          }

          break;
        case COMPOSITE_WALLET_MULTI_ACCOUNT:
          break;
      }

    } catch (CantLoadWalletException e) {
      throw new CantSendTransactionException(
          "I could not send the transaction",
          e,
          "TransferIntraWalletUsersModuleManager",
          "unknown reason");

    } catch (TransferIntraWalletUsersCantInsertRecordException e) {
      throw new CantSendTransactionException(
          "I could not send the transaction",
          e,
          "TransferIntraWalletUsersModuleManager",
          "unknown reason");
    } catch (CantCalculateBalanceException e) {
      throw new CantSendTransactionException(
          "I could not send the transaction",
          e,
          "TransferIntraWalletUsersModuleManager",
          "unknown reason");
    } catch (CantLoadWalletsException e) {
      throw new CantSendTransactionException(
          "I could not send the transaction",
          e,
          "TransferIntraWalletUsersModuleManager",
          "unknown reason");
    } catch (InvalidParameterException e) {
      throw new CantSendTransactionException(
          "I could not send the transaction",
          e,
          "TransferIntraWalletUsersModuleManager",
          "unknown reason");
    } catch (CantLoadTableToMemoryException e) {
      throw new CantSendTransactionException(
          "I could not send the transaction",
          e,
          "TransferIntraWalletUsersModuleManager",
          "unknown reason");
    } catch (Exception e) {
      throw new CantSendTransactionException(
          "I could not send the transaction",
          FermatException.wrapException(e),
          "TransferIntraWalletUsersModuleManager",
          "unknown reason");
    }
  }
  private void receivedToWallet(
      UUID id,
      String txHash,
      long cryptoAmount,
      String notes,
      Actors actortype,
      ReferenceWallet reference_wallet_sending,
      ReferenceWallet reference_wallet_receiving,
      String wallet_public_key_sending,
      String wallet_public_key_receiving,
      BlockchainNetworkType blockchainNetworkType)
      throws CantReceiveWalletTransactionException {

    try {
      TransferIntraWalletUsersWrapper transactionWrapper = dao.getTransaction(id);

      // checks what is the corresponding wallet to be debited.
      switch (reference_wallet_receiving) {
        case BASIC_WALLET_BITCOIN_WALLET:

          // Prepares the record to be used within transactions

          BitcoinWalletTransactionWalletRecord bitcoinWalletTransactionWalletRecord2 =
              buildBitcoinWalletRecord(
                  id,
                  new CryptoAddress("", CryptoCurrency.BITCOIN),
                  null,
                  cryptoAmount,
                  new CryptoAddress("", CryptoCurrency.BITCOIN),
                  notes,
                  System.currentTimeMillis(),
                  "",
                  wallet_public_key_sending,
                  wallet_public_key_receiving,
                  actortype,
                  actortype,
                  blockchainNetworkType);

          BitcoinWalletWallet bitcoinWalletWallet =
              this.bitcoinWalletManager.loadWallet(wallet_public_key_receiving);

          try {
            bitcoinWalletWallet
                .getBalance(BalanceType.AVAILABLE)
                .credit(bitcoinWalletTransactionWalletRecord2);

          } catch (CantRegisterCreditException e) {
            throw new CantReceiveWalletTransactionException(
                CantReceiveWalletTransactionException.DEFAULT_MESSAGE,
                FermatException.wrapException(e),
                "",
                "");
          }
          try {
            bitcoinWalletWallet
                .getBalance(BalanceType.BOOK)
                .credit(bitcoinWalletTransactionWalletRecord2);
          } catch (CantRegisterCreditException e) {
            bitcoinWalletWallet
                .getBalance(BalanceType.AVAILABLE)
                .debit(bitcoinWalletTransactionWalletRecord2);
            throw new CantReceiveWalletTransactionException(
                CantReceiveWalletTransactionException.DEFAULT_MESSAGE,
                FermatException.wrapException(e),
                "",
                "");
          }
          break;
        case BASIC_WALLET_DISCOUNT_WALLET:
          break;
        case BASIC_WALLET_FIAT_WALLET:
          break;
        case BASIC_WALLET_LOSS_PROTECTED_WALLET:
          // Prepares the record to be used within transactions
          BitcoinLossProtectedWallet bitcoinLossProtectedWallet =
              this.bitcoinLossWalletManager.loadWallet(wallet_public_key_receiving);
          BitcoinLossProtectedWalletTransactionRecord
              bitcoinLossProtectedWalletTransactionWalletRecord =
                  buildLossWalletRecord(
                      id,
                      new CryptoAddress("", CryptoCurrency.BITCOIN),
                      null,
                      cryptoAmount,
                      new CryptoAddress("", CryptoCurrency.BITCOIN),
                      notes,
                      System.currentTimeMillis(),
                      "",
                      wallet_public_key_sending,
                      wallet_public_key_receiving,
                      actortype,
                      actortype,
                      blockchainNetworkType);

          try {
            bitcoinLossProtectedWallet
                .getBalance(BalanceType.AVAILABLE)
                .credit(bitcoinLossProtectedWalletTransactionWalletRecord);

          } catch (CantRegisterCreditException e) {
            throw new CantReceiveWalletTransactionException(
                CantReceiveWalletTransactionException.DEFAULT_MESSAGE,
                FermatException.wrapException(e),
                "",
                "");
          }
          try {
            bitcoinLossProtectedWallet
                .getBalance(BalanceType.BOOK)
                .credit(bitcoinLossProtectedWalletTransactionWalletRecord);
          } catch (CantRegisterCreditException e) {
            bitcoinLossProtectedWallet
                .getBalance(BalanceType.AVAILABLE)
                .debit(bitcoinLossProtectedWalletTransactionWalletRecord);
            throw new CantReceiveWalletTransactionException(
                CantReceiveWalletTransactionException.DEFAULT_MESSAGE,
                FermatException.wrapException(e),
                "",
                "");
          }
          break;
      }
    } catch (CantLoadTableToMemoryException e) {
      throw new CantReceiveWalletTransactionException(
          CantReceiveWalletTransactionException.DEFAULT_MESSAGE, e, "", "");
    } catch (InvalidParameterException e) {
      throw new CantReceiveWalletTransactionException(
          CantReceiveWalletTransactionException.DEFAULT_MESSAGE, e, "", "");
    } catch (CantLoadWalletException e) {
      throw new CantReceiveWalletTransactionException(
          CantReceiveWalletTransactionException.DEFAULT_MESSAGE, e, "", "");
    } catch (CantLoadWalletsException e) {
      throw new CantReceiveWalletTransactionException(
          CantReceiveWalletTransactionException.DEFAULT_MESSAGE, e, "", "");
    } catch (Exception e) {
      throw new CantReceiveWalletTransactionException(
          CantReceiveWalletTransactionException.DEFAULT_MESSAGE,
          FermatException.wrapException(e),
          "",
          "");
    }
  }