Browse Source

desprout

pull/117/head
Duke Leto 4 years ago
parent
commit
ed2c9450f9
  1. 2
      src/Makefile.am
  2. 90
      src/gtest/test_transaction.cpp
  3. 44
      src/key_io.cpp
  4. 42
      src/keystore.cpp
  5. 8
      src/keystore.h
  6. 1
      src/paymentdisclosure.h
  7. 54
      src/rpc/misc.cpp
  8. 6
      src/test/rpc_wallet_tests.cpp
  9. 154
      src/utiltest.cpp
  10. 19
      src/utiltest.h
  11. 2
      src/wallet/asyncrpcoperation_shieldcoinbase.h
  12. 111
      src/wallet/crypter.cpp
  13. 570
      src/wallet/gtest/test_wallet.cpp
  14. 28
      src/wallet/rpcwallet.cpp
  15. 5
      src/wallet/wallet.h
  16. 16
      src/wallet/walletdb.cpp
  17. 11
      src/wallet/walletdb.h
  18. 38
      src/zcash/Address.cpp
  19. 34
      src/zcbenchmarks.cpp

2
src/Makefile.am

@ -347,8 +347,6 @@ endif
libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_wallet_a_SOURCES = \
utiltest.cpp \
utiltest.h \
zcbenchmarks.cpp \
zcbenchmarks.h \
wallet/asyncrpcoperation_mergetoaddress.cpp \

90
src/gtest/test_transaction.cpp

@ -1,90 +0,0 @@
#include <gtest/gtest.h>
#include "primitives/transaction.h"
#include "zcash/Note.hpp"
#include "zcash/Address.hpp"
#include <array>
extern ZCJoinSplit* params;
extern int GenZero(int n);
extern int GenMax(int n);
TEST(Transaction, JSDescriptionRandomized) {
// construct a merkle tree
SproutMerkleTree merkleTree;
libzcash::SproutSpendingKey k = libzcash::SproutSpendingKey::random();
libzcash::SproutPaymentAddress addr = k.address();
libzcash::SproutNote note(addr.a_pk, 100, uint256(), uint256());
// commitment from coin
uint256 commitment = note.cm();
// insert commitment into the merkle tree
merkleTree.append(commitment);
// compute the merkle root we will be working with
uint256 rt = merkleTree.root();
auto witness = merkleTree.witness();
// create JSDescription
uint256 joinSplitPubKey;
std::array<libzcash::JSInput, ZC_NUM_JS_INPUTS> inputs = {
libzcash::JSInput(witness, note, k),
libzcash::JSInput() // dummy input of zero value
};
std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS> outputs = {
libzcash::JSOutput(addr, 50),
libzcash::JSOutput(addr, 50)
};
std::array<size_t, ZC_NUM_JS_INPUTS> inputMap;
std::array<size_t, ZC_NUM_JS_OUTPUTS> outputMap;
{
auto jsdesc = JSDescription::Randomized(
false,
*params, joinSplitPubKey, rt,
inputs, outputs,
inputMap, outputMap,
0, 0, false);
std::set<size_t> inputSet(inputMap.begin(), inputMap.end());
std::set<size_t> expectedInputSet {0, 1};
EXPECT_EQ(expectedInputSet, inputSet);
std::set<size_t> outputSet(outputMap.begin(), outputMap.end());
std::set<size_t> expectedOutputSet {0, 1};
EXPECT_EQ(expectedOutputSet, outputSet);
}
{
auto jsdesc = JSDescription::Randomized(
false,
*params, joinSplitPubKey, rt,
inputs, outputs,
inputMap, outputMap,
0, 0, false, nullptr, GenZero);
std::array<size_t, ZC_NUM_JS_INPUTS> expectedInputMap {1, 0};
std::array<size_t, ZC_NUM_JS_OUTPUTS> expectedOutputMap {1, 0};
EXPECT_EQ(expectedInputMap, inputMap);
EXPECT_EQ(expectedOutputMap, outputMap);
}
{
auto jsdesc = JSDescription::Randomized(
false,
*params, joinSplitPubKey, rt,
inputs, outputs,
inputMap, outputMap,
0, 0, false, nullptr, GenMax);
std::array<size_t, ZC_NUM_JS_INPUTS> expectedInputMap {0, 1};
std::array<size_t, ZC_NUM_JS_OUTPUTS> expectedOutputMap {0, 1};
EXPECT_EQ(expectedInputMap, inputMap);
EXPECT_EQ(expectedOutputMap, outputMap);
}
}

44
src/key_io.cpp

