2018-04-01 07:32:10 -07:00
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
2018-04-03 20:14:22 -07:00
|
|
|
#include <main.h>
|
|
|
|
#include <utilmoneystr.h>
|
|
|
|
#include <chainparams.h>
|
|
|
|
#include <utilstrencodings.h>
|
|
|
|
#include <zcash/Address.hpp>
|
|
|
|
#include <wallet/wallet.h>
|
|
|
|
#include <amount.h>
|
2018-04-01 07:32:10 -07:00
|
|
|
#include <memory>
|
|
|
|
#include <string>
|
|
|
|
#include <set>
|
|
|
|
#include <vector>
|
|
|
|
#include <boost/filesystem.hpp>
|
|
|
|
#include <iostream>
|
2018-04-03 20:14:22 -07:00
|
|
|
#include <util.h>
|
2018-04-01 07:32:10 -07:00
|
|
|
|
2018-04-03 20:14:22 -07:00
|
|
|
#include <paymentdisclosure.h>
|
|
|
|
#include <paymentdisclosuredb.h>
|
2018-04-01 07:32:10 -07:00
|
|
|
|
2018-04-03 20:14:22 -07:00
|
|
|
#include <sodium.h>
|
2018-04-01 07:32:10 -07:00
|
|
|
|
|
|
|
#include <boost/uuid/uuid.hpp>
|
|
|
|
#include <boost/uuid/uuid_generators.hpp>
|
|
|
|
#include <boost/uuid/uuid_io.hpp>
|
|
|
|
|
|
|
|
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<std::mutex> 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) {
|
|
|
|
ECC_Start();
|
|
|
|
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.native() << std::endl;
|
|
|
|
|
|
|
|
PaymentDisclosureDBTest mydb(pathTemp);
|
|
|
|
|
|
|
|
for (int i=0; i<NUM_TRIES; i++) {
|
|
|
|
// Generate an ephemeral keypair for joinsplit sig.
|
|
|
|
uint256 joinSplitPubKey;
|
|
|
|
unsigned char buffer[crypto_sign_SECRETKEYBYTES] = {0};
|
|
|
|
crypto_sign_keypair(joinSplitPubKey.begin(), &buffer[0]);
|
|
|
|
|
|
|
|
// First 32 bytes contain private key, second 32 bytes contain public key.
|
|
|
|
ASSERT_EQ(0, memcmp(joinSplitPubKey.begin(), &buffer[0]+32, 32));
|
|
|
|
std::vector<unsigned char> 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<size_t>::max();
|
|
|
|
uint8_t n = random_uint256().GetCheapHash() % std::numeric_limits<uint8_t>::max();
|
|
|
|
PaymentDisclosureKey key { random_uint256(), js, n};
|
|
|
|
PaymentDisclosureInfo info;
|
|
|
|
info.esk = random_uint256();
|
|
|
|
info.joinSplitPrivKey = joinSplitPrivKey;
|
|
|
|
info.zaddr = libzcash::SpendingKey::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::SpendingKey::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
|
|
|
|
boost::array<unsigned char, 64> 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
|
|
|
|
|
|
|
|
ECC_Stop();
|
|
|
|
}
|