1 // Copyright (c) 2017 The Zcash developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 #include "asyncrpcqueue.h"
7 #include "consensus/upgrades.h"
13 #include "rpcserver.h"
16 #include "utilmoneystr.h"
19 #include "script/interpreter.h"
21 #include "rpcprotocol.h"
22 #include "zcash/IncrementalMerkleTree.hpp"
31 #include "asyncrpcoperation_shieldcoinbase.h"
33 #include "paymentdisclosure.h"
34 #include "paymentdisclosuredb.h"
36 using namespace libzcash;
38 static int find_output(UniValue obj, int n) {
39 UniValue outputMapValue = find_value(obj, "outputmap");
40 if (!outputMapValue.isArray()) {
41 throw JSONRPCError(RPC_WALLET_ERROR, "Missing outputmap for JoinSplit operation");
44 UniValue outputMap = outputMapValue.get_array();
45 assert(outputMap.size() == ZC_NUM_JS_OUTPUTS);
46 for (size_t i = 0; i < outputMap.size(); i++) {
47 if (outputMap[i].get_int() == n) {
52 throw std::logic_error("n is not present in outputmap");
55 AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase(
56 CMutableTransaction contextualTx,
57 std::vector<ShieldCoinbaseUTXO> inputs,
58 std::string toAddress,
60 UniValue contextInfo) :
61 tx_(contextualTx), inputs_(inputs), fee_(fee), contextinfo_(contextInfo)
63 assert(contextualTx.nVersion >= 2); // transaction format version must support vjoinsplit
65 if (fee < 0 || fee > MAX_MONEY) {
66 throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee is out of range");
69 if (inputs.size() == 0) {
70 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Empty inputs");
73 // Check the destination address is valid for this network i.e. not testnet being used on mainnet
74 CZCPaymentAddress address(toAddress);
76 tozaddr_ = address.Get();
77 } catch (const std::runtime_error& e) {
78 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("runtime error: ") + e.what());
81 // Log the context info
82 if (LogAcceptCategory("zrpcunsafe")) {
83 LogPrint("zrpcunsafe", "%s: z_shieldcoinbase initialized (context=%s)\n", getId(), contextInfo.write());
85 LogPrint("zrpc", "%s: z_shieldcoinbase initialized\n", getId());
91 // Enable payment disclosure if requested
92 paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", false);
95 AsyncRPCOperation_shieldcoinbase::~AsyncRPCOperation_shieldcoinbase() {
98 void AsyncRPCOperation_shieldcoinbase::main() {
100 unlock_utxos(); // clean up
104 set_state(OperationStatus::EXECUTING);
105 start_execution_clock();
107 bool success = false;
111 GenerateBitcoins(false, NULL, 0);
113 GenerateBitcoins(false, 0);
118 success = main_impl();
119 } catch (const UniValue& objError) {
120 int code = find_value(objError, "code").get_int();
121 std::string message = find_value(objError, "message").get_str();
122 set_error_code(code);
123 set_error_message(message);
124 } catch (const runtime_error& e) {
126 set_error_message("runtime error: " + string(e.what()));
127 } catch (const logic_error& e) {
129 set_error_message("logic error: " + string(e.what()));
130 } catch (const exception& e) {
132 set_error_message("general exception: " + string(e.what()));
135 set_error_message("unknown error");
140 GenerateBitcoins(GetBoolArg("-gen",false), pwalletMain, GetArg("-genproclimit", 1));
142 GenerateBitcoins(GetBoolArg("-gen",false), GetArg("-genproclimit", 1));
146 stop_execution_clock();
149 set_state(OperationStatus::SUCCESS);
151 set_state(OperationStatus::FAILED);
154 std::string s = strprintf("%s: z_shieldcoinbase finished (status=%s", getId(), getStateAsString());
156 s += strprintf(", txid=%s)\n", tx_.GetHash().ToString());
158 s += strprintf(", error=%s)\n", getErrorMessage());
162 unlock_utxos(); // clean up
164 // !!! Payment disclosure START
165 if (success && paymentDisclosureMode && paymentDisclosureData_.size()>0) {
166 uint256 txidhash = tx_.GetHash();
167 std::shared_ptr<PaymentDisclosureDB> db = PaymentDisclosureDB::sharedInstance();
168 for (PaymentDisclosureKeyInfo p : paymentDisclosureData_) {
169 p.first.hash = txidhash;
170 if (!db->Put(p.first, p.second)) {
171 LogPrint("paymentdisclosure", "%s: Payment Disclosure: Error writing entry to database for key %s\n", getId(), p.first.ToString());
173 LogPrint("paymentdisclosure", "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString());
177 // !!! Payment disclosure END
181 bool AsyncRPCOperation_shieldcoinbase::main_impl() {
183 CAmount minersFee = fee_;
185 size_t numInputs = inputs_.size();
187 // Check mempooltxinputlimit to avoid creating a transaction which the local mempool rejects
188 size_t limit = (size_t)GetArg("-mempooltxinputlimit", 0);
191 if (NetworkUpgradeActive(chainActive.Height() + 1, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) {
195 if (limit>0 && numInputs > limit) {
196 throw JSONRPCError(RPC_WALLET_ERROR,
197 strprintf("Number of inputs %d is greater than mempooltxinputlimit of %d",
201 CAmount targetAmount = 0;
202 for (ShieldCoinbaseUTXO & utxo : inputs_) {
203 targetAmount += utxo.amount;
206 if (targetAmount <= minersFee) {
207 throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS,
208 strprintf("Insufficient coinbase funds, have %s and miners fee is %s",
209 FormatMoney(targetAmount), FormatMoney(minersFee)));
212 CAmount sendAmount = targetAmount - minersFee;
213 LogPrint("zrpc", "%s: spending %s to shield %s with fee %s\n",
214 getId(), FormatMoney(targetAmount), FormatMoney(sendAmount), FormatMoney(minersFee));
216 // update the transaction with these inputs
217 CMutableTransaction rawTx(tx_);
218 for (ShieldCoinbaseUTXO & t : inputs_) {
219 CTxIn in(COutPoint(t.txid, t.vout));
220 rawTx.vin.push_back(in);
222 tx_ = CTransaction(rawTx);
224 // Prepare raw transaction to handle JoinSplits
225 CMutableTransaction mtx(tx_);
226 crypto_sign_keypair(joinSplitPubKey_.begin(), joinSplitPrivKey_);
227 mtx.joinSplitPubKey = joinSplitPubKey_;
228 tx_ = CTransaction(mtx);
231 UniValue obj(UniValue::VOBJ);
232 ShieldCoinbaseJSInfo info;
233 info.vpub_old = sendAmount;
235 JSOutput jso = JSOutput(tozaddr_, sendAmount);
236 info.vjsout.push_back(jso);
237 obj = perform_joinsplit(info);
239 sign_send_raw_transaction(obj);
245 * Sign and send a raw transaction.
246 * Raw transaction as hex string should be in object field "rawtxn"
248 void AsyncRPCOperation_shieldcoinbase::sign_send_raw_transaction(UniValue obj)
250 // Sign the raw transaction
251 UniValue rawtxnValue = find_value(obj, "rawtxn");
252 if (rawtxnValue.isNull()) {
253 throw JSONRPCError(RPC_WALLET_ERROR, "Missing hex data for raw transaction");
255 std::string rawtxn = rawtxnValue.get_str();
257 UniValue params = UniValue(UniValue::VARR);
258 params.push_back(rawtxn);
259 UniValue signResultValue = signrawtransaction(params, false);
260 UniValue signResultObject = signResultValue.get_obj();
261 UniValue completeValue = find_value(signResultObject, "complete");
262 bool complete = completeValue.get_bool();
264 // TODO: #1366 Maybe get "errors" and print array vErrors into a string
265 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Failed to sign transaction");
268 UniValue hexValue = find_value(signResultObject, "hex");
269 if (hexValue.isNull()) {
270 throw JSONRPCError(RPC_WALLET_ERROR, "Missing hex data for signed transaction");
272 std::string signedtxn = hexValue.get_str();
274 // Send the signed transaction
278 params.push_back(signedtxn);
279 UniValue sendResultValue = sendrawtransaction(params, false);
280 if (sendResultValue.isNull()) {
281 throw JSONRPCError(RPC_WALLET_ERROR, "Send raw transaction did not return an error or a txid.");
284 std::string txid = sendResultValue.get_str();
286 UniValue o(UniValue::VOBJ);
287 o.push_back(Pair("txid", txid));
290 // Test mode does not send the transaction to the network.
292 CDataStream stream(ParseHex(signedtxn), SER_NETWORK, PROTOCOL_VERSION);
296 UniValue o(UniValue::VOBJ);
297 o.push_back(Pair("test", 1));
298 o.push_back(Pair("txid", tx.GetHash().ToString()));
299 o.push_back(Pair("hex", signedtxn));
303 // Keep the signed transaction so we can hash to the same txid
304 CDataStream stream(ParseHex(signedtxn), SER_NETWORK, PROTOCOL_VERSION);
311 UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInfo & info) {
312 uint32_t consensusBranchId;
316 consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus());
317 anchor = pcoinsTip->GetBestAnchor();
321 if (anchor.IsNull()) {
322 throw std::runtime_error("anchor is null");
325 // Make sure there are two inputs and two outputs
326 while (info.vjsin.size() < ZC_NUM_JS_INPUTS) {
327 info.vjsin.push_back(JSInput());
330 while (info.vjsout.size() < ZC_NUM_JS_OUTPUTS) {
331 info.vjsout.push_back(JSOutput());
334 if (info.vjsout.size() != ZC_NUM_JS_INPUTS || info.vjsin.size() != ZC_NUM_JS_OUTPUTS) {
335 throw runtime_error("unsupported joinsplit input/output counts");
338 CMutableTransaction mtx(tx_);
340 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",
342 tx_.vjoinsplit.size(),
343 FormatMoney(info.vpub_old), FormatMoney(info.vpub_new),
344 FormatMoney(info.vjsin[0].note.value()), FormatMoney(info.vjsin[1].note.value()),
345 FormatMoney(info.vjsout[0].value), FormatMoney(info.vjsout[1].value)
348 // Generate the proof, this can take over a minute.
349 boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS> inputs
350 {info.vjsin[0], info.vjsin[1]};
351 boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS> outputs
352 {info.vjsout[0], info.vjsout[1]};
353 boost::array<size_t, ZC_NUM_JS_INPUTS> inputMap;
354 boost::array<size_t, ZC_NUM_JS_OUTPUTS> outputMap;
356 uint256 esk; // payment disclosure - secret
358 JSDescription jsdesc = JSDescription::Randomized(
369 &esk); // parameter expects pointer to esk, so pass in address
371 auto verifier = libzcash::ProofVerifier::Strict();
372 if (!(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey_))) {
373 throw std::runtime_error("error verifying joinsplit");
377 mtx.vjoinsplit.push_back(jsdesc);
379 // Empty output script.
381 CTransaction signTx(mtx);
382 uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId);
385 if (!(crypto_sign_detached(&mtx.joinSplitSig[0], NULL,
386 dataToBeSigned.begin(), 32,
390 throw std::runtime_error("crypto_sign_detached failed");
394 if (!(crypto_sign_verify_detached(&mtx.joinSplitSig[0],
395 dataToBeSigned.begin(), 32,
396 mtx.joinSplitPubKey.begin()
399 throw std::runtime_error("crypto_sign_verify_detached failed");
402 CTransaction rawTx(mtx);
405 CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
408 std::string encryptedNote1;
409 std::string encryptedNote2;
411 CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION);
412 ss2 << ((unsigned char) 0x00);
413 ss2 << jsdesc.ephemeralKey;
414 ss2 << jsdesc.ciphertexts[0];
415 ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_);
417 encryptedNote1 = HexStr(ss2.begin(), ss2.end());
420 CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION);
421 ss2 << ((unsigned char) 0x01);
422 ss2 << jsdesc.ephemeralKey;
423 ss2 << jsdesc.ciphertexts[1];
424 ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_);
426 encryptedNote2 = HexStr(ss2.begin(), ss2.end());
429 UniValue arrInputMap(UniValue::VARR);
430 UniValue arrOutputMap(UniValue::VARR);
431 for (size_t i = 0; i < ZC_NUM_JS_INPUTS; i++) {
432 arrInputMap.push_back(static_cast<uint64_t>(inputMap[i]));
434 for (size_t i = 0; i < ZC_NUM_JS_OUTPUTS; i++) {
435 arrOutputMap.push_back(static_cast<uint64_t>(outputMap[i]));
438 // !!! Payment disclosure START
439 unsigned char buffer[32] = {0};
440 memcpy(&buffer[0], &joinSplitPrivKey_[0], 32); // private key in first half of 64 byte buffer
441 std::vector<unsigned char> vch(&buffer[0], &buffer[0] + 32);
442 uint256 joinSplitPrivKey = uint256(vch);
443 size_t js_index = tx_.vjoinsplit.size() - 1;
445 for (int i = 0; i < ZC_NUM_JS_OUTPUTS; i++) {
446 uint8_t mapped_index = outputMap[i];
447 // placeholder for txid will be filled in later when tx has been finalized and signed.
448 PaymentDisclosureKey pdKey = {placeholder, js_index, mapped_index};
449 JSOutput output = outputs[mapped_index];
450 libzcash::PaymentAddress zaddr = output.addr; // randomized output
451 PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr};
452 paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo));
454 CZCPaymentAddress address(zaddr);
455 LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), address.ToString());
457 // !!! Payment disclosure END
459 UniValue obj(UniValue::VOBJ);
460 obj.push_back(Pair("encryptednote1", encryptedNote1));
461 obj.push_back(Pair("encryptednote2", encryptedNote2));
462 obj.push_back(Pair("rawtxn", HexStr(ss.begin(), ss.end())));
463 obj.push_back(Pair("inputmap", arrInputMap));
464 obj.push_back(Pair("outputmap", arrOutputMap));
469 * Override getStatus() to append the operation's context object to the default status object.
471 UniValue AsyncRPCOperation_shieldcoinbase::getStatus() const {
472 UniValue v = AsyncRPCOperation::getStatus();
473 if (contextinfo_.isNull()) {
477 UniValue obj = v.get_obj();
478 obj.push_back(Pair("method", "z_shieldcoinbase"));
479 obj.push_back(Pair("params", contextinfo_ ));
486 void AsyncRPCOperation_shieldcoinbase::lock_utxos() {
487 LOCK2(cs_main, pwalletMain->cs_wallet);
488 for (auto utxo : inputs_) {
489 COutPoint outpt(utxo.txid, utxo.vout);
490 pwalletMain->LockCoin(outpt);
497 void AsyncRPCOperation_shieldcoinbase::unlock_utxos() {
498 LOCK2(cs_main, pwalletMain->cs_wallet);
499 for (auto utxo : inputs_) {
500 COutPoint outpt(utxo.txid, utxo.vout);
501 pwalletMain->UnlockCoin(outpt);