You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

381 lines
9.5 KiB

/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file main.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
* RLP tool.
*/
#include <fstream>
#include <iostream>
#include <boost/algorithm/string.hpp>
#include "../test/JsonSpiritHeaders.h"
#include <libdevcore/CommonIO.h>
#include <libdevcore/RLP.h>
#include <libdevcrypto/SHA3.h>
using namespace std;
using namespace dev;
namespace js = json_spirit;
void help()
{
cout
<< "Usage rlp [OPTIONS] [ <file> | -- ]" << endl
<< "Options:" << endl
<< " -r,--render Render the given RLP. Options:" << endl
<< " --indent <string> Use string as the level indentation (default ' ')." << endl
<< " --hex-ints Render integers in hex." << endl
<< " --ascii-strings Render data as C-style strings or hex depending on content being ASCII." << endl
<< " --force-string Force all data to be rendered as C-style strings." << endl
<< " --force-escape When rendering as C-style strings, force all characters to be escaped." << endl
<< " --force-hex Force all data to be rendered as raw hex." << endl
<< " -l,--list-archive List the items in the RLP list by hash and size." << endl
<< " -e,--extract-archive Extract all items in the RLP list, named by hash." << endl
<< " -c,--create Given a simplified JSON string, output the RLP." << endl
<< "General options:" << endl
<< " -L,--lenience Try not to bomb out early if possible." << endl
<< " -x,--hex,--base-16 Treat input RLP as hex encoded data." << endl
<< " --64,--base-64 Treat input RLP as base-64 encoded data." << endl
<< " -b,--bin,--base-256 Treat input RLP as raw binary data." << endl
<< " -h,--help Print this help message and exit." << endl
<< " -V,--version Show the version and exit." << endl
;
exit(0);
}
void version()
{
cout << "rlp version " << dev::Version << endl;
exit(0);
}
enum class Mode {
ListArchive,
ExtractArchive,
Render,
Create
};
enum class Encoding {
Auto,
Hex,
Base64,
Binary,
};
bool isAscii(string const& _s)
{
// Always hex-encode anything beginning with 0x to avoid ambiguity.
if (_s.size() >= 2 && _s.substr(0, 2) == "0x")
return false;
for (char c: _s)
if (c < 32)
return false;
return true;
}
class RLPStreamer
{
public:
struct Prefs
{
string indent = " ";
bool hexInts = false;
bool hexPrefix = true;
bool forceString = true;
bool escapeAll = false;
bool forceHex = false;
};
RLPStreamer(ostream& _out, Prefs _p): m_out(_out), m_prefs(_p) {}
void output(RLP const& _d, unsigned _level = 0)
{
if (_d.isNull())
m_out << "null";
else if (_d.isInt())
if (m_prefs.hexInts)
m_out << (m_prefs.hexPrefix ? "0x" : "") << toHex(toCompactBigEndian(_d.toInt<bigint>(RLP::LaisezFaire), 1), 1);
else
m_out << _d.toInt<bigint>(RLP::LaisezFaire);
else if (_d.isData())
if (m_prefs.forceString || (!m_prefs.forceHex && isAscii(_d.toString())))
m_out << escaped(_d.toString(), m_prefs.escapeAll);
else
m_out << (m_prefs.hexPrefix ? "0x" : "") << toHex(_d.data());
else if (_d.isList())
{
m_out << "[";
string newline = "\n";
for (unsigned i = 0; i < _level + 1; ++i)
newline += m_prefs.indent;
int j = 0;
for (auto i: _d)
{
m_out << (j++ ?
(m_prefs.indent.empty() ? ", " : ("," + newline)) :
(m_prefs.indent.empty() ? " " : newline));
output(i, _level + 1);
}
newline = newline.substr(0, newline.size() - m_prefs.indent.size());
m_out << (m_prefs.indent.empty() ? (j ? " ]" : "]") : (j ? newline + "]" : "]"));
}
}
private:
std::ostream& m_out;
Prefs m_prefs;
};
int main(int argc, char** argv)
{
Encoding encoding = Encoding::Auto;
Mode mode = Mode::Render;
string inputFile = "--";
bool lenience = false;
RLPStreamer::Prefs prefs;
for (int i = 1; i < argc; ++i)
{
string arg = argv[i];
if (arg == "-h" || arg == "--help")
help();
else if (arg == "-r" || arg == "--render")
mode = Mode::Render;
else if (arg == "-c" || arg == "--create")
mode = Mode::Create;
else if ((arg == "-i" || arg == "--indent") && argc > i)
prefs.indent = argv[++i];
else if (arg == "--hex-ints")
prefs.hexInts = true;
else if (arg == "--ascii-strings")
prefs.forceString = prefs.forceHex = false;
else if (arg == "--force-string")
prefs.forceString = true;
else if (arg == "--force-hex")
prefs.forceHex = true;
else if (arg == "--force-escape")
prefs.escapeAll = true;
else if (arg == "-l" || arg == "--list-archive")
mode = Mode::ListArchive;
else if (arg == "-e" || arg == "--extract-archive")
mode = Mode::ExtractArchive;
else if (arg == "-L" || arg == "--lenience")
lenience = true;
else if (arg == "-V" || arg == "--version")
version();
else if (arg == "-x" || arg == "--hex" || arg == "--base-16")
encoding = Encoding::Hex;
else if (arg == "--64" || arg == "--base-64")
encoding = Encoding::Base64;
else if (arg == "-b" || arg == "--bin" || arg == "--base-256")
encoding = Encoding::Binary;
else
inputFile = arg;
}
bytes in;
if (inputFile == "--")
for (int i = cin.get(); i != -1; i = cin.get())
in.push_back((byte)i);
else
in = contents(inputFile);
bytes b;
if (mode != Mode::Create)
{
if (encoding == Encoding::Auto)
{
encoding = Encoding::Hex;
for (char b: in)
if (b != '\n' && b != ' ' && b != '\t')
{
if (encoding == Encoding::Hex && (b < '0' || b > '9' ) && (b < 'a' || b > 'f' ) && (b < 'A' || b > 'F' ))
{
cerr << "'" << b << "':" << (int)b << endl;
encoding = Encoding::Base64;
}
if (encoding == Encoding::Base64 && (b < '0' || b > '9' ) && (b < 'a' || b > 'z' ) && (b < 'A' || b > 'Z' ) && b != '+' && b != '/')
{
encoding = Encoding::Binary;
break;
}
}
}
switch (encoding)
{
case Encoding::Hex:
{
string s = asString(in);
boost::algorithm::replace_all(s, " ", "");
boost::algorithm::replace_all(s, "\n", "");
boost::algorithm::replace_all(s, "\t", "");
b = fromHex(s);
break;
}
case Encoding::Base64:
{
string s = asString(in);
boost::algorithm::replace_all(s, " ", "");
boost::algorithm::replace_all(s, "\n", "");
boost::algorithm::replace_all(s, "\t", "");
b = fromBase64(s);
break;
}
default:
swap(b, in);
break;
}
}
try
{
RLP rlp(b);
switch (mode)
{
case Mode::ListArchive:
{
if (!rlp.isList())
{
cout << "Error: Invalid format; RLP data is not a list." << endl;
exit(1);
}
cout << rlp.itemCount() << " items:" << endl;
for (auto i: rlp)
{
if (!i.isData())
{
cout << "Error: Invalid format; RLP list item is not data." << endl;
if (!lenience)
exit(1);
}
cout << " " << i.size() << " bytes: " << sha3(i.data()) << endl;
}
break;
}
case Mode::ExtractArchive:
{
if (!rlp.isList())
{
cout << "Error: Invalid format; RLP data is not a list." << endl;
exit(1);
}
cout << rlp.itemCount() << " items:" << endl;
for (auto i: rlp)
{
if (!i.isData())
{
cout << "Error: Invalid format; RLP list item is not data." << endl;
if (!lenience)
exit(1);
}
ofstream fout;
fout.open(toString(sha3(i.data())));
fout.write(reinterpret_cast<char const*>(i.data().data()), i.data().size());
}
break;
}
case Mode::Render:
{
RLPStreamer s(cout, prefs);
s.output(rlp);
cout << endl;
break;
}
case Mode::Create:
{
vector<js::mValue> v(1);
try {
js::read_string(asString(in), v[0]);
}
catch (...)
{
cerr << "Error: Invalid format; bad JSON." << endl;
exit(1);
}
RLPStream out;
while (!v.empty())
{
auto vb = v.back();
v.pop_back();
switch (vb.type())
{
case js::array_type:
{
js::mArray a = vb.get_array();
out.appendList(a.size());
for (int i = a.size() - 1; i >= 0; --i)
v.push_back(a[i]);
break;
}
case js::str_type:
{
string const& s = vb.get_str();
if (s.size() >= 2 && s.substr(0, 2) == "0x")
out << fromHex(s);
else
{
// assume it's a normal JS escaped string.
bytes ss;
ss.reserve(s.size());
for (unsigned i = 0; i < s.size(); ++i)
if (s[i] == '\\' && i + 1 < s.size())
{
if (s[++i] == 'x' && i + 2 < s.size())
ss.push_back(fromHex(s.substr(i, 2))[0]);
}
else if (s[i] != '\\')
ss.push_back((byte)s[i]);
out << ss;
}
break;
}
case js::int_type:
out << vb.get_int();
break;
default:
cerr << "ERROR: Unsupported type in JSON." << endl;
if (!lenience)
exit(1);
}
}
switch (encoding)
{
case Encoding::Hex: case Encoding::Auto:
cout << toHex(out.out()) << endl;
break;
case Encoding::Base64:
cout << toBase64(&out.out()) << endl;
break;
case Encoding::Binary:
cout.write((char const*)out.out().data(), out.out().size());
break;
}
break;
}
default:;
}
}
catch (...)
{
cerr << "Error: Invalid format; bad RLP." << endl;
exit(1);
}
return 0;
}