int main(int argc, char** argv) { // Init defaults Defaults::get(); /// Operating mode. OperationMode mode = OperationMode::Node; string dbPath; // unsigned prime = 0; // bool yesIReallyKnowWhatImDoing = false; /// File name for import/export. string filename; bool safeImport = false; /// Hashes/numbers for export range. string exportFrom = "1"; string exportTo = "latest"; Format exportFormat = Format::Binary; /// General params for Node operation NodeMode nodeMode = NodeMode::Full; bool interactive = false; #if ETH_JSONRPC || !ETH_TRUE int jsonRPCURL = -1; #endif string jsonAdmin; string genesisJSON; dev::eth::Network releaseNetwork = c_network; u256 gasFloor = UndefinedU256; string privateChain; bool upnp = true; WithExisting withExisting = WithExisting::Trust; string sentinel; /// Networking params. string clientName; string listenIP; unsigned short listenPort = 30303; string publicIP; string remoteHost; unsigned short remotePort = 30303; HostPeerPreferences hprefs; unsigned peers = hprefs.idealPeerCount; unsigned peerStretch = hprefs.stretchPeerCount; bool bootstrap = false; bool disableDiscovery = false; bool pinning = false; bool enableDiscovery = false; bool noPinning = false; unsigned networkId = (unsigned)-1; /// Mining params unsigned mining = 0; bool forceMining = false; bool mineOnWrongChain = false; Address signingKey; Address sessionKey; Address beneficiary = signingKey; strings presaleImports; /// Structured logging params bool structuredLogging = false; string structuredLoggingFormat = "%Y-%m-%dT%H:%M:%S"; string structuredLoggingURL; /// Transaction params TransactionPriority priority = TransactionPriority::Medium; // double etherPrice = 30.679; // double blockFees = 15.0; u256 askPrice = c_defaultGasPrice; u256 bidPrice = c_defaultGasPrice; // javascript console bool useConsole = false; /// Wallet password stuff string masterPassword; /// Whisper bool useWhisper = false; string configFile = getDataDir() + "/config.rlp"; bytes b = contents(configFile); strings passwordsToNote; Secrets toImport; if (b.size()) { try { RLP config(b); signingKey = config[0].toHash<Address>(); beneficiary = config[1].toHash<Address>(); } catch (...) {} } MinerCLI m(MinerCLI::OperationMode::None); for (int i = 1; i < argc; ++i) { string arg = argv[i]; if (m.interpretOption(i, argc, argv)) {} else if (arg == "--listen-ip" && i + 1 < argc) listenIP = argv[++i]; else if ((arg == "-l" || arg == "--listen" || arg == "--listen-port") && i + 1 < argc) { if (arg == "-l") cerr << "-l is DEPRECATED. It will be removed for the Frontier. Use --listen-port instead." << endl; listenPort = (short)atoi(argv[++i]); } else if ((arg == "-u" || arg == "--public-ip" || arg == "--public") && i + 1 < argc) { if (arg == "-u") cerr << "-u is DEPRECATED. It will be removed for the Frontier. Use --public-ip instead." << endl; publicIP = argv[++i]; } else if ((arg == "-r" || arg == "--remote") && i + 1 < argc) remoteHost = argv[++i]; else if ((arg == "-p" || arg == "--port") && i + 1 < argc) { if (arg == "-p") cerr << "-p is DEPRECATED. It will be removed for the Frontier. Use --port instead (or place directly as host:port)." << endl; remotePort = (short)atoi(argv[++i]); } else if (arg == "--password" && i + 1 < argc) passwordsToNote.push_back(argv[++i]); else if (arg == "--master" && i + 1 < argc) masterPassword = argv[++i]; else if ((arg == "-I" || arg == "--import") && i + 1 < argc) { mode = OperationMode::Import; filename = argv[++i]; } else if (arg == "--dont-check") safeImport = true; else if ((arg == "-E" || arg == "--export") && i + 1 < argc) { mode = OperationMode::Export; filename = argv[++i]; } /* else if (arg == "--prime" && i + 1 < argc) try { prime = stoi(argv[++i]); } catch (...) { cerr << "Bad " << arg << " option: " << argv[i] << endl; return -1; } else if (arg == "--yes-i-really-know-what-im-doing") yesIReallyKnowWhatImDoing = true; */ else if (arg == "--sentinel" && i + 1 < argc) sentinel = argv[++i]; else if (arg == "--mine-on-wrong-chain") mineOnWrongChain = true; else if (arg == "--format" && i + 1 < argc) { string m = argv[++i]; if (m == "binary") exportFormat = Format::Binary; else if (m == "hex") exportFormat = Format::Hex; else if (m == "human") exportFormat = Format::Human; else { cerr << "Bad " << arg << " option: " << m << endl; return -1; } } else if (arg == "--to" && i + 1 < argc) exportTo = argv[++i]; else if (arg == "--from" && i + 1 < argc) exportFrom = argv[++i]; else if (arg == "--only" && i + 1 < argc) exportTo = exportFrom = argv[++i]; else if ((arg == "-n" || arg == "-u" || arg == "--upnp") && i + 1 < argc) { if (arg == "-n") cerr << "-n is DEPRECATED. It will be removed for the Frontier. Use --upnp instead." << endl; string m = argv[++i]; if (isTrue(m)) upnp = true; else if (isFalse(m)) upnp = false; else { cerr << "Bad " << arg << " option: " << m << endl; return -1; } } else if (arg == "--network-id" && i + 1 < argc) try { networkId = stol(argv[++i]); } catch (...) { cerr << "Bad " << arg << " option: " << argv[i] << endl; return -1; } else if (arg == "--private" && i + 1 < argc) try { privateChain = argv[++i]; } catch (...) { cerr << "Bad " << arg << " option: " << argv[i] << endl; return -1; } else if (arg == "-K" || arg == "--kill-blockchain" || arg == "--kill") withExisting = WithExisting::Kill; else if (arg == "-R" || arg == "--rebuild") withExisting = WithExisting::Verify; else if (arg == "-R" || arg == "--rescue") withExisting = WithExisting::Rescue; else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc) { if (arg == "-c") cerr << "-c is DEPRECATED. It will be removed for the Frontier. Use --client-name instead." << endl; clientName = argv[++i]; } else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc) try { beneficiary = h160(fromHex(argv[++i], WhenError::Throw)); } catch (BadHexCharacter&) { cerr << "Bad hex in " << arg << " option: " << argv[i] << endl; return -1; } catch (...) { cerr << "Bad " << arg << " option: " << argv[i] << endl; return -1; } else if ((arg == "-s" || arg == "--import-secret") && i + 1 < argc) { Secret s(fromHex(argv[++i])); toImport.emplace_back(s); signingKey = toAddress(s); } else if ((arg == "-S" || arg == "--import-session-secret") && i + 1 < argc) { Secret s(fromHex(argv[++i])); toImport.emplace_back(s); sessionKey = toAddress(s); } else if ((arg == "--sign-key") && i + 1 < argc) sessionKey = Address(fromHex(argv[++i])); else if ((arg == "--session-sign-key") && i + 1 < argc) sessionKey = Address(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 == "--structured-logging-url" && i + 1 < argc) { structuredLogging = true; structuredLoggingURL = argv[++i]; } else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc) dbPath = argv[++i]; else if ((arg == "--genesis-json" || arg == "--genesis") && i + 1 < argc) { try { genesisJSON = contentsString(argv[++i]); } catch (...) { cerr << "Bad " << arg << " option: " << argv[i] << endl; return -1; } } else if (arg == "--frontier") releaseNetwork = eth::Network::Frontier; else if (arg == "--gas-floor" && i + 1 < argc) gasFloor = u256(argv[++i]); else if (arg == "--olympic") releaseNetwork = eth::Network::Olympic; /* 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 == "--ask" && i + 1 < argc) { try { askPrice = u256(argv[++i]); } catch (...) { cerr << "Bad " << arg << " option: " << argv[i] << endl; return -1; } } else if (arg == "--bid" && i + 1 < argc) { try { bidPrice = u256(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 " << arg << " option: " << m << endl; return -1; } } else if (arg == "-b" || arg == "--bootstrap") bootstrap = true; else if (arg == "--no-discovery") disableDiscovery = true; else if (arg == "--pin") pinning = true; else if (arg == "--hermit") pinning = disableDiscovery = true; else if (arg == "--sociable") noPinning = enableDiscovery = true; else if (arg == "--import-presale" && i + 1 < argc) presaleImports.push_back(argv[++i]); else if (arg == "-f" || arg == "--force-mining") forceMining = true; else if (arg == "--old-interactive") interactive = true; #if ETH_JSONRPC || !ETH_TRUE else if ((arg == "-j" || arg == "--json-rpc")) jsonRPCURL = jsonRPCURL == -1 ? SensibleHttpPort : jsonRPCURL; else if (arg == "--json-rpc-port" && i + 1 < argc) jsonRPCURL = atoi(argv[++i]); else if (arg == "--json-admin" && i + 1 < argc) jsonAdmin = argv[++i]; #endif #if ETH_JSCONSOLE || !ETH_TRUE else if (arg == "-i" || arg == "--interactive" || arg == "--console") useConsole = true; #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 == "--peer-stretch" && i + 1 < argc) peerStretch = atoi(argv[++i]); else if ((arg == "-o" || arg == "--mode") && i + 1 < argc) { string m = argv[++i]; if (m == "full") nodeMode = NodeMode::Full; else if (m == "peer") nodeMode = NodeMode::PeerServer; else { cerr << "Unknown mode: " << m << endl; return -1; } } #if ETH_EVMJIT else if (arg == "--vm" && i + 1 < argc) { string vmKind = argv[++i]; if (vmKind == "interpreter") VMFactory::setKind(VMKind::Interpreter); else if (vmKind == "jit") VMFactory::setKind(VMKind::JIT); else if (vmKind == "smart") VMFactory::setKind(VMKind::Smart); else { cerr << "Unknown VM kind: " << vmKind << endl; return -1; } } #endif else if (arg == "--shh") useWhisper = true; else if (arg == "-h" || arg == "--help") help(); else if (arg == "-V" || arg == "--version") version(); else { cerr << "Invalid argument: " << arg << endl; exit(-1); } } // Set up all the chain config stuff. resetNetwork(releaseNetwork); if (!privateChain.empty()) CanonBlockChain<Ethash>::forceGenesisExtraData(sha3(privateChain).asBytes()); if (!genesisJSON.empty()) CanonBlockChain<Ethash>::setGenesis(genesisJSON); if (gasFloor != UndefinedU256) c_gasFloorTarget = gasFloor; if (networkId == (unsigned)-1) networkId = (unsigned)c_network; if (g_logVerbosity > 0) { cout << EthGrayBold "(++)Ethereum" EthReset << endl; if (c_network == eth::Network::Olympic) cout << "Welcome to Olympic!" << endl; else if (c_network == eth::Network::Frontier) cout << "Welcome to the " EthMaroonBold "Frontier" EthReset "!" << endl; } m.execute(); KeyManager keyManager; for (auto const& s: passwordsToNote) keyManager.notePassword(s); writeFile(configFile, rlpList(signingKey, beneficiary)); if (sessionKey) signingKey = sessionKey; if (!clientName.empty()) clientName += "/"; string logbuf; std::string additional; if (interactive) g_logPost = [&](std::string const& a, char const*){ static SpinLock s_lock; SpinGuard l(s_lock); if (g_silence) logbuf += a + "\n"; else cout << "\r \r" << a << endl << additional << flush; // helpful to use OutputDebugString on windows #ifdef _WIN32 { OutputDebugStringA(a.data()); OutputDebugStringA("\n"); } #endif }; auto getPassword = [&](string const& prompt){ bool s = g_silence; g_silence = true; cout << endl; string ret = dev::getPassword(prompt); g_silence = s; return ret; }; auto getAccountPassword = [&](Address const& a){ return getPassword("Enter password for address " + keyManager.accountName(a) + " (" + a.abridged() + "; hint:" + keyManager.passwordHint(a) + "): "); }; StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL); auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP, listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); netPrefs.discovery = (privateChain.empty() && !disableDiscovery) || enableDiscovery; netPrefs.pin = (pinning || !privateChain.empty()) && !noPinning; auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); auto caps = useWhisper ? set<string>{"eth", "shh"} : set<string>{"eth"}; dev::WebThreeDirect web3( WebThreeDirect::composeClientVersion("++eth", clientName), dbPath, withExisting, nodeMode == NodeMode::Full ? caps : set<string>(), netPrefs, &nodesState); web3.ethereum()->setMineOnBadChain(mineOnWrongChain); web3.ethereum()->setSentinel(sentinel); auto toNumber = [&](string const& s) -> unsigned { if (s == "latest") return web3.ethereum()->number(); if (s.size() == 64 || (s.size() == 66 && s.substr(0, 2) == "0x")) return web3.ethereum()->blockChain().number(h256(s)); try { return stol(s); } catch (...) { cerr << "Bad block number/hash option: " << s << endl; exit(-1); } }; if (mode == OperationMode::Export) { ofstream fout(filename, std::ofstream::binary); ostream& out = (filename.empty() || filename == "--") ? cout : fout; unsigned last = toNumber(exportTo); for (unsigned i = toNumber(exportFrom); i <= last; ++i) { bytes block = web3.ethereum()->blockChain().block(web3.ethereum()->blockChain().numberHash(i)); switch (exportFormat) { case Format::Binary: out.write((char const*)block.data(), block.size()); break; case Format::Hex: out << toHex(block) << endl; break; case Format::Human: out << RLP(block) << endl; break; default:; } } return 0; } if (mode == OperationMode::Import) { ifstream fin(filename, std::ifstream::binary); istream& in = (filename.empty() || filename == "--") ? cin : fin; unsigned alreadyHave = 0; unsigned good = 0; unsigned futureTime = 0; unsigned unknownParent = 0; unsigned bad = 0; chrono::steady_clock::time_point t = chrono::steady_clock::now(); double last = 0; unsigned lastImported = 0; unsigned imported = 0; while (in.peek() != -1) { bytes block(8); in.read((char*)block.data(), 8); block.resize(RLP(block, RLP::LaissezFaire).actualSize()); in.read((char*)block.data() + 8, block.size() - 8); switch (web3.ethereum()->queueBlock(block, safeImport)) { case ImportResult::Success: good++; break; case ImportResult::AlreadyKnown: alreadyHave++; break; case ImportResult::UnknownParent: unknownParent++; break; case ImportResult::FutureTimeUnknown: unknownParent++; futureTime++; break; case ImportResult::FutureTimeKnown: futureTime++; break; default: bad++; break; } // sync chain with queue tuple<ImportRoute, bool, unsigned> r = web3.ethereum()->syncQueue(10); imported += get<2>(r); double e = chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - t).count() / 1000.0; if ((unsigned)e >= last + 10) { auto i = imported - lastImported; auto d = e - last; cout << i << " more imported at " << (round(i * 10 / d) / 10) << " blocks/s. " << imported << " imported in " << e << " seconds at " << (round(imported * 10 / e) / 10) << " blocks/s (#" << web3.ethereum()->number() << ")" << endl; last = (unsigned)e; lastImported = imported; // cout << web3.ethereum()->blockQueueStatus() << endl; } } while (web3.ethereum()->blockQueue().items().first + web3.ethereum()->blockQueue().items().second > 0) { this_thread::sleep_for(chrono::seconds(1)); web3.ethereum()->syncQueue(100000); } double e = chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - t).count() / 1000.0; cout << imported << " imported in " << e << " seconds at " << (round(imported * 10 / e) / 10) << " blocks/s (#" << web3.ethereum()->number() << ")" << endl; return 0; } /* if (c_network == eth::Network::Frontier && !yesIReallyKnowWhatImDoing) { auto pd = contents(getDataDir() + "primes"); unordered_set<unsigned> primes = RLP(pd).toUnorderedSet<unsigned>(); while (true) { if (!prime) try { prime = stoi(getPassword("To enter the Frontier, enter a 6 digit prime that you have not entered before: ")); } catch (...) {} if (isPrime(prime) && !primes.count(prime)) break; prime = 0; } primes.insert(prime); writeFile(getDataDir() + "primes", rlp(primes)); } */ if (keyManager.exists()) { if (masterPassword.empty() || !keyManager.load(masterPassword)) while (true) { masterPassword = getPassword("Please enter your MASTER password: "******"The password you entered is incorrect. If you have forgotten your password, and you wish to start afresh, manually remove the file: " + getDataDir("ethereum") + "/keys.info" << endl; } } else { while (masterPassword.empty()) { masterPassword = getPassword("Please enter a MASTER password to protect your key store (make it strong!): "); string confirm = getPassword("Please confirm the password by entering it again: "); if (masterPassword != confirm) { cout << "Passwords were different. Try again." << endl; masterPassword.clear(); } } keyManager.create(masterPassword); } for (auto const& presale: presaleImports) importPresale(keyManager, presale, [&](){ return getPassword("Enter your wallet password for " + presale + ": "); }); for (auto const& s: toImport) { keyManager.import(s, "Imported key (UNSAFE)"); if (!signingKey) signingKey = toAddress(s); } if (keyManager.accounts().empty()) { h128 uuid = keyManager.import(Secret::random(), "Default key"); if (!beneficiary) beneficiary = keyManager.address(uuid); if (!signingKey) signingKey = keyManager.address(uuid); writeFile(configFile, rlpList(signingKey, beneficiary)); } cout << ethCredits(); web3.setIdealPeerCount(peers); web3.setPeerStretch(peerStretch); // std::shared_ptr<eth::BasicGasPricer> gasPricer = make_shared<eth::BasicGasPricer>(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000)); std::shared_ptr<eth::TrivialGasPricer> gasPricer = make_shared<eth::TrivialGasPricer>(askPrice, bidPrice); eth::Client* c = nodeMode == NodeMode::Full ? web3.ethereum() : nullptr; StructuredLogger::starting(WebThreeDirect::composeClientVersion("++eth", clientName), dev::Version); if (c) { c->setGasPricer(gasPricer); c->setForceMining(forceMining); // TODO: expose sealant interface. c->setShouldPrecomputeDAG(m.shouldPrecompute()); c->setTurboMining(m.minerType() == MinerCLI::MinerType::GPU); c->setBeneficiary(beneficiary); c->setNetworkId(networkId); } cout << "Transaction Signer: " << signingKey << endl; cout << "Mining Benefactor: " << beneficiary << endl; if (bootstrap || !remoteHost.empty() || disableDiscovery) { web3.startNetwork(); cout << "Node ID: " << web3.enode() << endl; } else cout << "Networking disabled. To start, use netstart or pass -b or a remote host." << endl; if (useConsole && jsonRPCURL == -1) jsonRPCURL = SensibleHttpPort; #if ETH_JSONRPC || !ETH_TRUE shared_ptr<dev::WebThreeStubServer> jsonrpcServer; unique_ptr<jsonrpc::AbstractServerConnector> jsonrpcConnector; if (jsonRPCURL > -1) { jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonRPCURL, "", "", SensibleHttpThreads)); jsonrpcServer = make_shared<dev::WebThreeStubServer>(*jsonrpcConnector.get(), web3, make_shared<SimpleAccountHolder>([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector<KeyPair>(), keyManager, *gasPricer); jsonrpcServer->StartListening(); if (jsonAdmin.empty()) jsonAdmin = jsonrpcServer->newSession(SessionPermissions{{Privilege::Admin}}); else jsonrpcServer->addSession(jsonAdmin, SessionPermissions{{Privilege::Admin}}); cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl; writeFile(getDataDir("web3") + "/session.key", jsonAdmin); writeFile(getDataDir("web3") + "/session.url", "http://localhost:" + toString(jsonRPCURL)); } #endif if (bootstrap) for (auto const& i: Host::pocHosts()) web3.requirePeer(i.first, i.second); if (!remoteHost.empty()) web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort)); signal(SIGABRT, &sighandler); signal(SIGTERM, &sighandler); signal(SIGINT, &sighandler); if (interactive) interactiveMode(c, gasPricer, web3, keyManager, logbuf, additional, getPassword, getAccountPassword, netPrefs, beneficiary, signingKey, priority); else if (c) { unsigned n = c->blockChain().details().number; if (mining) c->startMining(); if (useConsole) { #if ETH_JSCONSOLE || !ETH_TRUE JSLocalConsole console; shared_ptr<dev::WebThreeStubServer> rpcServer = make_shared<dev::WebThreeStubServer>(*console.connector(), web3, make_shared<SimpleAccountHolder>([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector<KeyPair>(), keyManager, *gasPricer); string sessionKey = rpcServer->newSession(SessionPermissions{{Privilege::Admin}}); console.eval("web3.admin.setSessionKey('" + sessionKey + "')"); while (!g_exit) { console.readExpression(); stopMiningAfterXBlocks(c, n, mining); } rpcServer->StopListening(); #endif } else while (!g_exit) stopMiningAfterXBlocks(c, n, mining); } else while (!g_exit) this_thread::sleep_for(chrono::milliseconds(1000)); #if ETH_JSONRPC if (jsonrpcServer.get()) jsonrpcServer->StopListening(); #endif StructuredLogger::stopping(WebThreeDirect::composeClientVersion("++eth", clientName), dev::Version); auto netData = web3.saveNetwork(); if (!netData.empty()) writeFile((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp", netData); return 0; }
int main(int argc, char** argv) { setDefaultOrCLocale(); // Init secp256k1 context by calling one of the functions. toPublic(Secret{}); // Init defaults Ethash::init(); NoProof::init(); /// Operating mode. OperationMode mode = OperationMode::Node; /// File name for import/export. string filename; bool safeImport = false; /// Hashes/numbers for export range. string exportFrom = "1"; string exportTo = "latest"; Format exportFormat = Format::Binary; bool ipc = true; string jsonAdmin; ChainParams chainParams; bool upnp = true; WithExisting withExisting = WithExisting::Trust; /// Networking params. string listenIP; unsigned short listenPort = dev::p2p::c_defaultListenPort; string publicIP; string remoteHost; unsigned short remotePort = dev::p2p::c_defaultListenPort; unsigned peers = 11; unsigned peerStretch = 7; std::map<p2p::NodeID, pair<NodeIPEndpoint, bool>> preferredNodes; bool bootstrap = true; bool disableDiscovery = false; bool allowLocalDiscovery = false; static const unsigned NoNetworkID = (unsigned)-1; unsigned networkID = NoNetworkID; /// Mining params unsigned mining = 0; Address author; strings presaleImports; bytes extraData; /// Transaction params // TransactionPriority priority = TransactionPriority::Medium; // double etherPrice = 30.679; // double blockFees = 15.0; u256 askPrice = 0; u256 bidPrice = DefaultGasPrice; bool alwaysConfirm = true; /// Wallet password stuff string masterPassword; bool masterSet = false; /// Whisper bool testingMode = false; fs::path configFile = getDataDir() / fs::path("config.rlp"); bytes b = contents(configFile); strings passwordsToNote; Secrets toImport; if (b.size()) { try { RLP config(b); author = config[1].toHash<Address>(); } catch (...) {} } if (argc > 1 && (string(argv[1]) == "wallet" || string(argv[1]) == "account")) { AccountManager accountm; return !accountm.execute(argc, argv); } MinerCLI miner(MinerCLI::OperationMode::None); bool listenSet = false; bool chainConfigIsSet = false; fs::path configPath; string configJSON; po::options_description clientDefaultMode("CLIENT MODE (default)", c_lineWidth); auto addClientOption = clientDefaultMode.add_options(); addClientOption("mainnet", "Use the main network protocol"); addClientOption("ropsten", "Use the Ropsten testnet"); addClientOption("test", "Testing mode; disable PoW and provide test rpc interface"); addClientOption("config", po::value<string>()->value_name("<file>"), "Configure specialised blockchain using given JSON information\n"); addClientOption("ipc", "Enable IPC server (default: on)"); addClientOption("ipcpath", po::value<string>()->value_name("<path>"), "Set .ipc socket path (default: data directory)"); addClientOption("no-ipc", "Disable IPC server"); addClientOption("admin", po::value<string>()->value_name("<password>"), "Specify admin session key for JSON-RPC (default: auto-generated and printed at " "start-up)"); addClientOption("kill,K", "Kill the blockchain first"); addClientOption("rebuild,R", "Rebuild the blockchain from the existing database"); addClientOption("rescue", "Attempt to rescue a corrupt database\n"); addClientOption("import-presale", po::value<string>()->value_name("<file>"), "Import a pre-sale key; you'll need to specify the password to this key"); addClientOption("import-secret,s", po::value<string>()->value_name("<secret>"), "Import a secret key into the key store"); addClientOption("import-session-secret,S", po::value<string>()->value_name("<secret>"), "Import a secret session into the key store"); addClientOption("master", po::value<string>()->value_name("<password>"), "Give the master password for the key store; use --master \"\" to show a prompt"); addClientOption("password", po::value<string>()->value_name("<password>"), "Give a password for a private key\n"); po::options_description clientTransacting("CLIENT TRANSACTING", c_lineWidth); auto addTransactingOption = clientTransacting.add_options(); addTransactingOption("ask", po::value<u256>()->value_name("<wei>"), ("Set the minimum ask gas price under which no transaction will be mined (default: " + toString(DefaultGasPrice) + ")") .c_str()); addTransactingOption("bid", po::value<u256>()->value_name("<wei>"), ("Set the bid gas price to pay for transactions (default: " + toString(DefaultGasPrice) + ")") .c_str()); addTransactingOption("unsafe-transactions", "Allow all transactions to proceed without verification; EXTREMELY UNSAFE\n"); po::options_description clientMining("CLIENT MINING", c_lineWidth); auto addMininigOption = clientMining.add_options(); addMininigOption("address,a", po::value<Address>()->value_name("<addr>"), "Set the author (mining payout) address (default: auto)"); addMininigOption("mining,m", po::value<string>()->value_name("<on/off/number>"), "Enable mining; optionally for a specified number of blocks (default: off)"); addMininigOption("extra-data", po::value<string>(), "Set extra data for the sealed blocks\n"); po::options_description clientNetworking("CLIENT NETWORKING", c_lineWidth); auto addNetworkingOption = clientNetworking.add_options(); addNetworkingOption("bootstrap,b", "Connect to the default Ethereum peer servers (default unless --no-discovery used)"); addNetworkingOption("no-bootstrap", "Do not connect to the default Ethereum peer servers (default only when --no-discovery is " "used)"); addNetworkingOption("peers,x", po::value<int>()->value_name("<number>"), "Attempt to connect to a given number of peers (default: 11)"); addNetworkingOption("peer-stretch", po::value<int>()->value_name("<number>"), "Give the accepted connection multiplier (default: 7)"); addNetworkingOption("public-ip", po::value<string>()->value_name("<ip>"), "Force advertised public IP to the given IP (default: auto)"); addNetworkingOption("listen-ip", po::value<string>()->value_name("<ip>(:<port>)"), "Listen on the given IP for incoming connections (default: 0.0.0.0)"); addNetworkingOption("listen", po::value<unsigned short>()->value_name("<port>"), "Listen on the given port for incoming connections (default: 30303)"); addNetworkingOption("remote,r", po::value<string>()->value_name("<host>(:<port>)"), "Connect to the given remote host (default: none)"); addNetworkingOption("port", po::value<short>()->value_name("<port>"), "Connect to the given remote port (default: 30303)"); addNetworkingOption("network-id", po::value<unsigned>()->value_name("<n>"), "Only connect to other hosts with this network id"); addNetworkingOption("allow-local-discovery", po::bool_switch(&allowLocalDiscovery), "Include local addresses in the discovery process. Used for testing purposes."); #if ETH_MINIUPNPC addNetworkingOption( "upnp", po::value<string>()->value_name("<on/off>"), "Use UPnP for NAT (default: on)"); #endif stringstream peersetDescriptionStream; peersetDescriptionStream << "Comma delimited list of peers; element format: type:enode://publickey@ipAddress[:port[?discport=port]]\n" " Types:\n" " default Attempt connection when no other peers are available and pinning is disabled\n" " required Keep connected at all times\n\n" " Ports:\n" " The first port argument is the tcp port used for direct communication among peers. If the second port\n" " argument isn't supplied, the first port argument will also be the udp port used for node discovery.\n" " If neither the first nor second port arguments are supplied, a default port of " << dev::p2p::c_defaultListenPort << " will be used for\n" " both peer communication and node discovery."; string peersetDescription = peersetDescriptionStream.str(); addNetworkingOption("peerset", po::value<string>()->value_name("<list>"), peersetDescription.c_str()); addNetworkingOption("no-discovery", "Disable node discovery; implies --no-bootstrap"); addNetworkingOption("pin", "Only accept or connect to trusted peers\n"); std::string snapshotPath; po::options_description importExportMode("IMPORT/EXPORT MODES", c_lineWidth); auto addImportExportOption = importExportMode.add_options(); addImportExportOption( "import,I", po::value<string>()->value_name("<file>"), "Import blocks from file"); addImportExportOption( "export,E", po::value<string>()->value_name("<file>"), "Export blocks to file"); addImportExportOption("from", po::value<string>()->value_name("<n>"), "Export only from block n; n may be a decimal, a '0x' prefixed hash, or 'latest'"); addImportExportOption("to", po::value<string>()->value_name("<n>"), "Export only to block n (inclusive); n may be a decimal, a '0x' prefixed hash, or " "'latest'"); addImportExportOption("only", po::value<string>()->value_name("<n>"), "Equivalent to --export-from n --export-to n"); addImportExportOption( "format", po::value<string>()->value_name("<binary/hex/human>"), "Set export format"); addImportExportOption("dont-check", "Prevent checking some block aspects. Faster importing, but to apply only when the data is " "known to be valid"); addImportExportOption("download-snapshot", po::value<string>(&snapshotPath)->value_name("<path>"), "Download Parity Warp Sync snapshot data to the specified path"); addImportExportOption("import-snapshot", po::value<string>()->value_name("<path>"), "Import blockchain and state data from the Parity Warp Sync snapshot\n"); std::string const logChannels = "block blockhdr bq chain client debug discov error ethcap exec host impolite info net " "overlaydb p2pcap peer rlpx rpc snap statedb sync timer tq trace vmtrace warn warpcap watch"; LoggingOptions loggingOptions; po::options_description loggingProgramOptions( createLoggingProgramOptions(c_lineWidth, loggingOptions, logChannels)); po::options_description generalOptions("GENERAL OPTIONS", c_lineWidth); auto addGeneralOption = generalOptions.add_options(); addGeneralOption("data-dir,d", po::value<string>()->value_name("<path>"), ("Load configuration files and keystore from path (default: " + getDataDir().string() + ")").c_str()); addGeneralOption("version,V", "Show the version and exit"); addGeneralOption("help,h", "Show this help message and exit\n"); po::options_description vmOptions = vmProgramOptions(c_lineWidth); po::options_description dbOptions = db::databaseProgramOptions(c_lineWidth); po::options_description minerOptions = MinerCLI::createProgramOptions(c_lineWidth); po::options_description allowedOptions("Allowed options"); allowedOptions.add(clientDefaultMode) .add(clientTransacting) .add(clientMining) .add(minerOptions) .add(clientNetworking) .add(importExportMode) .add(vmOptions) .add(dbOptions) .add(loggingProgramOptions) .add(generalOptions); po::variables_map vm; try { po::parsed_options parsed = po::parse_command_line(argc, argv, allowedOptions); po::store(parsed, vm); po::notify(vm); } catch (po::error const& e) { cerr << e.what() << "\n"; return AlethErrors::ArgumentProcessingFailure; } miner.interpretOptions(vm); if (vm.count("import-snapshot")) { mode = OperationMode::ImportSnapshot; filename = vm["import-snapshot"].as<string>(); } if (vm.count("version")) { version(); return AlethErrors::Success; } if (vm.count("test")) { testingMode = true; disableDiscovery = true; bootstrap = false; } if (vm.count("peers")) peers = vm["peers"].as<int>(); if (vm.count("peer-stretch")) peerStretch = vm["peer-stretch"].as<int>(); if (vm.count("peerset")) { string peersetStr = vm["peerset"].as<string>(); vector<string> peersetList; boost::split(peersetList, peersetStr, boost::is_any_of(",")); bool parsingError = false; const string peerPattern = "^(default|required):(.*)"; regex rx(peerPattern); smatch match; for (auto const& p : peersetList) { if (regex_match(p, match, rx)) { bool required = match.str(1) == "required"; NodeSpec ns(match.str(2)); if (ns.isValid()) preferredNodes[ns.id()] = make_pair(ns.nodeIPEndpoint(), required); else { parsingError = true; break; } } else { parsingError = true; break; } } if (parsingError) { cerr << "Unrecognized peerset: " << peersetStr << "\n"; return AlethErrors::UnrecognizedPeerset; } } if (vm.count("import-presale")) presaleImports.push_back(vm["import-presale"].as<string>()); if (vm.count("admin")) jsonAdmin = vm["admin"].as<string>(); if (vm.count("ipc")) ipc = true; if (vm.count("no-ipc")) ipc = false; if (vm.count("mining")) { string m = vm["mining"].as<string>(); if (isTrue(m)) mining = ~(unsigned)0; else if (isFalse(m)) mining = 0; else try { mining = stoi(m); } catch (...) { cerr << "Unknown --mining option: " << m << "\n"; return AlethErrors::UnknownMiningOption; } } if (vm.count("bootstrap")) bootstrap = true; if (vm.count("no-bootstrap")) bootstrap = false; if (vm.count("no-discovery")) { disableDiscovery = true; bootstrap = false; } if (vm.count("unsafe-transactions")) alwaysConfirm = false; if (vm.count("data-dir")) setDataDir(vm["data-dir"].as<string>()); if (vm.count("ipcpath")) setIpcPath(vm["ipcpath"].as<string>()); if (vm.count("config")) { try { configPath = vm["config"].as<string>(); configJSON = contentsString(configPath.string()); if (configJSON.empty()) { cerr << "Config file not found or empty (" << configPath.string() << ")\n"; return AlethErrors::ConfigFileEmptyOrNotFound; } } catch (...) { cerr << "Bad --config option: " << vm["config"].as<string>() << "\n"; return AlethErrors::BadConfigOption; } } if (vm.count("extra-data")) { try { extraData = fromHex(vm["extra-data"].as<string>()); } catch (...) { cerr << "Bad " << "--extra-data" << " option: " << vm["extra-data"].as<string>() << "\n"; return AlethErrors::BadExtraDataOption; } } if (vm.count("mainnet")) { chainParams = ChainParams(genesisInfo(eth::Network::MainNetwork), genesisStateRoot(eth::Network::MainNetwork)); chainConfigIsSet = true; } if (vm.count("ropsten")) { chainParams = ChainParams(genesisInfo(eth::Network::Ropsten), genesisStateRoot(eth::Network::Ropsten)); chainConfigIsSet = true; } if (vm.count("ask")) { try { askPrice = vm["ask"].as<u256>(); } catch (...) { cerr << "Bad --ask option: " << vm["ask"].as<string>() << "\n"; return AlethErrors::BadAskOption; } } if (vm.count("bid")) { try { bidPrice = vm["bid"].as<u256>(); } catch (...) { cerr << "Bad --bid option: " << vm["bid"].as<string>() << "\n"; return AlethErrors::BadBidOption; } } if (vm.count("listen-ip")) { listenIP = vm["listen-ip"].as<string>(); listenSet = true; } if (vm.count("listen")) { listenPort = vm["listen"].as<unsigned short>(); listenSet = true; } if (vm.count("public-ip")) { publicIP = vm["public-ip"].as<string>(); } if (vm.count("remote")) { string host = vm["remote"].as<string>(); string::size_type found = host.find_first_of(':'); if (found != std::string::npos) { remoteHost = host.substr(0, found); remotePort = (short)atoi(host.substr(found + 1, host.length()).c_str()); } else remoteHost = host; } if (vm.count("port")) { remotePort = vm["port"].as<short>(); } if (vm.count("import")) { mode = OperationMode::Import; filename = vm["import"].as<string>(); } if (vm.count("export")) { mode = OperationMode::Export; filename = vm["export"].as<string>(); } if (vm.count("password")) passwordsToNote.push_back(vm["password"].as<string>()); if (vm.count("master")) { masterPassword = vm["master"].as<string>(); masterSet = true; } if (vm.count("dont-check")) safeImport = true; if (vm.count("format")) { string m = vm["format"].as<string>(); if (m == "binary") exportFormat = Format::Binary; else if (m == "hex") exportFormat = Format::Hex; else if (m == "human") exportFormat = Format::Human; else { cerr << "Bad " << "--format" << " option: " << m << "\n"; return AlethErrors::BadFormatOption; } } if (vm.count("to")) exportTo = vm["to"].as<string>(); if (vm.count("from")) exportFrom = vm["from"].as<string>(); if (vm.count("only")) exportTo = exportFrom = vm["only"].as<string>(); #if ETH_MINIUPNPC if (vm.count("upnp")) { string m = vm["upnp"].as<string>(); if (isTrue(m)) upnp = true; else if (isFalse(m)) upnp = false; else { cerr << "Bad " << "--upnp" << " option: " << m << "\n"; return AlethErrors::BadUpnpOption; } } #endif if (vm.count("network-id")) try { networkID = vm["network-id"].as<unsigned>(); } catch (...) { cerr << "Bad " << "--network-id" << " option: " << vm["network-id"].as<string>() << "\n"; return AlethErrors::BadNetworkIdOption; } if (vm.count("kill")) withExisting = WithExisting::Kill; if (vm.count("rebuild")) withExisting = WithExisting::Verify; if (vm.count("rescue")) withExisting = WithExisting::Rescue; if (vm.count("address")) try { author = vm["address"].as<Address>(); } catch (BadHexCharacter&) { cerr << "Bad hex in " << "--address" << " option: " << vm["address"].as<string>() << "\n"; return AlethErrors::BadHexValueInAddressOption; } catch (...) { cerr << "Bad " << "--address" << " option: " << vm["address"].as<string>() << "\n"; return AlethErrors::BadAddressOption; } if ((vm.count("import-secret"))) { Secret s(fromHex(vm["import-secret"].as<string>())); toImport.emplace_back(s); } if (vm.count("import-session-secret")) { Secret s(fromHex(vm["import-session-secret"].as<string>())); toImport.emplace_back(s); } if (vm.count("help")) { cout << "NAME:\n" << " aleth " << Version << '\n' << "USAGE:\n" << " aleth [options]\n\n" << "WALLET USAGE:\n"; AccountManager::streamAccountHelp(cout); AccountManager::streamWalletHelp(cout); cout << clientDefaultMode << clientTransacting << clientNetworking << clientMining << minerOptions; cout << importExportMode << dbOptions << vmOptions << loggingProgramOptions << generalOptions; return AlethErrors::Success; } if (!configJSON.empty()) { try { chainParams = chainParams.loadConfig(configJSON, {}, configPath); chainConfigIsSet = true; } catch (...) { cerr << "provided configuration is not well-formatted\n"; cerr << "well-formatted sample: \n" << genesisInfo(eth::Network::MainNetworkTest) << "\n"; return AlethErrors::ConfigFileInvalid; } } setupLogging(loggingOptions); if (!chainConfigIsSet) // default to mainnet if not already set with any of `--mainnet`, `--ropsten`, `--genesis`, `--config` chainParams = ChainParams(genesisInfo(eth::Network::MainNetwork), genesisStateRoot(eth::Network::MainNetwork)); if (loggingOptions.verbosity > 0) cout << EthGrayBold "aleth, a C++ Ethereum client" EthReset << "\n"; miner.execute(); fs::path secretsPath; if (testingMode) secretsPath = boost::filesystem::path(getDataDir()) / "keystore"; else secretsPath = SecretStore::defaultPath(); KeyManager keyManager(KeyManager::defaultPath(), secretsPath); for (auto const& s: passwordsToNote) keyManager.notePassword(s); // the first value is deprecated (never used) writeFile(configFile, rlpList(author, author)); string logbuf; std::string additional; auto getPassword = [&](string const& prompt) { bool s = g_silence; g_silence = true; cout << "\n"; string ret = dev::getPassword(prompt); g_silence = s; return ret; }; auto getResponse = [&](string const& prompt, unordered_set<string> const& acceptable) { bool s = g_silence; g_silence = true; cout << "\n"; string ret; while (true) { cout << prompt; getline(cin, ret); if (acceptable.count(ret)) break; cout << "Invalid response: " << ret << "\n"; } g_silence = s; return ret; }; auto getAccountPassword = [&](Address const& a){ return getPassword("Enter password for address " + keyManager.accountName(a) + " (" + a.abridged() + "; hint:" + keyManager.passwordHint(a) + "): "); }; auto netPrefs = publicIP.empty() ? NetworkConfig(listenIP, listenPort, upnp) : NetworkConfig(publicIP, listenIP ,listenPort, upnp); netPrefs.discovery = !disableDiscovery; netPrefs.allowLocalDiscovery = allowLocalDiscovery; netPrefs.pin = vm.count("pin") != 0; auto nodesState = contents(getDataDir() / fs::path(c_networkConfigFileName)); if (testingMode) chainParams.allowFutureBlocks = true; dev::WebThreeDirect web3(WebThreeDirect::composeClientVersion("aleth"), db::databasePath(), snapshotPath, chainParams, withExisting, netPrefs, &nodesState, testingMode); if (!extraData.empty()) web3.ethereum()->setExtraData(extraData); auto toNumber = [&](string const& s) -> unsigned { if (s == "latest") return web3.ethereum()->number(); if (s.size() == 64 || (s.size() == 66 && s.substr(0, 2) == "0x")) return web3.ethereum()->blockChain().number(h256(s)); try { return stol(s); } catch (...) { cerr << "Bad block number/hash option: " << s << "\n"; return AlethErrors::BadBlockNumberHashOption; } }; if (mode == OperationMode::Export) { ofstream fout(filename, std::ofstream::binary); ostream& out = (filename.empty() || filename == "--") ? cout : fout; unsigned last = toNumber(exportTo); for (unsigned i = toNumber(exportFrom); i <= last; ++i) { bytes block = web3.ethereum()->blockChain().block(web3.ethereum()->blockChain().numberHash(i)); switch (exportFormat) { case Format::Binary: out.write((char const*)block.data(), block.size()); break; case Format::Hex: out << toHex(block) << "\n"; break; case Format::Human: out << RLP(block) << "\n"; break; default:; } } return AlethErrors::Success; } if (mode == OperationMode::Import) { ifstream fin(filename, std::ifstream::binary); istream& in = (filename.empty() || filename == "--") ? cin : fin; unsigned alreadyHave = 0; unsigned good = 0; unsigned futureTime = 0; unsigned unknownParent = 0; unsigned bad = 0; chrono::steady_clock::time_point t = chrono::steady_clock::now(); double last = 0; unsigned lastImported = 0; unsigned imported = 0; while (in.peek() != -1) { bytes block(8); in.read((char*)block.data(), 8); block.resize(RLP(block, RLP::LaissezFaire).actualSize()); in.read((char*)block.data() + 8, block.size() - 8); switch (web3.ethereum()->queueBlock(block, safeImport)) { case ImportResult::Success: good++; break; case ImportResult::AlreadyKnown: alreadyHave++; break; case ImportResult::UnknownParent: unknownParent++; break; case ImportResult::FutureTimeUnknown: unknownParent++; futureTime++; break; case ImportResult::FutureTimeKnown: futureTime++; break; default: bad++; break; } // sync chain with queue tuple<ImportRoute, bool, unsigned> r = web3.ethereum()->syncQueue(10); imported += get<2>(r); double e = chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - t).count() / 1000.0; if ((unsigned)e >= last + 10) { auto i = imported - lastImported; auto d = e - last; cout << i << " more imported at " << (round(i * 10 / d) / 10) << " blocks/s. " << imported << " imported in " << e << " seconds at " << (round(imported * 10 / e) / 10) << " blocks/s (#" << web3.ethereum()->number() << ")" << "\n"; last = (unsigned)e; lastImported = imported; } } bool moreToImport = true; while (moreToImport) { this_thread::sleep_for(chrono::seconds(1)); tie(ignore, moreToImport, ignore) = web3.ethereum()->syncQueue(100000); } double e = chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - t).count() / 1000.0; cout << imported << " imported in " << e << " seconds at " << (round(imported * 10 / e) / 10) << " blocks/s (#" << web3.ethereum()->number() << ")\n"; return AlethErrors::Success; } try { if (keyManager.exists()) { if (!keyManager.load(masterPassword) && masterSet) { while (true) { masterPassword = getPassword("Please enter your MASTER password: "******"The password you entered is incorrect. If you have forgotten your password, and you wish to start afresh, manually remove the file: " << (getDataDir("ethereum") / fs::path("keys.info")).string() << "\n"; } } } else { if (masterSet) keyManager.create(masterPassword); else keyManager.create(std::string()); } } catch(...) { cerr << "Error initializing key manager: " << boost::current_exception_diagnostic_information() << "\n"; return AlethErrors::KeyManagerInitializationFailure; } for (auto const& presale: presaleImports) importPresale(keyManager, presale, [&](){ return getPassword("Enter your wallet password for " + presale + ": "); }); for (auto const& s: toImport) { keyManager.import(s, "Imported key (UNSAFE)"); } cout << "aleth " << Version << "\n"; if (mode == OperationMode::ImportSnapshot) { try { auto stateImporter = web3.ethereum()->createStateImporter(); auto blockChainImporter = web3.ethereum()->createBlockChainImporter(); SnapshotImporter importer(*stateImporter, *blockChainImporter); auto snapshotStorage(createSnapshotStorage(filename)); importer.import(*snapshotStorage, web3.ethereum()->blockChain().genesisHash()); // continue with regular sync from the snapshot block } catch (...) { cerr << "Error during importing the snapshot: " << boost::current_exception_diagnostic_information() << endl; return AlethErrors::SnapshotImportFailure; } } web3.setIdealPeerCount(peers); web3.setPeerStretch(peerStretch); std::shared_ptr<eth::TrivialGasPricer> gasPricer = make_shared<eth::TrivialGasPricer>(askPrice, bidPrice); Client& c = *(web3.ethereum()); c.setGasPricer(gasPricer); c.setSealer(miner.minerType()); c.setAuthor(author); if (networkID != NoNetworkID) c.setNetworkId(networkID); auto renderFullAddress = [&](Address const& _a) -> std::string { return toUUID(keyManager.uuid(_a)) + " - " + _a.hex(); }; if (author) cout << "Mining Beneficiary: " << renderFullAddress(author) << "\n"; if (bootstrap || !remoteHost.empty() || !disableDiscovery || listenSet || !preferredNodes.empty()) { web3.startNetwork(); cout << "Node ID: " << web3.enode() << "\n"; } else cout << "Networking disabled. To start, use netstart or pass --bootstrap or a remote host.\n"; unique_ptr<rpc::SessionManager> sessionManager; unique_ptr<SimpleAccountHolder> accountHolder; unique_ptr<ModularServer<>> jsonrpcIpcServer; AddressHash allowedDestinations; std::function<bool(TransactionSkeleton const&, bool)> authenticator; if (testingMode) authenticator = [](TransactionSkeleton const&, bool) -> bool { return true; }; else authenticator = [&](TransactionSkeleton const& _t, bool isProxy) -> bool { // "unlockAccount" functionality is done in the AccountHolder. if (!alwaysConfirm || allowedDestinations.count(_t.to)) return true; string r = getResponse(_t.userReadable(isProxy, [&](TransactionSkeleton const& _t) -> pair<bool, string> { h256 contractCodeHash = web3.ethereum()->postState().codeHash(_t.to); if (contractCodeHash == EmptySHA3) return std::make_pair(false, std::string()); // TODO: actually figure out the natspec. we'll need the natspec database here though. return std::make_pair(true, std::string()); }, [&](Address const& _a) { return _a.hex(); } ) + "\nEnter yes/no/always (always to this address): ", {"yes", "n", "N", "no", "NO", "always"}); if (r == "always") allowedDestinations.insert(_t.to); return r == "yes" || r == "always"; }; ExitHandler exitHandler; if (ipc) { using FullServer = ModularServer< rpc::EthFace, rpc::NetFace, rpc::Web3Face, rpc::PersonalFace, rpc::AdminEthFace, rpc::AdminNetFace, rpc::DebugFace, rpc::TestFace >; sessionManager.reset(new rpc::SessionManager()); accountHolder.reset(new SimpleAccountHolder([&](){ return web3.ethereum(); }, getAccountPassword, keyManager, authenticator)); auto ethFace = new rpc::Eth(*web3.ethereum(), *accountHolder.get()); rpc::TestFace* testEth = nullptr; if (testingMode) testEth = new rpc::Test(*web3.ethereum()); jsonrpcIpcServer.reset(new FullServer( ethFace, new rpc::Net(web3), new rpc::Web3(web3.clientVersion()), new rpc::Personal(keyManager, *accountHolder, *web3.ethereum()), new rpc::AdminEth(*web3.ethereum(), *gasPricer.get(), keyManager, *sessionManager.get()), new rpc::AdminNet(web3, *sessionManager.get()), new rpc::Debug(*web3.ethereum()), testEth )); auto ipcConnector = new IpcServer("geth"); jsonrpcIpcServer->addConnector(ipcConnector); ipcConnector->StartListening(); if (jsonAdmin.empty()) jsonAdmin = sessionManager->newSession(rpc::SessionPermissions{{rpc::Privilege::Admin}}); else sessionManager->addSession(jsonAdmin, rpc::SessionPermissions{{rpc::Privilege::Admin}}); cout << "JSONRPC Admin Session Key: " << jsonAdmin << "\n"; } if (web3.isNetworkStarted()) { for (auto const& p: preferredNodes) if (p.second.second) web3.requirePeer(p.first, p.second.first); else web3.addNode(p.first, p.second.first); if (bootstrap) for (auto const& i : defaultBootNodes()) web3.requirePeer(i.first, i.second); if (!remoteHost.empty()) web3.addNode(p2p::NodeID(), remoteHost + ":" + toString(remotePort)); } signal(SIGABRT, &ExitHandler::exitHandler); signal(SIGTERM, &ExitHandler::exitHandler); signal(SIGINT, &ExitHandler::exitHandler); unsigned n = c.blockChain().details().number; if (mining) c.startSealing(); while (!exitHandler.shouldExit()) stopSealingAfterXBlocks(&c, n, mining); if (jsonrpcIpcServer.get()) jsonrpcIpcServer->StopListening(); if (web3.isNetworkStarted()) { web3.stopNetwork(); auto netData = web3.saveNetwork(); if (!netData.empty()) writeFile(getDataDir() / fs::path(c_networkConfigFileName), netData); } return AlethErrors::Success; }