Implemented RPC calls z_importkey, z_exportkey, z_getnewaddress.
Modified RPC calls dumpwallet and importwallet to include spending keys.
This commit is contained in:
parent
2833fb14af
commit
c1c4594371
|
@ -97,7 +97,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "zcrawjoinsplit", 4 },
|
||||
{ "zcbenchmark", 1 },
|
||||
{ "zcbenchmark", 2 },
|
||||
{ "getblocksubsidy", 0}
|
||||
{ "getblocksubsidy", 0},
|
||||
{ "z_importkey", 1 }
|
||||
};
|
||||
|
||||
class CRPCConvertTable
|
||||
|
|
|
@ -381,7 +381,10 @@ static const CRPCCommand vRPCCommands[] =
|
|||
{ "wallet", "zcrawkeygen", &zc_raw_keygen, true },
|
||||
{ "wallet", "zcrawjoinsplit", &zc_raw_joinsplit, true },
|
||||
{ "wallet", "zcrawreceive", &zc_raw_receive, true },
|
||||
{ "wallet", "zcsamplejoinsplit", &zc_sample_joinsplit, true }
|
||||
{ "wallet", "zcsamplejoinsplit", &zc_sample_joinsplit, true },
|
||||
{ "wallet", "z_getnewaddress", &z_getnewaddress, true },
|
||||
{ "wallet", "z_exportkey", &z_exportkey, true },
|
||||
{ "wallet", "z_importkey", &z_importkey, true }
|
||||
#endif // ENABLE_WALLET
|
||||
};
|
||||
|
||||
|
|
|
@ -243,6 +243,10 @@ extern json_spirit::Value reconsiderblock(const json_spirit::Array& params, bool
|
|||
|
||||
extern json_spirit::Value getblocksubsidy(const json_spirit::Array& params, bool fHelp);
|
||||
|
||||
extern json_spirit::Value z_exportkey(const json_spirit::Array& params, bool fHelp); // in rpcdump.cpp
|
||||
extern json_spirit::Value z_importkey(const json_spirit::Array& params, bool fHelp); // in rpcdump.cpp
|
||||
extern json_spirit::Value z_getnewaddress(const json_spirit::Array& params, bool fHelp); // in rpcwallet.cpp
|
||||
|
||||
// in rest.cpp
|
||||
extern bool HTTPReq_REST(AcceptedConnection *conn,
|
||||
const std::string& strURI,
|
||||
|
|
|
@ -265,6 +265,32 @@ Value importwallet(const Array& params, bool fHelp)
|
|||
boost::split(vstr, line, boost::is_any_of(" "));
|
||||
if (vstr.size() < 2)
|
||||
continue;
|
||||
|
||||
// Let's see if the address is a valid Zcash spending key
|
||||
try {
|
||||
CZCSpendingKey spendingkey(vstr[0]);
|
||||
libzcash::SpendingKey key = spendingkey.Get();
|
||||
libzcash::PaymentAddress addr = key.address();
|
||||
if (pwalletMain->HaveSpendingKey(addr)) {
|
||||
LogPrintf("Skipping import of zaddr %s (key already present)\n", CZCPaymentAddress(addr).ToString());
|
||||
continue;
|
||||
}
|
||||
int64_t nTime = DecodeDumpTime(vstr[1]);
|
||||
LogPrintf("Importing zaddr %s...\n", CZCPaymentAddress(addr).ToString());
|
||||
if (!pwalletMain->AddZKey(key)) {
|
||||
// Something went wrong
|
||||
fGood = false;
|
||||
continue;
|
||||
}
|
||||
// Successfully imported zaddr. Now import the metadata.
|
||||
pwalletMain->mapZKeyMetadata[addr].nCreateTime = nTime;
|
||||
continue;
|
||||
}
|
||||
catch (...) {
|
||||
// Not a valid spending key, so carry on and see if it's a Bitcoin style address.
|
||||
}
|
||||
|
||||
|
||||
CBitcoinSecret vchSecret;
|
||||
if (!vchSecret.SetString(vstr[0]))
|
||||
continue;
|
||||
|
@ -419,7 +445,124 @@ Value dumpwallet(const Array& params, bool fHelp)
|
|||
}
|
||||
}
|
||||
file << "\n";
|
||||
|
||||
// dump the zkeys
|
||||
std::set<libzcash::PaymentAddress> addresses;
|
||||
pwalletMain->GetPaymentAddresses(addresses);
|
||||
file << "\n";
|
||||
file << "# Zkeys\n";
|
||||
file << "\n";
|
||||
for (auto addr : addresses ) {
|
||||
libzcash::SpendingKey key;
|
||||
if (pwalletMain->GetSpendingKey(addr, key)) {
|
||||
std::string strTime = EncodeDumpTime(pwalletMain->mapZKeyMetadata[addr].nCreateTime);
|
||||
file << strprintf("%s %s # zaddr=%s\n", CZCSpendingKey(key).ToString(), strTime, CZCPaymentAddress(addr).ToString());
|
||||
}
|
||||
}
|
||||
file << "\n";
|
||||
|
||||
file << "# End of dump\n";
|
||||
file.close();
|
||||
return Value::null;
|
||||
}
|
||||
|
||||
|
||||
Value z_importkey(const Array& params, bool fHelp)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return Value::null;
|
||||
|
||||
if (fHelp || params.size() < 1 || params.size() > 3)
|
||||
throw runtime_error(
|
||||
"z_importkey \"zkey\" ( \"label\" rescan )\n"
|
||||
"\nAdds a zkey (as returned by z_exportkey) to your wallet.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"zkey\" (string, required) The zkey (see z_exportkey)\n"
|
||||
"2. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
|
||||
"\nNote: This call can take minutes to complete if rescan is true.\n"
|
||||
"\nExamples:\n"
|
||||
"\nExport a zkey\n"
|
||||
+ HelpExampleCli("z_exportkey", "\"myaddress\"") +
|
||||
"\nImport the zkey with rescan\n"
|
||||
+ HelpExampleCli("z_importkey", "\"mykey\"") +
|
||||
"\nImport using a label and without rescan\n"
|
||||
+ HelpExampleCli("z_importkey", "\"mykey\" \"testing\" false") +
|
||||
"\nAs a JSON-RPC call\n"
|
||||
+ HelpExampleRpc("z_importkey", "\"mykey\", \"testing\", false")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
EnsureWalletIsUnlocked();
|
||||
|
||||
// Whether to perform rescan after import
|
||||
bool fRescan = true;
|
||||
if (params.size() > 1)
|
||||
fRescan = params[1].get_bool();
|
||||
|
||||
string strSecret = params[0].get_str();
|
||||
CZCSpendingKey spendingkey(strSecret);
|
||||
auto key = spendingkey.Get();
|
||||
auto addr = key.address();
|
||||
|
||||
{
|
||||
pwalletMain->MarkDirty();
|
||||
|
||||
// Don't throw error in case a key is already there
|
||||
if (pwalletMain->HaveSpendingKey(addr))
|
||||
return Value::null;
|
||||
|
||||
if (!pwalletMain-> AddZKey(key))
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding spending key to wallet");
|
||||
|
||||
pwalletMain->mapZKeyMetadata[addr].nCreateTime = 1;
|
||||
|
||||
// We want to scan for transactions and notes
|
||||
if (fRescan) {
|
||||
pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true);
|
||||
}
|
||||
}
|
||||
|
||||
return Value::null;
|
||||
}
|
||||
|
||||
|
||||
Value z_exportkey(const Array& params, bool fHelp)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return Value::null;
|
||||
|
||||
if (fHelp || params.size() != 1)
|
||||
throw runtime_error(
|
||||
"z_exportkey \"zaddr\"\n"
|
||||
"\nReveals the zkey corresponding to 'zaddr'.\n"
|
||||
"Then the z_importkey can be used with this output\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"zaddr\" (string, required) The zaddr for the private key\n"
|
||||
"\nResult:\n"
|
||||
"\"key\" (string) The private key\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("z_exportkey", "\"myaddress\"")
|
||||
+ HelpExampleCli("z_importkey", "\"mykey\"")
|
||||
+ HelpExampleRpc("z_exportkey", "\"myaddress\"")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
EnsureWalletIsUnlocked();
|
||||
|
||||
string strAddress = params[0].get_str();
|
||||
|
||||
CZCPaymentAddress address(strAddress);
|
||||
auto addr = address.Get();
|
||||
if (!pwalletMain->HaveSpendingKey(addr))
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private zkey for this zaddr");
|
||||
|
||||
libzcash::SpendingKey k;
|
||||
if (!pwalletMain->GetSpendingKey(addr, k))
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private zkey for this zaddr");
|
||||
|
||||
CZCSpendingKey spendingkey(k);
|
||||
return spendingkey.ToString();
|
||||
}
|
||||
|
||||
|
|
|
@ -2769,3 +2769,29 @@ Value zc_raw_keygen(const json_spirit::Array& params, bool fHelp)
|
|||
result.push_back(Pair("zcviewingkey", viewing_hex));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Value z_getnewaddress(const Array& params, bool fHelp)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return Value::null;
|
||||
|
||||
if (fHelp || params.size() > 1)
|
||||
throw runtime_error(
|
||||
"z_getnewaddress\n"
|
||||
"\nReturns a new zaddr for receiving payments.\n"
|
||||
"\nArguments:\n"
|
||||
"\nResult:\n"
|
||||
"\"zcashaddress\" (string) The new zaddr\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("z_getnewaddress", "")
|
||||
+ HelpExampleRpc("z_getnewaddress", "")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
CZCPaymentAddress pubaddr = pwalletMain->GenerateNewZKey();
|
||||
std::string result = pubaddr.ToString();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <boost/thread.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace libzcash;
|
||||
|
||||
/**
|
||||
* Settings
|
||||
|
@ -70,6 +71,47 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
|
|||
return &(it->second);
|
||||
}
|
||||
|
||||
// Generate a new spending key and return its public payment address
|
||||
CZCPaymentAddress CWallet::GenerateNewZKey()
|
||||
{
|
||||
AssertLockHeld(cs_wallet); // mapZKeyMetadata
|
||||
auto k = SpendingKey::random();
|
||||
auto addr = k.address();
|
||||
|
||||
// Check for collision, even though it is unlikely to ever occur
|
||||
if (CCryptoKeyStore::HaveSpendingKey(addr))
|
||||
throw std::runtime_error("CWallet::GenerateNewSpendingKey(): Collision detected");
|
||||
|
||||
// Create new metadata
|
||||
int64_t nCreationTime = GetTime();
|
||||
mapZKeyMetadata[addr] = CKeyMetadata(nCreationTime);
|
||||
|
||||
CZCPaymentAddress pubaddr(addr);
|
||||
if (!AddZKey(k))
|
||||
throw std::runtime_error("CWallet::GenerateNewSpendingKey(): AddZKey failed");
|
||||
return pubaddr;
|
||||
}
|
||||
|
||||
// Add spending key to keystore and persist to disk
|
||||
bool CWallet::AddZKey(const libzcash::SpendingKey &key)
|
||||
{
|
||||
AssertLockHeld(cs_wallet); // mapZKeyMetadata
|
||||
auto addr = key.address();
|
||||
|
||||
if (!CCryptoKeyStore::AddSpendingKey(key))
|
||||
return false;
|
||||
|
||||
if (!fFileBacked)
|
||||
return true;
|
||||
|
||||
if (!IsCrypted()) {
|
||||
return CWalletDB(strWalletFile).WriteZKey(addr,
|
||||
key,
|
||||
mapZKeyMetadata[addr]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
CPubKey CWallet::GenerateNewKey()
|
||||
{
|
||||
AssertLockHeld(cs_wallet); // mapKeyMetadata
|
||||
|
@ -149,11 +191,23 @@ bool CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CWallet::LoadZKeyMetadata(const PaymentAddress &addr, const CKeyMetadata &meta)
|
||||
{
|
||||
AssertLockHeld(cs_wallet); // mapZKeyMetadata
|
||||
mapZKeyMetadata[addr] = meta;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
|
||||
{
|
||||
return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret);
|
||||
}
|
||||
|
||||
bool CWallet::LoadZKey(const libzcash::SpendingKey &key)
|
||||
{
|
||||
return CCryptoKeyStore::AddSpendingKey(key);
|
||||
}
|
||||
|
||||
bool CWallet::AddCScript(const CScript& redeemScript)
|
||||
{
|
||||
if (!CCryptoKeyStore::AddCScript(redeemScript))
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include "wallet/crypter.h"
|
||||
#include "wallet/wallet_ismine.h"
|
||||
#include "wallet/walletdb.h"
|
||||
#include "zcash/Address.hpp"
|
||||
#include "base58.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
|
@ -486,6 +488,7 @@ public:
|
|||
|
||||
std::set<int64_t> setKeyPool;
|
||||
std::map<CKeyID, CKeyMetadata> mapKeyMetadata;
|
||||
std::map<libzcash::PaymentAddress, CKeyMetadata> mapZKeyMetadata;
|
||||
|
||||
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
|
||||
MasterKeyMap mapMasterKeys;
|
||||
|
@ -595,6 +598,19 @@ public:
|
|||
|
||||
void GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const;
|
||||
|
||||
/**
|
||||
* ZKeys
|
||||
*/
|
||||
//! Generates a new zaddr
|
||||
CZCPaymentAddress GenerateNewZKey();
|
||||
//! Adds spending key to the store, and saves it to disk
|
||||
bool AddZKey(const libzcash::SpendingKey &key);
|
||||
//! Adds spending key to the store, without saving it to disk (used by LoadWallet)
|
||||
bool LoadZKey(const libzcash::SpendingKey &key);
|
||||
//! Load spending key metadata (used by LoadWallet)
|
||||
bool LoadZKeyMetadata(const libzcash::PaymentAddress &addr, const CKeyMetadata &meta);
|
||||
|
||||
|
||||
/**
|
||||
* Increment the next transaction order id
|
||||
* @return next transaction order id
|
||||
|
|
|
@ -110,6 +110,17 @@ bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
|
|||
return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteZKey(const libzcash::PaymentAddress& addr, const libzcash::SpendingKey& key, const CKeyMetadata &keyMeta)
|
||||
{
|
||||
nWalletDBUpdated++;
|
||||
|
||||
if (!Write(std::make_pair(std::string("zkeymeta"), addr), keyMeta))
|
||||
return false;
|
||||
|
||||
// pair is: tuple_key("spendingkey", paymentaddress) --> secretkey
|
||||
return Write(std::make_pair(std::string("zkey"), addr), key, false);
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
|
||||
{
|
||||
nWalletDBUpdated++;
|
||||
|
@ -330,13 +341,15 @@ public:
|
|||
unsigned int nKeys;
|
||||
unsigned int nCKeys;
|
||||
unsigned int nKeyMeta;
|
||||
unsigned int nZKeys;
|
||||
unsigned int nZKeyMeta;
|
||||
bool fIsEncrypted;
|
||||
bool fAnyUnordered;
|
||||
int nFileVersion;
|
||||
vector<uint256> vWalletUpgrade;
|
||||
|
||||
CWalletScanState() {
|
||||
nKeys = nCKeys = nKeyMeta = 0;
|
||||
nKeys = nCKeys = nKeyMeta = nZKeys = nZKeyMeta = 0;
|
||||
fIsEncrypted = false;
|
||||
fAnyUnordered = false;
|
||||
nFileVersion = 0;
|
||||
|
@ -429,6 +442,23 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
|||
// so set the wallet birthday to the beginning of time.
|
||||
pwallet->nTimeFirstKey = 1;
|
||||
}
|
||||
else if (strType == "zkey")
|
||||
{
|
||||
libzcash::PaymentAddress addr;
|
||||
ssKey >> addr;
|
||||
libzcash::SpendingKey key;
|
||||
ssValue >> key;
|
||||
|
||||
if (!pwallet->LoadZKey(key))
|
||||
{
|
||||
strErr = "Error reading wallet database: LoadZKey failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
wss.nZKeys++;
|
||||
|
||||
CZCPaymentAddress pubaddr(addr);
|
||||
}
|
||||
else if (strType == "key" || strType == "wkey")
|
||||
{
|
||||
CPubKey vchPubKey;
|
||||
|
@ -538,6 +568,18 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
|||
(keyMeta.nCreateTime < pwallet->nTimeFirstKey))
|
||||
pwallet->nTimeFirstKey = keyMeta.nCreateTime;
|
||||
}
|
||||
else if (strType == "zkeymeta")
|
||||
{
|
||||
libzcash::PaymentAddress addr;
|
||||
ssKey >> addr;
|
||||
CKeyMetadata keyMeta;
|
||||
ssValue >> keyMeta;
|
||||
wss.nZKeyMeta++;
|
||||
|
||||
pwallet->LoadZKeyMetadata(addr, keyMeta);
|
||||
|
||||
// ignore earliest key creation time as taddr will exist before any zaddr
|
||||
}
|
||||
else if (strType == "defaultkey")
|
||||
{
|
||||
ssValue >> pwallet->vchDefaultKey;
|
||||
|
@ -685,6 +727,10 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
|
|||
LogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n",
|
||||
wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys);
|
||||
|
||||
// TODO: Keep track of encrypted ZKeys
|
||||
LogPrintf("ZKeys: %u plaintext, -- encrypted, %u w/metadata, %u total\n",
|
||||
wss.nZKeys, wss.nZKeyMeta, wss.nZKeys + 0);
|
||||
|
||||
// nTimeFirstKey is only reliable if all keys have metadata
|
||||
if ((wss.nKeys + wss.nCKeys) != wss.nKeyMeta)
|
||||
pwallet->nTimeFirstKey = 1; // 0 would be considered 'no value'
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "wallet/db.h"
|
||||
#include "key.h"
|
||||
#include "keystore.h"
|
||||
#include "zcash/Address.hpp"
|
||||
|
||||
#include <list>
|
||||
#include <stdint.h>
|
||||
|
@ -130,6 +131,9 @@ public:
|
|||
static bool Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKeys);
|
||||
static bool Recover(CDBEnv& dbenv, const std::string& filename);
|
||||
|
||||
/// 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);
|
||||
|
||||
private:
|
||||
CWalletDB(const CWalletDB&);
|
||||
void operator=(const CWalletDB&);
|
||||
|
|
Loading…
Reference in New Issue