text: // Copyright (c) 2009-2010 Satoshi Nakamoto
  // Copyright (c) 2009-2020 The Finalcoin Core developers
  // Distributed under the MIT software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #include <outputtype.h>
  
  #include <pubkey.h>
  #include <script/script.h>
  #include <script/sign.h>
  #include <script/signingprovider.h>
  #include <script/standard.h>
  #include <util/vector.h>
  
  #include <assert.h>
  #include <optional>
  #include <string>
  
  static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
  static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit";
  static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32";
  static const std::string OUTPUT_TYPE_STRING_BECH32M = "bech32m";
  
  std::optional<OutputType> ParseOutputType(const std::string& type)
  {
      if (type == OUTPUT_TYPE_STRING_LEGACY) {
          return OutputType::LEGACY;
      } else if (type == OUTPUT_TYPE_STRING_P2SH_SEGWIT) {
          return OutputType::P2SH_SEGWIT;
      } else if (type == OUTPUT_TYPE_STRING_BECH32) {
          return OutputType::BECH32;
      } else if (type == OUTPUT_TYPE_STRING_BECH32M) {
          return OutputType::BECH32M;
      }
      return std::nullopt;
  }
  
  const std::string& FormatOutputType(OutputType type)
  {
      switch (type) {
      case OutputType::LEGACY: return OUTPUT_TYPE_STRING_LEGACY;
      case OutputType::P2SH_SEGWIT: return OUTPUT_TYPE_STRING_P2SH_SEGWIT;
      case OutputType::BECH32: return OUTPUT_TYPE_STRING_BECH32;
      case OutputType::BECH32M: return OUTPUT_TYPE_STRING_BECH32M;
      } // no default case, so the compiler can warn about missing cases
      assert(false);
  }
  
  CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
  {
      switch (type) {
      case OutputType::LEGACY: return PKHash(key);
      case OutputType::P2SH_SEGWIT:
      case OutputType::BECH32: {
          if (!key.IsCompressed()) return PKHash(key);
          CTxDestination witdest = WitnessV0KeyHash(key);
          CScript witprog = GetScriptForDestination(witdest);
          if (type == OutputType::P2SH_SEGWIT) {
              return ScriptHash(witprog);
          } else {
              return witdest;
          }
      }
      case OutputType::BECH32M: {} // This function should never be used with BECH32M, so let it assert
      } // no default case, so the compiler can warn about missing cases
      assert(false);
  }
  
  std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
  {
      PKHash keyid(key);
      CTxDestination p2pkh{keyid};
      if (key.IsCompressed()) {
          CTxDestination segwit = WitnessV0KeyHash(keyid);
          CTxDestination p2sh = ScriptHash(GetScriptForDestination(segwit));
          return Vector(std::move(p2pkh), std::move(p2sh), std::move(segwit));
      } else {
          return Vector(std::move(p2pkh));
      }
  }
  
  CTxDestination AddAndGetDestinationForScript(FillableSigningProvider& keystore, const CScript& script, OutputType type)
  {
      // Add script to keystore
      keystore.AddCScript(script);
      // Note that scripts over 520 bytes are not yet supported.
      switch (type) {
      case OutputType::LEGACY:
          return ScriptHash(script);
      case OutputType::P2SH_SEGWIT:
      case OutputType::BECH32: {
          CTxDestination witdest = WitnessV0ScriptHash(script);
          CScript witprog = GetScriptForDestination(witdest);
          // Check if the resulting program is solvable (i.e. doesn't use an uncompressed key)
          if (!IsSolvable(keystore, witprog)) return ScriptHash(script);
          // Add the redeemscript, so that P2WSH and P2SH-P2WSH outputs are recognized as ours.
          keystore.AddCScript(witprog);
          if (type == OutputType::BECH32) {
              return witdest;
          } else {
              return ScriptHash(witprog);
          }
      }
      case OutputType::BECH32M: {} // This function should not be used for BECH32M, so let it assert
      } // no default case, so the compiler can warn about missing cases
      assert(false);
  }
  
  std::optional<OutputType> OutputTypeFromDestination(const CTxDestination& dest) {
      if (std::holds_alternative<PKHash>(dest) ||
          std::holds_alternative<ScriptHash>(dest)) {
          return OutputType::LEGACY;
      }
      if (std::holds_alternative<WitnessV0KeyHash>(dest) ||
          std::holds_alternative<WitnessV0ScriptHash>(dest)) {
          return OutputType::BECH32;
      }
      if (std::holds_alternative<WitnessV1Taproot>(dest) ||
          std::holds_alternative<WitnessUnknown>(dest)) {
          return OutputType::BECH32M;
      }
      return std::nullopt;
  }
  