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:
jl777
2016-12-12 12:19:13 +02:00
94 changed files with 3461 additions and 808 deletions

View File

@@ -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);