Skip to content

Commit

Permalink
Multi send
Browse files Browse the repository at this point in the history
  • Loading branch information
levoncrypto committed Jun 2, 2024
1 parent 7fb07bf commit 0af162e
Show file tree
Hide file tree
Showing 16 changed files with 480 additions and 115 deletions.
20 changes: 20 additions & 0 deletions qa/rpc-tests/llmq-is-spark.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,34 @@ 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)

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]))

Expand Down
21 changes: 14 additions & 7 deletions qa/rpc-tests/spark_mint.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,47 +17,54 @@ 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)

# 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
self.nodes[0].setsparkmintstatus(mints[2]["lTagHash"], True)
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()
Expand Down
9 changes: 7 additions & 2 deletions qa/rpc-tests/spark_setmintstatus_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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']])
Expand Down
14 changes: 12 additions & 2 deletions qa/rpc-tests/spark_spend_gettransaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}})
Expand Down
8 changes: 4 additions & 4 deletions src/qt/sendcoinsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -462,7 +462,7 @@ void SendCoinsDialog::on_sendButton_clicked()
QString questionString = tr("Are you sure you want to send?");
questionString.append("<br /><br />%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();
Expand All @@ -488,7 +488,7 @@ void SendCoinsDialog::on_sendButton_clicked()

// add total amount in all subdivision units
questionString.append("<hr />");
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()) {
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion src/qt/sparkmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "guiconstants.h"
#include "guiutil.h"
#include "sparkmodel.h"
#include "../wallet/wallet.h"

#include <QDateTime>
#include <QTimer>
Expand Down Expand Up @@ -83,7 +84,8 @@ CAmount SparkModel::mintSparkAll()

std::vector<std::pair<CWalletTx, CAmount>> wtxAndFee;
std::vector<spark::MintedCoinData> outputs;
std::string strError = wallet->MintAndStoreSpark(outputs, wtxAndFee, true, true);
std::vector<CRecipient> vecSend;
std::string strError = wallet->MintAndStoreSpark(vecSend, outputs, wtxAndFee, true, true);
if (strError != "") {
throw std::runtime_error("Fail to mint all public balance, " + strError);
}
Expand Down
30 changes: 19 additions & 11 deletions src/qt/walletmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1355,6 +1355,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareMintSparkTransaction(std::vecto
return OK;
}

std::vector<CRecipient> vecSend;
QSet<QString> setAddress; // Used to detect duplicates
int nAddresses = 0;
std::vector<spark::MintedCoinData> outputs;
Expand All @@ -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;
}
}
Expand All @@ -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) {
Expand Down
15 changes: 9 additions & 6 deletions src/qt/walletmodeltransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,19 @@ void WalletModelTransaction::setTransactionFee(const CAmount& newFee)
void WalletModelTransaction::reassignAmounts(int nChangePosRet)
{
int i = 0;
for (QList<SendCoinsRecipient>::iterator it = recipients.begin(); it != recipients.end(); ++it)
QList<SendCoinsRecipient>::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;
}
Expand All @@ -73,9 +75,10 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet)
}
}
} else {
rcp.amount = walletTransaction->tx->vout[i].nValue;
rcp.amount = it->nValue;
}
i++;
++rec;
}
}
}
Expand Down
Loading

0 comments on commit 0af162e

Please sign in to comment.