From 458d832cc64b89773f7f704fc905a6217c1e4485 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 4 Mar 2015 23:24:01 +0100 Subject: [PATCH] RLP tool. --- CMakeLists.txt | 1 + eth/main.cpp | 55 ++++---- libdevcore/CommonIO.cpp | 19 ++- libdevcore/CommonIO.h | 6 +- libethcore/Params.cpp | 4 +- libethcore/ProofOfWork.cpp | 2 - params.json | 8 +- rlp/CMakeLists.txt | 16 +++ rlp/base64.cpp | 128 +++++++++++++++++ rlp/base64.h | 40 ++++++ rlp/main.cpp | 279 +++++++++++++++++++++++++++++++++++++ 11 files changed, 516 insertions(+), 42 deletions(-) create mode 100644 rlp/CMakeLists.txt create mode 100644 rlp/base64.cpp create mode 100644 rlp/base64.h create mode 100644 rlp/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index dffdd9027..59cfe4ea3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,6 +150,7 @@ if (EVMJIT) endif() add_subdirectory(libdevcore) +add_subdirectory(rlp) add_subdirectory(libevmcore) add_subdirectory(liblll) diff --git a/eth/main.cpp b/eth/main.cpp index e3753364f..a7bed4f3b 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -102,33 +102,31 @@ void help() cout << "Usage eth [OPTIONS] " << endl << "Options:" << endl - << " -a,--address Set the coinbase (mining payout) address to addr (default: auto)." << endl - << " -b,--bootstrap Connect to the default Ethereum peerserver." << endl - << " -c,--client-name Add a name to your client's version string (default: blank)." << endl - << " -d,--db-path Load database from path (default: ~/.ethereum " << endl - << " /Etherum or Library/Application Support/Ethereum)." << endl - << " -f,--force-mining Mine even when there are no transaction to mine (Default: off)" << endl - << " -h,--help Show this help message and exit." << endl - << " -i,--interactive Enter interactive mode (default: non-interactive)." << endl + << " -a,--address Set the coinbase (mining payout) address to addr (default: auto)." << endl + << " -b,--bootstrap Connect to the default Ethereum peerserver." << endl + << " -c,--client-name Add a name to your client's version string (default: blank)." << endl + << " -d,--db-path Load database from path (default: ~/.ethereum " << endl + << " /Etherum or Library/Application Support/Ethereum)." << endl + << " -f,--force-mining Mine even when there are no transaction to mine (Default: off)" << endl + << " -h,--help Show this help message and exit." << endl + << " -i,--interactive Enter interactive mode (default: non-interactive)." << endl #if ETH_JSONRPC << " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: 8080)." << endl #endif - << " -l,--listen Listen on the given port for incoming connected (default: 30303)." << endl - << " -m,--mining Enable mining, optionally for a specified number of blocks (Default: off)" << endl - << " -n,--upnp Use upnp for NAT (default: on)." << endl - << " -L,--local-networking Use peers whose addresses are local." << endl - << " -o,--mode Start a full node or a peer node (Default: full)." << endl - << " -p,--port Connect to remote port (default: 30303)." << endl - << " -r,--remote Connect to remote host (default: none)." << endl - << " -s,--secret Set the secret key for use with send command (default: auto)." << endl - << " --structured-logging Enables structured logging." << endl - << " --structured-logging-format Give time format string for structured logging output." << endl - << " -t,--miners Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl - << " -u,--public-ip Force public ip to given (default; auto)." << endl - << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (Default: 8)." << endl - << " -x,--peers Attempt to connect to given number of peers (Default: 5)." << endl - << " -V,--version Show the version and exit." << endl + << " -l,--listen Listen on the given port for incoming connected (default: 30303)." << endl + << " -m,--mining Enable mining, optionally for a specified number of blocks (Default: off)" << endl + << " -n,--upnp Use upnp for NAT (default: on)." << endl + << " -L,--local-networking Use peers whose addresses are local." << endl + << " -o,--mode Start a full node or a peer node (Default: full)." << endl + << " -p,--port Connect to remote port (default: 30303)." << endl + << " -r,--remote Connect to remote host (default: none)." << endl + << " -s,--secret Set the secret key for use with send command (default: auto)." << endl + << " -t,--miners Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl + << " -u,--public-ip Force public ip to given (default; auto)." << endl + << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (Default: 8)." << endl + << " -x,--peers Attempt to connect to given number of peers (Default: 5)." << endl + << " -V,--version Show the version and exit." << endl #if ETH_EVMJIT << " --jit Use EVM JIT (default: off)." << endl #endif @@ -141,15 +139,14 @@ string credits(bool _interactive = false) std::ostringstream cout; cout << "Ethereum (++) " << dev::Version << endl - << " Code by Gav Wood, (c) 2013, 2014." << endl + << " Code by Gav Wood et al, (c) 2013, 2014, 2015." << endl << " Based on a design by Vitalik Buterin." << endl << endl; if (_interactive) - { - cout << "Type 'netstart 30303' to start networking" << endl; - cout << "Type 'connect " << Host::pocHost() << " 30303' to connect" << endl; - cout << "Type 'exit' to quit" << endl << endl; - } + cout + << "Type 'netstart 30303' to start networking" << endl + << "Type 'connect " << Host::pocHost() << " 30303' to connect" << endl + << "Type 'exit' to quit" << endl << endl; return cout.str(); } diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp index 288dbd1ff..4fa132073 100644 --- a/libdevcore/CommonIO.cpp +++ b/libdevcore/CommonIO.cpp @@ -84,7 +84,7 @@ bytes dev::contents(std::string const& _file) is.seekg (0, is.end); streamoff length = is.tellg(); if (length == 0) // return early, MSVC does not like reading 0 bytes - return {}; + return bytes(); is.seekg (0, is.beg); bytes ret(length); is.read((char*)ret.data(), length); @@ -92,9 +92,22 @@ bytes dev::contents(std::string const& _file) return ret; } -void dev::writeFile(std::string const& _file, bytes const& _data) +string dev::contentsString(std::string const& _file) { - ofstream(_file, ios::trunc).write((char const*)_data.data(), _data.size()); + std::ifstream is(_file, std::ifstream::binary); + if (!is) + return string(); + // get length of file: + is.seekg (0, is.end); + streamoff length = is.tellg(); + if (length == 0) // return early, MSVC does not like reading 0 bytes + return string(); + is.seekg (0, is.beg); + string ret; + ret.resize(length); + is.read((char*)ret.data(), length); + is.close(); + return ret; } void dev::writeFile(std::string const& _file, bytesConstRef _data) diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h index 5769b6c64..1fc8a65e4 100644 --- a/libdevcore/CommonIO.h +++ b/libdevcore/CommonIO.h @@ -42,13 +42,15 @@ namespace dev /// Retrieve and returns the contents of the given file. If the file doesn't exist or isn't readable, returns an empty bytes. bytes contents(std::string const& _file); +std::string contentsString(std::string const& _file); /// Retrieve and returns the allocated contents of the given file. If the file doesn't exist or isn't readable, returns nullptr. Don't forget to delete [] when finished. bytesRef contentsNew(std::string const& _file); -/// Write the given binary data into the given file, replacing the file if it pre-exists. -void writeFile(std::string const& _file, bytes const& _data); /// Write the given binary data into the given file, replacing the file if it pre-exists. void writeFile(std::string const& _file, bytesConstRef _data); +/// Write the given binary data into the given file, replacing the file if it pre-exists. +inline void writeFile(std::string const& _file, bytes const& _data) { writeFile(_file, bytesConstRef(&_data)); } +inline void writeFile(std::string const& _file, std::string const& _data) { writeFile(_file, bytesConstRef(_data)); } /// Nicely renders the given bytes to a string, optionally as HTML. /// @a _bytes: bytes array to be rendered as string. @a _width of a bytes line. diff --git a/libethcore/Params.cpp b/libethcore/Params.cpp index f36af1375..d1154abcc 100644 --- a/libethcore/Params.cpp +++ b/libethcore/Params.cpp @@ -28,13 +28,13 @@ namespace eth { //--- BEGIN: AUTOGENERATED FROM /feeStructure.json -u256 const c_genesisDifficulty = 2048; +u256 const c_genesisDifficulty = 131072; u256 const c_maximumExtraDataSize = 1024; u256 const c_epochDuration = 3000; u256 const c_genesisGasLimit = 1000000; u256 const c_minGasLimit = 125000; u256 const c_gasLimitBoundDivisor = 1024; -u256 const c_minimumDifficulty = 2048; +u256 const c_minimumDifficulty = 131072; u256 const c_difficultyBoundDivisor = 2048; u256 const c_durationLimit = 8; u256 const c_tierStepGas[] = {0, 2, 3, 5, 8, 10, 20, 0}; diff --git a/libethcore/ProofOfWork.cpp b/libethcore/ProofOfWork.cpp index 332d739bb..c879df2ce 100644 --- a/libethcore/ProofOfWork.cpp +++ b/libethcore/ProofOfWork.cpp @@ -66,7 +66,6 @@ public: writeFile(memoFile, m_caches[_header.seedHash]); } } - cdebug << "sha3 of cache: " << sha3(m_caches[_header.seedHash]); return m_caches[_header.seedHash]; } @@ -91,7 +90,6 @@ public: writeFile(memoFile, m_fulls[_header.seedHash]); } } - cdebug << "sha3 of full pad: " << sha3(m_fulls[_header.seedHash]); return m_fulls[_header.seedHash].data(); } diff --git a/params.json b/params.json index 1b5682544..744f92154 100644 --- a/params.json +++ b/params.json @@ -1,13 +1,13 @@ var x = { - "genesisDifficulty": { "v": 2048, "d": "Difficulty of the Genesis block." }, - "maximumExtraDataSize": { "v": 1024, "d": "Maximum size extra data may be after Genesis." }, - "epochDuration": { "v": 3000, "d": "Duration between proof-of-work epochs." }, "genesisGasLimit": { "v": 1000000, "d": "Gas limit of the Genesis block." }, "minGasLimit": { "v": 125000, "d": "Minimum the gas limit may ever be." }, "gasLimitBoundDivisor": { "v": 1024, "d": "The bound divisor of the gas limit, used in update calculations." }, - "minimumDifficulty": { "v": 2048, "d": "The minimum that the difficulty may ever be." }, + "genesisDifficulty": { "v": 131072, "d": "Difficulty of the Genesis block." }, + "minimumDifficulty": { "v": 131072, "d": "The minimum that the difficulty may ever be." }, "difficultyBoundDivisor": { "v": 2048, "d": "The bound divisor of the difficulty, used in the update calculations." }, "durationLimit": { "v": 8, "d": "The decision boundary on the blocktime duration used to determine whether difficulty should go up or not." }, + "maximumExtraDataSize": { "v": 1024, "d": "Maximum size extra data may be after Genesis." }, + "epochDuration": { "v": 3000, "d": "Duration between proof-of-work epochs." }, "tierStepGas": { "v": [ 0, 2, 3, 5, 8, 10, 20 ], "d": "Once per operation, for a selection of them." }, "expGas": { "v": 10, "d": "Once per EXP instuction." }, diff --git a/rlp/CMakeLists.txt b/rlp/CMakeLists.txt new file mode 100644 index 000000000..0e2b5f57b --- /dev/null +++ b/rlp/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_policy(SET CMP0015 NEW) +set(CMAKE_AUTOMOC OFF) + +aux_source_directory(. SRC_LIST) + +include_directories(BEFORE ..) +include_directories(${LEVELDB_INCLUDE_DIRS}) + +set(EXECUTABLE rlp) + +add_executable(${EXECUTABLE} ${SRC_LIST}) + +target_link_libraries(${EXECUTABLE} devcrypto) + +install( TARGETS ${EXECUTABLE} DESTINATION bin) + diff --git a/rlp/base64.cpp b/rlp/base64.cpp new file mode 100644 index 000000000..5e2b32000 --- /dev/null +++ b/rlp/base64.cpp @@ -0,0 +1,128 @@ +/* + base64.cpp and base64.h + + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch +*/ +/// code found on http://stackoverflow.com/questions/180947/base64-decode-snippet-in-c +/// originally by René Nyffenegger, modified by some other guy and then again by Gav Wood. + +#include "base64.h" + +#include + +using namespace std; +using namespace dev; + +static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +static inline bool is_base64(byte c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +std::string dev::base64_encode(bytesConstRef _in) { + std::string ret; + int i = 0; + int j = 0; + byte char_array_3[3]; + byte char_array_4[4]; + + auto buf = _in.data(); + auto bufLen = _in.size(); + + while (bufLen--) { + char_array_3[i++] = *(buf++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(i = 0; (i <4) ; i++) + ret += base64_chars[char_array_4[i]]; + i = 0; + } + } + + if (i) + { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + ret += base64_chars[char_array_4[j]]; + + while((i++ < 3)) + ret += '='; + } + + return ret; +} + +bytes dev::base64_decode(std::string const& encoded_string) { + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + byte char_array_4[4], char_array_3[3]; + bytes ret; + + while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) + char_array_4[i] = base64_chars.find(char_array_4[i]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret.push_back(char_array_3[i]); + i = 0; + } + } + + if (i) { + for (j = i; j <4; j++) + char_array_4[j] = 0; + + for (j = 0; j <4; j++) + char_array_4[j] = base64_chars.find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) ret.push_back(char_array_3[j]); + } + + return ret; +} diff --git a/rlp/base64.h b/rlp/base64.h new file mode 100644 index 000000000..53ba282c8 --- /dev/null +++ b/rlp/base64.h @@ -0,0 +1,40 @@ +/* + base64.cpp and base64.h + + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch +*/ +/// code found on http://stackoverflow.com/questions/180947/base64-decode-snippet-in-c +/// originally by René Nyffenegger, modified by some other guy and then again by Gav Wood. +#pragma once + +#include +#include +#include + +namespace dev +{ + +std::string base64_encode(bytesConstRef _in); +bytes base64_decode(std::string const& _in); + +} diff --git a/rlp/main.cpp b/rlp/main.cpp new file mode 100644 index 000000000..9bd51cdfc --- /dev/null +++ b/rlp/main.cpp @@ -0,0 +1,279 @@ +/* + 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 . +*/ +/** @file main.cpp + * @author Gav Wood + * @date 2014 + * RLP tool. + */ +#include +#include +#include +#include +#include +#include +#include +#include "base64.h" +using namespace std; +using namespace dev; + +void help() +{ + cout + << "Usage rlp [OPTIONS] [ | -- ]" << endl + << "Options:" << 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, +}; + +enum class Encoding { + Auto, + Hex, + Base64, + Binary, +}; + +bool isAscii(string const& _s) +{ + for (char c: _s) + if (c < 32) + return false; + return true; +} + +class RLPStreamer +{ +public: + struct Prefs + { + string indent = " "; + bool hexInts = false; + bool forceString = false; + 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 << toHex(toCompactBigEndian(_d.toInt(RLP::LaisezFaire))); + else + m_out << _d.toInt(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 << 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 == "-i" || arg == "--indent") && argc > i) + prefs.indent = argv[++i]; + else if (arg == "--hex-ints") + prefs.hexInts = true; + 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); + 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; + } + } + } + bytes b; + 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 = base64_decode(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(i.data().data()), i.data().size()); + } + break; + } + case Mode::Render: + { + RLPStreamer s(cout, prefs); + s.output(rlp); + cout << endl; + break; + } + default:; + } + } + catch (...) + { + cerr << "Error: Invalid format; bad RLP." << endl; + exit(1); + } + + return 0; +}