mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 20:12:57 +01:00
Use importmulti timestamp when importing watch only keys
When importing a watch-only address over importmulti with a specific timestamp, the wallet's nTimeFirstKey is currently set to 1. After this change, the provided timestamp will be used and stored as metadata associated with watch-only key. This can improve wallet performance because it can avoid the need to scan the entire blockchain for watch only addresses when timestamps are provided. Also adds timestamp to validateaddress return value (needed for tests). Fixes #9034.
This commit is contained in:
parent
a58370e6a2
commit
a80f98b1c7
@ -20,6 +20,7 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
print ("Mining blocks...")
|
print ("Mining blocks...")
|
||||||
self.nodes[0].generate(1)
|
self.nodes[0].generate(1)
|
||||||
self.nodes[1].generate(1)
|
self.nodes[1].generate(1)
|
||||||
|
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
|
||||||
|
|
||||||
# keyword definition
|
# keyword definition
|
||||||
PRIV_KEY = 'privkey'
|
PRIV_KEY = 'privkey'
|
||||||
@ -59,6 +60,9 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
address_assert = self.nodes[1].validateaddress(address['address'])
|
address_assert = self.nodes[1].validateaddress(address['address'])
|
||||||
assert_equal(address_assert['iswatchonly'], True)
|
assert_equal(address_assert['iswatchonly'], True)
|
||||||
assert_equal(address_assert['ismine'], False)
|
assert_equal(address_assert['ismine'], False)
|
||||||
|
assert_equal(address_assert['timestamp'], timestamp)
|
||||||
|
watchonly_address = address['address']
|
||||||
|
watchonly_timestamp = timestamp
|
||||||
|
|
||||||
|
|
||||||
# ScriptPubKey + internal
|
# ScriptPubKey + internal
|
||||||
@ -73,6 +77,7 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
address_assert = self.nodes[1].validateaddress(address['address'])
|
address_assert = self.nodes[1].validateaddress(address['address'])
|
||||||
assert_equal(address_assert['iswatchonly'], True)
|
assert_equal(address_assert['iswatchonly'], True)
|
||||||
assert_equal(address_assert['ismine'], False)
|
assert_equal(address_assert['ismine'], False)
|
||||||
|
assert_equal(address_assert['timestamp'], timestamp)
|
||||||
|
|
||||||
# ScriptPubKey + !internal
|
# ScriptPubKey + !internal
|
||||||
print("Should not import a scriptPubKey without internal flag")
|
print("Should not import a scriptPubKey without internal flag")
|
||||||
@ -87,6 +92,7 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
address_assert = self.nodes[1].validateaddress(address['address'])
|
address_assert = self.nodes[1].validateaddress(address['address'])
|
||||||
assert_equal(address_assert['iswatchonly'], False)
|
assert_equal(address_assert['iswatchonly'], False)
|
||||||
assert_equal(address_assert['ismine'], False)
|
assert_equal(address_assert['ismine'], False)
|
||||||
|
assert_equal('timestamp' in address_assert, False)
|
||||||
|
|
||||||
|
|
||||||
# Address + Public key + !Internal
|
# Address + Public key + !Internal
|
||||||
@ -103,6 +109,7 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
address_assert = self.nodes[1].validateaddress(address['address'])
|
address_assert = self.nodes[1].validateaddress(address['address'])
|
||||||
assert_equal(address_assert['iswatchonly'], True)
|
assert_equal(address_assert['iswatchonly'], True)
|
||||||
assert_equal(address_assert['ismine'], False)
|
assert_equal(address_assert['ismine'], False)
|
||||||
|
assert_equal(address_assert['timestamp'], timestamp)
|
||||||
|
|
||||||
|
|
||||||
# ScriptPubKey + Public key + internal
|
# ScriptPubKey + Public key + internal
|
||||||
@ -119,6 +126,7 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
address_assert = self.nodes[1].validateaddress(address['address'])
|
address_assert = self.nodes[1].validateaddress(address['address'])
|
||||||
assert_equal(address_assert['iswatchonly'], True)
|
assert_equal(address_assert['iswatchonly'], True)
|
||||||
assert_equal(address_assert['ismine'], False)
|
assert_equal(address_assert['ismine'], False)
|
||||||
|
assert_equal(address_assert['timestamp'], timestamp)
|
||||||
|
|
||||||
# ScriptPubKey + Public key + !internal
|
# ScriptPubKey + Public key + !internal
|
||||||
print("Should not import a scriptPubKey without internal and with public key")
|
print("Should not import a scriptPubKey without internal and with public key")
|
||||||
@ -135,11 +143,11 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
address_assert = self.nodes[1].validateaddress(address['address'])
|
address_assert = self.nodes[1].validateaddress(address['address'])
|
||||||
assert_equal(address_assert['iswatchonly'], False)
|
assert_equal(address_assert['iswatchonly'], False)
|
||||||
assert_equal(address_assert['ismine'], False)
|
assert_equal(address_assert['ismine'], False)
|
||||||
|
assert_equal('timestamp' in address_assert, False)
|
||||||
|
|
||||||
# Address + Private key + !watchonly
|
# Address + Private key + !watchonly
|
||||||
print("Should import an address with private key")
|
print("Should import an address with private key")
|
||||||
address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
|
address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
|
||||||
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
|
|
||||||
result = self.nodes[1].importmulti([{
|
result = self.nodes[1].importmulti([{
|
||||||
"scriptPubKey": {
|
"scriptPubKey": {
|
||||||
"address": address['address']
|
"address": address['address']
|
||||||
@ -170,6 +178,7 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
address_assert = self.nodes[1].validateaddress(address['address'])
|
address_assert = self.nodes[1].validateaddress(address['address'])
|
||||||
assert_equal(address_assert['iswatchonly'], False)
|
assert_equal(address_assert['iswatchonly'], False)
|
||||||
assert_equal(address_assert['ismine'], False)
|
assert_equal(address_assert['ismine'], False)
|
||||||
|
assert_equal('timestamp' in address_assert, False)
|
||||||
|
|
||||||
# ScriptPubKey + Private key + internal
|
# ScriptPubKey + Private key + internal
|
||||||
print("Should import a scriptPubKey with internal and with private key")
|
print("Should import a scriptPubKey with internal and with private key")
|
||||||
@ -184,6 +193,7 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
address_assert = self.nodes[1].validateaddress(address['address'])
|
address_assert = self.nodes[1].validateaddress(address['address'])
|
||||||
assert_equal(address_assert['iswatchonly'], False)
|
assert_equal(address_assert['iswatchonly'], False)
|
||||||
assert_equal(address_assert['ismine'], True)
|
assert_equal(address_assert['ismine'], True)
|
||||||
|
assert_equal(address_assert['timestamp'], timestamp)
|
||||||
|
|
||||||
# ScriptPubKey + Private key + !internal
|
# ScriptPubKey + Private key + !internal
|
||||||
print("Should not import a scriptPubKey without internal and with private key")
|
print("Should not import a scriptPubKey without internal and with private key")
|
||||||
@ -199,6 +209,7 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
address_assert = self.nodes[1].validateaddress(address['address'])
|
address_assert = self.nodes[1].validateaddress(address['address'])
|
||||||
assert_equal(address_assert['iswatchonly'], False)
|
assert_equal(address_assert['iswatchonly'], False)
|
||||||
assert_equal(address_assert['ismine'], False)
|
assert_equal(address_assert['ismine'], False)
|
||||||
|
assert_equal('timestamp' in address_assert, False)
|
||||||
|
|
||||||
|
|
||||||
# P2SH address
|
# P2SH address
|
||||||
@ -209,6 +220,7 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
self.nodes[1].generate(100)
|
self.nodes[1].generate(100)
|
||||||
transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
|
transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
|
||||||
self.nodes[1].generate(1)
|
self.nodes[1].generate(1)
|
||||||
|
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
|
||||||
transaction = self.nodes[1].gettransaction(transactionid)
|
transaction = self.nodes[1].gettransaction(transactionid)
|
||||||
|
|
||||||
print("Should import a p2sh")
|
print("Should import a p2sh")
|
||||||
@ -222,6 +234,7 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
address_assert = self.nodes[1].validateaddress(multi_sig_script['address'])
|
address_assert = self.nodes[1].validateaddress(multi_sig_script['address'])
|
||||||
assert_equal(address_assert['isscript'], True)
|
assert_equal(address_assert['isscript'], True)
|
||||||
assert_equal(address_assert['iswatchonly'], True)
|
assert_equal(address_assert['iswatchonly'], True)
|
||||||
|
assert_equal(address_assert['timestamp'], timestamp)
|
||||||
p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0]
|
p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0]
|
||||||
assert_equal(p2shunspent['spendable'], False)
|
assert_equal(p2shunspent['spendable'], False)
|
||||||
assert_equal(p2shunspent['solvable'], False)
|
assert_equal(p2shunspent['solvable'], False)
|
||||||
@ -235,6 +248,7 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
self.nodes[1].generate(100)
|
self.nodes[1].generate(100)
|
||||||
transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
|
transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
|
||||||
self.nodes[1].generate(1)
|
self.nodes[1].generate(1)
|
||||||
|
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
|
||||||
transaction = self.nodes[1].gettransaction(transactionid)
|
transaction = self.nodes[1].gettransaction(transactionid)
|
||||||
|
|
||||||
print("Should import a p2sh with respective redeem script")
|
print("Should import a p2sh with respective redeem script")
|
||||||
@ -246,6 +260,8 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
"redeemscript": multi_sig_script['redeemScript']
|
"redeemscript": multi_sig_script['redeemScript']
|
||||||
}])
|
}])
|
||||||
assert_equal(result[0]['success'], True)
|
assert_equal(result[0]['success'], True)
|
||||||
|
address_assert = self.nodes[1].validateaddress(multi_sig_script['address'])
|
||||||
|
assert_equal(address_assert['timestamp'], timestamp)
|
||||||
|
|
||||||
p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0]
|
p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0]
|
||||||
assert_equal(p2shunspent['spendable'], False)
|
assert_equal(p2shunspent['spendable'], False)
|
||||||
@ -260,6 +276,7 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
self.nodes[1].generate(100)
|
self.nodes[1].generate(100)
|
||||||
transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
|
transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
|
||||||
self.nodes[1].generate(1)
|
self.nodes[1].generate(1)
|
||||||
|
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
|
||||||
transaction = self.nodes[1].gettransaction(transactionid)
|
transaction = self.nodes[1].gettransaction(transactionid)
|
||||||
|
|
||||||
print("Should import a p2sh with respective redeem script and private keys")
|
print("Should import a p2sh with respective redeem script and private keys")
|
||||||
@ -272,6 +289,8 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
"keys": [ self.nodes[0].dumpprivkey(sig_address_1['address']), self.nodes[0].dumpprivkey(sig_address_2['address'])]
|
"keys": [ self.nodes[0].dumpprivkey(sig_address_1['address']), self.nodes[0].dumpprivkey(sig_address_2['address'])]
|
||||||
}])
|
}])
|
||||||
assert_equal(result[0]['success'], True)
|
assert_equal(result[0]['success'], True)
|
||||||
|
address_assert = self.nodes[1].validateaddress(multi_sig_script['address'])
|
||||||
|
assert_equal(address_assert['timestamp'], timestamp)
|
||||||
|
|
||||||
p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0]
|
p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0]
|
||||||
assert_equal(p2shunspent['spendable'], False)
|
assert_equal(p2shunspent['spendable'], False)
|
||||||
@ -319,6 +338,7 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
address_assert = self.nodes[1].validateaddress(address['address'])
|
address_assert = self.nodes[1].validateaddress(address['address'])
|
||||||
assert_equal(address_assert['iswatchonly'], False)
|
assert_equal(address_assert['iswatchonly'], False)
|
||||||
assert_equal(address_assert['ismine'], False)
|
assert_equal(address_assert['ismine'], False)
|
||||||
|
assert_equal('timestamp' in address_assert, False)
|
||||||
|
|
||||||
|
|
||||||
# ScriptPubKey + Public key + internal + Wrong pubkey
|
# ScriptPubKey + Public key + internal + Wrong pubkey
|
||||||
@ -338,6 +358,7 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
address_assert = self.nodes[1].validateaddress(address['address'])
|
address_assert = self.nodes[1].validateaddress(address['address'])
|
||||||
assert_equal(address_assert['iswatchonly'], False)
|
assert_equal(address_assert['iswatchonly'], False)
|
||||||
assert_equal(address_assert['ismine'], False)
|
assert_equal(address_assert['ismine'], False)
|
||||||
|
assert_equal('timestamp' in address_assert, False)
|
||||||
|
|
||||||
|
|
||||||
# Address + Private key + !watchonly + Wrong private key
|
# Address + Private key + !watchonly + Wrong private key
|
||||||
@ -357,6 +378,7 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
address_assert = self.nodes[1].validateaddress(address['address'])
|
address_assert = self.nodes[1].validateaddress(address['address'])
|
||||||
assert_equal(address_assert['iswatchonly'], False)
|
assert_equal(address_assert['iswatchonly'], False)
|
||||||
assert_equal(address_assert['ismine'], False)
|
assert_equal(address_assert['ismine'], False)
|
||||||
|
assert_equal('timestamp' in address_assert, False)
|
||||||
|
|
||||||
|
|
||||||
# ScriptPubKey + Private key + internal + Wrong private key
|
# ScriptPubKey + Private key + internal + Wrong private key
|
||||||
@ -375,6 +397,15 @@ class ImportMultiTest (BitcoinTestFramework):
|
|||||||
address_assert = self.nodes[1].validateaddress(address['address'])
|
address_assert = self.nodes[1].validateaddress(address['address'])
|
||||||
assert_equal(address_assert['iswatchonly'], False)
|
assert_equal(address_assert['iswatchonly'], False)
|
||||||
assert_equal(address_assert['ismine'], False)
|
assert_equal(address_assert['ismine'], False)
|
||||||
|
assert_equal('timestamp' in address_assert, False)
|
||||||
|
|
||||||
|
# restart nodes to check for proper serialization/deserialization of watch only address
|
||||||
|
stop_nodes(self.nodes)
|
||||||
|
self.nodes = start_nodes(2, self.options.tmpdir)
|
||||||
|
address_assert = self.nodes[1].validateaddress(watchonly_address)
|
||||||
|
assert_equal(address_assert['iswatchonly'], True)
|
||||||
|
assert_equal(address_assert['ismine'], False)
|
||||||
|
assert_equal(address_assert['timestamp'], watchonly_timestamp);
|
||||||
|
|
||||||
# Bad or missing timestamps
|
# Bad or missing timestamps
|
||||||
print("Should throw on invalid or missing timestamp values")
|
print("Should throw on invalid or missing timestamp values")
|
||||||
|
@ -208,6 +208,9 @@ UniValue validateaddress(const JSONRPCRequest& request)
|
|||||||
if (pwalletMain) {
|
if (pwalletMain) {
|
||||||
const auto& meta = pwalletMain->mapKeyMetadata;
|
const auto& meta = pwalletMain->mapKeyMetadata;
|
||||||
auto it = address.GetKeyID(keyID) ? meta.find(keyID) : meta.end();
|
auto it = address.GetKeyID(keyID) ? meta.find(keyID) : meta.end();
|
||||||
|
if (it == meta.end()) {
|
||||||
|
it = meta.find(CScriptID(scriptPubKey));
|
||||||
|
}
|
||||||
if (it != meta.end()) {
|
if (it != meta.end()) {
|
||||||
ret.push_back(Pair("timestamp", it->second.nCreateTime));
|
ret.push_back(Pair("timestamp", it->second.nCreateTime));
|
||||||
if (!it->second.hdKeypath.empty()) {
|
if (!it->second.hdKeypath.empty()) {
|
||||||
|
@ -161,7 +161,7 @@ void ImportScript(const CScript& script, const string& strLabel, bool isRedeemSc
|
|||||||
|
|
||||||
pwalletMain->MarkDirty();
|
pwalletMain->MarkDirty();
|
||||||
|
|
||||||
if (!pwalletMain->HaveWatchOnly(script) && !pwalletMain->AddWatchOnly(script))
|
if (!pwalletMain->HaveWatchOnly(script) && !pwalletMain->AddWatchOnly(script, 0 /* nCreateTime */))
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
|
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
|
||||||
|
|
||||||
if (isRedeemScript) {
|
if (isRedeemScript) {
|
||||||
@ -575,15 +575,17 @@ UniValue dumpwallet(const JSONRPCRequest& request)
|
|||||||
if (!file.is_open())
|
if (!file.is_open())
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
|
||||||
|
|
||||||
std::map<CKeyID, int64_t> mapKeyBirth;
|
std::map<CTxDestination, int64_t> mapKeyBirth;
|
||||||
std::set<CKeyID> setKeyPool;
|
std::set<CKeyID> setKeyPool;
|
||||||
pwalletMain->GetKeyBirthTimes(mapKeyBirth);
|
pwalletMain->GetKeyBirthTimes(mapKeyBirth);
|
||||||
pwalletMain->GetAllReserveKeys(setKeyPool);
|
pwalletMain->GetAllReserveKeys(setKeyPool);
|
||||||
|
|
||||||
// sort time/key pairs
|
// sort time/key pairs
|
||||||
std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
|
std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
|
||||||
for (std::map<CKeyID, int64_t>::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) {
|
for (const auto& entry : mapKeyBirth) {
|
||||||
vKeyBirth.push_back(std::make_pair(it->second, it->first));
|
if (const CKeyID* keyID = boost::get<CKeyID>(&entry.first)) { // set and test
|
||||||
|
vKeyBirth.push_back(std::make_pair(entry.second, *keyID));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mapKeyBirth.clear();
|
mapKeyBirth.clear();
|
||||||
std::sort(vKeyBirth.begin(), vKeyBirth.end());
|
std::sort(vKeyBirth.begin(), vKeyBirth.end());
|
||||||
@ -720,7 +722,7 @@ UniValue ProcessImport(const UniValue& data, const int64_t timestamp)
|
|||||||
|
|
||||||
pwalletMain->MarkDirty();
|
pwalletMain->MarkDirty();
|
||||||
|
|
||||||
if (!pwalletMain->HaveWatchOnly(redeemScript) && !pwalletMain->AddWatchOnly(redeemScript)) {
|
if (!pwalletMain->HaveWatchOnly(redeemScript) && !pwalletMain->AddWatchOnly(redeemScript, timestamp)) {
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
|
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -737,7 +739,7 @@ UniValue ProcessImport(const UniValue& data, const int64_t timestamp)
|
|||||||
|
|
||||||
pwalletMain->MarkDirty();
|
pwalletMain->MarkDirty();
|
||||||
|
|
||||||
if (!pwalletMain->HaveWatchOnly(redeemDestination) && !pwalletMain->AddWatchOnly(redeemDestination)) {
|
if (!pwalletMain->HaveWatchOnly(redeemDestination) && !pwalletMain->AddWatchOnly(redeemDestination, timestamp)) {
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
|
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -830,7 +832,7 @@ UniValue ProcessImport(const UniValue& data, const int64_t timestamp)
|
|||||||
|
|
||||||
pwalletMain->MarkDirty();
|
pwalletMain->MarkDirty();
|
||||||
|
|
||||||
if (!pwalletMain->HaveWatchOnly(pubKeyScript) && !pwalletMain->AddWatchOnly(pubKeyScript)) {
|
if (!pwalletMain->HaveWatchOnly(pubKeyScript) && !pwalletMain->AddWatchOnly(pubKeyScript, timestamp)) {
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
|
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -848,7 +850,7 @@ UniValue ProcessImport(const UniValue& data, const int64_t timestamp)
|
|||||||
|
|
||||||
pwalletMain->MarkDirty();
|
pwalletMain->MarkDirty();
|
||||||
|
|
||||||
if (!pwalletMain->HaveWatchOnly(scriptRawPubKey) && !pwalletMain->AddWatchOnly(scriptRawPubKey)) {
|
if (!pwalletMain->HaveWatchOnly(scriptRawPubKey) && !pwalletMain->AddWatchOnly(scriptRawPubKey, timestamp)) {
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
|
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -922,7 +924,7 @@ UniValue ProcessImport(const UniValue& data, const int64_t timestamp)
|
|||||||
|
|
||||||
pwalletMain->MarkDirty();
|
pwalletMain->MarkDirty();
|
||||||
|
|
||||||
if (!pwalletMain->HaveWatchOnly(script) && !pwalletMain->AddWatchOnly(script)) {
|
if (!pwalletMain->HaveWatchOnly(script) && !pwalletMain->AddWatchOnly(script, timestamp)) {
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
|
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,11 +206,11 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta)
|
bool CWallet::LoadKeyMetadata(const CTxDestination& keyID, const CKeyMetadata &meta)
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_wallet); // mapKeyMetadata
|
AssertLockHeld(cs_wallet); // mapKeyMetadata
|
||||||
UpdateTimeFirstKey(meta.nCreateTime);
|
UpdateTimeFirstKey(meta.nCreateTime);
|
||||||
mapKeyMetadata[pubkey.GetID()] = meta;
|
mapKeyMetadata[keyID] = meta;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,15 +256,22 @@ bool CWallet::LoadCScript(const CScript& redeemScript)
|
|||||||
return CCryptoKeyStore::AddCScript(redeemScript);
|
return CCryptoKeyStore::AddCScript(redeemScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWallet::AddWatchOnly(const CScript &dest)
|
bool CWallet::AddWatchOnly(const CScript& dest)
|
||||||
{
|
{
|
||||||
if (!CCryptoKeyStore::AddWatchOnly(dest))
|
if (!CCryptoKeyStore::AddWatchOnly(dest))
|
||||||
return false;
|
return false;
|
||||||
nTimeFirstKey = 1; // No birthday information for watch-only keys.
|
const CKeyMetadata& meta = mapKeyMetadata[CScriptID(dest)];
|
||||||
|
UpdateTimeFirstKey(meta.nCreateTime);
|
||||||
NotifyWatchonlyChanged(true);
|
NotifyWatchonlyChanged(true);
|
||||||
if (!fFileBacked)
|
if (!fFileBacked)
|
||||||
return true;
|
return true;
|
||||||
return CWalletDB(strWalletFile).WriteWatchOnly(dest);
|
return CWalletDB(strWalletFile).WriteWatchOnly(dest, meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime)
|
||||||
|
{
|
||||||
|
mapKeyMetadata[CScriptID(dest)].nCreateTime = nCreateTime;
|
||||||
|
return AddWatchOnly(dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWallet::RemoveWatchOnly(const CScript &dest)
|
bool CWallet::RemoveWatchOnly(const CScript &dest)
|
||||||
@ -3425,14 +3432,16 @@ public:
|
|||||||
void operator()(const CNoDestination &none) {}
|
void operator()(const CNoDestination &none) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const {
|
void CWallet::GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const {
|
||||||
AssertLockHeld(cs_wallet); // mapKeyMetadata
|
AssertLockHeld(cs_wallet); // mapKeyMetadata
|
||||||
mapKeyBirth.clear();
|
mapKeyBirth.clear();
|
||||||
|
|
||||||
// get birth times for keys with metadata
|
// get birth times for keys with metadata
|
||||||
for (std::map<CKeyID, CKeyMetadata>::const_iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++)
|
for (const auto& entry : mapKeyMetadata) {
|
||||||
if (it->second.nCreateTime)
|
if (entry.second.nCreateTime) {
|
||||||
mapKeyBirth[it->first] = it->second.nCreateTime;
|
mapKeyBirth[entry.first] = entry.second.nCreateTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// map in which we'll infer heights of other keys
|
// map in which we'll infer heights of other keys
|
||||||
CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganized; use a 144-block safety margin
|
CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganized; use a 144-block safety margin
|
||||||
|
@ -614,6 +614,17 @@ private:
|
|||||||
|
|
||||||
int64_t nTimeFirstKey;
|
int64_t nTimeFirstKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private version of AddWatchOnly method which does not accept a
|
||||||
|
* timestamp, and which will reset the wallet's nTimeFirstKey value to 1 if
|
||||||
|
* the watch key did not previously have a timestamp associated with it.
|
||||||
|
* Because this is an inherited virtual method, it is accessible despite
|
||||||
|
* being marked private, but it is marked private anyway to encourage use
|
||||||
|
* of the other AddWatchOnly which accepts a timestamp and sets
|
||||||
|
* nTimeFirstKey more intelligently for more efficient rescans.
|
||||||
|
*/
|
||||||
|
bool AddWatchOnly(const CScript& dest) override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/*
|
/*
|
||||||
* Main wallet lock.
|
* Main wallet lock.
|
||||||
@ -638,7 +649,9 @@ public:
|
|||||||
mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
|
mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<CKeyID, CKeyMetadata> mapKeyMetadata;
|
// Map from Key ID (for regular keys) or Script ID (for watch-only keys) to
|
||||||
|
// key metadata.
|
||||||
|
std::map<CTxDestination, CKeyMetadata> mapKeyMetadata;
|
||||||
|
|
||||||
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
|
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
|
||||||
MasterKeyMap mapMasterKeys;
|
MasterKeyMap mapMasterKeys;
|
||||||
@ -728,7 +741,7 @@ public:
|
|||||||
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
|
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
|
||||||
bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); }
|
bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); }
|
||||||
//! Load metadata (used by LoadWallet)
|
//! Load metadata (used by LoadWallet)
|
||||||
bool LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &metadata);
|
bool LoadKeyMetadata(const CTxDestination& pubKey, const CKeyMetadata &metadata);
|
||||||
|
|
||||||
bool LoadMinVersion(int nVersion) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; }
|
bool LoadMinVersion(int nVersion) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; }
|
||||||
void UpdateTimeFirstKey(int64_t nCreateTime);
|
void UpdateTimeFirstKey(int64_t nCreateTime);
|
||||||
@ -750,7 +763,7 @@ public:
|
|||||||
bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const;
|
bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const;
|
||||||
|
|
||||||
//! Adds a watch-only address to the store, and saves it to disk.
|
//! Adds a watch-only address to the store, and saves it to disk.
|
||||||
bool AddWatchOnly(const CScript &dest);
|
bool AddWatchOnly(const CScript& dest, int64_t nCreateTime);
|
||||||
bool RemoveWatchOnly(const CScript &dest);
|
bool RemoveWatchOnly(const CScript &dest);
|
||||||
//! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
|
//! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
|
||||||
bool LoadWatchOnly(const CScript &dest);
|
bool LoadWatchOnly(const CScript &dest);
|
||||||
@ -759,7 +772,7 @@ public:
|
|||||||
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
|
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
|
||||||
bool EncryptWallet(const SecureString& strWalletPassphrase);
|
bool EncryptWallet(const SecureString& strWalletPassphrase);
|
||||||
|
|
||||||
void GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const;
|
void GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increment the next transaction order id
|
* Increment the next transaction order id
|
||||||
|
@ -120,15 +120,19 @@ bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
|
|||||||
return Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false);
|
return Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWalletDB::WriteWatchOnly(const CScript &dest)
|
bool CWalletDB::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta)
|
||||||
{
|
{
|
||||||
nWalletDBUpdateCounter++;
|
nWalletDBUpdateCounter++;
|
||||||
|
if (!Write(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)), keyMeta))
|
||||||
|
return false;
|
||||||
return Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1');
|
return Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1');
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWalletDB::EraseWatchOnly(const CScript &dest)
|
bool CWalletDB::EraseWatchOnly(const CScript &dest)
|
||||||
{
|
{
|
||||||
nWalletDBUpdateCounter++;
|
nWalletDBUpdateCounter++;
|
||||||
|
if (!Erase(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest))))
|
||||||
|
return false;
|
||||||
return Erase(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)));
|
return Erase(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,6 +263,7 @@ class CWalletScanState {
|
|||||||
public:
|
public:
|
||||||
unsigned int nKeys;
|
unsigned int nKeys;
|
||||||
unsigned int nCKeys;
|
unsigned int nCKeys;
|
||||||
|
unsigned int nWatchKeys;
|
||||||
unsigned int nKeyMeta;
|
unsigned int nKeyMeta;
|
||||||
bool fIsEncrypted;
|
bool fIsEncrypted;
|
||||||
bool fAnyUnordered;
|
bool fAnyUnordered;
|
||||||
@ -266,7 +271,7 @@ public:
|
|||||||
vector<uint256> vWalletUpgrade;
|
vector<uint256> vWalletUpgrade;
|
||||||
|
|
||||||
CWalletScanState() {
|
CWalletScanState() {
|
||||||
nKeys = nCKeys = nKeyMeta = 0;
|
nKeys = nCKeys = nWatchKeys = nKeyMeta = 0;
|
||||||
fIsEncrypted = false;
|
fIsEncrypted = false;
|
||||||
fAnyUnordered = false;
|
fAnyUnordered = false;
|
||||||
nFileVersion = 0;
|
nFileVersion = 0;
|
||||||
@ -348,16 +353,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
|||||||
}
|
}
|
||||||
else if (strType == "watchs")
|
else if (strType == "watchs")
|
||||||
{
|
{
|
||||||
|
wss.nWatchKeys++;
|
||||||
CScript script;
|
CScript script;
|
||||||
ssKey >> *(CScriptBase*)(&script);
|
ssKey >> *(CScriptBase*)(&script);
|
||||||
char fYes;
|
char fYes;
|
||||||
ssValue >> fYes;
|
ssValue >> fYes;
|
||||||
if (fYes == '1')
|
if (fYes == '1')
|
||||||
pwallet->LoadWatchOnly(script);
|
pwallet->LoadWatchOnly(script);
|
||||||
|
|
||||||
// Watch-only addresses have no birthday information for now,
|
|
||||||
// so set the wallet birthday to the beginning of time.
|
|
||||||
pwallet->UpdateTimeFirstKey(1);
|
|
||||||
}
|
}
|
||||||
else if (strType == "key" || strType == "wkey")
|
else if (strType == "key" || strType == "wkey")
|
||||||
{
|
{
|
||||||
@ -458,15 +460,27 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
|||||||
}
|
}
|
||||||
wss.fIsEncrypted = true;
|
wss.fIsEncrypted = true;
|
||||||
}
|
}
|
||||||
else if (strType == "keymeta")
|
else if (strType == "keymeta" || strType == "watchmeta")
|
||||||
{
|
{
|
||||||
CPubKey vchPubKey;
|
CTxDestination keyID;
|
||||||
ssKey >> vchPubKey;
|
if (strType == "keymeta")
|
||||||
|
{
|
||||||
|
CPubKey vchPubKey;
|
||||||
|
ssKey >> vchPubKey;
|
||||||
|
keyID = vchPubKey.GetID();
|
||||||
|
}
|
||||||
|
else if (strType == "watchmeta")
|
||||||
|
{
|
||||||
|
CScript script;
|
||||||
|
ssKey >> *(CScriptBase*)(&script);
|
||||||
|
keyID = CScriptID(script);
|
||||||
|
}
|
||||||
|
|
||||||
CKeyMetadata keyMeta;
|
CKeyMetadata keyMeta;
|
||||||
ssValue >> keyMeta;
|
ssValue >> keyMeta;
|
||||||
wss.nKeyMeta++;
|
wss.nKeyMeta++;
|
||||||
|
|
||||||
pwallet->LoadKeyMetadata(vchPubKey, keyMeta);
|
pwallet->LoadKeyMetadata(keyID, keyMeta);
|
||||||
}
|
}
|
||||||
else if (strType == "defaultkey")
|
else if (strType == "defaultkey")
|
||||||
{
|
{
|
||||||
@ -620,7 +634,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
|
|||||||
wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys);
|
wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys);
|
||||||
|
|
||||||
// nTimeFirstKey is only reliable if all keys have metadata
|
// nTimeFirstKey is only reliable if all keys have metadata
|
||||||
if ((wss.nKeys + wss.nCKeys) != wss.nKeyMeta)
|
if ((wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta)
|
||||||
pwallet->UpdateTimeFirstKey(1);
|
pwallet->UpdateTimeFirstKey(1);
|
||||||
|
|
||||||
BOOST_FOREACH(uint256 hash, wss.vWalletUpgrade)
|
BOOST_FOREACH(uint256 hash, wss.vWalletUpgrade)
|
||||||
|
@ -135,7 +135,7 @@ public:
|
|||||||
|
|
||||||
bool WriteCScript(const uint160& hash, const CScript& redeemScript);
|
bool WriteCScript(const uint160& hash, const CScript& redeemScript);
|
||||||
|
|
||||||
bool WriteWatchOnly(const CScript &script);
|
bool WriteWatchOnly(const CScript &script, const CKeyMetadata &keymeta);
|
||||||
bool EraseWatchOnly(const CScript &script);
|
bool EraseWatchOnly(const CScript &script);
|
||||||
|
|
||||||
bool WriteBestBlock(const CBlockLocator& locator);
|
bool WriteBestBlock(const CBlockLocator& locator);
|
||||||
|
Loading…
Reference in New Issue
Block a user