Skip to content

Conversation

ryanofsky
Copy link
Contributor

@ryanofsky ryanofsky commented Nov 8, 2016

(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.

@fanquake
Copy link
Member

fanquake commented Nov 9, 2016

The fundrawtransaction test failed on two linux builds, same error for both:

.........
fundrawtransaction.py:
Initializing test directory /tmp/testz6xi8e_p/637
start_node: bitcoind started, waiting for RPC to come up
start_node: RPC succesfully started
start_node: bitcoind started, waiting for RPC to come up
start_node: RPC succesfully started
start_node: bitcoind started, waiting for RPC to come up
start_node: RPC succesfully started
start_node: bitcoind started, waiting for RPC to come up
start_node: RPC succesfully started
Mining blocks...
start_node: bitcoind started, waiting for RPC to come up
start_node: RPC succesfully started
start_node: bitcoind started, waiting for RPC to come up
start_node: RPC succesfully started
start_node: bitcoind started, waiting for RPC to come up
start_node: RPC succesfully started
start_node: bitcoind started, waiting for RPC to come up
start_node: RPC succesfully started
Stopping nodes
Cleaning up
Tests successful
stderr:
Warning: Error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect.
Pass: False, Duration: 41 s

@ryanofsky ryanofsky force-pushed the watchtime branch 2 times, most recently from 6ec339c to 6a52c4d Compare November 10, 2016 19:53
@ryanofsky
Copy link
Contributor Author

Fixed fundrawtransaction test, and added importmulti tests.

@ryanofsky ryanofsky changed the title WIP: Use importmulti timestamp when importing watch only keys Use importmulti timestamp when importing watch only keys Nov 10, 2016
@jonasschnelli
Copy link
Contributor

Concept ACK.
Plans to test this soon.

@laanwj laanwj added this to the 0.14.0 milestone Jan 19, 2017
@gmaxwell
Copy link
Contributor

utACK.

@@ -222,6 +219,17 @@ bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigne
return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret);
}

void CWallet::UpdateTimeFirstKey(int nCreateTime)
Copy link
Contributor

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.

Copy link
Contributor Author

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.

{
CPubKey vchPubKey;
ssKey >> vchPubKey;
uint160 keyId;
Copy link
Contributor

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?

Copy link
Contributor Author

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.

@@ -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++)
Copy link
Contributor

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?

Copy link
Contributor Author

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.

@morcos
Copy link
Contributor

morcos commented Jan 23, 2017

utACK 6891d5e

@jtimon
Copy link
Contributor

jtimon commented Jan 23, 2017

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));
Copy link
Contributor

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.

Copy link
Contributor Author

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)
{
Copy link
Contributor

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).

Copy link
Contributor

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

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.

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))))
Copy link
Contributor

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)?

Copy link
Contributor Author

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.

{
nWalletDBUpdated++;
if (!Write(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)),
keyMeta, false))
Copy link
Contributor

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).

Copy link
Contributor Author

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.

@@ -463,11 +463,6 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
wss.nKeyMeta++;

pwallet->LoadKeyMetadata(vchPubKey, keyMeta);

Copy link
Contributor

@jtimon jtimon Feb 1, 2017

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.

@jtimon
Copy link
Contributor

jtimon commented Feb 1, 2017

utACK individual commits: 99c1aa1 6891d5e

Can we squash already?

@ryanofsky
Copy link
Contributor Author

Squashed 6891d5e -> 89011a3, and will look into Matt's comments.

ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Feb 2, 2017
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)
ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Feb 2, 2017
Don't use fOverwrite=false option when writing watchonly key metadata. Noticed
by Matt Corallo <git@bluematt.me> in
bitcoin#9108 (comment)
ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Feb 2, 2017
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.
@ryanofsky
Copy link
Contributor Author

Squashed 1b99696 -> 3d6dbed (watchtime.3 -> watchtime.4)

@ryanofsky
Copy link
Contributor Author

Latest changes conflicted (trivially) with #9607 and #9121, so rebased 3d6dbed -> b78ec3c (watchtime.4 -> watchtime.5)

* 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.
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 5dbd05d

@TheBlueMatt
Copy link
Contributor

utACK b78ec3c +/- wanting another comment, Didn't look too closely at tests.

ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Feb 3, 2017
Expand comment as suggested by Matt Corallo <git@bluematt.me> in
bitcoin#9108 (comment)
@jonasschnelli
Copy link
Contributor

Correction of my comment above.
If you re-import a watch-only, it does not set the new timestamp but still response with "success". I think we should do this (fail when reimport a watch-only)...

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.

@ryanofsky
Copy link
Contributor Author

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 importmulti, not importaddress, importpubkey, etc.

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.

@jonasschnelli
Copy link
Contributor

Tested ACK of a58370e a80f98b on top of master (e87ce95).
I agree with @ryanofsky that the overwrite-/already-exists-behavior can be fixed later.

@morcos
Copy link
Contributor

morcos commented Feb 14, 2017

re-utACK a80f98b

@sipa
Copy link
Member

sipa commented Feb 15, 2017

utACK a80f98b

@laanwj laanwj merged commit a80f98b into bitcoin:master Feb 15, 2017
laanwj added a commit that referenced this pull request Feb 15, 2017
… (on top of #9682)

a80f98b Use importmulti timestamp when importing watch only keys (Russell Yanofsky)
a58370e Dedup nTimeFirstKey update logic (Russell Yanofsky)
ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Feb 15, 2017
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.
ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Feb 15, 2017
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.
ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Feb 21, 2017
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)
ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Mar 3, 2017
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)
deadalnix pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Jan 20, 2018
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
codablock pushed a commit to codablock/dash that referenced this pull request Feb 1, 2018
…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)
HashUnlimited pushed a commit to chaincoin/chaincoin that referenced this pull request Feb 27, 2018
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.
HashUnlimited pushed a commit to chaincoin/chaincoin that referenced this pull request Feb 28, 2018
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
lateminer pushed a commit to lateminer/bitcoin that referenced this pull request Jan 4, 2019
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.
andvgal pushed a commit to energicryptocurrency/gen2-energi that referenced this pull request Jan 6, 2019
…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)
CryptoCentric pushed a commit to absolute-community/absolute that referenced this pull request Feb 28, 2019
…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)
CryptoCentric pushed a commit to absolute-community/absolute that referenced this pull request Mar 2, 2019
…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)
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Sep 8, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants