@@ -40,5 +40,159 @@ BOOST_AUTO_TEST_CASE(CanProvide)
4040 BOOST_CHECK (keyman.CanProvide (p2sh_script, data));
4141}
4242
43+ BOOST_AUTO_TEST_CASE (Legacy_IsKeyActive)
44+ {
45+ CWallet wallet (m_node.chain .get (), " " , CreateMockableWalletDatabase ());
46+ {
47+ LOCK (wallet.cs_wallet );
48+ wallet.SetMinVersion (FEATURE_LATEST);
49+ wallet.m_keypool_size = 10 ;
50+ }
51+ LegacyScriptPubKeyMan& spkm = *wallet.GetOrCreateLegacyScriptPubKeyMan ();
52+
53+ // Start off empty
54+ BOOST_CHECK (spkm.GetScriptPubKeys ().empty ());
55+
56+ // Generate 20 keypool keys (10 internal, 10 external)
57+ {
58+ LOCK (wallet.cs_wallet );
59+ spkm.SetupGeneration ();
60+ }
61+
62+ // 4 scripts per keypool key (P2PK, P2PKH, P2WPKH, P2SH-P2WPKH)
63+ // Plus 4 scripts for the seed key
64+ auto scripts1 = spkm.GetScriptPubKeys ();
65+ BOOST_CHECK_EQUAL (scripts1.size (), 84 );
66+
67+ // All keys are active
68+ for (const CScript& script : scripts1) {
69+ BOOST_CHECK (spkm.IsKeyActive (script));
70+ }
71+
72+ // Requesting single from spkm should not deactivate key
73+ CTxDestination dest1;
74+ {
75+ LOCK (wallet.cs_wallet );
76+ auto result = spkm.GetNewDestination (OutputType::BECH32);
77+ dest1 = result.value ();
78+ }
79+ CScript script = GetScriptForDestination (dest1);
80+ BOOST_CHECK (spkm.IsKeyActive (script));
81+
82+ // Key pool size did not change
83+ auto scripts2 = spkm.GetScriptPubKeys ();
84+ BOOST_CHECK_EQUAL (scripts2.size (), 84 );
85+
86+ // Use key that is not the next key
87+ // (i.e. address gap in wallet recovery)
88+ {
89+ LOCK (wallet.cs_wallet );
90+ LOCK (spkm.cs_KeyStore );
91+ auto keys = spkm.MarkReserveKeysAsUsed (5 );
92+ BOOST_CHECK_EQUAL (keys.size (), 4 ); // Because we already used one with GetNewDestination
93+ }
94+
95+ // Key pool size did not change
96+ auto scripts3 = spkm.GetScriptPubKeys ();
97+ BOOST_CHECK_EQUAL (scripts3.size (), 84 );
98+
99+ // All keys are still active
100+ for (const CScript& script : scripts3) {
101+ BOOST_CHECK (spkm.IsKeyActive (script));
102+ }
103+
104+ // When user encrypts wallet for the first time,
105+ // all existing keys are removed from active keypool
106+ {
107+ LOCK (wallet.cs_wallet );
108+ // called by EncryptWallet()
109+ spkm.SetupGeneration (true );
110+ }
111+
112+ // 20 new keys were added
113+ auto scripts4 = spkm.GetScriptPubKeys ();
114+ BOOST_CHECK_EQUAL (scripts4.size (), 84 * 2 );
115+
116+ // All 10 original keys are now inactive
117+ for (const CScript& script : scripts3) {
118+ BOOST_CHECK (!spkm.IsKeyActive (script));
119+ }
120+ }
121+
122+ BOOST_AUTO_TEST_CASE (Descriptor_IsKeyActive)
123+ {
124+ CWallet wallet (m_node.chain .get (), " " , CreateMockableWalletDatabase ());
125+ {
126+ LOCK (wallet.cs_wallet );
127+ wallet.LoadMinVersion (FEATURE_LATEST);
128+ wallet.SetWalletFlag (WALLET_FLAG_DESCRIPTORS);
129+ wallet.m_keypool_size = 10 ;
130+ wallet.SetupDescriptorScriptPubKeyMans ();
131+ }
132+ DescriptorScriptPubKeyMan* spkm = dynamic_cast <DescriptorScriptPubKeyMan*>(wallet.GetScriptPubKeyMan (OutputType::BECH32, /* internal=*/ false ));
133+
134+ // Start off with 10 pre-generated keys, 1 script each
135+ auto scripts1 = spkm->GetScriptPubKeys ();
136+ BOOST_CHECK_EQUAL (scripts1.size (), 10 );
137+
138+ // All keys are active
139+ for (const CScript& script : scripts1) {
140+ BOOST_CHECK (spkm->IsKeyActive (script));
141+ }
142+
143+ // Requesting single key from spkm should not deactivate key
144+ auto dest1 = spkm->GetNewDestination (OutputType::BECH32);
145+ CScript script = GetScriptForDestination (dest1.value ());
146+ BOOST_CHECK (spkm->IsKeyActive (script));
147+
148+ // Key pool size did not change
149+ auto scripts2 = spkm->GetScriptPubKeys ();
150+ BOOST_CHECK_EQUAL (scripts2.size (), 10 );
151+
152+ // Use key that is not the next key
153+ // (i.e. address gap in wallet recovery)
154+ {
155+ LOCK (spkm->cs_desc_man );
156+ WalletDescriptor descriptor = spkm->GetWalletDescriptor ();
157+ FlatSigningProvider provider;
158+ std::vector<CScript> scripts3;
159+ descriptor.descriptor ->ExpandFromCache (/* pos=*/ 5 , descriptor.cache , scripts3, provider);
160+
161+ BOOST_CHECK_EQUAL (scripts3.size (), 1 );
162+ spkm->MarkUnusedAddresses (scripts3.front ());
163+ }
164+
165+ // Key pool size increased to replace used keys
166+ auto scripts4 = spkm->GetScriptPubKeys ();
167+ BOOST_CHECK_EQUAL (scripts4.size (), 16 );
168+
169+ // All keys are still active
170+ for (const CScript& script : scripts4) {
171+ BOOST_CHECK (spkm->IsKeyActive (script));
172+ }
173+
174+ // When user encrypts wallet for the first time,
175+ // all existing keys are removed from active keypool
176+ {
177+ LOCK (wallet.cs_wallet );
178+ // called by EncryptWallet()
179+ wallet.SetupDescriptorScriptPubKeyMans ();
180+ }
181+
182+ // This SPKM is not affected
183+ for (const CScript& script : scripts4) {
184+ BOOST_CHECK (spkm->IsKeyActive (script));
185+ }
186+
187+ // ...but at the wallet level all the keys from that SPKM are deactivated
188+ int num_script_keys_not_found = 0 ;
189+ for (const CScript& script : scripts4) {
190+ if (!wallet.IsDestinationActive (WitnessV0ScriptHash (script))) {
191+ ++num_script_keys_not_found;
192+ }
193+ }
194+ BOOST_CHECK_EQUAL (num_script_keys_not_found, 16 );
195+ }
196+
43197BOOST_AUTO_TEST_SUITE_END ()
44198} // namespace wallet
0 commit comments