diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index e4dfef5a0..ded24e66b 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -29,6 +29,22 @@ using namespace libzcash; +int find_output(Object obj, int n) { + Value outputMapValue = find_value(obj, "outputmap"); + if (outputMapValue.type() != array_type) { + throw JSONRPCError(RPC_WALLET_ERROR, "Missing outputmap for JoinSplit operation"); + } + + Array outputMap = outputMapValue.get_array(); + for (size_t i = 0; i < outputMap.size(); i++) { + if (outputMap[i] == n) { + return i; + } + } + + return -1; +} + AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany( std::string fromAddress, std::vector tOutputs, @@ -372,6 +388,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { */ Object obj; CAmount jsChange = 0; // this is updated after each joinsplit + int changeOutputIndex = -1; // this is updated after each joinsplit if jsChange > 0 bool minersFeeProcessed = false; if (t_outputs_total > 0) { @@ -429,6 +446,10 @@ bool AsyncRPCOperation_sendmany::main_impl() { } obj = perform_joinsplit(info, outPoints); + + if (jsChange > 0) { + changeOutputIndex = find_output(obj, 1); + } } } @@ -442,9 +463,6 @@ bool AsyncRPCOperation_sendmany::main_impl() { // Keep track of treestate within this transaction boost::unordered_map intermediates; std::vector previousCommitments; - - // NOTE: Randomization of input and output order could break this in future - const int changeOutputIndex = 1; while (zOutputsDeque.size() > 0) { AsyncJoinSplitInfo info; @@ -645,6 +663,10 @@ bool AsyncRPCOperation_sendmany::main_impl() { } obj = perform_joinsplit(info, witnesses, jsAnchor); + + if (jsChange > 0) { + changeOutputIndex = find_output(obj, 1); + } } } @@ -845,11 +867,20 @@ Object AsyncRPCOperation_sendmany::perform_joinsplit( ); // Generate the proof, this can take over a minute. - JSDescription jsdesc(*pzcashParams, + boost::array inputs + {info.vjsin[0], info.vjsin[1]}; + boost::array outputs + {info.vjsout[0], info.vjsout[1]}; + boost::array inputMap; + boost::array outputMap; + JSDescription jsdesc = JSDescription::Randomized( + *pzcashParams, joinSplitPubKey_, anchor, - {info.vjsin[0], info.vjsin[1]}, - {info.vjsout[0], info.vjsout[1]}, + inputs, + outputs, + inputMap, + outputMap, info.vpub_old, info.vpub_new, !this->testmode); @@ -910,10 +941,21 @@ Object AsyncRPCOperation_sendmany::perform_joinsplit( encryptedNote2 = HexStr(ss2.begin(), ss2.end()); } + Array arrInputMap; + Array arrOutputMap; + for (size_t i = 0; i < ZC_NUM_JS_INPUTS; i++) { + arrInputMap.push_back(inputMap[i]); + } + for (size_t i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { + arrOutputMap.push_back(outputMap[i]); + } + Object obj; obj.push_back(Pair("encryptednote1", encryptedNote1)); obj.push_back(Pair("encryptednote2", encryptedNote2)); obj.push_back(Pair("rawtxn", HexStr(ss.begin(), ss.end()))); + obj.push_back(Pair("inputmap", arrInputMap)); + obj.push_back(Pair("outputmap", arrOutputMap)); return obj; }