Introduce explicit -walletupgrade option

Do not automatically change the wallet format unless the user takes an
explicit action that implies an upgrade (encrypting, for now), or uses
-walletupgrade.

-walletupgrade optionally takes an integer argument: the client version
up to which upgrading is allowed. Without an argument, it is upgraded
to latest supported version. If an argument to -walletupgrade is
provided at the time the wallet is created, the new wallet will initially
not use features beyond that version.

Third, the current wallet version number is reported in getinfo.
This commit is contained in:
Pieter Wuille 2012-03-22 03:56:31 +01:00
parent 4a17e3e6b9
commit 439e1497e1
4 changed files with 85 additions and 22 deletions

View File

@ -336,6 +336,7 @@ Value getinfo(const Array& params, bool fHelp)
Object obj; Object obj;
obj.push_back(Pair("version", (int)CLIENT_VERSION)); obj.push_back(Pair("version", (int)CLIENT_VERSION));
obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION)); obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
obj.push_back(Pair("blocks", (int)nBestHeight)); obj.push_back(Pair("blocks", (int)nBestHeight));
obj.push_back(Pair("connections", (int)vNodes.size())); obj.push_back(Pair("connections", (int)vNodes.size()));

View File

@ -222,6 +222,7 @@ bool AppInit2(int argc, char* argv[])
" -rpcallowip=<ip> \t\t " + _("Allow JSON-RPC connections from specified IP address") + "\n" + " -rpcallowip=<ip> \t\t " + _("Allow JSON-RPC connections from specified IP address") + "\n" +
" -rpcconnect=<ip> \t " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n" + " -rpcconnect=<ip> \t " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n" +
" -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" + " -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" +
" -upgradewallet \t " + _("Upgrade wallet to latest format") + "\n" +
" -keypool=<n> \t " + _("Set key pool size to <n> (default: 100)") + "\n" + " -keypool=<n> \t " + _("Set key pool size to <n> (default: 100)") + "\n" +
" -rescan \t " + _("Rescan the block chain for missing wallet transactions") + "\n" + " -rescan \t " + _("Rescan the block chain for missing wallet transactions") + "\n" +
" -checkblocks=<n> \t\t " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" + " -checkblocks=<n> \t\t " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" +
@ -380,6 +381,36 @@ bool AppInit2(int argc, char* argv[])
else else
strErrors << _("Error loading wallet.dat") << "\n"; strErrors << _("Error loading wallet.dat") << "\n";
} }
if (GetBoolArg("-upgradewallet", fFirstRun))
{
int nMaxVersion = GetArg("-upgradewallet", 0);
if (nMaxVersion == 0) // the -walletupgrade without argument case
{
printf("Performing wallet upgrade to %i\n", FEATURE_LATEST);
nMaxVersion = CLIENT_VERSION;
pwalletMain->SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately
}
else
printf("Allowing wallet upgrade up to %i\n", nMaxVersion);
if (nMaxVersion < pwalletMain->GetVersion())
strErrors << _("Cannot downgrade wallet") << "\n";
pwalletMain->SetMaxVersion(nMaxVersion);
}
if (fFirstRun)
{
// Create new keyUser and set as default key
RandAddSeedPerfmon();
std::vector<unsigned char> newDefaultKey;
if (!pwalletMain->GetKeyFromPool(newDefaultKey, false))
strErrors << _("Cannot initialize keypool") << "\n";
pwalletMain->SetDefaultKey(newDefaultKey);
if (!pwalletMain->SetAddressBookName(CBitcoinAddress(pwalletMain->vchDefaultKey), ""))
strErrors << _("Cannot write default address") << "\n";
}
printf("%s", strErrors.str().c_str()); printf("%s", strErrors.str().c_str());
printf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart); printf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart);

View File

@ -17,7 +17,7 @@ using namespace std;
std::vector<unsigned char> CWallet::GenerateNewKey() std::vector<unsigned char> CWallet::GenerateNewKey()
{ {
bool fCompressed = true; // default to compressed public keys bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
RandAddSeedPerfmon(); RandAddSeedPerfmon();
CKey key; CKey key;
@ -25,7 +25,7 @@ std::vector<unsigned char> CWallet::GenerateNewKey()
// Compressed public keys were introduced in version 0.6.0 // Compressed public keys were introduced in version 0.6.0
if (fCompressed) if (fCompressed)
SetMinVersion(59900); SetMinVersion(FEATURE_COMPRPUBKEY);
if (!AddKey(key)) if (!AddKey(key))
throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed"); throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed");
@ -148,13 +148,20 @@ public:
) )
}; };
bool CWallet::SetMinVersion(int nVersion, CWalletDB* pwalletdbIn) bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit)
{ {
if (nWalletVersion >= nVersion) if (nWalletVersion >= nVersion)
return true; return true;
// when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way
if (fExplicit && nVersion > nWalletMaxVersion)
nVersion = FEATURE_LATEST;
nWalletVersion = nVersion; nWalletVersion = nVersion;
if (nVersion > nWalletMaxVersion)
nWalletMaxVersion = nVersion;
if (fFileBacked) if (fFileBacked)
{ {
CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile); CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile);
@ -174,6 +181,17 @@ bool CWallet::SetMinVersion(int nVersion, CWalletDB* pwalletdbIn)
return true; return true;
} }
bool CWallet::SetMaxVersion(int nVersion)
{
// cannot downgrade below current version
if (nWalletVersion > nVersion)
return false;
nWalletMaxVersion = nVersion;
return true;
}
bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
{ {
if (IsCrypted()) if (IsCrypted())
@ -228,7 +246,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
} }
// Encryption was introduced in version 0.4.0 // Encryption was introduced in version 0.4.0
SetMinVersion(40000, pwalletdbEncryption); SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true);
if (fFileBacked) if (fFileBacked)
{ {
@ -1253,19 +1271,6 @@ int CWallet::LoadWallet(bool& fFirstRunRet)
return nLoadWalletRet; return nLoadWalletRet;
fFirstRunRet = vchDefaultKey.empty(); fFirstRunRet = vchDefaultKey.empty();
if (!HaveKey(Hash160(vchDefaultKey)))
{
// Create new keyUser and set as default key
RandAddSeedPerfmon();
std::vector<unsigned char> newDefaultKey;
if (!GetKeyFromPool(newDefaultKey, false))
return DB_LOAD_FAIL;
SetDefaultKey(newDefaultKey);
if (!SetAddressBookName(CBitcoinAddress(vchDefaultKey), ""))
return DB_LOAD_FAIL;
}
CreateThread(ThreadFlushWalletDB, &strWalletFile); CreateThread(ThreadFlushWalletDB, &strWalletFile);
return DB_LOAD_OK; return DB_LOAD_OK;
} }

