Merge pull request #974 from sipa/walletupgrade
authorPieter Wuille <pieter.wuille@gmail.com>
Mon, 26 Mar 2012 02:44:52 +0000 (19:44 -0700)
committerPieter Wuille <pieter.wuille@gmail.com>
Mon, 26 Mar 2012 02:44:52 +0000 (19:44 -0700)
Introduce explicit -walletupgrade option

src/bitcoinrpc.cpp
src/init.cpp
src/wallet.cpp
src/wallet.h

index 874d19f..d8b9782 100644 (file)
@@ -336,6 +336,7 @@ Value getinfo(const Array& params, bool fHelp)
     Object obj;
     obj.push_back(Pair("version",       (int)CLIENT_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("blocks",        (int)nBestHeight));
     obj.push_back(Pair("connections",   (int)vNodes.size()));
index 603022e..4078b7e 100644 (file)
@@ -222,6 +222,7 @@ bool AppInit2(int argc, char* argv[])
             "  -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" +
             "  -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" +
             "  -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" +
@@ -380,6 +381,36 @@ bool AppInit2(int argc, char* argv[])
         else
             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(" wallet      %15"PRI64d"ms\n", GetTimeMillis() - nStart);
 
index 30590c8..27ed722 100644 (file)
@@ -17,7 +17,7 @@ using namespace std;
 
 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();
     CKey key;
@@ -25,7 +25,7 @@ std::vector<unsigned char> CWallet::GenerateNewKey()
 
     // Compressed public keys were introduced in version 0.6.0
     if (fCompressed)
-        SetMinVersion(59900);
+        SetMinVersion(FEATURE_COMPRPUBKEY);
 
     if (!AddKey(key))
         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)
         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;
 
+    if (nVersion > nWalletMaxVersion)
+        nWalletMaxVersion = nVersion;
+
     if (fFileBacked)
     {
         CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile);
@@ -174,6 +181,17 @@ bool CWallet::SetMinVersion(int nVersion, CWalletDB* pwalletdbIn)
     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)
 {
     if (IsCrypted())
@@ -228,7 +246,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
         }
 
         // Encryption was introduced in version 0.4.0
-        SetMinVersion(40000, pwalletdbEncryption);
+        SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true);
 
         if (fFileBacked)
         {
@@ -1253,19 +1271,6 @@ int CWallet::LoadWallet(bool& fFirstRunRet)
         return nLoadWalletRet;
     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);
     return DB_LOAD_OK;
 }
index fea3297..6a6ee0c 100644 (file)
@@ -14,6 +14,16 @@ class CWalletTx;
 class CReserveKey;
 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
 // transactions and balances, and provides the ability to create new
 // transactions
@@ -25,8 +35,12 @@ private:
 
     CWalletDB *pwalletdbEncryption;
 
+    // the current wallet version: clients below this version are not able to load the wallet
     int nWalletVersion;
 
+    // the maxmimum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded
+    int nWalletMaxVersion;
+
 public:
     mutable CCriticalSection cs_wallet;
 
@@ -42,14 +56,16 @@ public:
 
     CWallet()
     {
-        nWalletVersion = 0;
+        nWalletVersion = FEATURE_BASE;
+        nWalletMaxVersion = FEATURE_BASE;
         fFileBacked = false;
         nMasterKeyMaxID = 0;
         pwalletdbEncryption = NULL;
     }
     CWallet(std::string strWalletFileIn)
     {
-        nWalletVersion = 0;
+        nWalletVersion = FEATURE_BASE;
+        nWalletMaxVersion = FEATURE_BASE;
         strWalletFile = strWalletFileIn;
         fFileBacked = true;
         nMasterKeyMaxID = 0;
@@ -65,6 +81,9 @@ public:
 
     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
     // Generate a new key
     std::vector<unsigned char> GenerateNewKey();
@@ -73,12 +92,12 @@ public:
     // Adds a key to the store, without saving it to disk (used by LoadWallet)
     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.
     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)
-    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 LoadCScript(const CScript& redeemScript) { return CCryptoKeyStore::AddCScript(redeemScript); }
 
@@ -216,7 +235,14 @@ public:
 
     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; }
 };