upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/db.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/db.cpp')
-rw-r--r--src/db.cpp191
1 files changed, 191 insertions, 0 deletions
diff --git a/src/db.cpp b/src/db.cpp
index fc5f180..4cc54ac 100644
--- a/src/db.cpp
+++ b/src/db.cpp
@@ -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
726static 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
734static 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
750bool 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
822bool 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}