Skip to content

Commit f37c64e

Browse files
committed
Implicitly know about P2WPKH redeemscripts
Make CKeyStore automatically known about the redeemscripts necessary for P2SH-P2WPKH (and due to the extra checks in IsMine, also P2WPKH) spending.
1 parent 57273f2 commit f37c64e

File tree

6 files changed

+58
-25
lines changed

6 files changed

+58
-25
lines changed

src/keystore.cpp

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,31 @@ bool CKeyStore::AddKey(const CKey &key) {
1111
return AddKeyPubKey(key, key.GetPubKey());
1212
}
1313

14+
void CBasicKeyStore::ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey)
15+
{
16+
AssertLockHeld(cs_KeyStore);
17+
CKeyID key_id = pubkey.GetID();
18+
// We must actually know about this key already.
19+
assert(HaveKey(key_id) || mapWatchKeys.count(key_id));
20+
// This adds the redeemscripts necessary to detect P2WPKH and P2SH-P2WPKH
21+
// outputs. Technically P2WPKH outputs don't have a redeemscript to be
22+
// spent. However, our current IsMine logic requires the corresponding
23+
// P2SH-P2WPKH redeemscript to be present in the wallet in order to accept
24+
// payment even to P2WPKH outputs.
25+
// Also note that having superfluous scripts in the keystore never hurts.
26+
// They're only used to guide recursion in signing and IsMine logic - if
27+
// a script is present but we can't do anything with it, it has no effect.
28+
// "Implicitly" refers to fact that scripts are derived automatically from
29+
// existing keys, and are present in memory, even without being explicitly
30+
// loaded (e.g. from a file).
31+
if (pubkey.IsCompressed()) {
32+
CScript script = GetScriptForDestination(WitnessV0KeyHash(key_id));
33+
// This does not use AddCScript, as it may be overridden.
34+
CScriptID id(script);
35+
mapScripts[id] = std::move(script);
36+
}
37+
}
38+
1439
bool CBasicKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const
1540
{
1641
CKey key;
@@ -31,6 +56,7 @@ bool CBasicKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
3156
{
3257
LOCK(cs_KeyStore);
3358
mapKeys[pubkey.GetID()] = key;
59+
ImplicitlyLearnRelatedKeyScripts(pubkey);
3460
return true;
3561
}
3662

@@ -110,8 +136,10 @@ bool CBasicKeyStore::AddWatchOnly(const CScript &dest)
110136
LOCK(cs_KeyStore);
111137
setWatchOnly.insert(dest);
112138
CPubKey pubKey;
113-
if (ExtractPubKey(dest, pubKey))
139+
if (ExtractPubKey(dest, pubKey)) {
114140
mapWatchKeys[pubKey.GetID()] = pubKey;
141+
ImplicitlyLearnRelatedKeyScripts(pubKey);
142+
}
115143
return true;
116144
}
117145

@@ -120,8 +148,11 @@ bool CBasicKeyStore::RemoveWatchOnly(const CScript &dest)
120148
LOCK(cs_KeyStore);
121149
setWatchOnly.erase(dest);
122150
CPubKey pubKey;
123-
if (ExtractPubKey(dest, pubKey))
151+
if (ExtractPubKey(dest, pubKey)) {
124152
mapWatchKeys.erase(pubKey.GetID());
153+
}
154+
// Related CScripts are not removed; having superfluous scripts around is
155+
// harmless (see comment in ImplicitlyLearnRelatedKeyScripts).
125156
return true;
126157
}
127158

src/keystore.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ class CBasicKeyStore : public CKeyStore
5959
ScriptMap mapScripts;
6060
WatchOnlySet setWatchOnly;
6161

62+
void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey);
63+
6264
public:
6365
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
6466
bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;

src/test/script_standard_tests.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -508,12 +508,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
508508
scriptPubKey.clear();
509509
scriptPubKey << OP_0 << ToByteVector(pubkeys[0].GetID());
510510

511-
// Keystore has key, but no P2SH redeemScript
512-
result = IsMine(keystore, scriptPubKey, isInvalid);
513-
BOOST_CHECK_EQUAL(result, ISMINE_NO);
514-
BOOST_CHECK(!isInvalid);
515-
516-
// Keystore has key and P2SH redeemScript
511+
// Keystore implicitly has key and P2SH redeemScript
517512
keystore.AddCScript(scriptPubKey);
518513
result = IsMine(keystore, scriptPubKey, isInvalid);
519514
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);

src/wallet/crypter.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ bool CCryptoKeyStore::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<
245245
}
246246

247247
mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret);
248+
ImplicitlyLearnRelatedKeyScripts(vchPubKey);
248249
return true;
249250
}
250251

src/wallet/rpcwallet.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1307,7 +1307,7 @@ UniValue addwitnessaddress(const JSONRPCRequest& request)
13071307
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot convert between witness address types");
13081308
}
13091309
} else {
1310-
pwallet->AddCScript(witprogram);
1310+
pwallet->AddCScript(witprogram); // Implicit for single-key now, but necessary for multisig and for compatibility with older software
13111311
pwallet->SetAddressBook(w.result, "", "receive");
13121312
}
13131313

