Hush Full Node software. We were censored from Github, this is where all development happens now. https://hush.is
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

319 lines
12 KiB

// Copyright (c) 2017 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
/******************************************************************************
* Copyright © 2014-2019 The SuperNET Developers. *
* *
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
* the top-level directory of this distribution for the individual copyright *
* holder information and the developer policies on copyright and licensing. *
* *
* Unless otherwise agreed in a custom licensing agreement, no part of the *
* SuperNET software, including this file may be copied, modified, propagated *
* or distributed except according to the terms contained in the LICENSE file *
* *
* Removal or modification of this copyright notice is prohibited. *
* *
******************************************************************************/
#include "rpc/server.h"
#include "init.h"
#include "key_io.h"
#include "main.h"
#include "script/script.h"
#include "script/standard.h"
#include "sync.h"
#include "util.h"
#include "utiltime.h"
#include "wallet.h"
#include <fstream>
#include <stdint.h>
#include <boost/algorithm/string.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <univalue.h>
#include "paymentdisclosure.h"
#include "paymentdisclosuredb.h"
#include "zcash/Note.hpp"
#include "zcash/NoteEncryption.hpp"
using namespace std;
using namespace libzcash;
// Function declaration for function implemented in wallet/rpcwallet.cpp
bool EnsureWalletIsAvailable(bool avoidException);
/**
* RPC call to generate a payment disclosure
*/
UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp)
{
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
string enableArg = "paymentdisclosure";
auto fEnablePaymentDisclosure = fExperimentalMode && GetBoolArg("-" + enableArg, true);
string strPaymentDisclosureDisabledMsg = "";
if (!fEnablePaymentDisclosure) {
strPaymentDisclosureDisabledMsg = experimentalDisabledHelpMsg("z_getpaymentdisclosure", enableArg);
}
if (fHelp || params.size() < 3 || params.size() > 4 )
throw runtime_error(
"z_getpaymentdisclosure \"txid\" \"js_index\" \"output_index\" (\"message\") \n"
"\nGenerate a payment disclosure for a given joinsplit output.\n"
"\nEXPERIMENTAL FEATURE\n"
+ strPaymentDisclosureDisabledMsg +
"\nArguments:\n"
"1. \"txid\" (string, required) \n"
"2. \"js_index\" (string, required) \n"
"3. \"output_index\" (string, required) \n"
"4. \"message\" (string, optional) \n"
"\nResult:\n"
"\"paymentdisclosure\" (string) Hex data string, with \"zpd:\" prefix.\n"
"\nExamples:\n"
+ HelpExampleCli("z_getpaymentdisclosure", "96f12882450429324d5f3b48630e3168220e49ab7b0f066e5c2935a6b88bb0f2 0 0 \"refund\"")
+ HelpExampleRpc("z_getpaymentdisclosure", "\"96f12882450429324d5f3b48630e3168220e49ab7b0f066e5c2935a6b88bb0f2\", 0, 0, \"refund\"")
);
if (!fEnablePaymentDisclosure) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: payment disclosure is disabled.");
}
LOCK2(cs_main, pwalletMain->cs_wallet);
EnsureWalletIsUnlocked();
// Check wallet knows about txid
string txid = params[0].get_str();
uint256 hash;
hash.SetHex(txid);
CTransaction tx;
uint256 hashBlock;
// Check txid has been seen
if (!GetTransaction(hash, tx, hashBlock, true)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
}
// Check tx has been confirmed
if (hashBlock.IsNull()) {
throw JSONRPCError(RPC_MISC_ERROR, "Transaction has not been confirmed yet");
}
// Check is mine
if (!pwalletMain->mapWallet.count(hash)) {
throw JSONRPCError(RPC_MISC_ERROR, "Transaction does not belong to the wallet");
}
const CWalletTx& wtx = pwalletMain->mapWallet[hash];
// Check if shielded tx
if (wtx.vjoinsplit.empty()) {
throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not a shielded transaction");
}
// Check js_index
int js_index = params[1].get_int();
if (js_index < 0 || js_index >= wtx.vjoinsplit.size()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid js_index");
}
// Check output_index
int output_index = params[2].get_int();
if (output_index < 0 || output_index >= ZC_NUM_JS_OUTPUTS) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid output_index");
}
// Get message if it exists
string msg;
if (params.size() == 4) {
msg = params[3].get_str();
}
// Create PaymentDisclosureKey
PaymentDisclosureKey key = {hash, (size_t)js_index, (uint8_t)output_index };
// TODO: In future, perhaps init the DB in init.cpp
shared_ptr<PaymentDisclosureDB> db = PaymentDisclosureDB::sharedInstance();
PaymentDisclosureInfo info;
if (!db->Get(key, info)) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Could not find payment disclosure info for the given joinsplit output");
}
PaymentDisclosure pd( wtx.joinSplitPubKey, key, info, msg );
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << pd;
string strHex = HexStr(ss.begin(), ss.end());
return PAYMENT_DISCLOSURE_BLOB_STRING_PREFIX + strHex;
}
/**
* RPC call to validate a payment disclosure data blob.
*/
UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp)
{
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
string enableArg = "paymentdisclosure";
auto fEnablePaymentDisclosure = fExperimentalMode && GetBoolArg("-" + enableArg, true);
string strPaymentDisclosureDisabledMsg = "";
if (!fEnablePaymentDisclosure) {
strPaymentDisclosureDisabledMsg = experimentalDisabledHelpMsg("z_validatepaymentdisclosure", enableArg);
}
if (fHelp || params.size() != 1)
throw runtime_error(
"z_validatepaymentdisclosure \"paymentdisclosure\"\n"
"\nValidates a payment disclosure.\n"
"\nEXPERIMENTAL FEATURE\n"
+ strPaymentDisclosureDisabledMsg +
"\nArguments:\n"
"1. \"paymentdisclosure\" (string, required) Hex data string, with \"zpd:\" prefix.\n"
"\nExamples:\n"
+ HelpExampleCli("z_validatepaymentdisclosure", "\"zpd:706462ff004c561a0447ba2ec51184e6c204...\"")
+ HelpExampleRpc("z_validatepaymentdisclosure", "\"zpd:706462ff004c561a0447ba2ec51184e6c204...\"")
);
if (!fEnablePaymentDisclosure) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: payment disclosure is disabled.");
}
LOCK2(cs_main, pwalletMain->cs_wallet);
EnsureWalletIsUnlocked();
// Verify the payment disclosure input begins with "zpd:" prefix.
string strInput = params[0].get_str();
size_t pos = strInput.find(PAYMENT_DISCLOSURE_BLOB_STRING_PREFIX);
if (pos != 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, payment disclosure prefix not found.");
}
string hexInput = strInput.substr(strlen(PAYMENT_DISCLOSURE_BLOB_STRING_PREFIX));
if (!IsHex(hexInput))
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected payment disclosure data in hexadecimal format.");
}
// Unserialize the payment disclosure data into an object
PaymentDisclosure pd;
CDataStream ss(ParseHex(hexInput), SER_NETWORK, PROTOCOL_VERSION);
try {
ss >> pd;
// too much data is ignored, but if not enough data, exception of type ios_base::failure is thrown,
// CBaseDataStream::read(): end of data: iostream error
} catch (const std::exception &e) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, payment disclosure data is malformed.");
}
if (pd.payload.marker != PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, payment disclosure marker not found.");
}
if (pd.payload.version != PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Payment disclosure version is unsupported.");
}
uint256 hash = pd.payload.txid;
CTransaction tx;
uint256 hashBlock;
// Check if we have seen the transaction
if (!GetTransaction(hash, tx, hashBlock, true)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
}
// Check if the transaction has been confirmed
if (hashBlock.IsNull()) {
throw JSONRPCError(RPC_MISC_ERROR, "Transaction has not been confirmed yet");
}
// Check if shielded tx
if (tx.vjoinsplit.empty()) {
throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not a shielded transaction");
}
UniValue errs(UniValue::VARR);
UniValue o(UniValue::VOBJ);
o.push_back(Pair("txid", pd.payload.txid.ToString()));
// Check js_index
if (pd.payload.js >= tx.vjoinsplit.size()) {
errs.push_back("Payment disclosure refers to an invalid joinsplit index");
}
o.push_back(Pair("jsIndex", pd.payload.js));
if (pd.payload.n < 0 || pd.payload.n >= ZC_NUM_JS_OUTPUTS) {
errs.push_back("Payment disclosure refers to an invalid output index");
}
o.push_back(Pair("outputIndex", pd.payload.n));
o.push_back(Pair("version", pd.payload.version));
o.push_back(Pair("onetimePrivKey", pd.payload.esk.ToString()));
o.push_back(Pair("message", pd.payload.message));
o.push_back(Pair("joinSplitPubKey", tx.joinSplitPubKey.ToString()));
// Verify the payment disclosure was signed using the same key as the transaction i.e. the joinSplitPrivKey.
uint256 dataToBeSigned = SerializeHash(pd.payload, SER_GETHASH, 0);
bool sigVerified = (crypto_sign_verify_detached(pd.payloadSig.data(),
dataToBeSigned.begin(), 32,
tx.joinSplitPubKey.begin()) == 0);
o.push_back(Pair("signatureVerified", sigVerified));
if (!sigVerified) {
errs.push_back("Payment disclosure signature does not match transaction signature");
}
// Check the payment address is valid
SproutPaymentAddress zaddr = pd.payload.zaddr;
{
o.push_back(Pair("paymentAddress", EncodePaymentAddress(zaddr)));
try {
// Decrypt the note to get value and memo field
JSDescription jsdesc = tx.vjoinsplit[pd.payload.js];
uint256 h_sig = jsdesc.h_sig(*pzcashParams, tx.joinSplitPubKey);
ZCPaymentDisclosureNoteDecryption decrypter;
ZCNoteEncryption::Ciphertext ciphertext = jsdesc.ciphertexts[pd.payload.n];
uint256 pk_enc = zaddr.pk_enc;
auto plaintext = decrypter.decryptWithEsk(ciphertext, pk_enc, pd.payload.esk, h_sig, pd.payload.n);
CDataStream ssPlain(SER_NETWORK, PROTOCOL_VERSION);
ssPlain << plaintext;
SproutNotePlaintext npt;
ssPlain >> npt;
string memoHexString = HexStr(npt.memo().data(), npt.memo().data() + npt.memo().size());
o.push_back(Pair("memo", memoHexString));
o.push_back(Pair("value", ValueFromAmount(npt.value())));
// Check the blockchain commitment matches decrypted note commitment
uint256 cm_blockchain = jsdesc.commitments[pd.payload.n];
SproutNote note = npt.note(zaddr);
uint256 cm_decrypted = note.cm();
bool cm_match = (cm_decrypted == cm_blockchain);
o.push_back(Pair("commitmentMatch", cm_match));
if (!cm_match) {
errs.push_back("Commitment derived from payment disclosure does not match blockchain commitment");
}
} catch (const std::exception &e) {
errs.push_back(string("Error while decrypting payment disclosure note: ") + string(e.what()) );
}
}
bool isValid = errs.empty();
o.push_back(Pair("valid", isValid));
if (!isValid) {
o.push_back(Pair("errors", errs));
}
return o;
}