@ -1,5 +1,6 @@
// Copyright (c) 2014-2016 The Bitcoin Core developers
// Copyright (c) 2016-2018 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.
@ -84,15 +85,6 @@ private:
public:
PaymentAddressEncoder(const CChainParams& params) : m_params(params) {}
std::string operator()(const libzcash::SproutPaymentAddress& zaddr) const
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << zaddr;
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS);
data.insert(data.end(), ss.begin(), ss.end());
return EncodeBase58Check(data);
}
std::string operator()(const libzcash::SaplingPaymentAddress& zaddr) const
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
@ -117,17 +109,6 @@ private:
public:
ViewingKeyEncoder(const CChainParams& params) : m_params(params) {}
std::string operator()(const libzcash::SproutViewingKey& vk) const
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << vk;
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::ZCVIEWING_KEY);
data.insert(data.end(), ss.begin(), ss.end());
std::string ret = EncodeBase58Check(data);
memory_cleanse(data.data(), data.size());
return ret;
}
std::string operator()(const libzcash::SaplingIncomingViewingKey& vk) const
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
@ -152,17 +133,6 @@ private:
public:
SpendingKeyEncoder(const CChainParams& params) : m_params(params) {}
std::string operator()(const libzcash::SproutSpendingKey& zkey) const
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << zkey;
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::ZCSPENDING_KEY);
data.insert(data.end(), ss.begin(), ss.end());
std::string ret = EncodeBase58Check(data);
memory_cleanse(data.data(), data.size());
return ret;
}
std::string operator()(const libzcash::SaplingExtendedSpendingKey& zkey) const
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
@ -324,18 +294,6 @@ std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr)
libzcash::PaymentAddress DecodePaymentAddress(const std::string& str)
{
std::vector<unsigned char> data;
if (DecodeBase58Check(str, data)) {
const std::vector<unsigned char>& zaddr_prefix = Params().Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS);
if ((data.size() == libzcash::SerializedSproutPaymentAddressSize + zaddr_prefix.size()) &&
std::equal(zaddr_prefix.begin(), zaddr_prefix.end(), data.begin())) {
CSerializeData serialized(data.begin() + zaddr_prefix.size(), data.end());
CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION);
libzcash::SproutPaymentAddress ret;
ss >> ret;
return ret;
}
}
data.clear();
auto bech = bech32::Decode(str);
if (bech.first == Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS) &&
bech.second.size() == ConvertedSaplingPaymentAddressSize) {

42
src/keystore.cpp

@ -1,5 +1,6 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core 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.
@ -128,15 +129,6 @@ bool CBasicKeyStore::HaveWatchOnly() const
return (!setWatchOnly.empty());
}
bool CBasicKeyStore::AddSproutSpendingKey(const libzcash::SproutSpendingKey &sk)
{
LOCK(cs_SpendingKeyStore);
auto address = sk.address();
mapSproutSpendingKeys[address] = sk;
mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(sk.receiving_key())));
return true;
}
//! Sapling
bool CBasicKeyStore::AddSaplingSpendingKey(
const libzcash::SaplingExtendedSpendingKey &sk,
@ -155,14 +147,6 @@ bool CBasicKeyStore::AddSaplingSpendingKey(
return true;
}
bool CBasicKeyStore::AddSproutViewingKey(const libzcash::SproutViewingKey &vk)
{
LOCK(cs_SpendingKeyStore);
auto address = vk.address();
mapSproutViewingKeys[address] = vk;
mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(vk.sk_enc)));
return true;
}
bool CBasicKeyStore::AddSaplingFullViewingKey(
const libzcash::SaplingFullViewingKey &fvk,
@ -190,18 +174,7 @@ bool CBasicKeyStore::AddSaplingIncomingViewingKey(
return true;
}
bool CBasicKeyStore::RemoveSproutViewingKey(const libzcash::SproutViewingKey &vk)
{
LOCK(cs_SpendingKeyStore);
mapSproutViewingKeys.erase(vk.address());
return true;
}
bool CBasicKeyStore::HaveSproutViewingKey(const libzcash::SproutPaymentAddress &address) const
{
LOCK(cs_SpendingKeyStore);
return mapSproutViewingKeys.count(address) > 0;
}
bool CBasicKeyStore::HaveSaplingFullViewingKey(const libzcash::SaplingIncomingViewingKey &ivk) const
{
@ -215,19 +188,6 @@ bool CBasicKeyStore::HaveSaplingIncomingViewingKey(const libzcash::SaplingPaymen
return mapSaplingIncomingViewingKeys.count(addr) > 0;
}
bool CBasicKeyStore::GetSproutViewingKey(
const libzcash::SproutPaymentAddress &address,
libzcash::SproutViewingKey &vkOut) const
{
LOCK(cs_SpendingKeyStore);
SproutViewingKeyMap::const_iterator mi = mapSproutViewingKeys.find(address);
if (mi != mapSproutViewingKeys.end()) {
vkOut = mi->second;
return true;
}
return false;
}
bool CBasicKeyStore::GetSaplingFullViewingKey(const libzcash::SaplingIncomingViewingKey &ivk,
libzcash::SaplingFullViewingKey &fvkOut) const
{

8
src/keystore.h

@ -120,12 +120,7 @@ protected:
KeyMap mapKeys;
ScriptMap mapScripts;
WatchOnlySet setWatchOnly;
/*
SproutSpendingKeyMap mapSproutSpendingKeys;
SproutViewingKeyMap mapSproutViewingKeys;
NoteDecryptorMap mapNoteDecryptors;
*/
SaplingSpendingKeyMap mapSaplingSpendingKeys;
SaplingFullViewingKeyMap mapSaplingFullViewingKeys;
SaplingIncomingViewingKeyMap mapSaplingIncomingViewingKeys;
@ -246,7 +241,6 @@ public:
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
typedef std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > CryptedKeyMap;
//typedef std::map<libzcash::SproutPaymentAddress, std::vector<unsigned char> > CryptedSproutSpendingKeyMap;
//! Sapling
typedef std::map<libzcash::SaplingExtendedFullViewingKey, std::vector<unsigned char> > CryptedSaplingSpendingKeyMap;

1
src/paymentdisclosure.h

@ -1,4 +1,5 @@
// 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.

54
src/rpc/misc.cpp

@ -204,7 +204,7 @@ UniValue getinfo(const UniValue& params, bool fHelp, const CPubKey& mypk)
" \"version\": xxxxx, (numeric) the server version\n"
" \"protocolversion\": xxxxx, (numeric) the protocol version\n"
" \"walletversion\": xxxxx, (numeric) the wallet version\n"
" \"balance\": xxxxxxx, (numeric) the total Komodo balance of the wallet\n"
" \"balance\": xxxxxxx, (numeric) the total Hush balance of the wallet\n"
" \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n"
" \"timeoffset\": xxxxx, (numeric) the time offset\n"
" \"connections\": xxxxx, (numeric) the number of connections\n"
@ -590,30 +590,6 @@ UniValue validateaddress(const UniValue& params, bool fHelp, const CPubKey& mypk
}
class DescribePaymentAddressVisitor : public boost::static_visitor<UniValue>
{
public:
UniValue operator()(const libzcash::InvalidEncoding &zaddr) const { return UniValue(UniValue::VOBJ); }
UniValue operator()(const libzcash::SaplingPaymentAddress &zaddr) const {
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("type", "sapling"));
obj.push_back(Pair("diversifier", HexStr(zaddr.d)));
obj.push_back(Pair("diversifiedtransmissionkey", zaddr.pk_d.GetHex()));
#ifdef ENABLE_WALLET
if (pwalletMain) {
libzcash::SaplingIncomingViewingKey ivk;
libzcash::SaplingFullViewingKey fvk;
bool isMine = pwalletMain->GetSaplingIncomingViewingKey(zaddr, ivk) &&
pwalletMain->GetSaplingFullViewingKey(ivk, fvk) &&
pwalletMain->HaveSaplingSpendingKey(fvk);
obj.push_back(Pair("ismine", isMine));
}
#endif
return obj;
}
};
UniValue z_validateaddress(const UniValue& params, bool fHelp, const CPubKey& mypk)
{
if (fHelp || params.size() != 1)
@ -650,11 +626,25 @@ UniValue z_validateaddress(const UniValue& params, bool fHelp, const CPubKey& my
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("isvalid", isValid));
if (isValid)
auto zaddr = boost::get<libzcash::SaplingPaymentAddress>(&address);
if (isValid && (zaddr != nullptr))
{
ret.push_back(Pair("address", strAddress));
UniValue detail = boost::apply_visitor(DescribePaymentAddressVisitor(), address);
ret.pushKVs(detail);
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("type", "sapling"));
obj.push_back(Pair("diversifier", HexStr(zaddr->d)));
obj.push_back(Pair("diversifiedtransmissionkey", zaddr->pk_d.GetHex()));
#ifdef ENABLE_WALLET
if (pwalletMain) {
libzcash::SaplingIncomingViewingKey ivk;
libzcash::SaplingFullViewingKey fvk;
bool isMine = pwalletMain->GetSaplingIncomingViewingKey(*zaddr, ivk) &&
pwalletMain->GetSaplingFullViewingKey(ivk, fvk) &&
pwalletMain->HaveSaplingSpendingKey(fvk);
obj.push_back(Pair("ismine", isMine));
}
#endif
ret.pushKVs(obj);
}
return ret;
}
@ -733,9 +723,9 @@ UniValue createmultisig(const UniValue& params, bool fHelp, const CPubKey& mypk)
"\nArguments:\n"
"1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n"
"2. \"keys\" (string, required) A json array of keys which are Komodo addresses or hex-encoded public keys\n"
"2. \"keys\" (string, required) A json array of keys which are Hush addresses or hex-encoded public keys\n"
" [\n"
" \"key\" (string) Komodo address or hex-encoded public key\n"
" \"key\" (string) Hush address or hex-encoded public key\n"
" ,...\n"
" ]\n"
@ -769,10 +759,10 @@ UniValue verifymessage(const UniValue& params, bool fHelp, const CPubKey& mypk)
{
if (fHelp || params.size() != 3)
throw runtime_error(
"verifymessage \"komodoaddress\" \"signature\" \"message\"\n"
"verifymessage \"hushaddress\" \"signature\" \"message\"\n"
"\nVerify a signed message\n"
"\nArguments:\n"
"1. \"komodoaddress\" (string, required) The Komodo address to use for the signature.\n"
"1. \"hushaddress\" (string, required) The Hush address to use for the signature.\n"
"2. \"signature\" (string, required) The signature provided by the signer in base 64 encoding (see signmessage).\n"
"3. \"message\" (string, required) The message that was signed.\n"
"\nResult:\n"

6
src/test/rpc_wallet_tests.cpp

@ -571,9 +571,8 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport)
// Verify number of addresses stored in wallet is n1+n2
int numAddrs = myaddrs.size();
BOOST_CHECK(numAddrs == (2 * n1) + n2);
pwalletMain->GetSproutPaymentAddresses(addrs);
pwalletMain->GetSaplingPaymentAddresses(saplingAddrs);
BOOST_CHECK(addrs.size() + saplingAddrs.size() == numAddrs);
BOOST_CHECK(saplingAddrs.size() == numAddrs);
// Ask wallet to list addresses
BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listaddresses"));
@ -595,9 +594,6 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport)
std::string newaddress = retValue.get_str();
auto address = DecodePaymentAddress(newaddress);
BOOST_CHECK(IsValidPaymentAddress(address));
BOOST_ASSERT(boost::get<libzcash::SproutPaymentAddress>(&address) != nullptr);
auto newAddr = boost::get<libzcash::SproutPaymentAddress>(address);
BOOST_CHECK(pwalletMain->HaveSproutSpendingKey(newAddr));
// Check if too many args
BOOST_CHECK_THROW(CallRPC("z_getnewaddress toomanyargs"), runtime_error);

154
src/utiltest.cpp

@ -1,154 +0,0 @@
// 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.
#include "utiltest.h"
#include "consensus/upgrades.h"
#include <array>
CWalletTx GetValidReceive(ZCJoinSplit& params,
const libzcash::SproutSpendingKey& sk, CAmount value,
bool randomInputs,
int32_t version /* = 2 */) {
CMutableTransaction mtx;
mtx.nVersion = version;
mtx.vin.resize(2);
if (randomInputs) {
mtx.vin[0].prevout.hash = GetRandHash();
mtx.vin[1].prevout.hash = GetRandHash();
} else {
mtx.vin[0].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000001");
mtx.vin[1].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000002");
}
mtx.vin[0].prevout.n = 0;
mtx.vin[1].prevout.n = 0;
// Generate an ephemeral keypair.
uint256 joinSplitPubKey;
unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES];
crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey);
mtx.joinSplitPubKey = joinSplitPubKey;
std::array<libzcash::JSInput, 2> inputs = {
libzcash::JSInput(), // dummy input
libzcash::JSInput() // dummy input
};
std::array<libzcash::JSOutput, 2> outputs = {
libzcash::JSOutput(sk.address(), value),
libzcash::JSOutput(sk.address(), value)
};
// Prepare JoinSplits
uint256 rt;
JSDescription jsdesc {params, mtx.joinSplitPubKey, rt,
inputs, outputs, 2*value, 0, false};
mtx.vjoinsplit.push_back(jsdesc);
if (version >= 4) {
// Shielded Output
OutputDescription od;
mtx.vShieldedOutput.push_back(od);
}
// Empty output script.
uint32_t consensusBranchId = SPROUT_BRANCH_ID;
CScript scriptCode;
CTransaction signTx(mtx);
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId);
// Add the signature
assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL,
dataToBeSigned.begin(), 32,
joinSplitPrivKey
) == 0);
CTransaction tx {mtx};
CWalletTx wtx {NULL, tx};
return wtx;
}
libzcash::SproutNote GetNote(ZCJoinSplit& params,
const libzcash::SproutSpendingKey& sk,
const CTransaction& tx, size_t js, size_t n) {
ZCNoteDecryption decryptor {sk.receiving_key()};
auto hSig = tx.vjoinsplit[js].h_sig(params, tx.joinSplitPubKey);
auto note_pt = libzcash::SproutNotePlaintext::decrypt(
decryptor,
tx.vjoinsplit[js].ciphertexts[n],
tx.vjoinsplit[js].ephemeralKey,
hSig,
(unsigned char) n);
return note_pt.note(sk.address());
}
CWalletTx GetValidSpend(ZCJoinSplit& params,
const libzcash::SproutSpendingKey& sk,
const libzcash::SproutNote& note, CAmount value) {
CMutableTransaction mtx;
mtx.vout.resize(2);
mtx.vout[0].nValue = value;
mtx.vout[1].nValue = 0;
// Generate an ephemeral keypair.
uint256 joinSplitPubKey;
unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES];
crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey);
mtx.joinSplitPubKey = joinSplitPubKey;
// Fake tree for the unused witness
SproutMerkleTree tree;
libzcash::JSOutput dummyout;
libzcash::JSInput dummyin;
{
if (note.value() > value) {
libzcash::SproutSpendingKey dummykey = libzcash::SproutSpendingKey::random();
libzcash::SproutPaymentAddress dummyaddr = dummykey.address();
dummyout = libzcash::JSOutput(dummyaddr, note.value() - value);
} else if (note.value() < value) {
libzcash::SproutSpendingKey dummykey = libzcash::SproutSpendingKey::random();
libzcash::SproutPaymentAddress dummyaddr = dummykey.address();
libzcash::SproutNote dummynote(dummyaddr.a_pk, (value - note.value()), uint256(), uint256());
tree.append(dummynote.cm());
dummyin = libzcash::JSInput(tree.witness(), dummynote, dummykey);
}
}
tree.append(note.cm());
std::array<libzcash::JSInput, 2> inputs = {
libzcash::JSInput(tree.witness(), note, sk),
dummyin
};
std::array<libzcash::JSOutput, 2> outputs = {
dummyout, // dummy output
libzcash::JSOutput() // dummy output
};
// Prepare JoinSplits
uint256 rt = tree.root();
JSDescription jsdesc {params, mtx.joinSplitPubKey, rt,
inputs, outputs, 0, value, false};
mtx.vjoinsplit.push_back(jsdesc);
// Empty output script.
uint32_t consensusBranchId = SPROUT_BRANCH_ID;
CScript scriptCode;
CTransaction signTx(mtx);
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId);
// Add the signature
assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL,
dataToBeSigned.begin(), 32,
joinSplitPrivKey
) == 0);
CTransaction tx {mtx};
CWalletTx wtx {NULL, tx};
return wtx;
}

