diff options
Diffstat (limited to 'src/db.cpp')
| -rw-r--r-- | src/db.cpp | 191 |
1 files changed, 191 insertions, 0 deletions
| @@ -708,3 +708,194 @@ bool BackupWallet(const string& strDest) | |||
| 708 | printf("BackupWallet() : wrote %s\n", strDest.c_str()); | 708 | printf("BackupWallet() : wrote %s\n", strDest.c_str()); |
| 709 | return true; | 709 | return true; |
| 710 | } | 710 | } |
| 711 | |||
| 712 | |||
| 713 | // ---- plain-text key export/import ------------------------------------------ | ||
| 714 | // | ||
| 715 | // The last door out. Everything else in this file assumes Berkeley DB will | ||
| 716 | // cooperate; these two do not. A wallet.dat can be unreadable for reasons that | ||
| 717 | // have nothing to do with your keys being gone -- a version this build does not | ||
| 718 | // understand, a file truncated by a bad copy, an environment that cannot be | ||
| 719 | // recovered -- and until now that meant the coins were unreachable even though | ||
| 720 | // the secrets were sitting right there. Issue #40 is one person's account of | ||
| 721 | // exactly that. | ||
| 722 | // | ||
| 723 | // The format is deliberately dull: a text file, one key per line, no database, | ||
| 724 | // no environment, no framing. Anything that can read a line can recover from it. | ||
| 725 | |||
| 726 | static int HexDigitValue(char c) | ||
| 727 | { | ||
| 728 | if (c >= '0' && c <= '9') return c - '0'; | ||
| 729 | if (c >= 'a' && c <= 'f') return c - 'a' + 10; | ||
| 730 | if (c >= 'A' && c <= 'F') return c - 'A' + 10; | ||
| 731 | return -1; | ||
| 732 | } | ||
| 733 | |||
| 734 | static bool ParseHexInto(const string& str, vector<unsigned char>& vchRet) | ||
| 735 | { | ||
| 736 | vchRet.clear(); | ||
| 737 | if (str.empty() || (str.size() % 2) != 0) | ||
| 738 | return false; | ||
| 739 | for (size_t i = 0; i < str.size(); i += 2) | ||
| 740 | { | ||
| 741 | int hi = HexDigitValue(str[i]); | ||
| 742 | int lo = HexDigitValue(str[i + 1]); | ||
| 743 | if (hi < 0 || lo < 0) | ||
| 744 | return false; | ||
| 745 | vchRet.push_back((unsigned char)((hi << 4) | lo)); | ||
| 746 | } | ||
| 747 | return true; | ||
| 748 | } | ||
| 749 | |||
| 750 | bool DumpWallet(const string& strDest) | ||
| 751 | { | ||
| 752 | if (strDest.empty()) | ||
| 753 | return error("DumpWallet() : no destination given\n"); | ||
| 754 | |||
| 755 | // Never over an existing file. The whole point is to hold the only copy of | ||
| 756 | // something irreplaceable, and a mistyped path must not eat one. | ||
| 757 | if (FileExists(strDest.c_str())) | ||
| 758 | return error("DumpWallet() : %s already exists -- refusing to overwrite\n", | ||
| 759 | strDest.c_str()); | ||
| 760 | |||
| 761 | FILE* pf = fopen(strDest.c_str(), "w"); | ||
| 762 | if (!pf) | ||
| 763 | return error("DumpWallet() : cannot write %s\n", strDest.c_str()); | ||
| 764 | #ifndef _WIN32 | ||
| 765 | // Before a single key reaches it. | ||
| 766 | chmod(strDest.c_str(), S_IRUSR | S_IWUSR); | ||
| 767 | #endif | ||
| 768 | |||
| 769 | fprintf(pf, "# Bitflash wallet key dump\n"); | ||
| 770 | fprintf(pf, "# %s\n", DateTimeStr(GetTime()).c_str()); | ||
| 771 | fprintf(pf, "#\n"); | ||
| 772 | fprintf(pf, "# THIS FILE CONTAINS PRIVATE KEYS IN PLAIN TEXT.\n"); | ||
| 773 | fprintf(pf, "# Anyone who reads it can spend these coins. Treat it as cash.\n"); | ||
| 774 | fprintf(pf, "#\n"); | ||
| 775 | fprintf(pf, "# One key per line: <private-key-hex> <address> [label]\n"); | ||
| 776 | fprintf(pf, "# The key is the DER encoding, hex; restore with -importwallet=FILE.\n"); | ||
| 777 | fprintf(pf, "#\n"); | ||
| 778 | |||
| 779 | int nKeys = 0; | ||
| 780 | bool fOk = true; | ||
| 781 | CRITICAL_BLOCK(cs_mapKeys) | ||
| 782 | { | ||
| 783 | for (map<vector<unsigned char>, CPrivKey>::iterator mi = mapKeys.begin(); | ||
| 784 | mi != mapKeys.end(); ++mi) | ||
| 785 | { | ||
| 786 | const vector<unsigned char>& vchPubKey = (*mi).first; | ||
| 787 | const CPrivKey& vchPrivKey = (*mi).second; | ||
| 788 | |||
| 789 | string strAddress = PubKeyToAddress(vchPubKey); | ||
| 790 | string strLabel; | ||
| 791 | map<string, string>::iterator mia = mapAddressBook.find(strAddress); | ||
| 792 | if (mia != mapAddressBook.end()) | ||
| 793 | strLabel = (*mia).second; | ||
| 794 | |||
| 795 | // Labels are user text and could contain a newline, which would | ||
| 796 | // forge a second entry on read-back. | ||
| 797 | for (size_t i = 0; i < strLabel.size(); i++) | ||
| 798 | if (strLabel[i] == '\n' || strLabel[i] == '\r') | ||
| 799 | strLabel[i] = ' '; | ||
| 800 | |||
| 801 | if (fprintf(pf, "%s %s %s\n", | ||
| 802 | HexStr(vchPrivKey.begin(), vchPrivKey.end(), false).c_str(), | ||
| 803 | strAddress.c_str(), | ||
| 804 | strLabel.c_str()) < 0) | ||
| 805 | { fOk = false; break; } | ||
| 806 | nKeys++; | ||
| 807 | } | ||
| 808 | } | ||
| 809 | |||
| 810 | if (fclose(pf) != 0) | ||
| 811 | fOk = false; | ||
| 812 | if (!fOk) | ||
| 813 | { | ||
| 814 | remove(strDest.c_str()); | ||
| 815 | return error("DumpWallet() : writing %s failed\n", strDest.c_str()); | ||
| 816 | } | ||
| 817 | |||
| 818 | printf("DumpWallet() : wrote %d key(s) to %s\n", nKeys, strDest.c_str()); | ||
| 819 | return true; | ||
| 820 | } | ||
| 821 | |||
| 822 | bool ImportWallet(const string& strSrc, int& nAddedRet, int& nSkippedRet) | ||
| 823 | { | ||
| 824 | nAddedRet = 0; | ||
| 825 | nSkippedRet = 0; | ||
| 826 | |||
| 827 | FILE* pf = fopen(strSrc.c_str(), "r"); | ||
| 828 | if (!pf) | ||
| 829 | return error("ImportWallet() : cannot read %s\n", strSrc.c_str()); | ||
| 830 | |||
| 831 | char buf[8192]; | ||
| 832 | int nLine = 0; | ||
| 833 | int nBad = 0; | ||
| 834 | while (fgets(buf, sizeof(buf), pf)) | ||
| 835 | { | ||
| 836 | nLine++; | ||
| 837 | string strLine = buf; | ||
| 838 | while (!strLine.empty() && (strLine[strLine.size()-1] == '\n' || | ||
| 839 | strLine[strLine.size()-1] == '\r')) | ||
| 840 | strLine.resize(strLine.size() - 1); | ||
| 841 | if (strLine.empty() || strLine[0] == '#') | ||
| 842 | continue; | ||
| 843 | |||
| 844 | string strHex = strLine.substr(0, strLine.find(' ')); | ||
| 845 | vector<unsigned char> vchPrivKey; | ||
| 846 | if (!ParseHexInto(strHex, vchPrivKey)) | ||
| 847 | { | ||
| 848 | printf("ImportWallet() : line %d is not a hex key, skipped\n", nLine); | ||
| 849 | nBad++; | ||
| 850 | continue; | ||
| 851 | } | ||
| 852 | |||
| 853 | CKey key; | ||
| 854 | // A key this build cannot load is reported and stepped over rather than | ||
| 855 | // aborting the run: one damaged line in a salvage file must not cost | ||
| 856 | // the reader every other key in it. | ||
| 857 | try | ||
| 858 | { | ||
| 859 | CPrivKey vchSecure(vchPrivKey.begin(), vchPrivKey.end()); | ||
| 860 | if (!key.SetPrivKey(vchSecure)) | ||
| 861 | { | ||
| 862 | printf("ImportWallet() : line %d is not a usable key, skipped\n", nLine); | ||
| 863 | nBad++; | ||
| 864 | continue; | ||
| 865 | } | ||
| 866 | } | ||
| 867 | catch (const std::exception& e) | ||
| 868 | { | ||
| 869 | printf("ImportWallet() : line %d rejected (%s), skipped\n", nLine, e.what()); | ||
| 870 | nBad++; | ||
| 871 | continue; | ||
| 872 | } | ||
| 873 | |||
| 874 | bool fHave = false; | ||
| 875 | CRITICAL_BLOCK(cs_mapKeys) | ||
| 876 | fHave = mapKeys.count(key.GetPubKey()) > 0; | ||
| 877 | if (fHave) | ||
| 878 | { | ||
| 879 | nSkippedRet++; // already ours; importing twice is not an error | ||
| 880 | continue; | ||
| 881 | } | ||
| 882 | |||
| 883 | if (!AddKey(key)) | ||
| 884 | { | ||
| 885 | fclose(pf); | ||
| 886 | return error("ImportWallet() : failed to store key from line %d\n", nLine); | ||
| 887 | } | ||
| 888 | nAddedRet++; | ||
| 889 | } | ||
| 890 | fclose(pf); | ||
| 891 | |||
| 892 | printf("ImportWallet() : %d added, %d already present, %d unreadable\n", | ||
| 893 | nAddedRet, nSkippedRet, nBad); | ||
| 894 | |||
| 895 | // Imported keys are only useful once their transactions are noticed, and | ||
| 896 | // that scan is a full pass over the chain the node does at startup. | ||
| 897 | if (nAddedRet > 0) | ||
| 898 | printf("ImportWallet() : restart the node so it can find transactions " | ||
| 899 | "belonging to the new keys\n"); | ||
| 900 | return true; | ||
| 901 | } | ||