diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index a7affffa67..b567badcce 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -189,6 +189,13 @@ + + + + Enable &splitting when minting + + + diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 6017558d4f..c7fb7d3df6 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -188,6 +188,7 @@ void OptionsDialog::setMapper() /* Lelantus */ mapper->addMapping(ui->autoAnonymize, OptionsModel::AutoAnonymize); + mapper->addMapping(ui->fSplit, OptionsModel::Split); if (!lelantus::IsLelantusAllowed()) { ui->lelantusPage->setVisible(false); } diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 74d4ea6f03..8569b0cb81 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -85,6 +85,10 @@ void OptionsModel::Init(bool resetSettings) settings.setValue("fAutoAnonymize", false); fAutoAnonymize = settings.value("fAutoAnonymize", false).toBool(); + if (!settings.contains("fSplit")) + settings.setValue("fSplit", true); + fSplit = settings.value("fSplit", true).toBool(); + if (!settings.contains("fLelantusPage")) settings.setValue("fLelantusPage", false); fLelantusPage = settings.value("fLelantusPage", false).toBool(); @@ -279,6 +283,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return fCoinControlFeatures; case AutoAnonymize: return fAutoAnonymize; + case Split: + return fSplit; case LelantusPage: return fLelantusPage; case DatabaseCache: @@ -429,6 +435,10 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in settings.setValue("fAutoAnonymize", fAutoAnonymize); Q_EMIT autoAnonymizeChanged(fAutoAnonymize); break; + case Split: + fSplit = value.toBool(); + settings.setValue("fSplit", fSplit); + break; case LelantusPage: fLelantusPage = value.toBool(); settings.setValue("fLelantusPage", fLelantusPage); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 1ff155b86d..7d22dd2494 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -49,6 +49,7 @@ class OptionsModel : public QAbstractListModel Listen, // bool TorSetup, // bool AutoAnonymize, // bool + Split, // bool LelantusPage, // bool enableRapAddresses, // bool OptionIDRowCount, @@ -73,6 +74,7 @@ class OptionsModel : public QAbstractListModel bool getRapAddresses() { return fenableRapAddresses; } const QString& getOverriddenByCommandLine() { return strOverriddenByCommandLine; } bool getAutoAnonymize() { return fAutoAnonymize; } + bool getfSplit() { return fSplit; } bool getLelantusPage() {return fLelantusPage; } /* Restart flag helper */ @@ -89,6 +91,7 @@ class OptionsModel : public QAbstractListModel QString strThirdPartyTxUrls; bool fCoinControlFeatures; bool fAutoAnonymize; + bool fSplit; bool fLelantusPage; bool fenableRapAddresses; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index c625cf4b77..470675f6ef 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -1407,7 +1407,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareMintSparkTransaction(std::vecto int nChangePosRet = -1; std::string strFailReason; - bool fCreated = wallet->CreateSparkMintTransactions(outputs, wtxAndFees, nFeeRequired, reservekeys, nChangePosRet, fSubtractFeeFromAmount, strFailReason, coinControl, false); + bool fCreated = wallet->CreateSparkMintTransactions(outputs, wtxAndFees, nFeeRequired, reservekeys, nChangePosRet, fSubtractFeeFromAmount, strFailReason, optionsModel->getfSplit(), coinControl, false); transactions.clear(); transactions.reserve(wtxAndFees.size()); for (auto &wtxAndFee : wtxAndFees) { diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 9fdf6e2eb3..3c2100b2c1 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -199,6 +199,8 @@ static const CRPCConvertParam vRPCConvertParams[] = //Lelantus { "mintspark", 0 }, + { "mintspark", 1 }, + { "mintspark", 2 }, { "spendspark", 0 }, { "spendspark", 1 }, diff --git a/src/spark/sparkwallet.cpp b/src/spark/sparkwallet.cpp index ed296578fd..325868f189 100644 --- a/src/spark/sparkwallet.cpp +++ b/src/spark/sparkwallet.cpp @@ -762,6 +762,7 @@ bool CSparkWallet::CreateSparkMintTransactions( int& nChangePosInOut, bool subtractFeeFromAmount, std::string& strFailReason, + bool fSplit, const CCoinControl *coinControl, bool autoMintAll) { @@ -790,10 +791,17 @@ bool CSparkWallet::CreateSparkMintTransactions( std::list cacheWtxs; // vector pairs for each transparent address std::vector>> valueAndUTXO; - pwalletMain->AvailableCoinsForLMint(valueAndUTXO, coinControl); - - Shuffle(valueAndUTXO.begin(), valueAndUTXO.end(), FastRandomContext()); - + if (fSplit) { + pwalletMain->AvailableCoinsForLMint(valueAndUTXO, coinControl); + Shuffle(valueAndUTXO.begin(), valueAndUTXO.end(), FastRandomContext()); + } else { + std::vector vAvailableCoins; + pwalletMain->AvailableCoins(vAvailableCoins, true, coinControl); + CAmount balance = 0; + for (auto& coin : vAvailableCoins) + balance += coin.tx->tx->vout[coin.i].nValue; + valueAndUTXO.emplace_back(std::make_pair(balance, vAvailableCoins)); + } while (!valueAndUTXO.empty()) { // initialize diff --git a/src/spark/sparkwallet.h b/src/spark/sparkwallet.h index 8f707a5f94..a300f904a3 100644 --- a/src/spark/sparkwallet.h +++ b/src/spark/sparkwallet.h @@ -122,6 +122,7 @@ class CSparkWallet { int& nChangePosInOut, bool subtractFeeFromAmount, std::string& strFailReason, + bool fSplit, const CCoinControl *coinControl, bool autoMintAll = false); diff --git a/src/test/evospork_tests.cpp b/src/test/evospork_tests.cpp index fb01ac97bc..e2c71c0053 100644 --- a/src/test/evospork_tests.cpp +++ b/src/test/evospork_tests.cpp @@ -604,7 +604,7 @@ BOOST_AUTO_TEST_CASE(limit) for (int i=0; i<10; i++) { std::vector> wtxAndFee; std::vector mints{{address, 50*COIN, ""}}; - std::string error = pwalletMain->MintAndStoreSpark(mints, wtxAndFee, false); + std::string error = pwalletMain->MintAndStoreSpark(mints, wtxAndFee, false, true); BOOST_ASSERT(error.empty()); for (auto &w: wtxAndFee) sparkMints.emplace_back(*w.first.tx); diff --git a/src/test/fixtures.cpp b/src/test/fixtures.cpp index 65b9297a43..50f4d30a75 100644 --- a/src/test/fixtures.cpp +++ b/src/test/fixtures.cpp @@ -369,7 +369,7 @@ std::vector SparkTestingSetup::GenerateMints( data.address = address; outputs.push_back(data); - auto result = pwalletMain->MintAndStoreSpark(outputs, wtxAndFee, false); + auto result = pwalletMain->MintAndStoreSpark(outputs, wtxAndFee, false, true); if (result != "") { throw std::runtime_error(_("Fail to generate mints, ") + result); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 9ed20ecbd8..a171740f08 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -31,6 +31,7 @@ #include "lelantusjoinsplitbuilder.h" #include "bip47/paymentchannel.h" #include "bip47/account.h" +#include "wallet/coincontrol.h" #include @@ -3653,7 +3654,7 @@ UniValue mintspark(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() == 0 || request.params.size() > 2) + if (request.fHelp || request.params.size() == 0 || request.params.size() > 3) throw std::runtime_error( "mintspark {\"address\":{amount,memo...}}\n" + HelpRequiringPassphrase(pwallet) + "\n" @@ -3669,6 +3670,8 @@ UniValue mintspark(const JSONRPCRequest& request) "\nExamples:\n" "\nSend two amounts to two different spark addresses:\n" + HelpExampleCli("mintspark", "\"{\\\"sr1xtw3yd6v4ghgz873exv2r5nzfwryufxjzzz4xr48gl4jmh7fxml4568xr0nsdd7s4l5as2h50gakzjqrqpm7yrecne8ut8ylxzygj8klttsgm37tna4jk06acl2azph0dq4yxdqqgwa60\\\":{\\\"amount\\\":0.01, \\\"memo\\\":\\\"test_memo\\\"},\\\"sr1x7gcqdy670l2v4p9h2m4n5zgzde9y6ht86egffa0qrq40c6z329yfgvu8vyf99tgvnq4hwshvfxxhfzuyvz8dr3lt32j70x8l34japg73ca4w6z9x7c7ryd2gnafg9eg3gpr90gtunraw\\\":{\\\"amount\\\":0.01, \\\"memo\\\":\\\"\\\"}}\"") + + "\nSend two amounts to two different spark addresses, setting subtractFeeFromAmount flag and giving fromAddress array:\n" + + HelpExampleCli("mintspark", "\"{\\\"sr1xtw3yd6v4ghgz873exv2r5nzfwryufxjzzz4xr48gl4jmh7fxml4568xr0nsdd7s4l5as2h50gakzjqrqpm7yrecne8ut8ylxzygj8klttsgm37tna4jk06acl2azph0dq4yxdqqgwa60\\\":{\\\"amount\\\":0.01, \\\"memo\\\":\\\"test_memo\\\"},\\\"sr1x7gcqdy670l2v4p9h2m4n5zgzde9y6ht86egffa0qrq40c6z329yfgvu8vyf99tgvnq4hwshvfxxhfzuyvz8dr3lt32j70x8l34japg73ca4w6z9x7c7ryd2gnafg9eg3gpr90gtunraw\\\":{\\\"amount\\\":0.01, \\\"memo\\\":\\\"\\\"}}\" true [\\\"THhFWpJTDNyo6vL75kpob7UWVfwwp8t6kD\\\"]") + "\nSend two amounts to two different spark addresses setting memo:\n" + HelpExampleRpc("mintspark", "\"{\"sr1xtw3yd6v4ghgz873exv2r5nzfwryufxjzzz4xr48gl4jmh7fxml4568xr0nsdd7s4l5as2h50gakzjqrqpm7yrecne8ut8ylxzygj8klttsgm37tna4jk06acl2azph0dq4yxdqqgwa60\":{\"amount\":1},\\\"sr1x7gcqdy670l2v4p9h2m4n5zgzde9y6ht86egffa0qrq40c6z329yfgvu8vyf99tgvnq4hwshvfxxhfzuyvz8dr3lt32j70x8l34japg73ca4w6z9x7c7ryd2gnafg9eg3gpr90gtunraw\":{\"amount\":0.01, \"memo\":\"test_memo2\"}}\"") ); @@ -3725,8 +3728,31 @@ UniValue mintspark(const JSONRPCRequest& request) bool subtractFeeFromAmount = false; if (request.params.size() > 1) subtractFeeFromAmount = request.params[1].get_bool(); + + CCoinControl coinControl; + if (request.params.size() > 2) { + UniValue sendFrom = request.params[2].get_array(); + std::vector vAvailableCoins; + pwalletMain->AvailableCoins(vAvailableCoins, true); + for (unsigned int idx = 0; idx < sendFrom.size(); idx++) + { + std::string name_ =sendFrom[idx].get_str(); + CBitcoinAddress address(name_); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Firo address: ") + name_); + CScript scriptPubKey = GetScriptForDestination(address.Get()); + for (const auto& itr : vAvailableCoins) { + if (itr.tx->tx->vout[itr.i].scriptPubKey != scriptPubKey) { + continue; + } + coinControl.Select(COutPoint(itr.tx->GetHash(), itr.i)); + } + } + + } + std::vector> wtxAndFee; - std::string strError = pwallet->MintAndStoreSpark(outputs, wtxAndFee, subtractFeeFromAmount); + std::string strError = pwallet->MintAndStoreSpark(outputs, wtxAndFee, subtractFeeFromAmount, false, false, false, &coinControl); if (strError != "") throw JSONRPCError(RPC_WALLET_ERROR, strError); @@ -3761,7 +3787,7 @@ UniValue automintspark(const JSONRPCRequest& request) { std::vector> wtxAndFee; std::vector outputs; - std::string strError = pwallet->MintAndStoreSpark(outputs, wtxAndFee, true, true); + std::string strError = pwallet->MintAndStoreSpark(outputs, wtxAndFee, true, true, true); if (strError != "") throw JSONRPCError(RPC_WALLET_ERROR, strError); @@ -5775,7 +5801,7 @@ static const CRPCCommand commands[] = { "wallet", "getsparkaddressbalance", &getsparkaddressbalance, false }, { "wallet", "resetsparkmints", &resetsparkmints, false }, { "wallet", "setsparkmintstatus", &setsparkmintstatus, false }, - { "wallet", "mintspark", &mintspark, false }, + { "wallet", "mintspark", &mintspark, true }, { "wallet", "automintspark", &automintspark, false }, { "wallet", "spendspark", &spendspark, false }, { "wallet", "lelantustospark", &lelantustospark, false }, diff --git a/src/wallet/test/spark_tests.cpp b/src/wallet/test/spark_tests.cpp index 7ed925fb2d..1d05c9e9df 100644 --- a/src/wallet/test/spark_tests.cpp +++ b/src/wallet/test/spark_tests.cpp @@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE(mint_and_store_spark) std::vector mintedCoins; mintedCoins.push_back(data); - std::string result = pwalletMain->MintAndStoreSpark(mintedCoins, wtxAndFee, false); + std::string result = pwalletMain->MintAndStoreSpark(mintedCoins, wtxAndFee, false, true); BOOST_CHECK_EQUAL(result, ""); size_t mintAmount = 0; @@ -119,7 +119,7 @@ BOOST_AUTO_TEST_CASE(mint_subtract_fee) std::vector mintedCoins; mintedCoins.push_back(data); - std::string result = pwalletMain->MintAndStoreSpark(mintedCoins, wtxAndFee, true); + std::string result = pwalletMain->MintAndStoreSpark(mintedCoins, wtxAndFee, true, true); BOOST_CHECK_EQUAL(result, ""); size_t mintAmount = 0; @@ -207,10 +207,10 @@ BOOST_AUTO_TEST_CASE(spend) mintedCoins.push_back(data); std::vector> wtxAndFee; - std::string result = pwalletMain->MintAndStoreSpark(mintedCoins, wtxAndFee, false); + std::string result = pwalletMain->MintAndStoreSpark(mintedCoins, wtxAndFee, false, true); std::vector> wtxAndFee2; - pwalletMain->MintAndStoreSpark(mintedCoins, wtxAndFee2, false); + pwalletMain->MintAndStoreSpark(mintedCoins, wtxAndFee2, false, true); BOOST_CHECK_EQUAL("", result); @@ -308,7 +308,7 @@ BOOST_AUTO_TEST_CASE(mintspark_and_mint_all) std::vector mintedCoins; mintedCoins.push_back(data); - auto result = pwalletMain->MintAndStoreSpark(mintedCoins, wtxAndFee, false); + auto result = pwalletMain->MintAndStoreSpark(mintedCoins, wtxAndFee, false, true); BOOST_CHECK_EQUAL("", result); BOOST_CHECK_EQUAL(1, wtxAndFee.size()); BOOST_CHECK_EQUAL(10 * COIN, countMintsInBalance(wtxAndFee)); @@ -319,7 +319,7 @@ BOOST_AUTO_TEST_CASE(mintspark_and_mint_all) mintedCoins.clear(); mintedCoins.push_back(data); - result = pwalletMain->MintAndStoreSpark(mintedCoins, wtxAndFee, false); + result = pwalletMain->MintAndStoreSpark(mintedCoins, wtxAndFee, false, true); BOOST_CHECK_EQUAL("", result); BOOST_CHECK_GT(wtxAndFee.size(), 1); BOOST_CHECK_EQUAL(600 * COIN, countMintsInBalance(wtxAndFee)); @@ -330,7 +330,7 @@ BOOST_AUTO_TEST_CASE(mintspark_and_mint_all) auto balance = getAvailableCoinsForMintBalance(); BOOST_CHECK_GT(balance, 0); - result = pwalletMain->MintAndStoreSpark({}, wtxAndFee, false, true); + result = pwalletMain->MintAndStoreSpark({}, wtxAndFee, false, true, true); BOOST_CHECK_EQUAL("", result); BOOST_CHECK_GT(balance, countMintsInBalance(wtxAndFee)); BOOST_CHECK_EQUAL(balance, countMintsInBalance(wtxAndFee, true)); @@ -344,7 +344,7 @@ BOOST_AUTO_TEST_CASE(mintspark_and_mint_all) balance = getAvailableCoinsForMintBalance(); BOOST_CHECK_GT(balance, 0); - result = pwalletMain->MintAndStoreSpark({ }, wtxAndFee, false, true); + result = pwalletMain->MintAndStoreSpark({ }, wtxAndFee, false, true, true); BOOST_CHECK_EQUAL("", result); BOOST_CHECK_GT(balance, countMintsInBalance(wtxAndFee)); BOOST_CHECK_EQUAL(balance, countMintsInBalance(wtxAndFee, true)); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ab14516dfe..0b8a0e7603 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -5661,6 +5661,7 @@ std::string CWallet::MintAndStoreSpark( const std::vector& outputs, std::vector>& wtxAndFee, bool subtractFeeFromAmount, + bool fSplit, bool autoMintAll, bool fAskFee, const CCoinControl *coinControl) { @@ -5687,7 +5688,7 @@ std::string CWallet::MintAndStoreSpark( int nChangePosRet = -1; std::list reservekeys; - if (!sparkWallet->CreateSparkMintTransactions(outputs, wtxAndFee, nFeeRequired, reservekeys, nChangePosRet, subtractFeeFromAmount, strError, coinControl, autoMintAll)) { + if (!sparkWallet->CreateSparkMintTransactions(outputs, wtxAndFee, nFeeRequired, reservekeys, nChangePosRet, subtractFeeFromAmount, strError, fSplit, coinControl, autoMintAll)) { return strError; } @@ -8224,10 +8225,11 @@ bool CWallet::CreateSparkMintTransactions( int& nChangePosInOut, bool subtractFeeFromAmount, std::string& strFailReason, + bool fSplit, const CCoinControl *coinControl, bool autoMintAll) { - return sparkWallet->CreateSparkMintTransactions(outputs, wtxAndFee, nAllFeeRet, reservekeys, nChangePosInOut, subtractFeeFromAmount, strFailReason, coinControl, autoMintAll); + return sparkWallet->CreateSparkMintTransactions(outputs, wtxAndFee, nAllFeeRet, reservekeys, nChangePosInOut, subtractFeeFromAmount, strFailReason, fSplit, coinControl, autoMintAll); } std::pair CWallet::GetSparkBalance() diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index f1f1d0c6a9..8ac437eb26 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1047,6 +1047,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface int& nChangePosInOut, bool subtractFeeFromAmount, std::string& strFailReason, + bool fSplit, const CCoinControl *coinControl, bool autoMintAll = false); @@ -1093,6 +1094,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface const std::vector& outputs, std::vector>& wtxAndFee, bool subtractFeeFromAmount, + bool fSplit, bool autoMintAll = false, bool fAskFee = false, const CCoinControl *coinControl = NULL);