diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 874d19f6..d8b9782e 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -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())); diff --git a/src/init.cpp b/src/init.cpp index 603022e1..4078b7e0 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -222,6 +222,7 @@ bool AppInit2(int argc, char* argv[]) " -rpcallowip= \t\t " + _("Allow JSON-RPC connections from specified IP address") + "\n" + " -rpcconnect= \t " + _("Send commands to node running on (default: 127.0.0.1)") + "\n" + " -blocknotify= " + _("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= \t " + _("Set key pool size to (default: 100)") + "\n" + " -rescan \t " + _("Rescan the block chain for missing wallet transactions") + "\n" + " -checkblocks= \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 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); diff --git a/src/wallet.cpp b/src/wallet.cpp index 30590c80..27ed7228 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -17,7 +17,7 @@ using namespace std; std::vector 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 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 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; } diff --git a/src/wallet.h b/src/wallet.h index fea32977..6a6ee0c4 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -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 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 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 &vchPubKey, const std::vector &vchCryptedSecret); // Adds an encrypted key to the store, without saving it to disk (used by LoadWallet) - bool LoadCryptedKey(const std::vector &vchPubKey, const std::vector &vchCryptedSecret) { return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); } + bool LoadCryptedKey(const std::vector &vchPubKey, const std::vector &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 &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; } };