@@ -141,14 +141,61 @@ enum WalletFlags : uint64_t {
141
141
142
142
static constexpr uint64_t g_known_wallet_flags = WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET | WALLET_FLAG_KEY_ORIGIN_METADATA;
143
143
144
- /* * A key pool entry */
144
+ /* * A key from a CWallet's keypool
145
+ *
146
+ * The wallet holds one (for pre HD-split wallets) or several keypools. These
147
+ * are sets of keys that have not yet been used to provide addresses or receive
148
+ * change.
149
+ *
150
+ * The Bitcoin Core wallet was originally a collection of unrelated private
151
+ * keys with their associated addresses. If a non-HD wallet generated a
152
+ * key/address, gave that address out and then restored a backup from before
153
+ * that key's generation, then any funds sent to that address would be
154
+ * lost definitively.
155
+ *
156
+ * The keypool was implemented to avoid this scenario (commit: 10384941). The
157
+ * wallet would generate a set of keys (100 by default). When a new public key
158
+ * was required, either to give out as an address or to use in a change output,
159
+ * it would be drawn from the keypool. The keypool would then be topped up to
160
+ * maintain 100 keys. This ensured that as long as the wallet hadn't used more
161
+ * than 100 keys since the previous backup, all funds would be safe, since a
162
+ * restored wallet would be able to scan for all owned addresses.
163
+ *
164
+ * A keypool also allowed encrypted wallets to give out addresses without
165
+ * having to be decrypted to generate a new private key.
166
+ *
167
+ * With the introduction of HD wallets (commit: f1902510), the keypool
168
+ * essentially became an address look-ahead pool. Restoring old backups can no
169
+ * longer definitively lose funds as long as the addresses used were from the
170
+ * wallet's HD seed (since all private keys can be rederived from the seed).
171
+ * However, if many addresses were used since the backup, then the wallet may
172
+ * not know how far ahead in the HD chain to look for its addresses. The
173
+ * keypool is used to implement a 'gap limit'. The keypool maintains a set of
174
+ * keys (by default 1000) ahead of the last used key and scans for the
175
+ * addresses of those keys. This avoids the risk of not seeing transactions
176
+ * involving the wallet's addresses, or of re-using the same address.
177
+ *
178
+ * The HD-split wallet feature added a second keypool (commit: 02592f4c). There
179
+ * is an external keypool (for addresses to hand out) and an internal keypool
180
+ * (for change addresses).
181
+ *
182
+ * Keypool keys are stored in the wallet/keystore's keymap. The keypool data is
183
+ * stored as sets of indexes in the wallet (setInternalKeyPool,
184
+ * setExternalKeyPool and set_pre_split_keypool), and a map from the key to the
185
+ * index (m_pool_key_to_index). The CKeyPool object is used to
186
+ * serialize/deserialize the pool data to/from the database.
187
+ */
145
188
class CKeyPool
146
189
{
147
190
public:
191
+ // ! The time at which the key was generated. Set in AddKeypoolPubKeyWithDB
148
192
int64_t nTime;
193
+ // ! The public key
149
194
CPubKey vchPubKey;
150
- bool fInternal ; // for change outputs
151
- bool m_pre_split; // For keys generated before keypool split upgrade
195
+ // ! Whether this keypool entry is in the internal keypool (for change outputs)
196
+ bool fInternal ;
197
+ // ! Whether this key was generated for a keypool before the wallet was upgraded to HD-split
198
+ bool m_pre_split;
152
199
153
200
CKeyPool ();
154
201
CKeyPool (const CPubKey& vchPubKeyIn, bool internalIn);
@@ -187,6 +234,57 @@ class CKeyPool
187
234
}
188
235
};
189
236
237
+ /* * A wrapper to reserve a key from a wallet keypool
238
+ *
239
+ * CReserveKey is used to reserve a key from the keypool. It is passed around
240
+ * during the CreateTransaction/CommitTransaction procedure.
241
+ *
242
+ * Instantiating a CReserveKey does not reserve a keypool key. To do so,
243
+ * GetReservedKey() needs to be called on the object. Once a key has been
244
+ * reserved, call KeepKey() on the CReserveKey object to make sure it is not
245
+ * returned to the keypool. Call ReturnKey() to return the key to the keypool
246
+ * so it can be re-used (for example, if the key was used in a new transaction
247
+ * and that transaction was not completed and needed to be aborted).
248
+ *
249
+ * If a key is reserved and KeepKey() is not called, then the key will be
250
+ * returned to the keypool when the CReserveObject goes out of scope.
251
+ */
252
+ class CReserveKey
253
+ {
254
+ protected:
255
+ // ! The wallet to reserve the keypool key from
256
+ CWallet* pwallet;
257
+ // ! The index of the key in the keypool
258
+ int64_t nIndex{-1 };
259
+ // ! The public key
260
+ CPubKey vchPubKey;
261
+ // ! Whether this is from the internal (change output) keypool
262
+ bool fInternal {false };
263
+
264
+ public:
265
+ // ! Construct a CReserveKey object. This does NOT reserve a key from the keypool yet
266
+ explicit CReserveKey (CWallet* pwalletIn)
267
+ {
268
+ pwallet = pwalletIn;
269
+ }
270
+
271
+ CReserveKey (const CReserveKey&) = delete ;
272
+ CReserveKey& operator =(const CReserveKey&) = delete ;
273
+
274
+ // ! Destructor. If a key has been reserved and not KeepKey'ed, it will be returned to the keypool
275
+ ~CReserveKey ()
276
+ {
277
+ ReturnKey ();
278
+ }
279
+
280
+ // ! Reserve a key from the keypool
281
+ bool GetReservedKey (CPubKey &pubkey, bool internal = false );
282
+ // ! Return a key to the keypool
283
+ void ReturnKey ();
284
+ // ! Keep the key. Do not return it to the keypool when this object goes out of scope
285
+ void KeepKey ();
286
+ };
287
+
190
288
/* * Address book data */
191
289
class CAddressBookData
192
290
{
@@ -1201,34 +1299,6 @@ class CWallet final : public CCryptoKeyStore, private interfaces::Chain::Notific
1201
1299
*/
1202
1300
void MaybeResendWalletTxs ();
1203
1301
1204
- /* * A key allocated from the key pool. */
1205
- class CReserveKey
1206
- {
1207
- protected:
1208
- CWallet* pwallet;
1209
- int64_t nIndex{-1 };
1210
- CPubKey vchPubKey;
1211
- bool fInternal {false };
1212
-
1213
- public:
1214
- explicit CReserveKey (CWallet* pwalletIn)
1215
- {
1216
- pwallet = pwalletIn;
1217
- }
1218
-
1219
- CReserveKey (const CReserveKey&) = delete ;
1220
- CReserveKey& operator =(const CReserveKey&) = delete ;
1221
-
1222
- ~CReserveKey ()
1223
- {
1224
- ReturnKey ();
1225
- }
1226
-
1227
- void ReturnKey ();
1228
- bool GetReservedKey (CPubKey &pubkey, bool internal = false );
1229
- void KeepKey ();
1230
- };
1231
-
1232
1302
/* * RAII object to check and reserve a wallet rescan */
1233
1303
class WalletRescanReserver
1234
1304
{
0 commit comments