19
src/utiltest.h

@ -1,19 +0,0 @@
// Copyright (c) 2016 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "wallet/wallet.h"
#include "zcash/JoinSplit.hpp"
#include "zcash/Note.hpp"
#include "zcash/NoteEncryption.hpp"
CWalletTx GetValidReceive(ZCJoinSplit& params,
const libzcash::SproutSpendingKey& sk, CAmount value,
bool randomInputs,
int32_t version = 2);
libzcash::SproutNote GetNote(ZCJoinSplit& params,
const libzcash::SproutSpendingKey& sk,
const CTransaction& tx, size_t js, size_t n);
CWalletTx GetValidSpend(ZCJoinSplit& params,
const libzcash::SproutSpendingKey& sk,
const libzcash::SproutNote& note, CAmount value);

2
src/wallet/asyncrpcoperation_shieldcoinbase.h

@ -1,4 +1,5 @@
// 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.
@ -123,7 +124,6 @@ public:
ShieldToAddress(AsyncRPCOperation_shieldcoinbase *op, CAmount sendAmount) :
m_op(op), sendAmount(sendAmount) {}
bool operator()(const libzcash::SproutPaymentAddress &zaddr) const;
bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
bool operator()(const libzcash::InvalidEncoding& no) const;
};

111
src/wallet/crypter.cpp

