1 #include <gtest/gtest.h>
4 #include "utilmoneystr.h"
5 #include "chainparams.h"
6 #include "utilstrencodings.h"
7 #include "zcash/Address.hpp"
8 #include "wallet/wallet.h"
16 #include <boost/filesystem.hpp>
20 #include "wallet/paymentdisclosure.h"
21 #include "wallet/paymentdisclosuredb.h"
25 #include <boost/uuid/uuid.hpp>
26 #include <boost/uuid/uuid_generators.hpp>
27 #include <boost/uuid/uuid_io.hpp>
33 ./zcash-gtest --gtest_filter="paymentdisclosure.*"
35 Note: As an experimental feature, writing your own tests may require option flags to be set.
36 mapArgs["-experimentalfeatures"] = true;
37 mapArgs["-paymentdisclosure"] = true;
40 #define NUM_TRIES 10000
42 #define DUMP_DATABASE_TO_STDOUT false
44 static boost::uuids::random_generator uuidgen;
46 static uint256 random_uint256()
49 randombytes_buf(ret.begin(), 32);
53 // Subclass of PaymentDisclosureDB to add debugging methods
54 class PaymentDisclosureDBTest : public PaymentDisclosureDB {
56 PaymentDisclosureDBTest(const boost::filesystem::path& dbPath) : PaymentDisclosureDB(dbPath) {}
58 void DebugDumpAllStdout() {
59 ASSERT_NE(db, nullptr);
60 std::lock_guard<std::mutex> guard(lock_);
62 // Iterate over each item in the database and print them
63 leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
65 for (it->SeekToFirst(); it->Valid(); it->Next()) {
66 cout << it->key().ToString() << " : ";
67 // << it->value().ToString() << endl;
69 std::string strValue = it->value().ToString();
70 PaymentDisclosureInfo info;
71 CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION);
73 cout << info.ToString() << std::endl;
74 } catch (const std::exception& e) {
75 cout << e.what() << std::endl;
79 if (false == it->status().ok()) {
80 cerr << "An error was found iterating over the database" << endl;
81 cerr << it->status().ToString() << endl;
90 // This test creates random payment disclosure blobs and checks that they can be
91 // 1. inserted and retrieved from a database
92 // 2. serialized and deserialized without corruption
93 // Note that the zpd: prefix is not part of the payment disclosure blob itself. It is only
94 // used as convention to improve the user experience when sharing payment disclosure blobs.
95 TEST(paymentdisclosure, mainnet) {
96 SelectParams(CBaseChainParams::MAIN);
98 boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
99 boost::filesystem::create_directories(pathTemp);
100 mapArgs["-datadir"] = pathTemp.string();
102 std::cout << "Test payment disclosure database created in folder: " << pathTemp.string() << std::endl;
104 PaymentDisclosureDBTest mydb(pathTemp);
106 for (int i=0; i<NUM_TRIES; i++) {
107 // Generate an ephemeral keypair for joinsplit sig.
108 uint256 joinSplitPubKey;
109 unsigned char buffer[crypto_sign_SECRETKEYBYTES] = {0};
110 crypto_sign_keypair(joinSplitPubKey.begin(), &buffer[0]);
112 // First 32 bytes contain private key, second 32 bytes contain public key.
113 ASSERT_EQ(0, memcmp(joinSplitPubKey.begin(), &buffer[0]+32, 32));
114 std::vector<unsigned char> vch(&buffer[0], &buffer[0] + 32);
115 uint256 joinSplitPrivKey = uint256(vch);
117 // Create payment disclosure key and info data to store in test database
118 size_t js = random_uint256().GetCheapHash() % std::numeric_limits<size_t>::max();
119 uint8_t n = random_uint256().GetCheapHash() % std::numeric_limits<uint8_t>::max();
120 PaymentDisclosureKey key { random_uint256(), js, n};
121 PaymentDisclosureInfo info;
122 info.esk = random_uint256();
123 info.joinSplitPrivKey = joinSplitPrivKey;
124 info.zaddr = libzcash::SproutSpendingKey::random().address();
125 ASSERT_TRUE(mydb.Put(key, info));
127 // Retrieve info from test database into new local variable and test it matches
128 PaymentDisclosureInfo info2;
129 ASSERT_TRUE(mydb.Get(key, info2));
130 ASSERT_EQ(info, info2);
132 // Modify this local variable and confirm it no longer matches
133 info2.esk = random_uint256();
134 info2.joinSplitPrivKey = random_uint256();
135 info2.zaddr = libzcash::SproutSpendingKey::random().address();
136 ASSERT_NE(info, info2);
138 // Using the payment info object, let's create a dummy payload
139 PaymentDisclosurePayload payload;
140 payload.version = PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL;
141 payload.esk = info.esk;
142 payload.txid = key.hash;
145 payload.message = "random-" + boost::uuids::to_string(uuidgen()); // random message
146 payload.zaddr = info.zaddr;
148 // Serialize and hash the payload to generate a signature
149 uint256 dataToBeSigned = SerializeHash(payload, SER_GETHASH, 0);
151 // Compute the payload signature
152 unsigned char payloadSig[64];
153 if (!(crypto_sign_detached(&payloadSig[0], NULL,
154 dataToBeSigned.begin(), 32,
155 &buffer[0] // buffer containing both private and public key required
158 throw std::runtime_error("crypto_sign_detached failed");
162 if (!(crypto_sign_verify_detached(&payloadSig[0],
163 dataToBeSigned.begin(), 32,
164 joinSplitPubKey.begin()
167 throw std::runtime_error("crypto_sign_verify_detached failed");
170 // Convert signature buffer to boost array
171 std::array<unsigned char, 64> arrayPayloadSig;
172 memcpy(arrayPayloadSig.data(), &payloadSig[0], 64);
174 // Payment disclosure blob to pass around
175 PaymentDisclosure pd = {payload, arrayPayloadSig};
177 // Test payment disclosure constructors
178 PaymentDisclosure pd2(payload, arrayPayloadSig);
180 PaymentDisclosure pd3(joinSplitPubKey, key, info, payload.message);
183 // Verify serialization and deserialization works
184 CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
186 std::string ssHexString = HexStr(ss.begin(), ss.end());
188 PaymentDisclosure pdTmp;
189 CDataStream ssTmp(ParseHex(ssHexString), SER_NETWORK, PROTOCOL_VERSION);
191 ASSERT_EQ(pd, pdTmp);
193 CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION);
195 std::string ss2HexString = HexStr(ss2.begin(), ss2.end());
196 ASSERT_EQ(ssHexString, ss2HexString);
199 ASSERT_EQ(pd.payload.marker, PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES);
200 ASSERT_EQ(pdTmp.payload.marker, PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES);
201 ASSERT_EQ(0, ssHexString.find("706462ff")); // Little endian encoding of PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES value
204 PaymentDisclosure pdDummy;
205 ASSERT_NE(pd, pdDummy);
208 #if DUMP_DATABASE_TO_STDOUT == true
209 mydb.DebugDumpAllStdout();