diff --git a/src/util.cpp b/src/util.cpp index a84f59f..31134e8 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -24,6 +24,11 @@ namespace boost { #include #include #include + +#include +#include +#include +#include #include #include #include @@ -61,6 +66,7 @@ namespace boost { using namespace std; +namespace bt = boost::posix_time; map mapArgs; map > mapMultiArgs; @@ -80,6 +86,26 @@ bool fLogTimestamps = false; CMedianFilter vTimeOffsets(200,0); volatile bool fReopenDebugLog = false; +// Extended DecodeDumpTime implementation, see this page for details: +// http://stackoverflow.com/questions/3786201/parsing-of-date-time-from-string-boost +const std::locale formats[] = { + std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%dT%H:%M:%SZ")), + std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d %H:%M:%S")), + std::locale(std::locale::classic(),new bt::time_input_facet("%Y/%m/%d %H:%M:%S")), + std::locale(std::locale::classic(),new bt::time_input_facet("%d.%m.%Y %H:%M:%S")), + std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d")) +}; + +const size_t formats_n = sizeof(formats)/sizeof(formats[0]); + +std::time_t pt_to_time_t(const bt::ptime& pt) +{ + bt::ptime timet_start(boost::gregorian::date(1970,1,1)); + bt::time_duration diff = pt - timet_start; + return diff.ticks()/bt::time_duration::rep_type::ticks_per_second; +} + + // Init OpenSSL library multithreading support static CCriticalSection** ppmutexOpenSSL; void locking_callback(int mode, int i, const char* file, int line) @@ -917,6 +943,54 @@ string DecodeBase32(const string& str) return string((const char*)&vchRet[0], vchRet.size()); } +string EncodeDumpTime(int64 nTime) +{ + return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime); +} + +int64 DecodeDumpTime(const string& s) +{ + bt::ptime pt; + + for(size_t i=0; i> pt; + if(pt != bt::ptime()) break; + } + + return pt_to_time_t(pt); +} + +string EncodeDumpString(const string &str) +{ + std::stringstream ret; + BOOST_FOREACH(unsigned char c, str) { + if (c <= 32 || c >= 128 || c == '%') { + ret << '%' << HexStr(&c, &c + 1); + } else { + ret << c; + } + } + return ret.str(); +} + +string DecodeDumpString(const string &str) +{ + std::stringstream ret; + for (unsigned int pos = 0; pos < str.length(); pos++) { + unsigned char c = str[pos]; + if (c == '%' && pos+2 < str.length()) { + c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) | + ((str[pos+2]>>6)*9+((str[pos+2]-'0')&15)); + pos += 2; + } + ret << c; + } + return ret.str(); +} + bool WildcardMatch(const char* psz, const char* mask) { diff --git a/src/util.h b/src/util.h index f194969..7e3856b 100644 --- a/src/util.h +++ b/src/util.h @@ -206,6 +206,10 @@ std::vector DecodeBase32(const char* p, bool* pfInvalid = NULL); std::string DecodeBase32(const std::string& str); std::string EncodeBase32(const unsigned char* pch, size_t len); std::string EncodeBase32(const std::string& str); +std::string EncodeDumpTime(int64 nTime); +int64 DecodeDumpTime(const std::string& s); +std::string EncodeDumpString(const std::string &str); +std::string DecodeDumpString(const std::string &str); void ParseParameters(int argc, const char*const argv[]); bool WildcardMatch(const char* psz, const char* mask); bool WildcardMatch(const std::string& str, const std::string& mask); diff --git a/src/walletdb.cpp b/src/walletdb.cpp index fda8515..e79ae1d 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -6,6 +6,7 @@ #include "walletdb.h" #include "wallet.h" #include +#include using namespace std; using namespace boost; @@ -711,6 +712,170 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) return fSuccess; } +bool DumpWallet(CWallet* pwallet, const string& strDest) +{ + + if (!pwallet->fFileBacked) + return false; + while (!fShutdown) + { + // Populate maps + std::map mapKeyBirth; + std::set setKeyPool; + pwallet->GetKeyBirthTimes(mapKeyBirth); + pwallet->GetAllReserveKeys(setKeyPool); + + // sort time/key pairs + std::vector > vKeyBirth; + for (std::map::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) { + vKeyBirth.push_back(std::make_pair(it->second, it->first)); + } + mapKeyBirth.clear(); + std::sort(vKeyBirth.begin(), vKeyBirth.end()); + + // open outputfile as a stream + ofstream file; + file.open(strDest.c_str()); + if (!file.is_open()) + return false; + + // produce output + file << strprintf("# Wallet dump created by BottleCaps %s (%s)\n", CLIENT_BUILD.c_str(), CLIENT_DATE.c_str()); + file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()).c_str()); + file << strprintf("# * Best block at time of backup was %i (%s),\n", nBestHeight, hashBestChain.ToString().c_str()); + file << strprintf("# mined on %s\n", EncodeDumpTime(pindexBest->nTime).c_str()); + file << "\n"; + for (std::vector >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { + const CKeyID &keyid = it->second; + std::string strTime = EncodeDumpTime(it->first); + std::string strAddr = CBitcoinAddress(keyid).ToString(); + bool IsCompressed; + + CKey key; + if (pwallet->GetKey(keyid, key)) { + if (pwallet->mapAddressBook.count(keyid)) { + CSecret secret = key.GetSecret(IsCompressed); + file << strprintf("%s %s label=%s # addr=%s\n", + CBitcoinSecret(secret, IsCompressed).ToString().c_str(), + strTime.c_str(), + EncodeDumpString(pwallet->mapAddressBook[keyid]).c_str(), + strAddr.c_str()); + } else if (setKeyPool.count(keyid)) { + CSecret secret = key.GetSecret(IsCompressed); + file << strprintf("%s %s reserve=1 # addr=%s\n", + CBitcoinSecret(secret, IsCompressed).ToString().c_str(), + strTime.c_str(), + strAddr.c_str()); + } else { + CSecret secret = key.GetSecret(IsCompressed); + file << strprintf("%s %s change=1 # addr=%s\n", + CBitcoinSecret(secret, IsCompressed).ToString().c_str(), + strTime.c_str(), + strAddr.c_str()); + } + } + } + file << "\n"; + file << "# End of dump\n"; + file.close(); + return true; + } + return false; +} + + +bool ImportWallet(CWallet *pwallet, const string& strLocation) +{ + + if (!pwallet->fFileBacked) + return false; + while (!fShutdown) + { + // open inputfile as stream + ifstream file; + file.open(strLocation.c_str()); + if (!file.is_open()) + return false; + + int64 nTimeBegin = pindexBest->nTime; + + bool fGood = true; + + // read through input file checking and importing keys into wallet. + while (file.good()) { + std::string line; + std::getline(file, line); + if (line.empty() || line[0] == '#') + continue; + + std::vector vstr; + boost::split(vstr, line, boost::is_any_of(" ")); + if (vstr.size() < 2) + continue; + CBitcoinSecret vchSecret; + if (!vchSecret.SetString(vstr[0])) + continue; + + bool fCompressed; + CKey key; + CSecret secret = vchSecret.GetSecret(fCompressed); + key.SetSecret(secret, fCompressed); + CKeyID keyid = key.GetPubKey().GetID(); + + if (pwallet->HaveKey(keyid)) { + printf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString().c_str()); + continue; + } + int64 nTime = DecodeDumpTime(vstr[1]); + std::string strLabel; + bool fLabel = true; + for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) { + if (boost::algorithm::starts_with(vstr[nStr], "#")) + break; + if (vstr[nStr] == "change=1") + fLabel = false; + if (vstr[nStr] == "reserve=1") + fLabel = false; + if (boost::algorithm::starts_with(vstr[nStr], "label=")) { + strLabel = DecodeDumpString(vstr[nStr].substr(6)); + fLabel = true; + } + } + printf("Importing %s...\n", CBitcoinAddress(keyid).ToString().c_str()); + if (!pwallet->AddKey(key)) { + fGood = false; + continue; + } + pwallet->mapKeyMetadata[keyid].nCreateTime = nTime; + if (fLabel) + pwallet->SetAddressBookName(keyid, strLabel); + nTimeBegin = std::min(nTimeBegin, nTime); + } + file.close(); + + // rescan block chain looking for coins from new keys + CBlockIndex *pindex = pindexBest; + while (pindex && pindex->pprev && pindex->nTime > nTimeBegin - 7200) + pindex = pindex->pprev; + + if (!pwallet->nTimeFirstKey || nTimeBegin < pwallet->nTimeFirstKey) + pwallet->nTimeFirstKey = nTimeBegin; + + printf("Rescanning last %i blocks\n", pindexBest->nHeight - pindex->nHeight + 1); + pwallet->ScanForWalletTransactions(pindex); + pwallet->ReacceptWalletTransactions(); + pwallet->MarkDirty(); + + return fGood; + + } + + return false; + +} + + + bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename) { return CWalletDB::Recover(dbenv, filename, false);