From f7b1c1f26f537c4ba79eef257360aa81ce4a3208 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Fri, 5 Jun 2020 07:20:38 -0400 Subject: [PATCH] Delete Payment Disclosures This code will be "experimental" forever, only supports Sprout, and is a piece of shit. Deleting with fire. Sapling viewing keys are the replacement, which already exist. --- doc/payment-disclosure.md | 107 ------ qa/pull-tester/rpc-tests.sh | 1 - qa/rpc-tests/paymentdisclosure.py | 215 ------------ src/Makefile.am | 7 - src/gtest/test_paymentdisclosure.cpp | 211 ------------ src/paymentdisclosure.cpp | 65 ---- src/paymentdisclosure.h | 149 -------- src/paymentdisclosuredb.cpp | 93 ----- src/paymentdisclosuredb.h | 42 --- .../asyncrpcoperation_mergetoaddress.cpp | 20 +- src/wallet/asyncrpcoperation_mergetoaddress.h | 7 +- src/wallet/asyncrpcoperation_sendmany.cpp | 17 +- src/wallet/asyncrpcoperation_sendmany.h | 7 +- .../asyncrpcoperation_shieldcoinbase.cpp | 19 - src/wallet/asyncrpcoperation_shieldcoinbase.h | 7 - src/wallet/rpcdisclosure.cpp | 324 ------------------ src/zcash/NoteEncryption.cpp | 50 +-- src/zcash/NoteEncryption.hpp | 29 +- 18 files changed, 8 insertions(+), 1362 deletions(-) delete mode 100644 doc/payment-disclosure.md delete mode 100755 qa/rpc-tests/paymentdisclosure.py delete mode 100644 src/gtest/test_paymentdisclosure.cpp delete mode 100644 src/paymentdisclosure.cpp delete mode 100644 src/paymentdisclosure.h delete mode 100644 src/paymentdisclosuredb.cpp delete mode 100644 src/paymentdisclosuredb.h delete mode 100644 src/wallet/rpcdisclosure.cpp diff --git a/doc/payment-disclosure.md b/doc/payment-disclosure.md deleted file mode 100644 index 02b4167da..000000000 --- a/doc/payment-disclosure.md +++ /dev/null @@ -1,107 +0,0 @@ -# Payment Disclosure (Experimental Feature) - -**Summary** - -Use RPC calls `z_getpaymentdisclosure` and `z_validatepaymentdisclosure` to reveal details of a shielded payment. - -**Who should read this document** - -Frequent users of shielded transactions, payment processors, exchanges, block explorer - -### Experimental Feature - -This is an experimental feature. Enable it by launching `zcashd` with flags: - - zcashd -experimentalfeatures -paymentdisclosure -debug=paymentdisclosure -txindex=1 - -These flags can also be set as options in `zcash.conf`. - -All nodes that generate or validate payment disclosures must run with `txindex=1` enabled. - -### Background - -Payment Disclosure is an implementation of the work-in-progress Payment Disclosure ZIP [1]. - -The ZIP describes a method of proving that a payment was sent to a shielded address. In the typical case, this means enabling a sender to present a proof that they transferred funds to a recipient's shielded address. - -[1] https://github.com/zcash/zips/pull/119 - -### Example Use Case - -Alice the customer sends 10 HUSH to Bob the merchant at the shielded address shown on their website. However, Bob is not sure if he received the funds. - -Alice's node is running with payment disclosure enabled, so Alice generates a payment disclosure and provides it to Bob, who verifies the payment was made. - -If Bob is a bad merchant, Alice can present the payment disclosure to a third party to validate that payment was indeed made. - -### Solution - -A payment disclosure can be generated for any output of a JoinSplit using the RPC call: - - z_getpaymentdisclosure txid js_index output_index (message) - -An optional message can be supplied. This could be used for a refund address or some other reference, as currently it is not common practice to (ahead of time) include a refund address in the memo field when making a payment. - -To validate a payment disclosure, the following RPC call can be used: - - z_validatepaymentdisclosure hexdata - -### Example - -Generate a payment disclosure for the first joinsplit, second output (index starts from zero): - - hush-cli z_getpaymentdisclosure 79189528d611e811a1c7bb0358dd31343033d14b4c1e998d7c4799c40f8b652b 0 1 "Hello" - -This returns a payment disclosure in the form of a hex string: - - 706462ff000a3722aafa8190cdf9710bfad6da2af6d3a74262c1fc96ad47df814b0cd5641c2b658b0fc499477c8d991e4c4bd133303431dd5803bbc7a111e811d6289518790000000000000000017e861adb829d8cb1cbcf6330b8c2e25fb0d08041a67a857815a136f0227f8a5342bce5b3c0d894e2983000eb594702d3c1580817d0374e15078528e56bb6f80c0548656c6c6f59a7085395c9e706d82afe3157c54ad4ae5bf144fcc774a8d9c921c58471402019c156ec5641e2173c4fb6467df5f28530dc4636fa71f4d0e48fc5c560fac500 - -To validate the payment disclosure: - - hush-cli z_validatepaymentdisclosure HEXDATA - -This returns data related to the payment and the payment disclosure: - - { - "txid": "79189528d611e811a1c7bb0358dd31343033d14b4c1e998d7c4799c40f8b652b", - "jsIndex": 0, - "outputIndex": 1, - "version": 0, - "onetimePrivKey": "1c64d50c4b81df47ad96fcc16242a7d3f62adad6fa0b71f9cd9081faaa22370a", - "message": "Hello", - "joinSplitPubKey": "d1c465d16166b602992479acfac18e87dc18065f6cefde6a002e70bc371b9faf", - "signatureVerified": true, - "paymentAddress": "ztaZJXy8iX8nrk2ytXKDBoTWqPkhQcj6E2ifARnD3wfkFwsxXs5SoX7NGmrjkzSiSKn8VtLHTJae48vX5NakvmDhtGNY5eb", - "memo": "f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "value": 12.49900000, - "commitmentMatch": true, - "valid": true - } - -The `signatureVerified` field confirms that the payment disclosure was generated and signed with the joinSplitPrivKey, which should only be known by the node generating and sending the transaction 7918...652b in question. - -### Where is the data stored? - -For all nodes, payment disclosure does not touch `wallet.dat` in any way. - -For nodes that only validate payment disclosures, no data is stored locally. - -For nodes that generate payment disclosures, a LevelDB database is created in the node's datadir. For most users, this would be in the folder: - - $HOME/.zcash/paymentdisclosure - -If you decide you don't want to use payment disclosure, it is safe to shut down your node and delete the database folder. - -### Security Properties - -Please consult the work-in-progress ZIP for details about the protocol, security properties and caveats. - -### Reminder - -Feedback is most welcome! - -This is an experimental feature so there are no guarantees that the protocol, database format, RPC interface etc. will remain the same in the future. - -### Notes - -Currently there is no user friendly way to help senders identify which joinsplit output index maps to a given payment they made. It is possible to construct this from `debug.log`. Ideas and feedback are most welcome on how to improve the user experience. diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index a23f2908d..94429cb7f 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -17,7 +17,6 @@ testScripts=( 'dpow.py' 'dpowconfs.py' 'ac_private.py' - 'paymentdisclosure.py' 'prioritisetransaction.py' 'wallet_treestate.py' 'wallet_anchorfork.py' diff --git a/qa/rpc-tests/paymentdisclosure.py b/qa/rpc-tests/paymentdisclosure.py deleted file mode 100755 index 48d4712a9..000000000 --- a/qa/rpc-tests/paymentdisclosure.py +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/env python2 -# 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. - -from test_framework.test_framework import BitcoinTestFramework -from test_framework.authproxy import JSONRPCException -from test_framework.util import assert_equal, initialize_chain_clean, \ - start_node, connect_nodes_bi, wait_and_assert_operationid_status - -from decimal import Decimal - -class PaymentDisclosureTest (BitcoinTestFramework): - - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) - - def setup_network(self, split=False): - args = ['-debug=zrpcunsafe,paymentdisclosure', '-experimentalfeatures', '-paymentdisclosure', '-txindex=1'] - self.nodes = [] - self.nodes.append(start_node(0, self.options.tmpdir, args)) - self.nodes.append(start_node(1, self.options.tmpdir, args)) - # node 2 does not enable payment disclosure - args2 = ['-debug=zrpcunsafe', '-experimentalfeatures', '-txindex=1'] - self.nodes.append(start_node(2, self.options.tmpdir, args2)) - connect_nodes_bi(self.nodes,0,1) - connect_nodes_bi(self.nodes,1,2) - connect_nodes_bi(self.nodes,0,2) - self.is_network_split=False - self.sync_all() - - def run_test (self): - print "Mining blocks..." - - self.nodes[0].generate(4) - walletinfo = self.nodes[0].getwalletinfo() - assert_equal(walletinfo['immature_balance'], 40) - assert_equal(walletinfo['balance'], 0) - self.sync_all() - self.nodes[2].generate(3) - self.sync_all() - self.nodes[1].generate(101) - self.sync_all() - assert_equal(self.nodes[0].getbalance(), 40) - assert_equal(self.nodes[1].getbalance(), 10) - assert_equal(self.nodes[2].getbalance(), 30) - - mytaddr = self.nodes[0].getnewaddress() - myzaddr = self.nodes[0].z_getnewaddress() - - # Check that Node 2 has payment disclosure disabled. - try: - self.nodes[2].z_getpaymentdisclosure("invalidtxid", 0, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("payment disclosure is disabled" in errorString) - - # Check that Node 0 returns an error for an unknown txid - try: - self.nodes[0].z_getpaymentdisclosure("invalidtxid", 0, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("No information available about transaction" in errorString) - - # Shield coinbase utxos from node 0 of value 40, standard fee of 0.00010000 - recipients = [{"address":myzaddr, "amount":Decimal('40.0')-Decimal('0.0001')}] - myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - txid = wait_and_assert_operationid_status(self.nodes[0], myopid) - - # Check the tx has joinsplits - assert( len(self.nodes[0].getrawtransaction("" + txid, 1)["vjoinsplit"]) > 0 ) - - # Sync mempools - self.sync_all() - - # Confirm that you can't create a payment disclosure for an unconfirmed tx - try: - self.nodes[0].z_getpaymentdisclosure(txid, 0, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Transaction has not been confirmed yet" in errorString) - - try: - self.nodes[1].z_getpaymentdisclosure(txid, 0, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Transaction has not been confirmed yet" in errorString) - - # Mine tx - self.nodes[0].generate(1) - self.sync_all() - - # Confirm that Node 1 cannot create a payment disclosure for a transaction which does not impact its wallet - try: - self.nodes[1].z_getpaymentdisclosure(txid, 0, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Transaction does not belong to the wallet" in errorString) - - # Check that an invalid joinsplit index is rejected - try: - self.nodes[0].z_getpaymentdisclosure(txid, 1, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Invalid js_index" in errorString) - - try: - self.nodes[0].z_getpaymentdisclosure(txid, -1, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Invalid js_index" in errorString) - - # Check that an invalid output index is rejected - try: - self.nodes[0].z_getpaymentdisclosure(txid, 0, 2) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Invalid output_index" in errorString) - - try: - self.nodes[0].z_getpaymentdisclosure(txid, 0, -1) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Invalid output_index" in errorString) - - # Ask Node 0 to create and validate a payment disclosure for output 0 - message = "Here is proof of my payment!" - pd = self.nodes[0].z_getpaymentdisclosure(txid, 0, 0, message) - result = self.nodes[0].z_validatepaymentdisclosure(pd) - assert(result["valid"]) - output_value_sum = Decimal(result["value"]) - - # Ask Node 1 to confirm the payment disclosure is valid - result = self.nodes[1].z_validatepaymentdisclosure(pd) - assert(result["valid"]) - assert_equal(result["message"], message) - assert_equal(result["value"], output_value_sum) - - # Confirm that payment disclosure begins with prefix zpd: - assert(pd.startswith("zpd:")) - - # Confirm that payment disclosure without prefix zpd: fails validation - try: - self.nodes[1].z_validatepaymentdisclosure(pd[4:]) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("payment disclosure prefix not found" in errorString) - - # Check that total value of output index 0 and index 1 should equal shielding amount of 40 less standard fee. - pd = self.nodes[0].z_getpaymentdisclosure(txid, 0, 1) - result = self.nodes[0].z_validatepaymentdisclosure(pd) - output_value_sum += Decimal(result["value"]) - assert_equal(output_value_sum, Decimal('39.99990000')) - - # Create a z->z transaction, sending shielded funds from node 0 to node 1 - node1zaddr = self.nodes[1].z_getnewaddress() - recipients = [{"address":node1zaddr, "amount":Decimal('1')}] - myopid = self.nodes[0].z_sendmany(myzaddr, recipients) - txid = wait_and_assert_operationid_status(self.nodes[0], myopid) - self.sync_all() - self.nodes[0].generate(1) - self.sync_all() - - # Confirm that Node 0 can create a valid payment disclosure - pd = self.nodes[0].z_getpaymentdisclosure(txid, 0, 0, "a message of your choice") - result = self.nodes[0].z_validatepaymentdisclosure(pd) - assert(result["valid"]) - - # Confirm that Node 1, even as recipient of shielded funds, cannot create a payment disclosure - # as the transaction was created by Node 0 and Node 1's payment disclosure database does not - # contain the necessary data to do so, where the data would only have been available on Node 0 - # when executing z_shieldcoinbase. - try: - self.nodes[1].z_getpaymentdisclosure(txid, 0, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Could not find payment disclosure info for the given joinsplit output" in errorString) - - # Payment disclosures cannot be created for transparent transactions. - txid = self.nodes[2].sendtoaddress(mytaddr, 1.0) - self.sync_all() - - # No matter the type of transaction, if it has not been confirmed, it is ignored. - try: - self.nodes[0].z_getpaymentdisclosure(txid, 0, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Transaction has not been confirmed yet" in errorString) - - self.nodes[0].generate(1) - self.sync_all() - - # Confirm that a payment disclosure can only be generated for a shielded transaction. - try: - self.nodes[0].z_getpaymentdisclosure(txid, 0, 0) - assert(False) - except JSONRPCException as e: - errorString = e.error['message'] - assert("Transaction is not a shielded transaction" in errorString) - -if __name__ == '__main__': - PaymentDisclosureTest().main() diff --git a/src/Makefile.am b/src/Makefile.am index 2d2d7a691..ab04c24bc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -181,8 +181,6 @@ BITCOIN_CORE_H = \ netbase.h \ notaries_staked.h \ noui.h \ - paymentdisclosure.h \ - paymentdisclosuredb.h \ policy/fees.h \ pow.h \ prevector.h \ @@ -303,8 +301,6 @@ libbitcoin_server_a_SOURCES = \ notaries_staked.cpp \ noui.cpp \ notarisationdb.cpp \ - paymentdisclosure.cpp \ - paymentdisclosuredb.cpp \ policy/fees.cpp \ pow.cpp \ rest.cpp \ @@ -355,10 +351,7 @@ libbitcoin_wallet_a_SOURCES = \ wallet/asyncrpcoperation_shieldcoinbase.cpp \ wallet/crypter.cpp \ wallet/db.cpp \ - paymentdisclosure.cpp \ - paymentdisclosuredb.cpp \ transaction_builder.cpp \ - wallet/rpcdisclosure.cpp \ wallet/rpcdump.cpp \ cc/CCtokens.cpp \ cc/CCassetsCore.cpp \ diff --git a/src/gtest/test_paymentdisclosure.cpp b/src/gtest/test_paymentdisclosure.cpp deleted file mode 100644 index c166cdbe1..000000000 --- a/src/gtest/test_paymentdisclosure.cpp +++ /dev/null @@ -1,211 +0,0 @@ -#include - -#include "main.h" -#include "utilmoneystr.h" -#include "chainparams.h" -#include "utilstrencodings.h" -#include "zcash/Address.hpp" -#include "wallet/wallet.h" -#include "amount.h" - -#include -#include -#include -#include -#include -#include -#include -#include "util.h" - -#include "paymentdisclosure.h" -#include "paymentdisclosuredb.h" - -#include "sodium.h" - -#include -#include -#include - -using namespace std; - -/* - To run tests: - ./zcash-gtest --gtest_filter="paymentdisclosure.*" - - Note: As an experimental feature, writing your own tests may require option flags to be set. - mapArgs["-experimentalfeatures"] = true; - mapArgs["-paymentdisclosure"] = true; -*/ - -#define NUM_TRIES 10000 - -#define DUMP_DATABASE_TO_STDOUT false - -static boost::uuids::random_generator uuidgen; - -static uint256 random_uint256() -{ - uint256 ret; - randombytes_buf(ret.begin(), 32); - return ret; -} - -// Subclass of PaymentDisclosureDB to add debugging methods -class PaymentDisclosureDBTest : public PaymentDisclosureDB { -public: - PaymentDisclosureDBTest(const boost::filesystem::path& dbPath) : PaymentDisclosureDB(dbPath) {} - - void DebugDumpAllStdout() { - ASSERT_NE(db, nullptr); - std::lock_guard guard(lock_); - - // Iterate over each item in the database and print them - leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions()); - - for (it->SeekToFirst(); it->Valid(); it->Next()) { - cout << it->key().ToString() << " : "; - // << it->value().ToString() << endl; - try { - std::string strValue = it->value().ToString(); - PaymentDisclosureInfo info; - CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); - ssValue >> info; - cout << info.ToString() << std::endl; - } catch (const std::exception& e) { - cout << e.what() << std::endl; - } - } - - if (false == it->status().ok()) { - cerr << "An error was found iterating over the database" << endl; - cerr << it->status().ToString() << endl; - } - - delete it; - } -}; - - - -// This test creates random payment disclosure blobs and checks that they can be -// 1. inserted and retrieved from a database -// 2. serialized and deserialized without corruption -// Note that the zpd: prefix is not part of the payment disclosure blob itself. It is only -// used as convention to improve the user experience when sharing payment disclosure blobs. -TEST(paymentdisclosure, mainnet) { - SelectParams(CBaseChainParams::MAIN); - - boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - boost::filesystem::create_directories(pathTemp); - mapArgs["-datadir"] = pathTemp.string(); - - std::cout << "Test payment disclosure database created in folder: " << pathTemp.string() << std::endl; - - PaymentDisclosureDBTest mydb(pathTemp); - - for (int i=0; i vch(&buffer[0], &buffer[0] + 32); - uint256 joinSplitPrivKey = uint256(vch); - - // Create payment disclosure key and info data to store in test database - size_t js = random_uint256().GetCheapHash() % std::numeric_limits::max(); - uint8_t n = random_uint256().GetCheapHash() % std::numeric_limits::max(); - PaymentDisclosureKey key { random_uint256(), js, n}; - PaymentDisclosureInfo info; - info.esk = random_uint256(); - info.joinSplitPrivKey = joinSplitPrivKey; - info.zaddr = libzcash::SproutSpendingKey::random().address(); - ASSERT_TRUE(mydb.Put(key, info)); - - // Retrieve info from test database into new local variable and test it matches - PaymentDisclosureInfo info2; - ASSERT_TRUE(mydb.Get(key, info2)); - ASSERT_EQ(info, info2); - - // Modify this local variable and confirm it no longer matches - info2.esk = random_uint256(); - info2.joinSplitPrivKey = random_uint256(); - info2.zaddr = libzcash::SproutSpendingKey::random().address(); - ASSERT_NE(info, info2); - - // Using the payment info object, let's create a dummy payload - PaymentDisclosurePayload payload; - payload.version = PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL; - payload.esk = info.esk; - payload.txid = key.hash; - payload.js = key.js; - payload.n = key.n; - payload.message = "random-" + boost::uuids::to_string(uuidgen()); // random message - payload.zaddr = info.zaddr; - - // Serialize and hash the payload to generate a signature - uint256 dataToBeSigned = SerializeHash(payload, SER_GETHASH, 0); - - // Compute the payload signature - unsigned char payloadSig[64]; - if (!(crypto_sign_detached(&payloadSig[0], NULL, - dataToBeSigned.begin(), 32, - &buffer[0] // buffer containing both private and public key required - ) == 0)) - { - throw std::runtime_error("crypto_sign_detached failed"); - } - - // Sanity check - if (!(crypto_sign_verify_detached(&payloadSig[0], - dataToBeSigned.begin(), 32, - joinSplitPubKey.begin() - ) == 0)) - { - throw std::runtime_error("crypto_sign_verify_detached failed"); - } - - // Convert signature buffer to boost array - std::array arrayPayloadSig; - memcpy(arrayPayloadSig.data(), &payloadSig[0], 64); - - // Payment disclosure blob to pass around - PaymentDisclosure pd = {payload, arrayPayloadSig}; - - // Test payment disclosure constructors - PaymentDisclosure pd2(payload, arrayPayloadSig); - ASSERT_EQ(pd, pd2); - PaymentDisclosure pd3(joinSplitPubKey, key, info, payload.message); - ASSERT_EQ(pd, pd3); - - // Verify serialization and deserialization works - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << pd; - std::string ssHexString = HexStr(ss.begin(), ss.end()); - - PaymentDisclosure pdTmp; - CDataStream ssTmp(ParseHex(ssHexString), SER_NETWORK, PROTOCOL_VERSION); - ssTmp >> pdTmp; - ASSERT_EQ(pd, pdTmp); - - CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); - ss2 << pdTmp; - std::string ss2HexString = HexStr(ss2.begin(), ss2.end()); - ASSERT_EQ(ssHexString, ss2HexString); - - // Verify marker - ASSERT_EQ(pd.payload.marker, PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES); - ASSERT_EQ(pdTmp.payload.marker, PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES); - ASSERT_EQ(0, ssHexString.find("706462ff")); // Little endian encoding of PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES value - - // Sanity check - PaymentDisclosure pdDummy; - ASSERT_NE(pd, pdDummy); - } - -#if DUMP_DATABASE_TO_STDOUT == true - mydb.DebugDumpAllStdout(); -#endif -} diff --git a/src/paymentdisclosure.cpp b/src/paymentdisclosure.cpp deleted file mode 100644 index eb55a0536..000000000 --- a/src/paymentdisclosure.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// 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. - -#include "paymentdisclosure.h" - -#include "key_io.h" -#include "util.h" - -std::string PaymentDisclosureInfo::ToString() const { - return strprintf("PaymentDisclosureInfo(version=%d, esk=%s, joinSplitPrivKey=, address=%s)", - version, esk.ToString(), EncodePaymentAddress(zaddr)); -} - -std::string PaymentDisclosure::ToString() const { - std::string s = HexStr(payloadSig.begin(), payloadSig.end()); - return strprintf("PaymentDisclosure(payload=%s, payloadSig=%s)", payload.ToString(), s); -} - -std::string PaymentDisclosurePayload::ToString() const { - return strprintf("PaymentDisclosurePayload(version=%d, esk=%s, txid=%s, js=%d, n=%d, address=%s, message=%s)", - version, esk.ToString(), txid.ToString(), js, n, EncodePaymentAddress(zaddr), message); -} - -PaymentDisclosure::PaymentDisclosure(const uint256 &joinSplitPubKey, const PaymentDisclosureKey &key, const PaymentDisclosureInfo &info, const std::string &message) -{ - // Populate payload member variable - payload.version = info.version; // experimental = 0, production = 1 etc. - payload.esk = info.esk; - payload.txid = key.hash; - payload.js = key.js; - payload.n = key.n; - payload.zaddr = info.zaddr; - payload.message = message; - - // Serialize and hash the payload to generate a signature - uint256 dataToBeSigned = SerializeHash(payload, SER_GETHASH, 0); - - LogPrint("paymentdisclosure", "Payment Disclosure: signing raw payload = %s\n", dataToBeSigned.ToString()); - - // Prepare buffer to store ed25519 key pair in libsodium-compatible format - unsigned char bufferKeyPair[64]; - memcpy(&bufferKeyPair[0], info.joinSplitPrivKey.begin(), 32); - memcpy(&bufferKeyPair[32], joinSplitPubKey.begin(), 32); - - // Compute payload signature member variable - if (!(crypto_sign_detached(payloadSig.data(), NULL, - dataToBeSigned.begin(), 32, - &bufferKeyPair[0] - ) == 0)) - { - throw std::runtime_error("crypto_sign_detached failed"); - } - - // Sanity check - if (!(crypto_sign_verify_detached(payloadSig.data(), - dataToBeSigned.begin(), 32, - joinSplitPubKey.begin()) == 0)) - { - throw std::runtime_error("crypto_sign_verify_detached failed"); - } - - std::string sigString = HexStr(payloadSig.data(), payloadSig.data() + payloadSig.size()); - LogPrint("paymentdisclosure", "Payment Disclosure: signature = %s\n", sigString); -} diff --git a/src/paymentdisclosure.h b/src/paymentdisclosure.h deleted file mode 100644 index 5c3a20c33..000000000 --- a/src/paymentdisclosure.h +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (c) 2017 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. - -#ifndef ZCASH_PAYMENTDISCLOSURE_H -#define ZCASH_PAYMENTDISCLOSURE_H - -#include "uint256.h" -#include "clientversion.h" -#include "serialize.h" -#include "streams.h" -#include "version.h" - -// For JSOutPoint -#include "wallet/wallet.h" - -#include -#include -#include - - -// Ensure that the two different protocol messages, payment disclosure blobs and transactions, -// which are signed with the same key, joinSplitPrivKey, have disjoint encodings such that an -// encoding from one context will be rejected in the other. We know that the set of valid -// transaction versions is currently ({1..INT32_MAX}) so we will use a negative value for -// payment disclosure of -10328976 which in hex is 0xFF626470. Serialization is in little endian -// format, so a payment disclosure hex string begins 706462FF, which in ISO-8859-1 is "pdbÿ". -#define PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES -10328976 - -#define PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL 0 - -#define PAYMENT_DISCLOSURE_BLOB_STRING_PREFIX "zpd:" - -typedef JSOutPoint PaymentDisclosureKey; - -struct PaymentDisclosureInfo { - uint8_t version; // 0 = experimental, 1 = first production version, etc. - uint256 esk; // zcash/NoteEncryption.cpp - uint256 joinSplitPrivKey; // primitives/transaction.h - // ed25519 - not tied to implementation e.g. libsodium, see ed25519 rfc - - libzcash::SproutPaymentAddress zaddr; - - PaymentDisclosureInfo() : version(PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL) { - } - - PaymentDisclosureInfo(uint8_t v, uint256 esk, uint256 key, libzcash::SproutPaymentAddress zaddr) : version(v), esk(esk), joinSplitPrivKey(key), zaddr(zaddr) { } - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(version); - READWRITE(esk); - READWRITE(joinSplitPrivKey); - READWRITE(zaddr); - } - - std::string ToString() const; - - friend bool operator==(const PaymentDisclosureInfo& a, const PaymentDisclosureInfo& b) { - return (a.version == b.version && a.esk == b.esk && a.joinSplitPrivKey == b.joinSplitPrivKey && a.zaddr == b.zaddr); - } - - friend bool operator!=(const PaymentDisclosureInfo& a, const PaymentDisclosureInfo& b) { - return !(a == b); - } - -}; - - -struct PaymentDisclosurePayload { - int32_t marker = PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES; // to be disjoint from transaction encoding - uint8_t version; // 0 = experimental, 1 = first production version, etc. - uint256 esk; // zcash/NoteEncryption.cpp - uint256 txid; // primitives/transaction.h - uint64_t js; // Index into CTransaction.vjoinsplit - uint8_t n; // Index into JSDescription fields of length ZC_NUM_JS_OUTPUTS - libzcash::SproutPaymentAddress zaddr; // zcash/Address.hpp - std::string message; // parameter to RPC call - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(marker); - READWRITE(version); - READWRITE(esk); - READWRITE(txid); - READWRITE(js); - READWRITE(n); - READWRITE(zaddr); - READWRITE(message); - } - - std::string ToString() const; - - friend bool operator==(const PaymentDisclosurePayload& a, const PaymentDisclosurePayload& b) { - return ( - a.version == b.version && - a.esk == b.esk && - a.txid == b.txid && - a.js == b.js && - a.n == b.n && - a.zaddr == b.zaddr && - a.message == b.message - ); - } - - friend bool operator!=(const PaymentDisclosurePayload& a, const PaymentDisclosurePayload& b) { - return !(a == b); - } -}; - -struct PaymentDisclosure { - PaymentDisclosurePayload payload; - std::array payloadSig; - // We use boost array because serialize doesn't like char buffer, otherwise we could do: unsigned char payloadSig[64]; - - PaymentDisclosure() {}; - PaymentDisclosure(const PaymentDisclosurePayload payload, const std::array sig) : payload(payload), payloadSig(sig) {}; - PaymentDisclosure(const uint256& joinSplitPubKey, const PaymentDisclosureKey& key, const PaymentDisclosureInfo& info, const std::string& message); - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(payload); - READWRITE(payloadSig); - } - - std::string ToString() const; - - friend bool operator==(const PaymentDisclosure& a, const PaymentDisclosure& b) { - return (a.payload == b.payload && a.payloadSig == b.payloadSig); - } - - friend bool operator!=(const PaymentDisclosure& a, const PaymentDisclosure& b) { - return !(a == b); - } -}; - - - -typedef std::pair PaymentDisclosureKeyInfo; - - -#endif // ZCASH_PAYMENTDISCLOSURE_H diff --git a/src/paymentdisclosuredb.cpp b/src/paymentdisclosuredb.cpp deleted file mode 100644 index 8840dcda0..000000000 --- a/src/paymentdisclosuredb.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// 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. - -#include "paymentdisclosuredb.h" - -#include "util.h" -#include "dbwrapper.h" - -#include - -using namespace std; - -static boost::filesystem::path emptyPath; - -/** - * Static method to return the shared/default payment disclosure database. - */ -shared_ptr PaymentDisclosureDB::sharedInstance() { - // Thread-safe in C++11 and gcc 4.3 - static shared_ptr ptr = std::make_shared(); - return ptr; -} - -// C++11 delegated constructor -PaymentDisclosureDB::PaymentDisclosureDB() : PaymentDisclosureDB(emptyPath) { -} - -PaymentDisclosureDB::PaymentDisclosureDB(const boost::filesystem::path& dbPath) { - boost::filesystem::path path(dbPath); - if (path.empty()) { - path = GetDataDir() / "paymentdisclosure"; - LogPrintf("PaymentDisclosure: using default path for database: %s\n", path.string()); - } else { - LogPrintf("PaymentDisclosure: using custom path for database: %s\n", path.string()); - } - - TryCreateDirectory(path); - options.create_if_missing = true; - leveldb::Status status = leveldb::DB::Open(options, path.string(), &db); - dbwrapper_private::HandleError(status); // throws exception - LogPrintf("PaymentDisclosure: Opened LevelDB successfully\n"); -} - -PaymentDisclosureDB::~PaymentDisclosureDB() { - if (db != nullptr) { - delete db; - } -} - -bool PaymentDisclosureDB::Put(const PaymentDisclosureKey& key, const PaymentDisclosureInfo& info) -{ - if (db == nullptr) { - return false; - } - - std::lock_guard guard(lock_); - - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - ssValue.reserve(GetSerializeSize(ssValue, info)); - ssValue << info; - leveldb::Slice slice(&ssValue[0], ssValue.size()); - - leveldb::Status status = db->Put(writeOptions, key.ToString(), slice); - dbwrapper_private::HandleError(status); - return true; -} - -bool PaymentDisclosureDB::Get(const PaymentDisclosureKey& key, PaymentDisclosureInfo& info) -{ - if (db == nullptr) { - return false; - } - - std::lock_guard guard(lock_); - - std::string strValue; - leveldb::Status status = db->Get(readOptions, key.ToString(), &strValue); - if (!status.ok()) { - if (status.IsNotFound()) - return false; - LogPrintf("PaymentDisclosure: LevelDB read failure: %s\n", status.ToString()); - dbwrapper_private::HandleError(status); - } - - try { - CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); - ssValue >> info; - } catch (const std::exception&) { - return false; - } - return true; -} diff --git a/src/paymentdisclosuredb.h b/src/paymentdisclosuredb.h deleted file mode 100644 index 9352cac8f..000000000 --- a/src/paymentdisclosuredb.h +++ /dev/null @@ -1,42 +0,0 @@ -// 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. - -#ifndef ZCASH_PAYMENTDISCLOSUREDB_H -#define ZCASH_PAYMENTDISCLOSUREDB_H - -#include "paymentdisclosure.h" - -#include -#include -#include -#include -#include - -#include - -#include - - -class PaymentDisclosureDB -{ -protected: - leveldb::DB* db = nullptr; - leveldb::Options options; - leveldb::ReadOptions readOptions; - leveldb::WriteOptions writeOptions; - mutable std::mutex lock_; - -public: - static std::shared_ptr sharedInstance(); - - PaymentDisclosureDB(); - PaymentDisclosureDB(const boost::filesystem::path& dbPath); - ~PaymentDisclosureDB(); - - bool Put(const PaymentDisclosureKey& key, const PaymentDisclosureInfo& info); - bool Get(const PaymentDisclosureKey& key, PaymentDisclosureInfo& info); -}; - - -#endif // ZCASH_PAYMENTDISCLOSUREDB_H diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.cpp b/src/wallet/asyncrpcoperation_mergetoaddress.cpp index ed4efc014..4e47a5a2d 100644 --- a/src/wallet/asyncrpcoperation_mergetoaddress.cpp +++ b/src/wallet/asyncrpcoperation_mergetoaddress.cpp @@ -133,9 +133,6 @@ saplingNoteInputs_(saplingNoteInputs), recipient_(recipient), fee_(fee), context // Lock UTXOs lock_utxos(); lock_notes(); - - // Enable payment disclosure if requested - paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", true); } AsyncRPCOperation_mergetoaddress::~AsyncRPCOperation_mergetoaddress() @@ -210,25 +207,10 @@ void AsyncRPCOperation_mergetoaddress::main() unlock_utxos(); // clean up unlock_notes(); // clean up - - // !!! Payment disclosure START - if (success && paymentDisclosureMode && paymentDisclosureData_.size() > 0) { - uint256 txidhash = tx_.GetHash(); - std::shared_ptr db = PaymentDisclosureDB::sharedInstance(); - for (PaymentDisclosureKeyInfo p : paymentDisclosureData_) { - p.first.hash = txidhash; - if (!db->Put(p.first, p.second)) { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Error writing entry to database for key %s\n", getId(), p.first.ToString()); - } else { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString()); - } - } - } - // !!! Payment disclosure END } // Notes: -// 1. #1359 Currently there is no limit set on the number of joinsplits, so size of tx could be invalid. +// 1. #1359 Currently there is no limit set on the number of inputs+outputs, so size of tx could be invalid. // 2. #1277 Spendable notes are not locked, so an operation running in parallel could also try to use them. bool AsyncRPCOperation_mergetoaddress::main_impl() { diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.h b/src/wallet/asyncrpcoperation_mergetoaddress.h index be49baff0..4876921db 100644 --- a/src/wallet/asyncrpcoperation_mergetoaddress.h +++ b/src/wallet/asyncrpcoperation_mergetoaddress.h @@ -1,4 +1,6 @@ // Copyright (c) 2017 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. @@ -22,7 +24,6 @@ #include "amount.h" #include "asyncrpcoperation.h" -#include "paymentdisclosure.h" #include "primitives/transaction.h" #include "transaction_builder.h" #include "wallet.h" @@ -93,8 +94,6 @@ public: bool testmode = false; // Set to true to disable sending txs and generating proofs - bool paymentDisclosureMode = true; // Set to true to save esk for encrypted notes in payment disclosure database. - private: friend class TEST_FRIEND_AsyncRPCOperation_mergetoaddress; // class for unit testing @@ -148,8 +147,6 @@ private: void unlock_notes(); - // payment disclosure! - std::vector paymentDisclosureData_; }; diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index a1ecb296d..31f987bef 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -211,25 +211,10 @@ void AsyncRPCOperation_sendmany::main() { s += strprintf(", error=%s)\n", getErrorMessage()); } LogPrintf("%s",s); - - // !!! Payment disclosure START - if (success && paymentDisclosureMode && paymentDisclosureData_.size()>0) { - uint256 txidhash = tx_.GetHash(); - std::shared_ptr db = PaymentDisclosureDB::sharedInstance(); - for (PaymentDisclosureKeyInfo p : paymentDisclosureData_) { - p.first.hash = txidhash; - if (!db->Put(p.first, p.second)) { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Error writing entry to database for key %s\n", getId(), p.first.ToString()); - } else { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString()); - } - } - } - // !!! Payment disclosure END } // Notes: -// 1. #1159 Currently there is no limit set on the number of joinsplits, so size of tx could be invalid. +// 1. #1159 Currently there is no limit set on the number of shielded spends, so size of tx could be invalid. // 2. #1360 Note selection is not optimal // 3. #1277 Spendable notes are not locked, so an operation running in parallel could also try to use them bool AsyncRPCOperation_sendmany::main_impl() { diff --git a/src/wallet/asyncrpcoperation_sendmany.h b/src/wallet/asyncrpcoperation_sendmany.h index 8e39f341a..0b803ddd9 100644 --- a/src/wallet/asyncrpcoperation_sendmany.h +++ b/src/wallet/asyncrpcoperation_sendmany.h @@ -1,4 +1,5 @@ // 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. @@ -27,7 +28,6 @@ #include "zcash/JoinSplit.hpp" #include "zcash/Address.hpp" #include "wallet.h" -#include "paymentdisclosure.h" #include #include @@ -90,8 +90,6 @@ public: bool testmode = false; // Set to true to disable sending txs and generating proofs - bool paymentDisclosureMode = true; // Set to true to save esk for encrypted notes in payment disclosure database. - private: friend class TEST_FRIEND_AsyncRPCOperation_sendmany; // class for unit testing @@ -143,9 +141,6 @@ private: uint256 anchor); void sign_send_raw_transaction(UniValue obj); // throws exception if there was an error - - // payment disclosure! - std::vector paymentDisclosureData_; }; diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp index 5db0ca51b..de56ba3bc 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp @@ -48,9 +48,6 @@ #include "asyncrpcoperation_shieldcoinbase.h" -#include "paymentdisclosure.h" -#include "paymentdisclosuredb.h" - using namespace libzcash; extern uint64_t ASSETCHAINS_TIMELOCKGTE; @@ -108,8 +105,6 @@ AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase( // Lock UTXOs lock_utxos(); - // Enable payment disclosure if requested - paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", true); } AsyncRPCOperation_shieldcoinbase::~AsyncRPCOperation_shieldcoinbase() { @@ -181,20 +176,6 @@ void AsyncRPCOperation_shieldcoinbase::main() { unlock_utxos(); // clean up - // !!! Payment disclosure START - if (success && paymentDisclosureMode && paymentDisclosureData_.size()>0) { - uint256 txidhash = tx_.GetHash(); - std::shared_ptr db = PaymentDisclosureDB::sharedInstance(); - for (PaymentDisclosureKeyInfo p : paymentDisclosureData_) { - p.first.hash = txidhash; - if (!db->Put(p.first, p.second)) { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Error writing entry to database for key %s\n", getId(), p.first.ToString()); - } else { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString()); - } - } - } - // !!! Payment disclosure END } bool AsyncRPCOperation_shieldcoinbase::main_impl() { diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.h b/src/wallet/asyncrpcoperation_shieldcoinbase.h index db4fb83af..3eb1a6cfc 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.h +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.h @@ -34,8 +34,6 @@ #include -#include "paymentdisclosure.h" - // Default transaction fee if caller does not specify one. #define SHIELD_COINBASE_DEFAULT_MINERS_FEE 10000 @@ -81,8 +79,6 @@ public: bool testmode = false; // Set to true to disable sending txs and generating proofs bool cheatSpend = false; // set when this is shielding a cheating coinbase - bool paymentDisclosureMode = true; // Set to true to save esk for encrypted notes in payment disclosure database. - private: friend class ShieldToAddress; friend class TEST_FRIEND_AsyncRPCOperation_shieldcoinbase; // class for unit testing @@ -110,9 +106,6 @@ private: void lock_utxos(); void unlock_utxos(); - - // payment disclosure! - std::vector paymentDisclosureData_; }; class ShieldToAddress : public boost::static_visitor diff --git a/src/wallet/rpcdisclosure.cpp b/src/wallet/rpcdisclosure.cpp deleted file mode 100644 index 918e70cbc..000000000 --- a/src/wallet/rpcdisclosure.cpp +++ /dev/null @@ -1,324 +0,0 @@ -// Copyright (c) 2017 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. - -/****************************************************************************** - * 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 -#include - -#include -#include - -#include - -#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, const CPubKey& mypk) -{ - 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 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, const CPubKey& mypk) -{ - 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."); - } - - throw JSONRPCError(RPC_WALLET_ERROR, "Error: payment disclosures not implemented for Sapling yet"); - - 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 - PaymentAddress 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; -} diff --git a/src/zcash/NoteEncryption.cpp b/src/zcash/NoteEncryption.cpp index 63e073265..9f2d64563 100644 --- a/src/zcash/NoteEncryption.cpp +++ b/src/zcash/NoteEncryption.cpp @@ -1,3 +1,5 @@ +// Copyright (c) 2019-2020 The Hush developers + #include "NoteEncryption.hpp" #include #include "sodium.h" @@ -374,52 +376,6 @@ typename NoteDecryption::Plaintext NoteDecryption::decrypt return plaintext; } -// -// Payment disclosure - decrypt with esk -// -template -typename PaymentDisclosureNoteDecryption::Plaintext PaymentDisclosureNoteDecryption::decryptWithEsk - (const PaymentDisclosureNoteDecryption::Ciphertext &ciphertext, - const uint256 &pk_enc, - const uint256 &esk, - const uint256 &hSig, - unsigned char nonce - ) const -{ - uint256 dhsecret; - - if (crypto_scalarmult(dhsecret.begin(), esk.begin(), pk_enc.begin()) != 0) { - throw std::logic_error("Could not create DH secret"); - } - - // Regenerate keypair - uint256 epk = NoteEncryption::generate_pubkey(esk); - - unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE]; - KDF(K, dhsecret, epk, pk_enc, hSig, nonce); - - // The nonce is zero because we never reuse keys - unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {}; - - PaymentDisclosureNoteDecryption::Plaintext plaintext; - - // Message length is always NOTEENCRYPTION_AUTH_BYTES less than - // the ciphertext length. - if (crypto_aead_chacha20poly1305_ietf_decrypt(plaintext.begin(), NULL, - NULL, - ciphertext.begin(), PaymentDisclosureNoteDecryption::CLEN, - NULL, - 0, - cipher_nonce, K) != 0) { - throw note_decryption_failed(); - } - - return plaintext; -} - - - - template uint256 NoteEncryption::generate_privkey(const uint252 &a_sk) { @@ -461,6 +417,4 @@ uint252 random_uint252() template class NoteEncryption; template class NoteDecryption; -template class PaymentDisclosureNoteDecryption; - } diff --git a/src/zcash/NoteEncryption.hpp b/src/zcash/NoteEncryption.hpp index f6e692028..9c726d5cf 100644 --- a/src/zcash/NoteEncryption.hpp +++ b/src/zcash/NoteEncryption.hpp @@ -1,7 +1,4 @@ -/* -See the Zcash protocol specification for more information. -https://github.com/zcash/zips/blob/master/protocol/protocol.pdf -*/ +// Copyright (c) 2019-2020 The Hush developers #ifndef ZC_NOTE_ENCRYPTION_H_ #define ZC_NOTE_ENCRYPTION_H_ @@ -169,33 +166,9 @@ public: }; - -// Subclass PaymentDisclosureNoteDecryption provides a method to decrypt a note with esk. -template -class PaymentDisclosureNoteDecryption : public NoteDecryption { -protected: -public: - enum { CLEN=MLEN+NOTEENCRYPTION_AUTH_BYTES }; - typedef std::array Ciphertext; - typedef std::array Plaintext; - - PaymentDisclosureNoteDecryption() : NoteDecryption() {} - PaymentDisclosureNoteDecryption(uint256 sk_enc) : NoteDecryption(sk_enc) {} - - Plaintext decryptWithEsk( - const Ciphertext &ciphertext, - const uint256 &pk_enc, - const uint256 &esk, - const uint256 &hSig, - unsigned char nonce - ) const; -}; - } typedef libzcash::NoteEncryption ZCNoteEncryption; typedef libzcash::NoteDecryption ZCNoteDecryption; -typedef libzcash::PaymentDisclosureNoteDecryption ZCPaymentDisclosureNoteDecryption; - #endif /* ZC_NOTE_ENCRYPTION_H_ */