Auto merge of #2143 - str4d:1997-viewing-keys, r=str4d
Implement incoming viewing keys Closes #1997.
This commit is contained in:
@@ -82,10 +82,14 @@ _zcash_cli() {
|
||||
COMPREPLY=( $( compgen -W "add remove" -- "$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
fundrawtransaction|getblock|getblockheader|getmempoolancestors|getmempooldescendants|getrawtransaction|gettransaction|listaccounts|listreceivedbyaccount|listreceivedbyaddress|sendrawtransaction|z_importkey)
|
||||
fundrawtransaction|getblock|getblockheader|getmempoolancestors|getmempooldescendants|getrawtransaction|gettransaction|listaccounts|listreceivedbyaccount|listreceivedbyaddress|sendrawtransaction)
|
||||
COMPREPLY=( $( compgen -W "true false" -- "$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
z_importkey|z_importviewingkey)
|
||||
COMPREPLY=( $( compgen -W "yes no whenkeyisnew" -- "$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
move|setaccount)
|
||||
_zcash_accounts
|
||||
return 0
|
||||
|
||||
@@ -4,3 +4,31 @@ release-notes at release time)
|
||||
Notable changes
|
||||
===============
|
||||
|
||||
Incoming viewing keys
|
||||
---------------------
|
||||
|
||||
Support for incoming viewing keys, as described in
|
||||
[the Zcash protocol spec](https://github.com/zcash/zips/blob/master/protocol/protocol.pdf),
|
||||
has been added to the wallet.
|
||||
|
||||
Use the `z_exportviewingkey` RPC method to obtain the incoming viewing key for a
|
||||
z-address in a node's wallet. For Sprout z-addresses, these always begin with
|
||||
"ZiVK" (or "ZiVt" for testnet z-addresses). Use `z_importviewingkey` to import
|
||||
these into another node.
|
||||
|
||||
A node that possesses an incoming viewing key for a z-address can view all past
|
||||
transactions received by that address, as well as all future transactions sent
|
||||
to it, by using `z_listreceivedbyaddress`. They cannot spend any funds from the
|
||||
address. This is similar to the behaviour of "watch-only" t-addresses.
|
||||
|
||||
`z_gettotalbalance` now has an additional boolean parameter for including the
|
||||
balance of "watch-only" addresses (both transparent and shielded), which is set
|
||||
to `false` by default. `z_getbalance` has also been updated to work with
|
||||
watch-only addresses.
|
||||
|
||||
- **Caution:** for z-addresses, these balances will **not** be accurate if any
|
||||
funds have been sent from the address. This is because incoming viewing keys
|
||||
cannot detect spends, and so the "balance" is just the sum of all received
|
||||
notes, including ones that have been spent. Some future use-cases for incoming
|
||||
viewing keys will include synchronization data to keep their balances accurate
|
||||
(e.g. [#2542](https://github.com/zcash/zcash/issues/2542)).
|
||||
|
||||
@@ -170,5 +170,50 @@ class WalletNullifiersTest (BitcoinTestFramework):
|
||||
assert_equal(self.nodes[1].z_getbalance(myzaddr), zaddrremaining2)
|
||||
assert_equal(self.nodes[2].z_getbalance(myzaddr), zaddrremaining2)
|
||||
|
||||
# Test viewing keys
|
||||
|
||||
node3mined = Decimal('250.0')
|
||||
assert_equal({k: Decimal(v) for k, v in self.nodes[3].z_gettotalbalance().items()}, {
|
||||
'transparent': node3mined,
|
||||
'private': zsendmany2notevalue,
|
||||
'total': node3mined + zsendmany2notevalue,
|
||||
})
|
||||
|
||||
# add node 1 address and node 2 viewing key to node 3
|
||||
myzvkey = self.nodes[2].z_exportviewingkey(myzaddr)
|
||||
self.nodes[3].importaddress(mytaddr1)
|
||||
self.nodes[3].z_importviewingkey(myzvkey)
|
||||
|
||||
# Check the address has been imported
|
||||
assert_equal(myzaddr in self.nodes[3].z_listaddresses(), False)
|
||||
assert_equal(myzaddr in self.nodes[3].z_listaddresses(True), True)
|
||||
|
||||
# Node 3 should see the same received notes as node 2
|
||||
assert_equal(
|
||||
self.nodes[2].z_listreceivedbyaddress(myzaddr),
|
||||
self.nodes[3].z_listreceivedbyaddress(myzaddr))
|
||||
|
||||
# Node 3's balances should be unchanged without explicitly requesting
|
||||
# to include watch-only balances
|
||||
assert_equal({k: Decimal(v) for k, v in self.nodes[3].z_gettotalbalance().items()}, {
|
||||
'transparent': node3mined,
|
||||
'private': zsendmany2notevalue,
|
||||
'total': node3mined + zsendmany2notevalue,
|
||||
})
|
||||
|
||||
# Wallet can't cache nullifiers for notes received by addresses it only has a
|
||||
# viewing key for, and therefore can't detect spends. So it sees a balance
|
||||
# corresponding to the sum of all notes the address received.
|
||||
# TODO: Fix this during the Sapling upgrade (via #2277)
|
||||
assert_equal({k: Decimal(v) for k, v in self.nodes[3].z_gettotalbalance(1, True).items()}, {
|
||||
'transparent': node3mined + Decimal('1.0'),
|
||||
'private': zsendmany2notevalue + zsendmanynotevalue + zaddrremaining + zaddrremaining2,
|
||||
'total': node3mined + Decimal('1.0') + zsendmany2notevalue + zsendmanynotevalue + zaddrremaining + zaddrremaining2,
|
||||
})
|
||||
|
||||
# Check individual balances reflect the above
|
||||
assert_equal(self.nodes[3].z_getbalance(mytaddr1), Decimal('1.0'))
|
||||
assert_equal(self.nodes[3].z_getbalance(myzaddr), zsendmanynotevalue + zaddrremaining + zaddrremaining2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
WalletNullifiersTest().main ()
|
||||
|
||||
@@ -323,67 +323,60 @@ bool CBitcoinSecret::SetString(const std::string& strSecret)
|
||||
return SetString(strSecret.c_str());
|
||||
}
|
||||
|
||||
bool CZCPaymentAddress::Set(const libzcash::PaymentAddress& addr)
|
||||
template<class DATA_TYPE, CChainParams::Base58Type PREFIX, size_t SER_SIZE>
|
||||
bool CZCEncoding<DATA_TYPE, PREFIX, SER_SIZE>::Set(const DATA_TYPE& addr)
|
||||
{
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << addr;
|
||||
std::vector<unsigned char> addrSerialized(ss.begin(), ss.end());
|
||||
assert(addrSerialized.size() == libzcash::SerializedPaymentAddressSize);
|
||||
SetData(Params().Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS), &addrSerialized[0], libzcash::SerializedPaymentAddressSize);
|
||||
assert(addrSerialized.size() == SER_SIZE);
|
||||
SetData(Params().Base58Prefix(PREFIX), &addrSerialized[0], SER_SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
libzcash::PaymentAddress CZCPaymentAddress::Get() const
|
||||
template<class DATA_TYPE, CChainParams::Base58Type PREFIX, size_t SER_SIZE>
|
||||
DATA_TYPE CZCEncoding<DATA_TYPE, PREFIX, SER_SIZE>::Get() const
|
||||
{
|
||||
if (vchData.size() != libzcash::SerializedPaymentAddressSize) {
|
||||
if (vchData.size() != SER_SIZE) {
|
||||
throw std::runtime_error(
|
||||
"payment address is invalid"
|
||||
PrependName(" is invalid")
|
||||
);
|
||||
}
|
||||
|
||||
if (vchVersion != Params().Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS)) {
|
||||
if (vchVersion != Params().Base58Prefix(PREFIX)) {
|
||||
throw std::runtime_error(
|
||||
"payment address is for wrong network type"
|
||||
PrependName(" is for wrong network type")
|
||||
);
|
||||
}
|
||||
|
||||
std::vector<unsigned char> serialized(vchData.begin(), vchData.end());
|
||||
|
||||
CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION);
|
||||
libzcash::PaymentAddress ret;
|
||||
DATA_TYPE ret;
|
||||
ss >> ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CZCSpendingKey::Set(const libzcash::SpendingKey& addr)
|
||||
{
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << addr;
|
||||
std::vector<unsigned char> addrSerialized(ss.begin(), ss.end());
|
||||
assert(addrSerialized.size() == libzcash::SerializedSpendingKeySize);
|
||||
SetData(Params().Base58Prefix(CChainParams::ZCSPENDING_KEY), &addrSerialized[0], libzcash::SerializedSpendingKeySize);
|
||||
return true;
|
||||
}
|
||||
// Explicit instantiations for libzcash::PaymentAddress
|
||||
template bool CZCEncoding<libzcash::PaymentAddress,
|
||||
CChainParams::ZCPAYMENT_ADDRRESS,
|
||||
libzcash::SerializedPaymentAddressSize>::Set(const libzcash::PaymentAddress& addr);
|
||||
template libzcash::PaymentAddress CZCEncoding<libzcash::PaymentAddress,
|
||||
CChainParams::ZCPAYMENT_ADDRRESS,
|
||||
libzcash::SerializedPaymentAddressSize>::Get() const;
|
||||
|
||||
libzcash::SpendingKey CZCSpendingKey::Get() const
|
||||
{
|
||||
if (vchData.size() != libzcash::SerializedSpendingKeySize) {
|
||||
throw std::runtime_error(
|
||||
"spending key is invalid"
|
||||
);
|
||||
}
|
||||
|
||||
if (vchVersion != Params().Base58Prefix(CChainParams::ZCSPENDING_KEY)) {
|
||||
throw std::runtime_error(
|
||||
"spending key is for wrong network type"
|
||||
);
|
||||
}
|
||||
|
||||
std::vector<unsigned char> serialized(vchData.begin(), vchData.end());
|
||||
|
||||
CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION);
|
||||
libzcash::SpendingKey ret;
|
||||
ss >> ret;
|
||||
return ret;
|
||||
}
|
||||
// Explicit instantiations for libzcash::ViewingKey
|
||||
template bool CZCEncoding<libzcash::ViewingKey,
|
||||
CChainParams::ZCVIEWING_KEY,
|
||||
libzcash::SerializedViewingKeySize>::Set(const libzcash::ViewingKey& vk);
|
||||
template libzcash::ViewingKey CZCEncoding<libzcash::ViewingKey,
|
||||
CChainParams::ZCVIEWING_KEY,
|
||||
libzcash::SerializedViewingKeySize>::Get() const;
|
||||
|
||||
// Explicit instantiations for libzcash::SpendingKey
|
||||
template bool CZCEncoding<libzcash::SpendingKey,
|
||||
CChainParams::ZCSPENDING_KEY,
|
||||
libzcash::SerializedSpendingKeySize>::Set(const libzcash::SpendingKey& sk);
|
||||
template libzcash::SpendingKey CZCEncoding<libzcash::SpendingKey,
|
||||
CChainParams::ZCSPENDING_KEY,
|
||||
libzcash::SerializedSpendingKeySize>::Get() const;
|
||||
|
||||
38
src/base58.h
38
src/base58.h
@@ -96,26 +96,48 @@ public:
|
||||
bool operator> (const CBase58Data& b58) const { return CompareTo(b58) > 0; }
|
||||
};
|
||||
|
||||
class CZCPaymentAddress : public CBase58Data {
|
||||
template<class DATA_TYPE, CChainParams::Base58Type PREFIX, size_t SER_SIZE>
|
||||
class CZCEncoding : public CBase58Data {
|
||||
protected:
|
||||
virtual std::string PrependName(const std::string& s) const = 0;
|
||||
|
||||
public:
|
||||
bool Set(const DATA_TYPE& addr);
|
||||
|
||||
DATA_TYPE Get() const;
|
||||
};
|
||||
|
||||
class CZCPaymentAddress : public CZCEncoding<libzcash::PaymentAddress, CChainParams::ZCPAYMENT_ADDRRESS, libzcash::SerializedPaymentAddressSize> {
|
||||
protected:
|
||||
std::string PrependName(const std::string& s) const { return "payment address" + s; }
|
||||
|
||||
public:
|
||||
bool Set(const libzcash::PaymentAddress& addr);
|
||||
CZCPaymentAddress() {}
|
||||
|
||||
CZCPaymentAddress(const std::string& strAddress) { SetString(strAddress.c_str(), 2); }
|
||||
CZCPaymentAddress(const libzcash::PaymentAddress& addr) { Set(addr); }
|
||||
|
||||
libzcash::PaymentAddress Get() const;
|
||||
};
|
||||
|
||||
class CZCSpendingKey : public CBase58Data {
|
||||
class CZCViewingKey : public CZCEncoding<libzcash::ViewingKey, CChainParams::ZCVIEWING_KEY, libzcash::SerializedViewingKeySize> {
|
||||
protected:
|
||||
std::string PrependName(const std::string& s) const { return "viewing key" + s; }
|
||||
|
||||
public:
|
||||
CZCViewingKey() {}
|
||||
|
||||
CZCViewingKey(const std::string& strViewingKey) { SetString(strViewingKey.c_str(), 3); }
|
||||
CZCViewingKey(const libzcash::ViewingKey& vk) { Set(vk); }
|
||||
};
|
||||
|
||||
class CZCSpendingKey : public CZCEncoding<libzcash::SpendingKey, CChainParams::ZCSPENDING_KEY, libzcash::SerializedSpendingKeySize> {
|
||||
protected:
|
||||
std::string PrependName(const std::string& s) const { return "spending key" + s; }
|
||||
|
||||
public:
|
||||
bool Set(const libzcash::SpendingKey& addr);
|
||||
CZCSpendingKey() {}
|
||||
|
||||
CZCSpendingKey(const std::string& strAddress) { SetString(strAddress.c_str(), 2); }
|
||||
CZCSpendingKey(const libzcash::SpendingKey& addr) { Set(addr); }
|
||||
|
||||
libzcash::SpendingKey Get() const;
|
||||
};
|
||||
|
||||
/** base58-encoded Bitcoin addresses.
|
||||
|
||||
@@ -110,6 +110,8 @@ public:
|
||||
base58Prefixes[EXT_SECRET_KEY] = {0x04,0x88,0xAD,0xE4};
|
||||
// guarantees the first 2 characters, when base58 encoded, are "zc"
|
||||
base58Prefixes[ZCPAYMENT_ADDRRESS] = {0x16,0x9A};
|
||||
// guarantees the first 4 characters, when base58 encoded, are "ZiVK"
|
||||
base58Prefixes[ZCVIEWING_KEY] = {0xA8,0xAB,0xD3};
|
||||
// guarantees the first 2 characters, when base58 encoded, are "SK"
|
||||
base58Prefixes[ZCSPENDING_KEY] = {0xAB,0x36};
|
||||
|
||||
@@ -241,6 +243,8 @@ public:
|
||||
base58Prefixes[EXT_SECRET_KEY] = {0x04,0x35,0x83,0x94};
|
||||
// guarantees the first 2 characters, when base58 encoded, are "zt"
|
||||
base58Prefixes[ZCPAYMENT_ADDRRESS] = {0x16,0xB6};
|
||||
// guarantees the first 4 characters, when base58 encoded, are "ZiVt"
|
||||
base58Prefixes[ZCVIEWING_KEY] = {0xA8,0xAC,0x0C};
|
||||
// guarantees the first 2 characters, when base58 encoded, are "ST"
|
||||
base58Prefixes[ZCSPENDING_KEY] = {0xAC,0x08};
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ public:
|
||||
|
||||
ZCPAYMENT_ADDRRESS,
|
||||
ZCSPENDING_KEY,
|
||||
ZCVIEWING_KEY,
|
||||
|
||||
MAX_BASE58_TYPES
|
||||
};
|
||||
|
||||
@@ -89,7 +89,7 @@ void test_full_api(ZCJoinSplit* js)
|
||||
// Recipient should decrypt
|
||||
// Now the recipient should spend the money again
|
||||
auto h_sig = js->h_sig(randomSeed, nullifiers, pubKeyHash);
|
||||
ZCNoteDecryption decryptor(recipient_key.viewing_key());
|
||||
ZCNoteDecryption decryptor(recipient_key.receiving_key());
|
||||
|
||||
auto note_pt = NotePlaintext::decrypt(
|
||||
decryptor,
|
||||
|
||||
@@ -43,7 +43,64 @@ TEST(keystore_tests, store_and_retrieve_note_decryptor) {
|
||||
|
||||
keyStore.AddSpendingKey(sk);
|
||||
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
|
||||
EXPECT_EQ(ZCNoteDecryption(sk.viewing_key()), decOut);
|
||||
EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut);
|
||||
}
|
||||
|
||||
TEST(keystore_tests, StoreAndRetrieveViewingKey) {
|
||||
CBasicKeyStore keyStore;
|
||||
libzcash::ViewingKey vkOut;
|
||||
libzcash::SpendingKey skOut;
|
||||
ZCNoteDecryption decOut;
|
||||
|
||||
auto sk = libzcash::SpendingKey::random();
|
||||
auto vk = sk.viewing_key();
|
||||
auto addr = sk.address();
|
||||
|
||||
// Sanity-check: we can't get a viewing key we haven't added
|
||||
EXPECT_FALSE(keyStore.HaveViewingKey(addr));
|
||||
EXPECT_FALSE(keyStore.GetViewingKey(addr, vkOut));
|
||||
|
||||
// and we shouldn't have a spending key or decryptor either
|
||||
EXPECT_FALSE(keyStore.HaveSpendingKey(addr));
|
||||
EXPECT_FALSE(keyStore.GetSpendingKey(addr, skOut));
|
||||
EXPECT_FALSE(keyStore.GetNoteDecryptor(addr, decOut));
|
||||
|
||||
// and we can't find it in our list of addresses
|
||||
std::set<libzcash::PaymentAddress> addresses;
|
||||
keyStore.GetPaymentAddresses(addresses);
|
||||
EXPECT_FALSE(addresses.count(addr));
|
||||
|
||||
keyStore.AddViewingKey(vk);
|
||||
EXPECT_TRUE(keyStore.HaveViewingKey(addr));
|
||||
EXPECT_TRUE(keyStore.GetViewingKey(addr, vkOut));
|
||||
EXPECT_EQ(vk, vkOut);
|
||||
|
||||
// We should still not have the spending key...
|
||||
EXPECT_FALSE(keyStore.HaveSpendingKey(addr));
|
||||
EXPECT_FALSE(keyStore.GetSpendingKey(addr, skOut));
|
||||
|
||||
// ... but we should have a decryptor
|
||||
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
|
||||
EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut);
|
||||
|
||||
// ... and we should find it in our list of addresses
|
||||
addresses.clear();
|
||||
keyStore.GetPaymentAddresses(addresses);
|
||||
EXPECT_TRUE(addresses.count(addr));
|
||||
|
||||
keyStore.RemoveViewingKey(vk);
|
||||
EXPECT_FALSE(keyStore.HaveViewingKey(addr));
|
||||
EXPECT_FALSE(keyStore.GetViewingKey(addr, vkOut));
|
||||
EXPECT_FALSE(keyStore.HaveSpendingKey(addr));
|
||||
EXPECT_FALSE(keyStore.GetSpendingKey(addr, skOut));
|
||||
addresses.clear();
|
||||
keyStore.GetPaymentAddresses(addresses);
|
||||
EXPECT_FALSE(addresses.count(addr));
|
||||
|
||||
// We still have a decryptor because those are cached in memory
|
||||
// (and also we only remove viewing keys when adding a spending key)
|
||||
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
|
||||
EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
@@ -72,13 +129,13 @@ TEST(keystore_tests, store_and_retrieve_spending_key_in_encrypted_store) {
|
||||
ASSERT_TRUE(keyStore.GetSpendingKey(addr, keyOut));
|
||||
ASSERT_EQ(sk, keyOut);
|
||||
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
|
||||
EXPECT_EQ(ZCNoteDecryption(sk.viewing_key()), decOut);
|
||||
EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut);
|
||||
|
||||
ASSERT_TRUE(keyStore.EncryptKeys(vMasterKey));
|
||||
ASSERT_TRUE(keyStore.HaveSpendingKey(addr));
|
||||
ASSERT_FALSE(keyStore.GetSpendingKey(addr, keyOut));
|
||||
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
|
||||
EXPECT_EQ(ZCNoteDecryption(sk.viewing_key()), decOut);
|
||||
EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut);
|
||||
|
||||
// Unlocking with a random key should fail
|
||||
uint256 r2 {GetRandHash()};
|
||||
@@ -109,19 +166,19 @@ TEST(keystore_tests, store_and_retrieve_spending_key_in_encrypted_store) {
|
||||
ASSERT_TRUE(keyStore.GetSpendingKey(addr2, keyOut));
|
||||
ASSERT_EQ(sk2, keyOut);
|
||||
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr2, decOut));
|
||||
EXPECT_EQ(ZCNoteDecryption(sk2.viewing_key()), decOut);
|
||||
EXPECT_EQ(ZCNoteDecryption(sk2.receiving_key()), decOut);
|
||||
|
||||
ASSERT_TRUE(keyStore.Lock());
|
||||
ASSERT_TRUE(keyStore.HaveSpendingKey(addr2));
|
||||
ASSERT_FALSE(keyStore.GetSpendingKey(addr2, keyOut));
|
||||
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr2, decOut));
|
||||
EXPECT_EQ(ZCNoteDecryption(sk2.viewing_key()), decOut);
|
||||
EXPECT_EQ(ZCNoteDecryption(sk2.receiving_key()), decOut);
|
||||
|
||||
ASSERT_TRUE(keyStore.Unlock(vMasterKey));
|
||||
ASSERT_TRUE(keyStore.GetSpendingKey(addr2, keyOut));
|
||||
ASSERT_EQ(sk2, keyOut);
|
||||
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr2, decOut));
|
||||
EXPECT_EQ(ZCNoteDecryption(sk2.viewing_key()), decOut);
|
||||
EXPECT_EQ(ZCNoteDecryption(sk2.receiving_key()), decOut);
|
||||
|
||||
keyStore.GetPaymentAddresses(addrs);
|
||||
ASSERT_EQ(2, addrs.size());
|
||||
|
||||
@@ -89,6 +89,40 @@ bool CBasicKeyStore::AddSpendingKey(const libzcash::SpendingKey &sk)
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
auto address = sk.address();
|
||||
mapSpendingKeys[address] = sk;
|
||||
mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(sk.viewing_key())));
|
||||
mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(sk.receiving_key())));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::AddViewingKey(const libzcash::ViewingKey &vk)
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
auto address = vk.address();
|
||||
mapViewingKeys[address] = vk;
|
||||
mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(vk.sk_enc)));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::RemoveViewingKey(const libzcash::ViewingKey &vk)
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
mapViewingKeys.erase(vk.address());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::HaveViewingKey(const libzcash::PaymentAddress &address) const
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
return mapViewingKeys.count(address) > 0;
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::GetViewingKey(const libzcash::PaymentAddress &address,
|
||||
libzcash::ViewingKey &vkOut) const
|
||||
{
|
||||
LOCK(cs_SpendingKeyStore);
|
||||
ViewingKeyMap::const_iterator mi = mapViewingKeys.find(address);
|
||||
if (mi != mapViewingKeys.end()) {
|
||||
vkOut = mi->second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -55,12 +55,19 @@ public:
|
||||
virtual bool HaveSpendingKey(const libzcash::PaymentAddress &address) const =0;
|
||||
virtual bool GetSpendingKey(const libzcash::PaymentAddress &address, libzcash::SpendingKey& skOut) const =0;
|
||||
virtual void GetPaymentAddresses(std::set<libzcash::PaymentAddress> &setAddress) const =0;
|
||||
|
||||
//! Support for viewing keys
|
||||
virtual bool AddViewingKey(const libzcash::ViewingKey &vk) =0;
|
||||
virtual bool RemoveViewingKey(const libzcash::ViewingKey &vk) =0;
|
||||
virtual bool HaveViewingKey(const libzcash::PaymentAddress &address) const =0;
|
||||
virtual bool GetViewingKey(const libzcash::PaymentAddress &address, libzcash::ViewingKey& vkOut) const =0;
|
||||
};
|
||||
|
||||
typedef std::map<CKeyID, CKey> KeyMap;
|
||||
typedef std::map<CScriptID, CScript > ScriptMap;
|
||||
typedef std::set<CScript> WatchOnlySet;
|
||||
typedef std::map<libzcash::PaymentAddress, libzcash::SpendingKey> SpendingKeyMap;
|
||||
typedef std::map<libzcash::PaymentAddress, libzcash::ViewingKey> ViewingKeyMap;
|
||||
typedef std::map<libzcash::PaymentAddress, ZCNoteDecryption> NoteDecryptorMap;
|
||||
|
||||
/** Basic key store, that keeps keys in an address->secret map */
|
||||
@@ -71,6 +78,7 @@ protected:
|
||||
ScriptMap mapScripts;
|
||||
WatchOnlySet setWatchOnly;
|
||||
SpendingKeyMap mapSpendingKeys;
|
||||
ViewingKeyMap mapViewingKeys;
|
||||
NoteDecryptorMap mapNoteDecryptors;
|
||||
|
||||
public:
|
||||
@@ -166,8 +174,19 @@ public:
|
||||
setAddress.insert((*mi).first);
|
||||
mi++;
|
||||
}
|
||||
ViewingKeyMap::const_iterator mvi = mapViewingKeys.begin();
|
||||
while (mvi != mapViewingKeys.end())
|
||||
{
|
||||
setAddress.insert((*mvi).first);
|
||||
mvi++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool AddViewingKey(const libzcash::ViewingKey &vk);
|
||||
virtual bool RemoveViewingKey(const libzcash::ViewingKey &vk);
|
||||
virtual bool HaveViewingKey(const libzcash::PaymentAddress &address) const;
|
||||
virtual bool GetViewingKey(const libzcash::PaymentAddress &address, libzcash::ViewingKey& vkOut) const;
|
||||
};
|
||||
|
||||
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
|
||||
|
||||
@@ -103,9 +103,12 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||
{ "zcbenchmark", 1 },
|
||||
{ "zcbenchmark", 2 },
|
||||
{ "getblocksubsidy", 0},
|
||||
{ "z_listaddresses", 0},
|
||||
{ "z_listreceivedbyaddress", 1},
|
||||
{ "z_getbalance", 1},
|
||||
{ "z_gettotalbalance", 0},
|
||||
{ "z_gettotalbalance", 1},
|
||||
{ "z_gettotalbalance", 2},
|
||||
{ "z_sendmany", 1},
|
||||
{ "z_sendmany", 2},
|
||||
{ "z_sendmany", 3},
|
||||
@@ -114,6 +117,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||
{ "z_getoperationstatus", 0},
|
||||
{ "z_getoperationresult", 0},
|
||||
{ "z_importkey", 2 },
|
||||
{ "z_importviewingkey", 2 },
|
||||
{ "z_getpaymentdisclosure", 1},
|
||||
{ "z_getpaymentdisclosure", 2}
|
||||
};
|
||||
|
||||
@@ -395,6 +395,8 @@ static const CRPCCommand vRPCCommands[] =
|
||||
{ "wallet", "z_listaddresses", &z_listaddresses, true },
|
||||
{ "wallet", "z_exportkey", &z_exportkey, true },
|
||||
{ "wallet", "z_importkey", &z_importkey, true },
|
||||
{ "wallet", "z_exportviewingkey", &z_exportviewingkey, true },
|
||||
{ "wallet", "z_importviewingkey", &z_importviewingkey, true },
|
||||
{ "wallet", "z_exportwallet", &z_exportwallet, true },
|
||||
{ "wallet", "z_importwallet", &z_importwallet, true },
|
||||
|
||||
|
||||
@@ -279,6 +279,8 @@ extern UniValue getblocksubsidy(const UniValue& params, bool fHelp);
|
||||
|
||||
extern UniValue z_exportkey(const UniValue& params, bool fHelp); // in rpcdump.cpp
|
||||
extern UniValue z_importkey(const UniValue& params, bool fHelp); // in rpcdump.cpp
|
||||
extern UniValue z_exportviewingkey(const UniValue& params, bool fHelp); // in rpcdump.cpp
|
||||
extern UniValue z_importviewingkey(const UniValue& params, bool fHelp); // in rpcdump.cpp
|
||||
extern UniValue z_getnewaddress(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
extern UniValue z_listaddresses(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
extern UniValue z_exportwallet(const UniValue& params, bool fHelp); // in rpcdump.cpp
|
||||
|
||||
@@ -63,7 +63,7 @@ CWalletTx GetValidReceive(ZCJoinSplit& params,
|
||||
libzcash::Note GetNote(ZCJoinSplit& params,
|
||||
const libzcash::SpendingKey& sk,
|
||||
const CTransaction& tx, size_t js, size_t n) {
|
||||
ZCNoteDecryption decryptor {sk.viewing_key()};
|
||||
ZCNoteDecryption decryptor {sk.receiving_key()};
|
||||
auto hSig = tx.vjoinsplit[js].h_sig(params, tx.joinSplitPubKey);
|
||||
auto note_pt = libzcash::NotePlaintext::decrypt(
|
||||
decryptor,
|
||||
|
||||
@@ -555,7 +555,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
||||
intermediates.insert(std::make_pair(tree.root(), tree)); // chained js are interstitial (found in between block boundaries)
|
||||
|
||||
// Decrypt the change note's ciphertext to retrieve some data we need
|
||||
ZCNoteDecryption decryptor(spendingkey_.viewing_key());
|
||||
ZCNoteDecryption decryptor(spendingkey_.receiving_key());
|
||||
auto hSig = prevJoinSplit.h_sig(*pzcashParams, tx_.joinSplitPubKey);
|
||||
try {
|
||||
NotePlaintext plaintext = NotePlaintext::decrypt(
|
||||
|
||||
@@ -316,14 +316,14 @@ bool CCryptoKeyStore::AddSpendingKey(const libzcash::SpendingKey &sk)
|
||||
if (!EncryptSecret(vMasterKey, vchSecret, address.GetHash(), vchCryptedSecret))
|
||||
return false;
|
||||
|
||||
if (!AddCryptedSpendingKey(address, sk.viewing_key(), vchCryptedSecret))
|
||||
if (!AddCryptedSpendingKey(address, sk.receiving_key(), vchCryptedSecret))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCryptoKeyStore::AddCryptedSpendingKey(const libzcash::PaymentAddress &address,
|
||||
const libzcash::ViewingKey &vk,
|
||||
const libzcash::ReceivingKey &rk,
|
||||
const std::vector<unsigned char> &vchCryptedSecret)
|
||||
{
|
||||
{
|
||||
@@ -332,7 +332,7 @@ bool CCryptoKeyStore::AddCryptedSpendingKey(const libzcash::PaymentAddress &addr
|
||||
return false;
|
||||
|
||||
mapCryptedSpendingKeys[address] = vchCryptedSecret;
|
||||
mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(vk)));
|
||||
mapNoteDecryptors.insert(std::make_pair(address, ZCNoteDecryption(rk)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -384,7 +384,7 @@ bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
|
||||
std::vector<unsigned char> vchCryptedSecret;
|
||||
if (!EncryptSecret(vMasterKeyIn, vchSecret, address.GetHash(), vchCryptedSecret))
|
||||
return false;
|
||||
if (!AddCryptedSpendingKey(address, sk.viewing_key(), vchCryptedSecret))
|
||||
if (!AddCryptedSpendingKey(address, sk.receiving_key(), vchCryptedSecret))
|
||||
return false;
|
||||
}
|
||||
mapSpendingKeys.clear();
|
||||
|
||||
@@ -201,7 +201,7 @@ public:
|
||||
}
|
||||
}
|
||||
virtual bool AddCryptedSpendingKey(const libzcash::PaymentAddress &address,
|
||||
const libzcash::ViewingKey &vk,
|
||||
const libzcash::ReceivingKey &rk,
|
||||
const std::vector<unsigned char> &vchCryptedSecret);
|
||||
bool AddSpendingKey(const libzcash::SpendingKey &sk);
|
||||
bool HaveSpendingKey(const libzcash::PaymentAddress &address) const
|
||||
|
||||
@@ -328,7 +328,7 @@ TEST(wallet_tests, GetNoteNullifier) {
|
||||
|
||||
auto sk = libzcash::SpendingKey::random();
|
||||
auto address = sk.address();
|
||||
auto dec = ZCNoteDecryption(sk.viewing_key());
|
||||
auto dec = ZCNoteDecryption(sk.receiving_key());
|
||||
|
||||
auto wtx = GetValidReceive(sk, 10, true);
|
||||
auto note = GetNote(sk, wtx, 0, 1);
|
||||
|
||||
@@ -66,6 +66,53 @@ TEST(wallet_zkeys_tests, store_and_load_zkeys) {
|
||||
ASSERT_EQ(m.nCreateTime, now);
|
||||
}
|
||||
|
||||
/**
|
||||
* This test covers methods on CWallet
|
||||
* AddViewingKey()
|
||||
* RemoveViewingKey()
|
||||
* LoadViewingKey()
|
||||
*/
|
||||
TEST(wallet_zkeys_tests, StoreAndLoadViewingKeys) {
|
||||
SelectParams(CBaseChainParams::MAIN);
|
||||
|
||||
CWallet wallet;
|
||||
|
||||
// wallet should be empty
|
||||
std::set<libzcash::PaymentAddress> addrs;
|
||||
wallet.GetPaymentAddresses(addrs);
|
||||
ASSERT_EQ(0, addrs.size());
|
||||
|
||||
// manually add new viewing key to wallet
|
||||
auto sk = libzcash::SpendingKey::random();
|
||||
auto vk = sk.viewing_key();
|
||||
ASSERT_TRUE(wallet.AddViewingKey(vk));
|
||||
|
||||
// verify wallet did add it
|
||||
auto addr = sk.address();
|
||||
ASSERT_TRUE(wallet.HaveViewingKey(addr));
|
||||
// and that we don't have the corresponding spending key
|
||||
ASSERT_FALSE(wallet.HaveSpendingKey(addr));
|
||||
|
||||
// verify viewing key stored correctly
|
||||
libzcash::ViewingKey vkOut;
|
||||
wallet.GetViewingKey(addr, vkOut);
|
||||
ASSERT_EQ(vk, vkOut);
|
||||
|
||||
// Load a second viewing key into the wallet
|
||||
auto sk2 = libzcash::SpendingKey::random();
|
||||
ASSERT_TRUE(wallet.LoadViewingKey(sk2.viewing_key()));
|
||||
|
||||
// verify wallet did add it
|
||||
auto addr2 = sk2.address();
|
||||
ASSERT_TRUE(wallet.HaveViewingKey(addr2));
|
||||
ASSERT_FALSE(wallet.HaveSpendingKey(addr2));
|
||||
|
||||
// Remove the first viewing key
|
||||
ASSERT_TRUE(wallet.RemoveViewingKey(vk));
|
||||
ASSERT_FALSE(wallet.HaveViewingKey(addr));
|
||||
ASSERT_TRUE(wallet.HaveViewingKey(addr2));
|
||||
}
|
||||
|
||||
/**
|
||||
* This test covers methods on CWalletDB
|
||||
* WriteZKey()
|
||||
@@ -138,6 +185,50 @@ TEST(wallet_zkeys_tests, write_zkey_direct_to_db) {
|
||||
ASSERT_EQ(m.nCreateTime, now);
|
||||
}
|
||||
|
||||
/**
|
||||
* This test covers methods on CWalletDB
|
||||
* WriteViewingKey()
|
||||
*/
|
||||
TEST(wallet_zkeys_tests, WriteViewingKeyDirectToDB) {
|
||||
SelectParams(CBaseChainParams::TESTNET);
|
||||
|
||||
// Get temporary and unique path for file.
|
||||
// Note: / operator to append paths
|
||||
boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
|
||||
boost::filesystem::create_directories(pathTemp);
|
||||
mapArgs["-datadir"] = pathTemp.string();
|
||||
|
||||
bool fFirstRun;
|
||||
CWallet wallet("wallet-vkey.dat");
|
||||
ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun));
|
||||
|
||||
// No default CPubKey set
|
||||
ASSERT_TRUE(fFirstRun);
|
||||
|
||||
// create random viewing key and add it to database directly, bypassing wallet
|
||||
auto sk = libzcash::SpendingKey::random();
|
||||
auto vk = sk.viewing_key();
|
||||
auto addr = sk.address();
|
||||
int64_t now = GetTime();
|
||||
CKeyMetadata meta(now);
|
||||
CWalletDB db("wallet-vkey.dat");
|
||||
db.WriteViewingKey(vk);
|
||||
|
||||
// wallet should not be aware of viewing key
|
||||
ASSERT_FALSE(wallet.HaveViewingKey(addr));
|
||||
|
||||
// load the wallet again
|
||||
ASSERT_EQ(DB_LOAD_OK, wallet.LoadWallet(fFirstRun));
|
||||
|
||||
// wallet can now see the viewing key
|
||||
ASSERT_TRUE(wallet.HaveViewingKey(addr));
|
||||
|
||||
// check key is the same
|
||||
libzcash::ViewingKey vkOut;
|
||||
wallet.GetViewingKey(addr, vkOut);
|
||||
ASSERT_EQ(vk, vkOut);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -648,6 +648,94 @@ UniValue z_importkey(const UniValue& params, bool fHelp)
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
UniValue z_importviewingkey(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() < 1 || params.size() > 2)
|
||||
throw runtime_error(
|
||||
"z_importviewingkey \"vkey\" ( rescan startHeight )\n"
|
||||
"\nAdds a viewing key (as returned by z_exportviewingkey) to your wallet.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"vkey\" (string, required) The viewing key (see z_exportviewingkey)\n"
|
||||
"2. rescan (string, optional, default=\"whenkeyisnew\") Rescan the wallet for transactions - can be \"yes\", \"no\" or \"whenkeyisnew\"\n"
|
||||
"3. startHeight (numeric, optional, default=0) Block height to start rescan from\n"
|
||||
"\nNote: This call can take minutes to complete if rescan is true.\n"
|
||||
"\nExamples:\n"
|
||||
"\nImport a viewing key\n"
|
||||
+ HelpExampleCli("z_importviewingkey", "\"vkey\"") +
|
||||
"\nImport the viewing key without rescan\n"
|
||||
+ HelpExampleCli("z_importviewingkey", "\"vkey\", no") +
|
||||
"\nImport the viewing key with partial rescan\n"
|
||||
+ HelpExampleCli("z_importviewingkey", "\"vkey\" whenkeyisnew 30000") +
|
||||
"\nRe-import the viewing key with longer partial rescan\n"
|
||||
+ HelpExampleCli("z_importviewingkey", "\"vkey\" yes 20000") +
|
||||
"\nAs a JSON-RPC call\n"
|
||||
+ HelpExampleRpc("z_importviewingkey", "\"vkey\", \"no\"")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
EnsureWalletIsUnlocked();
|
||||
|
||||
// Whether to perform rescan after import
|
||||
bool fRescan = true;
|
||||
bool fIgnoreExistingKey = true;
|
||||
if (params.size() > 1) {
|
||||
auto rescan = params[1].get_str();
|
||||
if (rescan.compare("whenkeyisnew") != 0) {
|
||||
fIgnoreExistingKey = false;
|
||||
if (rescan.compare("no") == 0) {
|
||||
fRescan = false;
|
||||
} else if (rescan.compare("yes") != 0) {
|
||||
throw JSONRPCError(
|
||||
RPC_INVALID_PARAMETER,
|
||||
"rescan must be \"yes\", \"no\" or \"whenkeyisnew\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Height to rescan from
|
||||
int nRescanHeight = 0;
|
||||
if (params.size() > 2) {
|
||||
nRescanHeight = params[2].get_int();
|
||||
}
|
||||
if (nRescanHeight < 0 || nRescanHeight > chainActive.Height()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
|
||||
}
|
||||
|
||||
string strVKey = params[0].get_str();
|
||||
CZCViewingKey viewingkey(strVKey);
|
||||
auto vkey = viewingkey.Get();
|
||||
auto addr = vkey.address();
|
||||
|
||||
{
|
||||
if (pwalletMain->HaveSpendingKey(addr)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this viewing key");
|
||||
}
|
||||
|
||||
// Don't throw error in case a viewing key is already there
|
||||
if (pwalletMain->HaveViewingKey(addr)) {
|
||||
if (fIgnoreExistingKey) {
|
||||
return NullUniValue;
|
||||
}
|
||||
} else {
|
||||
pwalletMain->MarkDirty();
|
||||
|
||||
if (!pwalletMain->AddViewingKey(vkey)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding viewing key to wallet");
|
||||
}
|
||||
}
|
||||
|
||||
// We want to scan for transactions and notes
|
||||
if (fRescan) {
|
||||
pwalletMain->ScanForWalletTransactions(chainActive[nRescanHeight], true);
|
||||
}
|
||||
}
|
||||
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
UniValue z_exportkey(const UniValue& params, bool fHelp)
|
||||
{
|
||||
@@ -686,3 +774,43 @@ UniValue z_exportkey(const UniValue& params, bool fHelp)
|
||||
return spendingkey.ToString();
|
||||
}
|
||||
|
||||
UniValue z_exportviewingkey(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() != 1)
|
||||
throw runtime_error(
|
||||
"z_exportviewingkey \"zaddr\"\n"
|
||||
"\nReveals the viewing key corresponding to 'zaddr'.\n"
|
||||
"Then the z_importviewingkey can be used with this output\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"zaddr\" (string, required) The zaddr for the viewing key\n"
|
||||
"\nResult:\n"
|
||||
"\"vkey\" (string) The viewing key\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("z_exportviewingkey", "\"myaddress\"")
|
||||
+ HelpExampleRpc("z_exportviewingkey", "\"myaddress\"")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
EnsureWalletIsUnlocked();
|
||||
|
||||
string strAddress = params[0].get_str();
|
||||
|
||||
CZCPaymentAddress address(strAddress);
|
||||
auto addr = address.Get();
|
||||
|
||||
libzcash::ViewingKey vk;
|
||||
if (!pwalletMain->GetViewingKey(addr, vk)) {
|
||||
libzcash::SpendingKey k;
|
||||
if (!pwalletMain->GetSpendingKey(addr, k)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private key or viewing key for this zaddr");
|
||||
}
|
||||
vk = k.viewing_key();
|
||||
}
|
||||
|
||||
CZCViewingKey viewingkey(vk);
|
||||
return viewingkey.ToString();
|
||||
}
|
||||
|
||||
@@ -2667,7 +2667,7 @@ UniValue zc_raw_receive(const UniValue& params, bool fHelp)
|
||||
}
|
||||
}
|
||||
|
||||
ZCNoteDecryption decryptor(k.viewing_key());
|
||||
ZCNoteDecryption decryptor(k.receiving_key());
|
||||
|
||||
NotePlaintext npt = NotePlaintext::decrypt(
|
||||
decryptor,
|
||||
@@ -2902,6 +2902,7 @@ UniValue zc_raw_keygen(const UniValue& params, bool fHelp)
|
||||
"Output: {\n"
|
||||
" \"zcaddress\": zcaddr,\n"
|
||||
" \"zcsecretkey\": zcsecretkey,\n"
|
||||
" \"zcviewingkey\": zcviewingkey,\n"
|
||||
"}\n"
|
||||
);
|
||||
}
|
||||
@@ -2910,18 +2911,14 @@ UniValue zc_raw_keygen(const UniValue& params, bool fHelp)
|
||||
auto addr = k.address();
|
||||
auto viewing_key = k.viewing_key();
|
||||
|
||||
CDataStream viewing(SER_NETWORK, PROTOCOL_VERSION);
|
||||
|
||||
viewing << viewing_key;
|
||||
|
||||
CZCPaymentAddress pubaddr(addr);
|
||||
CZCSpendingKey spendingkey(k);
|
||||
std::string viewing_hex = HexStr(viewing.begin(), viewing.end());
|
||||
CZCViewingKey viewingkey(viewing_key);
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.push_back(Pair("zcaddress", pubaddr.ToString()));
|
||||
result.push_back(Pair("zcsecretkey", spendingkey.ToString()));
|
||||
result.push_back(Pair("zcviewingkey", viewing_hex));
|
||||
result.push_back(Pair("zcviewingkey", viewingkey.ToString()));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2960,9 +2957,10 @@ UniValue z_listaddresses(const UniValue& params, bool fHelp)
|
||||
|
||||
if (fHelp || params.size() > 1)
|
||||
throw runtime_error(
|
||||
"z_listaddresses\n"
|
||||
"z_listaddresses ( includeWatchonly )\n"
|
||||
"\nReturns the list of zaddr belonging to the wallet.\n"
|
||||
"\nArguments:\n"
|
||||
"1. includeWatchonly (bool, optional, default=false) Also include watchonly addresses (see 'z_importviewingkey')\n"
|
||||
"\nResult:\n"
|
||||
"[ (json array of string)\n"
|
||||
" \"zaddr\" (string) a zaddr belonging to the wallet\n"
|
||||
@@ -2975,16 +2973,23 @@ UniValue z_listaddresses(const UniValue& params, bool fHelp)
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
bool fIncludeWatchonly = false;
|
||||
if (params.size() > 0) {
|
||||
fIncludeWatchonly = params[0].get_bool();
|
||||
}
|
||||
|
||||
UniValue ret(UniValue::VARR);
|
||||
std::set<libzcash::PaymentAddress> addresses;
|
||||
pwalletMain->GetPaymentAddresses(addresses);
|
||||
for (auto addr : addresses ) {
|
||||
ret.push_back(CZCPaymentAddress(addr).ToString());
|
||||
if (fIncludeWatchonly || pwalletMain->HaveSpendingKey(addr)) {
|
||||
ret.push_back(CZCPaymentAddress(addr).ToString());
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1) {
|
||||
CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1, bool ignoreUnspendable=true) {
|
||||
set<CBitcoinAddress> setAddress;
|
||||
vector<COutput> vecOutputs;
|
||||
CAmount balance = 0;
|
||||
@@ -3006,6 +3011,10 @@ CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ignoreUnspendable && !out.fSpendable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (setAddress.size()) {
|
||||
CTxDestination address;
|
||||
if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) {
|
||||
@@ -3023,11 +3032,11 @@ CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1) {
|
||||
return balance;
|
||||
}
|
||||
|
||||
CAmount getBalanceZaddr(std::string address, int minDepth = 1) {
|
||||
CAmount getBalanceZaddr(std::string address, int minDepth = 1, bool ignoreUnspendable=true) {
|
||||
CAmount balance = 0;
|
||||
std::vector<CNotePlaintextEntry> entries;
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
pwalletMain->GetFilteredNotes(entries, address, minDepth);
|
||||
pwalletMain->GetFilteredNotes(entries, address, minDepth, true, ignoreUnspendable);
|
||||
for (auto & entry : entries) {
|
||||
balance += CAmount(entry.plaintext.value);
|
||||
}
|
||||
@@ -3079,14 +3088,14 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr.");
|
||||
}
|
||||
|
||||
if (!pwalletMain->HaveSpendingKey(zaddr)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key not found.");
|
||||
if (!(pwalletMain->HaveSpendingKey(zaddr) || pwalletMain->HaveViewingKey(zaddr))) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found.");
|
||||
}
|
||||
|
||||
|
||||
UniValue result(UniValue::VARR);
|
||||
std::vector<CNotePlaintextEntry> entries;
|
||||
pwalletMain->GetFilteredNotes(entries, fromaddress, nMinDepth, false);
|
||||
pwalletMain->GetFilteredNotes(entries, fromaddress, nMinDepth, false, false);
|
||||
for (CNotePlaintextEntry & entry : entries) {
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.push_back(Pair("txid",entry.jsop.hash.ToString()));
|
||||
@@ -3108,6 +3117,8 @@ UniValue z_getbalance(const UniValue& params, bool fHelp)
|
||||
throw runtime_error(
|
||||
"z_getbalance \"address\" ( minconf )\n"
|
||||
"\nReturns the balance of a taddr or zaddr belonging to the node’s wallet.\n"
|
||||
"\nCAUTION: If address is a watch-only zaddr, the returned balance may be larger than the actual balance,"
|
||||
"\nbecause spends cannot be detected with incoming viewing keys.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"address\" (string) The selected address. It may be a transparent or private address.\n"
|
||||
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
|
||||
@@ -3145,16 +3156,16 @@ UniValue z_getbalance(const UniValue& params, bool fHelp)
|
||||
} catch (const std::runtime_error&) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr.");
|
||||
}
|
||||
if (!pwalletMain->HaveSpendingKey(zaddr)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key not found.");
|
||||
if (!(pwalletMain->HaveSpendingKey(zaddr) || pwalletMain->HaveViewingKey(zaddr))) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found.");
|
||||
}
|
||||
}
|
||||
|
||||
CAmount nBalance = 0;
|
||||
if (fromTaddr) {
|
||||
nBalance = getBalanceTaddr(fromaddress, nMinDepth);
|
||||
nBalance = getBalanceTaddr(fromaddress, nMinDepth, false);
|
||||
} else {
|
||||
nBalance = getBalanceZaddr(fromaddress, nMinDepth);
|
||||
nBalance = getBalanceZaddr(fromaddress, nMinDepth, false);
|
||||
}
|
||||
|
||||
return ValueFromAmount(nBalance);
|
||||
@@ -3166,12 +3177,15 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp)
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() > 1)
|
||||
if (fHelp || params.size() > 2)
|
||||
throw runtime_error(
|
||||
"z_gettotalbalance ( minconf )\n"
|
||||
"z_gettotalbalance ( minconf includeWatchonly )\n"
|
||||
"\nReturn the total value of funds stored in the node’s wallet.\n"
|
||||
"\nCAUTION: If the wallet contains watch-only zaddrs, the returned private balance may be larger than the actual balance,"
|
||||
"\nbecause spends cannot be detected with incoming viewing keys.\n"
|
||||
"\nArguments:\n"
|
||||
"1. minconf (numeric, optional, default=1) Only include private and transparent transactions confirmed at least this many times.\n"
|
||||
"2. includeWatchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress' and 'z_importviewingkey')\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"transparent\": xxxxx, (numeric) the total balance of transparent funds\n"
|
||||
@@ -3190,19 +3204,24 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp)
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
int nMinDepth = 1;
|
||||
if (params.size() == 1) {
|
||||
if (params.size() > 0) {
|
||||
nMinDepth = params[0].get_int();
|
||||
}
|
||||
if (nMinDepth < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0");
|
||||
}
|
||||
|
||||
bool fIncludeWatchonly = false;
|
||||
if (params.size() > 1) {
|
||||
fIncludeWatchonly = params[1].get_bool();
|
||||
}
|
||||
|
||||
// getbalance and "getbalance * 1 true" should return the same number
|
||||
// but they don't because wtx.GetAmounts() does not handle tx where there are no outputs
|
||||
// pwalletMain->GetBalance() does not accept min depth parameter
|
||||
// so we use our own method to get balance of utxos.
|
||||
CAmount nBalance = getBalanceTaddr("", nMinDepth);
|
||||
CAmount nPrivateBalance = getBalanceZaddr("", nMinDepth);
|
||||
CAmount nBalance = getBalanceTaddr("", nMinDepth, !fIncludeWatchonly);
|
||||
CAmount nPrivateBalance = getBalanceZaddr("", nMinDepth, !fIncludeWatchonly);
|
||||
CAmount nTotalBalance = nBalance + nPrivateBalance;
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.push_back(Pair("transparent", FormatMoney(nBalance)));
|
||||
|
||||
@@ -107,6 +107,10 @@ bool CWallet::AddZKey(const libzcash::SpendingKey &key)
|
||||
if (!CCryptoKeyStore::AddSpendingKey(key))
|
||||
return false;
|
||||
|
||||
// check if we need to remove from viewing keys
|
||||
if (HaveViewingKey(addr))
|
||||
RemoveViewingKey(key.viewing_key());
|
||||
|
||||
if (!fFileBacked)
|
||||
return true;
|
||||
|
||||
@@ -190,10 +194,10 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
|
||||
|
||||
|
||||
bool CWallet::AddCryptedSpendingKey(const libzcash::PaymentAddress &address,
|
||||
const libzcash::ViewingKey &vk,
|
||||
const libzcash::ReceivingKey &rk,
|
||||
const std::vector<unsigned char> &vchCryptedSecret)
|
||||
{
|
||||
if (!CCryptoKeyStore::AddCryptedSpendingKey(address, vk, vchCryptedSecret))
|
||||
if (!CCryptoKeyStore::AddCryptedSpendingKey(address, rk, vchCryptedSecret))
|
||||
return false;
|
||||
if (!fFileBacked)
|
||||
return true;
|
||||
@@ -201,12 +205,12 @@ bool CWallet::AddCryptedSpendingKey(const libzcash::PaymentAddress &address,
|
||||
LOCK(cs_wallet);
|
||||
if (pwalletdbEncryption) {
|
||||
return pwalletdbEncryption->WriteCryptedZKey(address,
|
||||
vk,
|
||||
rk,
|
||||
vchCryptedSecret,
|
||||
mapZKeyMetadata[address]);
|
||||
} else {
|
||||
return CWalletDB(strWalletFile).WriteCryptedZKey(address,
|
||||
vk,
|
||||
rk,
|
||||
vchCryptedSecret,
|
||||
mapZKeyMetadata[address]);
|
||||
}
|
||||
@@ -236,9 +240,9 @@ bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigne
|
||||
return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret);
|
||||
}
|
||||
|
||||
bool CWallet::LoadCryptedZKey(const libzcash::PaymentAddress &addr, const libzcash::ViewingKey &vk, const std::vector<unsigned char> &vchCryptedSecret)
|
||||
bool CWallet::LoadCryptedZKey(const libzcash::PaymentAddress &addr, const libzcash::ReceivingKey &rk, const std::vector<unsigned char> &vchCryptedSecret)
|
||||
{
|
||||
return CCryptoKeyStore::AddCryptedSpendingKey(addr, vk, vchCryptedSecret);
|
||||
return CCryptoKeyStore::AddCryptedSpendingKey(addr, rk, vchCryptedSecret);
|
||||
}
|
||||
|
||||
bool CWallet::LoadZKey(const libzcash::SpendingKey &key)
|
||||
@@ -246,6 +250,38 @@ bool CWallet::LoadZKey(const libzcash::SpendingKey &key)
|
||||
return CCryptoKeyStore::AddSpendingKey(key);
|
||||
}
|
||||
|
||||
bool CWallet::AddViewingKey(const libzcash::ViewingKey &vk)
|
||||
{
|
||||
if (!CCryptoKeyStore::AddViewingKey(vk)) {
|
||||
return false;
|
||||
}
|
||||
nTimeFirstKey = 1; // No birthday information for viewing keys.
|
||||
if (!fFileBacked) {
|
||||
return true;
|
||||
}
|
||||
return CWalletDB(strWalletFile).WriteViewingKey(vk);
|
||||
}
|
||||
|
||||
bool CWallet::RemoveViewingKey(const libzcash::ViewingKey &vk)
|
||||
{
|
||||
AssertLockHeld(cs_wallet);
|
||||
if (!CCryptoKeyStore::RemoveViewingKey(vk)) {
|
||||
return false;
|
||||
}
|
||||
if (fFileBacked) {
|
||||
if (!CWalletDB(strWalletFile).EraseViewingKey(vk)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CWallet::LoadViewingKey(const libzcash::ViewingKey &vk)
|
||||
{
|
||||
return CCryptoKeyStore::AddViewingKey(vk);
|
||||
}
|
||||
|
||||
bool CWallet::AddCScript(const CScript& redeemScript)
|
||||
{
|
||||
if (!CCryptoKeyStore::AddCScript(redeemScript))
|
||||
@@ -946,7 +982,8 @@ void CWallet::MarkDirty()
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that every note in the wallet has a cached nullifier.
|
||||
* Ensure that every note in the wallet (for which we possess a spending key)
|
||||
* has a cached nullifier.
|
||||
*/
|
||||
bool CWallet::UpdateNullifierNoteMap()
|
||||
{
|
||||
@@ -960,16 +997,17 @@ bool CWallet::UpdateNullifierNoteMap()
|
||||
for (std::pair<const uint256, CWalletTx>& wtxItem : mapWallet) {
|
||||
for (mapNoteData_t::value_type& item : wtxItem.second.mapNoteData) {
|
||||
if (!item.second.nullifier) {
|
||||
auto i = item.first.js;
|
||||
GetNoteDecryptor(item.second.address, dec);
|
||||
auto hSig = wtxItem.second.vjoinsplit[i].h_sig(
|
||||
*pzcashParams, wtxItem.second.joinSplitPubKey);
|
||||
item.second.nullifier = GetNoteNullifier(
|
||||
wtxItem.second.vjoinsplit[i],
|
||||
item.second.address,
|
||||
dec,
|
||||
hSig,
|
||||
item.first.n);
|
||||
if (GetNoteDecryptor(item.second.address, dec)) {
|
||||
auto i = item.first.js;
|
||||
auto hSig = wtxItem.second.vjoinsplit[i].h_sig(
|
||||
*pzcashParams, wtxItem.second.joinSplitPubKey);
|
||||
item.second.nullifier = GetNoteNullifier(
|
||||
wtxItem.second.vjoinsplit[i],
|
||||
item.second.address,
|
||||
dec,
|
||||
hSig,
|
||||
item.first.n);
|
||||
}
|
||||
}
|
||||
}
|
||||
UpdateNullifierNoteMapWithTx(wtxItem.second);
|
||||
@@ -1231,7 +1269,9 @@ boost::optional<uint256> CWallet::GetNoteNullifier(const JSDescription& jsdesc,
|
||||
hSig,
|
||||
(unsigned char) n);
|
||||
auto note = note_pt.note(address);
|
||||
// SpendingKeys are only available if the wallet is unlocked
|
||||
// SpendingKeys are only available if:
|
||||
// - We have them (this isn't a viewing key)
|
||||
// - The wallet is unlocked
|
||||
libzcash::SpendingKey key;
|
||||
if (GetSpendingKey(address, key)) {
|
||||
ret = note.nullifier(key);
|
||||
@@ -3608,7 +3648,7 @@ bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee)
|
||||
* Find notes in the wallet filtered by payment address, min depth and ability to spend.
|
||||
* These notes are decrypted and added to the output parameter vector, outEntries.
|
||||
*/
|
||||
void CWallet::GetFilteredNotes(std::vector<CNotePlaintextEntry> & outEntries, std::string address, int minDepth, bool ignoreSpent)
|
||||
void CWallet::GetFilteredNotes(std::vector<CNotePlaintextEntry> & outEntries, std::string address, int minDepth, bool ignoreSpent, bool ignoreUnspendable)
|
||||
{
|
||||
bool fFilterAddress = false;
|
||||
libzcash::PaymentAddress filterPaymentAddress;
|
||||
@@ -3646,6 +3686,11 @@ void CWallet::GetFilteredNotes(std::vector<CNotePlaintextEntry> & outEntries, st
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip notes which cannot be spent
|
||||
if (ignoreUnspendable && !HaveSpendingKey(pa)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int i = jsop.js; // Index into CTransaction.vjoinsplit
|
||||
int j = jsop.n; // Index into JSDescription.ciphertexts
|
||||
|
||||
|
||||
@@ -950,9 +950,15 @@ public:
|
||||
//! Load spending key metadata (used by LoadWallet)
|
||||
bool LoadZKeyMetadata(const libzcash::PaymentAddress &addr, const CKeyMetadata &meta);
|
||||
//! Adds an encrypted spending key to the store, without saving it to disk (used by LoadWallet)
|
||||
bool LoadCryptedZKey(const libzcash::PaymentAddress &addr, const libzcash::ViewingKey &vk, const std::vector<unsigned char> &vchCryptedSecret);
|
||||
bool LoadCryptedZKey(const libzcash::PaymentAddress &addr, const libzcash::ReceivingKey &rk, const std::vector<unsigned char> &vchCryptedSecret);
|
||||
//! Adds an encrypted spending key to the store, and saves it to disk (virtual method, declared in crypter.h)
|
||||
bool AddCryptedSpendingKey(const libzcash::PaymentAddress &address, const libzcash::ViewingKey &vk, const std::vector<unsigned char> &vchCryptedSecret);
|
||||
bool AddCryptedSpendingKey(const libzcash::PaymentAddress &address, const libzcash::ReceivingKey &rk, const std::vector<unsigned char> &vchCryptedSecret);
|
||||
|
||||
//! Adds a viewing key to the store, and saves it to disk.
|
||||
bool AddViewingKey(const libzcash::ViewingKey &vk);
|
||||
bool RemoveViewingKey(const libzcash::ViewingKey &vk);
|
||||
//! Adds a viewing key to the store, without saving it to disk (used by LoadWallet)
|
||||
bool LoadViewingKey(const libzcash::ViewingKey &dest);
|
||||
|
||||
/**
|
||||
* Increment the next transaction order id
|
||||
@@ -1115,7 +1121,11 @@ public:
|
||||
void SetBroadcastTransactions(bool broadcast) { fBroadcastTransactions = broadcast; }
|
||||
|
||||
/* Find notes filtered by payment address, min depth, ability to spend */
|
||||
void GetFilteredNotes(std::vector<CNotePlaintextEntry> & outEntries, std::string address, int minDepth=1, bool ignoreSpent=true);
|
||||
void GetFilteredNotes(std::vector<CNotePlaintextEntry> & outEntries,
|
||||
std::string address,
|
||||
int minDepth=1,
|
||||
bool ignoreSpent=true,
|
||||
bool ignoreUnspendable=true);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey,
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteCryptedZKey(const libzcash::PaymentAddress & addr,
|
||||
const libzcash::ViewingKey &vk,
|
||||
const libzcash::ReceivingKey &rk,
|
||||
const std::vector<unsigned char>& vchCryptedSecret,
|
||||
const CKeyMetadata &keyMeta)
|
||||
{
|
||||
@@ -116,7 +116,7 @@ bool CWalletDB::WriteCryptedZKey(const libzcash::PaymentAddress & addr,
|
||||
if (!Write(std::make_pair(std::string("zkeymeta"), addr), keyMeta))
|
||||
return false;
|
||||
|
||||
if (!Write(std::make_pair(std::string("czkey"), addr), std::make_pair(vk, vchCryptedSecret), false))
|
||||
if (!Write(std::make_pair(std::string("czkey"), addr), std::make_pair(rk, vchCryptedSecret), false))
|
||||
return false;
|
||||
if (fEraseUnencryptedKey)
|
||||
{
|
||||
@@ -142,6 +142,18 @@ bool CWalletDB::WriteZKey(const libzcash::PaymentAddress& addr, const libzcash::
|
||||
return Write(std::make_pair(std::string("zkey"), addr), key, false);
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteViewingKey(const libzcash::ViewingKey &vk)
|
||||
{
|
||||
nWalletDBUpdated++;
|
||||
return Write(std::make_pair(std::string("vkey"), vk), '1');
|
||||
}
|
||||
|
||||
bool CWalletDB::EraseViewingKey(const libzcash::ViewingKey &vk)
|
||||
{
|
||||
nWalletDBUpdated++;
|
||||
return Erase(std::make_pair(std::string("vkey"), vk));
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
|
||||
{
|
||||
nWalletDBUpdated++;
|
||||
@@ -471,6 +483,19 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
||||
// so set the wallet birthday to the beginning of time.
|
||||
pwallet->nTimeFirstKey = 1;
|
||||
}
|
||||
else if (strType == "vkey")
|
||||
{
|
||||
libzcash::ViewingKey vk;
|
||||
ssKey >> vk;
|
||||
char fYes;
|
||||
ssValue >> fYes;
|
||||
if (fYes == '1')
|
||||
pwallet->LoadViewingKey(vk);
|
||||
|
||||
// Viewing keys have no birthday information for now,
|
||||
// so set the wallet birthday to the beginning of time.
|
||||
pwallet->nTimeFirstKey = 1;
|
||||
}
|
||||
else if (strType == "zkey")
|
||||
{
|
||||
libzcash::PaymentAddress addr;
|
||||
@@ -585,14 +610,14 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
||||
libzcash::PaymentAddress addr;
|
||||
ssKey >> addr;
|
||||
// Deserialization of a pair is just one item after another
|
||||
uint256 vkValue;
|
||||
ssValue >> vkValue;
|
||||
libzcash::ViewingKey vk(vkValue);
|
||||
uint256 rkValue;
|
||||
ssValue >> rkValue;
|
||||
libzcash::ReceivingKey rk(rkValue);
|
||||
vector<unsigned char> vchCryptedSecret;
|
||||
ssValue >> vchCryptedSecret;
|
||||
wss.nCKeys++;
|
||||
|
||||
if (!pwallet->LoadCryptedZKey(addr, vk, vchCryptedSecret))
|
||||
if (!pwallet->LoadCryptedZKey(addr, rk, vchCryptedSecret))
|
||||
{
|
||||
strErr = "Error reading wallet database: LoadCryptedZKey failed";
|
||||
return false;
|
||||
@@ -694,6 +719,7 @@ static bool IsKeyType(string strType)
|
||||
{
|
||||
return (strType== "key" || strType == "wkey" ||
|
||||
strType == "zkey" || strType == "czkey" ||
|
||||
strType == "vkey" ||
|
||||
strType == "mkey" || strType == "ckey");
|
||||
}
|
||||
|
||||
|
||||
@@ -136,10 +136,13 @@ public:
|
||||
/// Write spending key to wallet database, where key is payment address and value is spending key.
|
||||
bool WriteZKey(const libzcash::PaymentAddress& addr, const libzcash::SpendingKey& key, const CKeyMetadata &keyMeta);
|
||||
bool WriteCryptedZKey(const libzcash::PaymentAddress & addr,
|
||||
const libzcash::ViewingKey & vk,
|
||||
const libzcash::ReceivingKey & rk,
|
||||
const std::vector<unsigned char>& vchCryptedSecret,
|
||||
const CKeyMetadata &keyMeta);
|
||||
|
||||
bool WriteViewingKey(const libzcash::ViewingKey &vk);
|
||||
bool EraseViewingKey(const libzcash::ViewingKey &vk);
|
||||
|
||||
private:
|
||||
CWalletDB(const CWalletDB&);
|
||||
void operator=(const CWalletDB&);
|
||||
|
||||
@@ -12,12 +12,20 @@ uint256 PaymentAddress::GetHash() const {
|
||||
return Hash(ss.begin(), ss.end());
|
||||
}
|
||||
|
||||
uint256 ViewingKey::pk_enc() {
|
||||
uint256 ReceivingKey::pk_enc() const {
|
||||
return ZCNoteEncryption::generate_pubkey(*this);
|
||||
}
|
||||
|
||||
PaymentAddress ViewingKey::address() const {
|
||||
return PaymentAddress(a_pk, sk_enc.pk_enc());
|
||||
}
|
||||
|
||||
ReceivingKey SpendingKey::receiving_key() const {
|
||||
return ReceivingKey(ZCNoteEncryption::generate_privkey(*this));
|
||||
}
|
||||
|
||||
ViewingKey SpendingKey::viewing_key() const {
|
||||
return ViewingKey(ZCNoteEncryption::generate_privkey(*this));
|
||||
return ViewingKey(PRF_addr_a_pk(*this), receiving_key());
|
||||
}
|
||||
|
||||
SpendingKey SpendingKey::random() {
|
||||
@@ -25,7 +33,7 @@ SpendingKey SpendingKey::random() {
|
||||
}
|
||||
|
||||
PaymentAddress SpendingKey::address() const {
|
||||
return PaymentAddress(PRF_addr_a_pk(*this), viewing_key().pk_enc());
|
||||
return viewing_key().address();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
namespace libzcash {
|
||||
|
||||
const size_t SerializedPaymentAddressSize = 64;
|
||||
const size_t SerializedViewingKeySize = 64;
|
||||
const size_t SerializedSpendingKeySize = 32;
|
||||
|
||||
class PaymentAddress {
|
||||
@@ -38,11 +39,39 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class ViewingKey : public uint256 {
|
||||
class ReceivingKey : public uint256 {
|
||||
public:
|
||||
ViewingKey(uint256 sk_enc) : uint256(sk_enc) { }
|
||||
ReceivingKey() { }
|
||||
ReceivingKey(uint256 sk_enc) : uint256(sk_enc) { }
|
||||
|
||||
uint256 pk_enc();
|
||||
uint256 pk_enc() const;
|
||||
};
|
||||
|
||||
class ViewingKey {
|
||||
public:
|
||||
uint256 a_pk;
|
||||
ReceivingKey sk_enc;
|
||||
|
||||
ViewingKey() : a_pk(), sk_enc() { }
|
||||
ViewingKey(uint256 a_pk, ReceivingKey sk_enc) : a_pk(a_pk), sk_enc(sk_enc) { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
READWRITE(a_pk);
|
||||
READWRITE(sk_enc);
|
||||
}
|
||||
|
||||
PaymentAddress address() const;
|
||||
|
||||
friend inline bool operator==(const ViewingKey& a, const ViewingKey& b) {
|
||||
return a.a_pk == b.a_pk && a.sk_enc == b.sk_enc;
|
||||
}
|
||||
friend inline bool operator<(const ViewingKey& a, const ViewingKey& b) {
|
||||
return (a.a_pk < b.a_pk ||
|
||||
(a.a_pk == b.a_pk && a.sk_enc < b.sk_enc));
|
||||
}
|
||||
};
|
||||
|
||||
class SpendingKey : public uint252 {
|
||||
@@ -52,6 +81,7 @@ public:
|
||||
|
||||
static SpendingKey random();
|
||||
|
||||
ReceivingKey receiving_key() const;
|
||||
ViewingKey viewing_key() const;
|
||||
PaymentAddress address() const;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user