Merge remote-tracking branch 'zcash/master' into rebase
# Conflicts: # README.md # src/Makefile.gtest.include # src/chainparams.cpp # src/init.cpp # src/miner.cpp # src/wallet/wallet.cpp
This commit is contained in:
@@ -52,9 +52,12 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany(
|
||||
std::string fromAddress,
|
||||
std::vector<SendManyRecipient> tOutputs,
|
||||
std::vector<SendManyRecipient> zOutputs,
|
||||
int minDepth) :
|
||||
fromaddress_(fromAddress), t_outputs_(tOutputs), z_outputs_(zOutputs), mindepth_(minDepth)
|
||||
int minDepth,
|
||||
CAmount fee) :
|
||||
fromaddress_(fromAddress), t_outputs_(tOutputs), z_outputs_(zOutputs), mindepth_(minDepth), fee_(fee)
|
||||
{
|
||||
assert(fee_ > 0);
|
||||
|
||||
if (minDepth < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Minconf cannot be negative");
|
||||
}
|
||||
@@ -149,8 +152,8 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
||||
bool isSingleZaddrOutput = (t_outputs_.size()==0 && z_outputs_.size()==1);
|
||||
bool isMultipleZaddrOutput = (t_outputs_.size()==0 && z_outputs_.size()>=1);
|
||||
bool isPureTaddrOnlyTx = (isfromtaddr_ && z_outputs_.size() == 0);
|
||||
CAmount minersFee = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE;
|
||||
|
||||
CAmount minersFee = fee_;
|
||||
|
||||
// When spending coinbase utxos, you can only specify a single zaddr as the change must go somewhere
|
||||
// and if there are multiple zaddrs, we don't know where to send it.
|
||||
if (isfromtaddr_) {
|
||||
@@ -202,17 +205,29 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
||||
assert(!isfromzaddr_ || t_inputs_total == 0);
|
||||
|
||||
if (isfromtaddr_ && (t_inputs_total < targetAmount)) {
|
||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strprintf("Insufficient transparent funds, have %ld, need %ld", t_inputs_total, targetAmount));
|
||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS,
|
||||
strprintf("Insufficient transparent funds, have %s, need %s",
|
||||
FormatMoney(t_inputs_total), FormatMoney(targetAmount)));
|
||||
}
|
||||
|
||||
if (isfromzaddr_ && (z_inputs_total < targetAmount)) {
|
||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strprintf("Insufficient protected funds, have %ld, need %ld", z_inputs_total, targetAmount));
|
||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS,
|
||||
strprintf("Insufficient protected funds, have %s, need %s",
|
||||
FormatMoney(z_inputs_total), FormatMoney(targetAmount)));
|
||||
}
|
||||
|
||||
// If from address is a taddr, select UTXOs to spend
|
||||
CAmount selectedUTXOAmount = 0;
|
||||
bool selectedUTXOCoinbase = false;
|
||||
if (isfromtaddr_) {
|
||||
// Get dust threshold
|
||||
CKey secret;
|
||||
secret.MakeNewKey(true);
|
||||
CScript scriptPubKey = GetScriptForDestination(secret.GetPubKey().GetID());
|
||||
CTxOut out(CAmount(1), scriptPubKey);
|
||||
CAmount dustThreshold = out.GetDustThreshold(minRelayTxFee);
|
||||
CAmount dustChange = -1;
|
||||
|
||||
std::vector<SendManyInputUTXO> selectedTInputs;
|
||||
for (SendManyInputUTXO & t : t_inputs_) {
|
||||
bool b = std::get<3>(t);
|
||||
@@ -222,9 +237,21 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
||||
selectedUTXOAmount += std::get<2>(t);
|
||||
selectedTInputs.push_back(t);
|
||||
if (selectedUTXOAmount >= targetAmount) {
|
||||
break;
|
||||
// Select another utxo if there is change less than the dust threshold.
|
||||
dustChange = selectedUTXOAmount - targetAmount;
|
||||
if (dustChange == 0 || dustChange >= dustThreshold) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there is transparent change, is it valid or is it dust?
|
||||
if (dustChange < dustThreshold && dustChange != 0) {
|
||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS,
|
||||
strprintf("Insufficient transparent funds, have %s, need %s more to avoid creating invalid change output %s (dust threshold is %s)",
|
||||
FormatMoney(t_inputs_total), FormatMoney(dustThreshold - dustChange), FormatMoney(dustChange), FormatMoney(dustThreshold)));
|
||||
}
|
||||
|
||||
t_inputs_ = selectedTInputs;
|
||||
t_inputs_total = selectedUTXOAmount;
|
||||
|
||||
@@ -298,7 +325,22 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
||||
zOutputsDeque.push_back(o);
|
||||
}
|
||||
|
||||
|
||||
// When spending notes, take a snapshot of note witnesses and anchors as the treestate will
|
||||
// change upon arrival of new blocks which contain joinsplit transactions. This is likely
|
||||
// to happen as creating a chained joinsplit transaction can take longer than the block interval.
|
||||
if (z_inputs_.size() > 0) {
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
for (auto t : z_inputs_) {
|
||||
JSOutPoint jso = std::get<0>(t);
|
||||
std::vector<JSOutPoint> vOutPoints = { jso };
|
||||
uint256 inputAnchor;
|
||||
std::vector<boost::optional<ZCIncrementalWitness>> vInputWitnesses;
|
||||
pwalletMain->GetNoteWitnesses(vOutPoints, vInputWitnesses, inputAnchor);
|
||||
jsopWitnessAnchorMap[ jso.ToString() ] = WitnessAnchorData{ vInputWitnesses[0], inputAnchor };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* SCENARIO #2
|
||||
*
|
||||
@@ -321,7 +363,9 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
||||
if (selectedUTXOCoinbase) {
|
||||
assert(isSingleZaddrOutput);
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, strprintf(
|
||||
"Change %ld not allowed. When protecting coinbase funds, the wallet does not allow any change as there is currently no way to specify a change address in z_sendmany.", change));
|
||||
"Change %s not allowed. When protecting coinbase funds, the wallet does not "
|
||||
"allow any change as there is currently no way to specify a change address "
|
||||
"in z_sendmany.", FormatMoney(change)));
|
||||
} else {
|
||||
add_taddr_change_output_to_tx(change);
|
||||
LogPrint("zrpc", "%s: transparent change in transaction output (amount=%s)\n",
|
||||
@@ -496,15 +540,23 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Could not find previous JoinSplit anchor");
|
||||
}
|
||||
|
||||
assert(changeOutputIndex != -1);
|
||||
boost::optional<ZCIncrementalWitness> changeWitness;
|
||||
int n = 0;
|
||||
for (const uint256& commitment : prevJoinSplit.commitments) {
|
||||
tree.append(commitment);
|
||||
previousCommitments.push_back(commitment);
|
||||
previousCommitments.push_back(commitment);
|
||||
if (!changeWitness && changeOutputIndex == n++) {
|
||||
changeWitness = tree.witness();
|
||||
} else if (changeWitness) {
|
||||
changeWitness.get().append(commitment);
|
||||
}
|
||||
}
|
||||
ZCIncrementalWitness changeWitness = tree.witness();
|
||||
jsAnchor = changeWitness.root();
|
||||
uint256 changeCommitment = prevJoinSplit.commitments[changeOutputIndex];
|
||||
intermediates.insert(std::make_pair(tree.root(), tree));
|
||||
witnesses.push_back(changeWitness);
|
||||
if (changeWitness) {
|
||||
witnesses.push_back(changeWitness);
|
||||
}
|
||||
jsAnchor = tree.root();
|
||||
intermediates.insert(std::make_pair(tree.root(), tree)); // chained js are interstitial (found in between block boundaries)
|
||||
|
||||
// Decrypt the change note's ciphertext to retrieve some data we need
|
||||
ZCNoteDecryption decryptor(spendingkey_.viewing_key());
|
||||
@@ -538,6 +590,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
||||
//
|
||||
std::vector<Note> vInputNotes;
|
||||
std::vector<JSOutPoint> vOutPoints;
|
||||
std::vector<boost::optional<ZCIncrementalWitness>> vInputWitnesses;
|
||||
uint256 inputAnchor;
|
||||
int numInputsNeeded = (jsChange>0) ? 1 : 0;
|
||||
while (numInputsNeeded++ < ZC_NUM_JS_INPUTS && zInputsDeque.size() > 0) {
|
||||
@@ -547,6 +600,14 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
||||
CAmount noteFunds = std::get<2>(t);
|
||||
zInputsDeque.pop_front();
|
||||
|
||||
WitnessAnchorData wad = jsopWitnessAnchorMap[ jso.ToString() ];
|
||||
vInputWitnesses.push_back(wad.witness);
|
||||
if (inputAnchor.IsNull()) {
|
||||
inputAnchor = wad.anchor;
|
||||
} else if (inputAnchor != wad.anchor) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Selected input notes do not share the same anchor");
|
||||
}
|
||||
|
||||
vOutPoints.push_back(jso);
|
||||
vInputNotes.push_back(note);
|
||||
|
||||
@@ -563,12 +624,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
||||
|
||||
// Add history of previous commitments to witness
|
||||
if (vInputNotes.size() > 0) {
|
||||
std::vector<boost::optional<ZCIncrementalWitness>> vInputWitnesses;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
pwalletMain->GetNoteWitnesses(vOutPoints, vInputWitnesses, inputAnchor);
|
||||
}
|
||||
|
||||
|
||||
if (vInputWitnesses.size()==0) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Could not find witness for note commitment");
|
||||
}
|
||||
@@ -760,6 +816,11 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase=false) {
|
||||
t_inputs_.push_back(utxo);
|
||||
}
|
||||
|
||||
// sort in ascending order, so smaller utxos appear first
|
||||
std::sort(t_inputs_.begin(), t_inputs_.end(), [](SendManyInputUTXO i, SendManyInputUTXO j) -> bool {
|
||||
return ( std::get<2>(i) < std::get<2>(j));
|
||||
});
|
||||
|
||||
return t_inputs_.size() > 0;
|
||||
}
|
||||
|
||||
@@ -894,8 +955,11 @@ Object AsyncRPCOperation_sendmany::perform_joinsplit(
|
||||
info.vpub_new,
|
||||
!this->testmode);
|
||||
|
||||
if (!(jsdesc.Verify(*pzcashParams, joinSplitPubKey_))) {
|
||||
throw std::runtime_error("error verifying joinsplit");
|
||||
{
|
||||
auto verifier = libzcash::ProofVerifier::Strict();
|
||||
if (!(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey_))) {
|
||||
throw std::runtime_error("error verifying joinsplit");
|
||||
}
|
||||
}
|
||||
|
||||
mtx.vjoinsplit.push_back(jsdesc);
|
||||
|
||||
Reference in New Issue
Block a user