rpcdump.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. // ECOin - Copyright (c) - 2014/2022 - GPLv3 - epsylon@riseup.net (https://03c8.net)
  2. #include <iostream>
  3. #include <fstream>
  4. #include "init.h" // for pwalletMain
  5. #include "ecoinrpc.h"
  6. #include "ui_interface.h"
  7. #include "base58.h"
  8. #include <boost/date_time/posix_time/posix_time.hpp>
  9. #include <boost/lexical_cast.hpp>
  10. #include <boost/variant/get.hpp>
  11. #include <boost/algorithm/string.hpp>
  12. #define printf OutputDebugStringF
  13. using namespace json_spirit;
  14. using namespace std;
  15. void EnsureWalletIsUnlocked();
  16. namespace bt = boost::posix_time;
  17. const std::locale formats[] = {
  18. std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%dT%H:%M:%SZ")),
  19. std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d %H:%M:%S")),
  20. std::locale(std::locale::classic(),new bt::time_input_facet("%Y/%m/%d %H:%M:%S")),
  21. std::locale(std::locale::classic(),new bt::time_input_facet("%d.%m.%Y %H:%M:%S")),
  22. std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d"))
  23. };
  24. const size_t formats_n = sizeof(formats)/sizeof(formats[0]);
  25. std::time_t pt_to_time_t(const bt::ptime& pt)
  26. {
  27. bt::ptime timet_start(boost::gregorian::date(1970,1,1));
  28. bt::time_duration diff = pt - timet_start;
  29. return diff.ticks()/bt::time_duration::rep_type::ticks_per_second;
  30. }
  31. int64 DecodeDumpTime(const std::string& s)
  32. {
  33. bt::ptime pt;
  34. for(size_t i=0; i<formats_n; ++i)
  35. {
  36. std::istringstream is(s);
  37. is.imbue(formats[i]);
  38. is >> pt;
  39. if(pt != bt::ptime()) break;
  40. }
  41. return pt_to_time_t(pt);
  42. }
  43. std::string static EncodeDumpTime(int64 nTime) {
  44. return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime);
  45. }
  46. std::string static EncodeDumpString(const std::string &str) {
  47. std::stringstream ret;
  48. BOOST_FOREACH(unsigned char c, str) {
  49. if (c <= 32 || c >= 128 || c == '%') {
  50. ret << '%' << HexStr(&c, &c + 1);
  51. } else {
  52. ret << c;
  53. }
  54. }
  55. return ret.str();
  56. }
  57. std::string DecodeDumpString(const std::string &str) {
  58. std::stringstream ret;
  59. for (unsigned int pos = 0; pos < str.length(); pos++) {
  60. unsigned char c = str[pos];
  61. if (c == '%' && pos+2 < str.length()) {
  62. c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) |
  63. ((str[pos+2]>>6)*9+((str[pos+2]-'0')&15));
  64. pos += 2;
  65. }
  66. ret << c;
  67. }
  68. return ret.str();
  69. }
  70. class CTxDump
  71. {
  72. public:
  73. CBlockIndex *pindex;
  74. int64 nValue;
  75. bool fSpent;
  76. CWalletTx* ptx;
  77. int nOut;
  78. CTxDump(CWalletTx* ptx = NULL, int nOut = -1)
  79. {
  80. pindex = NULL;
  81. nValue = 0;
  82. fSpent = false;
  83. this->ptx = ptx;
  84. this->nOut = nOut;
  85. }
  86. };
  87. Value importprivkey(const Array& params, bool fHelp)
  88. {
  89. if (fHelp || params.size() < 1 || params.size() > 2)
  90. throw runtime_error(
  91. "importprivkey <ecoinprivkey> [label]\n"
  92. "Adds a private key (as returned by dumpprivkey) to your wallet.");
  93. string strSecret = params[0].get_str();
  94. string strLabel = "";
  95. if (params.size() > 1)
  96. strLabel = params[1].get_str();
  97. CEcoinSecret vchSecret;
  98. bool fGood = vchSecret.SetString(strSecret);
  99. if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
  100. if (fWalletUnlockStakingOnly) // ecoin: no importprivkey in mint-only mode
  101. throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for minting only.");
  102. CKey key;
  103. bool fCompressed;
  104. CSecret secret = vchSecret.GetSecret(fCompressed);
  105. key.SetSecret(secret, fCompressed);
  106. CKeyID vchAddress = key.GetPubKey().GetID();
  107. {
  108. LOCK2(cs_main, pwalletMain->cs_wallet);
  109. pwalletMain->MarkDirty();
  110. pwalletMain->SetAddressBookName(vchAddress, strLabel);
  111. if (!pwalletMain->AddKey(key))
  112. throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
  113. pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true);
  114. pwalletMain->ReacceptWalletTransactions();
  115. }
  116. return Value::null;
  117. }
  118. Value importwallet(const Array& params, bool fHelp)
  119. {
  120. if (fHelp || params.size() != 1)
  121. throw runtime_error(
  122. "importwallet <filename>\n"
  123. "Imports keys from a wallet dump file (see dumpwallet).");
  124. EnsureWalletIsUnlocked();
  125. ifstream file;
  126. file.open(params[0].get_str().c_str());
  127. if (!file.is_open())
  128. throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
  129. int64 nTimeBegin = pindexBest->nTime;
  130. bool fGood = true;
  131. while (file.good()) {
  132. std::string line;
  133. std::getline(file, line);
  134. if (line.empty() || line[0] == '#')
  135. continue;
  136. std::vector<std::string> vstr;
  137. boost::split(vstr, line, boost::is_any_of(" "));
  138. if (vstr.size() < 2)
  139. continue;
  140. CEcoinSecret vchSecret;
  141. if (!vchSecret.SetString(vstr[0]))
  142. continue;
  143. bool fCompressed;
  144. CKey key;
  145. CSecret secret = vchSecret.GetSecret(fCompressed);
  146. key.SetSecret(secret, fCompressed);
  147. CKeyID keyid = key.GetPubKey().GetID();
  148. if (pwalletMain->HaveKey(keyid)) {
  149. printf("Skipping import of %s (key already present)\n", CEcoinAddress(keyid).ToString().c_str());
  150. continue;
  151. }
  152. int64 nTime = DecodeDumpTime(vstr[1]);
  153. std::string strLabel;
  154. bool fLabel = true;
  155. for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
  156. if (boost::algorithm::starts_with(vstr[nStr], "#"))
  157. break;
  158. if (vstr[nStr] == "change=1")
  159. fLabel = false;
  160. if (vstr[nStr] == "reserve=1")
  161. fLabel = false;
  162. if (boost::algorithm::starts_with(vstr[nStr], "label=")) {
  163. strLabel = DecodeDumpString(vstr[nStr].substr(6));
  164. fLabel = true;
  165. }
  166. }
  167. printf("Importing %s...\n", CEcoinAddress(keyid).ToString().c_str());
  168. if (!pwalletMain->AddKey(key)) {
  169. fGood = false;
  170. continue;
  171. }
  172. pwalletMain->mapKeyMetadata[keyid].nCreateTime = nTime;
  173. if (fLabel)
  174. pwalletMain->SetAddressBookName(keyid, strLabel);
  175. nTimeBegin = std::min(nTimeBegin, nTime);
  176. }
  177. file.close();
  178. CBlockIndex *pindex = pindexBest;
  179. while (pindex && pindex->pprev && pindex->nTime > nTimeBegin - 7200)
  180. pindex = pindex->pprev;
  181. printf("Rescanning last %i blocks\n", pindexBest->nHeight - pindex->nHeight + 1);
  182. pwalletMain->ScanForWalletTransactions(pindex);
  183. pwalletMain->ReacceptWalletTransactions();
  184. pwalletMain->MarkDirty();
  185. if (!fGood)
  186. throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet");
  187. return Value::null;
  188. }
  189. Value dumpprivkey(const Array& params, bool fHelp)
  190. {
  191. if (fHelp || params.size() != 1)
  192. throw runtime_error(
  193. "dumpprivkey <ecoinaddress>\n"
  194. "Reveals the private key corresponding to <ecoinaddress>.");
  195. EnsureWalletIsUnlocked();
  196. string strAddress = params[0].get_str();
  197. CEcoinAddress address;
  198. if (!address.SetString(strAddress))
  199. throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Ecoin address");
  200. if (fWalletUnlockStakingOnly) // ecoin: no dumpprivkey in mint-only mode
  201. throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for minting only.");
  202. CKeyID keyID;
  203. if (!address.GetKeyID(keyID))
  204. throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
  205. CSecret vchSecret;
  206. bool fCompressed;
  207. if (!pwalletMain->GetSecret(keyID, vchSecret, fCompressed))
  208. throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
  209. return CEcoinSecret(vchSecret, fCompressed).ToString();
  210. }
  211. Value dumpwallet(const Array& params, bool fHelp)
  212. {
  213. if (fHelp || params.size() != 1)
  214. throw runtime_error(
  215. "dumpwallet <filename>\n"
  216. "Dumps all wallet keys in a human-readable format.");
  217. EnsureWalletIsUnlocked();
  218. ofstream file;
  219. file.open(params[0].get_str().c_str());
  220. if (!file.is_open())
  221. throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
  222. std::map<CKeyID, int64> mapKeyBirth;
  223. std::set<CKeyID> setKeyPool;
  224. pwalletMain->GetKeyBirthTimes(mapKeyBirth);
  225. pwalletMain->GetAllReserveKeys(setKeyPool);
  226. // sort time/key pairs
  227. std::vector<std::pair<int64, CKeyID> > vKeyBirth;
  228. for (std::map<CKeyID, int64>::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) {
  229. vKeyBirth.push_back(std::make_pair(it->second, it->first));
  230. }
  231. mapKeyBirth.clear();
  232. std::sort(vKeyBirth.begin(), vKeyBirth.end());
  233. // produce output
  234. file << strprintf("# Wallet dump created by Ecoin %s (%s)\n", CLIENT_BUILD.c_str(), CLIENT_DATE.c_str());
  235. file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()).c_str());
  236. file << strprintf("# * Best block at time of backup was %i (%s),\n", nBestHeight, hashBestChain.ToString().c_str());
  237. file << strprintf("# mined on %s\n", EncodeDumpTime(pindexBest->nTime).c_str());
  238. file << "\n";
  239. for (std::vector<std::pair<int64, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
  240. const CKeyID &keyid = it->second;
  241. std::string strTime = EncodeDumpTime(it->first);
  242. std::string strAddr = CEcoinAddress(keyid).ToString();
  243. bool IsCompressed;
  244. CKey key;
  245. if (pwalletMain->GetKey(keyid, key)) {
  246. if (pwalletMain->mapAddressBook.count(keyid)) {
  247. CSecret secret = key.GetSecret(IsCompressed);
  248. file << strprintf("%s %s label=%s # addr=%s\n", CEcoinSecret(secret, IsCompressed).ToString().c_str(), strTime.c_str(), EncodeDumpString(pwalletMain->mapAddressBook[keyid]).c_str(), strAddr.c_str());
  249. } else if (setKeyPool.count(keyid)) {
  250. CSecret secret = key.GetSecret(IsCompressed);
  251. file << strprintf("%s %s reserve=1 # addr=%s\n", CEcoinSecret(secret, IsCompressed).ToString().c_str(), strTime.c_str(), strAddr.c_str());
  252. } else {
  253. CSecret secret = key.GetSecret(IsCompressed);
  254. file << strprintf("%s %s change=1 # addr=%s\n", CEcoinSecret(secret, IsCompressed).ToString().c_str(), strTime.c_str(), strAddr.c_str());
  255. }
  256. }
  257. }
  258. file << "\n";
  259. file << "# End of dump\n";
  260. file.close();
  261. return Value::null;
  262. }