diff --git a/qa/rpc-tests/llmq-is-spark.py b/qa/rpc-tests/llmq-is-spark.py
index f45502f399..c5c4d9f9a7 100755
--- a/qa/rpc-tests/llmq-is-spark.py
+++ b/qa/rpc-tests/llmq-is-spark.py
@@ -43,7 +43,21 @@ def run_test(self):
break;
val = Decimal((val - 10000) / 1e+8).quantize(Decimal('1e-7'))
+ address = self.nodes[0].getnewaddress()[0]
+ for i in range(0, 3):
+ multiTxids = self.nodes[0].mintspark({address: {"amount": 1, "subtractFee": False}, sparkaddress: {"amount": 2, "memo": "Test", "subtractFee": False}})
+
+ for multiTxid in multiTxids:
+ sendTx = self.nodes[0].getrawtransaction(multiTxid, 1)
+ valToSend = 0
+ for vi in sendTx['vin']:
+ valToSend += vi['valueSat']
+ if valToSend > 30000:
+ break;
+ valToSend = Decimal((valToSend - 30000) / 1e+8).quantize(Decimal('1e-7'))
+
assert(self.wait_for_instantlock(mintTxid, self.nodes[0]))
+ assert(self.wait_for_instantlock(multiTxid, self.nodes[0]))
mintDspend = self.nodes[0].createrawtransaction(mintTx['vin'], {self.nodes[0].getnewaddress(): str(val)})
assert_raises_jsonrpc(-26, 'tx-txlock-conflict', self.nodes[0].sendrawtransaction, mintDspend)
@@ -51,6 +65,12 @@ def run_test(self):
self.nodes[0].generate(3)
assert (self.nodes[0].getrawtransaction(mintTxid, True)['confirmations'] > 0)
+ sendDspend = self.nodes[0].createrawtransaction(sendTx['vin'], {self.nodes[0].getnewaddress(): str(valToSend)})
+ assert_raises_jsonrpc(-26, 'tx-txlock-conflict', self.nodes[0].sendrawtransaction, sendDspend)
+
+ self.nodes[0].generate(3)
+ assert (self.nodes[0].getrawtransaction(multiTxid, True)['confirmations'] > 0)
+
spendTxid = self.nodes[0].spendspark({self.sporkAddress: {"amount": 0.1, "subtractFee": False}})
assert(self.wait_for_instantlock(spendTxid, self.nodes[0]))
diff --git a/qa/rpc-tests/spark_mint.py b/qa/rpc-tests/spark_mint.py
index a901251750..a816d0175a 100755
--- a/qa/rpc-tests/spark_mint.py
+++ b/qa/rpc-tests/spark_mint.py
@@ -17,10 +17,11 @@ def run_test(self):
self.nodes[0].generate(1001)
# generate coins
- amounts = [1, 1.1, 2, 10]
+ amounts = [1, 1.1, 2, 10, 3, 4]
# 10 confirmations
address = self.nodes[0].getnewsparkaddress()[0]
+ nAddress = self.nodes[0].getnewsparkaddress()[0]
self.nodes[0].mintspark({address: {"amount": amounts[0], "memo":"Test memo"}})
self.nodes[0].mintspark({address: {"amount": amounts[1], "memo": "Test memo"}})
self.nodes[0].generate(5)
@@ -28,12 +29,16 @@ def run_test(self):
# 5 confirmations
self.nodes[0].mintspark({address: {"amount": amounts[2], "memo": "Test memo"}})
self.nodes[0].mintspark({address: {"amount": amounts[3], "memo": "Test memo"}})
+
+ nAddress = self.nodes[0].getnewsparkaddress()[0]
+ self.nodes[0].mintspark({address: {"amount": amounts[4], "subtractFee": False}, nAddress: {"amount": amounts[5], "memo": "Test", "subtractFee": False}})
+
self.nodes[0].generate(5)
# get all mints and utxos
mints = self.verify_listsparkmints(amounts)
self.verify_listunspentsparkmints(amounts)
- assert_equal([False, False, False, False], list(map(lambda m : m["isUsed"], mints)))
+ assert_equal([False, False, False, False, False, False], list(map(lambda m : m["isUsed"], mints)))
# state modification test
# mark two coins as used
@@ -41,23 +46,25 @@ def run_test(self):
self.nodes[0].setsparkmintstatus(mints[3]["lTagHash"], True)
mints = self.verify_listsparkmints(amounts)
- self.verify_listunspentsparkmints([1, 1.1])
- assert_equal([False, False, True, True], list(map(lambda m : m["isUsed"], mints)))
+ self.verify_listunspentsparkmints([1, 1.1, 4, 10])
+ assert_equal([False, False, True, True, False, False], list(map(lambda m : m["isUsed"], mints)))
# set a coin as unused
self.nodes[0].setsparkmintstatus(mints[3]["lTagHash"], False)
mints = self.verify_listsparkmints(amounts)
- self.verify_listunspentsparkmints([1, 1.1, 10])
- assert_equal([False, False, True, False], list(map(lambda m : m["isUsed"], mints)))
+ self.verify_listunspentsparkmints([1, 1.1, 3, 4, 10])
+ assert_equal([False, False, True, False, False, False], list(map(lambda m : m["isUsed"], mints)))
self.nodes[0].setsparkmintstatus(mints[0]["lTagHash"], False)
self.nodes[0].setsparkmintstatus(mints[1]["lTagHash"], False)
self.nodes[0].setsparkmintstatus(mints[2]["lTagHash"], False)
self.nodes[0].setsparkmintstatus(mints[3]["lTagHash"], False)
+ self.nodes[0].setsparkmintstatus(mints[4]["lTagHash"], False)
+ self.nodes[0].setsparkmintstatus(mints[5]["lTagHash"], False)
mints = self.verify_listsparkmints(amounts)
self.verify_listunspentsparkmints(amounts)
- assert_equal([False, False, False, False], list(map(lambda m : m["isUsed"], mints)))
+ assert_equal([False, False, False, False, False, False], list(map(lambda m : m["isUsed"], mints)))
def verify_listsparkmints(self, expected_amounts):
mints = self.nodes[0].listsparkmints()
diff --git a/qa/rpc-tests/spark_setmintstatus_validation.py b/qa/rpc-tests/spark_setmintstatus_validation.py
index 6f3ea77607..0468b73df1 100755
--- a/qa/rpc-tests/spark_setmintstatus_validation.py
+++ b/qa/rpc-tests/spark_setmintstatus_validation.py
@@ -17,9 +17,9 @@ def run_test(self):
self.nodes[0].generate(801)
self.sync_all()
- sparkAddress = self.nodes[0].getnewsparkaddress()[0]
+ sparkAddress1 = self.nodes[0].getnewsparkaddress()[0]
txid = list()
- txid.append(self.nodes[0].mintspark({sparkAddress: {"amount": 1, "memo":"Test memo"}}))
+ txid.append(self.nodes[0].mintspark({sparkAddress1: {"amount": 1, "memo":"Test memo"}}))
spark_mint = self.nodes[0].listsparkmints()
@@ -56,6 +56,11 @@ def run_test(self):
assert not mint_info['isUsed'], \
'This mint with txid: {} should not be Used.'.format(txid)
+ sparkAddress2 = self.nodes[0].getnewsparkaddress()[0]
+ self.nodes[0].mintspark({sparkAddress1: {"amount": 1, "subtractFee": False}, sparkAddress2: {"amount": 2, "memo": "Test", "subtractFee": False}})
+ spark_mint = self.nodes[0].listsparkmints()
+
+ assert len(spark_mint) == 3, 'Should be number of mints.'
assert_raises(JSONRPCException, self.nodes[0].setsparkmintstatus, [(mint_info['lTagHash'], "sometext")])
assert_raises(JSONRPCException, self.nodes[0].setsparkmintstatus, [mint_info['lTagHash']])
diff --git a/qa/rpc-tests/spark_spend_gettransaction.py b/qa/rpc-tests/spark_spend_gettransaction.py
index 1af5301a71..c08fbf1750 100755
--- a/qa/rpc-tests/spark_spend_gettransaction.py
+++ b/qa/rpc-tests/spark_spend_gettransaction.py
@@ -34,8 +34,18 @@ def run_test(self):
self.nodes[0].generate(1)
self.sync_all()
- balance = self.nodes[0].getsparkbalance()
- assert balance['availableBalance'] / 1e8 == 10
+ sparkBalance = self.nodes[0].getsparkbalance()
+ assert sparkBalance['availableBalance'] / 1e8 == 10
+
+ address = self.nodes[0].getnewaddress()
+ for _ in range(10):
+ self.nodes[0].mintspark({address: {"amount": 1, "subtractFee": False}, sparkAddress: {"amount": 2, "memo": "Test", "subtractFee": False}})
+
+ self.nodes[0].generate(1)
+ self.sync_all()
+
+ sparkBalance = self.nodes[0].getsparkbalance()
+ assert sparkBalance['availableBalance'] / 1e8 == 30
# case 1: Spend many with watchonly address
spendto_wo_id = self.nodes[0].spendspark({watchonly_address: {"amount": 1, "subtractFee": False}})
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 8880bf0af7..441fa19f42 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -332,7 +332,7 @@ void SendCoinsDialog::on_sendButton_clicked()
prepareStatus = model->prepareJoinSplitTransaction(currentTransaction, &ctrl);
} else if ((fAnonymousMode == true) && spark::IsSparkAllowed()) {
prepareStatus = model->prepareSpendSparkTransaction(currentTransaction, &ctrl);
- } else if ((fAnonymousMode == false) && (recipients.size() == sparkAddressCount)) {
+ } else if ((fAnonymousMode == false) && (sparkAddressCount > 0)) {
if (spark::IsSparkAllowed())
prepareStatus = model->prepareMintSparkTransaction(transactions, recipients, wtxAndFees, reservekeys, &ctrl);
else {
@@ -462,7 +462,7 @@ void SendCoinsDialog::on_sendButton_clicked()
QString questionString = tr("Are you sure you want to send?");
questionString.append("
%1");
double txSize;
- if ((fAnonymousMode == false) && (recipients.size() == sparkAddressCount) && spark::IsSparkAllowed())
+ if ((fAnonymousMode == false) && (sparkAddressCount > 0) && spark::IsSparkAllowed())
{
for (auto &transaction : transactions) {
txFee += transaction.getTransactionFee();
@@ -488,7 +488,7 @@ void SendCoinsDialog::on_sendButton_clicked()
// add total amount in all subdivision units
questionString.append("
");
- if ((fAnonymousMode == false) && (recipients.size() == sparkAddressCount) && spark::IsSparkAllowed())
+ if ((fAnonymousMode == false) && (sparkAddressCount > 0) && spark::IsSparkAllowed())
{
totalAmount = mintSparkAmount + txFee;
} else if ((fAnonymousMode == true) && (recipients.size() == 1) && spark::IsSparkAllowed()) {
@@ -530,7 +530,7 @@ void SendCoinsDialog::on_sendButton_clicked()
sendStatus = model->sendPrivateCoins(currentTransaction);
} else if ((fAnonymousMode == true) && spark::IsSparkAllowed()) {
sendStatus = model->spendSparkCoins(currentTransaction);
- } else if ((fAnonymousMode == false) && (sparkAddressCount == recipients.size()) && spark::IsSparkAllowed()) {
+ } else if ((fAnonymousMode == false) && (sparkAddressCount > 0) && spark::IsSparkAllowed()) {
sendStatus = model->mintSparkCoins(transactions, wtxAndFees, reservekeys);
} else if ((fAnonymousMode == false) && (sparkAddressCount == 0)) {
sendStatus = model->sendCoins(currentTransaction);
diff --git a/src/qt/sparkmodel.cpp b/src/qt/sparkmodel.cpp
index 3d2682c156..24bcb25616 100644
--- a/src/qt/sparkmodel.cpp
+++ b/src/qt/sparkmodel.cpp
@@ -5,6 +5,7 @@
#include "guiconstants.h"
#include "guiutil.h"
#include "sparkmodel.h"
+#include "../wallet/wallet.h"
#include
#include
@@ -83,7 +84,8 @@ CAmount SparkModel::mintSparkAll()
std::vector> wtxAndFee;
std::vector outputs;
- std::string strError = wallet->MintAndStoreSpark(outputs, wtxAndFee, true, true);
+ std::vector vecSend;
+ std::string strError = wallet->MintAndStoreSpark(vecSend, outputs, wtxAndFee, true, true);
if (strError != "") {
throw std::runtime_error("Fail to mint all public balance, " + strError);
}
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index e6908f973d..9b3b4017d3 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -1355,6 +1355,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareMintSparkTransaction(std::vecto
return OK;
}
+ std::vector vecSend;
QSet setAddress; // Used to detect duplicates
int nAddresses = 0;
std::vector outputs;
@@ -1365,22 +1366,28 @@ WalletModel::SendCoinsReturn WalletModel::prepareMintSparkTransaction(std::vecto
fSubtractFeeFromAmount = true;
{ // User-entered Firo address / amount:
- if (!validateSparkAddress(rcp.address)) {
- return InvalidAddress;
- }
if (rcp.amount <= 0) {
return InvalidAmount;
}
+
setAddress.insert(rcp.address);
++nAddresses;
- spark::Address address(params);
- address.decode(rcp.address.toStdString());
- spark::MintedCoinData data;
- data.address = address;
- data.memo = "";
- data.v = rcp.amount;
- outputs.push_back(data);
+ if (validateAddress(rcp.address)) {
+ CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get());
+ CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount};
+ vecSend.push_back(recipient);
+ } else if (validateSparkAddress(rcp.address)) {
+ spark::Address address(params);
+ address.decode(rcp.address.toStdString());
+ spark::MintedCoinData data;
+ data.address = address;
+ data.memo = "";
+ data.v = rcp.amount;
+ outputs.push_back(data);
+ } else {
+ return InvalidAddress;
+ }
total += rcp.amount;
}
}
@@ -1401,7 +1408,8 @@ 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(vecSend,outputs, wtxAndFees, nFeeRequired, reservekeys, nChangePosRet, fSubtractFeeFromAmount, strFailReason, coinControl, false);
+
transactions.clear();
transactions.reserve(wtxAndFees.size());
for (auto &wtxAndFee : wtxAndFees) {
diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp
index c9109b9a12..21b0a7c217 100644
--- a/src/qt/walletmodeltransaction.cpp
+++ b/src/qt/walletmodeltransaction.cpp
@@ -50,17 +50,19 @@ void WalletModelTransaction::setTransactionFee(const CAmount& newFee)
void WalletModelTransaction::reassignAmounts(int nChangePosRet)
{
int i = 0;
- for (QList::iterator it = recipients.begin(); it != recipients.end(); ++it)
+ QList::iterator rec = recipients.begin();
+ for (auto it = walletTransaction->tx->vout.begin(); it != walletTransaction->tx->vout.end(); ++it)
{
- SendCoinsRecipient& rcp = (*it);
+ SendCoinsRecipient& rcp = (*rec);
{
if (i == nChangePosRet)
- i++;
- if (walletTransaction->tx->vout[i].scriptPubKey.IsSparkSMint()) {
+ continue;
+
+ if (it->scriptPubKey.IsSparkSMint()) {
bool ok = true;
spark::Coin coin(spark::Params::get_default());
try {
- spark::ParseSparkMintCoin(walletTransaction->tx->vout[i].scriptPubKey, coin);
+ spark::ParseSparkMintCoin(it->scriptPubKey, coin);
} catch (std::invalid_argument&) {
ok = false;
}
@@ -73,9 +75,10 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet)
}
}
} else {
- rcp.amount = walletTransaction->tx->vout[i].nValue;
+ rcp.amount = it->nValue;
}
i++;
+ ++rec;
}
}
}
diff --git a/src/spark/sparkwallet.cpp b/src/spark/sparkwallet.cpp
index 23bd19ab49..211044db6f 100644
--- a/src/spark/sparkwallet.cpp
+++ b/src/spark/sparkwallet.cpp
@@ -750,6 +750,7 @@ std::vector CSparkWallet::CreateSparkMintRecipients(
}
bool CSparkWallet::CreateSparkMintTransactions(
+ const std::vector& vecSend,
const std::vector& outputs,
std::vector>& wtxAndFee,
CAmount& nAllFeeRet,
@@ -760,8 +761,17 @@ bool CSparkWallet::CreateSparkMintTransactions(
const CCoinControl *coinControl,
bool autoMintAll)
{
-
int nChangePosRequest = nChangePosInOut;
+ CAmount nValue = 0;
+ for (const auto& recipient : vecSend)
+ {
+ if (nValue < 0 || recipient.nAmount < 0)
+ {
+ strFailReason = _("Transaction amounts must not be negative");
+ return false;
+ }
+ nValue += recipient.nAmount;
+ }
// Create transaction template
CWalletTx wtxNew;
@@ -774,11 +784,14 @@ bool CSparkWallet::CreateSparkMintTransactions(
assert(txNew.nLockTime <= (unsigned int) chainActive.Height());
assert(txNew.nLockTime < LOCKTIME_THRESHOLD);
std::vector outputs_ = outputs;
+ std::vector sendOutputs = vecSend;
CAmount valueToMint = 0;
for (auto& output : outputs_)
valueToMint += output.v;
+ CAmount totalValue = valueToMint + nValue;
+
{
LOCK2(cs_main, pwalletMain->cs_wallet);
{
@@ -809,31 +822,31 @@ bool CSparkWallet::CreateSparkMintTransactions(
// CAmount valueToMintInTx = std::min(
// ::Params().GetConsensus().nMaxValueLelantusMint, itr->first);
- CAmount valueToMintInTx = itr->first;
+ CAmount valueToSendInTx = itr->first;
if (!autoMintAll) {
- valueToMintInTx = std::min(valueToMintInTx, valueToMint);
+ valueToSendInTx = std::min(valueToSendInTx, totalValue);
}
- CAmount nValueToSelect, mintedValue;
+ CAmount nValueToSelect, sendValue;
std::set> setCoins;
bool skipCoin = false;
// Start with no fee and loop until there is enough fee
while (true) {
- mintedValue = valueToMintInTx;
+ sendValue = valueToSendInTx;
if (subtractFeeFromAmount)
- nValueToSelect = mintedValue;
+ nValueToSelect = sendValue;
else
- nValueToSelect = mintedValue + nFeeRet;
+ nValueToSelect = sendValue + nFeeRet;
// if no enough coins in this group then subtract fee from mint
if (nValueToSelect > itr->first && !subtractFeeFromAmount) {
- nValueToSelect = mintedValue;
- mintedValue -= nFeeRet;
+ nValueToSelect = sendValue;
+ sendValue -= nFeeRet;
}
- if (!MoneyRange(mintedValue) || mintedValue == 0) {
+ if (!MoneyRange(sendValue) || sendValue == 0) {
valueAndUTXO.erase(itr);
skipCoin = true;
break;
@@ -845,17 +858,22 @@ bool CSparkWallet::CreateSparkMintTransactions(
wtx.fFromMe = true;
wtx.changes.clear();
setCoins.clear();
+ std::vector remainingSendOutputs = sendOutputs;
std::vector remainingOutputs = outputs_;
std::vector singleTxOutputs;
+ std::vector singleTxSendOutputs;
if (autoMintAll) {
spark::MintedCoinData mintedCoinData;
- mintedCoinData.v = mintedValue;
+ mintedCoinData.v = sendValue;
mintedCoinData.memo = "";
mintedCoinData.address = getDefaultAddress();
singleTxOutputs.push_back(mintedCoinData);
} else {
- uint64_t remainingMintValue = mintedValue;
- while (remainingMintValue > 0){
+ uint64_t remainingMintValue = sendValue;
+ while (!remainingOutputs.empty()) {
+ if (remainingMintValue <= 0) {
+ break;
+ }
// Create the mint data and push into vector
uint64_t singleMintValue = std::min(remainingMintValue, remainingOutputs.begin()->v);
spark::MintedCoinData mintedCoinData;
@@ -871,21 +889,54 @@ bool CSparkWallet::CreateSparkMintTransactions(
if (remainingOutputs.begin()->v == 0)
remainingOutputs.erase(remainingOutputs.begin());
}
+
+ while (!remainingSendOutputs.empty() && remainingMintValue > 0) {
+ CAmount singleSendValue = std::min(static_cast(remainingMintValue), remainingSendOutputs.begin()->nAmount);
+ CRecipient sendCoinData;
+ sendCoinData.nAmount = singleSendValue;
+ sendCoinData.address = remainingSendOutputs.begin()->address;
+ sendCoinData.scriptPubKey = remainingSendOutputs.begin()->scriptPubKey;
+ singleTxSendOutputs.push_back(sendCoinData);
+
+ // subtract minted amount from remaining value
+ remainingMintValue -= singleSendValue;
+ remainingSendOutputs.begin()->nAmount -= singleSendValue;
+
+ if (remainingSendOutputs.begin()->nAmount == 0)
+ remainingSendOutputs.erase(remainingSendOutputs.begin());
+ }
}
if (subtractFeeFromAmount) {
- CAmount singleFee = nFeeRet / singleTxOutputs.size();
- CAmount reminder = nFeeRet % singleTxOutputs.size();
+ size_t totalOutputs = singleTxOutputs.size() + singleTxSendOutputs.size();
+ CAmount singleFee = nFeeRet / totalOutputs;
+ CAmount remainder = nFeeRet % totalOutputs;
+
for (size_t i = 0; i < singleTxOutputs.size(); ++i) {
if (singleTxOutputs[i].v <= singleFee) {
+ remainder += singleTxOutputs[i].v - singleFee;
singleTxOutputs.erase(singleTxOutputs.begin() + i);
- reminder += singleTxOutputs[i].v - singleFee;
--i;
+ } else {
+ singleTxOutputs[i].v -= singleFee;
+ if (remainder > 0 && singleTxOutputs[i].v > remainder) {
+ singleTxOutputs[i].v -= remainder;
+ remainder = 0;
+ }
}
- singleTxOutputs[i].v -= singleFee;
- if (reminder > 0 && singleTxOutputs[i].v > nFeeRet % singleTxOutputs.size()) {// first receiver pays the remainder not divisible by output count
- singleTxOutputs[i].v -= reminder;
- reminder = 0;
+ }
+
+ for (size_t i = 0; i < singleTxSendOutputs.size(); ++i) {
+ if (singleTxSendOutputs[i].nAmount <= singleFee) {
+ remainder += singleTxSendOutputs[i].nAmount - singleFee;
+ singleTxSendOutputs.erase(singleTxSendOutputs.begin() + i);
+ --i;
+ } else {
+ singleTxSendOutputs[i].nAmount -= singleFee;
+ if (remainder > 0 && singleTxSendOutputs[i].nAmount > remainder) {
+ singleTxSendOutputs[i].nAmount -= remainder;
+ remainder = 0;
+ }
}
}
}
@@ -904,6 +955,27 @@ bool CSparkWallet::CreateSparkMintTransactions(
tx.vout.push_back(txout);
}
+
+ for (const auto& recipient : singleTxSendOutputs)
+ {
+ CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
+
+ if (txout.IsDust(dustRelayFee))
+ {
+ if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
+ {
+ if (txout.nValue < 0)
+ strFailReason = _("The transaction amount is too small to pay the fee");
+ else
+ strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
+ }
+ else
+ strFailReason = _("Transaction amount too small");
+ return false;
+ }
+ tx.vout.push_back(txout);
+ }
+
// Choose coins to use
CAmount nValueIn = 0;
if (!pwalletMain->SelectCoins(itr->second, nValueToSelect, setCoins, nValueIn, coinControl)) {
@@ -1103,6 +1175,7 @@ bool CSparkWallet::CreateSparkMintTransactions(
//remove output from outputs_ vector if it got all requested value
outputs_ = remainingOutputs;
+ sendOutputs = remainingSendOutputs;
break; // Done, enough fee included.
}
@@ -1191,15 +1264,16 @@ bool CSparkWallet::CreateSparkMintTransactions(
nAllFeeRet += nFeeRet;
if (!autoMintAll) {
- valueToMint -= mintedValue;
- if (valueToMint == 0)
+ totalValue -= sendValue;
+ if (totalValue == 0) {
break;
+ }
}
}
}
}
- if (!autoMintAll && valueToMint > 0) {
+ if (!autoMintAll && totalValue > 0) {
return false;
}
diff --git a/src/spark/sparkwallet.h b/src/spark/sparkwallet.h
index 8f707a5f94..f99729a0e6 100644
--- a/src/spark/sparkwallet.h
+++ b/src/spark/sparkwallet.h
@@ -114,6 +114,7 @@ class CSparkWallet {
bool generate);
bool CreateSparkMintTransactions(
+ const std::vector& vecSend,
const std::vector& outputs,
std::vector>& wtxAndFee,
diff --git a/src/test/evospork_tests.cpp b/src/test/evospork_tests.cpp
index fb01ac97bc..85a16d9760 100644
--- a/src/test/evospork_tests.cpp
+++ b/src/test/evospork_tests.cpp
@@ -603,8 +603,9 @@ BOOST_AUTO_TEST_CASE(limit)
std::vector sparkMints;
for (int i=0; i<10; i++) {
std::vector> wtxAndFee;
+ std::vector vecSend;
std::vector mints{{address, 50*COIN, ""}};
- std::string error = pwalletMain->MintAndStoreSpark(mints, wtxAndFee, false);
+ std::string error = pwalletMain->MintAndStoreSpark(vecSend, mints, wtxAndFee, false);
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..6fd617e2f8 100644
--- a/src/test/fixtures.cpp
+++ b/src/test/fixtures.cpp
@@ -359,6 +359,7 @@ std::vector SparkTestingSetup::GenerateMints(
spark::Address address = pwalletMain->sparkWallet->getDefaultAddress();
std::vector> wtxAndFeeAll;
+ std::vector vecSend;
for (auto& a : amounts) {
std::vector outputs;
@@ -369,7 +370,7 @@ std::vector SparkTestingSetup::GenerateMints(
data.address = address;
outputs.push_back(data);
- auto result = pwalletMain->MintAndStoreSpark(outputs, wtxAndFee, false);
+ auto result = pwalletMain->MintAndStoreSpark(vecSend, outputs, wtxAndFee, false);
if (result != "") {
throw std::runtime_error(_("Fail to generate mints, ") + result);
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index a5ade6b12f..be98d710de 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -3639,22 +3639,23 @@ UniValue mintspark(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() == 0 || request.params.size() > 2)
throw std::runtime_error(
- "mintspark {\"address\":{amount,memo...}}\n"
- + HelpRequiringPassphrase(pwallet) + "\n"
- "\nArguments:\n"
- " {\n"
- " \"address\":amount (numeric or string) The Spark address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT +
- " is the value\n"
- " ,...\n"
- " }\n"
- "\nResult:\n"
- "\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n"
- " the number of addresses.\n"
- "\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 memo:\n"
- + HelpExampleRpc("mintspark", "\"{\"sr1xtw3yd6v4ghgz873exv2r5nzfwryufxjzzz4xr48gl4jmh7fxml4568xr0nsdd7s4l5as2h50gakzjqrqpm7yrecne8ut8ylxzygj8klttsgm37tna4jk06acl2azph0dq4yxdqqgwa60\":{\"amount\":1},\\\"sr1x7gcqdy670l2v4p9h2m4n5zgzde9y6ht86egffa0qrq40c6z329yfgvu8vyf99tgvnq4hwshvfxxhfzuyvz8dr3lt32j70x8l34japg73ca4w6z9x7c7ryd2gnafg9eg3gpr90gtunraw\":{\"amount\":0.01, \"memo\":\"test_memo2\"}}\"")
+ "mintspark {\"address\":{amount,subtractfee...}, \"address\":{amount,memo,subtractfee...}}\n"
+ + HelpRequiringPassphrase(pwallet) + "\n"
+ "\nArguments:\n"
+ "{\n"
+ " \"address\":amount (numeric or string), memo (string,only for private, not required), subtractfee (bool) The Spark address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value\n"
+ " ,...\n"
+ " }\n"
+ "\nResult:\n"
+ "\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n"
+ " the number of addresses.\n"
+ "\nExamples:\n"
+ "\nSend an amount to transparent address:\n"
+ + HelpExampleCli("mintspark", "\"{\\\"TR1FW48J6ozpRu25U8giSDdTrdXXUYau7U\\\":{\\\"amount\\\":0.01, \\\"subtractFee\\\": false}}\"") +
+ "\nSend an amount to a transparent address and two different private addresses:\n"
+ + HelpExampleCli("mintspark", "\"{\\\"TR1FW48J6ozpRu25U8giSDdTrdXXUYau7U\\\":{\\\"amount\\\":0.01, \\\"subtractFee\\\": false}, \\\"sr1hk87wuh660mss6vnxjf0syt4p6r6ptew97de3dvz698tl7p5p3w7h4m4hcw74mxnqhtz70r7gyydcx6pmkfmnew9q4z0c0muga3sd83h786znjx74ccsjwm284aswppqf2jd0sssendlj\\\":{\\\"amount\\\":0.01, \\\"memo\\\":\\\"test_memo\\\", \\\"subtractFee\\\": false},\\\"sr1x7gcqdy670l2v4p9h2m4n5zgzde9y6ht86egffa0qrq40c6z329yfgvu8vyf99tgvnq4hwshvfxxhfzuyvz8dr3lt32j70x8l34japg73ca4w6z9x7c7ryd2gnafg9eg3gpr90gtunraw\\\":{\\\"amount\\\":0.01, \\\"subtractFee\\\": false}}\"") +
+ "\nSend two amounts to two different transparent addresses and two different private addresses:\n"
+ + HelpExampleRpc("mintspark", "\"{\"TR1FW48J6ozpRu25U8giSDdTrdXXUYau7U\":{\"amount\":0.01, \"subtractFee\": false},\"TuzUyNtTznSNnT2rPXG6Mk7hHG8Svuuoci\":{\"amount\":0.01, \"subtractFee\": true}, \"sr1hk87wuh660mss6vnxjf0syt4p6r6ptew97de3dvz698tl7p5p3w7h4m4hcw74mxnqhtz70r7gyydcx6pmkfmnew9q4z0c0muga3sd83h786znjx74ccsjwm284aswppqf2jd0sssendlj\":{\"amount\":0.01, \"memo\":\"\", \"subtractFee\": false},\"sr1x7gcqdy670l2v4p9h2m4n5zgzde9y6ht86egffa0qrq40c6z329yfgvu8vyf99tgvnq4hwshvfxxhfzuyvz8dr3lt32j70x8l34japg73ca4w6z9x7c7ryd2gnafg9eg3gpr90gtunraw\":{\"amount\":0.01, \"memo\":\"test_memo\", \"subtractFee\": false}}\"")
);
EnsureWalletIsUnlocked(pwallet);
EnsureSparkWalletIsAvailable();
@@ -3671,46 +3672,87 @@ UniValue mintspark(const JSONRPCRequest& request)
const spark::Params* params = spark::Params::get_default();
unsigned char network = spark::GetNetworkType();
+ std::vector recipients;
std::vector outputs;
+ std::set setAddress;
BOOST_FOREACH(const std::string& name_, keys)
{
- spark::Address address(params);
+ spark::Address sAddress(params);
unsigned char coinNetwork;
+ bool isSparkAddress;
try {
- coinNetwork = address.decode(name_);
+ coinNetwork = sAddress.decode(name_);
+ isSparkAddress = true;
+ if (coinNetwork != network)
+ throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid address, wrong network type: ")+name_);
} catch (const std::exception &) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Spark address: ")+name_);
+ isSparkAddress = false;
}
- if (coinNetwork != network)
- throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid address, wrong network type: ")+name_);
- UniValue amountAndMemo = sendTo[name_].get_obj();
+ if (isSparkAddress) {
+ UniValue amountAndMemo = sendTo[name_].get_obj();
- CAmount nAmount(0);
- if (amountAndMemo.exists("amount"))
- nAmount = AmountFromValue(amountAndMemo["amount"]);
- else
- throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameters, no amount: ")+name_);
+ CAmount nAmount(0);
+ if (amountAndMemo.exists("amount"))
+ nAmount = AmountFromValue(amountAndMemo["amount"]);
+ else
+ throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameters, no amount: ")+name_);
- std::string memo = "";
- if (amountAndMemo.exists("memo"))
- memo = amountAndMemo["memo"].get_str();
+ std::string memo = "";
+ if (amountAndMemo.exists("memo"))
+ memo = amountAndMemo["memo"].get_str();
- if (nAmount <= 0)
- throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
- LogPrintf("rpcWallet.mintSpark() nAmount = %d \n", nAmount);
+ if (nAmount <= 0)
+ throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
+ LogPrintf("rpcWallet.mintSpark() nAmount = %d \n", nAmount);
- spark::MintedCoinData data;
- data.address = address;
- data.memo = memo;
- data.v = nAmount;
- outputs.push_back(data);
+ spark::MintedCoinData data;
+ data.address = sAddress;
+ data.memo = memo;
+ data.v = nAmount;
+ outputs.push_back(data);
+ continue;
+ }
+
+ CBitcoinAddress address(name_);
+ if (address.IsValid()) {
+ if (setAddress.count(address))
+ throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ")+name_);
+ setAddress.insert(address);
+
+ CScript scriptPubKey = GetScriptForDestination(address.Get());
+
+ UniValue amountObj = sendTo[name_].get_obj();
+ CAmount nAmount(0);
+ if (amountObj.exists("amount"))
+ nAmount = AmountFromValue(amountObj["amount"]);
+ else
+ throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameters, no amount: ") + name_);
+ if (nAmount <= 0)
+ throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
+
+ bool fSubtractFeeFromAmount = false;
+ if (amountObj.exists("subtractFee"))
+ fSubtractFeeFromAmount = amountObj["subtractFee"].get_bool();
+ else
+ throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameters, no subtractFee: ") + name_);
+
+ CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount};
+ recipients.push_back(recipient);
+
+ continue;
+ }
}
+
+ if (outputs.empty()) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "For transparent sending there is another RPC: spendmany");
+ }
+
bool subtractFeeFromAmount = false;
if (request.params.size() > 1)
subtractFeeFromAmount = request.params[1].get_bool();
std::vector> wtxAndFee;
- std::string strError = pwallet->MintAndStoreSpark(outputs, wtxAndFee, subtractFeeFromAmount);
+ std::string strError = pwallet->MintAndStoreSpark(recipients, outputs, wtxAndFee, subtractFeeFromAmount);
if (strError != "")
throw JSONRPCError(RPC_WALLET_ERROR, strError);
@@ -3745,7 +3787,8 @@ UniValue automintspark(const JSONRPCRequest& request) {
std::vector> wtxAndFee;
std::vector outputs;
- std::string strError = pwallet->MintAndStoreSpark(outputs, wtxAndFee, true, true);
+ std::vector recipients;
+ std::string strError = pwallet->MintAndStoreSpark(recipients, outputs, wtxAndFee, true, true);
if (strError != "")
throw JSONRPCError(RPC_WALLET_ERROR, strError);
diff --git a/src/wallet/test/spark_tests.cpp b/src/wallet/test/spark_tests.cpp
index 7ed925fb2d..94e550b3d0 100644
--- a/src/wallet/test/spark_tests.cpp
+++ b/src/wallet/test/spark_tests.cpp
@@ -76,7 +76,8 @@ BOOST_AUTO_TEST_CASE(mint_and_store_spark)
std::vector mintedCoins;
mintedCoins.push_back(data);
- std::string result = pwalletMain->MintAndStoreSpark(mintedCoins, wtxAndFee, false);
+ std::vector vecSend;
+ std::string result = pwalletMain->MintAndStoreSpark(vecSend, mintedCoins, wtxAndFee, false);
BOOST_CHECK_EQUAL(result, "");
size_t mintAmount = 0;
@@ -101,6 +102,68 @@ BOOST_AUTO_TEST_CASE(mint_and_store_spark)
sparkState->Reset();
}
+BOOST_AUTO_TEST_CASE(multi_mint_and_store_spark)
+{
+ pwalletMain->SetBroadcastTransactions(true);
+ GenerateBlocks(1001);
+
+ std::vector> wtxAndFee;
+
+ const uint64_t v = 1;
+ spark::Address sparkAddress = pwalletMain->sparkWallet->getDefaultAddress();
+
+ spark::MintedCoinData data;
+ data.address = sparkAddress;
+ data.v = v;
+ data.memo = "Test memo";
+
+ std::vector mintedCoins;
+ mintedCoins.push_back(data);
+
+ CPubKey newKey;
+ BOOST_CHECK(pwalletMain->GetKeyFromPool(newKey));
+
+ CBitcoinAddress address = CBitcoinAddress(newKey.GetID()).ToString();
+ std::string strAddress = address.ToString();
+ CScript scriptPubKey = GetScriptForDestination(address.Get());
+
+ const uint64_t val = 1;
+ CRecipient recipient;
+ recipient.address = strAddress;
+ recipient.fSubtractFeeFromAmount = false;
+ recipient.nAmount = val;
+ recipient.scriptPubKey = scriptPubKey;
+
+ std::vector vecSend;
+ vecSend.push_back(recipient);
+
+ std::string result = pwalletMain->MintAndStoreSpark(vecSend, mintedCoins, wtxAndFee, false);
+ BOOST_CHECK_EQUAL(result, "");
+
+ size_t mintAmount = 0;
+ size_t sendAmount = 0;
+ for (const auto& wtx : wtxAndFee) {
+ auto tx = wtx.first.tx.get();
+
+ for (const auto& out : tx->vout) {
+ if (out.scriptPubKey.IsSparkMint()) {
+ mintAmount += out.nValue;
+ } else if (out.scriptPubKey == recipient.scriptPubKey) {
+ sendAmount += out.nValue;
+ }
+ }
+
+ CMutableTransaction mtx(*tx);
+ BOOST_CHECK(GenerateBlock({mtx}));
+ }
+
+ BOOST_CHECK_EQUAL(data.v, mintAmount);
+ BOOST_CHECK_EQUAL(recipient.nAmount, sendAmount);
+
+ auto sparkState = spark::CSparkState::GetState();
+ sparkState->Reset();
+}
+
BOOST_AUTO_TEST_CASE(mint_subtract_fee)
{
pwalletMain->SetBroadcastTransactions(true);
@@ -119,7 +182,8 @@ BOOST_AUTO_TEST_CASE(mint_subtract_fee)
std::vector mintedCoins;
mintedCoins.push_back(data);
- std::string result = pwalletMain->MintAndStoreSpark(mintedCoins, wtxAndFee, true);
+ std::vector vecSend;
+ std::string result = pwalletMain->MintAndStoreSpark(vecSend, mintedCoins, wtxAndFee, true);
BOOST_CHECK_EQUAL(result, "");
size_t mintAmount = 0;
@@ -146,6 +210,68 @@ BOOST_AUTO_TEST_CASE(mint_subtract_fee)
sparkState->Reset();
}
+BOOST_AUTO_TEST_CASE(multi_mint_subtract_fee)
+{
+ pwalletMain->SetBroadcastTransactions(true);
+ GenerateBlocks(1001);
+
+ std::vector> wtxAndFee;
+
+ const uint64_t v = 1 * COIN;
+ spark::Address sparkAddress = pwalletMain->sparkWallet->getDefaultAddress();
+
+ spark::MintedCoinData data;
+ data.address = sparkAddress;
+ data.v = v;
+ data.memo = "Test memo";
+
+ std::vector mintedCoins;
+ mintedCoins.push_back(data);
+
+ CPubKey newKey;
+ BOOST_CHECK(pwalletMain->GetKeyFromPool(newKey));
+
+ CBitcoinAddress address = CBitcoinAddress(newKey.GetID()).ToString();
+ std::string strAddress = address.ToString();
+ CScript scriptPubKey = GetScriptForDestination(address.Get());
+
+ const uint64_t val = 1 * COIN;
+ CRecipient recipient;
+ recipient.address = strAddress;
+ recipient.fSubtractFeeFromAmount = true;
+ recipient.nAmount = val;
+ recipient.scriptPubKey = scriptPubKey;
+
+ std::vector vecSend;
+ vecSend.push_back(recipient);
+ std::string result = pwalletMain->MintAndStoreSpark(vecSend, mintedCoins, wtxAndFee, true);
+ BOOST_CHECK_EQUAL(result, "");
+
+ size_t mintAmount = 0;
+ size_t sendAmount = 0;
+ size_t fee = 0;
+ for (const auto& wtx : wtxAndFee) {
+ auto tx = wtx.first.tx.get();
+
+ for (const auto& out : tx->vout) {
+ if (out.scriptPubKey.IsSparkMint()) {
+ mintAmount += out.nValue;
+ } else if (out.scriptPubKey == recipient.scriptPubKey) {
+ sendAmount += out.nValue;
+ }
+ }
+ CMutableTransaction mtx(*tx);
+ BOOST_CHECK(GenerateBlock({mtx}));
+ fee += wtx.second;
+ }
+
+ BOOST_CHECK_EQUAL(data.v, mintAmount + (fee / 2));
+ BOOST_CHECK_EQUAL(recipient.nAmount, sendAmount + (fee / 2));
+
+ auto sparkState = spark::CSparkState::GetState();
+ sparkState->Reset();
+}
+
BOOST_AUTO_TEST_CASE(list_spark_mints)
{
GenerateBlocks(1001);
@@ -189,7 +315,6 @@ BOOST_AUTO_TEST_CASE(list_spark_mints)
sparkState->Reset();
}
-
BOOST_AUTO_TEST_CASE(spend)
{
pwalletMain->SetBroadcastTransactions(true);
@@ -207,10 +332,11 @@ BOOST_AUTO_TEST_CASE(spend)
mintedCoins.push_back(data);
std::vector> wtxAndFee;
- std::string result = pwalletMain->MintAndStoreSpark(mintedCoins, wtxAndFee, false);
+ std::vector vecSend;
+ std::string result = pwalletMain->MintAndStoreSpark(vecSend, mintedCoins, wtxAndFee, false);
std::vector> wtxAndFee2;
- pwalletMain->MintAndStoreSpark(mintedCoins, wtxAndFee2, false);
+ pwalletMain->MintAndStoreSpark(vecSend, mintedCoins, wtxAndFee2, false);
BOOST_CHECK_EQUAL("", result);
@@ -297,29 +423,86 @@ BOOST_AUTO_TEST_CASE(mintspark_and_mint_all)
GenerateBlocks(100, &externalScript);
std::vector> wtxAndFee;
- const uint64_t v = 10 * COIN;
+ const uint64_t v1 = 10 * COIN;
+ const uint64_t v2 = 10 * COIN;
- spark::Address sparkAddress = pwalletMain->sparkWallet->getDefaultAddress();
+ spark::Address sparkAddress1 = pwalletMain->sparkWallet->getDefaultAddress();
+ spark::Address sparkAddress2 = pwalletMain->sparkWallet->getDefaultAddress();
spark::MintedCoinData data;
+ data.address = sparkAddress1;
+ data.v = v1;
+ data.memo = "Test memo";
+ std::vector mintedCoins;
+ mintedCoins.push_back(data);
+
+ spark::MintedCoinData data2;
+ data2.address = sparkAddress2;
+ data2.v = v2;
+ data2.memo = "Test memo";
+
+ mintedCoins.push_back(data2);
+
+ std::vector vecSend;
+ auto result = pwalletMain->MintAndStoreSpark(vecSend, mintedCoins, wtxAndFee, false);
+ BOOST_CHECK_EQUAL("", result);
+ BOOST_CHECK_EQUAL(1, wtxAndFee.size()); //
+ BOOST_CHECK_EQUAL(20 * COIN, countMintsInBalance(wtxAndFee));
+ wtxAndFee.clear();
+ mintedCoins.clear();
+
+ CPubKey newKey;
+ BOOST_CHECK(pwalletMain->GetKeyFromPool(newKey));
+
+ CBitcoinAddress address = CBitcoinAddress(newKey.GetID()).ToString();
+ std::string strAddress = address.ToString();
+ CScript scriptPubKey = GetScriptForDestination(address.Get());
+
+ const uint64_t val = 5 * COIN;
+ CRecipient recipient;
+ recipient.address = strAddress;
+ recipient.fSubtractFeeFromAmount = false;
+ recipient.nAmount = val;
+ recipient.scriptPubKey = scriptPubKey;
+ vecSend.push_back(recipient);
+
+ spark::Address sparkAddress = pwalletMain->sparkWallet->getDefaultAddress();
+ const uint64_t v = 10 * COIN;
+
data.address = sparkAddress;
data.v = v;
data.memo = "Test memo";
- std::vector mintedCoins;
mintedCoins.push_back(data);
- auto result = pwalletMain->MintAndStoreSpark(mintedCoins, wtxAndFee, false);
+ result = pwalletMain->MintAndStoreSpark(vecSend, mintedCoins, wtxAndFee, false);
BOOST_CHECK_EQUAL("", result);
- BOOST_CHECK_EQUAL(1, wtxAndFee.size());
BOOST_CHECK_EQUAL(10 * COIN, countMintsInBalance(wtxAndFee));
+
+ size_t sendAmount = 0;
+ for (const auto& wtx : wtxAndFee) {
+ auto tx = wtx.first.tx.get();
+
+ for (const auto& out : tx->vout) {
+ if (out.scriptPubKey == recipient.scriptPubKey) {
+ sendAmount += out.nValue;
+ }
+ }
+
+ CMutableTransaction mtx(*tx);
+ BOOST_CHECK(GenerateBlock({mtx}));
+ }
+
+ BOOST_CHECK_EQUAL(5 * COIN, sendAmount);
+
wtxAndFee.clear();
mintedCoins.clear();
+ vecSend.clear();
data.v = 600 * COIN;;
mintedCoins.clear();
mintedCoins.push_back(data);
- result = pwalletMain->MintAndStoreSpark(mintedCoins, wtxAndFee, false);
+ result = pwalletMain->MintAndStoreSpark(vecSend, mintedCoins, wtxAndFee, false);
BOOST_CHECK_EQUAL("", result);
BOOST_CHECK_GT(wtxAndFee.size(), 1);
BOOST_CHECK_EQUAL(600 * COIN, countMintsInBalance(wtxAndFee));
@@ -330,7 +513,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);
BOOST_CHECK_EQUAL("", result);
BOOST_CHECK_GT(balance, countMintsInBalance(wtxAndFee));
BOOST_CHECK_EQUAL(balance, countMintsInBalance(wtxAndFee, true));
@@ -344,7 +527,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);
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 fababfae30..8e10ff85ee 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -4242,7 +4242,6 @@ bool CWallet::SelectCoins(const std::vector& vAvailableCoins, const CAm
// add preset inputs to the total value selected
nValueRet += nValueFromPresetInputs;
-
return res;
}
@@ -5610,6 +5609,7 @@ std::string CWallet::MintAndStoreLelantus(const CAmount& value,
}
std::string CWallet::MintAndStoreSpark(
+ const std::vector& vecSend,
const std::vector& outputs,
std::vector>& wtxAndFee,
bool subtractFeeFromAmount,
@@ -5630,6 +5630,10 @@ std::string CWallet::MintAndStoreSpark(
for (auto& output : outputs)
value += output.v;
+ for (auto& f : vecSend)
+ if (f.fSubtractFeeFromAmount)
+ subtractFeeFromAmount = true;
+
if ((value + payTxFee.GetFeePerK()) > GetBalance())
return _("Insufficient funds");
@@ -5639,7 +5643,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(vecSend, outputs, wtxAndFee, nFeeRequired, reservekeys, nChangePosRet, subtractFeeFromAmount, strError, coinControl, autoMintAll)) {
return strError;
}
@@ -5920,7 +5924,7 @@ bool CWallet::LelantusToSpark(std::string& strFailReason) {
COutPoint outPoint(result.GetHash(), i);
coinControl.Select(outPoint);
std::vector> wtxAndFee;
- MintAndStoreSpark({}, wtxAndFee, true, true, false, &coinControl);
+ MintAndStoreSpark({}, {}, wtxAndFee, true, true, false, &coinControl);
}
return true;
@@ -8169,6 +8173,7 @@ bool CompSigmaHeight(const CSigmaEntry &a, const CSigmaEntry &b) { return a.nHei
bool CompSigmaID(const CSigmaEntry &a, const CSigmaEntry &b) { return a.id < b.id; }
bool CWallet::CreateSparkMintTransactions(
+ const std::vector& vecSend,
const std::vector& outputs,
std::vector>& wtxAndFee,
CAmount& nAllFeeRet,
@@ -8179,7 +8184,7 @@ bool CWallet::CreateSparkMintTransactions(
const CCoinControl *coinControl,
bool autoMintAll)
{
- return sparkWallet->CreateSparkMintTransactions(outputs, wtxAndFee, nAllFeeRet, reservekeys, nChangePosInOut, subtractFeeFromAmount, strFailReason, coinControl, autoMintAll);
+ return sparkWallet->CreateSparkMintTransactions(vecSend, outputs, wtxAndFee, nAllFeeRet, reservekeys, nChangePosInOut, subtractFeeFromAmount, strFailReason, coinControl, autoMintAll);
}
std::pair CWallet::GetSparkBalance()
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index ab6e98fd7d..ed9c86c1b6 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -1038,6 +1038,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
bool IsSparkAddressMine(const std::string& address);
bool CreateSparkMintTransactions(
+ const std::vector& vecSend,
const std::vector& outputs,
std::vector>& wtxAndFee,
CAmount& nAllFeeRet,
@@ -1088,6 +1089,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
const CCoinControl *coinControl = NULL);
std::string MintAndStoreSpark(
+ const std::vector& vecSend,
const std::vector& outputs,
std::vector>& wtxAndFee,
bool subtractFeeFromAmount,