text: // Copyright (c) 2017-2019 The dogecash developers
  // Distributed under the MIT software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #include "amount.h"
  #include "chainparams.h"
  #include "coincontrol.h"
  #include "denomination_functions.h"
  #include "main.h"
  #include "txdb.h"
  #include "wallet/wallet.h"
  #include "wallet/walletdb.h"
  #include <boost/test/unit_test.hpp>
  #include <iostream>
  
  using namespace libzerocoin;
  
  BOOST_AUTO_TEST_SUITE(zerocoin_denom_tests)
  
  
  //translation from dogecash quantity to zerocoin denomination
  BOOST_AUTO_TEST_CASE(amount_to_denomination_test)
  {
      cout << "Running amount_to_denomination_test...\n";
  
      //valid amount (min edge)
      CAmount amount = 1 * COIN;
      BOOST_CHECK_MESSAGE(AmountToZerocoinDenomination(amount) == ZQ_ONE, "For COIN denomination should be ZQ_ONE");
  
      //valid amount (max edge)
      CAmount amount1 = 5000 * COIN;
      BOOST_CHECK_MESSAGE(AmountToZerocoinDenomination(amount1) == ZQ_FIVE_THOUSAND, "For 5000*COIN denomination should be ZQ_ONE");
  
      //invalid amount (too much)
      CAmount amount2 = 7000 * COIN;
      BOOST_CHECK_MESSAGE(AmountToZerocoinDenomination(amount2) == ZQ_ERROR, "For 7000*COIN denomination should be Invalid -> ZQ_ERROR");
  
      //invalid amount (not enough)
      CAmount amount3 = 1;
      BOOST_CHECK_MESSAGE(AmountToZerocoinDenomination(amount3) == ZQ_ERROR, "For 1 denomination should be Invalid -> ZQ_ERROR");
  }
  
  BOOST_AUTO_TEST_CASE(denomination_to_value_test)
  {
      cout << "Running ZerocoinDenominationToValue_test...\n";
  
      int64_t Value = 1 * COIN;
      CoinDenomination denomination = ZQ_ONE;
      BOOST_CHECK_MESSAGE(ZerocoinDenominationToAmount(denomination) == Value, "Wrong Value - should be 1");
  
      Value = 10 * COIN;
      denomination = ZQ_TEN;
      BOOST_CHECK_MESSAGE(ZerocoinDenominationToAmount(denomination) == Value, "Wrong Value - should be 10");
  
      Value = 50 * COIN;
      denomination = ZQ_FIFTY;
      BOOST_CHECK_MESSAGE(ZerocoinDenominationToAmount(denomination) == Value, "Wrong Value - should be 50");
  
      Value = 500 * COIN;
      denomination = ZQ_FIVE_HUNDRED;
      BOOST_CHECK_MESSAGE(ZerocoinDenominationToAmount(denomination) == Value, "Wrong Value - should be 500");
  
      Value = 100 * COIN;
      denomination = ZQ_ONE_HUNDRED;
      BOOST_CHECK_MESSAGE(ZerocoinDenominationToAmount(denomination) == Value, "Wrong Value - should be 100");
  
      Value = 0 * COIN;
      denomination = ZQ_ERROR;
      BOOST_CHECK_MESSAGE(ZerocoinDenominationToAmount(denomination) == Value, "Wrong Value - should be 0");
  }
  
  BOOST_AUTO_TEST_CASE(zerocoin_spend_test241)
  {
      const int nMaxNumberOfSpends = 4;
      const bool fMinimizeChange = false;
      const int DenomAmounts[] = {1, 2, 3, 4, 0, 0, 0, 0};
      CAmount nSelectedValue;
      std::list<CMintMeta> listMints;
      std::map<CoinDenomination, CAmount> mapDenom;
  
      int j = 0;
      CAmount nTotalAmount = 0;
      int CoinsHeld = 0;
  
      // Create a set of Minted coins that fits profile given by DenomAmounts
      // Also setup Map array corresponding to DenomAmount which is the current set of coins available
  
      for (const auto& denom : zerocoinDenomList) {
          for (int i = 0; i < DenomAmounts[j]; i++) {
              CAmount currentAmount = ZerocoinDenominationToAmount(denom);
              nTotalAmount += currentAmount;
              CBigNum value;
              CBigNum rand;
              CBigNum serial = CBigNum::RandKBitBigum(256);
              bool isUsed = false;
              CMintMeta meta;
              meta.denom = denom;
              meta.hashPubcoin = GetPubCoinHash(value);
              meta.hashSerial = GetSerialHash(serial);
              meta.isUsed = isUsed;
              meta.nVersion = 1;
              listMints.push_back(meta);
          }
          mapDenom.insert(std::pair<CoinDenomination, CAmount>(denom, DenomAmounts[j]));
          j++;
      }
      CoinsHeld = nTotalAmount / COIN;
      std::cout << "Curremt Amount held = " << CoinsHeld << ": ";
  
      // Show what we have
      j = 0;
      for (const auto& denom : zerocoinDenomList)
          std::cout << DenomAmounts[j++] << "*" << ZerocoinDenominationToAmount(denom) / COIN << " + ";
      std::cout << "\n";
  
      // For DenomAmounts[] = {1,2,3,4,0,0,0,0}; we can spend up to 200 without requiring more than 4 Spends
      // Amounts above this can not be met
      CAmount MaxLimit = 200;
      CAmount OneCoinAmount = ZerocoinDenominationToAmount(ZQ_ONE);
      CAmount nValueTarget = OneCoinAmount;
      int nCoinsReturned;
      int nNeededSpends = 0;  // Number of spends which would be needed if selection failed
  
      bool fDebug = 0;
  
      // Go through all possible spend between 1 and 241 and see if it's possible or not
      for (int i = 0; i < CoinsHeld; i++) {
          std::vector<CMintMeta> vSpends = SelectMintsFromList(nValueTarget, nSelectedValue,
                                                                   nMaxNumberOfSpends,
                                                                   fMinimizeChange,
                                                                   nCoinsReturned,
                                                                   listMints,
                                                                   mapDenom,
                                                                   nNeededSpends);
  
          if (fDebug) {
              if (vSpends.size() > 0) {
                  std::cout << "SUCCESS : Coins = " << nValueTarget / COIN << " # spends used = " << vSpends.size()
                  << " # of coins returned = " << nCoinsReturned
                            << " Spend Amount = " << nSelectedValue / COIN << " Held = " << CoinsHeld << "\n";
              } else {
                  std::cout << "FAILED : Coins = " << nValueTarget / COIN << " Held = " << CoinsHeld << "\n";
              }
          }
  
          if (i < MaxLimit) {
              BOOST_CHECK_MESSAGE(vSpends.size() < 5, "Too many spends");
              BOOST_CHECK_MESSAGE(vSpends.size() > 0, "No spends");
          } else {
              bool spends_not_ok = ((vSpends.size() >= 4) || (vSpends.size() == 0));
              BOOST_CHECK_MESSAGE(spends_not_ok, "Expected to fail but didn't");
          }
          nValueTarget += OneCoinAmount;
      }
      //std::cout << "241 Test done!\n";
  }
  BOOST_AUTO_TEST_CASE(zerocoin_spend_test115)
  {
      const int nMaxNumberOfSpends = 4;
      const bool fMinimizeChange = false;
      const int DenomAmounts[] = {0, 1, 1, 2, 0, 0, 0, 0};
      CAmount nSelectedValue;
      std::list<CMintMeta> listMints;
      std::map<CoinDenomination, CAmount> mapDenom;
  
      int j = 0;
      CAmount nTotalAmount = 0;
      int CoinsHeld = 0;
  
      // Create a set of Minted coins that fits profile given by DenomAmounts
      // Also setup Map array corresponding to DenomAmount which is the current set of coins available
      for (const auto& denom : zerocoinDenomList) {
          for (int i = 0; i < DenomAmounts[j]; i++) {
              CAmount currentAmount = ZerocoinDenominationToAmount(denom);
              nTotalAmount += currentAmount;
              CBigNum value;
              CBigNum rand;
              CBigNum serial = CBigNum::RandKBitBigum(256);
              bool isUsed = false;
              CMintMeta meta;
              meta.denom = denom;
              meta.hashPubcoin = GetPubCoinHash(value);
              meta.hashSerial = GetSerialHash(serial);
              meta.isUsed = isUsed;
              meta.nVersion = 1;
              listMints.push_back(meta);
          }
          mapDenom.insert(std::pair<CoinDenomination, CAmount>(denom, DenomAmounts[j]));
          j++;
      }
      CoinsHeld = nTotalAmount / COIN;
      std::cout << "Curremt Amount held = " << CoinsHeld << ": ";
  
      // Show what we have
      j = 0;
      for (const auto& denom : zerocoinDenomList)
          std::cout << DenomAmounts[j++] << "*" << ZerocoinDenominationToAmount(denom) / COIN << " + ";
      std::cout << "\n";
  
      CAmount OneCoinAmount = ZerocoinDenominationToAmount(ZQ_ONE);
      CAmount nValueTarget = OneCoinAmount;
  
      //bool fDebug = 0;
      int nCoinsReturned;
      int nNeededSpends = 0;  // Number of spends which would be needed if selection failed
  
      std::vector<CMintMeta> vSpends = SelectMintsFromList(nValueTarget, nSelectedValue,
                                                               nMaxNumberOfSpends,
                                                               fMinimizeChange,
                                                               nCoinsReturned,
                                                               listMints,
                                                               mapDenom,
                                                               nNeededSpends);
  
  //    if (fDebug) {
  //        if (vSpends.size() > 0) {
  //            std::cout << "SUCCESS : Coins = " << nValueTarget / COIN << " # spends used = " << vSpends.size()
  //            << " # of coins returned = " << nCoinsReturned
  //                      << " Spend Amount = " << nSelectedValue / COIN << " Held = " << CoinsHeld << "\n";
  //        } else {
  //            std::cout << "FAILED : Coins = " << nValueTarget / COIN << " Held = " << CoinsHeld << "\n";
  //        }
  //    }
  
      BOOST_CHECK_MESSAGE(vSpends.size() < 5, "Too many spends");
      BOOST_CHECK_MESSAGE(vSpends.size() > 0, "No spends");
      nValueTarget += OneCoinAmount;
  }
  BOOST_AUTO_TEST_CASE(zerocoin_spend_test_from_245)
  {
      const int nMaxNumberOfSpends = 5;
      // For 36:
      // const int nSpendValue = 36;
      // Here have a 50 so use for 36 since can't meet exact amount
      //    const int DenomAmounts[] = {0,1,4,1,0,0,0,0};
      // Here have 45 so use 4*10 for 36 since can't meet exact amount
      //    const int DenomAmounts[] = {0, 1, 4, 0, 0, 0, 0, 0};
      // For 51
      //const int nSpendValue = 51;
  
      // CoinsHeld = 245
      const int DenomAmounts[] = {0, 1, 4, 2, 1, 0, 0, 0};
      // We can spend up to this amount for above set for less 6 spends
      // Otherwise, 6 spends are required
      const int nMaxSpendAmount = 220;
      CAmount nSelectedValue;
      std::list<CMintMeta> listMints;
      std::map<CoinDenomination, CAmount> mapOfDenomsHeld;
  
      int j = 0;
      CAmount nTotalAmount = 0;
      int CoinsHeld = 0;
  
      // Create a set of Minted coins that fits profile given by DenomAmounts
      // Also setup Map array corresponding to DenomAmount which is the current set of coins available
      for (const auto& denom : zerocoinDenomList) {
          for (int i = 0; i < DenomAmounts[j]; i++) {
              CAmount currentAmount = ZerocoinDenominationToAmount(denom);
              nTotalAmount += currentAmount;
              CBigNum value;
              CBigNum rand;
              CBigNum serial = CBigNum::RandKBitBigum(256);
              bool isUsed = false;
              CMintMeta meta;
              meta.denom = denom;
              meta.hashPubcoin = GetPubCoinHash(value);
              meta.hashSerial = GetSerialHash(serial);
              meta.isUsed = isUsed;
              meta.nVersion = 1;
              listMints.push_back(meta);
          }
          mapOfDenomsHeld.insert(std::pair<CoinDenomination, CAmount>(denom, DenomAmounts[j]));
          j++;
      }
      CoinsHeld = nTotalAmount / COIN;
      std::cout << "Curremt Amount held = " << CoinsHeld << ": ";
  
      // Show what we have
      j = 0;
      for (const auto& denom : zerocoinDenomList)
          std::cout << DenomAmounts[j++] << "*" << ZerocoinDenominationToAmount(denom) / COIN << " + ";
      std::cout << "\n";
  
      CAmount OneCoinAmount = ZerocoinDenominationToAmount(ZQ_ONE);
      CAmount nValueTarget = OneCoinAmount;
  
    //  bool fDebug = 0;
      int nCoinsReturned;
      int nNeededSpends = 0;  // Number of spends which would be needed if selection failed
  
      // Go through all possible spend between 1 and 241 and see if it's possible or not
      for (int i = 0; i < CoinsHeld; i++) {
          std::vector<CMintMeta> vSpends = SelectMintsFromList(nValueTarget, nSelectedValue,
                                                                   nMaxNumberOfSpends,
                                                                   false,
                                                                   nCoinsReturned,
                                                                   listMints,
                                                                   mapOfDenomsHeld,
                                                                   nNeededSpends);
  
  //        if (fDebug) {
  //            if (vSpends.size() > 0) {
  //                std::cout << "SUCCESS : Coins = " << nValueTarget / COIN << " # spends = " << vSpends.size()
  //                          << " # coins returned = " << nCoinsReturned
  //                          << " Amount = " << nSelectedValue / COIN << " Held = " << CoinsHeld << " ";
  //            } else {
  //                std::cout << "UNABLE TO SPEND : Coins = " << nValueTarget / COIN << " Held = " << CoinsHeld << "\n";
  //            }
  //        }
  
          bool spends_not_ok = ((vSpends.size() > nMaxNumberOfSpends) || (vSpends.size() == 0));
          if (i < nMaxSpendAmount) BOOST_CHECK_MESSAGE(!spends_not_ok, "Too many spends");
          else BOOST_CHECK_MESSAGE(spends_not_ok, "Expected to fail but didn't");
  
          std::vector<CMintMeta> vSpendsAlt = SelectMintsFromList(nValueTarget, nSelectedValue,
                                                                      nMaxNumberOfSpends,
                                                                      true,
                                                                      nCoinsReturned,
                                                                      listMints,
                                                                      mapOfDenomsHeld,
                                                                      nNeededSpends);
  
  
  //        if (fDebug) {
  //            if (vSpendsAlt.size() > 0) {
  //                std::cout << "# spends = " << vSpendsAlt.size()
  //                          << " # coins returned = " << nCoinsReturned
  //                          << " Amount = " << nSelectedValue / COIN << "\n";
  //            } else {
  //                std::cout << "UNABLE TO SPEND : Coins = " << nValueTarget / COIN << " Held = " << CoinsHeld << "\n";
  //            }
  //        }
  
          spends_not_ok = ((vSpendsAlt.size() > nMaxNumberOfSpends) || (vSpendsAlt.size() == 0));
          if (i < nMaxSpendAmount) BOOST_CHECK_MESSAGE(!spends_not_ok, "Too many spends");
          else BOOST_CHECK_MESSAGE(spends_not_ok, "Expected to fail but didn't");
  
          nValueTarget += OneCoinAmount;
      }
  }
  
  
  BOOST_AUTO_TEST_CASE(zerocoin_spend_test_from_145)
  {
      const int nMaxNumberOfSpends = 5;
      // CoinsHeld = 145
      const int DenomAmounts[] = {0, 1, 4, 2, 0, 0, 0, 0};
      CAmount nSelectedValue;
      std::list<CMintMeta> listMints;
      std::map<CoinDenomination, CAmount> mapOfDenomsHeld;
  
      int j = 0;
      CAmount nTotalAmount = 0;
      int CoinsHeld = 0;
  
      // Create a set of Minted coins that fits profile given by DenomAmounts
      // Also setup Map array corresponding to DenomAmount which is the current set of coins available
      for (const auto& denom : zerocoinDenomList) {
          for (int i = 0; i < DenomAmounts[j]; i++) {
              CAmount currentAmount = ZerocoinDenominationToAmount(denom);
              nTotalAmount += currentAmount;
              CBigNum value;
              CBigNum rand;
              CBigNum serial = CBigNum::RandKBitBigum(256);
              bool isUsed = false;
              CMintMeta meta;
              meta.denom = denom;
              meta.hashPubcoin = GetPubCoinHash(value);
              meta.hashSerial = GetSerialHash(serial);
              meta.isUsed = isUsed;
              meta.nVersion = 1;
              listMints.push_back(meta);
          }
          mapOfDenomsHeld.insert(std::pair<CoinDenomination, CAmount>(denom, DenomAmounts[j]));
          j++;
      }
      CoinsHeld = nTotalAmount / COIN;
      std::cout << "Curremt Amount held = " << CoinsHeld << ": ";
      // We can spend up to this amount for above set for less 6 spends
      // Otherwise, 6 spends are required
      const int nMaxSpendAmount = 130;
  
      // Show what we have
      j = 0;
      for (const auto& denom : zerocoinDenomList)
          std::cout << DenomAmounts[j++] << "*" << ZerocoinDenominationToAmount(denom) / COIN << " + ";
      std::cout << "\n";
  
      CAmount OneCoinAmount = ZerocoinDenominationToAmount(ZQ_ONE);
      CAmount nValueTarget = OneCoinAmount;
  
      //bool fDebug = 0;
      int nCoinsReturned;
      int nNeededSpends = 0;  // Number of spends which would be needed if selection failed
  
      // Go through all possible spend between 1 and 241 and see if it's possible or not
      for (int i = 0; i < CoinsHeld; i++) {
          std::vector<CMintMeta> vSpends = SelectMintsFromList(nValueTarget, nSelectedValue,
                                                                   nMaxNumberOfSpends,
                                                                   false,
                                                                   nCoinsReturned,
                                                                   listMints,
                                                                   mapOfDenomsHeld,
                                                                   nNeededSpends);
  
  //        if (fDebug) {
  //            if (vSpends.size() > 0) {
  //                std::cout << "SUCCESS : Coins = " << nValueTarget / COIN << " # spends = " << vSpends.size()
  //                          << " # coins returned = " << nCoinsReturned
  //                          << " Amount = " << nSelectedValue / COIN << " Held = " << CoinsHeld << " ";
  //            } else {
  //                std::cout << "UNABLE TO SPEND : Coins = " << nValueTarget / COIN << " Held = " << CoinsHeld << "\n";
  //            }
  //        }
  
          bool spends_not_ok = ((vSpends.size() > nMaxNumberOfSpends) || (vSpends.size() == 0));
          if (i < nMaxSpendAmount) BOOST_CHECK_MESSAGE(!spends_not_ok, "Too many spends");
          else BOOST_CHECK_MESSAGE(spends_not_ok, "Expected to fail but didn't");
  
          std::vector<CMintMeta> vSpendsAlt = SelectMintsFromList(nValueTarget, nSelectedValue,
                                                                      nMaxNumberOfSpends,
                                                                      true,
                                                                      nCoinsReturned,
                                                                      listMints,
                                                                      mapOfDenomsHeld,
                                                                      nNeededSpends);
  
  
  //        if (fDebug) {
  //            if (vSpendsAlt.size() > 0) {
  //                std::cout << "# spends = " << vSpendsAlt.size()
  //                          << " # coins returned = " << nCoinsReturned
  //                          << " Amount = " << nSelectedValue / COIN << "\n";
  //            } else {
  //                std::cout << "UNABLE TO SPEND : Coins = " << nValueTarget / COIN << " Held = " << CoinsHeld << "\n";
  //            }
  //        }
  
          spends_not_ok = ((vSpendsAlt.size() > nMaxNumberOfSpends) || (vSpendsAlt.size() == 0));
          if (i < nMaxSpendAmount) BOOST_CHECK_MESSAGE(!spends_not_ok, "Too many spends");
          else BOOST_CHECK_MESSAGE(spends_not_ok, "Expected to fail but didn't");
  
  
          nValueTarget += OneCoinAmount;
      }
  }
  
  
  BOOST_AUTO_TEST_CASE(zerocoin_spend_test99)
  {
      const int nMaxNumberOfSpends = 4;
      const bool fMinimizeChange = false;
      const int DenomAmounts[] = {0, 1, 4, 2, 1, 0, 0, 0};
      CAmount nSelectedValue;
      std::list<CMintMeta> listMints;
      std::map<CoinDenomination, CAmount> mapOfDenomsHeld;
  
      int j = 0;
      CAmount nTotalAmount = 0;
      int CoinsHeld = 0;
  
      // Create a set of Minted coins that fits profile given by DenomAmounts
      // Also setup Map array corresponding to DenomAmount which is the current set of coins available
      for (const auto& denom : zerocoinDenomList) {
          for (int i = 0; i < DenomAmounts[j]; i++) {
              CAmount currentAmount = ZerocoinDenominationToAmount(denom);
              nTotalAmount += currentAmount;
              CBigNum value;
              CBigNum rand;
              CBigNum serial = CBigNum::RandKBitBigum(256);
              bool isUsed = false;
              CMintMeta meta;
              meta.denom = denom;
              meta.hashPubcoin = GetPubCoinHash(value);
              meta.hashSerial = GetSerialHash(serial);
              meta.isUsed = isUsed;
              meta.nVersion = 1;
              listMints.push_back(meta);
          }
          mapOfDenomsHeld.insert(std::pair<CoinDenomination, CAmount>(denom, DenomAmounts[j]));
          j++;
      }
      CoinsHeld = nTotalAmount / COIN;
      std::cout << "Curremt Amount held = " << CoinsHeld << ": ";
  
      // Show what we have
      j = 0;
      for (const auto& denom : zerocoinDenomList)
          std::cout << DenomAmounts[j++] << "*" << ZerocoinDenominationToAmount(denom) / COIN << " + ";
      std::cout << "\n";
  
      CAmount OneCoinAmount = ZerocoinDenominationToAmount(ZQ_ONE);
      CAmount nValueTarget = 99 * OneCoinAmount;
  
  //    bool fDebug = 0;
      int nCoinsReturned;
      int nNeededSpends = 0;  // Number of spends which would be needed if selection failed
  
      std::vector<CMintMeta> vSpends = SelectMintsFromList(nValueTarget, nSelectedValue,
                                                               nMaxNumberOfSpends,
                                                               fMinimizeChange,
                                                               nCoinsReturned,
                                                              listMints,
                                                               mapOfDenomsHeld,
                                                               nNeededSpends);
  
  //    if (fDebug) {
  //        if (vSpends.size() > 0) {
  //            std::cout << "SUCCESS : Coins = " << nValueTarget / COIN << " # spends used = " << vSpends.size()
  //            << " # of coins returned = " << nCoinsReturned
  //                      << " Spend Amount = " << nSelectedValue / COIN << " Held = " << CoinsHeld << "\n";
  //        } else {
  //            std::cout << "FAILED : Coins = " << nValueTarget / COIN << " Held = " << CoinsHeld << "\n";
  //        }
  //    }
  
      BOOST_CHECK_MESSAGE(vSpends.size() < 5, "Too many spends");
      BOOST_CHECK_MESSAGE(vSpends.size() > 0, "No spends");
      nValueTarget += OneCoinAmount;
  }
  
  BOOST_AUTO_TEST_SUITE_END()
  