text: // Copyright (c) 2011-2017 The Cryptonote developers
  // Copyright (c) 2018 The Circle Foundation
  // Copyright (c) 2019 Aluisyo
  // Distributed under the MIT/X11 software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #include "PaymentGateService.h"
  
  #include <future>
  
  #include "Common/SignalHandler.h"
  #include "InProcessNode/InProcessNode.h"
  #include "Logging/LoggerRef.h"
  #include "PaymentGate/PaymentServiceJsonRpcServer.h"
  
  #include "CryptoNoteCore/CoreConfig.h"
  #include "CryptoNoteCore/Core.h"
  #include "CryptoNoteProtocol/CryptoNoteProtocolHandler.h"
  #include "P2p/NetNode.h"
  #include "Rpc/RpcServer.h"
  #include "PaymentGate/WalletFactory.h"
  #include <System/Context.h>
  #include "Wallet/WalletGreen.h"
  
  #ifdef ERROR
  #undef ERROR
  #endif
  
  #ifdef _WIN32
  #include <direct.h>
  #else
  #include <unistd.h>
  #endif
  
  using namespace PaymentService;
  
  void changeDirectory(const std::string& path) {
    if (chdir(path.c_str())) {
      throw std::runtime_error("Couldn't change directory to \'" + path + "\': " + strerror(errno));
    }
  }
  
  void stopSignalHandler(PaymentGateService* pg) {
    pg->stop();
  }
  
  bool PaymentGateService::init(int argc, char** argv) {
    if (!config.init(argc, argv)) {
      return false;
    }
  
    logger.setMaxLevel(static_cast<Logging::Level>(config.gateConfiguration.logLevel));
    logger.addLogger(consoleLogger);
  
    Logging::LoggerRef log(logger, "main");
  
    if (config.gateConfiguration.testnet) {
      log(Logging::INFO) << "Starting in testnet mode";
      currencyBuilder.testnet(true);
    }
  
    if (!config.gateConfiguration.serverRoot.empty()) {
      changeDirectory(config.gateConfiguration.serverRoot);
      log(Logging::INFO) << "Current working directory now is " << config.gateConfiguration.serverRoot;
    }
  
    fileStream.open(config.gateConfiguration.logFile, std::ofstream::app);
  
    if (!fileStream) {
      throw std::runtime_error("Couldn't open log file");
    }
  
    fileLogger.attachToStream(fileStream);
    logger.addLogger(fileLogger);
  
    return true;
  }
  
  WalletConfiguration PaymentGateService::getWalletConfig() const {
    return WalletConfiguration{
      config.gateConfiguration.containerFile,
      config.gateConfiguration.containerPassword
    };
  }
  
  const CryptoNote::Currency PaymentGateService::getCurrency() {
    return currencyBuilder.currency();
  }
  
  void PaymentGateService::run() {
    
    System::Dispatcher localDispatcher;
    System::Event localStopEvent(localDispatcher);
  
    this->dispatcher = &localDispatcher;
    this->stopEvent = &localStopEvent;
  
    Tools::SignalHandler::install(std::bind(&stopSignalHandler, this));
  
    Logging::LoggerRef log(logger, "run");
  
    if (config.startInprocess) {
      runInProcess(log);
    } else {
      runRpcProxy(log);
    }
  
    this->dispatcher = nullptr;
    this->stopEvent = nullptr;
  }
  
  void PaymentGateService::stop() {
    Logging::LoggerRef log(logger, "stop");
  
    log(Logging::INFO) << "Stop signal caught";
  
    if (dispatcher != nullptr) {
      dispatcher->remoteSpawn([&]() {
        if (stopEvent != nullptr) {
          stopEvent->set();
        }
      });
    }
  }
  
  void PaymentGateService::runInProcess(Logging::LoggerRef& log) {
    if (!config.coreConfig.configFolderDefaulted) {
      if (!Tools::directoryExists(config.coreConfig.configFolder)) {
        throw std::runtime_error("Directory does not exist: " + config.coreConfig.configFolder);
      }
    } else {
      if (!Tools::create_directories_if_necessary(config.coreConfig.configFolder)) {
        throw std::runtime_error("Can't create directory: " + config.coreConfig.configFolder);
      }
    }
  
    log(Logging::INFO) << "Starting Payment Gate with local node";
  
    CryptoNote::Currency currency = currencyBuilder.currency();
    CryptoNote::core core(currency, NULL, logger);
  
    CryptoNote::CryptoNoteProtocolHandler protocol(currency, *dispatcher, core, NULL, logger);
    CryptoNote::NodeServer p2pNode(*dispatcher, protocol, logger);
    CryptoNote::RpcServer rpcServer(*dispatcher, logger, core, p2pNode, protocol);
    CryptoNote::Checkpoints checkpoints(logger);
    for (const auto& cp : CryptoNote::CHECKPOINTS) {
      checkpoints.add_checkpoint(cp.height, cp.blockId);
    }
    protocol.set_p2p_endpoint(&p2pNode);
    core.set_cryptonote_protocol(&protocol);
  
    log(Logging::INFO) << "initializing p2pNode";
    if (!p2pNode.init(config.netNodeConfig)) {
      throw std::runtime_error("Failed to init p2pNode");
    }
  
    log(Logging::INFO) << "initializing core";
    CryptoNote::MinerConfig emptyMiner;
    core.init(config.coreConfig, emptyMiner, true);
  
    std::promise<std::error_code> initPromise;
    auto initFuture = initPromise.get_future();
  
    std::unique_ptr<CryptoNote::INode> node(new CryptoNote::InProcessNode(core, protocol));
  
    node->init([&initPromise, &log](std::error_code ec) {
      if (ec) {
        log(Logging::WARNING, Logging::YELLOW) << "Failed to init node: " << ec.message();
      } else {
        log(Logging::INFO) << "node is inited successfully";
      }
  
      initPromise.set_value(ec);
    });
  
    auto ec = initFuture.get();
    if (ec) {
      throw std::system_error(ec);
    }
  
    log(Logging::INFO) << "Starting core rpc server on "
      << config.remoteNodeConfig.daemonHost << ":" << config.remoteNodeConfig.daemonPort;
    rpcServer.start(config.remoteNodeConfig.daemonHost, config.remoteNodeConfig.daemonPort);
    log(Logging::INFO) << "Core rpc server started ok";
  
    log(Logging::INFO) << "Spawning p2p server";
  
    System::Event p2pStarted(*dispatcher);
    
    System::Context<> context(*dispatcher, [&]() {
      p2pStarted.set();
      p2pNode.run();
    });
  
    p2pStarted.wait();
  
    runWalletService(currency, *node);
  
    log(Logging::INFO) << "Stopping core rpc server...";
    rpcServer.stop();
    p2pNode.sendStopSignal();
    context.get();
    node->shutdown();
    core.deinit();
    p2pNode.deinit(); 
  }
  
  void PaymentGateService::runRpcProxy(Logging::LoggerRef& log) {
    log(Logging::INFO) << "Starting Payment Gate with remote node";
    CryptoNote::Currency currency = currencyBuilder.currency();
    
    std::unique_ptr<CryptoNote::INode> node(
      PaymentService::NodeFactory::createNode(
        config.remoteNodeConfig.daemonHost, 
        config.remoteNodeConfig.daemonPort));
  
    runWalletService(currency, *node);
  }
  
  void PaymentGateService::runWalletService(const CryptoNote::Currency& currency, CryptoNote::INode& node) {
    PaymentService::WalletConfiguration walletConfiguration{
      config.gateConfiguration.containerFile,
      config.gateConfiguration.containerPassword
    };
  
    std::unique_ptr<CryptoNote::WalletGreen> wallet(new CryptoNote::WalletGreen(*dispatcher, currency, node));
  
    service = new PaymentService::WalletService(currency, *dispatcher, node, *wallet, *wallet, walletConfiguration, logger);
    std::unique_ptr<PaymentService::WalletService> serviceGuard(service);
    try {
      service->init();
    } catch (std::exception& e) {
      Logging::LoggerRef(logger, "run")(Logging::ERROR, Logging::BRIGHT_RED) << "Failed to init walletService reason: " << e.what();
      return;
    }
  
    if (config.gateConfiguration.printAddresses) {
      // print addresses and exit
      std::vector<std::string> addresses;
      service->getAddresses(addresses);
      for (const auto& address: addresses) {
        std::cout << "Address: " << address << std::endl;
      }
    } else {
      PaymentService::PaymentServiceJsonRpcServer rpcServer(*dispatcher, *stopEvent, *service, logger);
      rpcServer.start(config.gateConfiguration.bindAddress, config.gateConfiguration.bindPort,
        config.gateConfiguration.rpcUser, config.gateConfiguration.rpcPassword);
  
      try {
        service->saveWallet();
      } catch (std::exception& ex) {
        Logging::LoggerRef(logger, "saveWallet")(Logging::WARNING, Logging::YELLOW) << "Couldn't save container: " << ex.what();
      }
    }
  }
  