@ -1,6 +1,7 @@
// Copyright (c) 2009-2013 The Bitcoin Core 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.
// file COPYING or https://www.opensource.org/licenses/mit-license.php
/******************************************************************************
* Copyright © 2014-2019 The SuperNET Developers. *
@ -168,22 +169,6 @@ static bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsi
return key.VerifyPubKey(vchPubKey);
}
static bool DecryptSproutSpendingKey(const CKeyingMaterial& vMasterKey,
const std::vector<unsigned char>& vchCryptedSecret,
const libzcash::SproutPaymentAddress& address,
libzcash::SproutSpendingKey& sk)
{
CKeyingMaterial vchSecret;
if (!DecryptSecret(vMasterKey, vchCryptedSecret, address.GetHash(), vchSecret))
return false;
if (vchSecret.size() != libzcash::SerializedSproutSpendingKeySize)
return false;
CSecureDataStream ss(vchSecret, SER_NETWORK, PROTOCOL_VERSION);
ss >> sk;
return sk.address() == address;
}
static bool DecryptSaplingSpendingKey(const CKeyingMaterial& vMasterKey,
const std::vector<unsigned char>& vchCryptedSecret,
@ -207,7 +192,7 @@ bool CCryptoKeyStore::SetCrypted()
LOCK2(cs_KeyStore, cs_SpendingKeyStore);
if (fUseCrypto)
return true;
if (!(mapKeys.empty() && mapSproutSpendingKeys.empty() && mapSaplingSpendingKeys.empty()))
if (!(mapKeys.empty() && mapSaplingSpendingKeys.empty()))
return false;
fUseCrypto = true;
return true;
@ -260,21 +245,6 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
if (fDecryptionThoroughlyChecked)
break;
}
CryptedSproutSpendingKeyMap::const_iterator miSprout = mapCryptedSproutSpendingKeys.begin();
for (; miSprout != mapCryptedSproutSpendingKeys.end(); ++miSprout)
{
const libzcash::SproutPaymentAddress &address = (*miSprout).first;
const std::vector<unsigned char> &vchCryptedSecret = (*miSprout).second;
libzcash::SproutSpendingKey sk;
if (!DecryptSproutSpendingKey(vMasterKeyIn, vchCryptedSecret, address, sk))
{
keyFail = true;
break;
}
keyPass = true;
if (fDecryptionThoroughlyChecked)
break;
}
CryptedSaplingSpendingKeyMap::const_iterator miSapling = mapCryptedSaplingSpendingKeys.begin();
for (; miSapling != mapCryptedSaplingSpendingKeys.end(); ++miSapling)
{
@ -292,7 +262,7 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
}
if (keyPass && keyFail)
{
LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n");
LogPrintf("Oh shit! The wallet is probably corrupted: Some keys decrypt but not all.\n");
assert(false);
}
if (keyFail || !keyPass)
@ -440,30 +410,6 @@ bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) co
return false;
}
bool CCryptoKeyStore::AddSproutSpendingKey(const libzcash::SproutSpendingKey &sk)
{
{
LOCK(cs_SpendingKeyStore);
if (!IsCrypted())
return CBasicKeyStore::AddSproutSpendingKey(sk);
if (IsLocked())
return false;
std::vector<unsigned char> vchCryptedSecret;
CSecureDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << sk;
CKeyingMaterial vchSecret(ss.begin(), ss.end());
auto address = sk.address();
if (!EncryptSecret(vMasterKey, vchSecret, address.GetHash(), vchCryptedSecret))
return false;
if (!AddCryptedSproutSpendingKey(address, sk.receiving_key(), vchCryptedSecret))
return false;
}
return true;
}
bool CCryptoKeyStore::AddSaplingSpendingKey(
const libzcash::SaplingExtendedSpendingKey &sk,
const libzcash::SaplingPaymentAddress &defaultAddr)
@ -494,22 +440,6 @@ bool CCryptoKeyStore::AddSaplingSpendingKey(
return true;
}
bool CCryptoKeyStore::AddCryptedSproutSpendingKey(
const libzcash::SproutPaymentAddress &address,
const libzcash::ReceivingKey &rk,
const std::vector<unsigned char> &vchCryptedSecret)
{
{
LOCK(cs_SpendingKeyStore);
if (!SetCrypted())
return false;
mapCryptedSproutSpendingKeys[address] = vchCryptedSecret;
mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(rk)));
}
return true;
}
bool CCryptoKeyStore::AddCryptedSaplingSpendingKey(
const libzcash::SaplingExtendedFullViewingKey &extfvk,
const std::vector<unsigned char> &vchCryptedSecret,
@ -531,23 +461,6 @@ bool CCryptoKeyStore::AddCryptedSaplingSpendingKey(
return true;
}
bool CCryptoKeyStore::GetSproutSpendingKey(const libzcash::SproutPaymentAddress &address, libzcash::SproutSpendingKey &skOut) const
{
{
LOCK(cs_SpendingKeyStore);
if (!IsCrypted())
return CBasicKeyStore::GetSproutSpendingKey(address, skOut);
CryptedSproutSpendingKeyMap::const_iterator mi = mapCryptedSproutSpendingKeys.find(address);
if (mi != mapCryptedSproutSpendingKeys.end())
{
const std::vector<unsigned char> &vchCryptedSecret = (*mi).second;
return DecryptSproutSpendingKey(vMasterKey, vchCryptedSecret, address, skOut);
}
}
return false;
}
bool CCryptoKeyStore::GetSaplingSpendingKey(const libzcash::SaplingFullViewingKey &fvk, libzcash::SaplingExtendedSpendingKey &skOut) const
{
{
@ -603,22 +516,6 @@ bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
}
}
mapKeys.clear();
BOOST_FOREACH(SproutSpendingKeyMap::value_type& mSproutSpendingKey, mapSproutSpendingKeys)
{
const libzcash::SproutSpendingKey &sk = mSproutSpendingKey.second;
CSecureDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << sk;
CKeyingMaterial vchSecret(ss.begin(), ss.end());
libzcash::SproutPaymentAddress address = sk.address();
std::vector<unsigned char> vchCryptedSecret;
if (!EncryptSecret(vMasterKeyIn, vchSecret, address.GetHash(), vchCryptedSecret)) {
return false;
}
if (!AddCryptedSproutSpendingKey(address, sk.receiving_key(), vchCryptedSecret)) {
return false;
}
}
mapSproutSpendingKeys.clear();
//! Sapling key support
BOOST_FOREACH(SaplingSpendingKeyMap::value_type& mSaplingSpendingKey, mapSaplingSpendingKeys)
{

570
src/wallet/gtest/test_wallet.cpp

@ -139,341 +139,8 @@ TEST(WalletTests, SetupDatadirLocationRunAsFirstTest) {
boost::filesystem::create_directories(pathTemp);
mapArgs["-datadir"] = pathTemp.string();
}
/*
TEST(WalletTests, SproutNoteDataSerialisation) {
auto sk = libzcash::SproutSpendingKey::random();
auto wtx = GetValidReceive(sk, 10, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
mapSproutNoteData_t noteData;
JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
SproutNoteData nd {sk.address(), nullifier};
SproutMerkleTree tree;
nd.witnesses.push_front(tree.witness());
noteData[jsoutpt] = nd;
CDataStream ss(SER_DISK, CLIENT_VERSION);
ss << noteData;
mapSproutNoteData_t noteData2;
ss >> noteData2;
EXPECT_EQ(noteData, noteData2);
EXPECT_EQ(noteData[jsoutpt].witnesses, noteData2[jsoutpt].witnesses);
}
TEST(WalletTests, FindUnspentSproutNotes) {
SelectParams(CBaseChainParams::TESTNET);
CWallet wallet;
auto sk = libzcash::SproutSpendingKey::random();
wallet.AddSproutSpendingKey(sk);
auto wtx = GetValidReceive(sk, 10, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
mapSproutNoteData_t noteData;
JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
SproutNoteData nd {sk.address(), nullifier};
noteData[jsoutpt] = nd;
wtx.SetSproutNoteData(noteData);
wallet.AddToWallet(wtx, true, NULL);
EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
// We currently have an unspent and unconfirmed note in the wallet (depth of -1)
std::vector<CSproutNotePlaintextEntry> sproutEntries;
std::vector<SaplingNoteEntry> saplingEntries;
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0);
EXPECT_EQ(0, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", -1);
EXPECT_EQ(1, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
// Fake-mine the transaction
EXPECT_EQ(-1, chainActive.Height());
CBlock block;
block.vtx.push_back(wtx);
block.hashMerkleRoot = block.BuildMerkleTree();
auto blockHash = block.GetHash();
CBlockIndex fakeIndex {block};
mapBlockIndex.insert(std::make_pair(blockHash, &fakeIndex));
chainActive.SetTip(&fakeIndex);
EXPECT_TRUE(chainActive.Contains(&fakeIndex));
EXPECT_EQ(0, chainActive.Height());
wtx.SetMerkleBranch(block);
wallet.AddToWallet(wtx, true, NULL);
EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
// We now have an unspent and confirmed note in the wallet (depth of 1)
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0);
EXPECT_EQ(1, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1);
EXPECT_EQ(1, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2);
EXPECT_EQ(0, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
// Let's spend the note.
auto wtx2 = GetValidSpend(sk, note, 5);
wallet.AddToWallet(wtx2, true, NULL);
EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
// Fake-mine a spend transaction
EXPECT_EQ(0, chainActive.Height());
CBlock block2;
block2.vtx.push_back(wtx2);
block2.hashMerkleRoot = block2.BuildMerkleTree();
block2.hashPrevBlock = blockHash;
auto blockHash2 = block2.GetHash();
CBlockIndex fakeIndex2 {block2};
mapBlockIndex.insert(std::make_pair(blockHash2, &fakeIndex2));
fakeIndex2.SetHeight(1);
chainActive.SetTip(&fakeIndex2);
EXPECT_TRUE(chainActive.Contains(&fakeIndex2));
EXPECT_EQ(1, chainActive.Height());
wtx2.SetMerkleBranch(block2);
wallet.AddToWallet(wtx2, true, NULL);
EXPECT_TRUE(wallet.IsSproutSpent(nullifier));
// The note has been spent. By default, GetFilteredNotes() ignores spent notes.
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0);
EXPECT_EQ(0, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
// Let's include spent notes to retrieve it.
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0, false);
EXPECT_EQ(1, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
// The spent note has two confirmations.
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, false);
EXPECT_EQ(1, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
// It does not have 3 confirmations.
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 3, false);
EXPECT_EQ(0, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
// Let's receive a new note
CWalletTx wtx3;
{
auto wtx = GetValidReceive(sk, 20, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
mapSproutNoteData_t noteData;
JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
SproutNoteData nd {sk.address(), nullifier};
noteData[jsoutpt] = nd;
wtx.SetSproutNoteData(noteData);
wallet.AddToWallet(wtx, true, NULL);
EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
wtx3 = wtx;
}
// Fake-mine the new transaction
EXPECT_EQ(1, chainActive.Height());
CBlock block3;
block3.vtx.push_back(wtx3);
block3.hashMerkleRoot = block3.BuildMerkleTree();
block3.hashPrevBlock = blockHash2;
auto blockHash3 = block3.GetHash();
CBlockIndex fakeIndex3 {block3};
mapBlockIndex.insert(std::make_pair(blockHash3, &fakeIndex3));
fakeIndex3.SetHeight(2);
chainActive.SetTip(&fakeIndex3);
EXPECT_TRUE(chainActive.Contains(&fakeIndex3));
EXPECT_EQ(2, chainActive.Height());
wtx3.SetMerkleBranch(block3);
wallet.AddToWallet(wtx3, true, NULL);
// We now have an unspent note which has one confirmation, in addition to our spent note.
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1);
EXPECT_EQ(1, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
// Let's return the spent note too.
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1, false);
EXPECT_EQ(2, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
// Increasing number of confirmations will exclude our new unspent note.
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, false);
EXPECT_EQ(1, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
// If we also ignore spent notes at this depth, we won't find any notes.
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, true);
EXPECT_EQ(0, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
// Tear down
chainActive.SetTip(NULL);
mapBlockIndex.erase(blockHash);
mapBlockIndex.erase(blockHash2);
mapBlockIndex.erase(blockHash3);
}
TEST(WalletTests, SetSproutNoteAddrsInCWalletTx) {
auto sk = libzcash::SproutSpendingKey::random();
auto wtx = GetValidReceive(sk, 10, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
EXPECT_EQ(0, wtx.mapSproutNoteData.size());
mapSproutNoteData_t noteData;
JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
SproutNoteData nd {sk.address(), nullifier};
noteData[jsoutpt] = nd;
wtx.SetSproutNoteData(noteData);
EXPECT_EQ(noteData, wtx.mapSproutNoteData);
}
TEST(WalletTests, SetSaplingNoteAddrsInCWalletTx) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
auto consensusParams = Params().GetConsensus();
TestWallet wallet;
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
HDSeed seed(rawSeed);
auto sk = libzcash::SaplingExtendedSpendingKey::Master(seed);
auto expsk = sk.expsk;
auto fvk = expsk.full_viewing_key();
auto ivk = fvk.in_viewing_key();
auto pk = sk.DefaultAddress();
libzcash::SaplingNote note(pk, 50000);
auto cm = note.cm().get();
SaplingMerkleTree tree;
tree.append(cm);
auto anchor = tree.root();
auto witness = tree.witness();
auto nf = note.nullifier(fvk, witness.position());
ASSERT_TRUE(nf);
uint256 nullifier = nf.get();
auto builder = TransactionBuilder(consensusParams, 1);
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
builder.AddSaplingOutput(fvk.ovk, pk, 50000, {});
builder.SetFee(0);
auto maybe_tx = builder.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
auto tx = maybe_tx.get();
CWalletTx wtx {&wallet, tx};
EXPECT_EQ(0, wtx.mapSaplingNoteData.size());
mapSaplingNoteData_t noteData;
SaplingOutPoint op {wtx.GetHash(), 0};
SaplingNoteData nd;
nd.nullifier = nullifier;
nd.ivk = ivk;
nd.witnesses.push_front(witness);
nd.witnessHeight = 123;
noteData.insert(std::make_pair(op, nd));
wtx.SetSaplingNoteData(noteData);
EXPECT_EQ(noteData, wtx.mapSaplingNoteData);
// Test individual fields in case equality operator is defined/changed.
EXPECT_EQ(ivk, wtx.mapSaplingNoteData[op].ivk);
EXPECT_EQ(nullifier, wtx.mapSaplingNoteData[op].nullifier);
EXPECT_EQ(nd.witnessHeight, wtx.mapSaplingNoteData[op].witnessHeight);
EXPECT_TRUE(witness == wtx.mapSaplingNoteData[op].witnesses.front());
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
TEST(WalletTests, SetSproutInvalidNoteAddrsInCWalletTx) {
CWalletTx wtx;
EXPECT_EQ(0, wtx.mapSproutNoteData.size());
mapSproutNoteData_t noteData;
auto sk = libzcash::SproutSpendingKey::random();
JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
SproutNoteData nd {sk.address(), uint256()};
noteData[jsoutpt] = nd;
EXPECT_THROW(wtx.SetSproutNoteData(noteData), std::logic_error);
}
// The following test is the same as SetInvalidSaplingNoteDataInCWalletTx
// TEST(WalletTests, SetSaplingInvalidNoteAddrsInCWalletTx)
// Cannot add note data for an index which does not exist in tx.vShieldedOutput
TEST(WalletTests, SetInvalidSaplingNoteDataInCWalletTx) {
CWalletTx wtx;
EXPECT_EQ(0, wtx.mapSaplingNoteData.size());
mapSaplingNoteData_t noteData;
SaplingOutPoint op {uint256(), 1};
SaplingNoteData nd;
noteData.insert(std::make_pair(op, nd));
EXPECT_THROW(wtx.SetSaplingNoteData(noteData), std::logic_error);
}
TEST(WalletTests, GetSproutNoteNullifier) {
CWallet wallet;
auto sk = libzcash::SproutSpendingKey::random();
auto address = sk.address();
auto dec = ZCNoteDecryption(sk.receiving_key());
auto wtx = GetValidReceive(sk, 10, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
auto hSig = wtx.vjoinsplit[0].h_sig(
*params, wtx.joinSplitPubKey);
auto ret = wallet.GetSproutNoteNullifier(
wtx.vjoinsplit[0],
address,
dec,
hSig, 1);
EXPECT_NE(nullifier, ret);
wallet.AddSproutSpendingKey(sk);
ret = wallet.GetSproutNoteNullifier(
wtx.vjoinsplit[0],
address,
dec,
hSig, 1);
EXPECT_EQ(nullifier, ret);
}
TEST(WalletTests, FindMySaplingNotes) {
SelectParams(CBaseChainParams::REGTEST);
@ -577,92 +244,6 @@ TEST(WalletTests, FindMySaplingNotesWithIvkOnly) {
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
TEST(WalletTests, FindMySproutNotes) {
CWallet wallet;
auto sk = libzcash::SproutSpendingKey::random();
auto sk2 = libzcash::SproutSpendingKey::random();
wallet.AddSproutSpendingKey(sk2);
auto wtx = GetValidReceive(sk, 10, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
auto noteMap = wallet.FindMySproutNotes(wtx);
EXPECT_EQ(0, noteMap.size());
wallet.AddSproutSpendingKey(sk);
noteMap = wallet.FindMySproutNotes(wtx);
EXPECT_EQ(2, noteMap.size());
JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
SproutNoteData nd {sk.address(), nullifier};
EXPECT_EQ(1, noteMap.count(jsoutpt));
EXPECT_EQ(nd, noteMap[jsoutpt]);
}
TEST(WalletTests, FindMySproutNotesInEncryptedWallet) {
TestWallet wallet;
uint256 r {GetRandHash()};
CKeyingMaterial vMasterKey (r.begin(), r.end());
auto sk = libzcash::SproutSpendingKey::random();
wallet.AddSproutSpendingKey(sk);
ASSERT_TRUE(wallet.EncryptKeys(vMasterKey));
auto wtx = GetValidReceive(sk, 10, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
auto noteMap = wallet.FindMySproutNotes(wtx);
EXPECT_EQ(2, noteMap.size());
JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
SproutNoteData nd {sk.address(), nullifier};
EXPECT_EQ(1, noteMap.count(jsoutpt));
EXPECT_NE(nd, noteMap[jsoutpt]);
ASSERT_TRUE(wallet.Unlock(vMasterKey));
noteMap = wallet.FindMySproutNotes(wtx);
EXPECT_EQ(2, noteMap.size());
EXPECT_EQ(1, noteMap.count(jsoutpt));
EXPECT_EQ(nd, noteMap[jsoutpt]);
}
TEST(WalletTests, GetConflictedSproutNotes) {
CWallet wallet;
auto sk = libzcash::SproutSpendingKey::random();
wallet.AddSproutSpendingKey(sk);
auto wtx = GetValidReceive(sk, 10, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
auto wtx2 = GetValidSpend(sk, note, 5);
auto wtx3 = GetValidSpend(sk, note, 10);
auto hash2 = wtx2.GetHash();
auto hash3 = wtx3.GetHash();
// No conflicts for no spends
EXPECT_EQ(0, wallet.GetConflicts(hash2).size());
wallet.AddToWallet(wtx, true, NULL);
EXPECT_EQ(0, wallet.GetConflicts(hash2).size());
// No conflicts for one spend
wallet.AddToWallet(wtx2, true, NULL);
EXPECT_EQ(0, wallet.GetConflicts(hash2).size());
// Conflicts for two spends
wallet.AddToWallet(wtx3, true, NULL);
auto c3 = wallet.GetConflicts(hash2);
EXPECT_EQ(2, c3.size());
EXPECT_EQ(std::set<uint256>({hash2, hash3}), c3);
}
// Generate note A and spend to create note B, from which we spend to create two conflicting transactions
TEST(WalletTests, GetConflictedSaplingNotes) {
SelectParams(CBaseChainParams::REGTEST);
@ -1735,88 +1316,6 @@ TEST(WalletTests, SetBestChainIgnoresTxsWithoutShieldedData) {
wallet.SetBestChain(walletdb, loc);
}
TEST(WalletTests, UpdateSproutNullifierNoteMap) {
TestWallet wallet;
uint256 r {GetRandHash()};
CKeyingMaterial vMasterKey (r.begin(), r.end());
auto sk = libzcash::SproutSpendingKey::random();
wallet.AddSproutSpendingKey(sk);
ASSERT_TRUE(wallet.EncryptKeys(vMasterKey));
auto wtx = GetValidReceive(sk, 10, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
// Pretend that we called FindMySproutNotes while the wallet was locked
mapSproutNoteData_t noteData;
JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
SproutNoteData nd {sk.address()};
noteData[jsoutpt] = nd;
wtx.SetSproutNoteData(noteData);
wallet.AddToWallet(wtx, true, NULL);
EXPECT_EQ(0, wallet.mapSproutNullifiersToNotes.count(nullifier));
EXPECT_FALSE(wallet.UpdateNullifierNoteMap());
ASSERT_TRUE(wallet.Unlock(vMasterKey));
EXPECT_TRUE(wallet.UpdateNullifierNoteMap());
EXPECT_EQ(1, wallet.mapSproutNullifiersToNotes.count(nullifier));
EXPECT_EQ(wtx.GetHash(), wallet.mapSproutNullifiersToNotes[nullifier].hash);
EXPECT_EQ(0, wallet.mapSproutNullifiersToNotes[nullifier].js);
EXPECT_EQ(1, wallet.mapSproutNullifiersToNotes[nullifier].n);
}
TEST(WalletTests, UpdatedSproutNoteData) {
TestWallet wallet;
auto sk = libzcash::SproutSpendingKey::random();
wallet.AddSproutSpendingKey(sk);
auto wtx = GetValidReceive(sk, 10, true);
auto note = GetNote(sk, wtx, 0, 0);
auto note2 = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
auto nullifier2 = note2.nullifier(sk);
auto wtx2 = wtx;
// First pretend we added the tx to the wallet and
// we don't have the key for the second note
mapSproutNoteData_t noteData;
JSOutPoint jsoutpt {wtx.GetHash(), 0, 0};
SproutNoteData nd {sk.address(), nullifier};
noteData[jsoutpt] = nd;
wtx.SetSproutNoteData(noteData);
// Pretend we mined the tx by adding a fake witness
SproutMerkleTree tree;
wtx.mapSproutNoteData[jsoutpt].witnesses.push_front(tree.witness());
wtx.mapSproutNoteData[jsoutpt].witnessHeight = 100;
// Now pretend we added the key for the second note, and
// the tx was "added" to the wallet again to update it.
// This happens via the 'z_importkey' RPC method.
JSOutPoint jsoutpt2 {wtx2.GetHash(), 0, 1};
SproutNoteData nd2 {sk.address(), nullifier2};
noteData[jsoutpt2] = nd2;
wtx2.SetSproutNoteData(noteData);
// The txs should initially be different
EXPECT_NE(wtx.mapSproutNoteData, wtx2.mapSproutNoteData);
EXPECT_EQ(1, wtx.mapSproutNoteData[jsoutpt].witnesses.size());
EXPECT_EQ(100, wtx.mapSproutNoteData[jsoutpt].witnessHeight);
// After updating, they should be the same
EXPECT_TRUE(wallet.UpdatedNoteData(wtx2, wtx));
EXPECT_EQ(wtx.mapSproutNoteData, wtx2.mapSproutNoteData);
EXPECT_EQ(1, wtx.mapSproutNoteData[jsoutpt].witnesses.size());
EXPECT_EQ(100, wtx.mapSproutNoteData[jsoutpt].witnessHeight);
// TODO: The new note should get witnessed (but maybe not here) (#1350)
}
TEST(WalletTests, UpdatedSaplingNoteData) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
@ -1941,37 +1440,6 @@ TEST(WalletTests, UpdatedSaplingNoteData) {
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
TEST(WalletTests, MarkAffectedSproutTransactionsDirty) {
TestWallet wallet;
auto sk = libzcash::SproutSpendingKey::random();
wallet.AddSproutSpendingKey(sk);
auto wtx = GetValidReceive(sk, 10, true);
auto hash = wtx.GetHash();
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
auto wtx2 = GetValidSpend(sk, note, 5);
mapSproutNoteData_t noteData;
JSOutPoint jsoutpt {hash, 0, 1};
SproutNoteData nd {sk.address(), nullifier};
noteData[jsoutpt] = nd;
wtx.SetSproutNoteData(noteData);
wallet.AddToWallet(wtx, true, NULL);
wallet.MarkAffectedTransactionsDirty(wtx);
// After getting a cached value, the first tx should be clean
wallet.mapWallet[hash].GetDebit(ISMINE_ALL);
EXPECT_TRUE(wallet.mapWallet[hash].fDebitCached);
// After adding the note spend, the first tx should be dirty
wallet.AddToWallet(wtx2, true, NULL);
wallet.MarkAffectedTransactionsDirty(wtx2);
EXPECT_FALSE(wallet.mapWallet[hash].fDebitCached);
}
TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
@ -1999,7 +1467,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) {
auto scriptPubKey = GetScriptForDestination(tsk.GetPubKey().GetID());
// Generate shielding tx from transparent to Sapling
// 0.0005 t-ZEC in, 0.0004 z-ZEC out, 0.0001 t-ZEC fee
// 0.0005 t-HUSH in, 0.0004 z-HUSH out, 0.0001 t-HUSH fee
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
builder.AddTransparentInput(COutPoint(), scriptPubKey, 50000);
builder.AddSaplingOutput(fvk.ovk, pk, 40000, {});
@ -2093,39 +1561,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) {
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
TEST(WalletTests, SproutNoteLocking) {
TestWallet wallet;
auto sk = libzcash::SproutSpendingKey::random();
wallet.AddSproutSpendingKey(sk);
auto wtx = GetValidReceive(sk, 10, true);
auto wtx2 = GetValidReceive(sk, 10, true);
JSOutPoint jsoutpt {wtx.GetHash(), 0, 0};
JSOutPoint jsoutpt2 {wtx2.GetHash(),0, 0};
// Test selective locking
wallet.LockNote(jsoutpt);
EXPECT_TRUE(wallet.IsLockedNote(jsoutpt));
EXPECT_FALSE(wallet.IsLockedNote(jsoutpt2));
// Test selective unlocking
wallet.UnlockNote(jsoutpt);
EXPECT_FALSE(wallet.IsLockedNote(jsoutpt));
// Test multiple locking
wallet.LockNote(jsoutpt);
wallet.LockNote(jsoutpt2);
EXPECT_TRUE(wallet.IsLockedNote(jsoutpt));
EXPECT_TRUE(wallet.IsLockedNote(jsoutpt2));
// Test unlock all
wallet.UnlockAllSproutNotes();
EXPECT_FALSE(wallet.IsLockedNote(jsoutpt));
EXPECT_FALSE(wallet.IsLockedNote(jsoutpt2));
}
*/
TEST(WalletTests, SaplingNoteLocking) {
TestWallet wallet;

28
src/wallet/rpcwallet.cpp

@ -5487,34 +5487,6 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp
"Cannot send between Sprout and Sapling addresses using z_mergetoaddress");
}
// Find unspent notes and update estimated size
for (const CSproutNotePlaintextEntry& entry : sproutEntries) {
noteCounter++;
CAmount nValue = entry.plaintext.value();
if (!maxedOutNotesFlag) {
// If we haven't added any notes yet and the merge is to a
// z-address, we have already accounted for the first JoinSplit.
size_t increase = (sproutNoteInputs.empty() && !isToSproutZaddr) || (sproutNoteInputs.size() % 2 == 0) ? JOINSPLIT_SIZE : 0;
if (estimatedTxSize + increase >= max_tx_size ||
(sproutNoteLimit > 0 && noteCounter > sproutNoteLimit))
{
maxedOutNotesFlag = true;
} else {
estimatedTxSize += increase;
auto zaddr = entry.address;
SproutSpendingKey zkey;
pwalletMain->GetSproutSpendingKey(zaddr, zkey);
sproutNoteInputs.emplace_back(entry.jsop, entry.plaintext.note(zaddr), nValue, zkey);
mergedNoteValue += nValue;
}
}
if (maxedOutNotesFlag) {
remainingNoteValue += nValue;
}
}
for (const SaplingNoteEntry& entry : saplingEntries) {
noteCounter++;
CAmount nValue = entry.note.value();

5
src/wallet/wallet.h

@ -1408,7 +1408,6 @@ private:
public:
PaymentAddressBelongsToWallet(CWallet *wallet) : m_wallet(wallet) {}
bool operator()(const libzcash::SproutPaymentAddress &zaddr) const;
bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
bool operator()(const libzcash::InvalidEncoding& no) const;
};
@ -1421,7 +1420,6 @@ private:
public:
IncomingViewingKeyBelongsToWallet(CWallet *wallet) : m_wallet(wallet) {}
bool operator()(const libzcash::SproutPaymentAddress &zaddr) const;
bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
bool operator()(const libzcash::InvalidEncoding& no) const;
};
@ -1433,7 +1431,6 @@ private:
public:
HaveSpendingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
bool operator()(const libzcash::SproutPaymentAddress &zaddr) const;
bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
bool operator()(const libzcash::InvalidEncoding& no) const;
};
@ -1445,7 +1442,6 @@ private:
public:
GetSpendingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
boost::optional<libzcash::SpendingKey> operator()(const libzcash::SproutPaymentAddress &zaddr) const;
boost::optional<libzcash::SpendingKey> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
boost::optional<libzcash::SpendingKey> operator()(const libzcash::InvalidEncoding& no) const;
};
@ -1517,7 +1513,6 @@ public:
) : m_wallet(wallet), params(params), nTime(_nTime), hdKeypath(_hdKeypath), seedFpStr(_seedFp), log(_log) {}
SpendingKeyAddResult operator()(const libzcash::SproutSpendingKey &sk) const;
SpendingKeyAddResult operator()(const libzcash::SaplingExtendedSpendingKey &sk) const;
SpendingKeyAddResult operator()(const libzcash::InvalidEncoding& no) const;
};