test/functional/segwit.py

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -356,8 +356,10 @@ def run_test(self):
356356
[p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
357357
# normal P2PKH and P2PK with compressed keys should always be spendable
358358
spendable_anytime.extend([p2pkh, p2pk])
359-
# P2SH_P2PK, P2SH_P2PKH, and witness with compressed keys are spendable after direct importaddress
360-
spendable_after_importaddress.extend([p2wpkh, p2sh_p2wpkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
359+
# P2SH_P2PK, P2SH_P2PKH with compressed keys are spendable after direct importaddress
360+
spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
361+
# P2WPKH and P2SH_P2WPKH with compressed keys should always be spendable
362+
spendable_anytime.extend([p2wpkh, p2sh_p2wpkh])
361363

362364
for i in uncompressed_spendable_address:
363365
v = self.nodes[0].validateaddress(i)
@@ -373,7 +375,7 @@ def run_test(self):
373375
spendable_anytime.extend([p2pkh, p2pk])
374376
# P2SH_P2PK and P2SH_P2PKH are spendable after direct importaddress
375377
spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh])
376-
# witness with uncompressed keys are never seen
378+
# Witness output types with uncompressed keys are never seen
377379
unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
378380

379381
for i in compressed_solvable_address:
@@ -384,10 +386,10 @@ def run_test(self):
384386
solvable_after_importaddress.extend([bare, p2sh, p2wsh, p2sh_p2wsh])
385387
else:
386388
[p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
387-
# normal P2PKH and P2PK with compressed keys should always be seen
388-
solvable_anytime.extend([p2pkh, p2pk])
389-
# P2SH_P2PK, P2SH_P2PKH, and witness with compressed keys are seen after direct importaddress
390-
solvable_after_importaddress.extend([p2wpkh, p2sh_p2wpkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
389+
# normal P2PKH, P2PK, P2WPKH and P2SH_P2WPKH with compressed keys should always be seen
390+
solvable_anytime.extend([p2pkh, p2pk, p2wpkh, p2sh_p2wpkh])
391+
# P2SH_P2PK, P2SH_P2PKH with compressed keys are seen after direct importaddress
392+
solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
391393

392394
for i in uncompressed_solvable_address:
393395
v = self.nodes[0].validateaddress(i)
@@ -403,7 +405,7 @@ def run_test(self):
403405
solvable_anytime.extend([p2pkh, p2pk])
404406
# P2SH_P2PK, P2SH_P2PKH with uncompressed keys are seen after direct importaddress
405407
solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh])
406-
# witness with uncompressed keys are never seen
408+
# Witness output types with uncompressed keys are never seen
407409
unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
408410

409411
op1 = CScript([OP_1])
@@ -496,6 +498,8 @@ def run_test(self):
496498
spendable_after_addwitnessaddress = [] # These outputs should be seen after importaddress
497499
solvable_after_addwitnessaddress=[] # These outputs should be seen after importaddress but not spendable
498500
unseen_anytime = [] # These outputs should never be seen
501+
solvable_anytime = [] # These outputs should be solvable after importpubkey
502+
unseen_anytime = [] # These outputs should never be seen
499503

500504
uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]]))
501505
uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]]))
@@ -514,9 +518,8 @@ def run_test(self):
514518
premature_witaddress.append(script_to_p2sh(p2wsh))
515519
else:
516520
[p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
517-
# P2WPKH, P2SH_P2WPKH are spendable after addwitnessaddress
518-
spendable_after_addwitnessaddress.extend([p2wpkh, p2sh_p2wpkh])
519-
premature_witaddress.append(script_to_p2sh(p2wpkh))
521+
# P2WPKH, P2SH_P2WPKH are always spendable
522+
spendable_anytime.extend([p2wpkh, p2sh_p2wpkh])
520523

521524
for i in uncompressed_spendable_address + uncompressed_solvable_address:
522525
v = self.nodes[0].validateaddress(i)
@@ -538,10 +541,11 @@ def run_test(self):
538541
premature_witaddress.append(script_to_p2sh(p2wsh))
539542
else:
540543
[p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
541-
# P2SH_P2PK, P2SH_P2PKH with compressed keys are seen after addwitnessaddress
542-
solvable_after_addwitnessaddress.extend([p2wpkh, p2sh_p2wpkh])
543-
premature_witaddress.append(script_to_p2sh(p2wpkh))
544+
# P2SH_P2PK, P2SH_P2PKH with compressed keys are always solvable
545+
solvable_anytime.extend([p2wpkh, p2sh_p2wpkh])
544546

547+
self.mine_and_test_listunspent(spendable_anytime, 2)
548+
self.mine_and_test_listunspent(solvable_anytime, 1)
545549
self.mine_and_test_listunspent(spendable_after_addwitnessaddress + solvable_after_addwitnessaddress + unseen_anytime, 0)
546550

547551
# addwitnessaddress should refuse to return a witness address if an uncompressed key is used
@@ -558,8 +562,8 @@ def run_test(self):
558562
witaddress = self.nodes[0].addwitnessaddress(i)
559563
assert_equal(witaddress, self.nodes[0].addwitnessaddress(witaddress))
560564

561-
spendable_txid.append(self.mine_and_test_listunspent(spendable_after_addwitnessaddress, 2))
562-
solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress, 1))
565+
spendable_txid.append(self.mine_and_test_listunspent(spendable_after_addwitnessaddress + spendable_anytime, 2))
566+
solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress + solvable_anytime, 1))
563567
self.mine_and_test_listunspent(unseen_anytime, 0)
564568

565569
# Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works

0 commit comments

Comments
 (0)