]> Git Repo - VerusCoin.git/blob - src/wallet/asyncrpcoperation_shieldcoinbase.cpp
Refactoring: SproutNote member variable value moved to BaseNote.
[VerusCoin.git] / src / wallet / asyncrpcoperation_shieldcoinbase.cpp
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.
4
5 #include "asyncrpcqueue.h"
6 #include "amount.h"
7 #include "consensus/upgrades.h"
8 #include "core_io.h"
9 #include "init.h"
10 #include "main.h"
11 #include "net.h"
12 #include "netbase.h"
13 #include "rpcserver.h"
14 #include "timedata.h"
15 #include "util.h"
16 #include "utilmoneystr.h"
17 #include "wallet.h"
18 #include "walletdb.h"
19 #include "script/interpreter.h"
20 #include "utiltime.h"
21 #include "rpcprotocol.h"
22 #include "zcash/IncrementalMerkleTree.hpp"
23 #include "sodium.h"
24 #include "miner.h"
25
26 #include <iostream>
27 #include <chrono>
28 #include <thread>
29 #include <string>
30
31 #include "asyncrpcoperation_shieldcoinbase.h"
32
33 #include "paymentdisclosure.h"
34 #include "paymentdisclosuredb.h"
35
36 using namespace libzcash;
37
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");
42     }
43
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) {
48             return i;
49         }
50     }
51
52     throw std::logic_error("n is not present in outputmap");
53 }
54
55 AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase(
56         CMutableTransaction contextualTx,
57         std::vector<ShieldCoinbaseUTXO> inputs,
58         std::string toAddress,
59         CAmount fee,
60         UniValue contextInfo) :
61         tx_(contextualTx), inputs_(inputs), fee_(fee), contextinfo_(contextInfo)
62 {
63     assert(contextualTx.nVersion >= 2);  // transaction format version must support vjoinsplit
64
65     if (fee < 0 || fee > MAX_MONEY) {
66         throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee is out of range");
67     }
68
69     if (inputs.size() == 0) {
70         throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Empty inputs");
71     }
72
73     //  Check the destination address is valid for this network i.e. not testnet being used on mainnet
74     CZCPaymentAddress address(toAddress);
75     try {
76         tozaddr_ = address.Get();
77     } catch (const std::runtime_error& e) {
78         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("runtime error: ") + e.what());
79     }
80
81     // Log the context info
82     if (LogAcceptCategory("zrpcunsafe")) {
83         LogPrint("zrpcunsafe", "%s: z_shieldcoinbase initialized (context=%s)\n", getId(), contextInfo.write());
84     } else {
85         LogPrint("zrpc", "%s: z_shieldcoinbase initialized\n", getId());
86     }
87
88     // Lock UTXOs
89     lock_utxos();
90
91     // Enable payment disclosure if requested
92     paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", false);
93 }
94
95 AsyncRPCOperation_shieldcoinbase::~AsyncRPCOperation_shieldcoinbase() {
96 }
97
98 void AsyncRPCOperation_shieldcoinbase::main() {
99     if (isCancelled()) {
100         unlock_utxos(); // clean up
101         return;
102     }
103
104     set_state(OperationStatus::EXECUTING);
105     start_execution_clock();
106
107     bool success = false;
108
109 #ifdef ENABLE_MINING
110   #ifdef ENABLE_WALLET
111     GenerateBitcoins(false, NULL, 0);
112   #else
113     GenerateBitcoins(false, 0);
114   #endif
115 #endif
116
117     try {
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) {
125         set_error_code(-1);
126         set_error_message("runtime error: " + string(e.what()));
127     } catch (const logic_error& e) {
128         set_error_code(-1);
129         set_error_message("logic error: " + string(e.what()));
130     } catch (const exception& e) {
131         set_error_code(-1);
132         set_error_message("general exception: " + string(e.what()));
133     } catch (...) {
134         set_error_code(-2);
135         set_error_message("unknown error");
136     }
137
138 #ifdef ENABLE_MINING
139   #ifdef ENABLE_WALLET
140     GenerateBitcoins(GetBoolArg("-gen",false), pwalletMain, GetArg("-genproclimit", 1));
141   #else
142     GenerateBitcoins(GetBoolArg("-gen",false), GetArg("-genproclimit", 1));
143   #endif
144 #endif
145
146     stop_execution_clock();
147
148     if (success) {
149         set_state(OperationStatus::SUCCESS);
150     } else {
151         set_state(OperationStatus::FAILED);
152     }
153
154     std::string s = strprintf("%s: z_shieldcoinbase finished (status=%s", getId(), getStateAsString());
155     if (success) {
156         s += strprintf(", txid=%s)\n", tx_.GetHash().ToString());
157     } else {
158         s += strprintf(", error=%s)\n", getErrorMessage());
159     }
160     LogPrintf("%s",s);
161
162     unlock_utxos(); // clean up
163
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());
172             } else {
173                 LogPrint("paymentdisclosure", "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString());
174             }
175         }
176     }
177     // !!! Payment disclosure END
178 }
179
180
181 bool AsyncRPCOperation_shieldcoinbase::main_impl() {
182
183     CAmount minersFee = fee_;
184
185     size_t numInputs = inputs_.size();
186
187     // Check mempooltxinputlimit to avoid creating a transaction which the local mempool rejects
188     size_t limit = (size_t)GetArg("-mempooltxinputlimit", 0);
189     {
190         LOCK(cs_main);
191         if (NetworkUpgradeActive(chainActive.Height() + 1, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) {
192             limit = 0;
193         }
194     }
195     if (limit>0 && numInputs > limit) {
196         throw JSONRPCError(RPC_WALLET_ERROR,
197             strprintf("Number of inputs %d is greater than mempooltxinputlimit of %d",
198             numInputs, limit));
199     }
200
201     CAmount targetAmount = 0;
202     for (ShieldCoinbaseUTXO & utxo : inputs_) {
203         targetAmount += utxo.amount;
204     }
205
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)));
210     }
211
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));
215
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);
221     }
222     tx_ = CTransaction(rawTx);
223
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);
229
230     // Create joinsplit
231     UniValue obj(UniValue::VOBJ);
232     ShieldCoinbaseJSInfo info;
233     info.vpub_old = sendAmount;
234     info.vpub_new = 0;
235     JSOutput jso = JSOutput(tozaddr_, sendAmount);
236     info.vjsout.push_back(jso);
237     obj = perform_joinsplit(info);
238
239     sign_send_raw_transaction(obj);
240     return true;
241 }
242
243
244 /**
245  * Sign and send a raw transaction.
246  * Raw transaction as hex string should be in object field "rawtxn"
247  */
248 void AsyncRPCOperation_shieldcoinbase::sign_send_raw_transaction(UniValue obj)
249 {
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");
254     }
255     std::string rawtxn = rawtxnValue.get_str();
256
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();
263     if (!complete) {
264         // TODO: #1366 Maybe get "errors" and print array vErrors into a string
265         throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Failed to sign transaction");
266     }
267
268     UniValue hexValue = find_value(signResultObject, "hex");
269     if (hexValue.isNull()) {
270         throw JSONRPCError(RPC_WALLET_ERROR, "Missing hex data for signed transaction");
271     }
272     std::string signedtxn = hexValue.get_str();
273
274     // Send the signed transaction
275     if (!testmode) {
276         params.clear();
277         params.setArray();
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.");
282         }
283
284         std::string txid = sendResultValue.get_str();
285
286         UniValue o(UniValue::VOBJ);
287         o.push_back(Pair("txid", txid));
288         set_result(o);
289     } else {
290         // Test mode does not send the transaction to the network.
291
292         CDataStream stream(ParseHex(signedtxn), SER_NETWORK, PROTOCOL_VERSION);
293         CTransaction tx;
294         stream >> tx;
295
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));
300         set_result(o);
301     }
302
303     // Keep the signed transaction so we can hash to the same txid
304     CDataStream stream(ParseHex(signedtxn), SER_NETWORK, PROTOCOL_VERSION);
305     CTransaction tx;
306     stream >> tx;
307     tx_ = tx;
308 }
309
310
311 UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInfo & info) {
312     uint32_t consensusBranchId;
313     uint256 anchor;
314     {
315         LOCK(cs_main);
316         consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus());
317         anchor = pcoinsTip->GetBestAnchor();
318     }
319
320
321     if (anchor.IsNull()) {
322         throw std::runtime_error("anchor is null");
323     }
324
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());
328     }
329
330     while (info.vjsout.size() < ZC_NUM_JS_OUTPUTS) {
331         info.vjsout.push_back(JSOutput());
332     }
333
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");
336     }
337
338     CMutableTransaction mtx(tx_);
339
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",
341             getId(),
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)
346             );
347
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;
355
356     uint256 esk; // payment disclosure - secret
357
358     JSDescription jsdesc = JSDescription::Randomized(
359             *pzcashParams,
360             joinSplitPubKey_,
361             anchor,
362             inputs,
363             outputs,
364             inputMap,
365             outputMap,
366             info.vpub_old,
367             info.vpub_new,
368             !this->testmode,
369             &esk); // parameter expects pointer to esk, so pass in address
370     {
371         auto verifier = libzcash::ProofVerifier::Strict();
372         if (!(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey_))) {
373             throw std::runtime_error("error verifying joinsplit");
374         }
375     }
376
377     mtx.vjoinsplit.push_back(jsdesc);
378
379     // Empty output script.
380     CScript scriptCode;
381     CTransaction signTx(mtx);
382     uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId);
383
384     // Add the signature
385     if (!(crypto_sign_detached(&mtx.joinSplitSig[0], NULL,
386             dataToBeSigned.begin(), 32,
387             joinSplitPrivKey_
388             ) == 0))
389     {
390         throw std::runtime_error("crypto_sign_detached failed");
391     }
392
393     // Sanity check
394     if (!(crypto_sign_verify_detached(&mtx.joinSplitSig[0],
395             dataToBeSigned.begin(), 32,
396             mtx.joinSplitPubKey.begin()
397             ) == 0))
398     {
399         throw std::runtime_error("crypto_sign_verify_detached failed");
400     }
401
402     CTransaction rawTx(mtx);
403     tx_ = rawTx;
404
405     CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
406     ss << rawTx;
407
408     std::string encryptedNote1;
409     std::string encryptedNote2;
410     {
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_);
416
417         encryptedNote1 = HexStr(ss2.begin(), ss2.end());
418     }
419     {
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_);
425
426         encryptedNote2 = HexStr(ss2.begin(), ss2.end());
427     }
428
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]));
433     }
434     for (size_t i = 0; i < ZC_NUM_JS_OUTPUTS; i++) {
435         arrOutputMap.push_back(static_cast<uint64_t>(outputMap[i]));
436     }
437
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;
444     uint256 placeholder;
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));
453
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());
456     }
457     // !!! Payment disclosure END
458
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));
465     return obj;
466 }
467
468 /**
469  * Override getStatus() to append the operation's context object to the default status object.
470  */
471 UniValue AsyncRPCOperation_shieldcoinbase::getStatus() const {
472     UniValue v = AsyncRPCOperation::getStatus();
473     if (contextinfo_.isNull()) {
474         return v;
475     }
476
477     UniValue obj = v.get_obj();
478     obj.push_back(Pair("method", "z_shieldcoinbase"));
479     obj.push_back(Pair("params", contextinfo_ ));
480     return obj;
481 }
482
483 /**
484  * Lock input utxos
485  */
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);
491     }
492 }
493
494 /**
495  * Unlock input utxos
496  */
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);
502     }
503 }
This page took 0.052903 seconds and 4 git commands to generate.