main.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. /*
  2. This file is part of cpp-ethereum.
  3. cpp-ethereum is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 3 of the License, or
  6. (at your option) any later version.
  7. cpp-ethereum is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. /** @file main.cpp
  15. * @author Gav Wood <i@gavwood.com>
  16. * @date 2014
  17. * RLP tool.
  18. */
  19. #include <clocale>
  20. #include <fstream>
  21. #include <iostream>
  22. #include <boost/algorithm/string.hpp>
  23. #include <boost/filesystem.hpp>
  24. #include <json_spirit/JsonSpiritHeaders.h>
  25. #include <libdevcore/CommonIO.h>
  26. #include <libdevcore/RLP.h>
  27. #include <libdevcore/SHA3.h>
  28. #include <libdevcrypto/Common.h>
  29. #include <libdevcrypto/CryptoPP.h>
  30. using namespace std;
  31. using namespace dev;
  32. namespace js = json_spirit;
  33. void help()
  34. {
  35. cout
  36. << "Usage rlp <mode> [OPTIONS]" << endl
  37. << "Modes:" << endl
  38. << " create <json> Given a simplified JSON string, output the RLP." << endl
  39. << " render [ <file> | -- ] Render the given RLP. Options:" << endl
  40. << " --indent <string> Use string as the level indentation (default ' ')." << endl
  41. << " --hex-ints Render integers in hex." << endl
  42. << " --string-ints Render integers in the same way as strings." << endl
  43. << " --ascii-strings Render data as C-style strings or hex depending on content being ASCII." << endl
  44. << " --force-string Force all data to be rendered as C-style strings." << endl
  45. << " --force-escape When rendering as C-style strings, force all characters to be escaped." << endl
  46. << " --force-hex Force all data to be rendered as raw hex." << endl
  47. << " list [ <file> | -- ] List the items in the RLP list by hash and size." << endl
  48. << " extract [ <file> | -- ] Extract all items in the RLP list, named by hash." << endl
  49. << " assemble [ <manifest> | <base path> ] <file> ... Given a manifest & files, output the RLP." << endl
  50. << " -D,--dapp Dapp-building mode; equivalent to --encrypt --64." << endl
  51. << endl
  52. << "General options:" << endl
  53. << " -e,--encrypt Encrypt the RLP data prior to output." << endl
  54. << " -L,--lenience Try not to bomb out early if possible." << endl
  55. << " -x,--hex,--base-16 Treat input RLP as hex encoded data." << endl
  56. << " -k,--keccak Output Keccak-256 hash only." << endl
  57. << " --64,--base-64 Treat input RLP as base-64 encoded data." << endl
  58. << " -b,--bin,--base-256 Treat input RLP as raw binary data." << endl
  59. << " -q,--quiet Don't place additional information on stderr." << endl
  60. << " -h,--help Print this help message and exit." << endl
  61. << " -V,--version Show the version and exit." << endl
  62. ;
  63. exit(0);
  64. }
  65. void version()
  66. {
  67. cout << "rlp version " << dev::Version << endl;
  68. exit(0);
  69. }
  70. /*
  71. The equivalent of setlocale(LC_ALL, “C”) is called before any user code is run.
  72. If the user has an invalid environment setting then it is possible for the call
  73. to set locale to fail, so there are only two possible actions, the first is to
  74. throw a runtime exception and cause the program to quit (default behaviour),
  75. or the second is to modify the environment to something sensible (least
  76. surprising behaviour).
  77. The follow code produces the least surprising behaviour. It will use the user
  78. specified default locale if it is valid, and if not then it will modify the
  79. environment the process is running in to use a sensible default. This also means
  80. that users do not need to install language packs for their OS.
  81. */
  82. void setDefaultOrCLocale()
  83. {
  84. #if __unix__
  85. if (!std::setlocale(LC_ALL, ""))
  86. {
  87. setenv("LC_ALL", "C", 1);
  88. }
  89. #endif
  90. }
  91. enum class Mode {
  92. AssembleArchive,
  93. ListArchive,
  94. ExtractArchive,
  95. Render,
  96. Create
  97. };
  98. enum class Encoding {
  99. Auto,
  100. Hex,
  101. Base64,
  102. Binary,
  103. Keccak,
  104. };
  105. bool isAscii(string const& _s)
  106. {
  107. // Always hex-encode anything beginning with 0x to avoid ambiguity.
  108. if (_s.size() >= 2 && _s.substr(0, 2) == "0x")
  109. return false;
  110. for (char c: _s)
  111. if (c < 32)
  112. return false;
  113. return true;
  114. }
  115. class RLPStreamer
  116. {
  117. public:
  118. struct Prefs
  119. {
  120. string indent;
  121. bool hexInts = false;
  122. bool stringInts = true;
  123. bool hexPrefix = true;
  124. bool forceString = false;
  125. bool escapeAll = false;
  126. bool forceHex = true;
  127. };
  128. RLPStreamer(ostream& _out, Prefs _p): m_out(_out), m_prefs(_p) {}
  129. void output(RLP const& _d, unsigned _level = 0)
  130. {
  131. if (_d.isNull())
  132. m_out << "null";
  133. else if (_d.isInt() && !m_prefs.stringInts)
  134. if (m_prefs.hexInts)
  135. m_out << (m_prefs.hexPrefix ? "0x" : "") << toHex(toCompactBigEndian(_d.toInt<bigint>(RLP::LaissezFaire), 1), 1);
  136. else
  137. m_out << _d.toInt<bigint>(RLP::LaissezFaire);
  138. else if (_d.isData() || (_d.isInt() && m_prefs.stringInts))
  139. if (m_prefs.forceString || (!m_prefs.forceHex && isAscii(_d.toString())))
  140. m_out << escaped(_d.toString(), m_prefs.escapeAll);
  141. else
  142. m_out << "\"" << (m_prefs.hexPrefix ? "0x" : "") << toHex(_d.toBytes()) << "\"";
  143. else if (_d.isList())
  144. {
  145. m_out << "[";
  146. string newline = "\n";
  147. for (unsigned i = 0; i < _level + 1; ++i)
  148. newline += m_prefs.indent;
  149. int j = 0;
  150. for (auto i: _d)
  151. {
  152. m_out << (j++ ?
  153. (m_prefs.indent.empty() ? ", " : ("," + newline)) :
  154. (m_prefs.indent.empty() ? " " : newline));
  155. output(i, _level + 1);
  156. }
  157. newline = newline.substr(0, newline.size() - m_prefs.indent.size());
  158. m_out << (m_prefs.indent.empty() ? (j ? " ]" : "]") : (j ? newline + "]" : "]"));
  159. }
  160. }
  161. private:
  162. std::ostream& m_out;
  163. Prefs m_prefs;
  164. };
  165. void putOut(bytes _out, Encoding _encoding, bool _encrypt, bool _quiet)
  166. {
  167. dev::h256 h = dev::sha3(_out);
  168. if (_encrypt)
  169. crypto::Secp256k1PP::get()->encrypt(toPublic(Secret(h)), _out);
  170. if (!_quiet)
  171. cerr << "Keccak of RLP: " << h.hex() << endl;
  172. switch (_encoding)
  173. {
  174. case Encoding::Hex: case Encoding::Auto:
  175. cout << toHex(_out) << endl;
  176. break;
  177. case Encoding::Base64:
  178. cout << toBase64(&_out) << endl;
  179. break;
  180. case Encoding::Binary:
  181. cout.write((char const*)_out.data(), _out.size());
  182. break;
  183. case Encoding::Keccak:
  184. cout << sha3(_out).hex() << endl;
  185. break;
  186. }
  187. }
  188. int main(int argc, char** argv)
  189. {
  190. setDefaultOrCLocale();
  191. Encoding encoding = Encoding::Auto;
  192. Mode mode = Mode::Render;
  193. string inputFile;
  194. strings otherInputs;
  195. bool lenience = false;
  196. bool quiet = false;
  197. bool encrypt = false;
  198. RLPStreamer::Prefs prefs;
  199. for (int i = 1; i < argc; ++i)
  200. {
  201. string arg = argv[i];
  202. if (arg == "-h" || arg == "--help")
  203. help();
  204. else if (arg == "render")
  205. mode = Mode::Render;
  206. else if (arg == "create")
  207. mode = Mode::Create;
  208. else if ((arg == "-i" || arg == "--indent") && i + 1 < argc)
  209. prefs.indent = argv[++i];
  210. else if (arg == "--hex-ints")
  211. prefs.hexInts = true;
  212. else if (arg == "--string-ints")
  213. prefs.stringInts = true;
  214. else if (arg == "--ascii-strings")
  215. prefs.forceString = prefs.forceHex = false;
  216. else if (arg == "--force-string")
  217. prefs.forceString = true;
  218. else if (arg == "--force-hex")
  219. prefs.forceHex = true, prefs.forceString = false;
  220. else if (arg == "--force-escape")
  221. prefs.escapeAll = true;
  222. else if (arg == "-n" || arg == "--nice")
  223. prefs.forceString = true, prefs.stringInts = false, prefs.forceHex = false, prefs.indent = " ";
  224. else if (arg == "list")
  225. mode = Mode::ListArchive;
  226. else if (arg == "extract")
  227. mode = Mode::ExtractArchive;
  228. else if (arg == "assemble")
  229. mode = Mode::AssembleArchive;
  230. else if (arg == "-L" || arg == "--lenience")
  231. lenience = true;
  232. else if (arg == "-D" || arg == "--dapp")
  233. encrypt = true, encoding = Encoding::Base64;
  234. else if (arg == "-V" || arg == "--version")
  235. version();
  236. else if (arg == "-q" || arg == "--quiet")
  237. quiet = true;
  238. else if (arg == "-x" || arg == "--hex" || arg == "--base-16")
  239. encoding = Encoding::Hex;
  240. else if (arg == "-k" || arg == "--keccak")
  241. encoding = Encoding::Keccak;
  242. else if (arg == "--64" || arg == "--base-64")
  243. encoding = Encoding::Base64;
  244. else if (arg == "-b" || arg == "--bin" || arg == "--base-256")
  245. encoding = Encoding::Binary;
  246. else if (arg == "-e" || arg == "--encrypt")
  247. encrypt = true;
  248. else if (inputFile.empty())
  249. inputFile = arg;
  250. else
  251. otherInputs.push_back(arg);
  252. }
  253. bytes in;
  254. if (inputFile == "--")
  255. for (int i = cin.get(); i != -1; i = cin.get())
  256. in.push_back((byte)i);
  257. else if (boost::filesystem::is_regular_file(inputFile))
  258. in = contents(inputFile);
  259. else
  260. in = asBytes(inputFile);
  261. bytes b;
  262. if (mode != Mode::Create && mode != Mode::AssembleArchive)
  263. {
  264. if (encoding == Encoding::Auto)
  265. {
  266. encoding = Encoding::Hex;
  267. for (char b: in)
  268. if (b != '\n' && b != ' ' && b != '\t')
  269. {
  270. if (encoding == Encoding::Hex && (b < '0' || b > '9' ) && (b < 'a' || b > 'f' ) && (b < 'A' || b > 'F' ))
  271. {
  272. // cerr << "'" << b << "':" << (int)b << endl;
  273. encoding = Encoding::Base64;
  274. }
  275. if (encoding == Encoding::Base64 && (b < '0' || b > '9' ) && (b < 'a' || b > 'z' ) && (b < 'A' || b > 'Z' ) && b != '+' && b != '/')
  276. {
  277. encoding = Encoding::Binary;
  278. break;
  279. }
  280. }
  281. }
  282. switch (encoding)
  283. {
  284. case Encoding::Hex:
  285. {
  286. string s = asString(in);
  287. boost::algorithm::replace_all(s, " ", "");
  288. boost::algorithm::replace_all(s, "\n", "");
  289. boost::algorithm::replace_all(s, "\t", "");
  290. b = fromHex(s);
  291. break;
  292. }
  293. case Encoding::Base64:
  294. {
  295. string s = asString(in);
  296. boost::algorithm::replace_all(s, " ", "");
  297. boost::algorithm::replace_all(s, "\n", "");
  298. boost::algorithm::replace_all(s, "\t", "");
  299. b = fromBase64(s);
  300. break;
  301. }
  302. default:
  303. swap(b, in);
  304. break;
  305. }
  306. }
  307. try
  308. {
  309. RLP rlp(b);
  310. switch (mode)
  311. {
  312. case Mode::ListArchive:
  313. {
  314. if (!rlp.isList())
  315. {
  316. cout << "Error: Invalid format; RLP data is not a list." << endl;
  317. exit(1);
  318. }
  319. cout << rlp.itemCount() << " items:" << endl;
  320. for (auto i: rlp)
  321. {
  322. if (!i.isData())
  323. {
  324. cout << "Error: Invalid format; RLP list item is not data." << endl;
  325. if (!lenience)
  326. exit(1);
  327. }
  328. cout << " " << i.size() << " bytes: " << sha3(i.data()) << endl;
  329. }
  330. break;
  331. }
  332. case Mode::ExtractArchive:
  333. {
  334. if (!rlp.isList())
  335. {
  336. cout << "Error: Invalid format; RLP data is not a list." << endl;
  337. exit(1);
  338. }
  339. cout << rlp.itemCount() << " items:" << endl;
  340. for (auto i: rlp)
  341. {
  342. if (!i.isData())
  343. {
  344. cout << "Error: Invalid format; RLP list item is not data." << endl;
  345. if (!lenience)
  346. exit(1);
  347. }
  348. ofstream fout;
  349. fout.open(toString(sha3(i.data())));
  350. fout.write(reinterpret_cast<char const*>(i.data().data()), i.data().size());
  351. }
  352. break;
  353. }
  354. case Mode::AssembleArchive:
  355. {
  356. if (boost::filesystem::is_directory(inputFile))
  357. {
  358. js::mArray entries;
  359. auto basePath = boost::filesystem::canonical(boost::filesystem::path(inputFile)).string();
  360. for (string& i: otherInputs)
  361. {
  362. js::mObject entry;
  363. strings parsed;
  364. boost::algorithm::split(parsed, i, boost::is_any_of(","));
  365. i = parsed[0];
  366. for (unsigned j = 1; j < parsed.size(); ++j)
  367. {
  368. strings nv;
  369. boost::algorithm::split(nv, parsed[j], boost::is_any_of(":"));
  370. if (nv.size() == 2)
  371. entry[nv[0]] = nv[1];
  372. else{} // TODO: error
  373. }
  374. if (!entry.count("path"))
  375. {
  376. std::string path = boost::filesystem::canonical(boost::filesystem::path(parsed[0])).string().substr(basePath.size());
  377. if (path == "/index.html")
  378. path = "/";
  379. entry["path"] = path;
  380. }
  381. entry["hash"] = toHex(dev::sha3(contents(parsed[0])).ref());
  382. entries.push_back(entry);
  383. }
  384. js::mObject o;
  385. o["entries"] = entries;
  386. auto os = js::write_string(js::mValue(o), false);
  387. in = asBytes(os);
  388. }
  389. strings addedInputs;
  390. for (auto i: otherInputs)
  391. if (!boost::filesystem::is_regular_file(i))
  392. cerr << "Skipped " << i << std::endl;
  393. else
  394. addedInputs.push_back(i);
  395. RLPStream r(addedInputs.size() + 1);
  396. r.append(in);
  397. for (auto i: addedInputs)
  398. r.append(contents(i));
  399. putOut(r.out(), encoding, encrypt, quiet);
  400. break;
  401. }
  402. case Mode::Render:
  403. {
  404. RLPStreamer s(cout, prefs);
  405. s.output(rlp);
  406. cout << endl;
  407. break;
  408. }
  409. case Mode::Create:
  410. {
  411. vector<js::mValue> v(1);
  412. try {
  413. js::read_string(asString(in), v[0]);
  414. }
  415. catch (...)
  416. {
  417. cerr << "Error: Invalid format; bad JSON." << endl;
  418. exit(1);
  419. }
  420. RLPStream out;
  421. while (!v.empty())
  422. {
  423. auto vb = v.back();
  424. v.pop_back();
  425. switch (vb.type())
  426. {
  427. case js::array_type:
  428. {
  429. js::mArray a = vb.get_array();
  430. out.appendList(a.size());
  431. for (int i = a.size() - 1; i >= 0; --i)
  432. v.push_back(a[i]);
  433. break;
  434. }
  435. case js::str_type:
  436. {
  437. string const& s = vb.get_str();
  438. if (s.size() >= 2 && s.substr(0, 2) == "0x")
  439. out << fromHex(s);
  440. else
  441. {
  442. // assume it's a normal JS escaped string.
  443. bytes ss;
  444. ss.reserve(s.size());
  445. for (unsigned i = 0; i < s.size(); ++i)
  446. if (s[i] == '\\' && i + 1 < s.size())
  447. {
  448. if (s[++i] == 'x' && i + 2 < s.size())
  449. ss.push_back(fromHex(s.substr(i, 2))[0]);
  450. }
  451. else if (s[i] != '\\')
  452. ss.push_back((byte)s[i]);
  453. out << ss;
  454. }
  455. break;
  456. }
  457. case js::int_type:
  458. out << vb.get_int();
  459. break;
  460. default:
  461. cerr << "ERROR: Unsupported type in JSON." << endl;
  462. if (!lenience)
  463. exit(1);
  464. }
  465. }
  466. putOut(out.out(), encoding, encrypt, quiet);
  467. break;
  468. }
  469. default:;
  470. }
  471. }
  472. catch (...)
  473. {
  474. cerr << "Error: Invalid format; bad RLP." << endl;
  475. exit(1);
  476. }
  477. return 0;
  478. }