diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.cpp b/src/wallet/asyncrpcoperation_mergetoaddress.cpp index 488e7f5b7..4dace19bb 100644 --- a/src/wallet/asyncrpcoperation_mergetoaddress.cpp +++ b/src/wallet/asyncrpcoperation_mergetoaddress.cpp @@ -429,9 +429,6 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() * END SCENARIO #0 */ - - UniValue obj(UniValue::VOBJ); - /** * SCENARIO #1 * @@ -440,6 +437,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() * There are no zaddrs or joinsplits involved. */ if (isPureTaddrOnlyTx) { + UniValue obj(UniValue::VOBJ); obj.push_back(Pair("rawtxn", EncodeHexTx(tx_))); sign_send_raw_transaction(obj); return true; @@ -448,8 +446,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() * END SCENARIO #1 */ - sign_send_raw_transaction(obj); - return true; + return false; } diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index db7f7bd66..a1ecb296d 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2016 The Zcash developers // Copyright (c) 2019-2020 The Hush developers // Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// file COPYING or https://www.opensource.org/licenses/mit-license.php /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * @@ -566,430 +566,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { * END SCENARIO #0 */ - - // Grab the current consensus branch ID - { - LOCK(cs_main); - consensusBranchId_ = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); - } - - /** - * SCENARIO #1 - * - * taddr -> taddrs - * - * There are no zaddrs or joinsplits involved. - */ - if (isPureTaddrOnlyTx) { - add_taddr_outputs_to_tx(); - - CAmount funds = selectedUTXOAmount; - CAmount fundsSpent = t_outputs_total + minersFee; - CAmount change = funds - fundsSpent; - - if (change > 0) { - add_taddr_change_output_to_tx(0,change); - - LogPrint("zrpc", "%s: transparent change in transaction output (amount=%s)\n", - getId(), - FormatMoney(change) - ); - } - - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("rawtxn", EncodeHexTx(tx_))); - sign_send_raw_transaction(obj); - return true; - } - /** - * END SCENARIO #1 - */ - - - // Prepare raw transaction to handle JoinSplits - CMutableTransaction mtx(tx_); - crypto_sign_keypair(joinSplitPubKey_.begin(), joinSplitPrivKey_); - mtx.joinSplitPubKey = joinSplitPubKey_; - //if ((uint32_t)chainActive.LastTip()->nTime < ASSETCHAINS_STAKED_HF_TIMESTAMP) - if ( !hush_hardfork_active((uint32_t)chainActive.LastTip()->nTime) ) - mtx.nLockTime = (uint32_t)time(NULL) - 60; // jl777 - else - mtx.nLockTime = (uint32_t)chainActive.Tip()->GetMedianTimePast(); - - tx_ = CTransaction(mtx); - - // Copy zinputs and zoutputs to more flexible containers - std::deque zInputsDeque; // zInputsDeque stores minimum numbers of notes for target amount - CAmount tmp = 0; - for (auto o : z_sprout_inputs_) { - zInputsDeque.push_back(o); - tmp += std::get<2>(o); - if (tmp >= targetAmount) { - break; - } - } - std::deque zOutputsDeque; - for (auto o : z_outputs_) { - 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_sprout_inputs_.size() > 0) { - LOCK2(cs_main, pwalletMain->cs_wallet); - for (auto t : z_sprout_inputs_) { - JSOutPoint jso = std::get<0>(t); - std::vector vOutPoints = { jso }; - uint256 inputAnchor; - std::vector> vInputWitnesses; - pwalletMain->GetSproutNoteWitnesses(vOutPoints, vInputWitnesses, inputAnchor); - jsopWitnessAnchorMap[ jso.ToString() ] = WitnessAnchorData{ vInputWitnesses[0], inputAnchor }; - } - } - - - /** - * SCENARIO #2 - * - * taddr -> taddrs - * -> zaddrs - * - * Note: Consensus rule states that coinbase utxos can only be sent to a zaddr. TODO: Do they? - * Local wallet rule does not allow any change when sending coinbase utxos - * since there is currently no way to specify a change address and we don't - * want users accidentally sending excess funds to a recipient. - */ - if (isfromtaddr_) { - add_taddr_outputs_to_tx(); - - CAmount funds = selectedUTXOAmount; - CAmount fundsSpent = t_outputs_total + minersFee + z_outputs_total; - CAmount change = funds - fundsSpent; - - if (change > 0) { - if (selectedUTXOCoinbase) { - assert(isSingleZaddrOutput); - throw JSONRPCError(RPC_WALLET_ERROR, strprintf( - "Change %s not allowed. When shielding 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 { - CBitcoinAddress ba = CBitcoinAddress(fromtaddr_); - add_taddr_change_output_to_tx(&ba,change); - LogPrint("zrpc", "%s: transparent change in transaction output (amount=%s)\n", - getId(), - FormatMoney(change) - ); - } - } - - // Create joinsplits, where each output represents a zaddr recipient. - UniValue obj(UniValue::VOBJ); - while (zOutputsDeque.size() > 0) { - AsyncJoinSplitInfo info; - info.vpub_old = 0; - info.vpub_new = 0; - int n = 0; - while (n++ 0) { - SendManyRecipient smr = zOutputsDeque.front(); - std::string address = std::get<0>(smr); - CAmount value = std::get<1>(smr); - std::string hexMemo = std::get<2>(smr); - zOutputsDeque.pop_front(); - - PaymentAddress pa = DecodePaymentAddress(address); - JSOutput jso = JSOutput(boost::get(pa), value); - if (hexMemo.size() > 0) { - jso.memo = get_memo_from_hex_string(hexMemo); - } - info.vjsout.push_back(jso); - - // Funds are removed from the value pool and enter the private pool - info.vpub_old += value; - } - obj = perform_joinsplit(info); - } - sign_send_raw_transaction(obj); - return true; - } - /** - * END SCENARIO #2 - */ - - - - /** - * SCENARIO #3 - * - * zaddr -> taddrs - * -> zaddrs - * - * Send to zaddrs by chaining JoinSplits together and immediately consuming any change - * Send to taddrs by creating dummy z outputs and accumulating value in a change note - * which is used to set vpub_new in the last chained joinsplit. - */ - UniValue obj(UniValue::VOBJ); - CAmount jsChange = 0; // this is updated after each joinsplit - int changeOutputIndex = -1; // this is updated after each joinsplit if jsChange > 0 - bool vpubNewProcessed = false; // updated when vpub_new for miner fee and taddr outputs is set in last joinsplit - CAmount vpubNewTarget = minersFee; - if (t_outputs_total > 0) { - add_taddr_outputs_to_tx(); - vpubNewTarget += t_outputs_total; - } - - // Keep track of treestate within this transaction - boost::unordered_map intermediates; - std::vector previousCommitments; - - while (!vpubNewProcessed) { - AsyncJoinSplitInfo info; - info.vpub_old = 0; - info.vpub_new = 0; - - CAmount jsInputValue = 0; - uint256 jsAnchor; - std::vector> witnesses; - - JSDescription prevJoinSplit; - - // Keep track of previous JoinSplit and its commitments - if (tx_.vjoinsplit.size() > 0) { - prevJoinSplit = tx_.vjoinsplit.back(); - } - - // If there is no change, the chain has terminated so we can reset the tracked treestate. - if (jsChange==0 && tx_.vjoinsplit.size() > 0) { - intermediates.clear(); - previousCommitments.clear(); - } - - // - // Consume change as the first input of the JoinSplit. - // - if (jsChange > 0) { - LOCK2(cs_main, pwalletMain->cs_wallet); - - // Update tree state with previous joinsplit - SproutMerkleTree tree; - auto it = intermediates.find(prevJoinSplit.anchor); - if (it != intermediates.end()) { - tree = it->second; - } else if (!pcoinsTip->GetSproutAnchorAt(prevJoinSplit.anchor, tree)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Could not find previous JoinSplit anchor"); - } - - assert(changeOutputIndex != -1); - boost::optional changeWitness; - int n = 0; - for (const uint256& commitment : prevJoinSplit.commitments) { - tree.append(commitment); - previousCommitments.push_back(commitment); - if (!changeWitness && changeOutputIndex == n++) { - changeWitness = tree.witness(); - } else if (changeWitness) { - changeWitness.get().append(commitment); - } - } - 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(boost::get(spendingkey_).receiving_key()); - auto hSig = prevJoinSplit.h_sig(*pzcashParams, tx_.joinSplitPubKey); - try { - SproutNotePlaintext plaintext = SproutNotePlaintext::decrypt( - decryptor, - prevJoinSplit.ciphertexts[changeOutputIndex], - prevJoinSplit.ephemeralKey, - hSig, - (unsigned char) changeOutputIndex); - - SproutNote note = plaintext.note(boost::get(frompaymentaddress_)); - info.notes.push_back(note); - - jsInputValue += plaintext.value(); - - LogPrint("zrpcunsafe", "%s: spending change (amount=%s)\n", - getId(), - FormatMoney(plaintext.value()) - ); - - } catch (const std::exception& e) { - throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error decrypting output note of previous JoinSplit: %s", e.what())); - } - } - - - // - // Consume spendable non-change notes - // - std::vector vInputNotes; - std::vector vOutPoints; - std::vector> vInputWitnesses; - uint256 inputAnchor; - int numInputsNeeded = (jsChange>0) ? 1 : 0; - while (numInputsNeeded++ < ZC_NUM_JS_INPUTS && zInputsDeque.size() > 0) { - SendManyInputJSOP t = zInputsDeque.front(); - JSOutPoint jso = std::get<0>(t); - SproutNote note = std::get<1>(t); - 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); - - jsInputValue += noteFunds; - - int wtxHeight = -1; - int wtxDepth = -1; - { - LOCK2(cs_main, pwalletMain->cs_wallet); - const CWalletTx& wtx = pwalletMain->mapWallet[jso.hash]; - // Zero-confirmation notes belong to transactions which have not yet been mined - if (mapBlockIndex.find(wtx.hashBlock) == mapBlockIndex.end()) { - throw JSONRPCError(RPC_WALLET_ERROR, strprintf("mapBlockIndex does not contain block hash %s", wtx.hashBlock.ToString())); - } - wtxHeight = komodo_blockheight(wtx.hashBlock); - wtxDepth = wtx.GetDepthInMainChain(); - } - LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, height=%d, confirmations=%d)\n", - getId(), - jso.hash.ToString().substr(0, 10), - jso.js, - int(jso.n), // uint8_t - FormatMoney(noteFunds), - wtxHeight, - wtxDepth - ); - } - - // Add history of previous commitments to witness - if (vInputNotes.size() > 0) { - - if (vInputWitnesses.size()==0) { - throw JSONRPCError(RPC_WALLET_ERROR, "Could not find witness for note commitment"); - } - - for (auto & optionalWitness : vInputWitnesses) { - if (!optionalWitness) { - throw JSONRPCError(RPC_WALLET_ERROR, "Witness for note commitment is null"); - } - SproutWitness w = *optionalWitness; // could use .get(); - if (jsChange > 0) { - for (const uint256& commitment : previousCommitments) { - w.append(commitment); - } - if (jsAnchor != w.root()) { - throw JSONRPCError(RPC_WALLET_ERROR, "Witness for spendable note does not have same anchor as change input"); - } - } - witnesses.push_back(w); - } - - // The jsAnchor is null if this JoinSplit is at the start of a new chain - if (jsAnchor.IsNull()) { - jsAnchor = inputAnchor; - } - - // Add spendable notes as inputs - std::copy(vInputNotes.begin(), vInputNotes.end(), std::back_inserter(info.notes)); - } - - // Find recipient to transfer funds to - std::string address, hexMemo; - CAmount value = 0; - if (zOutputsDeque.size() > 0) { - SendManyRecipient smr = zOutputsDeque.front(); - address = std::get<0>(smr); - value = std::get<1>(smr); - hexMemo = std::get<2>(smr); - zOutputsDeque.pop_front(); - } - - // Reset change - jsChange = 0; - CAmount outAmount = value; - - // Set vpub_new in the last joinsplit (when there are no more notes to spend or zaddr outputs to satisfy) - if (zOutputsDeque.size() == 0 && zInputsDeque.size() == 0) { - assert(!vpubNewProcessed); - if (jsInputValue < vpubNewTarget) { - throw JSONRPCError(RPC_WALLET_ERROR, - strprintf("Insufficient funds for vpub_new %s (miners fee %s, taddr outputs %s)", - FormatMoney(vpubNewTarget), FormatMoney(minersFee), FormatMoney(t_outputs_total))); - } - outAmount += vpubNewTarget; - info.vpub_new += vpubNewTarget; // funds flowing back to public pool - vpubNewProcessed = true; - jsChange = jsInputValue - outAmount; - assert(jsChange >= 0); - } - else { - // This is not the last joinsplit, so compute change and any amount still due to the recipient - if (jsInputValue > outAmount) { - jsChange = jsInputValue - outAmount; - } else if (outAmount > jsInputValue) { - // Any amount due is owed to the recipient. Let the miners fee get paid first. - CAmount due = outAmount - jsInputValue; - SendManyRecipient r = SendManyRecipient(address, due, hexMemo); - zOutputsDeque.push_front(r); - - // reduce the amount being sent right now to the value of all inputs - value = jsInputValue; - } - } - - // create output for recipient - if (address.empty()) { - assert(value==0); - info.vjsout.push_back(JSOutput()); // dummy output while we accumulate funds into a change note for vpub_new - } else { - PaymentAddress pa = DecodePaymentAddress(address); - // If we are here, we know we have no Sapling outputs. - JSOutput jso = JSOutput(boost::get(pa), value); - if (hexMemo.size() > 0) { - jso.memo = get_memo_from_hex_string(hexMemo); - } - info.vjsout.push_back(jso); - } - - // create output for any change - if (jsChange>0) { - info.vjsout.push_back(JSOutput(boost::get(frompaymentaddress_), jsChange)); - - LogPrint("zrpcunsafe", "%s: generating note for change (amount=%s)\n", - getId(), - FormatMoney(jsChange) - ); - } - - obj = perform_joinsplit(info, witnesses, jsAnchor); - - if (jsChange > 0) { - changeOutputIndex = find_output(obj, 1); - } - } - - // Sanity check in case changes to code block above exits loop by invoking 'break' - assert(zInputsDeque.size() == 0); - assert(zOutputsDeque.size() == 0); - assert(vpubNewProcessed); - - sign_send_raw_transaction(obj); - return true; + return false; } @@ -1143,19 +720,6 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() { saplingEntries.clear(); } - for (CSproutNotePlaintextEntry & entry : sproutEntries) { - z_sprout_inputs_.push_back(SendManyInputJSOP(entry.jsop, entry.plaintext.note(boost::get(frompaymentaddress_)), CAmount(entry.plaintext.value()))); - std::string data(entry.plaintext.memo().begin(), entry.plaintext.memo().end()); - LogPrint("zrpcunsafe", "%s: found unspent Sprout note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, memo=%s)\n", - getId(), - entry.jsop.hash.ToString().substr(0, 10), - entry.jsop.js, - int(entry.jsop.n), // uint8_t - FormatMoney(entry.plaintext.value()), - HexStr(data).substr(0, 10) - ); - } - for (auto entry : saplingEntries) { z_sapling_inputs_.push_back(entry); std::string data(entry.memo.begin(), entry.memo.end()); @@ -1167,15 +731,7 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() { HexStr(data).substr(0, 10)); } - if (z_sprout_inputs_.empty() && z_sapling_inputs_.empty()) { - return false; - } - // sort in descending order, so big notes appear first - std::sort(z_sprout_inputs_.begin(), z_sprout_inputs_.end(), - [](SendManyInputJSOP i, SendManyInputJSOP j) -> bool { - return std::get<2>(i) > std::get<2>(j); - }); std::sort(z_sapling_inputs_.begin(), z_sapling_inputs_.end(), [](SaplingNoteEntry i, SaplingNoteEntry j) -> bool { return i.note.value() > j.note.value(); @@ -1184,189 +740,6 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() { return true; } -UniValue AsyncRPCOperation_sendmany::perform_joinsplit(AsyncJoinSplitInfo & info) { - std::vector> witnesses; - uint256 anchor; - { - LOCK(cs_main); - anchor = pcoinsTip->GetBestAnchor(SPROUT); // As there are no inputs, ask the wallet for the best anchor - } - return perform_joinsplit(info, witnesses, anchor); -} - - -UniValue AsyncRPCOperation_sendmany::perform_joinsplit(AsyncJoinSplitInfo & info, std::vector & outPoints) { - std::vector> witnesses; - uint256 anchor; - { - LOCK(cs_main); - pwalletMain->GetSproutNoteWitnesses(outPoints, witnesses, anchor); - } - return perform_joinsplit(info, witnesses, anchor); -} - -UniValue AsyncRPCOperation_sendmany::perform_joinsplit( - AsyncJoinSplitInfo & info, - std::vector> witnesses, - uint256 anchor) -{ - if (anchor.IsNull()) { - throw std::runtime_error("anchor is null"); - } - - if (!(witnesses.size() == info.notes.size())) { - throw runtime_error("number of notes and witnesses do not match"); - } - - for (size_t i = 0; i < witnesses.size(); i++) { - if (!witnesses[i]) { - throw runtime_error("joinsplit input could not be found in tree"); - } - info.vjsin.push_back(JSInput(*witnesses[i], info.notes[i], boost::get(spendingkey_))); - } - - // Make sure there are two inputs and two outputs - while (info.vjsin.size() < ZC_NUM_JS_INPUTS) { - info.vjsin.push_back(JSInput()); - } - - while (info.vjsout.size() < ZC_NUM_JS_OUTPUTS) { - info.vjsout.push_back(JSOutput()); - } - - if (info.vjsout.size() != ZC_NUM_JS_INPUTS || info.vjsin.size() != ZC_NUM_JS_OUTPUTS) { - throw runtime_error("unsupported joinsplit input/output counts"); - } - - CMutableTransaction mtx(tx_); - - LogPrint("zrpcunsafe", "%s: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n", - getId(), - tx_.vjoinsplit.size(), - FormatMoney(info.vpub_old), FormatMoney(info.vpub_new), - FormatMoney(info.vjsin[0].note.value()), FormatMoney(info.vjsin[1].note.value()), - FormatMoney(info.vjsout[0].value), FormatMoney(info.vjsout[1].value) - ); - - // Generate the proof, this can take over a minute. - std::array inputs - {info.vjsin[0], info.vjsin[1]}; - std::array outputs - {info.vjsout[0], info.vjsout[1]}; - std::array inputMap; - std::array outputMap; - uint256 esk; // payment disclosure - secret - - JSDescription jsdesc = JSDescription::Randomized( - *pzcashParams, - joinSplitPubKey_, - anchor, - inputs, - outputs, - inputMap, - outputMap, - info.vpub_old, - info.vpub_new, - !this->testmode, - &esk); // parameter expects pointer to esk, so pass in address - { - auto verifier = libzcash::ProofVerifier::Strict(); - if (!(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey_))) { - throw std::runtime_error("error verifying joinsplit"); - } - } - - mtx.vjoinsplit.push_back(jsdesc); - - // Empty output script. - CScript scriptCode; - CTransaction signTx(mtx); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId_); - - // Add the signature - if (!(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, - dataToBeSigned.begin(), 32, - joinSplitPrivKey_ - ) == 0)) - { - throw std::runtime_error("crypto_sign_detached failed"); - } - - // Sanity check - if (!(crypto_sign_verify_detached(&mtx.joinSplitSig[0], - dataToBeSigned.begin(), 32, - mtx.joinSplitPubKey.begin() - ) == 0)) - { - throw std::runtime_error("crypto_sign_verify_detached failed"); - } - - CTransaction rawTx(mtx); - tx_ = rawTx; - - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << rawTx; - - std::string encryptedNote1; - std::string encryptedNote2; - { - CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); - ss2 << ((unsigned char) 0x00); - ss2 << jsdesc.ephemeralKey; - ss2 << jsdesc.ciphertexts[0]; - ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); - - encryptedNote1 = HexStr(ss2.begin(), ss2.end()); - } - { - CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); - ss2 << ((unsigned char) 0x01); - ss2 << jsdesc.ephemeralKey; - ss2 << jsdesc.ciphertexts[1]; - ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); - - encryptedNote2 = HexStr(ss2.begin(), ss2.end()); - } - - UniValue arrInputMap(UniValue::VARR); - UniValue arrOutputMap(UniValue::VARR); - for (size_t i = 0; i < ZC_NUM_JS_INPUTS; i++) { - arrInputMap.push_back(static_cast(inputMap[i])); - } - for (size_t i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { - arrOutputMap.push_back(static_cast(outputMap[i])); - } - - - // !!! Payment disclosure START - unsigned char buffer[32] = {0}; - memcpy(&buffer[0], &joinSplitPrivKey_[0], 32); // private key in first half of 64 byte buffer - std::vector vch(&buffer[0], &buffer[0] + 32); - uint256 joinSplitPrivKey = uint256(vch); - size_t js_index = tx_.vjoinsplit.size() - 1; - uint256 placeholder; - for (int i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { - uint8_t mapped_index = outputMap[i]; - // placeholder for txid will be filled in later when tx has been finalized and signed. - PaymentDisclosureKey pdKey = {placeholder, js_index, mapped_index}; - JSOutput output = outputs[mapped_index]; - libzcash::SproutPaymentAddress zaddr = output.addr; // randomized output - PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr}; - paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo)); - - LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), EncodePaymentAddress(zaddr)); - } - // !!! Payment disclosure END - - UniValue obj(UniValue::VOBJ); - 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; -} - void AsyncRPCOperation_sendmany::add_taddr_outputs_to_tx() { CMutableTransaction rawTx(tx_);