text: // Copyright (c) 2009-2010 Satoshi Nakamoto
  // Copyright (c) 2009-2017 The Bitcoin Core developers
  // Copyright (c) 2018-2018 The bitcoinphantom Core developers
  // Distributed under the MIT software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #include <chainparams.h>
  #include <primitives/block.h>
  #include <init.h>
  #include <net.h>
  #include <scheduler.h>
  #include <util/system.h>
  #include <util/moneystr.h>
  #include <validation.h>
  #include <walletinitinterface.h>
  #include <wallet/rpcwallet.h>
  #include <wallet/wallet.h>
  #include <wallet/walletutil.h>
  
  class WalletInit : public WalletInitInterface {
  public:
  
      //! Return the wallets help message.
      void AddWalletOptions() const override;
  
      //! Wallets parameter interaction
      bool ParameterInteraction() const override;
  
      //! Register wallet RPCs.
      void RegisterRPC(CRPCTable &tableRPC) const override;
  
      //! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
      //  This function will perform salvage on the wallet if requested, as long as only one wallet is
      //  being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
      bool Verify() const override;
  
      //! Load wallet databases.
      bool Open() const override;
  
      //! Complete startup of wallets.
      void Start(CScheduler& scheduler) const override;
  
      //! Flush all wallets in preparation for shutdown.
      void Flush() const override;
  
      //! Stop all wallets. Wallets will be flushed first.
      void Stop() const override;
  
      //! Close all wallets.
      void Close() const override;
  };
  
  const WalletInitInterface& g_wallet_init_interface = WalletInit();
  
  void WalletInit::AddWalletOptions() const
  {
      gArgs.AddArg("-addresstype", strprintf("What type of addresses to use (\"legacy\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), false, OptionsCategory::WALLET);
      gArgs.AddArg("-changetype", "What type of change to use (\"legacy\"). Default is same as -addresstype", false, OptionsCategory::WALLET);
      gArgs.AddArg("-disablewallet", "Do not load the wallet and disable wallet RPC calls", false, OptionsCategory::WALLET);
      gArgs.AddArg("-discardfee=<amt>", strprintf("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). "
                                                                  "Note: An output is discarded if it is dust at this rate, but we will always discard up to the dust relay fee and a discard fee above that is limited by the fee estimate for the longest target",
                                                                CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE)), false, OptionsCategory::WALLET);
      gArgs.AddArg("-fallbackfee=<amt>", strprintf("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)",
                                                                 CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)), false, OptionsCategory::WALLET);
      gArgs.AddArg("-keypool=<n>", strprintf("Set key pool size to <n> (default: %u)", DEFAULT_KEYPOOL_SIZE), false, OptionsCategory::WALLET);
      gArgs.AddArg("-mintxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)",
                                                              CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)), false, OptionsCategory::WALLET);
      gArgs.AddArg("-paytxfee=<amt>", strprintf("Fee (in %s/kB) to add to transactions you send (default: %s)",
                                                              CURRENCY_UNIT, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK())), false, OptionsCategory::WALLET);
      gArgs.AddArg("-rescan", "Rescan the block chain for missing wallet transactions on startup", false, OptionsCategory::WALLET);
      gArgs.AddArg("-salvagewallet", "Attempt to recover private keys from a corrupt wallet on startup", false, OptionsCategory::WALLET);
      gArgs.AddArg("-spendzeroconfchange", strprintf("Spend unconfirmed change when sending transactions (default: %u)", DEFAULT_SPEND_ZEROCONF_CHANGE), false, OptionsCategory::WALLET);
      gArgs.AddArg("-txconfirmtarget=<n>", strprintf("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)", DEFAULT_TX_CONFIRM_TARGET), false, OptionsCategory::WALLET);
      gArgs.AddArg("-upgradewallet", "Upgrade wallet to latest format on startup", false, OptionsCategory::WALLET);
      gArgs.AddArg("-wallet=<path>", "Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)", false, OptionsCategory::WALLET);
      gArgs.AddArg("-walletbroadcast",  strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), false, OptionsCategory::WALLET);
      gArgs.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", false, OptionsCategory::WALLET);
      gArgs.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)", false, OptionsCategory::WALLET);
      gArgs.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), false, OptionsCategory::WALLET);
      gArgs.AddArg("-zapwallettxes=<mode>", "Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup"
                                 " (1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)", false, OptionsCategory::WALLET);
  
      gArgs.AddArg("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE), true, OptionsCategory::WALLET_DEBUG_TEST);
      gArgs.AddArg("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET), true, OptionsCategory::WALLET_DEBUG_TEST);
      gArgs.AddArg("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB), true, OptionsCategory::WALLET_DEBUG_TEST);
      gArgs.AddArg("-walletrejectlongchains", strprintf("Wallet will not create transactions that violate mempool chain limits (default: %u)", DEFAULT_WALLET_REJECT_LONG_CHAINS), true, OptionsCategory::WALLET_DEBUG_TEST);
  }
  
  bool WalletInit::ParameterInteraction() const
  {
      if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
          for (const std::string& wallet : gArgs.GetArgs("-wallet")) {
              LogPrintf("%s: parameter interaction: -disablewallet -> ignoring -wallet=%s\n", __func__, wallet);
          }
  
          return true;
      }
  
      gArgs.SoftSetArg("-wallet", "");
      const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1;
  
      if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) {
          LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__);
      }
          // determine mining Algo
      std::string strAlgo = gArgs.GetArg("-algo", "scrypt");
      transform(strAlgo.begin(),strAlgo.end(),strAlgo.begin(),::tolower);
      ALGO = GetAlgoByName(strAlgo);
  
      gArgs.SoftSetArg("-fallbackfee", "0.01");
      
      LogPrintf("%s: Using algo: %s %d\n", __func__, strAlgo.c_str(), ALGO);
  
      if (gArgs.GetBoolArg("-salvagewallet", false)) {
          if (is_multiwallet) {
              return InitError(strprintf("%s is only allowed with a single wallet file", "-salvagewallet"));
          }
          // Rewrite just private keys: rescan to find transactions
          if (gArgs.SoftSetBoolArg("-rescan", true)) {
              LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__);
          }
      }
  
      bool zapwallettxes = gArgs.GetBoolArg("-zapwallettxes", false);
      // -zapwallettxes implies dropping the mempool on startup
      if (zapwallettxes && gArgs.SoftSetBoolArg("-persistmempool", false)) {
          LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -persistmempool=0\n", __func__);
      }
  
      // -zapwallettxes implies a rescan
      if (zapwallettxes) {
          if (is_multiwallet) {
              return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes"));
          }
          if (gArgs.SoftSetBoolArg("-rescan", true)) {
              LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -rescan=1\n", __func__);
          }
      }
  
      if (is_multiwallet) {
          if (gArgs.GetBoolArg("-upgradewallet", false)) {
              return InitError(strprintf("%s is only allowed with a single wallet file", "-upgradewallet"));
          }
      }
  
      if (gArgs.GetBoolArg("-sysperms", false))
          return InitError("-sysperms is not allowed in combination with enabled wallet functionality");
      if (gArgs.GetArg("-prune", 0) && gArgs.GetBoolArg("-rescan", false))
          return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again."));
  
      if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB)
          InitWarning(AmountHighWarn("-minrelaytxfee") + " " +
                      _("The wallet will avoid paying less than the minimum relay fee."));
  
      if (gArgs.IsArgSet("-maxtxfee"))
      {
          CAmount nMaxFee = 0;
          if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee))
              return InitError(AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", "")));
          if (nMaxFee > HIGH_MAX_TX_FEE)
              InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction."));
          maxTxFee = nMaxFee;
          if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee)
          {
              return InitError(strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),
                                         gArgs.GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString()));
          }
      }
  
      return true;
  }
  
  void WalletInit::RegisterRPC(CRPCTable &t) const
  {
      if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
          return;
      }
  
      RegisterWalletRPCCommands(t);
  }
  
  bool WalletInit::Verify() const
  {
      if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
          return true;
      }
  
      if (gArgs.IsArgSet("-walletdir")) {
          fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
          if (!fs::exists(wallet_dir)) {
              return InitError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
          } else if (!fs::is_directory(wallet_dir)) {
              return InitError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
          } else if (!wallet_dir.is_absolute()) {
              return InitError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
          }
      }
  
      LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
  
      uiInterface.InitMessage(_("Verifying wallet(s)..."));
  
      std::vector<std::string> wallet_files = gArgs.GetArgs("-wallet");
  
      // Parameter interaction code should have thrown an error if -salvagewallet
      // was enabled with more than wallet file, so the wallet_files size check
      // here should have no effect.
      bool salvage_wallet = gArgs.GetBoolArg("-salvagewallet", false) && wallet_files.size() <= 1;
  
      // Keep track of each wallet absolute path to detect duplicates.
      std::set<fs::path> wallet_paths;
  
      for (const auto wallet_file : wallet_files) {
          fs::path wallet_path = fs::absolute(wallet_file, GetWalletDir());
  
          if (!wallet_paths.insert(wallet_path).second) {
              return InitError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
          }
  
          std::string error_string;
          std::string warning_string;
          bool verify_success = CWallet::Verify(wallet_file, salvage_wallet, error_string, warning_string);
          if (!error_string.empty()) InitError(error_string);
          if (!warning_string.empty()) InitWarning(warning_string);
          if (!verify_success) return false;
      }
  
      return true;
  }
  
  bool WalletInit::Open() const
  {
      if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
          LogPrintf("Wallet disabled!\n");
          return true;
      }
  
      for (const std::string& walletFile : gArgs.GetArgs("-wallet")) {
          std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(walletFile, fs::absolute(walletFile, GetWalletDir()));
          if (!pwallet) {
              return false;
          }
          AddWallet(pwallet);
      }
  
      return true;
  }
  
  void WalletInit::Start(CScheduler& scheduler) const
  {
      for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
          pwallet->postInitProcess();
      }
  
      // Run a thread to flush wallet periodically
      scheduler.scheduleEvery(MaybeCompactWalletDB, 500);
  }
  
  void WalletInit::Flush() const
  {
      for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
          pwallet->Flush(false);
      }
  }
  
  void WalletInit::Stop() const
  {
      for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
          pwallet->Flush(true);
      }
  }
  
  void WalletInit::Close() const
  {
      for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
          RemoveWallet(pwallet);
      }
  }
  