View File

@ -14,6 +14,16 @@ class CWalletTx;
class CReserveKey; class CReserveKey;
class CWalletDB; class CWalletDB;
enum WalletFeature
{
FEATURE_BASE = 10500, // the earliest version new wallets supports (only useful for getinfo's clientversion output)
FEATURE_WALLETCRYPT = 40000, // wallet encryption
FEATURE_COMPRPUBKEY = 60000, // compressed public keys
FEATURE_LATEST = 60000
};
// A CWallet is an extension of a keystore, which also maintains a set of // A CWallet is an extension of a keystore, which also maintains a set of
// transactions and balances, and provides the ability to create new // transactions and balances, and provides the ability to create new
// transactions // transactions
@ -25,8 +35,12 @@ private:
CWalletDB *pwalletdbEncryption; CWalletDB *pwalletdbEncryption;
// the current wallet version: clients below this version are not able to load the wallet
int nWalletVersion; int nWalletVersion;
// the maxmimum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded
int nWalletMaxVersion;
public: public:
mutable CCriticalSection cs_wallet; mutable CCriticalSection cs_wallet;
@ -42,14 +56,16 @@ public:
CWallet() CWallet()
{ {
nWalletVersion = 0; nWalletVersion = FEATURE_BASE;
nWalletMaxVersion = FEATURE_BASE;
fFileBacked = false; fFileBacked = false;
nMasterKeyMaxID = 0; nMasterKeyMaxID = 0;
pwalletdbEncryption = NULL; pwalletdbEncryption = NULL;
} }
CWallet(std::string strWalletFileIn) CWallet(std::string strWalletFileIn)
{ {
nWalletVersion = 0; nWalletVersion = FEATURE_BASE;
nWalletMaxVersion = FEATURE_BASE;
strWalletFile = strWalletFileIn; strWalletFile = strWalletFileIn;
fFileBacked = true; fFileBacked = true;
nMasterKeyMaxID = 0; nMasterKeyMaxID = 0;
@ -65,6 +81,9 @@ public:
std::vector<unsigned char> vchDefaultKey; std::vector<unsigned char> vchDefaultKey;
// check whether we are allowed to upgrade (or already support) to the named feature
bool CanSupportFeature(enum WalletFeature wf) { return nWalletMaxVersion >= wf; }
// keystore implementation // keystore implementation
// Generate a new key // Generate a new key
std::vector<unsigned char> GenerateNewKey(); std::vector<unsigned char> GenerateNewKey();
@ -73,12 +92,12 @@ public:
// Adds a key to the store, without saving it to disk (used by LoadWallet) // Adds a key to the store, without saving it to disk (used by LoadWallet)
bool LoadKey(const CKey& key) { return CCryptoKeyStore::AddKey(key); } bool LoadKey(const CKey& key) { return CCryptoKeyStore::AddKey(key); }
bool LoadMinVersion(int nVersion) { nWalletVersion = nVersion; return true; } bool LoadMinVersion(int nVersion) { nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; }
// Adds an encrypted key to the store, and saves it to disk. // Adds an encrypted key to the store, and saves it to disk.
bool AddCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); bool AddCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
// Adds an encrypted key to the store, without saving it to disk (used by LoadWallet) // Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
bool LoadCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); } bool LoadCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { SetMinVersion(FEATURE_WALLETCRYPT); return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); }
bool AddCScript(const CScript& redeemScript); bool AddCScript(const CScript& redeemScript);
bool LoadCScript(const CScript& redeemScript) { return CCryptoKeyStore::AddCScript(redeemScript); } bool LoadCScript(const CScript& redeemScript) { return CCryptoKeyStore::AddCScript(redeemScript); }
@ -216,7 +235,14 @@ public:
bool SetDefaultKey(const std::vector<unsigned char> &vchPubKey); bool SetDefaultKey(const std::vector<unsigned char> &vchPubKey);
bool SetMinVersion(int nVersion, CWalletDB* pwalletdbIn = NULL); // signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower
bool SetMinVersion(enum WalletFeature, CWalletDB* pwalletdbIn = NULL, bool fExplicit = false);
// change which version we're allowed to upgrade to (note that this does not immediately imply upgrading to that format)
bool SetMaxVersion(int nVersion);
// get the current wallet format (the oldest client version guaranteed to understand this wallet)
int GetVersion() { return nWalletVersion; }
}; };