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.
 
 
 
 
 

1479 lines
39 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
* Ethereum client.
*/
#include <thread>
#include <chrono>
#include <fstream>
#include <iostream>
#include <signal.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/trim_all.hpp>
#include <libdevcrypto/FileSystem.h>
#include <libevmcore/Instruction.h>
#include <libdevcore/StructuredLogger.h>
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include <libethereum/All.h>
#include <libwebthree/WebThree.h>
#if ETH_JSONRPC
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h>
#endif
#include "BuildInfo.h"
#undef KEY_EVENT // from windows.h
#include <ncurses.h>
#undef OK
#include <form.h>
#undef OK
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace p2p;
using namespace boost::algorithm;
using dev::eth::Instruction;
bool isTrue(std::string const& _m)
{
return _m == "on" || _m == "yes" || _m == "true" || _m == "1";
}
bool isFalse(std::string const& _m)
{
return _m == "off" || _m == "no" || _m == "false" || _m == "0";
}
void help()
{
cout
<< "Usage neth [OPTIONS]" << endl
<< "Options:" << endl
<< " -a,--address <addr> Set the coinbase (mining payout) address to addr (default: auto)." << endl
<< " -b,--bootstrap Connect to the default Ethereum peerserver." << endl
<< " -B,--block-fees <n> Set the block fee profit in the reference unit e.g. ¢ (Default: 15)." << endl
<< " -c,--client-name <name> Add a name to your client's version string (default: blank)." << endl
<< " -d,--db-path <path> Load database from path (default: ~/.ethereum " << endl
<< " <APPDATA>/Etherum or Library/Application Support/Ethereum)." << endl
<< " -e,--ether-price <n> Set the ether price in the reference unit e.g. ¢ (Default: 30.679)." << 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
#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
<< " -K,--kill-blockchain First kill the blockchain." << endl
<< " --listen-ip <ip> Listen on the given IP for incoming connections (default: 0.0.0.0)." << endl
<< " -l,--listen <port> Listen on the given port for incoming connections (default: 30303)." << endl
<< " -u,--public-ip <ip> Force public ip to given (default; auto)." << endl
<< " -m,--mining <on/off> Enable mining (default: off)" << endl
<< " -n,--upnp <on/off> Use upnp for NAT (default: on)." << endl
<< " -o,--mode <full/peer> Start a full node or a peer node (Default: full)." << endl
<< " -p,--port <port> Connect to remote port (default: 30303)." << endl
<< " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl
<< " -r,--remote <host> Connect to remote host (default: none)." << endl
<< " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl
<< " -t,--miners <number> Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl
<< " -v,--verbosity <0..9> Set the log verbosity from 0 to 9 (tmp forced to 1)." << endl
<< " -x,--peers <number> 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
;
exit(0);
}
void interactiveHelp()
{
cout
<< "Commands:" << endl
<< " netstart <port> Starts the network sybsystem on a specific port." << endl
<< " netstop Stops the network subsystem." << endl
#if ETH_JSONRPC
<< " jsonstart <port> Starts the JSON-RPC server." << endl
<< " jsonstop Stops the JSON-RPC server." << endl
#endif
<< " connect <addr> <port> Connects to a specific peer." << endl
<< " minestart Starts mining." << endl
<< " minestop Stops mining." << endl
<< " address Gives the current address." << endl
<< " secret Gives the current secret" << endl
<< " block Gives the current block height." << endl
<< " balance Gives the current balance." << endl
<< " peers List the peers that are connected" << endl
<< " transact Execute a given transaction." << endl
<< " send Execute a given transaction with current secret." << endl
<< " contract Create a new contract with current secret." << endl
<< " inspect <contract> Dumps a contract to <APPDATA>/<contract>.evm." << endl
<< " verbosity (<level>) Gets or sets verbosity level." << endl
<< " setblockfees <n> Set the block fee profit in the reference unit e.g. ¢ (Default: 15)" << endl
<< " setetherprice <p> Resets the ether price." << endl
<< " setpriority <p> Resets the transaction priority." << endl
<< " reset Resets ncurses windows" << endl
<< " exit Exits the application." << endl;
}
string credits()
{
std::ostringstream ccout;
ccout
<< "NEthereum (++) " << dev::Version << endl
<< " Code by Gav Wood & caktux, (c) 2013, 2014, 2015." << endl
<< " Based on a design by Vitalik Buterin." << endl << endl;
ccout << "Type 'netstart 30303' to start networking" << endl;
ccout << "Type 'connect " << Host::pocHost() << " 30303' to connect" << endl;
ccout << "Type 'exit' to quit" << endl << endl;
return ccout.str();
}
void version()
{
cout << "neth version " << dev::Version << endl;
cout << "eth network protocol version: " << dev::eth::c_protocolVersion << endl;
cout << "Client database version: " << dev::eth::c_databaseVersion << endl;
cout << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl;
exit(0);
}
Address c_config = Address("661005d2720d855f1d9976f88bb10c1a3398c77f");
string pretty(h160 _a, dev::eth::State _st)
{
string ns;
h256 n;
if (h160 nameReg = (u160)_st.storage(c_config, 0))
n = _st.storage(nameReg, (u160)(_a));
if (n)
{
std::string s((char const*)n.data(), 32);
if (s.find_first_of('\0') != string::npos)
s.resize(s.find_first_of('\0'));
ns = " " + s;
}
return ns;
}
bool g_exit = false;
void sighandler(int)
{
g_exit = true;
}
namespace nc
{
class nc_window_streambuf: public std::streambuf
{
public:
nc_window_streambuf(WINDOW* p, std::ostream& os, unsigned long cursesAttr = 0);
nc_window_streambuf(WINDOW* p, unsigned long _cursesAttr = 0);
nc_window_streambuf(nc_window_streambuf const& _rhs);
virtual ~nc_window_streambuf();
nc_window_streambuf& operator=(nc_window_streambuf const& _rhs);
virtual int overflow(int c);
virtual int sync();
private:
void copy(nc_window_streambuf const& _rhs);
WINDOW* m_pnl;
unsigned long m_flags;
std::ostream* m_os;
std::streambuf* m_old;
};
nc_window_streambuf::nc_window_streambuf(WINDOW * p, unsigned long _cursesAttr):
m_pnl(p),
m_flags(_cursesAttr),
m_os(0),
m_old(0)
{
// Tell parent class that we want to call overflow() for each
// input char:
setp(0, 0);
setg(0, 0, 0);
scrollok(p, true);
mvwinch(p, 0, 0);
}
nc_window_streambuf::nc_window_streambuf(WINDOW* _p, std::ostream& _os, unsigned long _cursesAttr):
m_pnl(_p),
m_flags(_cursesAttr),
m_os(&_os),
m_old(_os.rdbuf())
{
setp(0, 0);
setg(0, 0, 0);
_os.rdbuf(this);
scrollok(_p, true);
mvwinch(_p, 0, 0);
}
void nc_window_streambuf::copy(nc_window_streambuf const& _rhs)
{
if (this != &_rhs)
{
m_pnl = _rhs.m_pnl;
m_flags = _rhs.m_flags;
m_os = _rhs.m_os;
m_old = _rhs.m_old;
}
}
nc_window_streambuf::nc_window_streambuf(nc_window_streambuf const& _rhs):
std::streambuf()
{
copy(_rhs);
}
nc_window_streambuf& nc_window_streambuf::operator=(nc_window_streambuf const& _rhs)
{
copy(_rhs);
return *this;
}
nc_window_streambuf::~nc_window_streambuf()
{
if (m_os)
m_os->rdbuf(m_old);
}
int nc_window_streambuf::overflow(int c)
{
int ret = c;
if (c != EOF)
{
int x = 0;
int y = 0;
int mx = 0;
int my = 0;
getyx(m_pnl, y, x);
getmaxyx(m_pnl, my, mx);
if (y < 1)
y = 1;
if (x < 2)
x = 2;
if (x > mx - 4)
{
if (y + 1 >= my)
scroll(m_pnl);
else
y++;
x = 2;
}
if (m_flags)
{
wattron(m_pnl, m_flags);
if (mvwaddch(m_pnl, y, x++, (chtype)c) == ERR)
ret = EOF;
wattroff(m_pnl, m_flags);
}
else if (mvwaddch(m_pnl, y, x++, (chtype)c) == ERR)
ret = EOF;
}
if (c == EOF) // || std::isspace(c)
if (sync() == EOF)
ret = EOF;
return ret;
}
int nc_window_streambuf::sync()
{
if (stdscr && m_pnl)
return (wrefresh(m_pnl) == ERR) ? EOF : 0;
return EOF;
}
}
vector<string> form_dialog(vector<string> _sfields, vector<string> _lfields, vector<string> _bfields, int _cols, int _rows, string _post_form);
enum class NodeMode
{
PeerServer,
Full
};
int main(int argc, char** argv)
{
string listenIP;
unsigned short listenPort = 30303;
string publicIP;
string remoteHost;
unsigned short remotePort = 30303;
string dbPath;
unsigned mining = ~(unsigned)0;
NodeMode mode = NodeMode::Full;
unsigned peers = 5;
int miners = -1;
#if ETH_JSONRPC
int jsonrpc = 8080;
#endif
bool bootstrap = false;
bool upnp = true;
bool forceMining = false;
bool killChain = false;
bool jit = false;
bool structuredLogging = false;
string structuredLoggingFormat = "%Y-%m-%dT%H:%M:%S";
string clientName;
TransactionPriority priority = TransactionPriority::Medium;
double etherPrice = 30.679;
double blockFees = 15.0;
// Init defaults
Defaults::get();
// Our address.
KeyPair us = KeyPair::create();
Address coinbase = us.address();
string configFile = getDataDir() + "/config.rlp";
bytes b = contents(configFile);
if (b.size())
{
RLP config(b);
us = KeyPair(config[0].toHash<Secret>());
coinbase = config[1].toHash<Address>();
}
else
{
RLPStream config(2);
config << us.secret() << coinbase;
writeFile(configFile, config.out());
}
for (int i = 1; i < argc; ++i)
{
string arg = argv[i];
if (arg == "--listen-ip" && i + 1 < argc)
listenIP = argv[++i];
else if ((arg == "-l" || arg == "--listen" || arg == "--listen-port") && i + 1 < argc)
listenPort = (short)atoi(argv[++i]);
else if ((arg == "-u" || arg == "--public-ip" || arg == "--public") && i + 1 < argc)
publicIP = argv[++i];
else if ((arg == "-r" || arg == "--remote") && i + 1 < argc)
remoteHost = argv[++i];
else if ((arg == "-p" || arg == "--port") && i + 1 < argc)
remotePort = (short)atoi(argv[++i]);
else if ((arg == "-n" || arg == "--upnp") && i + 1 < argc)
{
string m = argv[++i];
if (isTrue(m))
upnp = true;
else if (isFalse(m))
upnp = false;
else
{
cerr << "Invalid UPnP option: " << m << endl;
return -1;
}
}
else if (arg == "-K" || arg == "--kill-blockchain")
killChain = true;
else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc)
clientName = argv[++i];
else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc)
{
try
{
coinbase = h160(fromHex(argv[++i], WhenError::Throw));
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, coinbase rejected";
cwarn << boost::diagnostic_information(_e);
break;
}
catch (...)
{
cwarn << "coinbase rejected";
break;
}
}
else if ((arg == "-s" || arg == "--secret") && i + 1 < argc)
us = KeyPair(h256(fromHex(argv[++i])));
else if (arg == "--structured-logging-format" && i + 1 < argc)
structuredLoggingFormat = string(argv[++i]);
else if (arg == "--structured-logging")
structuredLogging = true;
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
dbPath = argv[++i];
else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
{
try
{
blockFees = stof(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[++i] << endl;
return -1;
}
}
else if ((arg == "-e" || arg == "--ether-price") && i + 1 < argc)
{
try
{
etherPrice = stof(argv[++i]);
}
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[++i] << endl;
return -1;
}
}
else if ((arg == "-P" || arg == "--priority") && i + 1 < argc)
{
string m = boost::to_lower_copy(string(argv[++i]));
if (m == "lowest")
priority = TransactionPriority::Lowest;
else if (m == "low")
priority = TransactionPriority::Low;
else if (m == "medium" || m == "mid" || m == "default" || m == "normal")
priority = TransactionPriority::Medium;
else if (m == "high")
priority = TransactionPriority::High;
else if (m == "highest")
priority = TransactionPriority::Highest;
else
try {
priority = (TransactionPriority)(max(0, min(100, stoi(m))) * 8 / 100);
}
catch (...) {
cerr << "Unknown " << arg << " option: " << m << endl;
return -1;
}
}
else if ((arg == "-m" || arg == "--mining") && i + 1 < argc)
{
string m = argv[++i];
if (isTrue(m))
mining = ~(unsigned)0;
else if (isFalse(m))
mining = 0;
else
try {
mining = stoi(m);
}
catch (...) {
cerr << "Unknown -m/--mining option: " << m << endl;
return -1;
}
}
else if (arg == "-b" || arg == "--bootstrap")
bootstrap = true;
else if (arg == "-f" || arg == "--force-mining")
forceMining = true;
#if ETH_JSONRPC
else if ((arg == "-j" || arg == "--json-rpc"))
jsonrpc = jsonrpc ? jsonrpc : 8080;
else if (arg == "--json-rpc-port" && i + 1 < argc)
jsonrpc = atoi(argv[++i]);
#endif
else if ((arg == "-v" || arg == "--verbosity") && i + 1 < argc)
g_logVerbosity = atoi(argv[++i]);
else if ((arg == "-x" || arg == "--peers") && i + 1 < argc)
peers = atoi(argv[++i]);
else if ((arg == "-t" || arg == "--miners") && i + 1 < argc)
miners = atoi(argv[++i]);
else if ((arg == "-o" || arg == "--mode") && i + 1 < argc)
{
string m = argv[++i];
if (m == "full")
mode = NodeMode::Full;
else if (m == "peer")
mode = NodeMode::PeerServer;
else
{
cerr << "Unknown mode: " << m << endl;
return -1;
}
}
else if (arg == "--jit")
{
#if ETH_EVMJIT
jit = true;
#else
cerr << "EVM JIT not enabled" << endl;
return -1;
#endif
}
else if (arg == "-h" || arg == "--help")
help();
else if (arg == "-V" || arg == "--version")
version();
else
{
cerr << "Invalid argument: " << arg << endl;
exit(-1);
}
}
if (!clientName.empty())
clientName += "/";
cout << credits();
StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat);
VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter);
NetworkPreferences netPrefs(publicIP, listenIP ,listenPort, upnp);
auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp");
std::string clientImplString = "NEthereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : "");
dev::WebThreeDirect web3(
clientImplString,
dbPath,
killChain,
mode == NodeMode::Full ? set<string>{"eth", "shh"} : set<string>(),
netPrefs,
&nodesState,
miners
);
web3.setIdealPeerCount(peers);
std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000));
eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr;
StructuredLogger::starting(clientImplString, dev::Version);
if (c)
{
c->setGasPricer(gasPricer);
c->setForceMining(forceMining);
c->setAddress(coinbase);
}
cout << "Transaction Signer: " << us.address() << endl;
cout << "Mining Benefactor: " << coinbase << endl;
web3.startNetwork();
if (bootstrap)
web3.addNode(p2p::NodeId(), Host::pocHost());
if (remoteHost.size())
web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort));
if (c && mining)
c->startMining();
#if ETH_JSONRPC
shared_ptr<WebThreeStubServer> jsonrpcServer;
unique_ptr<jsonrpc::AbstractServerConnector> jsonrpcConnector;
if (jsonrpc > -1)
{
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector<KeyPair>({us})));
jsonrpcServer->setIdentities({us});
jsonrpcServer->StartListening();
}
#endif
signal(SIGABRT, &sighandler);
signal(SIGTERM, &sighandler);
signal(SIGINT, &sighandler);
std::ostringstream ccout;
// Initialize ncurses
char* str = new char[255];
int width;
int height;
int y = 0;
int x = 2;
string cmd;
WINDOW * mainwin, * consolewin, * logwin, * blockswin, * pendingwin, *addswin, * contractswin, * peerswin;
if (!(mainwin = initscr()))
{
cerr << "Error initialising ncurses.";
delete [] str;
return -1;
}
getmaxyx(mainwin, height, width);
int qheight = height * 3 / 5;
int qwidth = width / 4 - 4;
nonl();
cbreak();
timeout(30000);
echo();
keypad(mainwin, true);
// Initialize color pairs
start_color();
init_pair(1, COLOR_WHITE, COLOR_BLACK);
init_pair(2, COLOR_RED, COLOR_BLACK);
init_pair(3, 7, COLOR_BLACK);
use_default_colors();
logwin = newwin(height * 2 / 5 - 2, width * 2 / 3, qheight, 0);
nc::nc_window_streambuf outbuf(logwin, std::cout);
consolewin = newwin(qheight, width / 4, 0, 0);
nc::nc_window_streambuf coutbuf(consolewin, ccout);
blockswin = newwin(qheight, width / 4, 0, width / 4);
pendingwin = newwin(height * 1 / 5, width / 4, 0, width * 2 / 4);
peerswin = newwin(height * 2 / 5, width / 4, height * 1 / 5, width * 2 / 4);
addswin = newwin(height * 2 / 5 - 2, width / 3, qheight, width * 2 / 3);
contractswin = newwin(qheight, width / 4, 0, width * 3 / 4);
int vl = qheight - 4;
wsetscrreg(consolewin, 1, vl);
wsetscrreg(blockswin, 1, vl);
wsetscrreg(pendingwin, 1, vl);
wsetscrreg(peerswin, 1, vl);
wsetscrreg(addswin, 1, vl);
wsetscrreg(contractswin, 1, vl);
mvwprintw(mainwin, 1, 1, " > ");
wresize(mainwin, 3, width);
mvwin(mainwin, height - 3, 0);
wmove(mainwin, 1, 4);
while (true)
{
wclrtobot(consolewin);
wclrtobot(pendingwin);
wclrtobot(peerswin);
wclrtobot(addswin);
wclrtobot(contractswin);
ccout << credits();
// Prompt
wmove(mainwin, 1, 4);
getstr(str);
string s(str);
istringstream iss(s);
iss >> cmd;
// Address
ccout << "Address:" << endl;
ccout << toHex(us.address().asArray()) << endl << endl;
mvwprintw(mainwin, 1, 1, " > ");
clrtoeol();
if (s.length() > 1)
{
ccout << "> ";
ccout << str << endl;
}
if (cmd == "netstart")
{
unsigned port;
iss >> port;
if (port)
netPrefs.listenPort = port;
web3.setNetworkPreferences(netPrefs);
web3.startNetwork();
}
else if (cmd == "connect")
{
string addr;
unsigned port;
iss >> addr >> port;
web3.addNode(p2p::NodeId(), addr + ":" + toString(port ? port : p2p::c_defaultIPPort));
}
else if (cmd == "netstop")
{
web3.stopNetwork();
}
else if (c && cmd == "minestart")
{
c->startMining();
}
else if (c && cmd == "minestop")
{
c->stopMining();
}
else if (c && cmd == "mineforce")
{
string enable;
iss >> enable;
c->setForceMining(isTrue(enable));
}
else if (c && cmd == "setblockfees")
{
iss >> blockFees;
gasPricer->setRefBlockFees(u256(blockFees * 1000));
cout << "Block fees: " << blockFees << endl;
}
else if (c && cmd == "setetherprice")
{
iss >> etherPrice;
gasPricer->setRefPrice(u256(double(ether / 1000) / etherPrice));
cout << "ether Price: " << etherPrice << endl;
}
else if (c && cmd == "setpriority")
{
string m;
iss >> m;
boost::to_lower(m);
if (m == "lowest")
priority = TransactionPriority::Lowest;
else if (m == "low")
priority = TransactionPriority::Low;
else if (m == "medium" || m == "mid" || m == "default" || m == "normal")
priority = TransactionPriority::Medium;
else if (m == "high")
priority = TransactionPriority::High;
else if (m == "highest")
priority = TransactionPriority::Highest;
else
try {
priority = (TransactionPriority)(max(0, min(100, stoi(m))) * 8 / 100);
}
catch (...) {
cerr << "Unknown priority: " << m << endl;
}
cout << "Priority: " << (int)priority << "/8" << endl;
}
else if (cmd == "verbosity")
{
if (iss.peek() != -1)
iss >> g_logVerbosity;
cout << "Verbosity: " << g_logVerbosity << endl;
}
#if ETH_JSONRPC
else if (cmd == "jsonport")
{
if (iss.peek() != -1)
iss >> jsonrpc;
cout << "JSONRPC Port: " << jsonrpc << endl;
}
else if (cmd == "jsonstart")
{
if (jsonrpc < 0)
jsonrpc = 8080;
#if ETH_DEBUG
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", 1));
#else
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc, "", "", 4));
#endif
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector<KeyPair>({us})));
jsonrpcServer->setIdentities({us});
jsonrpcServer->StartListening();
}
else if (cmd == "jsonstop")
{
if (jsonrpcServer.get())
jsonrpcServer->StopListening();
jsonrpcServer.reset();
}
#endif
else if (cmd == "address")
{
ccout << "Current address:" << endl;
ccout << toHex(us.address().asArray()) << endl;
}
else if (cmd == "secret")
{
ccout << "Current secret:" << endl;
ccout << toHex(us.secret().asArray()) << endl;
}
else if (c && cmd == "block")
{
unsigned n = c->blockChain().details().number;
ccout << "Current block # ";
ccout << toString(n) << endl;
}
else if (cmd == "peers")
{
for (auto it: web3.peers())
cout << it.host << ":" << it.port << ", " << it.clientVersion << ", "
<< std::chrono::duration_cast<std::chrono::milliseconds>(it.lastPing).count() << "ms"
<< endl;
}
else if (c && cmd == "balance")
{
u256 balance = c->balanceAt(us.address());
ccout << "Current balance:" << endl;
ccout << toString(balance) << endl;
}
else if (c && cmd == "transact")
{
auto const& bc = c->blockChain();
auto h = bc.currentHash();
auto blockData = bc.block(h);
BlockInfo info(blockData);
vector<string> s;
s.push_back("Address");
vector<string> l;
l.push_back("Amount");
stringstream label;
label << "Gas price";
l.push_back(label.str());
l.push_back("Gas");
vector<string> b;
b.push_back("Secret");
b.push_back("Data");
vector<string> fields = form_dialog(s, l, b, height, width, cmd);
int fs = fields.size();
if (fs < 6)
{
if (fs > 0)
cwarn << "Missing parameter";
}
else
{
fields[0].erase(std::remove(fields[0].begin(), fields[0].end(), ' '), fields[0].end());
fields[4].erase(std::remove(fields[4].begin(), fields[4].end(), ' '), fields[4].end());
fields[5].erase(std::find_if(fields[5].rbegin(), fields[5].rend(), std::bind1st(std::not_equal_to<char>(), ' ')).base(), fields[5].end());
int size = fields[0].length();
u256 amount;
u256 gasPrice;
u256 gas;
stringstream ssa;
ssa << fields[1];
ssa >> amount;
stringstream ssg;
ssg << fields[3];
ssg >> gas;
stringstream ssp;
ssp << fields[2];
ssp >> gasPrice;
if (!gasPrice)
gasPrice = gasPricer->bid(priority);
string sechex = fields[4];
string sdata = fields[5];
cnote << "Data:";
cnote << sdata;
bytes data = dev::eth::parseData(sdata);
cnote << "Bytes:";
string sbd = asString(data);
bytes bbd = asBytes(sbd);
stringstream ssbd;
ssbd << bbd;
cnote << ssbd.str();
int ssize = fields[4].length();
u256 minGas = (u256)Client::txGas(data, 0);
if (size < 40)
{
if (size > 0)
cwarn << "Invalid address length:" << size;
}
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else if (ssize < 40)
{
if (ssize > 0)
cwarn << "Invalid secret length:" << ssize;
}
else
{
try
{
Secret secret = h256(fromHex(sechex, WhenError::Throw));
Address dest = h160(fromHex(fields[0], WhenError::Throw));
c->submitTransaction(secret, amount, dest, data, gas, gasPrice);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
}
}
}
else if (c && cmd == "send")
{
vector<string> s;
s.push_back("Address");
vector<string> l;
l.push_back("Amount");
vector<string> b;
vector<string> fields = form_dialog(s, l, b, height, width, cmd);
int fs = fields.size();
if (fs < 2)
{
if (fs > 0)
cwarn << "Missing parameter";
}
else
{
fields[0].erase(std::remove(fields[0].begin(), fields[0].end(), ' '), fields[0].end());
int size = fields[0].length();
u256 amount;
stringstream sss;
sss << fields[1];
sss >> amount;
if (size < 40)
{
if (size > 0)
cwarn << "Invalid address length:" << size;
}
else
{
auto const& bc = c->blockChain();
auto h = bc.currentHash();
auto blockData = bc.block(h);
BlockInfo info(blockData);
u256 minGas = (u256)Client::txGas(bytes(), 0);
try
{
Address dest = h160(fromHex(fields[0], WhenError::Throw));
c->submitTransaction(us.secret(), amount, dest, bytes(), minGas);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
}
}
}
else if (c && cmd == "contract")
{
auto const& bc = c->blockChain();
auto h = bc.currentHash();
auto blockData = bc.block(h);
BlockInfo info(blockData);
vector<string> s;
vector<string> l;
l.push_back("Endowment");
stringstream label;
label << "Gas price";
l.push_back(label.str());
l.push_back("Gas");
vector<string> b;
b.push_back("Code (hex)");
vector<string> fields = form_dialog(s, l, b, height, width, cmd);
int fs = fields.size();
if (fs < 4)
{
if (fs > 0)
cwarn << "Missing parameter";
}
else
{
u256 endowment;
u256 gas;
u256 gasPrice;
stringstream sse;
sse << fields[0];
sse >> endowment;
stringstream ssg;
ssg << fields[2];
ssg >> gas;
stringstream ssp;
ssp << fields[1];
ssp >> gasPrice;
string sinit = fields[3];
trim_all(sinit);
int size = sinit.length();
bytes init;
cnote << "Init:";
cnote << sinit;
cnote << "Code size:" << size;
if (size < 1)
cwarn << "No code submitted";
else
{
cnote << "Assembled:";
stringstream ssc;
try
{
init = fromHex(sinit, WhenError::Throw);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, code rejected";
cwarn << boost::diagnostic_information(_e);
init = bytes();
}
catch (...)
{
cwarn << "code rejected";
init = bytes();
}
ssc.str(string());
ssc << disassemble(init);
cnote << "Init:";
cnote << ssc.str();
}
u256 minGas = (u256)Client::txGas(init, 0);
if (!init.size())
cwarn << "Contract creation aborted, no init code.";
else if (endowment < 0)
cwarn << "Invalid endowment";
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else
{
c->submitTransaction(us.secret(), endowment, init, gas);
}
}
}
else if (c && cmd == "inspect")
{
string rechex;
iss >> rechex;
if (rechex.length() != 40)
cwarn << "Invalid address length";
else
{
auto address = h160(fromHex(rechex));
stringstream s;
try
{
auto storage = c->storageAt(address);
for (auto const& i: storage)
s << "@" << showbase << hex << i.first << " " << showbase << hex << i.second << endl;
s << endl << disassemble(c->codeAt(address)) << endl;
string outFile = getDataDir() + "/" + rechex + ".evm";
ofstream ofs;
ofs.open(outFile, ofstream::binary);
ofs.write(s.str().c_str(), s.str().length());
ofs.close();
cnote << "Saved" << rechex << "to" << outFile;
}
catch (dev::InvalidTrie const& _e)
{
cwarn << "Corrupted trie.\n" << diagnostic_information(_e);
}
}
}
else if (cmd == "reset")
{
vector<WINDOW*> ws { consolewin, blockswin, pendingwin, peerswin, contractswin, addswin, mainwin };
for (auto &w: ws)
{
wclear(w);
wrefresh(w);
}
}
else if (cmd == "help")
interactiveHelp();
else if (cmd == "exit")
break;
else if (cmd != "")
cwarn << "Unrecognised command. Type 'help' for a list of available commands.";
// Clear cmd at each pass
cmd = "";
// Contracts and addresses count / offset
int cc = 1;
int ca = 0;
if (c) {
// Lock to prevent corrupt block-chain errors
auto const& bc = c->blockChain();
ccout << "Genesis hash: " << bc.genesisHash() << endl;
// Blocks
y = 1;
for (auto h = bc.currentHash(); h != bc.genesisHash(); h = bc.details(h).parent)
{
auto d = bc.details(h);
string s = "# " + std::to_string(d.number) + ' ' + toString(h); // .abridged();
mvwaddnstr(blockswin, y++, x, s.c_str(), qwidth);
auto b = bc.block(h);
for (auto const& i: RLP(b)[1])
{
Transaction t(i.data(), CheckSignature::Sender);
auto s = t.receiveAddress() ?
boost::format(" %1% %2%> %3%: %4% [%5%]") %
toString(t.safeSender()) %
(c->codeAt(t.receiveAddress(), PendingBlock).size() ? '*' : '-') %
toString(t.receiveAddress()) %
toString(formatBalance(t.value())) %
toString((unsigned)t.nonce()) :
boost::format(" %1% +> %2%: %3% [%4%]") %
toString(t.safeSender()) %
toString(right160(sha3(rlpList(t.safeSender(), t.nonce())))) %
toString(formatBalance(t.value())) %
toString((unsigned)t.nonce());
mvwaddnstr(blockswin, y++, x, s.str().c_str(), qwidth - 2);
if (y > qheight - 2)
break;
}
if (y > qheight - 2)
break;
}
// Pending
y = 1;
for (Transaction const& t: c->pending())
{
auto s = t.receiveAddress() ?
boost::format("%1% %2%> %3%: %4% [%5%]") %
toString(t.safeSender()) %
(c->codeAt(t.receiveAddress(), PendingBlock).size() ? '*' : '-') %
toString(t.receiveAddress()) %
toString(formatBalance(t.value())) %
toString((unsigned)t.nonce()) :
boost::format("%1% +> %2%: %3% [%4%]") %
toString(t.safeSender()) %
toString(right160(sha3(rlpList(t.safeSender(), t.nonce())))) %
toString(formatBalance(t.value())) %
toString((unsigned)t.nonce());
mvwaddnstr(pendingwin, y++, x, s.str().c_str(), qwidth);
if (y > height * 1 / 5 - 2)
break;
}
#if ETH_FATDB
// Contracts and addresses
y = 1;
auto acs = c->addresses();
ca = acs.size();
for (auto const& i: acs)
if (c->codeAt(i, PendingBlock).size())
{
auto s = boost::format("%1%%2% : %3% [%4%]") %
toString(i) %
pretty(i, c->postState()) %
toString(formatBalance(c->balanceAt(i))) %
toString((unsigned)c->countAt(i, PendingBlock));
mvwaddnstr(contractswin, cc++, x, s.str().c_str(), qwidth);
if (cc > qheight - 2)
break;
}
for (auto const& i: acs)
if (c->codeAt(i, PendingBlock).empty())
{
auto s = boost::format("%1%%2% : %3% [%4%]") %
toString(i) %
pretty(i, c->postState()) %
toString(formatBalance(c->balanceAt(i))) %
toString((unsigned)c->countAt(i, PendingBlock));
mvwaddnstr(addswin, y++, x, s.str().c_str(), width / 2 - 4);
if (y > height * 3 / 5 - 4)
break;
}
#else
mvwaddnstr(contractswin, 1, x, "build with ETH_FATDB to list contracts", qwidth);
mvwaddnstr(addswin, 1, x, "build with ETH_FATDB to list addresses", width / 2 - 4);
#endif
// Peers
y = 1;
for (PeerSessionInfo const& i: web3.peers())
{
auto s = boost::format("%1% ms - %2%:%3% - %4%") %
toString(chrono::duration_cast<chrono::milliseconds>(i.lastPing).count()) %
i.host %
toString(i.port) %
i.clientVersion;
mvwaddnstr(peerswin, y++, x, s.str().c_str(), qwidth);
if (y > height * 2 / 5 - 4)
break;
}
}
box(consolewin, 0, 0);
box(blockswin, 0, 0);
box(pendingwin, 0, 0);
box(peerswin, 0, 0);
box(addswin, 0, 0);
box(contractswin, 0, 0);
box(mainwin, 0, 0);
// Balance
stringstream ssb;
u256 balance;
if (c)
balance = c->balanceAt(us.address());
ssb << "Balance: ";
if (c)
ssb << formatBalance(balance);
mvwprintw(consolewin, 0, x, ssb.str().c_str());
// Block
mvwprintw(blockswin, 0, x, "Block # ");
if (c) {
unsigned n = c->blockChain().details().number;
mvwprintw(blockswin, 0, 10, toString(n).c_str());
}
// Pending
stringstream pc;
pc << "Pending: ";
if (c)
pc << toString(c->pending().size());
else
pc << 0;
mvwprintw(pendingwin, 0, x, pc.str().c_str());
// Contracts
stringstream sc;
sc << "Contracts: " << cc - 1;
mvwprintw(contractswin, 0, x, sc.str().c_str());
// Peers
mvwprintw(peerswin, 0, x, "Peers: ");
mvwprintw(peerswin, 0, 9, toString(web3.peers().size()).c_str());
// Mining flag
if (c && c->isMining())
{
mvwprintw(consolewin, qheight - 1, width / 4 - 11, "Mining ON");
dev::eth::MineProgress p = c->miningProgress();
auto speed = boost::format("%2% kH/s @ %1%s") % (p.ms / 1000) % (p.ms ? p.hashes / p.ms : 0);
mvwprintw(consolewin, qheight - 2, width / 4 - speed.str().length() - 2, speed.str().c_str());
}
else
mvwprintw(consolewin, qheight - 1, width / 4 - 12, "Mining OFF");
wmove(consolewin, 1, x);
// Addresses
stringstream ac;
ac << "Addresses: " << ca;
mvwprintw(addswin, 0, x, ac.str().c_str());
wrefresh(consolewin);
wrefresh(blockswin);
wrefresh(pendingwin);
wrefresh(peerswin);
wrefresh(addswin);
wrefresh(contractswin);
wrefresh(mainwin);
}
delwin(addswin);
delwin(contractswin);
delwin(peerswin);
delwin(pendingwin);
delwin(blockswin);
delwin(consolewin);
delwin(logwin);
delwin(mainwin);
endwin();
refresh();
#if ETH_JSONRPC
if (jsonrpcServer.get())
jsonrpcServer->StopListening();
#endif
return 0;
}
void print_in_middle(WINDOW *win, int starty, int startx, int width, string str, chtype color)
{
int length;
int x = 0;
int y = 0;
float temp;
if (startx != 0)
x = startx;
if (starty != 0)
y = starty;
if (width == 0)
width = 80;
length = str.length();
temp = (width - length) / 2;
x = x + (int)temp;
wattron(win, color);
mvwprintw(win, y, x, "%s", str.c_str());
wattroff(win, color);
refresh();
}
vector<string> form_dialog(vector<string> _sv, vector<string> _lv, vector<string> _bv, int _cols, int _rows, string _post_form)
{
vector<string> vs;
WINDOW *form_win;
int _sfields = _sv.size();
int _lfields = _lv.size();
int _bfields = _bv.size();
int maxfields = _sfields + _lfields + _bfields;
vector<FIELD*> field(maxfields + 1);
int ch;
int starty = 6;
int height = _cols;
int width = _rows;
// Initialize the fields
int si;
int li;
int bi = 0;
vector<int> labels;
for (si = 0; si < _sfields; ++si)
{
starty++; // Leave room for our labels, no window yet so that or fake fields...
field[si] = new_field(1, 40, starty++, 1, 0, 0);
labels.push_back(starty);
set_field_back(field[si], A_UNDERLINE);
set_field_type(field[si], TYPE_ALNUM, 40);
}
for (li = _sfields; li < _sfields + _lfields; ++li)
{
starty++;
field[li] = new_field(1, 64, starty++, 1, 3, 0);
labels.push_back(starty);
set_field_back(field[li], A_UNDERLINE);
}
for (bi = _sfields + _lfields; bi < maxfields; ++bi)
{
starty++;
field[bi] = new_field(5, 72, starty++, 1, 0, 0);
labels.push_back(starty);
field_opts_off(field[bi], O_STATIC);
set_field_back(field[bi], A_UNDERLINE);
starty += 4;
}
// The FORM expects a NULL terminated list of fields
field[maxfields] = NULL;
// Create the form and post it
FORM *form = new_form(field.data());
// Calculate the area required for the form
scale_form(form, &_rows, &_cols);
// Create the window to be associated with the form
form_win = newwin(_rows + 4, _cols + 8, (height / 2 - _rows / 2 - 2), (width / 2 - _cols / 2 - 2));
// Set main window and sub window
set_form_win(form, form_win);
set_form_sub(form, derwin(form_win, _rows, _cols, 2, 2));
nodelay(form_win, true);
keypad(form_win, true);
noecho();
timeout(0);
box(form_win, 0, 0);
print_in_middle(form_win, 1, 0, _cols, _post_form, COLOR_PAIR(2));
post_form(form);
// Set labels
int ca = 0;
int cf;
for (cf = 0; cf < _sfields; ++cf)
{
wattron(form_win, COLOR_PAIR(3));
mvwprintw(form_win, labels[ca], 3, _sv[cf].c_str());
wattroff(form_win, COLOR_PAIR(3));
ca++;
}
for (cf = 0; cf < _lfields; ++cf)
{
wattron(form_win, COLOR_PAIR(3));
mvwprintw(form_win, labels[ca], 3, _lv[cf].c_str());
mvwprintw(form_win, labels[ca] + 1, _cols - 1, "wei");
wattroff(form_win, COLOR_PAIR(3));
ca++;
}
for (cf = 0; cf < _bfields; ++cf)
{
wattron(form_win, COLOR_PAIR(3));
mvwprintw(form_win, labels[ca], 3, _bv[cf].c_str());
wattroff(form_win, COLOR_PAIR(3));
ca++;
}
wrefresh(form_win);
print_in_middle(form_win, 3, 0, _cols, string("Use the TAB key to switch between fields."), COLOR_PAIR(1));
print_in_middle(form_win, 4, 0, _cols, string("Use UP, DOWN arrow keys to switch between lines."), COLOR_PAIR(1));
print_in_middle(form_win, 6, 0, _cols, string("Press ENTER to submit the form and ESC to cancel."), COLOR_PAIR(1));
refresh();
while ((ch = wgetch(form_win)) != 27 && ch != 13) // KEY_F(1))
{
switch (ch)
{
case 9: // Tab
form_driver(form, REQ_NEXT_FIELD);
form_driver(form, REQ_END_LINE);
break;
case KEY_DOWN:
form_driver(form, REQ_NEXT_LINE);
break;
case KEY_UP:
form_driver(form, REQ_PREV_LINE);
break;
case KEY_LEFT:
form_driver(form, REQ_LEFT_CHAR);
break;
case KEY_RIGHT:
form_driver(form, REQ_RIGHT_CHAR);
break;
case KEY_BACKSPACE: // Backspace
case KEY_DC:
case KEY_DL:
case 127:
form_driver(form, REQ_DEL_PREV);
wrefresh(form_win);
break;
case KEY_ENTER: // Enter
case 13:
case 27: // Esc
break;
default:
form_driver(form, ch);
break;
}
}
if (form_driver(form, REQ_VALIDATION) != E_OK)
cwarn << "Validation error";
int fi;
for (fi = 0; fi < maxfields; ++fi)
free_field(field[fi]);
free_form(form);
unpost_form(form);
echo();
timeout(30000);
delwin(form_win);
if (ch == 13)
for (int fi = 0; fi < maxfields; ++fi)
vs.push_back(field_buffer(field[fi], 0));
return vs;
}