-
Notifications
You must be signed in to change notification settings - Fork 37.7k
Use importmulti timestamp when importing watch only keys (on top of #9682) #9108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The fundrawtransaction test failed on two linux builds, same error for both:
|
6ec339c
to
6a52c4d
Compare
Fixed fundrawtransaction test, and added importmulti tests. |
Concept ACK. |
utACK. |
src/wallet/wallet.cpp
Outdated
@@ -222,6 +219,17 @@ bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigne | |||
return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); | |||
} | |||
|
|||
void CWallet::UpdateTimeFirstKey(int nCreateTime) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't this be int64_t nCreateTime
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, fixed in 6891d5e.
src/wallet/walletdb.cpp
Outdated
{ | ||
CPubKey vchPubKey; | ||
ssKey >> vchPubKey; | ||
uint160 keyId; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason for not using CKeyID
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again this can hold CScriptID values, and is changed to CTxDestination in 777b3db.
src/wallet/wallet.cpp
Outdated
@@ -3247,7 +3262,7 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const { | |||
mapKeyBirth.clear(); | |||
|
|||
// get birth times for keys with metadata | |||
for (std::map<CKeyID, CKeyMetadata>::const_iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++) | |||
for (std::map<uint160, CKeyMetadata>::const_iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see the reason for changing this from CKeyID
to uint160
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed from uint160 to CTxDestination in 777b3db. The map isn't keyed by CKeyID anymore because it can also contain CScriptID entries for watch only keys.
utACK 6891d5e |
Concept ACK |
src/rpc/misc.cpp
Outdated
{ | ||
ret.push_back(Pair("hdkeypath", pwalletMain->mapKeyMetadata[keyID].hdKeypath)); | ||
ret.push_back(Pair("hdmasterkeyid", pwalletMain->mapKeyMetadata[keyID].hdMasterKeyID.GetHex())); | ||
auto it = pwalletMain->mapKeyMetadata.find(CScriptID(scriptPubKey)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't you try address.GetKeyID(keyID) first before you give up and use the CScriptID? Otherwise you end up using the P2SH of the P2PH.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed lookup order in 1ea9543. I didn't understand what you mean by P2SH of the P2PH, but I think it does make sense to prefer regular key metadata over watchonly key metadata if both exist.
@@ -251,11 +259,18 @@ bool CWallet::AddWatchOnly(const CScript &dest) | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd significantly prefer to not expose this version, making it explicit to callers that the key's nCreateTime is being set to 0 (which, after this PR, implies the wallet's nTimeFirstKey is set to 1).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The second two calls to AddWatchOnly in processImport probably could take the timestamp argument.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Grr, meant for this to be on AddWatchOnly at line https://github.com/bitcoin/bitcoin/pull/9108/files#diff-b2bb174788c7409b671c46ccc86034bdL251
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, added the two missing timestamp arguments in f87008a, and expanded the test to verify the fix.
I very much agree that it's not good to expose the AddWatchOnly version not taking a timestamp, so I made it private in 1b99696. I also experimented with getting rid of the overload by adding the timestamp to AddWatchOnly methods in the keystore superclasses, but this was a bigger change and made the keystore and walletdb code more confusing, so I went with this simpler fix.
src/wallet/walletdb.cpp
Outdated
return Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1'); | ||
} | ||
|
||
bool CWalletDB::EraseWatchOnly(const CScript &dest) | ||
{ | ||
nWalletDBUpdated++; | ||
if (!Erase(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need to ignore this error - otherwise if a watchonly has been added in a previous version we will fail to erase here (as it might have no watchmeta)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having no watchmeta shouldn't be a problem. Erase still returns true if the key is not found. It just false if there is a database error.
src/wallet/walletdb.cpp
Outdated
{ | ||
nWalletDBUpdated++; | ||
if (!Write(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)), | ||
keyMeta, false)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe do overwrite here? Seems strange that we update in-wallet-state without updating on-disk state (and its probably a supported use-case to re-add something to wallet to set its date differently).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enabled overwrite in 50474f1. This was just a mistake copying the line of code from taken from CWalletDB::WriteKey.
src/wallet/walletdb.cpp
Outdated
@@ -463,11 +463,6 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, | |||
wss.nKeyMeta++; | |||
|
|||
pwallet->LoadKeyMetadata(vchPubKey, keyMeta); | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing this here?
pwallet->UpdateTimeFirstKey(keyMeta.nCreateTime);
EDIT: no, LoadKeyMetadata calls it.
Change validateaddress RPC back to preferring regular key metadata instead of watch-only key metadata as suggested by Matt Corallo <git@bluematt.me> in bitcoin#9108 (comment)
Don't use fOverwrite=false option when writing watchonly key metadata. Noticed by Matt Corallo <git@bluematt.me> in bitcoin#9108 (comment)
Fix two AddWatchOnly calls that were missing timestamps noticed by Matt Corallo <git@bluematt.me> in bitcoin#9108 (comment). Also expand tests to check that these and other timestamps are recorded.
Squashed 1b99696 -> 3d6dbed (watchtime.3 -> watchtime.4) |
Latest changes conflicted (trivially) with #9607 and #9121, so rebased 3d6dbed -> b78ec3c (watchtime.4 -> watchtime.5) |
src/wallet/wallet.h
Outdated
* 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ugh, I totally missed that its an override, thanks for catching that. Can you further document here that this method implies setting the first key time to 1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added in 5dbd05d
utACK b78ec3c +/- wanting another comment, Didn't look too closely at tests. |
Expand comment as suggested by Matt Corallo <git@bluematt.me> in bitcoin#9108 (comment)
Correction of my comment above. diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 0a32259..401614c 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -922,9 +922,12 @@ UniValue ProcessImport(const UniValue& data, const int64_t timestamp)
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
}
- pwalletMain->MarkDirty();
+ if (pwalletMain->HaveWatchOnly(script)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains this address or script");
+ }
- if (!pwalletMain->HaveWatchOnly(script) && !pwalletMain->AddWatchOnly(script, timestamp)) {
+ pwalletMain->MarkDirty();
+ if (!pwalletMain->AddWatchOnly(script, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
An alternative would be to overwrite the timestamp with the new one. |
Ok, I can add logic to error on attempts to import watch only keys that have already imported. Note that your fix above is incomplete because it only covers I'll do it in a different PR though, unless you think it needs to be here, because this is longstanding behavior not really related to saving timestamps. |
Tested ACK of a58370e a80f98b on top of master (e87ce95). |
re-utACK a80f98b |
utACK a80f98b |
A new AssertLockHeld(cs_wallet) call was added in commit a58370e "Dedup nTimeFirstKey update logic" (part of PR bitcoin#9108). The lock held assertion will fail when loading prexisting wallets files from before the bitcoin#9108 merge that have watch-only keys.
A new AssertLockHeld(cs_wallet) call was added in commit a58370e "Dedup nTimeFirstKey update logic" (part of PR bitcoin#9108). The lock held assertion will fail when loading prexisting wallets files from before the bitcoin#9108 merge that have watch-only keys.
Previously if an existing watch only key was reimported with a new timestamp, the new timestamp would not be saved in the key metadata, and would not be used to update the wallet nTimeFirstKey value (which could cause rescanning to start at the wrong point and miss transactions). Issue was pointed out by Jonas Schnelli <dev@jonasschnelli.ch> in bitcoin#9108 (comment)
Previously if an existing watch only key was reimported with a new timestamp, the new timestamp would not be saved in the key metadata, and would not be used to update the wallet nTimeFirstKey value (which could cause rescanning to start at the wrong point and miss transactions). Issue was pointed out by Jonas Schnelli <dev@jonasschnelli.ch> in bitcoin#9108 (comment)
Summary: Previously if an existing watch only key was reimported with a new timestamp, the new timestamp would not be saved in the key metadata, and would not be used to update the wallet nTimeFirstKey value (which could cause rescanning to start at the wrong point and miss transactions). Issue was pointed out by Jonas Schnelli <dev@jonasschnelli.ch> in bitcoin/bitcoin#9108 (comment) Backport core's PR9818 and PR11483 Test Plan: make check ./test/functional/test_runner.py importmulti Reviewers: #bitcoin_abc, jasonbcox Reviewed By: #bitcoin_abc, jasonbcox Differential Revision: https://reviews.bitcoinabc.org/D976
…ly keys (on top of bitcoin#9682) a80f98b Use importmulti timestamp when importing watch only keys (Russell Yanofsky) a58370e Dedup nTimeFirstKey update logic (Russell Yanofsky)
A new AssertLockHeld(cs_wallet) call was added in commit a58370e "Dedup nTimeFirstKey update logic" (part of PR bitcoin#9108). The lock held assertion will fail when loading prexisting wallets files from before the bitcoin#9108 merge that have watch-only keys.
Previously if an existing watch only key was reimported with a new timestamp, the new timestamp would not be saved in the key metadata, and would not be used to update the wallet nTimeFirstKey value (which could cause rescanning to start at the wrong point and miss transactions). Issue was pointed out by Jonas Schnelli <dev@jonasschnelli.ch> in bitcoin#9108 (comment) Conflicts: qa/rpc-tests/importmulti.py
A new AssertLockHeld(cs_wallet) call was added in commit a58370e "Dedup nTimeFirstKey update logic" (part of PR bitcoin#9108). The lock held assertion will fail when loading prexisting wallets files from before the bitcoin#9108 merge that have watch-only keys.
…ly keys (on top of bitcoin#9682) a80f98b Use importmulti timestamp when importing watch only keys (Russell Yanofsky) a58370e Dedup nTimeFirstKey update logic (Russell Yanofsky)
…ly keys (on top of bitcoin#9682) a80f98b Use importmulti timestamp when importing watch only keys (Russell Yanofsky) a58370e Dedup nTimeFirstKey update logic (Russell Yanofsky)
…ly keys (on top of bitcoin#9682) a80f98b Use importmulti timestamp when importing watch only keys (Russell Yanofsky) a58370e Dedup nTimeFirstKey update logic (Russell Yanofsky)
(This is based on #9682 to avoid merge conflicts in importmulti.py. Only the two commits "Dedup nTimeFirstKey update logic" and "Use importmulti timestamp when importing watch only keys" pertain to this PR. The other three commits belong to #9682).
This fixes issue #9034.
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.