Merge pull request #5531 from str4d/overhaul-legacy-transparent-keypool
Only use legacy transparent keypool for internal keys
This commit is contained in:
commit
d2b900a0c8
|
@ -40,13 +40,13 @@ class KeyPoolTest(BitcoinTestFramework):
|
||||||
bitcoind_processes[0].wait()
|
bitcoind_processes[0].wait()
|
||||||
# Restart node 0
|
# Restart node 0
|
||||||
nodes[0] = start_node(0, self.options.tmpdir)
|
nodes[0] = start_node(0, self.options.tmpdir)
|
||||||
# Keep creating keys
|
# We can't create any external addresses, which don't use the keypool.
|
||||||
addr = nodes[0].getnewaddress()
|
# We should get an error that we need to unlock the wallet.
|
||||||
try:
|
try:
|
||||||
addr = nodes[0].getnewaddress()
|
addr = nodes[0].getnewaddress()
|
||||||
raise AssertionError('Keypool should be exhausted after one address')
|
raise AssertionError('Wallet should be locked.')
|
||||||
except JSONRPCException as e:
|
except JSONRPCException as e:
|
||||||
assert(e.error['code']==-12)
|
assert_equal(e.error['code'], -13)
|
||||||
|
|
||||||
# put three new keys in the keypool
|
# put three new keys in the keypool
|
||||||
nodes[0].walletpassphrase('test', 12000)
|
nodes[0].walletpassphrase('test', 12000)
|
||||||
|
|
|
@ -96,10 +96,10 @@ class WalletBackupTest(BitcoinTestFramework):
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
|
|
||||||
# As above, this mirrors the original bash test.
|
# As above, this mirrors the original bash test.
|
||||||
def start_three(self):
|
def start_three(self, extra_args=None):
|
||||||
self.nodes[0] = start_node(0, self.options.tmpdir)
|
self.nodes[0] = start_node(0, self.options.tmpdir, extra_args)
|
||||||
self.nodes[1] = start_node(1, self.options.tmpdir)
|
self.nodes[1] = start_node(1, self.options.tmpdir, extra_args)
|
||||||
self.nodes[2] = start_node(2, self.options.tmpdir)
|
self.nodes[2] = start_node(2, self.options.tmpdir, extra_args)
|
||||||
connect_nodes(self.nodes[0], 3)
|
connect_nodes(self.nodes[0], 3)
|
||||||
connect_nodes(self.nodes[1], 3)
|
connect_nodes(self.nodes[1], 3)
|
||||||
connect_nodes(self.nodes[2], 3)
|
connect_nodes(self.nodes[2], 3)
|
||||||
|
@ -191,6 +191,29 @@ class WalletBackupTest(BitcoinTestFramework):
|
||||||
self.start_three()
|
self.start_three()
|
||||||
sync_blocks(self.nodes)
|
sync_blocks(self.nodes)
|
||||||
|
|
||||||
|
# We made extra transactions that involved addresses generated after the
|
||||||
|
# backups were taken, and external addresses do not use the keypool, so
|
||||||
|
# the balances shouldn't line up.
|
||||||
|
balance0backup = self.nodes[0].getbalance()
|
||||||
|
balance1backup = self.nodes[1].getbalance()
|
||||||
|
balance2backup = self.nodes[2].getbalance()
|
||||||
|
assert(balance0backup != balance0)
|
||||||
|
assert(balance1backup != balance1)
|
||||||
|
assert(balance2backup != balance2)
|
||||||
|
|
||||||
|
# However, because addresses are derived deterministically, we can
|
||||||
|
# recover the balances by generating the extra addresses and then
|
||||||
|
# rescanning.
|
||||||
|
for i in range(5):
|
||||||
|
self.nodes[0].getnewaddress()
|
||||||
|
self.nodes[1].getnewaddress()
|
||||||
|
self.nodes[2].getnewaddress()
|
||||||
|
|
||||||
|
logging.info("Re-starting nodes with -rescan")
|
||||||
|
self.stop_three()
|
||||||
|
self.start_three(['-rescan'])
|
||||||
|
sync_blocks(self.nodes)
|
||||||
|
|
||||||
assert_equal(self.nodes[0].getbalance(), balance0)
|
assert_equal(self.nodes[0].getbalance(), balance0)
|
||||||
assert_equal(self.nodes[1].getbalance(), balance1)
|
assert_equal(self.nodes[1].getbalance(), balance1)
|
||||||
assert_equal(self.nodes[2].getbalance(), balance2)
|
assert_equal(self.nodes[2].getbalance(), balance2)
|
||||||
|
@ -215,9 +238,9 @@ class WalletBackupTest(BitcoinTestFramework):
|
||||||
|
|
||||||
sync_blocks(self.nodes)
|
sync_blocks(self.nodes)
|
||||||
|
|
||||||
assert_equal(self.nodes[0].getbalance(), balance0)
|
assert_equal(self.nodes[0].getbalance(), balance0backup)
|
||||||
assert_equal(self.nodes[1].getbalance(), balance1)
|
assert_equal(self.nodes[1].getbalance(), balance1backup)
|
||||||
assert_equal(self.nodes[2].getbalance(), balance2)
|
assert_equal(self.nodes[2].getbalance(), balance2backup)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -182,14 +182,11 @@ UniValue getnewaddress(const UniValue& params, bool fHelp)
|
||||||
const CChainParams& chainparams = Params();
|
const CChainParams& chainparams = Params();
|
||||||
EnsureWalletIsBackedUp(chainparams);
|
EnsureWalletIsBackedUp(chainparams);
|
||||||
|
|
||||||
if (!pwalletMain->IsLocked())
|
EnsureWalletIsUnlocked();
|
||||||
pwalletMain->TopUpKeyPool();
|
|
||||||
|
|
||||||
// Generate a new key that is added to wallet
|
// Generate a new key that is added to wallet
|
||||||
std::optional<CPubKey> newKey = pwalletMain->GetKeyFromPool();
|
CPubKey newKey = pwalletMain->GenerateNewKey(true);
|
||||||
if (!newKey.has_value())
|
CKeyID keyID = newKey.GetID();
|
||||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
|
|
||||||
CKeyID keyID = newKey.value().GetID();
|
|
||||||
|
|
||||||
std::string dummy_account;
|
std::string dummy_account;
|
||||||
pwalletMain->SetAddressBook(keyID, dummy_account, "receive");
|
pwalletMain->SetAddressBook(keyID, dummy_account, "receive");
|
||||||
|
|
|
@ -271,12 +271,12 @@ CPubKey CWallet::GenerateNewKey(bool external)
|
||||||
transparent::AccountKey accountKey = this->GetLegacyAccountKey();
|
transparent::AccountKey accountKey = this->GetLegacyAccountKey();
|
||||||
std::optional<CPubKey> pubkey = std::nullopt;
|
std::optional<CPubKey> pubkey = std::nullopt;
|
||||||
do {
|
do {
|
||||||
auto index = hdChain.GetLegacyTKeyCounter();
|
auto index = hdChain.GetLegacyTKeyCounter(external);
|
||||||
auto key = external ?
|
auto key = external ?
|
||||||
accountKey.DeriveExternalSpendingKey(index) :
|
accountKey.DeriveExternalSpendingKey(index) :
|
||||||
accountKey.DeriveInternalSpendingKey(index);
|
accountKey.DeriveInternalSpendingKey(index);
|
||||||
|
|
||||||
hdChain.IncrementLegacyTKeyCounter();
|
hdChain.IncrementLegacyTKeyCounter(external);
|
||||||
if (key.has_value()) {
|
if (key.has_value()) {
|
||||||
pubkey = AddTransparentSecretKey(
|
pubkey = AddTransparentSecretKey(
|
||||||
hdChain.GetSeedFingerprint(),
|
hdChain.GetSeedFingerprint(),
|
||||||
|
@ -2279,7 +2279,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
|
||||||
|
|
||||||
Lock();
|
Lock();
|
||||||
Unlock(strWalletPassphrase);
|
Unlock(strWalletPassphrase);
|
||||||
NewKeyPool();
|
// TODO: migrate to a new mnemonic when encrypting an unencrypted wallet?
|
||||||
Lock();
|
Lock();
|
||||||
|
|
||||||
// Need to completely rewrite the wallet file; if we don't, bdb might keep
|
// Need to completely rewrite the wallet file; if we don't, bdb might keep
|
||||||
|
@ -3122,6 +3122,11 @@ void CWallet::GenerateNewSeed(Language language)
|
||||||
// as a hdchain object
|
// as a hdchain object
|
||||||
CHDChain newHdChain(seed.Fingerprint(), nCreationTime);
|
CHDChain newHdChain(seed.Fingerprint(), nCreationTime);
|
||||||
SetMnemonicHDChain(newHdChain, false);
|
SetMnemonicHDChain(newHdChain, false);
|
||||||
|
|
||||||
|
// Now that we can derive keys deterministically, clear out the legacy
|
||||||
|
// transparent keypool of all randomly-generated keys, and fill it with
|
||||||
|
// internal keys (for use as change addresses or miner outputs).
|
||||||
|
NewKeyPool();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWallet::SetMnemonicSeed(const MnemonicSeed& seed)
|
bool CWallet::SetMnemonicSeed(const MnemonicSeed& seed)
|
||||||
|
@ -5024,8 +5029,9 @@ bool CWallet::SetDefaultKey(const CPubKey &vchPubKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark old keypool keys as used,
|
* Mark old keypool keys as used, and derive new internal keys.
|
||||||
* and generate all new keys
|
*
|
||||||
|
* This is only used when first migrating to HD-derived transparent keys.
|
||||||
*/
|
*/
|
||||||
bool CWallet::NewKeyPool()
|
bool CWallet::NewKeyPool()
|
||||||
{
|
{
|
||||||
|
@ -5043,7 +5049,7 @@ bool CWallet::NewKeyPool()
|
||||||
for (int i = 0; i < nKeys; i++)
|
for (int i = 0; i < nKeys; i++)
|
||||||
{
|
{
|
||||||
int64_t nIndex = i+1;
|
int64_t nIndex = i+1;
|
||||||
walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey(true)));
|
walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey(false)));
|
||||||
setKeyPool.insert(nIndex);
|
setKeyPool.insert(nIndex);
|
||||||
}
|
}
|
||||||
LogPrintf("CWallet::NewKeyPool wrote %d new keys\n", nKeys);
|
LogPrintf("CWallet::NewKeyPool wrote %d new keys\n", nKeys);
|
||||||
|
@ -5073,7 +5079,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
|
||||||
int64_t nEnd = 1;
|
int64_t nEnd = 1;
|
||||||
if (!setKeyPool.empty())
|
if (!setKeyPool.empty())
|
||||||
nEnd = *(--setKeyPool.end()) + 1;
|
nEnd = *(--setKeyPool.end()) + 1;
|
||||||
if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey(true))))
|
if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey(false))))
|
||||||
throw runtime_error("TopUpKeyPool(): writing generated key failed");
|
throw runtime_error("TopUpKeyPool(): writing generated key failed");
|
||||||
setKeyPool.insert(nEnd);
|
setKeyPool.insert(nEnd);
|
||||||
LogPrintf("keypool added key %d, size=%u\n", nEnd, setKeyPool.size());
|
LogPrintf("keypool added key %d, size=%u\n", nEnd, setKeyPool.size());
|
||||||
|
@ -5130,23 +5136,6 @@ void CWallet::ReturnKey(int64_t nIndex)
|
||||||
LogPrintf("keypool return %d\n", nIndex);
|
LogPrintf("keypool return %d\n", nIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<CPubKey> CWallet::GetKeyFromPool()
|
|
||||||
{
|
|
||||||
int64_t nIndex = 0;
|
|
||||||
CKeyPool keypool;
|
|
||||||
{
|
|
||||||
LOCK(cs_wallet);
|
|
||||||
ReserveKeyFromKeyPool(nIndex, keypool);
|
|
||||||
if (nIndex == -1)
|
|
||||||
{
|
|
||||||
if (IsLocked()) return std::nullopt;
|
|
||||||
return GenerateNewKey(true);
|
|
||||||
}
|
|
||||||
KeepKey(nIndex);
|
|
||||||
return keypool.vchPubKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t CWallet::GetOldestKeyPoolTime()
|
int64_t CWallet::GetOldestKeyPoolTime()
|
||||||
{
|
{
|
||||||
int64_t nIndex = 0;
|
int64_t nIndex = 0;
|
||||||
|
@ -5715,9 +5704,9 @@ bool CWallet::InitLoadWallet(const CChainParams& params, bool clearWitnessCaches
|
||||||
if (fFirstRun)
|
if (fFirstRun)
|
||||||
{
|
{
|
||||||
// Create new keyUser and set as default key
|
// Create new keyUser and set as default key
|
||||||
std::optional<CPubKey> newDefaultKey = walletInstance->GetKeyFromPool();
|
if (!walletInstance->IsCrypted()) {
|
||||||
if (newDefaultKey.has_value()) {
|
CPubKey newDefaultKey = walletInstance->GenerateNewKey(true);
|
||||||
walletInstance->SetDefaultKey(newDefaultKey.value());
|
walletInstance->SetDefaultKey(newDefaultKey);
|
||||||
if (!walletInstance->SetAddressBook(walletInstance->vchDefaultKey.GetID(), "", "receive"))
|
if (!walletInstance->SetAddressBook(walletInstance->vchDefaultKey.GetID(), "", "receive"))
|
||||||
return UIError(_("Cannot write default address") += "\n");
|
return UIError(_("Cannot write default address") += "\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1531,12 +1531,13 @@ public:
|
||||||
*/
|
*/
|
||||||
static CAmount GetRequiredFee(unsigned int nTxBytes);
|
static CAmount GetRequiredFee(unsigned int nTxBytes);
|
||||||
|
|
||||||
|
private:
|
||||||
bool NewKeyPool();
|
bool NewKeyPool();
|
||||||
|
public:
|
||||||
bool TopUpKeyPool(unsigned int kpSize = 0);
|
bool TopUpKeyPool(unsigned int kpSize = 0);
|
||||||
void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool);
|
void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool);
|
||||||
void KeepKey(int64_t nIndex);
|
void KeepKey(int64_t nIndex);
|
||||||
void ReturnKey(int64_t nIndex);
|
void ReturnKey(int64_t nIndex);
|
||||||
std::optional<CPubKey> GetKeyFromPool();
|
|
||||||
int64_t GetOldestKeyPoolTime();
|
int64_t GetOldestKeyPoolTime();
|
||||||
void GetAllReserveKeys(std::set<CKeyID>& setAddress) const;
|
void GetAllReserveKeys(std::set<CKeyID>& setAddress) const;
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,8 @@ private:
|
||||||
uint256 seedFp;
|
uint256 seedFp;
|
||||||
int64_t nCreateTime; // 0 means unknown
|
int64_t nCreateTime; // 0 means unknown
|
||||||
uint32_t accountCounter;
|
uint32_t accountCounter;
|
||||||
uint32_t legacyTKeyCounter;
|
uint32_t legacyTKeyExternalCounter;
|
||||||
|
uint32_t legacyTKeyInternalCounter;
|
||||||
uint32_t legacySaplingKeyCounter;
|
uint32_t legacySaplingKeyCounter;
|
||||||
bool mnemonicSeedBackupConfirmed;
|
bool mnemonicSeedBackupConfirmed;
|
||||||
|
|
||||||
|
@ -61,7 +62,8 @@ private:
|
||||||
seedFp.SetNull();
|
seedFp.SetNull();
|
||||||
nCreateTime = 0;
|
nCreateTime = 0;
|
||||||
accountCounter = 0;
|
accountCounter = 0;
|
||||||
legacyTKeyCounter = 0;
|
legacyTKeyExternalCounter = 0;
|
||||||
|
legacyTKeyInternalCounter = 0;
|
||||||
legacySaplingKeyCounter = 0;
|
legacySaplingKeyCounter = 0;
|
||||||
mnemonicSeedBackupConfirmed = false;
|
mnemonicSeedBackupConfirmed = false;
|
||||||
}
|
}
|
||||||
|
@ -69,7 +71,7 @@ public:
|
||||||
static const int VERSION_HD_BASE = 1;
|
static const int VERSION_HD_BASE = 1;
|
||||||
static const int CURRENT_VERSION = VERSION_HD_BASE;
|
static const int CURRENT_VERSION = VERSION_HD_BASE;
|
||||||
|
|
||||||
CHDChain(uint256 seedFpIn, int64_t nCreateTimeIn): nVersion(CHDChain::CURRENT_VERSION), seedFp(seedFpIn), nCreateTime(nCreateTimeIn), accountCounter(0), legacyTKeyCounter(0), legacySaplingKeyCounter(0), mnemonicSeedBackupConfirmed(false) {}
|
CHDChain(uint256 seedFpIn, int64_t nCreateTimeIn): nVersion(CHDChain::CURRENT_VERSION), seedFp(seedFpIn), nCreateTime(nCreateTimeIn), accountCounter(0), legacyTKeyExternalCounter(0), legacyTKeyInternalCounter(0), legacySaplingKeyCounter(0), mnemonicSeedBackupConfirmed(false) {}
|
||||||
|
|
||||||
ADD_SERIALIZE_METHODS;
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
|
@ -80,7 +82,8 @@ public:
|
||||||
READWRITE(seedFp);
|
READWRITE(seedFp);
|
||||||
READWRITE(nCreateTime);
|
READWRITE(nCreateTime);
|
||||||
READWRITE(accountCounter);
|
READWRITE(accountCounter);
|
||||||
READWRITE(legacyTKeyCounter);
|
READWRITE(legacyTKeyExternalCounter);
|
||||||
|
READWRITE(legacyTKeyInternalCounter);
|
||||||
READWRITE(legacySaplingKeyCounter);
|
READWRITE(legacySaplingKeyCounter);
|
||||||
READWRITE(mnemonicSeedBackupConfirmed);
|
READWRITE(mnemonicSeedBackupConfirmed);
|
||||||
}
|
}
|
||||||
|
@ -105,12 +108,16 @@ public:
|
||||||
accountCounter += 1;
|
accountCounter += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t GetLegacyTKeyCounter() {
|
uint32_t GetLegacyTKeyCounter(bool external) {
|
||||||
return legacyTKeyCounter;
|
return external ? legacyTKeyExternalCounter : legacyTKeyInternalCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IncrementLegacyTKeyCounter() {
|
void IncrementLegacyTKeyCounter(bool external) {
|
||||||
legacyTKeyCounter += 1;
|
if (external) {
|
||||||
|
legacyTKeyExternalCounter += 1;
|
||||||
|
} else {
|
||||||
|
legacyTKeyInternalCounter += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t GetLegacySaplingKeyCounter() const {
|
uint32_t GetLegacySaplingKeyCounter() const {
|
||||||
|
|
Loading…
Reference in New Issue