Coinbase utxos can only be spent when sending to a single zaddr.
Change from the transaction will be sent to the same zaddr.
This commit is contained in:
@@ -123,11 +123,12 @@ void AsyncRPCOperation_sendmany::main() {
|
|||||||
// 3. Spendable notes are not locked, so another operation could also try to use them
|
// 3. Spendable notes are not locked, so another operation could also try to use them
|
||||||
bool AsyncRPCOperation_sendmany::main_impl() {
|
bool AsyncRPCOperation_sendmany::main_impl() {
|
||||||
|
|
||||||
|
bool isSingleZaddrOutput = (t_outputs_.size()==0 && z_outputs_.size()==1);
|
||||||
bool isPureTaddrOnlyTx = (isfromtaddr_ && z_outputs_.size() == 0);
|
bool isPureTaddrOnlyTx = (isfromtaddr_ && z_outputs_.size() == 0);
|
||||||
CAmount minersFee = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE;
|
CAmount minersFee = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE;
|
||||||
|
|
||||||
// Regardless of the from address, add all taddr outputs to the raw transaction.
|
// Regardless of the from address, add all taddr outputs to the raw transaction.
|
||||||
if (isfromtaddr_ && !find_utxos()) {
|
if (isfromtaddr_ && !find_utxos(isSingleZaddrOutput)) {
|
||||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds, no UTXOs found for taddr from address.");
|
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds, no UTXOs found for taddr from address.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,9 +169,14 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
|||||||
|
|
||||||
// If from address is a taddr, select UTXOs to spend
|
// If from address is a taddr, select UTXOs to spend
|
||||||
CAmount selectedUTXOAmount = 0;
|
CAmount selectedUTXOAmount = 0;
|
||||||
|
bool selectedUTXOCoinbase = false;
|
||||||
if (isfromtaddr_) {
|
if (isfromtaddr_) {
|
||||||
std::vector<SendManyInputUTXO> selectedTInputs;
|
std::vector<SendManyInputUTXO> selectedTInputs;
|
||||||
for (SendManyInputUTXO & t : t_inputs_) {
|
for (SendManyInputUTXO & t : t_inputs_) {
|
||||||
|
bool b = std::get<3>(t);
|
||||||
|
if (b) {
|
||||||
|
selectedUTXOCoinbase = true;
|
||||||
|
}
|
||||||
selectedUTXOAmount += std::get<2>(t);
|
selectedUTXOAmount += std::get<2>(t);
|
||||||
selectedTInputs.push_back(t);
|
selectedTInputs.push_back(t);
|
||||||
if (selectedUTXOAmount >= targetAmount) {
|
if (selectedUTXOAmount >= targetAmount) {
|
||||||
@@ -254,7 +260,9 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
|||||||
* taddr -> taddrs
|
* taddr -> taddrs
|
||||||
* -> zaddrs
|
* -> zaddrs
|
||||||
*
|
*
|
||||||
* There are no notes consumed, only notes produced.
|
* Note: Consensus rule states that coinbase utxos can only be sent to a zaddr.
|
||||||
|
* Any change over and above the amount specified by the user will be sent
|
||||||
|
* to the same zaddr the user is sending funds to.
|
||||||
*/
|
*/
|
||||||
if (isfromtaddr_) {
|
if (isfromtaddr_) {
|
||||||
add_taddr_outputs_to_tx();
|
add_taddr_outputs_to_tx();
|
||||||
@@ -263,8 +271,19 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
|||||||
CAmount fundsSpent = t_outputs_total + minersFee + z_outputs_total;
|
CAmount fundsSpent = t_outputs_total + minersFee + z_outputs_total;
|
||||||
CAmount change = funds - fundsSpent;
|
CAmount change = funds - fundsSpent;
|
||||||
|
|
||||||
|
// If there is a single zaddr and there are coinbase utxos, change goes to the zaddr.
|
||||||
if (change > 0) {
|
if (change > 0) {
|
||||||
add_taddr_change_output_to_tx(change);
|
if (isSingleZaddrOutput && selectedUTXOCoinbase) {
|
||||||
|
std::string address = std::get<0>(zOutputsDeque.front());
|
||||||
|
SendManyRecipient smr(address, change, std::string());
|
||||||
|
zOutputsDeque.push_back(smr);
|
||||||
|
} else if (!isSingleZaddrOutput && selectedUTXOCoinbase) {
|
||||||
|
// This should not happen and is not allowed
|
||||||
|
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet selected Coinbase UTXOs as valid inputs when it should not have done");
|
||||||
|
} else {
|
||||||
|
// If there is a single zaddr and no coinbase utxos, just use a regular output for change.
|
||||||
|
add_taddr_change_output_to_tx(change);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create joinsplits, where each output represents a zaddr recipient.
|
// Create joinsplits, where each output represents a zaddr recipient.
|
||||||
@@ -635,7 +654,7 @@ void AsyncRPCOperation_sendmany::sign_send_raw_transaction(Object obj)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool AsyncRPCOperation_sendmany::find_utxos() {
|
bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase=false) {
|
||||||
set<CBitcoinAddress> setAddress = {fromtaddr_};
|
set<CBitcoinAddress> setAddress = {fromtaddr_};
|
||||||
vector<COutput> vecOutputs;
|
vector<COutput> vecOutputs;
|
||||||
|
|
||||||
@@ -659,11 +678,21 @@ bool AsyncRPCOperation_sendmany::find_utxos() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Also examine out.fSpendable ?
|
// By default we ignore coinbase outputs
|
||||||
|
bool isCoinbase = out.tx->IsCoinBase();
|
||||||
|
if (out.tx->IsCoinBase() && fAcceptCoinbase==false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
CAmount nValue = out.tx->vout[out.i].nValue;
|
CAmount nValue = out.tx->vout[out.i].nValue;
|
||||||
SendManyInputUTXO utxo(out.tx->GetTxid(), out.i, nValue);
|
SendManyInputUTXO utxo(out.tx->GetTxid(), out.i, nValue, isCoinbase);
|
||||||
t_inputs_.push_back(utxo);
|
t_inputs_.push_back(utxo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fAcceptCoinbase==false && t_inputs_.size()==0) {
|
||||||
|
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find any non-coinbase UTXOs to spend.");
|
||||||
|
}
|
||||||
|
|
||||||
return t_inputs_.size() > 0;
|
return t_inputs_.size() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ using namespace json_spirit;
|
|||||||
// A recipient is a tuple of address, amount, memo (optional if zaddr)
|
// A recipient is a tuple of address, amount, memo (optional if zaddr)
|
||||||
typedef std::tuple<std::string, CAmount, std::string> SendManyRecipient;
|
typedef std::tuple<std::string, CAmount, std::string> SendManyRecipient;
|
||||||
|
|
||||||
// Input UTXO is a tuple of txid, vout, amount
|
// Input UTXO is a tuple (quadruple) of txid, vout, amount, coinbase)
|
||||||
typedef std::tuple<uint256, int, CAmount> SendManyInputUTXO;
|
typedef std::tuple<uint256, int, CAmount, bool> SendManyInputUTXO;
|
||||||
|
|
||||||
// Input NPT is a pair of the plaintext note and amount
|
// Input NPT is a pair of the plaintext note and amount
|
||||||
typedef std::pair<NotePlaintext, CAmount> SendManyInputNPT;
|
typedef std::pair<NotePlaintext, CAmount> SendManyInputNPT;
|
||||||
@@ -80,7 +80,7 @@ private:
|
|||||||
void add_taddr_change_output_to_tx(CAmount amount);
|
void add_taddr_change_output_to_tx(CAmount amount);
|
||||||
void add_taddr_outputs_to_tx();
|
void add_taddr_outputs_to_tx();
|
||||||
bool find_unspent_notes();
|
bool find_unspent_notes();
|
||||||
bool find_utxos();
|
bool find_utxos(bool fAcceptCoinbase);
|
||||||
boost::array<unsigned char, ZC_MEMO_SIZE> get_memo_from_hex_string(std::string s);
|
boost::array<unsigned char, ZC_MEMO_SIZE> get_memo_from_hex_string(std::string s);
|
||||||
bool main_impl();
|
bool main_impl();
|
||||||
Object perform_joinsplit( AsyncJoinSplitInfo &);
|
Object perform_joinsplit( AsyncJoinSplitInfo &);
|
||||||
|
|||||||
Reference in New Issue
Block a user