16
src/wallet/walletdb.cpp

@ -653,6 +653,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
}
else if (strType == "czkey")
{
/*
libzcash::SproutPaymentAddress addr;
ssKey >> addr;
// Deserialization of a pair is just one item after another
@ -663,12 +664,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
ssValue >> vchCryptedSecret;
wss.nCKeys++;
//if (!pwallet->LoadCryptedZKey(addr, rk, vchCryptedSecret))
//{
// strErr = "Error reading wallet database: LoadCryptedZKey failed";
// return false;
//}
//wss.fIsEncrypted = true;
if (!pwallet->LoadCryptedZKey(addr, rk, vchCryptedSecret))
{
strErr = "Error reading wallet database: LoadCryptedZKey failed";
return false;
}
wss.fIsEncrypted = true;
*/
}
else if (strType == "csapzkey")
{
@ -704,11 +706,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
}
else if (strType == "zkeymeta")
{
/*
libzcash::SproutPaymentAddress addr;
ssKey >> addr;
CKeyMetadata keyMeta;
ssValue >> keyMeta;
wss.nZKeyMeta++;
*/
// pwallet->LoadZKeyMetadata(addr, keyMeta);

11
src/wallet/walletdb.h

@ -1,7 +1,8 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2013 The Bitcoin Core developers
// Copyright (c) 2009-2013 The Hush developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
// file COPYING or https://www.opensource.org/licenses/mit-license.php
/******************************************************************************
* Copyright © 2014-2019 The SuperNET Developers. *
@ -199,23 +200,15 @@ public:
bool WriteHDChain(const CHDChain& chain);
/// Write spending key to wallet database, where key is payment address and value is spending key.
bool WriteZKey(const libzcash::SproutPaymentAddress& addr, const libzcash::SproutSpendingKey& key, const CKeyMetadata &keyMeta);
bool WriteSaplingZKey(const libzcash::SaplingIncomingViewingKey &ivk,
const libzcash::SaplingExtendedSpendingKey &key,
const CKeyMetadata &keyMeta);
bool WriteSaplingPaymentAddress(const libzcash::SaplingPaymentAddress &addr,
const libzcash::SaplingIncomingViewingKey &ivk);
bool WriteCryptedZKey(const libzcash::SproutPaymentAddress & addr,
const libzcash::ReceivingKey & rk,
const std::vector<unsigned char>& vchCryptedSecret,
const CKeyMetadata &keyMeta);
bool WriteCryptedSaplingZKey(const libzcash::SaplingExtendedFullViewingKey &extfvk,
const std::vector<unsigned char>& vchCryptedSecret,
const CKeyMetadata &keyMeta);
bool WriteSproutViewingKey(const libzcash::SproutViewingKey &vk);
bool EraseSproutViewingKey(const libzcash::SproutViewingKey &vk);
private:
CWalletDB(const CWalletDB&);
void operator=(const CWalletDB&);

38
src/zcash/Address.cpp

@ -1,3 +1,7 @@
// Copyright (c) 2019-2020 The Hush developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php
#include "Address.hpp"
#include "NoteEncryption.hpp"
#include "hash.h"
@ -13,36 +17,6 @@ const uint32_t SAPLING_BRANCH_ID = 0x76b809bb;
namespace libzcash {
uint256 SproutPaymentAddress::GetHash() const {
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << *this;
return Hash(ss.begin(), ss.end());
}
uint256 ReceivingKey::pk_enc() const {
return ZCNoteEncryption::generate_pubkey(*this);
}
SproutPaymentAddress SproutViewingKey::address() const {
return SproutPaymentAddress(a_pk, sk_enc.pk_enc());
}
ReceivingKey SproutSpendingKey::receiving_key() const {
return ReceivingKey(ZCNoteEncryption::generate_privkey(*this));
}
SproutViewingKey SproutSpendingKey::viewing_key() const {
return SproutViewingKey(PRF_addr_a_pk(*this), receiving_key());
}
SproutSpendingKey SproutSpendingKey::random() {
return SproutSpendingKey(random_uint252());
}
SproutPaymentAddress SproutSpendingKey::address() const {
return viewing_key().address();
}
//! Sapling
uint256 SaplingPaymentAddress::GetHash() const {
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
@ -119,10 +93,6 @@ class IsValidAddressForNetwork : public boost::static_visitor<bool> {
public:
IsValidAddressForNetwork(uint32_t consensusBranchId) : branchId(consensusBranchId) {}
bool operator()(const libzcash::SproutPaymentAddress &addr) const {
return true;
}
bool operator()(const libzcash::InvalidEncoding &addr) const {
return false;
}

34
src/zcbenchmarks.cpp

@ -25,7 +25,6 @@
#include "sodium.h"
#include "streams.h"
#include "txdb.h"
#include "utiltest.h"
#include "wallet/wallet.h"
#include "zcbenchmarks.h"
@ -41,7 +40,7 @@ void pre_wallet_load()
{
LogPrintf("%s: In progress...\n", __func__);
if (ShutdownRequested())
throw new std::runtime_error("The node is shutting down");
throw new std::runtime_error("The Hush node is shutting down");
if (pwalletMain)
pwalletMain->Flush(false);
@ -93,37 +92,6 @@ double benchmark_sleep()
return timer_stop(tv_start);
}
std::vector<double> benchmark_create_joinsplit_threaded(int nThreads)
{
std::vector<double> ret;
std::vector<std::future<double>> tasks;
std::vector<std::thread> threads;
for (int i = 0; i < nThreads; i++) {
std::packaged_task<double(void)> task(&benchmark_create_joinsplit);
tasks.emplace_back(task.get_future());
threads.emplace_back(std::move(task));
}
std::future_status status;
for (auto it = tasks.begin(); it != tasks.end(); it++) {
it->wait();
ret.push_back(it->get());
}
for (auto it = threads.begin(); it != threads.end(); it++) {
it->join();
}
return ret;
}
double benchmark_verify_joinsplit(const JSDescription &joinsplit)
{
struct timeval tv_start;
timer_start(tv_start);
uint256 joinSplitPubKey;
auto verifier = libzcash::ProofVerifier::Strict();
joinsplit.Verify(*pzcashParams, verifier, joinSplitPubKey);
return timer_stop(tv_start);
}
#ifdef ENABLE_MINING
double benchmark_solve_equihash()
{

Loading…
Cancel
Save