diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 24afcb656..2515959ad 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -227,6 +227,7 @@ &Config + @@ -1798,6 +1799,11 @@ font-size: 14pt Smart + + + &Sentinel... + + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index c69e6e391..d7fc0b409 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -307,6 +307,14 @@ void Main::on_gasPrices_triggered() } } +void Main::on_sentinel_triggered() +{ + bool ok; + QString sentinel = QInputDialog::getText(nullptr, "Enter sentinel address", "Enter the sentinel address for bad block reporting (e.g. http://badblockserver.com:8080). Enter nothing to disable.", QLineEdit::Normal, QString::fromStdString(ethereum()->sentinel()), &ok); + if (ok) + ethereum()->setSentinel(sentinel.toStdString()); +} + void Main::on_newIdentity_triggered() { KeyPair kp = KeyPair::create(); diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 5ea1f5515..b1939534b 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -199,6 +199,7 @@ private slots: // Config void on_gasPrices_triggered(); + void on_sentinel_triggered(); void refreshWhisper(); void refreshBlockChain(); diff --git a/eth/main.cpp b/eth/main.cpp index c45dfe766..e60da08f5 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -115,6 +115,7 @@ void interactiveHelp() << " reprocess Reprocess a given block." << endl << " dumptrace Dumps a transaction trace" << endl << "to . should be one of pretty, standard, standard+." << endl << " dumpreceipt Dumps a transation receipt." << endl + << " hashrate Print the current hashrate in hashes per second if the client is mining." << endl << " exit Exits the application." << endl; } @@ -193,7 +194,7 @@ void help() << "General Options:" << endl << " -d,--db-path Load database from path (default: " << getDataDir() << ")" << endl #if ETH_EVMJIT || !ETH_TRUE - << " --vm= Select VM. Options are: jit, smart. (default: interpreter)" << endl + << " -J,--jit Enable EVM JIT (default: off)." << endl #endif << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (default: 8)." << endl << " -V,--version Show the version and exit." << endl @@ -284,1567 +285,1463 @@ void stopMiningAfterXBlocks(eth::Client* _c, unsigned _start, unsigned _mining) this_thread::sleep_for(chrono::milliseconds(100)); } -int main(int argc, char** argv) +void interactiveMode(eth::Client* c, std::shared_ptr gasPricer, WebThreeDirect& web3, KeyManager& keyManager, string& logbuf, string& additional, function getPassword, function getAccountPassword, NetworkPreferences netPrefs, Address beneficiary, Address signingKey, TransactionPriority priority) { - // 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 - int jsonrpc = -1; -#endif - string jsonAdmin; - bool upnp = true; - WithExisting killChain = WithExisting::Trust; - string sentinel; - - /// Networking params. - string clientName; - string listenIP; - unsigned short listenPort = 30303; - string publicIP; - string remoteHost; - unsigned short remotePort = 30303; - unsigned peers = 11; - bool bootstrap = false; - bool disableDiscovery = false; - bool pinning = false; - unsigned networkId = 0; - - /// Mining params - unsigned mining = 0; - bool forceMining = false; - bool mineOnWrongChain = false; - Address signingKey; - Address sessionKey; - Address beneficiary = signingKey; - - /// 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("500000000000"); - u256 bidPrice("500000000000"); - - // javascript console - bool useConsole = false; - - /// Wallet password stuff - string masterPassword; - - string configFile = getDataDir() + "/config.rlp"; - bytes b = contents(configFile); - - strings passwordsToNote; - Secrets toImport; - if (b.size()) + additional = "Press Enter"; + string l; + while (!g_exit) { - RLP config(b); - if (config[0].size() == 32) // secret key - import and forget. + g_silence = false; + cout << logbuf << "Press Enter" << flush; + std::getline(cin, l); + logbuf.clear(); + g_silence = true; + +#if ETH_READLINE + if (l.size()) + add_history(l.c_str()); + if (auto c = readline("> ")) { - Secret s = config[0].toHash(); - toImport.push_back(s); + l = c; + free(c); } - else // new format - just use it as an address. - signingKey = config[0].toHash
(); - beneficiary = config[1].toHash
(); - } - - 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) + else + break; +#else + string l; + cout << "> " << flush; + std::getline(cin, l); +#endif + istringstream iss(l); + string cmd; + iss >> cmd; + boost::to_lower(cmd); + if (cmd == "netstart") { - if (arg == "-l") - cerr << "-l is DEPRECATED. It will be removed for the Frontier. Use --listen-port instead." << endl; - listenPort = (short)atoi(argv[++i]); + iss >> netPrefs.listenPort; + web3.setNetworkPreferences(netPrefs); + web3.startNetwork(); } - else if ((arg == "-u" || arg == "--public-ip" || arg == "--public") && i + 1 < argc) + else if (cmd == "connect") { - if (arg == "-u") - cerr << "-u is DEPRECATED. It will be removed for the Frontier. Use --public-ip instead." << endl; - publicIP = argv[++i]; + string addrPort; + iss >> addrPort; + web3.addNode(p2p::NodeId(), addrPort); } - else if ((arg == "-r" || arg == "--remote") && i + 1 < argc) - remoteHost = argv[++i]; - else if ((arg == "-p" || arg == "--port") && i + 1 < argc) + 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") { - 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]); + string enable; + iss >> enable; + c->setForceMining(isTrue(enable)); } - 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) + else if (cmd == "verbosity") { - mode = OperationMode::Import; - filename = argv[++i]; + if (iss.peek() != -1) + iss >> g_logVerbosity; + cout << "Verbosity: " << g_logVerbosity << endl; } - else if (arg == "--dont-check") - safeImport = true; - else if ((arg == "-E" || arg == "--export") && i + 1 < argc) + else if (cmd == "address") { - mode = OperationMode::Export; - filename = argv[++i]; + cout << "Current mining beneficiary:" << endl << beneficiary << endl; + cout << "Current signing account:" << endl << signingKey << endl; } - 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) + else if (c && cmd == "blockhashfromnumber") { - 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 + if (iss.peek() != -1) { - cerr << "Bad " << arg << " option: " << m << endl; - return -1; + unsigned number; + iss >> number; + cout << " hash of block: " << c->hashFromNumber(number).hex() << endl; } } - 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) + else if (c && cmd == "numberfromblockhash") { - 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 + if (iss.peek() != -1) { - cerr << "Bad " << arg << " option: " << m << endl; - return -1; + string stringHash; + iss >> stringHash; + + h256 hash = h256(fromHex(stringHash)); + cout << " number of block: " << c->numberFromHash(hash) << endl; } } - 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 == "-K" || arg == "--kill-blockchain" || arg == "--kill") - killChain = WithExisting::Kill; - else if (arg == "-R" || arg == "--rebuild") - killChain = WithExisting::Verify; - else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc) + else if (c && cmd == "block") + cout << "Current block: " << c->blockChain().details().number << endl; + else if (c && cmd == "blockqueue") + cout << "Current blockqueue status: " << endl << c->blockQueueStatus() << endl; + else if (c && cmd == "hashrate") + cout << "Current hash rate: " << toString(c->hashrate()) << " hashes per second." << endl; + else if (c && cmd == "findblock") { - 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 (...) + if (iss.peek() != -1) { - cerr << "Bad " << arg << " option: " << argv[i] << endl; - return -1; + string stringHash; + iss >> stringHash; + + h256 hash = h256(fromHex(stringHash)); + + // search in blockchain + cout << "search in blockchain... " << endl; + try + { + cout << c->blockInfo(hash) << endl; + } + catch(Exception& _e) + { + cout << "block not in blockchain" << endl; + cout << boost::diagnostic_information(_e) << endl; + } + + cout << "search in blockqueue... " << endl; + + switch(c->blockQueue().blockStatus(hash)) + { + case QueueStatus::Ready: + cout << "Ready" << endl; + break; + case QueueStatus::Importing: + cout << "Importing" << endl; + break; + case QueueStatus::UnknownParent: + cout << "UnknownParent" << endl; + break; + case QueueStatus::Bad: + cout << "Bad" << endl; + break; + case QueueStatus::Unknown: + cout << "Unknown" << endl; + break; + default: + cout << "invalid queueStatus" << endl; + } } - else if ((arg == "-s" || arg == "--import-secret") && i + 1 < argc) - { - Secret s(fromHex(argv[++i])); - toImport.push_back(s); - signingKey = toAddress(s); - } - else if ((arg == "-S" || arg == "--import-session-secret") && i + 1 < argc) - { - Secret s(fromHex(argv[++i])); - toImport.push_back(s); - sessionKey = toAddress(s); + else + cwarn << "Require parameter: findblock HASH"; } - 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) + else if (c && cmd == "firstunknown") + cout << "first unknown blockhash: " << c->blockQueue().firstUnknown().hex() << endl; + else if (c && cmd == "retryunknown") + c->retryUnknown(); + else if (cmd == "peers") { - structuredLogging = true; - structuredLoggingURL = argv[++i]; + for (auto it: web3.peers()) + cout << it.host << ":" << it.port << ", " << it.clientVersion << ", " + << std::chrono::duration_cast(it.lastPing).count() << "ms" + << endl; } - else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc) - dbPath = argv[++i]; - else if (arg == "--genesis-nonce" && i + 1 < argc) + else if (cmd == "newaccount") { - try - { - CanonBlockChain::setGenesisNonce(Nonce(argv[++i])); + string name; + std::getline(iss, name); + auto s = Secret::random(); + string password; + while (password.empty()) + { + password = getPassword("Please enter a password to protect this key (press enter for protection only be the MASTER password/keystore): "); + string confirm = getPassword("Please confirm the password by entering it again: "); + if (password != confirm) + { + cout << "Passwords were different. Try again." << endl; + password.clear(); + } } - catch (...) + if (!password.empty()) { - cerr << "Bad " << arg << " option: " << argv[i] << endl; - return -1; + cout << "Enter a hint for this password: " << flush; + string hint; + std::getline(cin, hint); + keyManager.import(s, name, password, hint); } + else + keyManager.import(s, name); + cout << "New account created: " << toAddress(s); } -/* else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc) + else if (c && cmd == "accounts") { - try + cout << "Accounts:" << endl; + u256 total = 0; + for (auto const& address: keyManager.accounts()) { - blockFees = stof(argv[++i]); + auto b = c->balanceAt(address); + cout << ((address == signingKey) ? "SIGNING " : " ") << ((address == beneficiary) ? "COINBASE " : " ") << keyManager.accountName(address) << " (" << address << "): " << formatBalance(b) << " = " << b << " wei" << endl; + total += b; } - catch (...) - { - cerr << "Bad " << arg << " option: " << argv[i] << endl; - return -1; + cout << "Total: " << formatBalance(total) << " = " << total << " wei" << endl; + } + else if (c && cmd == "transact") + { + auto const& bc =c->blockChain(); + auto h = bc.currentHash(); + auto blockData = bc.block(h); + BlockInfo info(blockData); + if (iss.peek() != -1) + { + string hexAddr; + u256 amount; + u256 gasPrice; + u256 gas; + string sechex; + string sdata; + + iss >> hexAddr >> amount >> gasPrice >> gas >> sechex >> sdata; + + if (!gasPrice) + gasPrice = gasPricer->bid(priority); + + 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 = sechex.length(); + int size = hexAddr.length(); + u256 minGas = (u256)Transaction::gasRequired(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)); + Address dest = h160(fromHex(hexAddr)); + 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 + cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA"; } - else if ((arg == "-e" || arg == "--ether-price") && i + 1 < argc) + + else if (c && cmd == "transactnonce") { - try + auto const& bc =c->blockChain(); + auto h = bc.currentHash(); + auto blockData = bc.block(h); + BlockInfo info(blockData); + if (iss.peek() != -1) + { + string hexAddr; + u256 amount; + u256 gasPrice; + u256 gas; + string sechex; + string sdata; + u256 nonce; + + iss >> hexAddr >> amount >> gasPrice >> gas >> sechex >> sdata >> nonce; + + if (!gasPrice) + gasPrice = gasPricer->bid(priority); + + 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 = sechex.length(); + int size = hexAddr.length(); + u256 minGas = (u256)Transaction::gasRequired(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)); + Address dest = h160(fromHex(hexAddr)); + c->submitTransaction(secret, amount, dest, data, gas, gasPrice, nonce); + } + catch (BadHexCharacter& _e) + { + cwarn << "invalid hex character, transaction rejected"; + cwarn << boost::diagnostic_information(_e); + } + catch (...) + { + cwarn << "transaction rejected"; + } + } + } + else + cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA NONCE"; + } + + else if (c && cmd == "txcreate") + { + auto const& bc =c->blockChain(); + auto h = bc.currentHash(); + auto blockData = bc.block(h); + BlockInfo info(blockData); + if (iss.peek() != -1) + { + u256 amount; + u256 gasPrice; + u256 gas; + string sechex; + string sdata; + + iss >> amount >> gasPrice >> gas >> sechex >> sdata; + + if (!gasPrice) + gasPrice = gasPricer->bid(priority); + + 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 = sechex.length(); + u256 minGas = (u256)Transaction::gasRequired(data, 0); + 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)); + cout << " new contract address : " << c->submitTransaction(secret, amount, data, gas, gasPrice) << endl; + } + catch (BadHexCharacter& _e) + { + cwarn << "invalid hex character, transaction rejected"; + cwarn << boost::diagnostic_information(_e); + } + catch (...) + { + cwarn << "transaction rejected"; + } + } + } + else + cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET INIT"; + } +#if ETH_FATDB + else if (c && cmd == "listcontracts") + { + auto acs =c->addresses(); + string ss; + for (auto const& i: acs) + if ( c->codeAt(i, PendingBlock).size()) + { + ss = toString(i) + " : " + toString( c->balanceAt(i)) + " [" + toString((unsigned) c->countAt(i)) + "]"; + cout << ss << endl; + } + } + else if (c && cmd == "listaccounts") + { + auto acs =c->addresses(); + string ss; + for (auto const& i: acs) + if ( c->codeAt(i, PendingBlock).empty()) + { + ss = toString(i) + " : " + toString( c->balanceAt(i)) + " [" + toString((unsigned) c->countAt(i)) + "]"; + cout << ss << endl; + } + } + else if (c && cmd == "balanceat") + { + if (iss.peek() != -1) { - etherPrice = stof(argv[++i]); + string stringHash; + iss >> stringHash; + + Address address = h160(fromHex(stringHash)); + + cout << "balance of " << stringHash << " is: " << toString(c->balanceAt(address)) << endl; } - catch (...) + } + else if (c && cmd == "balanceatblock") + { + if (iss.peek() != -1) { - cerr << "Bad " << arg << " option: " << argv[i] << endl; - return -1; + string stringHash; + unsigned blocknumber; + iss >> stringHash >> blocknumber; + + Address address = h160(fromHex(stringHash)); + + cout << "balance of " << stringHash << " is: " << toString(c->balanceAt(address, blocknumber)) << endl; } - }*/ - else if (arg == "--ask" && i + 1 < argc) + } + else if (c && cmd == "storageat") { - try + if (iss.peek() != -1) { - askPrice = u256(argv[++i]); + string stringHash; + iss >> stringHash; + + Address address = h160(fromHex(stringHash)); + + cout << "storage at " << stringHash << " is: " << endl; + for (auto s: c->storageAt(address)) + cout << toHex(s.first) << " : " << toHex(s.second) << endl; } - catch (...) + } + else if (c && cmd == "storageatblock") + { + if (iss.peek() != -1) { - cerr << "Bad " << arg << " option: " << argv[i] << endl; - return -1; + string stringHash; + unsigned blocknumber; + iss >> stringHash >> blocknumber; + + Address address = h160(fromHex(stringHash)); + + cout << "storage at " << stringHash << " is: " << endl; + for (auto s: c->storageAt(address, blocknumber)) + cout << "\"0x" << toHex(s.first) << "\" : \"0x" << toHex(s.second) << "\"," << endl; } } - else if (arg == "--bid" && i + 1 < argc) + else if (c && cmd == "codeat") { - try + if (iss.peek() != -1) { - bidPrice = u256(argv[++i]); + string stringHash; + iss >> stringHash; + + Address address = h160(fromHex(stringHash)); + + cout << "code at " << stringHash << " is: " << toHex(c->codeAt(address)) << endl; } - catch (...) + } +#endif + + else if (c && cmd == "send") + { + if (iss.peek() != -1) { - cerr << "Bad " << arg << " option: " << argv[i] << endl; - return -1; + string hexAddr; + u256 amount; + + iss >> hexAddr >> amount; + int size = hexAddr.length(); + 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)Transaction::gasRequired(bytes(), 0); + try + { + Address dest = h160(fromHex(hexAddr, WhenError::Throw)); + c->submitTransaction(keyManager.secret(signingKey, [&](){ return getAccountPassword(signingKey); }), amount, dest, bytes(), minGas); + } + catch (BadHexCharacter& _e) + { + cwarn << "invalid hex character, transaction rejected"; + cwarn << boost::diagnostic_information(_e); + } + catch (...) + { + cwarn << "transaction rejected"; + } + } } + else + cwarn << "Require parameters: send ADDRESS AMOUNT"; } - else if ((arg == "-P" || arg == "--priority") && i + 1 < argc) + else if (c && cmd == "contract") { - 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; + auto const& bc =c->blockChain(); + auto h = bc.currentHash(); + auto blockData = bc.block(h); + BlockInfo info(blockData); + if (iss.peek() != -1) + { + u256 endowment; + u256 gas; + u256 gasPrice; + string sinit; + iss >> endowment >> gasPrice >> gas >> sinit; + 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)Transaction::gasRequired(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(keyManager.secret(signingKey, [&](){ return getAccountPassword(signingKey); }), endowment, init, gas, gasPrice); + } else - try { - priority = (TransactionPriority)(max(0, min(100, stoi(m))) * 8 / 100); + cwarn << "Require parameters: contract ENDOWMENT GASPRICE GAS CODEHEX"; + } + else if (c && cmd == "dumpreceipt") + { + unsigned block; + unsigned index; + iss >> block >> index; + dev::eth::TransactionReceipt r = c->blockChain().receipts(c->blockChain().numberHash(block)).receipts[index]; + auto rb = r.rlp(); + cout << "RLP: " << RLP(rb) << endl; + cout << "Hex: " << toHex(rb) << endl; + cout << r << endl; + } + else if (c && cmd == "reprocess") + { + string block; + iss >> block; + h256 blockHash; + try + { + if (block.size() == 64 || block.size() == 66) + blockHash = h256(block); + else + blockHash = c->blockChain().numberHash(stoi(block)); + c->state(blockHash); + } + catch (...) + {} + } + else if (c && cmd == "dumptrace") + { + unsigned block; + unsigned index; + string filename; + string format; + iss >> block >> index >> filename >> format; + ofstream f; + f.open(filename); + + dev::eth::State state = c->state(index + 1,c->blockChain().numberHash(block)); + if (index < state.pending().size()) + { + Executive e(state, c->blockChain(), 0); + Transaction t = state.pending()[index]; + state = state.fromPending(index); + try + { + OnOpFunc oof; + if (format == "pretty") + oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM) + { + dev::eth::VM* vm = vvm; + dev::eth::ExtVM const* ext = static_cast(vextVM); + f << endl << " STACK" << endl; + for (auto i: vm->stack()) + f << (h256)i << endl; + f << " MEMORY" << endl << dev::memDump(vm->memory()); + f << " STORAGE" << endl; + for (auto const& i: ext->state().storage(ext->myAddress)) + f << showbase << hex << i.first << ": " << i.second << endl; + f << dec << ext->depth << " | " << ext->myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm->curPC() << " : " << dev::eth::instructionInfo(instr).name << " | " << dec << gas << " | -" << dec << gasCost << " | " << newMemSize << "x32"; + }; + else if (format == "standard") + oof = [&](uint64_t, Instruction instr, bigint, bigint, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM) + { + dev::eth::VM* vm = vvm; + dev::eth::ExtVM const* ext = static_cast(vextVM); + f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)gas, 1)) << endl; + }; + else if (format == "standard+") + oof = [&](uint64_t, Instruction instr, bigint, bigint, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM) + { + dev::eth::VM* vm = vvm; + dev::eth::ExtVM const* ext = static_cast(vextVM); + if (instr == Instruction::STOP || instr == Instruction::RETURN || instr == Instruction::SUICIDE) + for (auto const& i: ext->state().storage(ext->myAddress)) + f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl; + f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)gas, 1)) << endl; + }; + e.initialize(t); + if (!e.execute()) + e.go(oof); + e.finalize(); } - catch (...) { - cerr << "Unknown " << arg << " option: " << m << endl; - return -1; + catch(Exception const& _e) + { + // TODO: a bit more information here. this is probably quite worrying as the transaction is already in the blockchain. + cwarn << diagnostic_information(_e); } + } } - else if ((arg == "-m" || arg == "--mining") && i + 1 < argc) + else if (c && cmd == "inspect") { - string m = argv[++i]; - if (isTrue(m)) - mining = ~(unsigned)0; - else if (isFalse(m)) - mining = 0; + string rechex; + iss >> rechex; + + if (rechex.length() != 40) + cwarn << "Invalid address length"; else - try { - mining = stoi(m); + { + auto h = h160(fromHex(rechex)); + stringstream s; + + try + { + auto storage =c->storageAt(h, PendingBlock); + for (auto const& i: storage) + s << "@" << showbase << hex << i.first << " " << showbase << hex << i.second << endl; + s << endl << disassemble( c->codeAt(h, PendingBlock)) << 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 (...) { - cerr << "Unknown " << arg << " option: " << m << endl; - return -1; + catch (dev::InvalidTrie) + { + cwarn << "Corrupted trie."; } + } } - else if (arg == "-b" || arg == "--bootstrap") - bootstrap = true; - else if (arg == "--no-discovery") - disableDiscovery = true; - else if (arg == "--pin") - pinning = true; - else if (arg == "-f" || arg == "--force-mining") - forceMining = true; - else if (arg == "-i" || arg == "--interactive") - interactive = true; -#if ETH_JSONRPC - else if ((arg == "-j" || arg == "--json-rpc")) - jsonrpc = jsonrpc == -1 ? SensibleHttpPort : jsonrpc; - else if (arg == "--json-rpc-port" && i + 1 < argc) - jsonrpc = atoi(argv[++i]); - else if (arg == "--json-admin" && i + 1 < argc) - jsonAdmin = argv[++i]; -#endif -#if ETH_JSCONSOLE - else if (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 == "-o" || arg == "--mode") && i + 1 < argc) + else if (cmd == "setsigningkey") { - string m = argv[++i]; - if (m == "full") - nodeMode = NodeMode::Full; - else if (m == "peer") - nodeMode = NodeMode::PeerServer; + if (iss.peek() != -1) + { + string hexSec; + iss >> hexSec; + signingKey = Address(fromHex(hexSec)); + } else + cwarn << "Require parameter: setSecret HEXSECRETKEY"; + } + else if (cmd == "setaddress") + { + if (iss.peek() != -1) { - cerr << "Unknown mode: " << m << endl; - return -1; + string hexAddr; + iss >> hexAddr; + if (hexAddr.length() != 40) + cwarn << "Invalid address length: " << hexAddr.length(); + else + { + try + { + beneficiary = h160(fromHex(hexAddr, WhenError::Throw)); + } + catch (BadHexCharacter& _e) + { + cwarn << "invalid hex character, coinbase rejected"; + cwarn << boost::diagnostic_information(_e); + } + catch (...) + { + cwarn << "coinbase rejected"; + } + } } + else + cwarn << "Require parameter: setAddress HEXADDRESS"; } -#if ETH_EVMJIT - else if (arg == "--vm=jit") - VMFactory::setKind(VMKind::JIT); - else if (arg == "--vm=smart") - VMFactory::setKind(VMKind::Smart); -#endif - else if (arg == "-h" || arg == "--help") - help(); - else if (arg == "-V" || arg == "--version") - version(); - else + else if (cmd == "exportconfig") { - cerr << "Invalid argument: " << arg << endl; - exit(-1); + if (iss.peek() != -1) + { + string path; + iss >> path; + RLPStream config(2); + config << signingKey << beneficiary; + writeFile(path, config.out()); + } + else + cwarn << "Require parameter: exportConfig PATH"; + } + else if (cmd == "importconfig") + { + if (iss.peek() != -1) + { + string path; + iss >> path; + bytes b = contents(path); + if (b.size()) + { + RLP config(b); + signingKey = config[0].toHash
(); + beneficiary = config[1].toHash
(); + } + else + cwarn << path << "has no content!"; + } + else + cwarn << "Require parameter: importConfig PATH"; } + else if (cmd == "help") + interactiveHelp(); + else if (cmd == "exit") + break; + else + cout << "Unrecognised command. Type 'help' for help in interactive mode." << endl; } +} - m.execute(); +int main(int argc, char** argv) +{ + // Init defaults + Defaults::get(); - KeyManager keyManager; - for (auto const& s: passwordsToNote) - keyManager.notePassword(s); + /// Operating mode. + OperationMode mode = OperationMode::Node; + string dbPath; + unsigned prime = 0; + bool yesIReallyKnowWhatImDoing = false; - { - RLPStream config(2); - config << signingKey << beneficiary; - writeFile(configFile, config.out()); - } + /// File name for import/export. + string filename; + bool safeImport = false; - if (sessionKey) - signingKey = sessionKey; + /// Hashes/numbers for export range. + string exportFrom = "1"; + string exportTo = "latest"; + Format exportFormat = Format::Binary; - if (!clientName.empty()) - clientName += "/"; + /// General params for Node operation + NodeMode nodeMode = NodeMode::Full; + bool interactive = false; +#if ETH_JSONRPC + int jsonrpc = -1; +#endif + string jsonAdmin; + bool upnp = true; + WithExisting killChain = WithExisting::Trust; + bool jit = false; + string sentinel; - string logbuf; - std::string additional; - if (interactive) - g_logPost = [&](std::string const& a, char const*){ - static SpinLock s_lock; - SpinGuard l(s_lock); + /// Networking params. + string clientName; + string listenIP; + unsigned short listenPort = 30303; + string publicIP; + string remoteHost; + unsigned short remotePort = 30303; + unsigned peers = 11; + bool bootstrap = false; + bool disableDiscovery = false; + bool pinning = false; + unsigned networkId = 0; - if (g_silence) - logbuf += a + "\n"; - else - cout << "\r \r" << a << endl << additional << flush; + /// Mining params + unsigned mining = 0; + bool forceMining = false; + bool mineOnWrongChain = false; + Address signingKey; + Address sessionKey; + Address beneficiary = signingKey; - // helpful to use OutputDebugString on windows - #ifdef _WIN32 - { - OutputDebugStringA(a.data()); - OutputDebugStringA("\n"); - } - #endif - }; + /// Structured logging params + bool structuredLogging = false; + string structuredLoggingFormat = "%Y-%m-%dT%H:%M:%S"; + string structuredLoggingURL; - auto getPassword = [&](string const& prompt){ - auto 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) + "): "); - }; + /// Transaction params + TransactionPriority priority = TransactionPriority::Medium; +// double etherPrice = 30.679; +// double blockFees = 15.0; + u256 askPrice("500000000000"); + u256 bidPrice("500000000000"); - StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL); - auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); - netPrefs.discovery = !disableDiscovery; - netPrefs.pin = pinning; - auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); - dev::WebThreeDirect web3( - WebThreeDirect::composeClientVersion("++eth", clientName), - dbPath, - killChain, - nodeMode == NodeMode::Full ? set{"eth"/*, "shh"*/} : set(), - netPrefs, - &nodesState); - web3.ethereum()->setMineOnBadChain(mineOnWrongChain); - web3.ethereum()->setSentinel(sentinel); + // javascript console + bool useConsole = false; - 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); - } - }; + /// Wallet password stuff + string masterPassword; - if (mode == OperationMode::Export) - { - ofstream fout(filename, std::ofstream::binary); - ostream& out = (filename.empty() || filename == "--") ? cout : fout; + string configFile = getDataDir() + "/config.rlp"; + bytes b = contents(configFile); - unsigned last = toNumber(exportTo); - for (unsigned i = toNumber(exportFrom); i <= last; ++i) + strings passwordsToNote; + Secrets toImport; + if (b.size()) + { + RLP config(b); + if (config[0].size() == 32) // secret key - import and forget. { - 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:; - } + Secret s = config[0].toHash(); + toImport.push_back(s); } - return 0; + else // new format - just use it as an address. + signingKey = config[0].toHash
(); + beneficiary = config[1].toHash
(); } - if (mode == OperationMode::Import) + MinerCLI m(MinerCLI::OperationMode::None); + + for (int i = 1; i < argc; ++i) { - 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) + 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) { - bytes block(8); - in.read((char*)block.data(), 8); - block.resize(RLP(block, RLP::LaisezFaire).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 r = web3.ethereum()->syncQueue(10); - imported += get<2>(r); - - double e = chrono::duration_cast(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; - } + if (arg == "-l") + cerr << "-l is DEPRECATED. It will be removed for the Frontier. Use --listen-port instead." << endl; + listenPort = (short)atoi(argv[++i]); } - - while (web3.ethereum()->blockQueue().items().first + web3.ethereum()->blockQueue().items().second > 0) + else if ((arg == "-u" || arg == "--public-ip" || arg == "--public") && i + 1 < argc) { - this_thread::sleep_for(chrono::seconds(1)); - web3.ethereum()->syncQueue(100000); + if (arg == "-u") + cerr << "-u is DEPRECATED. It will be removed for the Frontier. Use --public-ip instead." << endl; + publicIP = argv[++i]; } - double e = chrono::duration_cast(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 primes = RLP(pd).toUnorderedSet(); - while (true) + else if ((arg == "-r" || arg == "--remote") && i + 1 < argc) + remoteHost = argv[++i]; + else if ((arg == "-p" || arg == "--port") && i + 1 < argc) { - 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; + 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]); } - 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: "); - if (keyManager.load(masterPassword)) - break; - cout << "Password invalid. Try again." << endl; - } - } - else - { - while (masterPassword.empty()) + 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) { - 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(); - } + mode = OperationMode::Import; + filename = argv[++i]; } - keyManager.create(masterPassword); - } - - for (auto const& s: toImport) - { - keyManager.import(s, "Imported key (UNSAFE)"); - if (!signingKey) - signingKey = toAddress(s); - } - - if (keyManager.accounts().empty()) - keyManager.import(Secret::random(), "Default key"); - - cout << ethCredits(); - web3.setIdealPeerCount(peers); -// std::shared_ptr gasPricer = make_shared(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000)); - std::shared_ptr gasPricer = make_shared(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); - c->setTurboMining(m.minerType() == MinerCLI::MinerType::GPU); - c->setAddress(beneficiary); - c->setNetworkId(networkId); - } - - cout << "Transaction Signer: " << signingKey << endl; - cout << "Mining Benefactor: " << beneficiary << endl; - - if (bootstrap || !remoteHost.empty()) - { - 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 ETH_JSONRPC || !ETH_TRUE - shared_ptr jsonrpcServer; - unique_ptr jsonrpcConnector; - if (jsonrpc > -1) - { - jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); - jsonrpcServer = shared_ptr(new dev::WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector(), keyManager, *gasPricer)); - jsonrpcServer->setMiningBenefactorChanger([&](Address const& a) { beneficiary = a; }); - jsonrpcServer->StartListening(); - if (jsonAdmin.empty()) - jsonAdmin = jsonrpcServer->newSession(SessionPermissions{{Priviledge::Admin}}); - else - jsonrpcServer->addSession(jsonAdmin, SessionPermissions{{Priviledge::Admin}}); - cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl; - } -#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) - { - additional = "Press Enter"; - string l; - while (!g_exit) + else if (arg == "--dont-check") + safeImport = true; + else if ((arg == "-E" || arg == "--export") && i + 1 < argc) { - g_silence = false; - cout << logbuf << "Press Enter" << flush; - std::getline(cin, l); - logbuf.clear(); - g_silence = true; - -#if ETH_READLINE - if (l.size()) - add_history(l.c_str()); - if (auto c = readline("> ")) - { - l = c; - free(c); - } - else - break; -#else - string l; - cout << "> " << flush; - std::getline(cin, l); -#endif - istringstream iss(l); - string cmd; - iss >> cmd; - boost::to_lower(cmd); - if (cmd == "netstart") - { - iss >> netPrefs.listenPort; - web3.setNetworkPreferences(netPrefs); - web3.startNetwork(); - } - else if (cmd == "connect") - { - string addrPort; - iss >> addrPort; - web3.addNode(p2p::NodeId(), addrPort); - } - else if (cmd == "netstop") + mode = OperationMode::Export; + filename = argv[++i]; + } + else if (arg == "--prime" && i + 1 < argc) + try { - web3.stopNetwork(); + prime = stoi(argv[++i]); } - else if (c && cmd == "minestart") + catch (...) { - c->startMining(); + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; } - else if (c && cmd == "minestop") + 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 { - c->stopMining(); + cerr << "Bad " << arg << " option: " << m << endl; + return -1; } - else if (c && cmd == "mineforce") + } + 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 { - string enable; - iss >> enable; - c->setForceMining(isTrue(enable)); + cerr << "Bad " << arg << " option: " << m << endl; + return -1; } -/* else if (c && cmd == "setblockfees") - { - iss >> blockFees; - try - { - gasPricer->setRefBlockFees(u256(blockFees * 1000)); - } - catch (Overflow const& _e) - { - cout << boost::diagnostic_information(_e); - } - - cout << "Block fees: " << blockFees << endl; + } + else if (arg == "--network-id" && i + 1 < argc) + try { + networkId = stol(argv[++i]); } - else if (c && cmd == "setetherprice") + catch (...) { - iss >> etherPrice; - if (etherPrice == 0) - cout << "ether price cannot be set to zero" << endl; - else - { - try - { - gasPricer->setRefPrice(u256(double(ether / 1000) / etherPrice)); - } - catch (Overflow const& _e) - { - cout << boost::diagnostic_information(_e); - } - } - cout << "ether Price: " << etherPrice << endl; + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; } - 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; + else if (arg == "-K" || arg == "--kill-blockchain" || arg == "--kill") + killChain = WithExisting::Kill; + else if (arg == "-R" || arg == "--rebuild") + killChain = WithExisting::Verify; + 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)); } -#if ETH_JSONRPC || !ETH_TRUE - else if (cmd == "jsonport") + catch (BadHexCharacter&) { - if (iss.peek() != -1) - iss >> jsonrpc; - cout << "JSONRPC Port: " << jsonrpc << endl; + cerr << "Bad hex in " << arg << " option: " << argv[i] << endl; + return -1; } - else if (cmd == "jsonstart") + catch (...) { - if (jsonrpc < 0) - jsonrpc = SensibleHttpPort; - jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); - jsonrpcServer = shared_ptr(new dev::WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector(), keyManager, *gasPricer)); - jsonrpcServer->setMiningBenefactorChanger([&](Address const& a) { beneficiary = a; }); - jsonrpcServer->StartListening(); - if (jsonAdmin.empty()) - jsonAdmin = jsonrpcServer->newSession(SessionPermissions{{Priviledge::Admin}}); - else - jsonrpcServer->addSession(jsonAdmin, SessionPermissions{{Priviledge::Admin}}); - cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl; + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; } - else if (cmd == "jsonstop") + else if ((arg == "-s" || arg == "--import-secret") && i + 1 < argc) + { + Secret s(fromHex(argv[++i])); + toImport.push_back(s); + signingKey = toAddress(s); + } + else if ((arg == "-S" || arg == "--import-session-secret") && i + 1 < argc) + { + Secret s(fromHex(argv[++i])); + toImport.push_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-nonce" && i + 1 < argc) + { + try { - if (jsonrpcServer.get()) - jsonrpcServer->StopListening(); - jsonrpcServer.reset(); + CanonBlockChain::setGenesisNonce(Nonce(argv[++i])); } -#endif - else if (cmd == "address") + catch (...) { - cout << "Current mining beneficiary:" << endl << beneficiary << endl; - cout << "Current signing account:" << endl << signingKey << endl; + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; } - else if (c && cmd == "blockhashfromnumber") + } +/* else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc) + { + try { - if (iss.peek() != -1) - { - unsigned number; - iss >> number; - cout << " hash of block: " << c->hashFromNumber(number).hex() << endl; - } + blockFees = stof(argv[++i]); } - else if (c && cmd == "numberfromblockhash") + catch (...) { - if (iss.peek() != -1) - { - string stringHash; - iss >> stringHash; - - h256 hash = h256(fromHex(stringHash)); - cout << " number of block: " << c->numberFromHash(hash) << endl; - } + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; } - else if (c && cmd == "block") + } + else if ((arg == "-e" || arg == "--ether-price") && i + 1 < argc) + { + try { - cout << "Current block: " << c->blockChain().details().number << endl; + etherPrice = stof(argv[++i]); } - else if (c && cmd == "blockqueue") + catch (...) { - cout << "Current blockqueue status: " << endl << c->blockQueueStatus() << endl; + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; } - else if (c && cmd == "findblock") + }*/ + else if (arg == "--ask" && i + 1 < argc) + { + try { - if (iss.peek() != -1) - { - string stringHash; - iss >> stringHash; - - h256 hash = h256(fromHex(stringHash)); - - // search in blockchain - cout << "search in blockchain... " << endl; - try - { - cout << c->blockInfo(hash) << endl; - } - catch(Exception& _e) - { - cout << "block not in blockchain" << endl; - cout << boost::diagnostic_information(_e) << endl; - } - - cout << "search in blockqueue... " << endl; - - switch(c->blockQueue().blockStatus(hash)) - { - case QueueStatus::Ready: - cout << "Ready" << endl; - break; - case QueueStatus::Importing: - cout << "Importing" << endl; - break; - case QueueStatus::UnknownParent: - cout << "UnknownParent" << endl; - break; - case QueueStatus::Bad: - cout << "Bad" << endl; - break; - case QueueStatus::Unknown: - cout << "Unknown" << endl; - break; - default: - cout << "invalid queueStatus" << endl; - } - } - else - cwarn << "Require parameter: findblock HASH"; + askPrice = u256(argv[++i]); } - else if (c && cmd == "firstunknown") + catch (...) { - cout << "first unknown blockhash: " << c->blockQueue().firstUnknown().hex() << endl; + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; } - else if (c && cmd == "retryunknown") + } + else if (arg == "--bid" && i + 1 < argc) + { + try { - c->retryUnknown(); + bidPrice = u256(argv[++i]); } - else if (cmd == "peers") + catch (...) { - for (auto it: web3.peers()) - cout << it.host << ":" << it.port << ", " << it.clientVersion << ", " - << std::chrono::duration_cast(it.lastPing).count() << "ms" - << endl; + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; } - else if (cmd == "newaccount") - { - string name; - std::getline(iss, name); - auto s = Secret::random(); - string password; - while (password.empty()) - { - password = getPassword("Please enter a password to protect this key (press enter for protection only be the MASTER password/keystore): "); - string confirm = getPassword("Please confirm the password by entering it again: "); - if (password != confirm) - { - cout << "Passwords were different. Try again." << endl; - password.clear(); - } + } + 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); } - if (!password.empty()) - { - cout << "Enter a hint for this password: " << flush; - string hint; - std::getline(cin, hint); - keyManager.import(s, name, password, hint); + catch (...) { + cerr << "Unknown " << arg << " option: " << m << endl; + return -1; } - else - keyManager.import(s, name); - cout << "New account created: " << toAddress(s); - } - else if (c && cmd == "accounts") - { - cout << "Accounts:" << endl; - u256 total = 0; - for (auto const& address: keyManager.accounts()) - { - auto b = c->balanceAt(address); - cout << ((address == signingKey) ? "SIGNING " : " ") << ((address == beneficiary) ? "COINBASE " : " ") << keyManager.accountName(address) << " (" << address << "): " << formatBalance(b) << " = " << b << " wei" << endl; - total += b; + } + 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); } - cout << "Total: " << formatBalance(total) << " = " << total << " wei" << endl; - } - else if (c && cmd == "transact") - { - auto const& bc =c->blockChain(); - auto h = bc.currentHash(); - auto blockData = bc.block(h); - BlockInfo info(blockData); - if (iss.peek() != -1) - { - string hexAddr; - u256 amount; - u256 gasPrice; - u256 gas; - string sechex; - string sdata; - - iss >> hexAddr >> amount >> gasPrice >> gas >> sechex >> sdata; - - if (!gasPrice) - gasPrice = gasPricer->bid(priority); - - 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 = sechex.length(); - int size = hexAddr.length(); - u256 minGas = (u256)Transaction::gasRequired(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)); - Address dest = h160(fromHex(hexAddr)); - 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"; - } - } + catch (...) { + cerr << "Unknown " << arg << " option: " << m << endl; + return -1; } - else - cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA"; - } - - else if (c && cmd == "transactnonce") + } + else if (arg == "-b" || arg == "--bootstrap") + bootstrap = true; + else if (arg == "--no-discovery") + disableDiscovery = true; + else if (arg == "--pin") + pinning = true; + else if (arg == "-f" || arg == "--force-mining") + forceMining = true; + else if (arg == "-i" || arg == "--interactive") + interactive = true; +#if ETH_JSONRPC + else if ((arg == "-j" || arg == "--json-rpc")) + jsonrpc = jsonrpc == -1 ? SensibleHttpPort : jsonrpc; + else if (arg == "--json-rpc-port" && i + 1 < argc) + jsonrpc = atoi(argv[++i]); + else if (arg == "--json-admin" && i + 1 < argc) + jsonAdmin = argv[++i]; +#endif +#if ETH_JSCONSOLE + else if (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 == "-o" || arg == "--mode") && i + 1 < argc) + { + string m = argv[++i]; + if (m == "full") + nodeMode = NodeMode::Full; + else if (m == "peer") + nodeMode = NodeMode::PeerServer; + else { - auto const& bc =c->blockChain(); - auto h = bc.currentHash(); - auto blockData = bc.block(h); - BlockInfo info(blockData); - if (iss.peek() != -1) - { - string hexAddr; - u256 amount; - u256 gasPrice; - u256 gas; - string sechex; - string sdata; - u256 nonce; - - iss >> hexAddr >> amount >> gasPrice >> gas >> sechex >> sdata >> nonce; - - if (!gasPrice) - gasPrice = gasPricer->bid(priority); - - 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 = sechex.length(); - int size = hexAddr.length(); - u256 minGas = (u256)Transaction::gasRequired(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)); - Address dest = h160(fromHex(hexAddr)); - c->submitTransaction(secret, amount, dest, data, gas, gasPrice, nonce); - } - catch (BadHexCharacter& _e) - { - cwarn << "invalid hex character, transaction rejected"; - cwarn << boost::diagnostic_information(_e); - } - catch (...) - { - cwarn << "transaction rejected"; - } - } - } - else - cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA NONCE"; + cerr << "Unknown mode: " << m << endl; + return -1; } + } +#if ETH_EVMJIT + else if (arg == "-J" || arg == "--jit") + jit = true; +#endif + else if (arg == "-h" || arg == "--help") + help(); + else if (arg == "-V" || arg == "--version") + version(); + else + { + cerr << "Invalid argument: " << arg << endl; + exit(-1); + } + } - else if (c && cmd == "txcreate") - { - auto const& bc =c->blockChain(); - auto h = bc.currentHash(); - auto blockData = bc.block(h); - BlockInfo info(blockData); - if (iss.peek() != -1) - { - u256 amount; - u256 gasPrice; - u256 gas; - string sechex; - string sdata; - - iss >> amount >> gasPrice >> gas >> sechex >> sdata; - - if (!gasPrice) - gasPrice = gasPricer->bid(priority); - - 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 = sechex.length(); - u256 minGas = (u256)Transaction::gasRequired(data, 0); - 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)); - cout << " new contract address : " << c->submitTransaction(secret, amount, data, gas, gasPrice) << endl; - } - catch (BadHexCharacter& _e) - { - cwarn << "invalid hex character, transaction rejected"; - cwarn << boost::diagnostic_information(_e); - } - catch (...) - { - cwarn << "transaction rejected"; - } - } - } - else - cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET INIT"; - } -#if ETH_FATDB - else if (c && cmd == "listcontracts") - { - auto acs =c->addresses(); - string ss; - for (auto const& i: acs) - if ( c->codeAt(i, PendingBlock).size()) - { - ss = toString(i) + " : " + toString( c->balanceAt(i)) + " [" + toString((unsigned) c->countAt(i)) + "]"; - cout << ss << endl; - } - } - else if (c && cmd == "listaccounts") + m.execute(); + + KeyManager keyManager; + for (auto const& s: passwordsToNote) + keyManager.notePassword(s); + + { + RLPStream config(2); + config << signingKey << beneficiary; + writeFile(configFile, config.out()); + } + + 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 { - auto acs =c->addresses(); - string ss; - for (auto const& i: acs) - if ( c->codeAt(i, PendingBlock).empty()) - { - ss = toString(i) + " : " + toString( c->balanceAt(i)) + " [" + toString((unsigned) c->countAt(i)) + "]"; - cout << ss << endl; - } + OutputDebugStringA(a.data()); + OutputDebugStringA("\n"); } - else if (c && cmd == "balanceat") - { - if (iss.peek() != -1) - { - string stringHash; - iss >> stringHash; + #endif + }; - Address address = h160(fromHex(stringHash)); + auto getPassword = [&](string const& prompt){ + auto 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) + "): "); + }; - cout << "balance of " << stringHash << " is: " << toString(c->balanceAt(address)) << endl; - } - } - else if (c && cmd == "balanceatblock") + StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat, structuredLoggingURL); + VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); + auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); + netPrefs.discovery = !disableDiscovery; + netPrefs.pin = pinning; + auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); + dev::WebThreeDirect web3( + WebThreeDirect::composeClientVersion("++eth", clientName), + dbPath, + killChain, + nodeMode == NodeMode::Full ? set{"eth"/*, "shh"*/} : set(), + 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) { - if (iss.peek() != -1) - { - string stringHash; - unsigned blocknumber; - iss >> stringHash >> blocknumber; - - Address address = h160(fromHex(stringHash)); - - cout << "balance of " << stringHash << " is: " << toString(c->balanceAt(address, blocknumber)) << endl; - } + 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:; } - else if (c && cmd == "storageat") - { - if (iss.peek() != -1) - { - string stringHash; - iss >> stringHash; + } + return 0; + } - Address address = h160(fromHex(stringHash)); + 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); - cout << "storage at " << stringHash << " is: " << endl; - for (auto s: c->storageAt(address)) - cout << toHex(s.first) << " : " << toHex(s.second) << endl; - } - } - else if (c && cmd == "storageatblock") + switch (web3.ethereum()->queueBlock(block, safeImport)) { - if (iss.peek() != -1) - { - string stringHash; - unsigned blocknumber; - iss >> stringHash >> blocknumber; - - Address address = h160(fromHex(stringHash)); - - cout << "storage at " << stringHash << " is: " << endl; - for (auto s: c->storageAt(address, blocknumber)) - cout << "\"0x" << toHex(s.first) << "\" : \"0x" << toHex(s.second) << "\"," << endl; - } + 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; } - else if (c && cmd == "codeat") - { - if (iss.peek() != -1) - { - string stringHash; - iss >> stringHash; - Address address = h160(fromHex(stringHash)); + // sync chain with queue + tuple r = web3.ethereum()->syncQueue(10); + imported += get<2>(r); - cout << "code at " << stringHash << " is: " << toHex(c->codeAt(address)) << endl; - } + double e = chrono::duration_cast(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; } -#endif + } - else if (c && cmd == "send") - { - if (iss.peek() != -1) - { - string hexAddr; - u256 amount; + 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::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; + } - iss >> hexAddr >> amount; - int size = hexAddr.length(); - 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)Transaction::gasRequired(bytes(), 0); - try - { - Address dest = h160(fromHex(hexAddr, WhenError::Throw)); - c->submitTransaction(keyManager.secret(signingKey, [&](){ return getAccountPassword(signingKey); }), amount, dest, bytes(), minGas); - } - catch (BadHexCharacter& _e) - { - cwarn << "invalid hex character, transaction rejected"; - cwarn << boost::diagnostic_information(_e); - } - catch (...) - { - cwarn << "transaction rejected"; - } - } - } - else - cwarn << "Require parameters: send ADDRESS AMOUNT"; - } - else if (c && cmd == "contract") - { - auto const& bc =c->blockChain(); - auto h = bc.currentHash(); - auto blockData = bc.block(h); - BlockInfo info(blockData); - if (iss.peek() != -1) - { - u256 endowment; - u256 gas; - u256 gasPrice; - string sinit; - iss >> endowment >> gasPrice >> gas >> sinit; - 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)Transaction::gasRequired(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(keyManager.secret(signingKey, [&](){ return getAccountPassword(signingKey); }), endowment, init, gas, gasPrice); - } - else - cwarn << "Require parameters: contract ENDOWMENT GASPRICE GAS CODEHEX"; - } - else if (c && cmd == "dumpreceipt") - { - unsigned block; - unsigned index; - iss >> block >> index; - dev::eth::TransactionReceipt r = c->blockChain().receipts(c->blockChain().numberHash(block)).receipts[index]; - auto rb = r.rlp(); - cout << "RLP: " << RLP(rb) << endl; - cout << "Hex: " << toHex(rb) << endl; - cout << r << endl; - } - else if (c && cmd == "reprocess") - { - string block; - iss >> block; - h256 blockHash; + if (c_network == eth::Network::Frontier && !yesIReallyKnowWhatImDoing) + { + auto pd = contents(getDataDir() + "primes"); + unordered_set primes = RLP(pd).toUnorderedSet(); + while (true) + { + if (!prime) try { - if (block.size() == 64 || block.size() == 66) - blockHash = h256(block); - else - blockHash = c->blockChain().numberHash(stoi(block)); - c->state(blockHash); - } - catch (...) - {} - } - else if (c && cmd == "dumptrace") - { - unsigned block; - unsigned index; - string filename; - string format; - iss >> block >> index >> filename >> format; - ofstream f; - f.open(filename); - - dev::eth::State state = c->state(index + 1,c->blockChain().numberHash(block)); - if (index < state.pending().size()) - { - Executive e(state, c->blockChain(), 0); - Transaction t = state.pending()[index]; - state = state.fromPending(index); - try - { - OnOpFunc oof; - if (format == "pretty") - oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM) - { - dev::eth::VM* vm = vvm; - dev::eth::ExtVM const* ext = static_cast(vextVM); - f << endl << " STACK" << endl; - for (auto i: vm->stack()) - f << (h256)i << endl; - f << " MEMORY" << endl << dev::memDump(vm->memory()); - f << " STORAGE" << endl; - for (auto const& i: ext->state().storage(ext->myAddress)) - f << showbase << hex << i.first << ": " << i.second << endl; - f << dec << ext->depth << " | " << ext->myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm->curPC() << " : " << dev::eth::instructionInfo(instr).name << " | " << dec << gas << " | -" << dec << gasCost << " | " << newMemSize << "x32"; - }; - else if (format == "standard") - oof = [&](uint64_t, Instruction instr, bigint, bigint, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM) - { - dev::eth::VM* vm = vvm; - dev::eth::ExtVM const* ext = static_cast(vextVM); - f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)gas, 1)) << endl; - }; - else if (format == "standard+") - oof = [&](uint64_t, Instruction instr, bigint, bigint, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM) - { - dev::eth::VM* vm = vvm; - dev::eth::ExtVM const* ext = static_cast(vextVM); - if (instr == Instruction::STOP || instr == Instruction::RETURN || instr == Instruction::SUICIDE) - for (auto const& i: ext->state().storage(ext->myAddress)) - f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl; - f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)gas, 1)) << endl; - }; - e.initialize(t); - if (!e.execute()) - e.go(oof); - e.finalize(); - } - catch(Exception const& _e) - { - // TODO: a bit more information here. this is probably quite worrying as the transaction is already in the blockchain. - cwarn << diagnostic_information(_e); - } + prime = stoi(getPassword("To enter the Frontier, enter a 6 digit prime that you have not entered before: ")); } - } - else if (c && cmd == "inspect") - { - string rechex; - iss >> rechex; - - if (rechex.length() != 40) - cwarn << "Invalid address length"; - else - { - auto h = h160(fromHex(rechex)); - stringstream s; + catch (...) {} + if (isPrime(prime) && !primes.count(prime)) + break; + prime = 0; + } + primes.insert(prime); + writeFile(getDataDir() + "primes", rlp(primes)); + } - try - { - auto storage =c->storageAt(h, PendingBlock); - for (auto const& i: storage) - s << "@" << showbase << hex << i.first << " " << showbase << hex << i.second << endl; - s << endl << disassemble( c->codeAt(h, PendingBlock)) << 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) - { - cwarn << "Corrupted trie."; - } - } - } - else if (cmd == "setsigningkey") - { - if (iss.peek() != -1) - { - string hexSec; - iss >> hexSec; - signingKey = Address(fromHex(hexSec)); - } - else - cwarn << "Require parameter: setSecret HEXSECRETKEY"; - } - else if (cmd == "setaddress") - { - if (iss.peek() != -1) - { - string hexAddr; - iss >> hexAddr; - if (hexAddr.length() != 40) - cwarn << "Invalid address length: " << hexAddr.length(); - else - { - try - { - beneficiary = h160(fromHex(hexAddr, WhenError::Throw)); - } - catch (BadHexCharacter& _e) - { - cwarn << "invalid hex character, coinbase rejected"; - cwarn << boost::diagnostic_information(_e); - } - catch (...) - { - cwarn << "coinbase rejected"; - } - } - } - else - cwarn << "Require parameter: setAddress HEXADDRESS"; - } - else if (cmd == "exportconfig") + if (keyManager.exists()) + { + if (masterPassword.empty() || !keyManager.load(masterPassword)) + while (true) { - if (iss.peek() != -1) - { - string path; - iss >> path; - RLPStream config(2); - config << signingKey << beneficiary; - writeFile(path, config.out()); - } - else - cwarn << "Require parameter: exportConfig PATH"; + masterPassword = getPassword("Please enter your MASTER password: "); + if (keyManager.load(masterPassword)) + break; + cout << "Password invalid. Try again." << endl; } - else if (cmd == "importconfig") + } + 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) { - if (iss.peek() != -1) - { - string path; - iss >> path; - bytes b = contents(path); - if (b.size()) - { - RLP config(b); - signingKey = config[0].toHash
(); - beneficiary = config[1].toHash
(); - } - else - cwarn << path << "has no content!"; - } - else - cwarn << "Require parameter: importConfig PATH"; + cout << "Passwords were different. Try again." << endl; + masterPassword.clear(); } - else if (cmd == "help") - interactiveHelp(); - else if (cmd == "exit") - break; - else - cout << "Unrecognised command. Type 'help' for help in interactive mode." << endl; } -#if ETH_JSONRPC - if (jsonrpcServer.get()) - jsonrpcServer->StopListening(); -#endif + keyManager.create(masterPassword); + } + + for (auto const& s: toImport) + { + keyManager.import(s, "Imported key (UNSAFE)"); + if (!signingKey) + signingKey = toAddress(s); + } + + if (keyManager.accounts().empty()) + keyManager.import(Secret::random(), "Default key"); + + cout << ethCredits(); + web3.setIdealPeerCount(peers); +// std::shared_ptr gasPricer = make_shared(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000)); + std::shared_ptr gasPricer = make_shared(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); + c->setTurboMining(m.minerType() == MinerCLI::MinerType::GPU); + c->setAddress(beneficiary); + c->setNetworkId(networkId); + } + + cout << "Transaction Signer: " << signingKey << endl; + cout << "Mining Benefactor: " << beneficiary << endl; + + if (bootstrap || !remoteHost.empty()) + { + 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 && jsonrpc == -1) + jsonrpc = SensibleHttpPort; + +#if ETH_JSONRPC || !ETH_TRUE + shared_ptr jsonrpcServer; + unique_ptr jsonrpcConnector; + if (jsonrpc > -1) + { + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); + jsonrpcServer = make_shared(*jsonrpcConnector.get(), web3, make_shared([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector(), keyManager, *gasPricer); + jsonrpcServer->setMiningBenefactorChanger([&](Address const& a) { beneficiary = a; }); + jsonrpcServer->StartListening(); + if (jsonAdmin.empty()) + jsonAdmin = jsonrpcServer->newSession(SessionPermissions{{Priviledge::Admin}}); + else + jsonrpcServer->addSession(jsonAdmin, SessionPermissions{{Priviledge::Admin}}); + cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl; } +#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; + unsigned n = c->blockChain().details().number; if (mining) c->startMining(); if (useConsole) { -#if ETH_JSCONSOLE +#if ETH_JSCONSOLE || !ETH_TRUE JSLocalConsole console; - - jsonrpcServer = shared_ptr(new dev::WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector(), keyManager, *gasPricer)); - jsonrpcServer->setMiningBenefactorChanger([&](Address const& a) { beneficiary = a; }); - jsonrpcServer->StartListening(); - if (jsonAdmin.empty()) - jsonAdmin = jsonrpcServer->newSession(SessionPermissions{{Priviledge::Admin}}); - else - jsonrpcServer->addSession(jsonAdmin, SessionPermissions{{Priviledge::Admin}}); - cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl; - + // TODO: set port properly? while (!g_exit) { console.readExpression(); stopMiningAfterXBlocks(c, n, mining); } - jsonrpcServer->StopListening(); #endif } @@ -1856,9 +1753,15 @@ int main(int argc, char** argv) 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; } + diff --git a/ethkey/KeyAux.h b/ethkey/KeyAux.h index ae8eaed92..af7d8e048 100644 --- a/ethkey/KeyAux.h +++ b/ethkey/KeyAux.h @@ -32,6 +32,7 @@ #include #include #include +#include #include "BuildInfo.h" using namespace std; using namespace dev; @@ -105,7 +106,9 @@ public: ImportWithAddress, Export, Recode, - Kill + Kill, + SignTx, + DecodeTx, }; KeyCLI(OperationMode _mode = OperationMode::None): m_mode(_mode) {} @@ -131,8 +134,13 @@ public: auto v = argv[++i]; m_kdfParams[n] = v; } - else if (arg == "--new-bare") - m_mode = OperationMode::NewBare; + else if (arg == "--sign-tx" && i + 1 < argc) + { + m_mode = OperationMode::SignTx; + m_signKey = argv[++i]; + } + else if (arg == "--decode-tx") + m_mode = OperationMode::DecodeTx; else if (arg == "--import-bare") m_mode = OperationMode::ImportBare; else if (arg == "--list-bare") @@ -173,7 +181,7 @@ public: m_mode = OperationMode::Recode; else if (arg == "--no-icap") m_icap = false; - else if (m_mode == OperationMode::ImportBare || m_mode == OperationMode::InspectBare || m_mode == OperationMode::KillBare || m_mode == OperationMode::Recode || m_mode == OperationMode::Export || m_mode == OperationMode::RecodeBare || m_mode == OperationMode::ExportBare) + else if (m_mode == OperationMode::DecodeTx || m_mode == OperationMode::SignTx || m_mode == OperationMode::ImportBare || m_mode == OperationMode::InspectBare || m_mode == OperationMode::KillBare || m_mode == OperationMode::Recode || m_mode == OperationMode::Export || m_mode == OperationMode::RecodeBare || m_mode == OperationMode::ExportBare) m_inputs.push_back(arg); else return false; @@ -209,6 +217,127 @@ public: } } } + else if (m_mode == OperationMode::DecodeTx) + { + string const& i = m_inputs[0]; + bytes b = fromHex(i); + if (b.empty()) + { + std::string s = contentsString(i); + b = fromHex(s); + if (b.empty()) + b = asBytes(s); + } + if (b.empty()) + cerr << "Unknown file or bad hex: " << i << endl; + else + try + { + TransactionBase t(b, CheckTransaction::Everything); + cout << "Transaction " << t.sha3().hex() << endl; + if (t.isCreation()) + { + cout << " type: creation" << endl; + cout << " code: " << toHex(t.data()) << endl; + } + else + { + cout << " type: message" << endl; + cout << " to: " << t.to().hex() << endl; + cout << " data: " << (t.data().empty() ? "none" : toHex(t.data())) << endl; + } + cout << " from: " << t.from().hex() << endl; + cout << " value: " << formatBalance(t.value()) << " (" << t.value() << " wei)" << endl; + cout << " nonce: " << t.nonce() << endl; + cout << " gas: " << t.gas() << endl; + cout << " gas price: " << formatBalance(t.gasPrice()) << " (" << t.gasPrice() << " wei)" << endl; + cout << " signing hash: " << t.sha3(WithoutSignature).hex() << endl; + cout << " v: " << (int)t.signature().v << endl; + cout << " r: " << t.signature().r << endl; + cout << " s: " << t.signature().s << endl; + } + catch (Exception& ex) + { + cerr << "Invalid transaction: " << ex.what() << endl; + } + } + else if (m_mode == OperationMode::SignTx) + { + Secret s; + + string json = contentsString(m_signKey); + if (!json.empty()) + { + SecretStore store(m_secretsPath); + s = Secret(store.secret(store.readKeyContent(json), [&](){ return getPassword("Enter password for key: "); })); + } + else + { + if (h128 u = fromUUID(m_signKey)) + { + SecretStore store(m_secretsPath); + s = Secret(store.secret(u, [&](){ return getPassword("Enter password for key: "); })); + } + else if (Address a = Address(m_signKey)) + { + KeyManager wallet(m_walletPath, m_secretsPath); + if (wallet.exists()) + { + openWallet(wallet); + s = wallet.secret(a, [&](){ return getPassword("Enter password for key: "); }); + } + else + { + cerr << "Wallet doesn't exist." << endl; + exit(-1); + } + } + else + { + cerr << "Bad file, UUID and address: " << m_signKey << endl; + exit(-1); + } + } + if (!s) + { + cerr << "UUID/address not found: " << m_signKey << endl; + exit(-1); + } + + for (string const& i: m_inputs) + { + bytes b = fromHex(i); + bool isFile = false; + if (b.empty()) + { + isFile = true; + std::string s = contentsString(i); + b = fromHex(s); + if (b.empty()) + b = asBytes(s); + } + if (b.empty()) + cerr << "Unknown file or bad hex: " << i << endl; + else + try + { + TransactionBase t(b, CheckTransaction::None); + t.sign(s); + cout << t.sha3() << ": "; + if (isFile) + { + writeFile(i + ".signed", t.data()); + cout << i + ".signed" << endl; + } + else + cout << toHex(t.data()) << endl; + } + catch (Exception& ex) + { + cerr << "Invalid transaction: " << ex.what() << endl; + } + } + } else if (m_mode < OperationMode::CreateWallet) { SecretStore store(m_secretsPath); @@ -297,17 +426,7 @@ public: { KeyManager wallet(m_walletPath, m_secretsPath); if (wallet.exists()) - while (true) - { - if (wallet.load(m_masterPassword)) - break; - if (!m_masterPassword.empty()) - { - cout << "Password invalid. Try again." << endl; - m_masterPassword.clear(); - } - m_masterPassword = getPassword("Please enter your MASTER password: "); - } + openWallet(wallet); else { cerr << "Couldn't open wallet. Does it exist?" << endl; @@ -419,6 +538,10 @@ public: << " --wallet-path Specify Ethereum wallet path (default: " << KeyManager::defaultPath() << ")" << endl << " -m, --master Specify wallet (master) password." << endl << endl + << "Transaction operating modes:" << endl + << " -d,--decode-tx [|] Decode given transaction." << endl + << " -s,--sign-tx [
|| ] [ | , ... ] (Re-)Sign given transaction." << endl + << endl << "Encryption configuration:" << endl << " --kdf Specify KDF to use when encrypting (default: sc rypt)" << endl << " --kdf-param Specify a parameter for the KDF." << endl @@ -445,6 +568,21 @@ public: } private: + void openWallet(KeyManager& _w) + { + while (true) + { + if (_w.load(m_masterPassword)) + break; + if (!m_masterPassword.empty()) + { + cout << "Password invalid. Try again." << endl; + m_masterPassword.clear(); + } + m_masterPassword = getPassword("Please enter your MASTER password: "); + } + } + KDF kdf() const { return m_kdf == "pbkdf2" ? KDF::PBKDF2_SHA256 : KDF::Scrypt; } /// Operating mode. @@ -468,6 +606,9 @@ private: /// Importing strings m_inputs; + /// Signing + string m_signKey; + string m_kdf = "scrypt"; map m_kdfParams; // string m_cipher; diff --git a/ethminer/MinerAux.h b/ethminer/MinerAux.h index ec6ee57e7..b8305370c 100644 --- a/ethminer/MinerAux.h +++ b/ethminer/MinerAux.h @@ -217,7 +217,7 @@ public: auto boundary = bi.boundary(); m = boost::to_lower_copy(string(argv[++i])); bi.nonce = h64(m); - auto r = EthashAux::eval(bi.seedHash(), powHash, bi.nonce); + auto r = EthashAux::eval(seedHash, powHash, bi.nonce); bool valid = r.value < boundary; cout << (valid ? "VALID :-)" : "INVALID :-(") << endl; cout << r.value << (valid ? " < " : " >= ") << boundary << endl; diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index 5450f9311..5591e94bf 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -28,7 +28,7 @@ using namespace dev; namespace dev { -char const* Version = "0.9.27"; +char const* Version = "0.9.28"; const u256 UndefinedU256 = ~(u256)0; diff --git a/libdevcore/Common.h b/libdevcore/Common.h index c7fdb6dab..edd14f4a5 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -64,6 +64,8 @@ using byte = uint8_t; #define DEV_IGNORE_EXCEPTIONS(X) try { X; } catch (...) {} +#define DEV_IF_NO_ELSE(X) if(!(X)){}else + namespace dev { diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index ed09e60ee..0faba8e8e 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -50,8 +50,8 @@ enum class HexPrefix /// Convert a series of bytes to the corresponding string of hex duplets. /// @param _w specifies the width of the first of the elements. Defaults to two - enough to represent a byte. /// @example toHex("A\x69") == "4169" -template -std::string toHex(_T const& _data, int _w = 2, HexPrefix _prefix = HexPrefix::DontAdd) +template +std::string toHex(T const& _data, int _w = 2, HexPrefix _prefix = HexPrefix::DontAdd) { std::ostringstream ret; unsigned ii = 0; @@ -99,27 +99,27 @@ bytes asNibbles(bytesConstRef const& _s); /// Converts a templated integer value to the big-endian byte-stream represented on a templated collection. /// The size of the collection object will be unchanged. If it is too small, it will not represent the /// value properly, if too big then the additional elements will be zeroed out. -/// @a _Out will typically be either std::string or bytes. -/// @a _T will typically by unsigned, u160, u256 or bigint. -template -inline void toBigEndian(_T _val, _Out& o_out) +/// @a Out will typically be either std::string or bytes. +/// @a T will typically by unsigned, u160, u256 or bigint. +template +inline void toBigEndian(T _val, Out& o_out) { for (auto i = o_out.size(); i != 0; _val >>= 8, i--) { - _T v = _val & (_T)0xff; - o_out[i - 1] = (typename _Out::value_type)(uint8_t)v; + T v = _val & (T)0xff; + o_out[i - 1] = (typename Out::value_type)(uint8_t)v; } } /// Converts a big-endian byte-stream represented on a templated collection to a templated integer value. /// @a _In will typically be either std::string or bytes. -/// @a _T will typically by unsigned, u160, u256 or bigint. -template -inline _T fromBigEndian(_In const& _bytes) +/// @a T will typically by unsigned, u160, u256 or bigint. +template +inline T fromBigEndian(_In const& _bytes) { - _T ret = (_T)0; + T ret = (T)0; for (auto i: _bytes) - ret = (_T)((ret << 8) | (byte)(typename std::make_unsigned::type)i); + ret = (T)((ret << 8) | (byte)(typename std::make_unsigned::type)i); return ret; } @@ -131,11 +131,11 @@ inline bytes toBigEndian(u160 _val) { bytes ret(20); toBigEndian(_val, ret); ret /// Convenience function for toBigEndian. /// @returns a byte array just big enough to represent @a _val. -template -inline bytes toCompactBigEndian(_T _val, unsigned _min = 0) +template +inline bytes toCompactBigEndian(T _val, unsigned _min = 0) { int i = 0; - for (_T v = _val; v; ++i, v >>= 8) {} + for (T v = _val; v; ++i, v >>= 8) {} bytes ret(std::max(_min, i), 0); toBigEndian(_val, ret); return ret; @@ -147,11 +147,11 @@ inline bytes toCompactBigEndian(byte _val, unsigned _min = 0) /// Convenience function for toBigEndian. /// @returns a string just big enough to represent @a _val. -template -inline std::string toCompactBigEndianString(_T _val, unsigned _min = 0) +template +inline std::string toCompactBigEndianString(T _val, unsigned _min = 0) { int i = 0; - for (_T v = _val; v; ++i, v >>= 8) {} + for (T v = _val; v; ++i, v >>= 8) {} std::string ret(std::max(_min, i), '\0'); toBigEndian(_val, ret); return ret; @@ -179,8 +179,8 @@ std::string escaped(std::string const& _s, bool _all = true); /// Determines the length of the common prefix of the two collections given. /// @returns the number of elements both @a _t and @a _u share, in order, at the beginning. /// @example commonPrefix("Hello world!", "Hello, world!") == 5 -template -unsigned commonPrefix(_T const& _t, _U const& _u) +template +unsigned commonPrefix(T const& _t, _U const& _u) { unsigned s = std::min(_t.size(), _u.size()); for (unsigned i = 0;; ++i) @@ -196,8 +196,8 @@ std::string randomWord(); // General datatype convenience functions. /// Determine bytes required to encode the given integer value. @returns 0 if @a _i is zero. -template -inline unsigned bytesRequired(_T _i) +template +inline unsigned bytesRequired(T _i) { unsigned i = 0; for (; _i != 0; ++i, _i >>= 8) {} @@ -206,39 +206,39 @@ inline unsigned bytesRequired(_T _i) /// Trims a given number of elements from the front of a collection. /// Only works for POD element types. -template -void trimFront(_T& _t, unsigned _elements) +template +void trimFront(T& _t, unsigned _elements) { - static_assert(std::is_pod::value, ""); + static_assert(std::is_pod::value, ""); memmove(_t.data(), _t.data() + _elements, (_t.size() - _elements) * sizeof(_t[0])); _t.resize(_t.size() - _elements); } /// Pushes an element on to the front of a collection. /// Only works for POD element types. -template -void pushFront(_T& _t, _U _e) +template +void pushFront(T& _t, _U _e) { - static_assert(std::is_pod::value, ""); + static_assert(std::is_pod::value, ""); _t.push_back(_e); memmove(_t.data() + 1, _t.data(), (_t.size() - 1) * sizeof(_e)); _t[0] = _e; } /// Concatenate two vectors of elements of POD types. -template -inline std::vector<_T>& operator+=(std::vector::value, _T>::type>& _a, std::vector<_T> const& _b) +template +inline std::vector& operator+=(std::vector::value, T>::type>& _a, std::vector const& _b) { auto s = _a.size(); _a.resize(_a.size() + _b.size()); - memcpy(_a.data() + s, _b.data(), _b.size() * sizeof(_T)); + memcpy(_a.data() + s, _b.data(), _b.size() * sizeof(T)); return _a; } /// Concatenate two vectors of elements. -template -inline std::vector<_T>& operator+=(std::vector::value, _T>::type>& _a, std::vector<_T> const& _b) +template +inline std::vector& operator+=(std::vector::value, T>::type>& _a, std::vector const& _b) { _a.reserve(_a.size() + _b.size()); for (auto& i: _b) @@ -289,16 +289,16 @@ template std::vector operator+(std::vector _a, U const& } /// Concatenate two vectors of elements. -template -inline std::vector<_T> operator+(std::vector<_T> const& _a, std::vector<_T> const& _b) +template +inline std::vector operator+(std::vector const& _a, std::vector const& _b) { - std::vector<_T> ret(_a); + std::vector ret(_a); return ret += _b; } /// Merge two sets of elements. -template -inline std::set<_T>& operator+=(std::set<_T>& _a, std::set<_T> const& _b) +template +inline std::set& operator+=(std::set& _a, std::set const& _b) { for (auto& i: _b) _a.insert(i); @@ -306,13 +306,28 @@ inline std::set<_T>& operator+=(std::set<_T>& _a, std::set<_T> const& _b) } /// Merge two sets of elements. -template -inline std::set<_T> operator+(std::set<_T> const& _a, std::set<_T> const& _b) +template +inline std::set operator+(std::set const& _a, std::set const& _b) { - std::set<_T> ret(_a); + std::set ret(_a); return ret += _b; } +template +std::unordered_map& operator+=(std::unordered_map& _x, std::unordered_map const& _y) +{ + for (auto const& i: _y) + _x.insert(i); + return _x; +} + +template +std::unordered_map operator+(std::unordered_map const& _x, std::unordered_map const& _y) +{ + std::unordered_map ret(_x); + return ret += _y; +} + /// Make normal string from fixed-length string. std::string toString(string32 const& _s); diff --git a/libdevcore/RLP.cpp b/libdevcore/RLP.cpp index 330893c76..92d97aec4 100644 --- a/libdevcore/RLP.cpp +++ b/libdevcore/RLP.cpp @@ -320,7 +320,7 @@ std::ostream& dev::operator<<(std::ostream& _out, RLP const& _d) if (_d.isNull()) _out << "null"; else if (_d.isInt()) - _out << std::showbase << std::hex << std::nouppercase << _d.toInt(RLP::LaisezFaire) << dec; + _out << std::showbase << std::hex << std::nouppercase << _d.toInt(RLP::LaissezFaire) << dec; else if (_d.isData()) _out << escaped(_d.toString(), false); else if (_d.isList()) diff --git a/libdevcore/RLP.h b/libdevcore/RLP.h index dee438be4..67f464b9d 100644 --- a/libdevcore/RLP.h +++ b/libdevcore/RLP.h @@ -71,7 +71,7 @@ public: FailIfTooSmall = 16, Strict = ThrowOnFail | FailIfTooBig, VeryStrict = ThrowOnFail | FailIfTooBig | FailIfTooSmall, - LaisezFaire = AllowNonCanon + LaissezFaire = AllowNonCanon }; using Strictness = int; diff --git a/libdevcore/RangeMask.h b/libdevcore/RangeMask.h index 7c402fc98..eccc8305d 100644 --- a/libdevcore/RangeMask.h +++ b/libdevcore/RangeMask.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,12 @@ class RLPStream; using UnsignedRange = std::pair; using UnsignedRanges = std::vector; +/** + * Set of elements of a certain "ground range" representable by unions of ranges inside this + * ground range. + * Ranges are given as pairs (begin, end), denoting the interval [begin, end), i.e. end is excluded. + * Supports set-theoretic operators, size and iteration. + */ template class RangeMask { @@ -44,14 +51,19 @@ public: using Range = std::pair; using Ranges = std::vector; + /// Constructs an empty range mask with empty ground range. RangeMask(): m_all(0, 0) {} + /// Constructs an empty range mask with ground range [_begin, _end). RangeMask(T _begin, T _end): m_all(_begin, _end) {} + /// Constructs an empty range mask with ground range _c. RangeMask(Range const& _c): m_all(_c) {} + /// @returns the union with the range mask _m, taking also the union of the ground ranges. RangeMask unionedWith(RangeMask const& _m) const { return operator+(_m); } RangeMask operator+(RangeMask const& _m) const { return RangeMask(*this) += _m; } - RangeMask lowest(T _items) const + /// @returns a new range mask containing the smallest _items elements (not ranges). + RangeMask lowest(decltype(T{} - T{}) _items) const { RangeMask ret(m_all); for (auto i = m_ranges.begin(); i != m_ranges.end() && _items; ++i) @@ -59,8 +71,10 @@ public: return ret; } + /// @returns the complement of the range mask relative to the ground range. RangeMask operator~() const { return inverted(); } + /// @returns a copy of this range mask representing the complement relative to the ground range. RangeMask inverted() const { RangeMask ret(m_all); @@ -76,6 +90,8 @@ public: return ret; } + /// Changes the range mask to its complement relative to the ground range and returns a + /// reference to itself. RangeMask& invert() { return *this = inverted(); } template RangeMask operator-(S const& _m) const { auto ret = *this; return ret -= _m; } @@ -92,61 +108,13 @@ public: return *this; } RangeMask& operator+=(Range const& _m) { return unionWith(_m); } - RangeMask& unionWith(Range const& _m) - { - for (auto i = _m.first; i < _m.second;) - { - assert(i >= m_all.first); - assert(i < m_all.second); - // for each number, we find the element equal or next lower. this, if any, must contain the value. - auto uit = m_ranges.upper_bound(i); - auto it = uit == m_ranges.begin() ? m_ranges.end() : std::prev(uit); - if (it == m_ranges.end() || it->second < i) - // lower range is too low to merge. - // if the next higher range is too high. - if (uit == m_ranges.end() || uit->first > _m.second) - { - // just create a new range - m_ranges[i] = _m.second; - break; - } - else - { - if (uit->first == i) - // move i to end of range - i = uit->second; - else - { - // merge with the next higher range - // move i to end of range - i = m_ranges[i] = uit->second; - i = uit->second; - m_ranges.erase(uit); - } - } - else if (it->second == i) - { - // if the next higher range is too high. - if (uit == m_ranges.end() || uit->first > _m.second) - { - // merge with the next lower range - m_ranges[it->first] = _m.second; - break; - } - else - { - // merge with both next lower & next higher. - i = m_ranges[it->first] = uit->second; - m_ranges.erase(uit); - } - } - else - i = it->second; - } - return *this; - } + /// Modifies this range mask to also include the range _m, which has to be a subset of + /// the ground range. + RangeMask& unionWith(Range const& _m); + /// Adds the single element _i to the range mask. RangeMask& operator+=(T _m) { return unionWith(_m); } + /// Adds the single element _i to the range mask. RangeMask& unionWith(T _i) { return operator+=(Range(_i, _i + 1)); @@ -181,10 +149,12 @@ public: m_all = std::make_pair(0, 0); } + /// @returns the ground range. std::pair const& all() const { return m_all; } + /// Extends the ground range to include _i. void extendAll(T _i) { m_all = std::make_pair(std::min(m_all.first, _i), std::max(m_all.second, _i + 1)); } - class const_iterator + class const_iterator: public std::iterator { friend class RangeMask; @@ -208,6 +178,8 @@ public: const_iterator begin() const { return const_iterator(*this, false); } const_iterator end() const { return const_iterator(*this, true); } + /// @returns the smallest element in the range mask that is larger than _t or the end of the + /// base range if such an element does not exist. T next(T _t) const { _t++; @@ -219,6 +191,7 @@ public: return uit == m_ranges.end() ? m_all.second : uit->first; } + /// @returns the number of elements (not ranges) in the range mask. size_t size() const { size_t c = 0; @@ -228,7 +201,9 @@ public: } private: + /// The ground range. UnsignedRange m_all; + /// Mapping begin -> end containing the ranges. std::map m_ranges; }; @@ -241,4 +216,65 @@ template inline std::ostream& operator<<(std::ostream& _out, RangeMask return _out; } +template +RangeMask& RangeMask::unionWith(typename RangeMask::Range const& _m) +{ + for (auto i = _m.first; i < _m.second;) + { + assert(i >= m_all.first); + assert(i < m_all.second); + // For each number, we find the element equal or next lower. this, if any, must contain the value. + // First range that starts after i. + auto rangeAfter = m_ranges.upper_bound(i); + // Range before rangeAfter or "end" if the rangeAfter is the first ever... + auto it = rangeAfter == m_ranges.begin() ? m_ranges.end() : std::prev(rangeAfter); + if (it == m_ranges.end() || it->second < i) + { + // i is either before the first range or between two ranges (with some distance + // so that we cannot merge it onto "it"). + // lower range is too low to merge. + // if the next higher range is too high. + if (rangeAfter == m_ranges.end() || rangeAfter->first > _m.second) + { + // just create a new range + m_ranges[i] = _m.second; + break; + } + else + { + if (rangeAfter->first == i) + // move i to end of range + i = rangeAfter->second; + else + { + // merge with the next higher range + // move i to end of range + i = m_ranges[i] = rangeAfter->second; + m_ranges.erase(rangeAfter); + } + } + } + else if (it->second == i) + { + // The range before i ends with i. + // if the next higher range is too high. + if (rangeAfter == m_ranges.end() || rangeAfter->first > _m.second) + { + // merge with the next lower range + m_ranges[it->first] = _m.second; + break; + } + else + { + // merge with both next lower & next higher. + i = m_ranges[it->first] = rangeAfter->second; + m_ranges.erase(rangeAfter); + } + } + else + i = it->second; + } + return *this; +} + } diff --git a/libethcore/CMakeLists.txt b/libethcore/CMakeLists.txt index a527ad1f4..4dd626642 100644 --- a/libethcore/CMakeLists.txt +++ b/libethcore/CMakeLists.txt @@ -28,7 +28,7 @@ add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) target_link_libraries(${EXECUTABLE} ethash) target_link_libraries(${EXECUTABLE} devcrypto) -#target_link_libraries(${EXECUTABLE} evmcore) +target_link_libraries(${EXECUTABLE} evmcore) if (ETHASHCL) target_link_libraries(${EXECUTABLE} ethash-cl) diff --git a/libethcore/Transaction.cpp b/libethcore/Transaction.cpp new file mode 100644 index 000000000..5e08acd4d --- /dev/null +++ b/libethcore/Transaction.cpp @@ -0,0 +1,118 @@ +/* + 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 TransactionBase.cpp + * @author Gav Wood + * @date 2014 + */ + +#include +#include +#include +#include +#include +#include "Transaction.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +TransactionBase::TransactionBase(bytesConstRef _rlpData, CheckTransaction _checkSig) +{ + int field = 0; + RLP rlp(_rlpData); + try + { + if (!rlp.isList()) + BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction RLP must be a list")); + + m_nonce = rlp[field = 0].toInt(); + m_gasPrice = rlp[field = 1].toInt(); + m_gas = rlp[field = 2].toInt(); + m_type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall; + m_receiveAddress = rlp[field = 3].isEmpty() ? Address() : rlp[field = 3].toHash
(RLP::VeryStrict); + m_value = rlp[field = 4].toInt(); + + if (!rlp[field = 5].isData()) + BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction data RLP must be an array")); + + m_data = rlp[field = 5].toBytes(); + byte v = rlp[field = 6].toInt() - 27; + h256 r = rlp[field = 7].toInt(); + h256 s = rlp[field = 8].toInt(); + + if (rlp.itemCount() > 9) + BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("to many fields in the transaction RLP")); + + m_vrs = SignatureStruct{ r, s, v }; + if (_checkSig >= CheckTransaction::Cheap && !m_vrs.isValid()) + BOOST_THROW_EXCEPTION(InvalidSignature()); + if (_checkSig == CheckTransaction::Everything) + m_sender = sender(); + } + catch (Exception& _e) + { + _e << errinfo_name("invalid transaction format") << BadFieldError(field, toHex(rlp[field].data().toBytes())); + throw; + } +} + +Address const& TransactionBase::safeSender() const noexcept +{ + try + { + return sender(); + } + catch (...) + { + cwarn << "safeSender() did throw an exception: " << boost::current_exception_diagnostic_information(); + return ZeroAddress; + } +} + +Address const& TransactionBase::sender() const +{ + if (!m_sender) + { + auto p = recover(m_vrs, sha3(WithoutSignature)); + if (!p) + BOOST_THROW_EXCEPTION(InvalidSignature()); + m_sender = right160(dev::sha3(bytesConstRef(p.data(), sizeof(p)))); + } + return m_sender; +} + +void TransactionBase::sign(Secret const& _priv) +{ + auto sig = dev::sign(_priv, sha3(WithoutSignature)); + SignatureStruct sigStruct = *(SignatureStruct const*)&sig; + if (sigStruct.isValid()) + m_vrs = sigStruct; +} + +void TransactionBase::streamRLP(RLPStream& _s, IncludeSignature _sig) const +{ + if (m_type == NullTransaction) + return; + _s.appendList((_sig ? 3 : 0) + 6); + _s << m_nonce << m_gasPrice << m_gas; + if (m_type == MessageCall) + _s << m_receiveAddress; + else + _s << ""; + _s << m_value << m_data; + if (_sig) + _s << (m_vrs.v + 27) << (u256)m_vrs.r << (u256)m_vrs.s; +} diff --git a/libethcore/Transaction.h b/libethcore/Transaction.h new file mode 100644 index 000000000..e5eeb74b4 --- /dev/null +++ b/libethcore/Transaction.h @@ -0,0 +1,177 @@ +/* + 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 TransactionBase.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include + +namespace dev +{ +namespace eth +{ + +/// Named-boolean type to encode whether a signature be included in the serialisation process. +enum IncludeSignature +{ + WithoutSignature = 0, ///< Do not include a signature. + WithSignature = 1, ///< Do include a signature. +}; + +enum class CheckTransaction +{ + None, + Cheap, + Everything +}; + +/// Encodes a transaction, ready to be exported to or freshly imported from RLP. +class TransactionBase +{ +public: + /// Constructs a null transaction. + TransactionBase() {} + + /// Constructs a signed message-call transaction. + TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); } + + /// Constructs a signed contract-creation transaction. + TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); } + + /// Constructs an unsigned message-call transaction. + TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce = 0): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {} + + /// Constructs an unsigned contract-creation transaction. + TransactionBase(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce = 0): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {} + + /// Constructs a transaction from the given RLP. + explicit TransactionBase(bytesConstRef _rlp, CheckTransaction _checkSig); + + /// Constructs a transaction from the given RLP. + explicit TransactionBase(bytes const& _rlp, CheckTransaction _checkSig): TransactionBase(&_rlp, _checkSig) {} + + + /// Checks equality of transactions. + bool operator==(TransactionBase const& _c) const { return m_type == _c.m_type && (m_type == ContractCreation || m_receiveAddress == _c.m_receiveAddress) && m_value == _c.m_value && m_data == _c.m_data; } + /// Checks inequality of transactions. + bool operator!=(TransactionBase const& _c) const { return !operator==(_c); } + + /// @returns sender of the transaction from the signature (and hash). + Address const& sender() const; + /// Like sender() but will never throw. @returns a null Address if the signature is invalid. + Address const& safeSender() const noexcept; + /// Force the sender to a particular value. This will result in an invalid transaction RLP. + void forceSender(Address const& _a) { m_sender = _a; } + + /// @returns true if transaction is non-null. + explicit operator bool() const { return m_type != NullTransaction; } + + /// @returns true if transaction is contract-creation. + bool isCreation() const { return m_type == ContractCreation; } + + /// @returns true if transaction is message-call. + bool isMessageCall() const { return m_type == MessageCall; } + + /// Serialises this transaction to an RLPStream. + void streamRLP(RLPStream& _s, IncludeSignature _sig = WithSignature) const; + + /// @returns the RLP serialisation of this transaction. + bytes rlp(IncludeSignature _sig = WithSignature) const { RLPStream s; streamRLP(s, _sig); return s.out(); } + + /// @returns the SHA3 hash of the RLP serialisation of this transaction. + h256 sha3(IncludeSignature _sig = WithSignature) const { if (_sig == WithSignature && m_hashWith) return m_hashWith; RLPStream s; streamRLP(s, _sig); auto ret = dev::sha3(s.out()); if (_sig == WithSignature) m_hashWith = ret; return ret; } + + /// @returns the amount of ETH to be transferred by this (message-call) transaction, in Wei. Synonym for endowment(). + u256 value() const { return m_value; } + /// @returns the amount of ETH to be endowed by this (contract-creation) transaction, in Wei. Synonym for value(). + u256 endowment() const { return m_value; } + + /// @returns the base fee and thus the implied exchange rate of ETH to GAS. + u256 gasPrice() const { return m_gasPrice; } + + /// @returns the total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended. + u256 gas() const { return m_gas; } + + /// @returns the receiving address of the message-call transaction (undefined for contract-creation transactions). + Address receiveAddress() const { return m_receiveAddress; } + + /// Synonym for receiveAddress(). + Address to() const { return m_receiveAddress; } + + /// Synonym for safeSender(). + Address from() const { return safeSender(); } + + /// @returns the data associated with this (message-call) transaction. Synonym for initCode(). + bytes const& data() const { return m_data; } + /// @returns the initialisation code associated with this (contract-creation) transaction. Synonym for data(). + bytes const& initCode() const { return m_data; } + + /// @returns the transaction-count of the sender. + u256 nonce() const { return m_nonce; } + + /// @returns the signature of the transaction. Encodes the sender. + SignatureStruct const& signature() const { return m_vrs; } + + void sign(Secret const& _priv); ///< Sign the transaction. + +protected: + /// Type of transaction. + enum Type + { + NullTransaction, ///< Null transaction. + ContractCreation, ///< Transaction to create contracts - receiveAddress() is ignored. + MessageCall ///< Transaction to invoke a message call - receiveAddress() is used. + }; + + Type m_type = NullTransaction; ///< Is this a contract-creation transaction or a message-call transaction? + u256 m_nonce; ///< The transaction-count of the sender. + u256 m_value; ///< The amount of ETH to be transferred by this transaction. Called 'endowment' for contract-creation transactions. + Address m_receiveAddress; ///< The receiving address of the transaction. + u256 m_gasPrice; ///< The base fee and thus the implied exchange rate of ETH to GAS. + u256 m_gas; ///< The total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended. + bytes m_data; ///< The data associated with the transaction, or the initialiser if it's a creation transaction. + SignatureStruct m_vrs; ///< The signature of the transaction. Encodes the sender. + + mutable h256 m_hashWith; ///< Cached hash of transaction with signature. + mutable Address m_sender; ///< Cached sender, determined from signature. + mutable bigint m_gasRequired = 0; ///< Memoised amount required for the transaction to run. +}; + +/// Nice name for vector of Transaction. +using TransactionBases = std::vector; + +/// Simple human-readable stream-shift operator. +inline std::ostream& operator<<(std::ostream& _out, TransactionBase const& _t) +{ + _out << _t.sha3().abridged() << "{"; + if (_t.receiveAddress()) + _out << _t.receiveAddress().abridged(); + else + _out << "[CREATE]"; + + _out << "/" << _t.data().size() << "$" << _t.value() << "+" << _t.gas() << "@" << _t.gasPrice(); + _out << "<-" << _t.safeSender().abridged() << " #" << _t.nonce() << "}"; + return _out; +} + +} +} diff --git a/libethereum/BasicGasPricer.cpp b/libethereum/BasicGasPricer.cpp new file mode 100644 index 000000000..145d23594 --- /dev/null +++ b/libethereum/BasicGasPricer.cpp @@ -0,0 +1,95 @@ +/* + 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 BasicGasPricer.cpp + * @author Gav Wood + * @date 2015 + */ + +#include +#include "BasicGasPricer.h" +#include "BlockChain.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +void BasicGasPricer::update(BlockChain const& _bc) +{ + unsigned c = 0; + h256 p = _bc.currentHash(); + m_gasPerBlock = _bc.info(p).gasLimit; + + map dist; + u256 total = 0; + + // make gasPrice versus gasUsed distribution for the last 1000 blocks + while (c < 1000 && p) + { + BlockInfo bi = _bc.info(p); + if (bi.transactionsRoot != EmptyTrie) + { + auto bb = _bc.block(p); + RLP r(bb); + BlockReceipts brs(_bc.receipts(bi.hash())); + size_t i = 0; + for (auto const& tr: r[1]) + { + Transaction tx(tr.data(), CheckTransaction::None); + u256 gu = brs.receipts[i].gasUsed(); + dist[tx.gasPrice()] += gu; + total += gu; + i++; + } + } + p = bi.parentHash; + ++c; + } + + // fill m_octiles with weighted gasPrices + if (total > 0) + { + m_octiles[0] = dist.begin()->first; + + // calc mean + u256 mean = 0; + for (auto const& i: dist) + mean += i.first * i.second; + mean /= total; + + // calc standard deviation + u256 sdSquared = 0; + for (auto const& i: dist) + sdSquared += i.second * (i.first - mean) * (i.first - mean); + sdSquared /= total; + + if (sdSquared) + { + long double sd = sqrt(sdSquared.convert_to()); + long double normalizedSd = sd / mean.convert_to(); + + // calc octiles normalized to gaussian distribution + boost::math::normal gauss(1.0, (normalizedSd > 0.01) ? normalizedSd : 0.01); + for (size_t i = 1; i < 8; i++) + m_octiles[i] = u256(mean.convert_to() * boost::math::quantile(gauss, i / 8.0)); + m_octiles[8] = dist.rbegin()->first; + } + else + { + for (size_t i = 0; i < 9; i++) + m_octiles[i] = (i + 1) * mean / 5; + } + } +} diff --git a/libethereum/BasicGasPricer.h b/libethereum/BasicGasPricer.h new file mode 100644 index 000000000..77d4547c5 --- /dev/null +++ b/libethereum/BasicGasPricer.h @@ -0,0 +1,53 @@ +/* + 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 BasicGasPricer.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include "GasPricer.h" + +namespace dev +{ +namespace eth +{ + +class BasicGasPricer: public GasPricer +{ +public: + explicit BasicGasPricer(u256 _weiPerRef, u256 _refsPerBlock): m_weiPerRef(_weiPerRef), m_refsPerBlock(_refsPerBlock) {} + + void setRefPrice(u256 _weiPerRef) { if ((bigint)m_refsPerBlock * _weiPerRef > std::numeric_limits::max() ) BOOST_THROW_EXCEPTION(Overflow() << errinfo_comment("ether price * block fees is larger than 2**256-1, choose a smaller number.") ); else m_weiPerRef = _weiPerRef; } + void setRefBlockFees(u256 _refsPerBlock) { if ((bigint)m_weiPerRef * _refsPerBlock > std::numeric_limits::max() ) BOOST_THROW_EXCEPTION(Overflow() << errinfo_comment("ether price * block fees is larger than 2**256-1, choose a smaller number.") ); else m_refsPerBlock = _refsPerBlock; } + + u256 ask(State const&) const override { return m_weiPerRef * m_refsPerBlock / m_gasPerBlock; } + u256 bid(TransactionPriority _p = TransactionPriority::Medium) const override { return m_octiles[(int)_p] > 0 ? m_octiles[(int)_p] : (m_weiPerRef * m_refsPerBlock / m_gasPerBlock); } + + void update(BlockChain const& _bc) override; + +private: + u256 m_weiPerRef; + u256 m_refsPerBlock; + u256 m_gasPerBlock = 3141592; + std::array m_octiles; +}; + +} +} diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 47725e088..4e0266b5b 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -127,7 +127,7 @@ static const unsigned c_minCacheSize = 1024 * 1024 * 32; #endif -BlockChain::BlockChain(bytes const& _genesisBlock, std::string _path, WithExisting _we, ProgressCallback const& _p) +BlockChain::BlockChain(bytes const& _genesisBlock, std::string const& _path, WithExisting _we, ProgressCallback const& _p) { // initialise deathrow. m_cacheUsage.resize(c_collectionQueueSize); @@ -137,8 +137,7 @@ BlockChain::BlockChain(bytes const& _genesisBlock, std::string _path, WithExisti m_genesisBlock = _genesisBlock; m_genesisHash = sha3(RLP(m_genesisBlock)[0].data()); - open(_path, _we); - if (_we == WithExisting::Verify) + if (open(_path, _we) != c_minorProtocolVersion) rebuild(_path, _p); } @@ -147,24 +146,41 @@ BlockChain::~BlockChain() close(); } -void BlockChain::open(std::string const& _path, WithExisting _we) +unsigned BlockChain::open(std::string const& _path, WithExisting _we) { - std::string path = _path.empty() ? Defaults::get()->m_dbPath : _path; - boost::filesystem::create_directories(path); + string path = _path.empty() ? Defaults::get()->m_dbPath : _path; + string chainPath = path + "/" + toHex(m_genesisHash.ref().cropped(0, 4)); + string extrasPath = chainPath + "/" + toString(c_databaseVersion); + + boost::filesystem::create_directories(extrasPath); + + bytes status = contents(extrasPath + "/minor"); + unsigned lastMinor = c_minorProtocolVersion; + DEV_IGNORE_EXCEPTIONS(lastMinor = (unsigned)RLP(status)); + if (c_minorProtocolVersion != lastMinor) + { + cnote << "Killing extras database (DB minor version:" << lastMinor << " != our miner version: " << c_minorProtocolVersion << ")."; + DEV_IGNORE_EXCEPTIONS(boost::filesystem::remove_all(extrasPath + "/details.old")); + boost::filesystem::rename(extrasPath + "/extras", extrasPath + "/extras.old"); + boost::filesystem::remove_all(extrasPath + "/state"); + writeFile(extrasPath + "/minor", rlp(c_minorProtocolVersion)); + lastMinor = (unsigned)RLP(status); + } if (_we == WithExisting::Kill) { - boost::filesystem::remove_all(path + "/blocks"); - boost::filesystem::remove_all(path + "/details"); + cnote << "Killing blockchain & extras database (WithExisting::Kill)."; + boost::filesystem::remove_all(chainPath + "/blocks"); + boost::filesystem::remove_all(extrasPath + "/extras"); } ldb::Options o; o.create_if_missing = true; o.max_open_files = 256; - ldb::DB::Open(o, path + "/blocks", &m_blocksDB); - ldb::DB::Open(o, path + "/details", &m_extrasDB); + ldb::DB::Open(o, chainPath + "/blocks", &m_blocksDB); + ldb::DB::Open(o, extrasPath + "/extras", &m_extrasDB); if (!m_blocksDB || !m_extrasDB) { - if (boost::filesystem::space(path + "/blocks").available < 1024) + if (boost::filesystem::space(chainPath + "/blocks").available < 1024) { cwarn << "Not enough available space found on hard drive. Please free some up and then re-run. Bailing."; BOOST_THROW_EXCEPTION(NotEnoughAvailableSpace()); @@ -194,7 +210,8 @@ void BlockChain::open(std::string const& _path, WithExisting _we) m_lastBlockHash = l.empty() ? m_genesisHash : *(h256*)l.data(); m_lastBlockNumber = number(m_lastBlockHash); - cnote << "Opened blockchain DB. Latest: " << currentHash(); + cnote << "Opened blockchain DB. Latest: " << currentHash() << (lastMinor == c_minorProtocolVersion ? "(rebuild not needed)" : "*** REBUILD NEEDED ***"); + return lastMinor; } void BlockChain::close() @@ -208,11 +225,11 @@ void BlockChain::close() m_blocks.clear(); } -#define IGNORE_EXCEPTIONS(X) try { X; } catch (...) {} - void BlockChain::rebuild(std::string const& _path, std::function const& _progress, bool _prepPoW) { - std::string path = _path.empty() ? Defaults::get()->m_dbPath : _path; + string path = _path.empty() ? Defaults::get()->m_dbPath : _path; + string chainPath = path + "/" + toHex(m_genesisHash.ref().cropped(0, 4)); + string extrasPath = chainPath + "/" + toString(c_databaseVersion); #if ETH_PROFILING_GPERF ProfilerStart("BlockChain_rebuild.log"); @@ -220,16 +237,21 @@ void BlockChain::rebuild(std::string const& _path, std::function; class BlockChain { public: - BlockChain(bytes const& _genesisBlock, std::string _path, WithExisting _we, ProgressCallback const& _p = ProgressCallback()); + BlockChain(bytes const& _genesisBlock, std::string const& _path, WithExisting _we = WithExisting::Trust, ProgressCallback const& _p = ProgressCallback()); ~BlockChain(); /// Attempt a database re-open. @@ -178,6 +178,9 @@ public: std::vector withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest) const; std::vector withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest, unsigned _topLevel, unsigned _index) const; + /// Returns true if transaction is known. Thread-safe + bool isKnownTransaction(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); return !!ta; } + /// Get a transaction from its hash. Thread-safe. bytes transaction(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytes(); return transaction(ta.blockHash, ta.index); } std::pair transactionLocation(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return std::pair(h256(), 0); return std::make_pair(ta.blockHash, ta.index); } @@ -261,7 +264,7 @@ public: private: static h256 chunkId(unsigned _level, unsigned _index) { return h256(_index * 0xff + _level); } - void open(std::string const& _path, WithExisting _we = WithExisting::Trust); + unsigned open(std::string const& _path, WithExisting _we = WithExisting::Trust); void close(); template T queryExtras(h256 const& _h, std::unordered_map& _m, boost::shared_mutex& _x, T const& _n, ldb::DB* _extrasDB = nullptr) const diff --git a/libethereum/BlockChainSync.cpp b/libethereum/BlockChainSync.cpp index 63ac620ce..04bb44d0b 100644 --- a/libethereum/BlockChainSync.cpp +++ b/libethereum/BlockChainSync.cpp @@ -791,10 +791,6 @@ bool PV60Sync::invariants() const return false; if (m_state == SyncState::Hashes) { - bool hashes = false; - host().foreachPeer([&](std::shared_ptr _p) { if (_p->m_asking == Asking::Hashes) hashes = true; return !hashes; }); - if (!hashes) - return false; if (!m_syncingLatestHash) return false; if (m_syncingNeededBlocks.empty() != (!m_syncingLastReceivedHash)) @@ -802,10 +798,6 @@ bool PV60Sync::invariants() const } if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks) { - bool blocks = false; - host().foreachPeer([&](std::shared_ptr _p) { if (_p->m_asking == Asking::Blocks) blocks = true; return !blocks; }); - if (!blocks) - return false; if (downloadMan().isComplete()) return false; } diff --git a/libethereum/BlockChainSync.h b/libethereum/BlockChainSync.h index 449dd49d1..793b05b5b 100644 --- a/libethereum/BlockChainSync.h +++ b/libethereum/BlockChainSync.h @@ -24,7 +24,6 @@ #include #include -#include #include #include #include "CommonNet.h" diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp index 89de9ccf1..3a5138ca3 100644 --- a/libethereum/BlockQueue.cpp +++ b/libethereum/BlockQueue.cpp @@ -184,7 +184,7 @@ void BlockQueue::drainVerified_WITH_BOTH_LOCKS() ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, bool _isOurs) { - cdebug << std::this_thread::get_id(); + clog(BlockQueueTraceChannel) << std::this_thread::get_id(); // Check if we already know this block. h256 h = BlockInfo::headerHash(_block); @@ -336,7 +336,6 @@ void BlockQueue::updateBad_WITH_LOCK(h256 const& _bad) void BlockQueue::collectUnknownBad_WITH_BOTH_LOCKS(h256 const& _bad) { - DEV_INVARIANT_CHECK; list badQueue(1, _bad); while (!badQueue.empty()) { @@ -504,7 +503,6 @@ void BlockQueue::noteReady_WITH_LOCK(h256 const& _good) } if (notify) m_moreToVerify.notify_all(); - DEV_INVARIANT_CHECK; } void BlockQueue::retryAllUnknown() diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index c7d7c60c4..e812fb4e9 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #if ETH_JSONRPC || !ETH_TRUE #include #include @@ -38,53 +37,82 @@ #include "Defaults.h" #include "Executive.h" #include "EthereumHost.h" +#include "Utility.h" using namespace std; using namespace dev; using namespace dev::eth; using namespace p2p; -VersionChecker::VersionChecker(string const& _dbPath): - m_path(_dbPath.size() ? _dbPath : Defaults::dbPath()) +std::ostream& dev::eth::operator<<(std::ostream& _out, ActivityReport const& _r) { - bytes statusBytes = contents(m_path + "/status"); - RLP status(statusBytes); - try - { - auto protocolVersion = (unsigned)status[0]; - (void)protocolVersion; - auto minorProtocolVersion = (unsigned)status[1]; - auto databaseVersion = (unsigned)status[2]; - h256 ourGenesisHash = CanonBlockChain::genesis().hash(); - auto genesisHash = status.itemCount() > 3 ? (h256)status[3] : ourGenesisHash; - - m_action = - databaseVersion != c_databaseVersion || genesisHash != ourGenesisHash ? - WithExisting::Kill - : minorProtocolVersion != eth::c_minorProtocolVersion ? - WithExisting::Verify - : - WithExisting::Trust; - } - catch (...) - { - m_action = WithExisting::Kill; - } + _out << "Since " << toString(_r.since) << " (" << std::chrono::duration_cast(std::chrono::system_clock::now() - _r.since).count(); + _out << "): " << _r.ticks << "ticks"; + return _out; +} + +#ifdef _WIN32 +const char* ClientNote::name() { return EthTeal "^" EthBlue " i"; } +const char* ClientChat::name() { return EthTeal "^" EthWhite " o"; } +const char* ClientTrace::name() { return EthTeal "^" EthGray " O"; } +const char* ClientDetail::name() { return EthTeal "^" EthCoal " 0"; } +#else +const char* ClientNote::name() { return EthTeal "⧫" EthBlue " ℹ"; } +const char* ClientChat::name() { return EthTeal "⧫" EthWhite " ◌"; } +const char* ClientTrace::name() { return EthTeal "⧫" EthGray " ◎"; } +const char* ClientDetail::name() { return EthTeal "⧫" EthCoal " ●"; } +#endif + +static const Addresses c_canaries = +{ + Address("4bb7e8ae99b645c2b7860b8f3a2328aae28bd80a"), // gav + Address("1baf27b88c48dd02b744999cf3522766929d2b2a"), // vitalik + Address("a8edb1ac2c86d3d9d78f96cd18001f60df29e52c"), // jeff + Address("60d11b58744784dc97f878f7e3749c0f1381a004") // christoph +}; + +VersionChecker::VersionChecker(string const& _dbPath) +{ + upgradeDatabase(_dbPath); } -void VersionChecker::setOk() +Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId): + Client(_extNet, make_shared(), _dbPath, _forceAction, _networkId) { - if (m_action != WithExisting::Trust) - { - try - { - boost::filesystem::create_directory(m_path); - } - catch (...) - { - cwarn << "Unhandled exception! Failed to create directory: " << m_path << "\n" << boost::current_exception_diagnostic_information(); - } - writeFile(m_path + "/status", rlpList(eth::c_protocolVersion, eth::c_minorProtocolVersion, c_databaseVersion, CanonBlockChain::genesis().hash())); - } + startWorking(); +} + +Client::Client(p2p::Host* _extNet, std::shared_ptr _gp, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId): + Worker("eth", 0), + m_vc(_dbPath), + m_bc(_dbPath, _forceAction, [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }), + m_gp(_gp), + m_stateDB(State::openDB(_dbPath, _forceAction)), + m_preMine(m_stateDB, BaseState::CanonGenesis), + m_postMine(m_stateDB) +{ + m_lastGetWork = std::chrono::system_clock::now() - chrono::seconds(30); + m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue); + m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue); + m_bq.setOnBad([=](Exception& ex){ this->onBadBlock(ex); }); + m_bc.setOnBad([=](Exception& ex){ this->onBadBlock(ex); }); + m_farm.onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); }); + + m_gp->update(m_bc); + + auto host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId)); + m_host = host; + _extNet->addCapability(host, EthereumHost::staticName(), EthereumHost::c_oldProtocolVersion); //TODO: remove this one v61+ protocol is common + + if (_dbPath.size()) + Defaults::setDBPath(_dbPath); + doWork(); + + startWorking(); +} + +Client::~Client() +{ + stopWorking(); } ImportResult Client::queueBlock(bytes const& _block, bool _isSafe) @@ -211,144 +239,18 @@ void Client::onBadBlock(Exception& _ex) const #endif } -void BasicGasPricer::update(BlockChain const& _bc) -{ - unsigned c = 0; - h256 p = _bc.currentHash(); - m_gasPerBlock = _bc.info(p).gasLimit; - - map dist; - u256 total = 0; - - // make gasPrice versus gasUsed distribution for the last 1000 blocks - while (c < 1000 && p) - { - BlockInfo bi = _bc.info(p); - if (bi.transactionsRoot != EmptyTrie) - { - auto bb = _bc.block(p); - RLP r(bb); - BlockReceipts brs(_bc.receipts(bi.hash())); - size_t i = 0; - for (auto const& tr: r[1]) - { - Transaction tx(tr.data(), CheckTransaction::None); - u256 gu = brs.receipts[i].gasUsed(); - dist[tx.gasPrice()] += gu; - total += gu; - i++; - } - } - p = bi.parentHash; - ++c; - } - - // fill m_octiles with weighted gasPrices - if (total > 0) - { - m_octiles[0] = dist.begin()->first; - - // calc mean - u256 mean = 0; - for (auto const& i: dist) - mean += i.first * i.second; - mean /= total; - - // calc standard deviation - u256 sdSquared = 0; - for (auto const& i: dist) - sdSquared += i.second * (i.first - mean) * (i.first - mean); - sdSquared /= total; - - if (sdSquared) - { - long double sd = sqrt(sdSquared.convert_to()); - long double normalizedSd = sd / mean.convert_to(); - - // calc octiles normalized to gaussian distribution - boost::math::normal gauss(1.0, (normalizedSd > 0.01) ? normalizedSd : 0.01); - for (size_t i = 1; i < 8; i++) - m_octiles[i] = u256(mean.convert_to() * boost::math::quantile(gauss, i / 8.0)); - m_octiles[8] = dist.rbegin()->first; - } - else - { - for (size_t i = 0; i < 9; i++) - m_octiles[i] = (i + 1) * mean / 5; - } - } -} - -std::ostream& dev::eth::operator<<(std::ostream& _out, ActivityReport const& _r) -{ - _out << "Since " << toString(_r.since) << " (" << std::chrono::duration_cast(std::chrono::system_clock::now() - _r.since).count(); - _out << "): " << _r.ticks << "ticks"; - return _out; -} - -#ifdef _WIN32 -const char* ClientNote::name() { return EthTeal "^" EthBlue " i"; } -const char* ClientChat::name() { return EthTeal "^" EthWhite " o"; } -const char* ClientTrace::name() { return EthTeal "^" EthGray " O"; } -const char* ClientDetail::name() { return EthTeal "^" EthCoal " 0"; } -#else -const char* ClientNote::name() { return EthTeal "⧫" EthBlue " ℹ"; } -const char* ClientChat::name() { return EthTeal "⧫" EthWhite " ◌"; } -const char* ClientTrace::name() { return EthTeal "⧫" EthGray " ◎"; } -const char* ClientDetail::name() { return EthTeal "⧫" EthCoal " ●"; } -#endif - -Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId): - Client(_extNet, make_shared(), _dbPath, _forceAction, _networkId) -{ - startWorking(); -} - -Client::Client(p2p::Host* _extNet, std::shared_ptr _gp, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId): - Worker("eth", 0), - m_vc(_dbPath), - m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }), - m_gp(_gp), - m_stateDB(State::openDB(_dbPath, max(m_vc.action(), _forceAction))), - m_preMine(m_stateDB, BaseState::CanonGenesis), - m_postMine(m_stateDB) -{ - m_lastGetWork = std::chrono::system_clock::now() - chrono::seconds(30); - m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue); - m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue); - m_bq.setOnBad([=](Exception& ex){ this->onBadBlock(ex); }); - m_bc.setOnBad([=](Exception& ex){ this->onBadBlock(ex); }); - m_farm.onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); }); - - m_gp->update(m_bc); - - auto host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId)); - m_host = host; - _extNet->addCapability(host, EthereumHost::staticName(), EthereumHost::c_oldProtocolVersion); //TODO: remove this one v61+ protocol is common - - if (_dbPath.size()) - Defaults::setDBPath(_dbPath); - m_vc.setOk(); - doWork(); - - startWorking(); -} - -Client::~Client() -{ - stopWorking(); -} - -static const Address c_canary("0x"); - bool Client::isChainBad() const { - return stateAt(c_canary, 0) != 0; + unsigned numberBad = 0; + for (auto const& a: c_canaries) + if (!!stateAt(a, 0)) + numberBad++; + return numberBad >= 2; } bool Client::isUpgradeNeeded() const { - return stateAt(c_canary, 0) == 2; + return stateAt(c_canaries[0], 0) == 2; } void Client::setNetworkId(u256 _n) @@ -793,7 +695,7 @@ void Client::rejigMining() { clog(ClientTrace) << "Rejigging mining..."; DEV_WRITE_GUARDED(x_working) - m_working.commitToMine(m_bc); + m_working.commitToMine(m_bc, m_extraData); DEV_READ_GUARDED(x_working) { DEV_WRITE_GUARDED(x_postMine) diff --git a/libethereum/Client.h b/libethereum/Client.h index f38c7c099..343ca5b60 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -64,33 +64,6 @@ class VersionChecker { public: VersionChecker(std::string const& _dbPath); - - void setOk(); - WithExisting action() const { return m_action; } - -private: - WithExisting m_action; - std::string m_path; -}; - -class BasicGasPricer: public GasPricer -{ -public: - explicit BasicGasPricer(u256 _weiPerRef, u256 _refsPerBlock): m_weiPerRef(_weiPerRef), m_refsPerBlock(_refsPerBlock) {} - - void setRefPrice(u256 _weiPerRef) { if ((bigint)m_refsPerBlock * _weiPerRef > std::numeric_limits::max() ) BOOST_THROW_EXCEPTION(Overflow() << errinfo_comment("ether price * block fees is larger than 2**256-1, choose a smaller number.") ); else m_weiPerRef = _weiPerRef; } - void setRefBlockFees(u256 _refsPerBlock) { if ((bigint)m_weiPerRef * _refsPerBlock > std::numeric_limits::max() ) BOOST_THROW_EXCEPTION(Overflow() << errinfo_comment("ether price * block fees is larger than 2**256-1, choose a smaller number.") ); else m_refsPerBlock = _refsPerBlock; } - - u256 ask(State const&) const override { return m_weiPerRef * m_refsPerBlock / m_gasPerBlock; } - u256 bid(TransactionPriority _p = TransactionPriority::Medium) const override { return m_octiles[(int)_p] > 0 ? m_octiles[(int)_p] : (m_weiPerRef * m_refsPerBlock / m_gasPerBlock); } - - void update(BlockChain const& _bc) override; - -private: - u256 m_weiPerRef; - u256 m_refsPerBlock; - u256 m_gasPerBlock = 3141592; - std::array m_octiles; }; struct ClientNote: public LogChannel { static const char* name(); static const int verbosity = 2; }; @@ -240,6 +213,10 @@ public: ActivityReport activityReport() { ActivityReport ret; std::swap(m_report, ret); return ret; } /// Set a JSONRPC server to which we can report bad blocks. void setSentinel(std::string const& _server) { m_sentinel = _server; } + /// Get the JSONRPC server to which we report bad blocks. + std::string const& sentinel() const { return m_sentinel; } + /// Set the extra data that goes into mined blocks. + void setExtraData(bytes const& _extraData) { m_extraData = _extraData; } protected: /// InterfaceStub methods @@ -357,6 +334,7 @@ private: std::atomic m_syncBlockQueue = {false}; std::string m_sentinel; + bytes m_extraData; }; } diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index 2ac413a6a..d6d259380 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -480,3 +480,24 @@ int ClientBase::compareBlockHashes(h256 _h1, h256 _h2) const } return -1; } + +bool ClientBase::isKnown(h256 const& _hash) const +{ + return _hash == PendingBlockHash || + _hash == LatestBlockHash || + _hash == EarliestBlockHash || + bc().isKnown(_hash); +} + +bool ClientBase::isKnown(BlockNumber _block) const +{ + return _block == PendingBlock || + _block == LatestBlock || + bc().numberHash(_block) != h256(); +} + +bool ClientBase::isKnownTransaction(h256 const& _transactionHash) const +{ + return bc().isKnownTransaction(_transactionHash); +} + diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index ebcc48c18..d2d22a08f 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -149,6 +149,10 @@ public: /// Get the coinbase address virtual Address address() const override; + virtual bool isKnown(h256 const& _hash) const override; + virtual bool isKnown(BlockNumber _block) const override; + virtual bool isKnownTransaction(h256 const& _transactionHash) const override; + /// TODO: consider moving it to a separate interface virtual void startMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::startMining")); } diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 9980f4339..55a1d1bf0 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -286,7 +286,7 @@ void EthereumHost::onPeerTransactions(std::shared_ptr _peer, RLP c unsigned itemCount = _r.itemCount(); clog(NetAllDetail) << "Transactions (" << dec << itemCount << "entries)"; Guard l(_peer->x_knownTransactions); - for (unsigned i = 0; i < min(itemCount, 256); ++i) // process 256 transactions at most. TODO: much better solution. + for (unsigned i = 0; i < min(itemCount, 32); ++i) // process 256 transactions at most. TODO: much better solution. { auto h = sha3(_r[i].data()); _peer->m_knownTransactions.insert(h); diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index 182c3d4cd..a4b9d0006 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -31,7 +31,6 @@ #include #include -#include #include #include #include "CommonNet.h" diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 88878334c..eea5d23dc 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -86,7 +86,7 @@ unsigned EthereumPeer::askOverride() const if (s->info().clientVersion.substr(0, badGeth.size()) == badGeth) return 1; bytes const& d = repMan().data(*s, name()); - return d.empty() ? c_maxBlocksAsk : RLP(d).toInt(RLP::LaisezFaire); + return d.empty() ? c_maxBlocksAsk : RLP(d).toInt(RLP::LaissezFaire); } void EthereumPeer::setRude() diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h index d506ffb3f..e8842c8af 100644 --- a/libethereum/EthereumPeer.h +++ b/libethereum/EthereumPeer.h @@ -31,7 +31,6 @@ #include #include -#include #include #include #include "CommonNet.h" diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 41a8d1961..89fa00214 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -53,7 +53,7 @@ public: std::string json(bool _styled = false) const; - OnOpFunc onOp() { return [&](uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM) { (*this)(_steps, _inst, _newMemSize, _gasCost, _gas, _vm, _extVM); }; } + OnOpFunc onOp() { return [=](uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM) { (*this)(_steps, _inst, _newMemSize, _gasCost, _gas, _vm, _extVM); }; } private: bool m_showMnemonics = false; diff --git a/libethereum/GasPricer.cpp b/libethereum/GasPricer.cpp new file mode 100644 index 000000000..21f061afc --- /dev/null +++ b/libethereum/GasPricer.cpp @@ -0,0 +1,26 @@ +/* + 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 GasPricer.cpp + * @author Gav Wood + * @date 2015 + */ + +#include "GasPricer.h" + +using namespace std; +using namespace dev; +using namespace dev::eth; diff --git a/libethereum/GasPricer.h b/libethereum/GasPricer.h new file mode 100644 index 000000000..f0fc6b520 --- /dev/null +++ b/libethereum/GasPricer.h @@ -0,0 +1,74 @@ +/* + 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 GasPricer.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include + +namespace dev +{ +namespace eth +{ + +class State; +class BlockChain; + +enum class TransactionPriority +{ + Lowest = 0, + Low = 2, + Medium = 4, + High = 6, + Highest = 8 +}; + +class GasPricer +{ +public: + GasPricer() = default; + virtual ~GasPricer() = default; + + virtual u256 ask(State const&) const = 0; + virtual u256 bid(TransactionPriority _p = TransactionPriority::Medium) const = 0; + + virtual void update(BlockChain const&) {} +}; + +class TrivialGasPricer: public GasPricer +{ +public: + TrivialGasPricer() = default; + TrivialGasPricer(u256 const& _ask, u256 const& _bid): m_ask(_ask), m_bid(_bid) {} + + void setAsk(u256 const& _ask) { m_ask = _ask; } + void setBid(u256 const& _bid) { m_bid = _bid; } + + u256 ask() const { return m_ask; } + u256 ask(State const&) const override { return m_ask; } + u256 bid(TransactionPriority = TransactionPriority::Medium) const override { return m_bid; } + +private: + u256 m_ask = 10 * szabo; + u256 m_bid = 10 * szabo; +}; + +} +} diff --git a/libethereum/Interface.h b/libethereum/Interface.h index f631fb43e..f24f1b5c3 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -133,12 +133,15 @@ public: // [BLOCK QUERY API] + virtual bool isKnownTransaction(h256 const& _transactionHash) const = 0; virtual Transaction transaction(h256 _transactionHash) const = 0; virtual std::pair transactionLocation(h256 const& _transactionHash) const = 0; virtual h256 hashFromNumber(BlockNumber _number) const = 0; virtual BlockNumber numberFromHash(h256 _blockHash) const = 0; virtual int compareBlockHashes(h256 _h1, h256 _h2) const = 0; + virtual bool isKnown(BlockNumber _block) const = 0; + virtual bool isKnown(h256 const& _hash) const = 0; virtual BlockInfo blockInfo(h256 _hash) const = 0; virtual BlockDetails blockDetails(h256 _hash) const = 0; virtual Transaction transaction(h256 _blockHash, unsigned _i) const = 0; @@ -151,7 +154,7 @@ public: BlockInfo blockInfo(BlockNumber _block) const { return blockInfo(hashFromNumber(_block)); } BlockDetails blockDetails(BlockNumber _block) const { return blockDetails(hashFromNumber(_block)); } - Transaction transaction(BlockNumber _block, unsigned _i) const { if (_block == PendingBlock) { auto p = pending(); return _i < p.size() ? p[_i] : Transaction(); } return transaction(hashFromNumber(_block)); } + Transaction transaction(BlockNumber _block, unsigned _i) const { auto p = transactions(_block); return _i < p.size() ? p[_i] : Transaction(); } unsigned transactionCount(BlockNumber _block) const { if (_block == PendingBlock) { auto p = pending(); return p.size(); } return transactionCount(hashFromNumber(_block)); } Transactions transactions(BlockNumber _block) const { if (_block == PendingBlock) return pending(); return transactions(hashFromNumber(_block)); } TransactionHashes transactionHashes(BlockNumber _block) const { if (_block == PendingBlock) return pendingHashes(); return transactionHashes(hashFromNumber(_block)); } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 19640b09e..b383d0a6a 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -41,6 +41,7 @@ using namespace std; using namespace dev; using namespace dev::eth; +namespace fs = boost::filesystem; #define ctrace clog(StateTrace) #define ETH_TIMED_ENACTMENTS 0 @@ -52,23 +53,27 @@ const char* StateDetail::name() { return EthViolet "⚙" EthWhite " ◌"; } const char* StateTrace::name() { return EthViolet "⚙" EthGray " ◎"; } const char* StateChat::name() { return EthViolet "⚙" EthWhite " ◌"; } -OverlayDB State::openDB(std::string _path, WithExisting _we) +OverlayDB State::openDB(std::string const& _basePath, WithExisting _we) { - if (_path.empty()) - _path = Defaults::get()->m_dbPath; - boost::filesystem::create_directory(_path); + std::string path = _basePath.empty() ? Defaults::get()->m_dbPath : _basePath; if (_we == WithExisting::Kill) - boost::filesystem::remove_all(_path + "/state"); + { + cnote << "Killing state database (WithExisting::Kill)."; + boost::filesystem::remove_all(path + "/state"); + } + + path += "/" + toHex(CanonBlockChain::genesis().hash().ref().cropped(0, 4)) + "/" + toString(c_databaseVersion); + boost::filesystem::create_directory(path); ldb::Options o; o.max_open_files = 256; o.create_if_missing = true; ldb::DB* db = nullptr; - ldb::DB::Open(o, _path + "/state", &db); + ldb::DB::Open(o, path + "/state", &db); if (!db) { - if (boost::filesystem::space(_path + "/state").available < 1024) + if (boost::filesystem::space(path + "/state").available < 1024) { cwarn << "Not enough available space found on hard drive. Please free some up and then re-run. Bailing."; BOOST_THROW_EXCEPTION(NotEnoughAvailableSpace()); @@ -603,7 +608,6 @@ string State::vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequire m_currentBlock.noteDirty(); LastHashes lh = _bc.lastHashes((unsigned)m_previousBlock.number); - vector receipts; string ret; unsigned i = 0; @@ -613,10 +617,6 @@ string State::vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequire st.setShowMnemonics(); execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, st.onOp()); ret += (ret.empty() ? "[" : ",") + st.json(); - - RLPStream receiptRLP; - m_receipts.back().streamRLP(receiptRLP); - receipts.push_back(receiptRLP.out()); ++i; } return ret.empty() ? "[]" : (ret + "]"); @@ -883,7 +883,7 @@ LogBloom State::logBloom() const return ret; } -void State::commitToMine(BlockChain const& _bc) +void State::commitToMine(BlockChain const& _bc, bytes const& _extraData) { uncommitToMine(); @@ -966,6 +966,7 @@ void State::commitToMine(BlockChain const& _bc) m_currentBlock.gasUsed = gasUsed(); m_currentBlock.stateRoot = m_state.root(); m_currentBlock.parentHash = m_previousBlock.hash(); + m_currentBlock.extraData = _extraData; m_committedToMine = true; } diff --git a/libethereum/State.h b/libethereum/State.h index 6ad8fed09..2a63aeda4 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -37,6 +37,7 @@ #include "Transaction.h" #include "TransactionReceipt.h" #include "AccountDiff.h" +#include "GasPricer.h" namespace dev { @@ -80,45 +81,6 @@ enum class BaseState CanonGenesis }; -enum class TransactionPriority -{ - Lowest = 0, - Low = 2, - Medium = 4, - High = 6, - Highest = 8 -}; - -class GasPricer -{ -public: - GasPricer() = default; - virtual ~GasPricer() = default; - - virtual u256 ask(State const&) const = 0; - virtual u256 bid(TransactionPriority _p = TransactionPriority::Medium) const = 0; - - virtual void update(BlockChain const&) {} -}; - -class TrivialGasPricer: public GasPricer -{ -public: - TrivialGasPricer() = default; - TrivialGasPricer(u256 const& _ask, u256 const& _bid): m_ask(_ask), m_bid(_bid) {} - - void setAsk(u256 const& _ask) { m_ask = _ask; } - void setBid(u256 const& _bid) { m_bid = _bid; } - - u256 ask() const { return m_ask; } - u256 ask(State const&) const override { return m_ask; } - u256 bid(TransactionPriority = TransactionPriority::Medium) const override { return m_bid; } - -private: - u256 m_ask = 10 * szabo; - u256 m_bid = 10 * szabo; -}; - enum class Permanence { Reverted, @@ -171,7 +133,7 @@ public: Address address() const { return m_ourAddress; } /// Open a DB - useful for passing into the constructor & keeping for other states that are necessary. - static OverlayDB openDB(std::string _path, WithExisting _we = WithExisting::Trust); + static OverlayDB openDB(std::string const& _path, WithExisting _we = WithExisting::Trust); static OverlayDB openDB(WithExisting _we = WithExisting::Trust) { return openDB(std::string(), _we); } OverlayDB const& db() const { return m_db; } OverlayDB& db() { return m_db; } @@ -194,7 +156,7 @@ public: /// The only thing left to do after this is to actually mine(). /// /// This may be called multiple times and without issue. - void commitToMine(BlockChain const& _bc); + void commitToMine(BlockChain const& _bc, bytes const& _extraData = {}); /// @returns true iff commitToMine() has been called without any subsequest transactions added &c. bool isCommittedToMine() const { return m_committedToMine; } diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index 40a7914d3..70f82c6d2 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -94,99 +94,16 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, TransactionException cons return _out; } -Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig) +Transaction::Transaction(bytesConstRef _rlpData, CheckTransaction _checkSig): + TransactionBase(_rlpData, _checkSig) { - int field = 0; - RLP rlp(_rlpData); - try - { - if (!rlp.isList()) - BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction RLP must be a list")); - - m_nonce = rlp[field = 0].toInt(); - m_gasPrice = rlp[field = 1].toInt(); - m_gas = rlp[field = 2].toInt(); - m_type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall; - m_receiveAddress = rlp[field = 3].isEmpty() ? Address() : rlp[field = 3].toHash
(RLP::VeryStrict); - m_value = rlp[field = 4].toInt(); - - if (!rlp[field = 5].isData()) - BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("transaction data RLP must be an array")); - - m_data = rlp[field = 5].toBytes(); - byte v = rlp[field = 6].toInt() - 27; - h256 r = rlp[field = 7].toInt(); - h256 s = rlp[field = 8].toInt(); - - if (rlp.itemCount() > 9) - BOOST_THROW_EXCEPTION(InvalidTransactionFormat() << errinfo_comment("to many fields in the transaction RLP")); - - m_vrs = SignatureStruct{ r, s, v }; - if (_checkSig >= CheckTransaction::Cheap && !m_vrs.isValid()) - BOOST_THROW_EXCEPTION(InvalidSignature()); - if (_checkSig == CheckTransaction::Everything) - m_sender = sender(); - } - catch (Exception& _e) - { - _e << errinfo_name("invalid transaction format") << BadFieldError(field, toHex(rlp[field].data().toBytes())); - throw; - } if (_checkSig >= CheckTransaction::Cheap && !checkPayment()) BOOST_THROW_EXCEPTION(OutOfGasIntrinsic() << RequirementError(gasRequired(), (bigint)gas())); } -Address const& Transaction::safeSender() const noexcept -{ - try - { - return sender(); - } - catch (...) - { - cwarn << "safeSender() did throw an exception: " << boost::current_exception_diagnostic_information(); - return ZeroAddress; - } -} - -Address const& Transaction::sender() const -{ - if (!m_sender) - { - auto p = recover(m_vrs, sha3(WithoutSignature)); - if (!p) - BOOST_THROW_EXCEPTION(InvalidSignature()); - m_sender = right160(dev::sha3(bytesConstRef(p.data(), sizeof(p)))); - } - return m_sender; -} - bigint Transaction::gasRequired() const { if (!m_gasRequired) m_gasRequired = Transaction::gasRequired(m_data); return m_gasRequired; } - -void Transaction::sign(Secret _priv) -{ - auto sig = dev::sign(_priv, sha3(WithoutSignature)); - SignatureStruct sigStruct = *(SignatureStruct const*)&sig; - if (sigStruct.isValid()) - m_vrs = sigStruct; -} - -void Transaction::streamRLP(RLPStream& _s, IncludeSignature _sig) const -{ - if (m_type == NullTransaction) - return; - _s.appendList((_sig ? 3 : 0) + 6); - _s << m_nonce << m_gasPrice << m_gas; - if (m_type == MessageCall) - _s << m_receiveAddress; - else - _s << ""; - _s << m_value << m_data; - if (_sig) - _s << (m_vrs.v + 27) << (u256)m_vrs.r << (u256)m_vrs.s; -} diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index 4de9d7e92..77ba1824d 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -24,6 +24,7 @@ #include #include #include +#include #include namespace dev @@ -31,20 +32,6 @@ namespace dev namespace eth { -/// Named-boolean type to encode whether a signature be included in the serialisation process. -enum IncludeSignature -{ - WithoutSignature = 0, ///< Do not include a signature. - WithSignature = 1, ///< Do include a signature. -}; - -enum class CheckTransaction -{ - None, - Cheap, - Everything -}; - enum class TransactionException { None = 0, @@ -92,23 +79,31 @@ struct ExecutionResult std::ostream& operator<<(std::ostream& _out, ExecutionResult const& _er); /// Encodes a transaction, ready to be exported to or freshly imported from RLP. -class Transaction +class Transaction: public TransactionBase { public: /// Constructs a null transaction. Transaction() {} /// Constructs a signed message-call transaction. - Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); } + Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce, Secret const& _secret): + TransactionBase(_value, _gasPrice, _gas, _dest, _data, _nonce, _secret) + {} /// Constructs a signed contract-creation transaction. - Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); } + Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce, Secret const& _secret): + TransactionBase(_value, _gasPrice, _gas, _data, _nonce, _secret) + {} /// Constructs an unsigned message-call transaction. - Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce = 0): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {} + Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce = 0): + TransactionBase(_value, _gasPrice, _gas, _dest, _data, _nonce) + {} /// Constructs an unsigned contract-creation transaction. - Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce = 0): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {} + Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce = 0): + TransactionBase(_value, _gasPrice, _gas, _data, _nonce) + {} /// Constructs a transaction from the given RLP. explicit Transaction(bytesConstRef _rlp, CheckTransaction _checkSig); @@ -116,68 +111,6 @@ public: /// Constructs a transaction from the given RLP. explicit Transaction(bytes const& _rlp, CheckTransaction _checkSig): Transaction(&_rlp, _checkSig) {} - - /// Checks equality of transactions. - bool operator==(Transaction const& _c) const { return m_type == _c.m_type && (m_type == ContractCreation || m_receiveAddress == _c.m_receiveAddress) && m_value == _c.m_value && m_data == _c.m_data; } - /// Checks inequality of transactions. - bool operator!=(Transaction const& _c) const { return !operator==(_c); } - - /// @returns sender of the transaction from the signature (and hash). - Address const& sender() const; - /// Like sender() but will never throw. @returns a null Address if the signature is invalid. - Address const& safeSender() const noexcept; - /// Force the sender to a particular value. This will result in an invalid transaction RLP. - void forceSender(Address const& _a) { m_sender = _a; } - - /// @returns true if transaction is non-null. - explicit operator bool() const { return m_type != NullTransaction; } - - /// @returns true if transaction is contract-creation. - bool isCreation() const { return m_type == ContractCreation; } - - /// @returns true if transaction is message-call. - bool isMessageCall() const { return m_type == MessageCall; } - - /// Serialises this transaction to an RLPStream. - void streamRLP(RLPStream& _s, IncludeSignature _sig = WithSignature) const; - - /// @returns the RLP serialisation of this transaction. - bytes rlp(IncludeSignature _sig = WithSignature) const { RLPStream s; streamRLP(s, _sig); return s.out(); } - - /// @returns the SHA3 hash of the RLP serialisation of this transaction. - h256 sha3(IncludeSignature _sig = WithSignature) const { if (_sig == WithSignature && m_hashWith) return m_hashWith; RLPStream s; streamRLP(s, _sig); auto ret = dev::sha3(s.out()); if (_sig == WithSignature) m_hashWith = ret; return ret; } - - /// @returns the amount of ETH to be transferred by this (message-call) transaction, in Wei. Synonym for endowment(). - u256 value() const { return m_value; } - /// @returns the amount of ETH to be endowed by this (contract-creation) transaction, in Wei. Synonym for value(). - u256 endowment() const { return m_value; } - - /// @returns the base fee and thus the implied exchange rate of ETH to GAS. - u256 gasPrice() const { return m_gasPrice; } - - /// @returns the total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended. - u256 gas() const { return m_gas; } - - /// @returns the receiving address of the message-call transaction (undefined for contract-creation transactions). - Address receiveAddress() const { return m_receiveAddress; } - - /// Synonym for receiveAddress(). - Address to() const { return m_receiveAddress; } - - /// Synonym for safeSender(). - Address from() const { return safeSender(); } - - /// @returns the data associated with this (message-call) transaction. Synonym for initCode(). - bytes const& data() const { return m_data; } - /// @returns the initialisation code associated with this (contract-creation) transaction. Synonym for data(). - bytes const& initCode() const { return m_data; } - - /// @returns the transaction-count of the sender. - u256 nonce() const { return m_nonce; } - - /// @returns the signature of the transaction. Encodes the sender. - SignatureStruct const& signature() const { return m_vrs; } - /// @returns true if the transaction contains enough gas for the basic payment. bool checkPayment() const { return m_gas >= gasRequired(); } @@ -188,46 +121,11 @@ public: template static bigint gasRequired(T const& _data, u256 _gas = 0) { bigint ret = c_txGas + _gas; for (auto i: _data) ret += i ? c_txDataNonZeroGas : c_txDataZeroGas; return ret; } private: - /// Type of transaction. - enum Type - { - NullTransaction, ///< Null transaction. - ContractCreation, ///< Transaction to create contracts - receiveAddress() is ignored. - MessageCall ///< Transaction to invoke a message call - receiveAddress() is used. - }; - - void sign(Secret _priv); ///< Sign the transaction. - - Type m_type = NullTransaction; ///< Is this a contract-creation transaction or a message-call transaction? - u256 m_nonce; ///< The transaction-count of the sender. - u256 m_value; ///< The amount of ETH to be transferred by this transaction. Called 'endowment' for contract-creation transactions. - Address m_receiveAddress; ///< The receiving address of the transaction. - u256 m_gasPrice; ///< The base fee and thus the implied exchange rate of ETH to GAS. - u256 m_gas; ///< The total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended. - bytes m_data; ///< The data associated with the transaction, or the initialiser if it's a creation transaction. - SignatureStruct m_vrs; ///< The signature of the transaction. Encodes the sender. - - mutable h256 m_hashWith; ///< Cached hash of transaction with signature. - mutable Address m_sender; ///< Cached sender, determined from signature. mutable bigint m_gasRequired = 0; ///< Memoised amount required for the transaction to run. }; /// Nice name for vector of Transaction. using Transactions = std::vector; -/// Simple human-readable stream-shift operator. -inline std::ostream& operator<<(std::ostream& _out, Transaction const& _t) -{ - _out << _t.sha3().abridged() << "{"; - if (_t.receiveAddress()) - _out << _t.receiveAddress().abridged(); - else - _out << "[CREATE]"; - - _out << "/" << _t.data().size() << "$" << _t.value() << "+" << _t.gas() << "@" << _t.gasPrice(); - _out << "<-" << _t.safeSender().abridged() << " #" << _t.nonce() << "}"; - return _out; -} - } } diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 8fcf3cfb6..d2ced467e 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -98,11 +98,7 @@ ImportResult TransactionQueue::import(Transaction const& _transaction, ImportCal std::unordered_map TransactionQueue::transactions() const { ReadGuard l(m_lock); - auto ret = m_current; - for (auto const& i: m_future) - if (i.second.nonce() < maxNonce_WITH_LOCK(i.second.sender())) - ret.insert(i); - return ret; + return m_current; } ImportResult TransactionQueue::manageImport_WITH_LOCK(h256 const& _h, Transaction const& _transaction, ImportCallback const& _cb) @@ -113,6 +109,8 @@ ImportResult TransactionQueue::manageImport_WITH_LOCK(h256 const& _h, Transactio // If it doesn't work, the signature is bad. // The transaction's nonce may yet be invalid (or, it could be "valid" but we may be missing a marginally older transaction). + // Remove any prior transaction with the same nonce but a lower gas price. + // Bomb out if there's a prior transaction with higher gas price. auto r = m_senders.equal_range(_transaction.from()); for (auto it = r.first; it != r.second; ++it) if (m_current.count(it->second) && m_current[it->second].nonce() == _transaction.nonce()) diff --git a/libethereum/Utility.cpp b/libethereum/Utility.cpp index adfea4a51..a9158a363 100644 --- a/libethereum/Utility.cpp +++ b/libethereum/Utility.cpp @@ -22,11 +22,16 @@ #include "Utility.h" #include -#include +#include #include +#include +#include +#include +#include "Defaults.h" using namespace std; using namespace dev; using namespace dev::eth; +namespace fs = boost::filesystem; bytes dev::eth::parseData(string const& _args) { @@ -84,3 +89,47 @@ bytes dev::eth::parseData(string const& _args) return m_data; } + +void dev::eth::upgradeDatabase(std::string const& _basePath) +{ + std::string path = _basePath.empty() ? Defaults::get()->dbPath() : _basePath; + + if (fs::exists(path + "/state") && fs::exists(path + "/details") && fs::exists(path + "/blocks")) + { + // upgrade + cnote << "Upgrading database to new layout..."; + bytes statusBytes = contents(path + "/status"); + RLP status(statusBytes); + try + { + auto minorProtocolVersion = (unsigned)status[1]; + auto databaseVersion = (unsigned)status[2]; + auto genesisHash = (h256)status[3]; + + string chainPath = path + "/" + toHex(genesisHash.ref().cropped(0, 4)); + string extrasPath = chainPath + "/" + toString(databaseVersion); + + // write status + if (!fs::exists(chainPath + "/blocks")) + { + boost::filesystem::create_directories(chainPath); + fs::rename(path + "/blocks", chainPath + "/blocks"); + + if (!fs::exists(extrasPath + "/extras")) + { + boost::filesystem::create_directories(extrasPath); + fs::rename(path + "/details", extrasPath + "/extras"); + fs::rename(path + "/state", extrasPath + "/state"); + writeFile(extrasPath + "/minor", rlp(minorProtocolVersion)); + + fs::remove_all(path + "/status"); + } + } + } + catch (...) + { + cwarn << "Couldn't upgrade - bad status"; + } + } +} + diff --git a/libethereum/Utility.h b/libethereum/Utility.h index 893604139..0dfe8509b 100644 --- a/libethereum/Utility.h +++ b/libethereum/Utility.h @@ -42,5 +42,7 @@ namespace eth */ bytes parseData(std::string const& _args); +void upgradeDatabase(std::string const& _basePath); + } } diff --git a/libp2p/Host.h b/libp2p/Host.h index 9523d0cca..b7ebb3951 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -33,7 +33,6 @@ #include #include -#include #include #include #include "NodeTable.h" diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 6827a27d9..08c411ca0 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -382,7 +382,7 @@ void Session::doRead() } catch (std::exception const& _e) { - clog(NetWarn) << "Exception decoding frame header RLP:" << bytesConstRef(m_data.data(), h128::size).cropped(3); + clog(NetWarn) << "Exception decoding frame header RLP:" << _e.what() << bytesConstRef(m_data.data(), h128::size).cropped(3); drop(BadProtocol); return; } diff --git a/libp2p/Session.h b/libp2p/Session.h index 0a55b2653..8d8c3ea1e 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -31,7 +31,6 @@ #include #include -#include #include #include "RLPXFrameCoder.h" #include "RLPXSocket.h" diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 99c7e3425..3683c01ca 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -142,7 +142,7 @@ string WebThreeStubServerBase::eth_getStorageAt(string const& _address, string c { try { - return toJS(client()->stateAt(jsToAddress(_address), jsToU256(_position), jsToBlockNumber(_blockNumber))); + return toJS(toCompactBigEndian(client()->stateAt(jsToAddress(_address), jsToU256(_position), jsToBlockNumber(_blockNumber)), 1)); } catch (...) { @@ -162,11 +162,15 @@ string WebThreeStubServerBase::eth_getTransactionCount(string const& _address, s } } -string WebThreeStubServerBase::eth_getBlockTransactionCountByHash(string const& _blockHash) +Json::Value WebThreeStubServerBase::eth_getBlockTransactionCountByHash(string const& _blockHash) { try { - return toJS(client()->transactionCount(jsToFixed<32>(_blockHash))); + h256 blockHash = jsToFixed<32>(_blockHash); + if (!client()->isKnown(blockHash)) + return Json::Value(Json::nullValue); + + return toJS(client()->transactionCount(blockHash)); } catch (...) { @@ -174,10 +178,14 @@ string WebThreeStubServerBase::eth_getBlockTransactionCountByHash(string const& } } -string WebThreeStubServerBase::eth_getBlockTransactionCountByNumber(string const& _blockNumber) +Json::Value WebThreeStubServerBase::eth_getBlockTransactionCountByNumber(string const& _blockNumber) { try { + BlockNumber blockNumber = jsToBlockNumber(_blockNumber); + if (!client()->isKnown(blockNumber)) + return Json::Value(Json::nullValue); + return toJS(client()->transactionCount(jsToBlockNumber(_blockNumber))); } catch (...) @@ -186,11 +194,15 @@ string WebThreeStubServerBase::eth_getBlockTransactionCountByNumber(string const } } -string WebThreeStubServerBase::eth_getUncleCountByBlockHash(string const& _blockHash) +Json::Value WebThreeStubServerBase::eth_getUncleCountByBlockHash(string const& _blockHash) { try { - return toJS(client()->uncleCount(jsToFixed<32>(_blockHash))); + h256 blockHash = jsToFixed<32>(_blockHash); + if (!client()->isKnown(blockHash)) + return Json::Value(Json::nullValue); + + return toJS(client()->uncleCount(blockHash)); } catch (...) { @@ -198,11 +210,15 @@ string WebThreeStubServerBase::eth_getUncleCountByBlockHash(string const& _block } } -string WebThreeStubServerBase::eth_getUncleCountByBlockNumber(string const& _blockNumber) +Json::Value WebThreeStubServerBase::eth_getUncleCountByBlockNumber(string const& _blockNumber) { try { - return toJS(client()->uncleCount(jsToBlockNumber(_blockNumber))); + BlockNumber blockNumber = jsToBlockNumber(_blockNumber); + if (!client()->isKnown(blockNumber)) + return Json::Value(Json::nullValue); + + return toJS(client()->uncleCount(blockNumber)); } catch (...) { @@ -330,7 +346,10 @@ Json::Value WebThreeStubServerBase::eth_getBlockByHash(string const& _blockHash, { try { - auto h = jsToFixed<32>(_blockHash); + h256 h = jsToFixed<32>(_blockHash); + if (!client()->isKnown(h)) + return Json::Value(Json::nullValue); + if (_includeTransactions) return toJson(client()->blockInfo(h), client()->blockDetails(h), client()->uncleHashes(h), client()->transactions(h)); else @@ -346,7 +365,10 @@ Json::Value WebThreeStubServerBase::eth_getBlockByNumber(string const& _blockNum { try { - auto h = jsToBlockNumber(_blockNumber); + BlockNumber h = jsToBlockNumber(_blockNumber); + if (!client()->isKnown(h)) + return Json::Value(Json::nullValue); + if (_includeTransactions) return toJson(client()->blockInfo(h), client()->blockDetails(h), client()->uncleHashes(h), client()->transactions(h)); else @@ -363,6 +385,9 @@ Json::Value WebThreeStubServerBase::eth_getTransactionByHash(string const& _tran try { h256 h = jsToFixed<32>(_transactionHash); + if (!client()->isKnownTransaction(h)) + return Json::Value(Json::nullValue); + auto l = client()->transactionLocation(h); return toJson(client()->transaction(h), l, client()->numberFromHash(l.first)); } diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index d3e16d0f4..94fbd1acb 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -106,10 +106,10 @@ public: virtual std::string eth_getBalance(std::string const& _address, std::string const& _blockNumber); virtual std::string eth_getStorageAt(std::string const& _address, std::string const& _position, std::string const& _blockNumber); virtual std::string eth_getTransactionCount(std::string const& _address, std::string const& _blockNumber); - virtual std::string eth_getBlockTransactionCountByHash(std::string const& _blockHash); - virtual std::string eth_getBlockTransactionCountByNumber(std::string const& _blockNumber); - virtual std::string eth_getUncleCountByBlockHash(std::string const& _blockHash); - virtual std::string eth_getUncleCountByBlockNumber(std::string const& _blockNumber); + virtual Json::Value eth_getBlockTransactionCountByHash(std::string const& _blockHash); + virtual Json::Value eth_getBlockTransactionCountByNumber(std::string const& _blockNumber); + virtual Json::Value eth_getUncleCountByBlockHash(std::string const& _blockHash); + virtual Json::Value eth_getUncleCountByBlockNumber(std::string const& _blockNumber); virtual std::string eth_getCode(std::string const& _address, std::string const& _blockNumber); virtual std::string eth_sendTransaction(Json::Value const& _json); virtual std::string eth_call(Json::Value const& _json, std::string const& _blockNumber); diff --git a/libweb3jsonrpc/abstractwebthreestubserver.h b/libweb3jsonrpc/abstractwebthreestubserver.h index 82864a61a..6d0db7394 100644 --- a/libweb3jsonrpc/abstractwebthreestubserver.h +++ b/libweb3jsonrpc/abstractwebthreestubserver.h @@ -27,10 +27,10 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerbindAndAddMethod(jsonrpc::Procedure("eth_getBalance", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getBalanceI); this->bindAndAddMethod(jsonrpc::Procedure("eth_getStorageAt", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getStorageAtI); this->bindAndAddMethod(jsonrpc::Procedure("eth_getTransactionCount", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getTransactionCountI); - this->bindAndAddMethod(jsonrpc::Procedure("eth_getBlockTransactionCountByHash", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getBlockTransactionCountByHashI); - this->bindAndAddMethod(jsonrpc::Procedure("eth_getBlockTransactionCountByNumber", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getBlockTransactionCountByNumberI); - this->bindAndAddMethod(jsonrpc::Procedure("eth_getUncleCountByBlockHash", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getUncleCountByBlockHashI); - this->bindAndAddMethod(jsonrpc::Procedure("eth_getUncleCountByBlockNumber", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getUncleCountByBlockNumberI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_getBlockTransactionCountByHash", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getBlockTransactionCountByHashI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_getBlockTransactionCountByNumber", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getBlockTransactionCountByNumberI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_getUncleCountByBlockHash", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getUncleCountByBlockHashI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_getUncleCountByBlockNumber", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getUncleCountByBlockNumberI); this->bindAndAddMethod(jsonrpc::Procedure("eth_getCode", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_getCodeI); this->bindAndAddMethod(jsonrpc::Procedure("eth_sendTransaction", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_sendTransactionI); this->bindAndAddMethod(jsonrpc::Procedure("eth_call", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_OBJECT,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_callI); @@ -476,10 +476,10 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServersetExtraData(rlpList(0, _clientVersion, m_net.id())); + } if (_interfaces.count("shh")) m_whisper = m_net.registerCapability(new WhisperHost); diff --git a/libwhisper/BloomFilter.cpp b/libwhisper/BloomFilter.cpp deleted file mode 100644 index 554274011..000000000 --- a/libwhisper/BloomFilter.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* -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 BloomFilter.cpp -* @author Vladislav Gluhovsky -* @date June 2015 -*/ - -#include "BloomFilter.h" - -using namespace std; -using namespace dev; -using namespace dev::shh; - - diff --git a/libwhisper/BloomFilter.h b/libwhisper/BloomFilter.h index a624be157..7b5d179c6 100644 --- a/libwhisper/BloomFilter.h +++ b/libwhisper/BloomFilter.h @@ -42,14 +42,12 @@ public: void addRaw(FixedHash const& _h); void removeRaw(FixedHash const& _h); bool containsRaw(FixedHash const& _h) const { return this->contains(_h); } - - enum { BitsPerBloom = 3 }; private: void init() { for (unsigned i = 0; i < CounterSize; ++i) m_refCounter[i] = 0; } static bool isBitSet(FixedHash const& _h, unsigned _index); - enum { CounterSize = 8 * TopicBloomFilterBase::size }; + static const unsigned CounterSize = N * 8; std::array m_refCounter; }; @@ -91,7 +89,7 @@ bool TopicBloomFilterBase::isBitSet(FixedHash const& _h, unsigned _index) return (_h[iByte] & c_powerOfTwoBitMmask[iBit]) != 0; } -using TopicBloomFilter = TopicBloomFilterBase; +using TopicBloomFilter = TopicBloomFilterBase; } } diff --git a/libwhisper/Common.cpp b/libwhisper/Common.cpp index 748180647..7c7a9801b 100644 --- a/libwhisper/Common.cpp +++ b/libwhisper/Common.cpp @@ -100,7 +100,7 @@ TopicBloomFilterHash TopicFilter::exportBloom() const TopicBloomFilterHash ret; for (TopicMask const& t: m_topicMasks) for (auto const& i: t) - ret |= i.first.template bloomPart(); + ret |= i.first.template bloomPart(); return ret; } diff --git a/libwhisper/Common.h b/libwhisper/Common.h index d5d926291..277ce0902 100644 --- a/libwhisper/Common.h +++ b/libwhisper/Common.h @@ -58,8 +58,9 @@ enum WhisperPacket PacketCount }; -static const int c_topicBloomFilterSize = 64; -static const int c_whisperProtocolVersion = 3; +static const unsigned TopicBloomFilterSize = 64; +static const unsigned BitsPerBloom = 3; +static const unsigned WhisperProtocolVersion = 3; using AbridgedTopic = FixedHash<4>; using Topic = h256; @@ -67,7 +68,7 @@ using Topic = h256; using AbridgedTopics = std::vector; using Topics = h256s; -using TopicBloomFilterHash = FixedHash; +using TopicBloomFilterHash = FixedHash; AbridgedTopic abridge(Topic const& _topic); AbridgedTopics abridge(Topics const& _topics); diff --git a/libwhisper/Message.cpp b/libwhisper/Message.cpp index 50020b783..0b235b984 100644 --- a/libwhisper/Message.cpp +++ b/libwhisper/Message.cpp @@ -181,3 +181,12 @@ void Envelope::proveWork(unsigned _ms) } } } + +bool Envelope::matchesBloomFilter(TopicBloomFilterHash const& f) const +{ + for (AbridgedTopic t: m_topic) + if (f.contains(t.template bloomPart())) + return true; + + return false; +} diff --git a/libwhisper/Message.h b/libwhisper/Message.h index 3529054e0..ae61f210a 100644 --- a/libwhisper/Message.h +++ b/libwhisper/Message.h @@ -80,6 +80,8 @@ public: unsigned workProved() const; void proveWork(unsigned _ms); + bool matchesBloomFilter(TopicBloomFilterHash const& f) const; + private: Envelope(unsigned _exp, unsigned _ttl, AbridgedTopics const& _topic): m_expiry(_exp), m_ttl(_ttl), m_topic(_topic) {} diff --git a/libwhisper/WhisperHost.cpp b/libwhisper/WhisperHost.cpp index 150d5cd63..f9ff33658 100644 --- a/libwhisper/WhisperHost.cpp +++ b/libwhisper/WhisperHost.cpp @@ -51,6 +51,8 @@ void WhisperHost::streamMessage(h256 _m, RLPStream& _s) const void WhisperHost::inject(Envelope const& _m, WhisperPeer* _p) { + // this function processes messages originated both by local host (_p == null), and by remote peers (_p != null) + cnote << this << ": inject: " << _m.expiry() << _m.ttl() << _m.topic() << toHex(_m.data()); if (_m.expiry() <= (unsigned)time(0)) @@ -66,21 +68,25 @@ void WhisperHost::inject(Envelope const& _m, WhisperPeer* _p) m_expiryQueue.insert(make_pair(_m.expiry(), h)); } - DEV_GUARDED(m_filterLock) - { - for (auto const& f: m_filters) - if (f.second.filter.matches(_m)) - for (auto& i: m_watches) - if (i.second.id == f.first) - i.second.changes.push_back(h); - } + int rating = 1; // rating for local host is based upon: 1. installed watch; 2. proof of work + + if (_p) // incoming message from remote peer + DEV_GUARDED(m_filterLock) + for (auto const& f: m_filters) + if (f.second.filter.matches(_m)) + for (auto& i: m_watches) + if (i.second.id == f.first) + { + i.second.changes.push_back(h); + rating += 10; // subject to review + } // TODO p2p: capability-based rating for (auto i: peerSessions()) { auto w = i.first->cap().get(); if (w == _p) - w->addRating(1); + w->addRating(rating); else w->noteNewMessage(h, _m); } diff --git a/libwhisper/WhisperHost.h b/libwhisper/WhisperHost.h index a6de09c38..fc1bcb557 100644 --- a/libwhisper/WhisperHost.h +++ b/libwhisper/WhisperHost.h @@ -50,7 +50,7 @@ class WhisperHost: public HostCapability, public Interface, public public: WhisperHost(); virtual ~WhisperHost(); - unsigned protocolVersion() const { return c_whisperProtocolVersion; } + unsigned protocolVersion() const { return WhisperProtocolVersion; } /// remove old messages void cleanup(); std::map all() const { dev::ReadGuard l(x_messages); return m_messages; } diff --git a/libwhisper/WhisperPeer.cpp b/libwhisper/WhisperPeer.cpp index d749aa26e..393c3749c 100644 --- a/libwhisper/WhisperPeer.cpp +++ b/libwhisper/WhisperPeer.cpp @@ -91,19 +91,17 @@ void WhisperPeer::sendMessages() if (m_advertiseTopicsOfInterest) sendTopicsOfInterest(host()->bloom()); + multimap available; + DEV_GUARDED(x_unseen) + m_unseen.swap(available); + RLPStream amalg; - unsigned msgCount = 0; - { - Guard l(x_unseen); - msgCount = m_unseen.size(); - while (m_unseen.size()) - { - auto p = *m_unseen.begin(); - m_unseen.erase(m_unseen.begin()); - host()->streamMessage(p.second, amalg); - } - } - + + // send the highest rated messages first + for (auto i = available.rbegin(); i != available.rend(); ++i) + host()->streamMessage(i->second, amalg); + + unsigned msgCount = available.size(); if (msgCount) { RLPStream s; @@ -114,8 +112,27 @@ void WhisperPeer::sendMessages() void WhisperPeer::noteNewMessage(h256 _h, Envelope const& _m) { + unsigned rate = ratingForPeer(_m); Guard l(x_unseen); - m_unseen.insert(make_pair(rating(_m), _h)); + m_unseen.insert(make_pair(rate, _h)); +} + +unsigned WhisperPeer::ratingForPeer(Envelope const& e) const +{ + // we try to estimate, how valuable this nessage will be for the remote peer, + // according to the following criteria: + // 1. bloom filter + // 2. proof of work + + static const unsigned BloomFilterMatchReward = 256; // vlad todo: move to common.h + unsigned rating = 0; + + DEV_GUARDED(x_bloom) + if (e.matchesBloomFilter(m_bloom)) + rating += BloomFilterMatchReward; + + rating += e.sha3().firstBitSet(); + return rating; } void WhisperPeer::sendTopicsOfInterest(TopicBloomFilterHash const& _bloom) diff --git a/libwhisper/WhisperPeer.h b/libwhisper/WhisperPeer.h index 48f984013..4d093d259 100644 --- a/libwhisper/WhisperPeer.h +++ b/libwhisper/WhisperPeer.h @@ -53,7 +53,7 @@ public: virtual ~WhisperPeer(); WhisperHost* host() const; static std::string name() { return "shh"; } - static u256 version() { return c_whisperProtocolVersion; } + static u256 version() { return WhisperProtocolVersion; } static unsigned messageCount() { return PacketCount; } TopicBloomFilterHash bloom() const { dev::Guard g(x_bloom); return m_bloom; } void sendTopicsOfInterest(TopicBloomFilterHash const& _bloom); ///< sends our bloom filter to remote peer @@ -62,7 +62,7 @@ public: private: virtual bool interpret(unsigned _id, RLP const&) override; void sendMessages(); - unsigned rating(Envelope const&) const { return 0; } // TODO + unsigned ratingForPeer(Envelope const& e) const; void noteNewMessage(h256 _h, Envelope const& _m); void setBloom(TopicBloomFilterHash const& _b) { dev::Guard g(x_bloom); m_bloom = _b; } diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index e07641db8..eae3f8834 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -206,6 +206,21 @@ QVariantList ClientModel::gasCosts() const return res; } +void ClientModel::addAccount(QString const& _secret) +{ + KeyPair key(Secret(_secret.toStdString())); + m_accountsSecret.push_back(key); + Address address = key.address(); + m_accounts[address] = Account(u256(0), Account::NormalCreation); + m_ethAccounts->setAccounts(m_accountsSecret); +} + +QString ClientModel::resolveAddress(QString const& _secret) +{ + KeyPair key(Secret(_secret.toStdString())); + return "0x" + QString::fromStdString(key.address().hex()); +} + void ClientModel::setupScenario(QVariantMap _scenario) { onStateReset(); @@ -336,6 +351,7 @@ void ClientModel::executeSequence(vector const& _sequence) if (!transaction.isFunctionCall) { callAddress(Address(address.toStdString()), bytes(), transaction); + onNewTransaction(); continue; } @@ -674,7 +690,7 @@ RecordLogEntry* ClientModel::lastBlock() const strGas << blockInfo.gasUsed; stringstream strNumber; strNumber << blockInfo.number; - RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(dev::toHex(blockInfo.hash().ref()))), QString(), QString(), QString(), false, RecordLogEntry::RecordType::Block, QString::fromStdString(strGas.str()), QString(), tr("Block"), QVariantMap(), QVariantList()); + RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(dev::toHex(blockInfo.hash().ref()))), QString(), QString(), QString(), false, RecordLogEntry::RecordType::Block, QString::fromStdString(strGas.str()), QString(), tr("Block"), QVariantMap(), QVariantMap(), QVariantList()); QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership); return record; } @@ -735,6 +751,7 @@ void ClientModel::onNewTransaction() Address contractAddress = (bool)tr.address ? tr.address : tr.contractAddress; auto contractAddressIter = m_contractNames.find(contractAddress); QVariantMap inputParameters; + QVariantMap returnParameters; QVariantList logs; if (contractAddressIter != m_contractNames.end()) { @@ -754,6 +771,11 @@ void ClientModel::onNewTransaction() returned += "("; returned += returnValues.join(", "); returned += ")"; + + QStringList returnParams = encoder.decode(funcDef->returnParameters(), tr.result.output); + for (int k = 0; k < returnParams.length(); ++k) + returnParameters.insert(funcDef->returnParameters().at(k)->name(), returnParams.at(k)); + bytes data = tr.inputParameters; data.erase(data.begin(), data.begin() + 4); QStringList parameters = encoder.decode(funcDef->parametersList(), data); @@ -837,9 +859,25 @@ void ClientModel::onNewTransaction() } RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction, - gasUsed, sender, label, inputParameters, logs); + gasUsed, sender, label, inputParameters, returnParameters, logs); QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership); emit newRecord(log); + + // retrieving all accounts balance + QVariantMap state; + QVariantMap accountBalances; + for (auto const& ctr : m_contractAddresses) + { + u256 wei = m_client->balanceAt(ctr.second, PendingBlock); + accountBalances.insert("0x" + QString::fromStdString(ctr.second.hex()), QEther(wei, QEther::Wei).format()); + } + for (auto const& account : m_accounts) + { + u256 wei = m_client->balanceAt(account.first, PendingBlock); + accountBalances.insert("0x" + QString::fromStdString(account.first.hex()), QEther(wei, QEther::Wei).format()); + } + state.insert("accounts", accountBalances); + emit newState(recordIndex, state); } } diff --git a/mix/ClientModel.h b/mix/ClientModel.h index b5b783b3d..92f06b48b 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -32,6 +32,7 @@ #include #include #include "MachineStates.h" +#include "QEther.h" namespace dev { @@ -115,6 +116,8 @@ class RecordLogEntry: public QObject Q_PROPERTY(QString label MEMBER m_label CONSTANT) /// input parameters Q_PROPERTY(QVariantMap parameters MEMBER m_inputParameters CONSTANT) + /// return parameters + Q_PROPERTY(QVariantMap returnParameters MEMBER m_returnParameters CONSTANT) /// logs Q_PROPERTY(QVariantList logs MEMBER m_logs CONSTANT) @@ -128,9 +131,9 @@ public: RecordLogEntry(): m_recordIndex(0), m_call(false), m_type(RecordType::Transaction) {} RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call, RecordType _type, QString _gasUsed, - QString _sender, QString _label, QVariantMap _inputParameters, QVariantList _logs): + QString _sender, QString _label, QVariantMap _inputParameters, QVariantMap _returnParameters, QVariantList _logs): m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call), m_type(_type), m_gasUsed(_gasUsed), - m_sender(_sender), m_label(_label), m_inputParameters(_inputParameters), m_logs(_logs) {} + m_sender(_sender), m_label(_label), m_inputParameters(_inputParameters), m_returnParameters(_returnParameters), m_logs(_logs) {} private: unsigned m_recordIndex; @@ -146,6 +149,7 @@ private: QString m_sender; QString m_label; QVariantMap m_inputParameters; + QVariantMap m_returnParameters; QVariantList m_logs; }; @@ -183,6 +187,10 @@ public: Q_INVOKABLE QString encodeStringParam(QString const& _param); /// To Hex number Q_INVOKABLE QString toHex(QString const& _int); + /// Add new account to the model + Q_INVOKABLE void addAccount(QString const& _secret); + /// Return the address associated with the current secret + Q_INVOKABLE QString resolveAddress(QString const& _secret); public slots: /// Setup scenario, run transaction sequence, show debugger for the last transaction @@ -236,6 +244,8 @@ signals: void newRecord(RecordLogEntry* _r); /// State (transaction log) cleared void stateCleared(); + /// new state has been processed + void newState(unsigned _record, QVariantMap _accounts); private: RecordLogEntry* lastBlock() const; diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index 655be1c08..1d566308b 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -543,7 +543,7 @@ void CodeModel::retrieveSubType(SolidityType& _wrapperType, dev::solidity::Type SolidityType CodeModel::nodeType(dev::solidity::Type const* _type) { - SolidityType r { SolidityType::Type::UnsignedInteger, 32, 1, false, false, QString::fromStdString(_type->toString()), std::vector(), std::vector(), nullptr }; + SolidityType r { SolidityType::Type::UnsignedInteger, 32, 1, false, false, QString::fromStdString(_type->toString(true)), std::vector(), std::vector(), nullptr }; if (!_type) return r; switch (_type->getCategory()) diff --git a/mix/qml.qrc b/mix/qml.qrc index 16bb01b83..34295b022 100644 --- a/mix/qml.qrc +++ b/mix/qml.qrc @@ -69,5 +69,7 @@ qml/ScenarioExecution.qml qml/ScenarioLoader.qml qml/ScenarioButton.qml + qml/Watchers.qml + qml/KeyValuePanel.qml diff --git a/mix/qml/Block.qml b/mix/qml/Block.qml index 75d34a766..732166628 100644 --- a/mix/qml/Block.qml +++ b/mix/qml/Block.qml @@ -22,6 +22,7 @@ ColumnLayout property int blockIndex property variant scenario property string labelColor: "#414141" + signal txSelected(var txIndex) function calculateHeight() { @@ -36,6 +37,14 @@ ColumnLayout return trHeight } + function editTx(txIndex) + { + transactionDialog.stateAccounts = scenario.accounts + transactionDialog.execute = false + transactionDialog.editMode = true + transactionDialog.open(txIndex, blockIndex, transactions.get(txIndex)) + } + onOpenedTrChanged: { Layout.preferredHeight = calculateHeight() @@ -156,11 +165,11 @@ ColumnLayout Image { anchors.top: parent.top anchors.left: parent.left - anchors.leftMargin: -9 - anchors.topMargin: -9 + anchors.leftMargin: -4 + anchors.topMargin: 0 id: saveStatusImage source: "qrc:/qml/img/recyclediscard@2x.png" - width: statusWidth + 20 + width: statusWidth + 10 fillMode: Image.PreserveAspectFit } @@ -194,185 +203,125 @@ ColumnLayout Rectangle { Layout.preferredWidth: blockWidth - Layout.preferredHeight: parent.height + Layout.preferredHeight: trHeight + height: trHeight color: "#DEDCDC" id: rowContentTr anchors.top: parent.top + property bool selected: false + Connections + { + target: blockChainPanel + onTxSelected: { + if (root.blockIndex !== blockIndex || index !== txIndex) + rowContentTr.deselect() + } + } + + function deselect() + { + rowContentTr.selected = false + rowContentTr.color = "#DEDCDC" + hash.color = labelColor + func.color = labelColor + } + MouseArea { anchors.fill: parent + onClicked: { + if (!rowContentTr.selected) + { + rowContentTr.selected = true + rowContentTr.color = "#4F4F4F" + hash.color = "#EAB920" + func.color = "#EAB920" + txSelected(index) + } + else + rowContentTr.deselect() + + } onDoubleClicked: { - transactionDialog.stateAccounts = scenario.accounts - transactionDialog.execute = false - transactionDialog.open(index, blockIndex, transactions.get(index)) + root.editTx(index) } } - ColumnLayout + RowLayout { - anchors.top: parent.top - width: parent.width - spacing: 20 - RowLayout + Layout.fillWidth: true + Layout.preferredHeight: trHeight - 10 + anchors.verticalCenter: parent.verticalCenter + Rectangle { - anchors.top: parent.top - Layout.fillWidth: true - Rectangle + Layout.preferredWidth: fromWidth + anchors.left: parent.left + anchors.leftMargin: horizontalMargin + Text { - Layout.preferredWidth: fromWidth - anchors.left: parent.left - anchors.leftMargin: horizontalMargin - Text - { - id: hash - width: parent.width - 30 - elide: Text.ElideRight - anchors.verticalCenter: parent.verticalCenter - maximumLineCount: 1 - color: labelColor - font.pointSize: dbgStyle.absoluteSize(1) - font.bold: true - text: { - if (index >= 0) - return transactions.get(index).sender - else - return "" - } - } - } - - Rectangle - { - Layout.preferredWidth: toWidth - Text - { - id: func - text: { - if (index >= 0) - parent.parent.userFrienldyToken(transactions.get(index).label) - else - return "" - } - elide: Text.ElideRight - anchors.verticalCenter: parent.verticalCenter - color: labelColor - font.pointSize: dbgStyle.absoluteSize(1) - font.bold: true - maximumLineCount: 1 - width: parent.width - } - } - - function userFrienldyToken(value) - { - if (value && value.indexOf("<") === 0) - { - if (value.split("> ")[1] === " - ") - return value.split(" - ")[0].replace("<", "") + id: hash + width: parent.width - 30 + elide: Text.ElideRight + anchors.verticalCenter: parent.verticalCenter + maximumLineCount: 1 + color: labelColor + font.pointSize: dbgStyle.absoluteSize(1) + font.bold: true + text: { + if (index >= 0) + return clientModel.resolveAddress(transactions.get(index).sender) else - return value.split(" - ")[0].replace("<", "") + "." + value.split("> ")[1] + "()"; - } - else - return value - } - - Rectangle - { - Layout.preferredWidth: valueWidth - Text - { - id: returnValue - elide: Text.ElideRight - anchors.verticalCenter: parent.verticalCenter - maximumLineCount: 1 - color: labelColor - font.bold: true - font.pointSize: dbgStyle.absoluteSize(1) - width: parent.width - 30 - text: { - if (index >= 0 && transactions.get(index).returned) - return transactions.get(index).returned - else - return "" - } + return "" } } + } - Rectangle + Rectangle + { + Layout.preferredWidth: toWidth + Text { - Layout.preferredWidth: logsWidth - Layout.preferredHeight: trHeight - 10 - width: logsWidth - color: "transparent" - Text - { - id: logs - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: 10 - color: labelColor - font.bold: true - font.pointSize: dbgStyle.absoluteSize(1) - text: { - if (index >= 0 && transactions.get(index).logs && transactions.get(index).logs.count) - return transactions.get(index).logs.count - else - return "" - } - } - MouseArea { - anchors.fill: parent - onClicked: { - rowTransaction.displayContent(); - } + id: func + text: { + if (index >= 0) + parent.parent.userFrienldyToken(transactions.get(index).label) + else + return "" } + elide: Text.ElideRight + anchors.verticalCenter: parent.verticalCenter + color: labelColor + font.pointSize: dbgStyle.absoluteSize(1) + font.bold: true + maximumLineCount: 1 + width: parent.width } } - RowLayout + function userFrienldyToken(value) { - id: rowDetailedContent - visible: false - Layout.preferredHeight:{ - if (index >= 0 && transactions.get(index).logs) - return 100 * transactions.get(index).logs.count - else - return 100 - } - onVisibleChanged: + if (value && value.indexOf("<") === 0) { - var lognb = transactions.get(index).logs.count - if (visible) - { - rowContentTr.Layout.preferredHeight = trHeight + 100 * lognb - openedTr += 100 * lognb - } + if (value.split("> ")[1] === " - ") + return value.split(" - ")[0].replace("<", "") else - { - rowContentTr.Layout.preferredHeight = trHeight - openedTr -= 100 * lognb - } + return value.split(" - ")[0].replace("<", "") + "." + value.split("> ")[1] + "()"; } - - Text { - anchors.left: parent.left - anchors.leftMargin: horizontalMargin - id: logsText - } - } + else + return value + } } } Rectangle { width: debugActionWidth - height: trHeight - anchors.left: rowContentTr.right - anchors.topMargin: -6 + height: trHeight - 10 + anchors.right: rowContentTr.right anchors.top: rowContentTr.top - anchors.leftMargin: -50 + anchors.rightMargin: 10 color: "transparent" Image { @@ -380,7 +329,6 @@ ColumnLayout source: "qrc:/qml/img/rightarrow@2x.png" width: debugActionWidth fillMode: Image.PreserveAspectFit - anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter visible: transactions.get(index).recordIndex !== undefined } diff --git a/mix/qml/BlockChain.qml b/mix/qml/BlockChain.qml index 02ff72be2..ccc027444 100644 --- a/mix/qml/BlockChain.qml +++ b/mix/qml/BlockChain.qml @@ -13,12 +13,18 @@ import "." ColumnLayout { id: blockChainPanel + property alias trDialog: transactionDialog + property alias blockChainRepeater: blockChainRepeater property variant model + property var states: ({}) spacing: 0 property int previousWidth property variant debugTrRequested: [] signal chainChanged signal chainReloaded + signal txSelected(var blockIndex, var txIndex) + signal rebuilding + signal accountAdded(string address, string amount) Connections { @@ -40,20 +46,23 @@ ColumnLayout { var minWidth = scenarioMinWidth - 20 // margin if (width <= minWidth || previousWidth <= minWidth) { - fromWidth = 100 - toWidth = 100 - valueWidth = 200 + fromWidth = 250 + toWidth = 240 } else { var diff = (width - previousWidth) / 3; - fromWidth = fromWidth + diff < 100 ? 100 : fromWidth + diff - toWidth = toWidth + diff < 100 ? 100 : toWidth + diff - valueWidth = valueWidth + diff < 200 ? 200 : valueWidth + diff + fromWidth = fromWidth + diff < 250 ? 250 : fromWidth + diff + toWidth = toWidth + diff < 240 ? 240 : toWidth + diff } previousWidth = width } + function getState(record) + { + return states[record] + } + function load(scenario) { if (!scenario) @@ -61,6 +70,7 @@ ColumnLayout { if (model) rebuild.startBlinking() model = scenario + states = [] blockModel.clear() for (var b in model.blocks) blockModel.append(model.blocks[b]) @@ -68,10 +78,8 @@ ColumnLayout { } property int statusWidth: 30 - property int fromWidth: 150 - property int toWidth: 100 - property int valueWidth: 200 - property int logsWidth: 40 + property int fromWidth: 250 + property int toWidth: 240 property int debugActionWidth: 40 property int horizontalMargin: 10 property int cellSpacing: 10 @@ -80,7 +88,7 @@ ColumnLayout { { id: header spacing: 0 - Layout.preferredHeight: 30 + Layout.preferredHeight: 24 Rectangle { Layout.preferredWidth: statusWidth @@ -91,12 +99,13 @@ ColumnLayout { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter source: "qrc:/qml/img/recycleicon@2x.png" - width: statusWidth + 20 + width: statusWidth + 10 fillMode: Image.PreserveAspectFit } } Rectangle { + anchors.verticalCenter: parent.verticalCenter Layout.preferredWidth: fromWidth Label { @@ -109,21 +118,13 @@ ColumnLayout { Label { text: "To" + anchors.verticalCenter: parent.verticalCenter Layout.preferredWidth: toWidth + cellSpacing } Label - { - text: "Value" - Layout.preferredWidth: valueWidth + cellSpacing - } - Label - { - text: "Logs" - Layout.preferredWidth: logsWidth + cellSpacing - } - Label { text: "" + anchors.verticalCenter: parent.verticalCenter Layout.preferredWidth: debugActionWidth } } @@ -162,8 +163,22 @@ ColumnLayout { { id: blockChainRepeater model: blockModel + + function editTx(blockIndex, txIndex) + { + itemAt(blockIndex).editTx(txIndex) + } + Block { + Connections + { + target: block + onTxSelected: { + blockChainPanel.txSelected(index, txIndex) + } + } + id: block scenario: blockChainPanel.model Layout.preferredWidth: blockChainScrollView.width Layout.preferredHeight: @@ -248,6 +263,8 @@ ColumnLayout { Rectangle { Layout.preferredWidth: parent.width + Layout.preferredHeight: 70 + color: "transparent" RowLayout { anchors.horizontalCenter: parent.horizontalCenter @@ -270,7 +287,9 @@ ColumnLayout { { if (ensureNotFuturetime.running) return; + rebuilding() stopBlinking() + states = [] var retBlocks = []; var bAdded = 0; for (var j = 0; j < model.blocks.length; j++) @@ -316,7 +335,7 @@ ColumnLayout { blockModel.append(model.blocks[j]) ensureNotFuturetime.start() - clientModel.setupScenario(model); + clientModel.setupScenario(model); } buttonShortcut: "" sourceImg: "qrc:/qml/img/recycleicon@2x.png" @@ -346,6 +365,7 @@ ColumnLayout { var item = TransactionHelper.defaultTransaction() transactionDialog.stateAccounts = model.accounts transactionDialog.execute = true + transactionDialog.editMode = false transactionDialog.open(model.blocks[model.blocks.length - 1].transactions.length, model.blocks.length - 1, item) } width: 100 @@ -397,7 +417,6 @@ ColumnLayout { } else addNewBlock() - } function addNewBlock() @@ -410,7 +429,7 @@ ColumnLayout { height: 30 buttonShortcut: "" - sourceImg: "qrc:/qml/img/addblock@2x.png" + sourceImg: "qrc:/qml/img/newblock@2x.png" } } @@ -448,11 +467,13 @@ ColumnLayout { tr.recordIndex = _r.recordIndex tr.logs = _r.logs tr.sender = _r.sender + tr.returnParameters = _r.returnParameters var trModel = blockModel.getTransaction(blockIndex, trIndex) trModel.returned = _r.returned trModel.recordIndex = _r.recordIndex trModel.logs = _r.logs trModel.sender = _r.sender + trModel.returnParameters = _r.returnParameters blockModel.setTransaction(blockIndex, trIndex, trModel) return; } @@ -472,9 +493,15 @@ ColumnLayout { itemTr.sender = _r.sender itemTr.recordIndex = _r.recordIndex itemTr.logs = _r.logs + itemTr.returnParameters = _r.returnParameters model.blocks[model.blocks.length - 1].transactions.push(itemTr) blockModel.appendTransaction(itemTr) } + + onNewState: { + states[_record] = _accounts + } + onMiningComplete: { } @@ -484,7 +511,12 @@ ColumnLayout { id: newAccount text: qsTr("New Account..") onClicked: { - model.accounts.push(projectModel.stateListModel.newAccount("1000000", QEther.Ether)) + var ac = projectModel.stateListModel.newAccount("O", QEther.Wei) + model.accounts.push(ac) + clientModel.addAccount(ac.secret); + for (var k in Object.keys(blockChainPanel.states)) + blockChainPanel.states[k].accounts["0x" + ac.address] = "0 wei" // add the account in all the previous state (balance at O) + accountAdded(ac.address, "0") } Layout.preferredWidth: 100 Layout.preferredHeight: 30 diff --git a/mix/qml/KeyValuePanel.qml b/mix/qml/KeyValuePanel.qml new file mode 100644 index 000000000..056384b9e --- /dev/null +++ b/mix/qml/KeyValuePanel.qml @@ -0,0 +1,134 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Dialogs 1.1 +import QtQuick.Layouts 1.1 +import Qt.labs.settings 1.0 +import org.ethereum.qml.QEther 1.0 +import "js/Debugger.js" as Debugger +import "js/ErrorLocationFormater.js" as ErrorLocationFormater +import "js/TransactionHelper.js" as TransactionHelper +import "js/QEtherHelper.js" as QEtherHelper +import "." + +ColumnLayout { + id: root + property alias title: titleLabel.text + property variant _data + property string role + property alias model: modelKeyValue + + function add(key, value) + { + modelKeyValue.append({ "key": key, "value": value }) + } + + function clear() + { + modelKeyValue.clear() + } + + function init() + { + modelKeyValue.clear() + if (typeof(computeData) !== "undefined" && computeData instanceof Function) + computeData() + else + { + if (_data !== undefined && _data[role] !== undefined) + { + var keys = Object.keys(_data[role]) + for (var k in keys) + { + modelKeyValue.append({ "key": keys[k] === "" ? "undefined" : keys[k], "value": _data[role][keys[k]] }) + } + } + } + } + + RowLayout + { + Layout.preferredHeight: 20 + Layout.fillWidth: true + Label + { + id: titleLabel + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + color: "white" + } + } + + RowLayout + { + Layout.fillWidth: true + Layout.preferredHeight: 100 + ListModel + { + id: modelKeyValue + } + + Rectangle + { + Layout.fillWidth: true + Layout.fillHeight: true + color: "white" + radius: 2 + ScrollView + { + id: columnValues + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + anchors.fill: parent + clip: true + ColumnLayout + { + anchors.margins: 10 + Repeater + { + id: repeaterKeyValue + model: modelKeyValue + RowLayout + { + Layout.fillWidth: true + Layout.preferredHeight: 30 + spacing: 0 + Rectangle + { + Layout.preferredWidth: columnValues.width / 2 + Label + { + anchors.left: parent.left + anchors.leftMargin: 10 + text: { + if (index >= 0 && repeaterKeyValue.model.get(index).key !== undefined) + return repeaterKeyValue.model.get(index).key + else + return "" + } + } + } + + Rectangle + { + Layout.preferredWidth: columnValues.width / 2 - 10 + Label + { + anchors.right: parent.right + anchors.rightMargin: 10 + text: { + if (index >= 0 && repeaterKeyValue.model.get(index).value !== undefined) + return repeaterKeyValue.model.get(index).value + else + return "" + } + } + } + } + } + } + + } + } + } +} + diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index 7f180a899..376135695 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -31,7 +31,7 @@ Rectangle { property alias codeEditor: codeEditor property bool webViewHorizontal: codeWebSplitter.orientation === Qt.Vertical //vertical splitter positions elements vertically, splits screen horizontally property bool firstCompile: true - property int scenarioMinWidth: 590 + property int scenarioMinWidth: 620 Connections { target: codeModel diff --git a/mix/qml/ScenarioExecution.qml b/mix/qml/ScenarioExecution.qml index 3e9d8e089..687c8e99f 100644 --- a/mix/qml/ScenarioExecution.qml +++ b/mix/qml/ScenarioExecution.qml @@ -8,6 +8,7 @@ import "js/Debugger.js" as Debugger import "js/ErrorLocationFormater.js" as ErrorLocationFormater import "." + Rectangle { color: "#ededed" property alias bc: blockChain @@ -18,50 +19,109 @@ Rectangle { onProjectLoaded: { loader.init() } - } - Column + ScrollView { - anchors.margins: 10 anchors.fill: parent - spacing: 10 - ScenarioLoader - { - height: 100 - width: parent.width - id: loader + onWidthChanged: { + columnExe.width = width - 40 } - Connections + ColumnLayout { - target: blockChain - onChainChanged: - { - loader.needSaveOrReload() - } - } + id: columnExe + Layout.preferredWidth: parent.width + width: parent.width - 40 + anchors.left: parent.left + anchors.leftMargin: 15 + ColumnLayout + { + id: scenarioColumn + width: parent.width + spacing: 10 + ScenarioLoader + { + anchors.horizontalCenter: parent.horizontalCenter + height: 100 + Layout.preferredWidth: 400 + width: 400 + id: loader + } - Rectangle - { - width: parent.parent.width - height: 1 - color: "#cccccc" - } + Connections + { + target: blockChain + onChainChanged: + { + loader.needSaveOrReload() + } + } - Connections - { - target: loader - onLoaded: + Rectangle + { + Layout.preferredWidth: parent.width + height: 1 + color: "#cccccc" + } + + Connections + { + target: loader + onLoaded: + { + watchers.clear() + blockChain.load(scenario) + } + } + + BlockChain + { + id: blockChain + width: parent.width + } + + Connections + { + target: blockChain + property var currentSelectedBlock + property var currentSelectedTx + onTxSelected: { + currentSelectedBlock = blockIndex + currentSelectedTx = txIndex + updateWatchers(blockIndex, txIndex) + } + + function updateWatchers(blockIndex, txIndex){ + var tx = blockChain.model.blocks[blockIndex].transactions[txIndex] + var state = blockChain.getState(tx.recordIndex) + watchers.updateWidthTx(tx, state, blockIndex, txIndex) + } + + onRebuilding: { + watchers.clear() + } + + onAccountAdded: { + watchers.addAccount(address, "0 wei") + } + } + } + + Watchers { - blockChain.load(scenario) + id: watchers + bc: blockChain + Layout.fillWidth: true + Layout.preferredHeight: 740 } - } - BlockChain - { - id: blockChain - width: parent.width + Rectangle + { + color: "transparent" + Layout.preferredHeight: 50 + Layout.fillWidth: true + } } } } diff --git a/mix/qml/ScenarioLoader.qml b/mix/qml/ScenarioLoader.qml index bc26b103d..58247b213 100644 --- a/mix/qml/ScenarioLoader.qml +++ b/mix/qml/ScenarioLoader.qml @@ -107,11 +107,13 @@ ColumnLayout Rectangle { id: editIconRect + anchors.top: scenarioName.top + anchors.topMargin: 6 anchors.left: scenarioName.right - anchors.leftMargin: 15 + anchors.leftMargin: 20 Image { - source: "qrc:/qml/img/edit.png" - width: 10 + source: "qrc:/qml/img/edittransaction.png" + width: 30 fillMode: Image.PreserveAspectFit anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter @@ -126,7 +128,6 @@ ColumnLayout scenarioNameEdit.save() else scenarioNameEdit.edit() - } } } diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index 992113cc6..d3bf1c860 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -257,7 +257,8 @@ Item { _secret = clientModel.newSecret(); var address = clientModel.address(_secret); var name = qsTr("Account") + "-" + address.substring(0, 4); - return { name: name, secret: _secret, balance: QEtherHelper.createEther(_balance, _unit), address: address }; + var amount = QEtherHelper.createEther(_balance, _unit) + return { name: name, secret: _secret, balance: amount, address: address }; } function duplicateState(index) diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index f4aa9a5f0..4edc601de 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -16,7 +16,8 @@ Dialog { width: 580 height: 500 visible: false - title: qsTr("Edit Transaction") + title: editMode ? qsTr("Edit Transaction") : qsTr("Add Transaction") + property bool editMode property int transactionIndex property int blockIndex property alias gas: gasValueEdit.gasValue; @@ -390,7 +391,7 @@ Dialog { objectName: "trTypeExecute" exclusiveGroup: rbbuttonList height: 30 - text: qsTr("Execute Contract") + text: qsTr("Transact with Contract") } } } @@ -687,7 +688,7 @@ Dialog { } Button { - text: qsTr("Update"); + text: editMode ? qsTr("Update") : qsTr("Ok") onClicked: { var invalid = InputValidator.validate(paramsModel, paramValues); if (invalid.length === 0) diff --git a/mix/qml/Watchers.qml b/mix/qml/Watchers.qml new file mode 100644 index 000000000..9625d9424 --- /dev/null +++ b/mix/qml/Watchers.qml @@ -0,0 +1,203 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Dialogs 1.1 +import QtQuick.Layouts 1.1 +import Qt.labs.settings 1.0 +import org.ethereum.qml.QEther 1.0 +import "js/Debugger.js" as Debugger +import "js/ErrorLocationFormater.js" as ErrorLocationFormater +import "js/TransactionHelper.js" as TransactionHelper +import "js/QEtherHelper.js" as QEtherHelper +import "." + +Rectangle { + color: "#4F4F4F" + radius: 4 + property variant tx + property variant currentState + property variant bc + property var blockIndex + property var txIndex + + function clear() + { + from.text = "" + to.text = "" + value.text = "" + inputParams.clear() + returnParams.clear() + accounts.clear() + events.clear() + } + + function addAccount(address, amount) + { + accounts.add(address, amount) + } + + function updateWidthTx(_tx, _state, _blockIndex, _txIndex) + { + from.text = clientModel.resolveAddress(_tx.sender) + to.text = _tx.label + value.text = _tx.value.format() + tx = _tx + blockIndex = _blockIndex + txIndex = _txIndex + currentState = _state + inputParams.init() + if (_tx.isContractCreation) + { + returnParams.role = "creationAddr" + returnParams._data = { + creationAddr : { + } + } + returnParams._data.creationAddr[qsTr("contract address")] = _tx.returned + } + else + { + returnParams.role = "returnParameters" + returnParams._data = tx + } + returnParams.init() + accounts.init() + events.init() + } + + Column { + anchors.fill: parent + spacing: 15 + Rectangle + { + height: 15 + width: parent.width - 30 + color: "transparent" + Row + { + id: rowHeader + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: rowHeader.parent.top + anchors.topMargin: 6 + spacing: 5 + Label { + id: fromLabel + text: qsTr("from") + visible: from.text !== "" + color: "#EAB920" + } + Label { + id: from + color: "#EAB920" + elide: Text.ElideRight + maximumLineCount: 1 + clip: true + width: 200 + } + Label { + id: toLabel + text: qsTr("to") + visible: from.text !== "" + color: "#EAB920" + } + Label { + id: to + color: "#EAB920" + elide: Text.ElideRight + maximumLineCount: 1 + clip: true + width: 100 + } + Label { + id: value + color: "#EAB920" + font.italic: true + clip: true + } + } + + Image { + anchors.right: rowHeader.parent.right + anchors.top: rowHeader.parent.top + anchors.topMargin: -3 + source: "qrc:/qml/img/edittransaction2.png" + height: 30 + fillMode: Image.PreserveAspectFit + visible: from.text !== "" + MouseArea + { + anchors.fill: parent + onClicked: + { + bc.blockChainRepeater.editTx(blockIndex, txIndex) + } + } + } + } + + Rectangle { + height: 1 + width: parent.width - 30 + anchors.horizontalCenter: parent.horizontalCenter + border.color: "#cccccc" + border.width: 1 + } + + KeyValuePanel + { + height: 150 + width: parent.width - 30 + anchors.horizontalCenter: parent.horizontalCenter + id: inputParams + title: qsTr("INPUT PARAMETERS") + role: "parameters" + _data: tx + } + + KeyValuePanel + { + height: 150 + width: parent.width - 30 + anchors.horizontalCenter: parent.horizontalCenter + id: returnParams + title: qsTr("RETURN PARAMETERS") + role: "returnParameters" + _data: tx + } + + KeyValuePanel + { + height: 150 + width: parent.width - 30 + anchors.horizontalCenter: parent.horizontalCenter + id: accounts + title: qsTr("ACCOUNTS") + role: "accounts" + _data: currentState + } + + KeyValuePanel + { + height: 150 + width: parent.width - 30 + anchors.horizontalCenter: parent.horizontalCenter + id: events + title: qsTr("EVENTS") + function computeData() + { + model.clear() + var ret = [] + for (var k in tx.logs) + { + var param = "" + for (var p in tx.logs[k].param) + { + param += " " + tx.logs[k].param[p].value + " " + } + param = "(" + param + ")" + model.append({ "key": tx.logs[k].name, "value": param }) + } + } + } + } +} diff --git a/mix/res.qrc b/mix/res.qrc index b50aa3d9f..c868ff34b 100644 --- a/mix/res.qrc +++ b/mix/res.qrc @@ -90,5 +90,10 @@ qml/img/saveicon@2x.png qml/img/sendtransactionicon.png qml/img/sendtransactionicon@2x.png + qml/img/edittransaction@2x.png + qml/img/newblock@2x.png + qml/img/edittransaction2@2x.png + qml/img/edittransaction.png + qml/img/edittransaction2.png diff --git a/rlp/main.cpp b/rlp/main.cpp index 3924f9c44..8ff7202c3 100644 --- a/rlp/main.cpp +++ b/rlp/main.cpp @@ -49,6 +49,7 @@ void help() << "General options:" << endl << " -L,--lenience Try not to bomb out early if possible." << endl << " -x,--hex,--base-16 Treat input RLP as hex encoded data." << endl + << " -k,--keccak Output Keccak-256 hash only." << endl << " --64,--base-64 Treat input RLP as base-64 encoded data." << endl << " -b,--bin,--base-256 Treat input RLP as raw binary data." << endl << " -h,--help Print this help message and exit." << endl @@ -75,6 +76,7 @@ enum class Encoding { Hex, Base64, Binary, + Keccak, }; bool isAscii(string const& _s) @@ -111,9 +113,9 @@ public: m_out << "null"; else if (_d.isInt() && !m_prefs.stringInts) if (m_prefs.hexInts) - m_out << (m_prefs.hexPrefix ? "0x" : "") << toHex(toCompactBigEndian(_d.toInt(RLP::LaisezFaire), 1), 1); + m_out << (m_prefs.hexPrefix ? "0x" : "") << toHex(toCompactBigEndian(_d.toInt(RLP::LaissezFaire), 1), 1); else - m_out << _d.toInt(RLP::LaisezFaire); + m_out << _d.toInt(RLP::LaissezFaire); else if (_d.isData() || (_d.isInt() && m_prefs.stringInts)) if (m_prefs.forceString || (!m_prefs.forceHex && isAscii(_d.toString()))) m_out << escaped(_d.toString(), m_prefs.escapeAll); @@ -186,6 +188,8 @@ int main(int argc, char** argv) version(); else if (arg == "-x" || arg == "--hex" || arg == "--base-16") encoding = Encoding::Hex; + else if (arg == "-k" || arg == "--keccak") + encoding = Encoding::Keccak; else if (arg == "--64" || arg == "--base-64") encoding = Encoding::Base64; else if (arg == "-b" || arg == "--bin" || arg == "--base-256") @@ -371,6 +375,9 @@ int main(int argc, char** argv) case Encoding::Binary: cout.write((char const*)out.out().data(), out.out().size()); break; + case Encoding::Keccak: + cout << sha3(out.out()).hex() << endl; + break; } break; } diff --git a/test/libdevcore/RangeMask.cpp b/test/libdevcore/RangeMask.cpp new file mode 100644 index 000000000..05f58963a --- /dev/null +++ b/test/libdevcore/RangeMask.cpp @@ -0,0 +1,148 @@ +/* + 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 RangeMask.cpp + * @author Christian + * @date 2015 + */ + +#include +#include "../TestHelper.h" + +using namespace std; +using namespace dev; + +namespace dev +{ +namespace test +{ + +BOOST_AUTO_TEST_SUITE(RangeMaskTest) + +BOOST_AUTO_TEST_CASE(constructor) +{ + using RM = RangeMask; + using Range = pair; + for (RM r: {RM(), RM(1, 10), RM(Range(2, 10))}) + { + BOOST_CHECK(r.empty()); + BOOST_CHECK(!r.contains(0)); + BOOST_CHECK(!r.contains(1)); + BOOST_CHECK_EQUAL(0, r.size()); + } + BOOST_CHECK(RM().full()); + BOOST_CHECK(!RM(1, 10).full()); + BOOST_CHECK(!RM(Range(2, 10)).full()); +} + +BOOST_AUTO_TEST_CASE(simple_unions) +{ + using RM = RangeMask; + using Range = pair; + RM m(Range(0, 2000)); + m.unionWith(Range(1, 2)); + BOOST_CHECK_EQUAL(m.size(), 1); + m.unionWith(Range(50, 250)); + BOOST_CHECK_EQUAL(m.size(), 201); + m.unionWith(Range(10, 16)); + BOOST_CHECK_EQUAL(m.size(), 207); + BOOST_CHECK(m.contains(1)); + BOOST_CHECK(m.contains(11)); + BOOST_CHECK(m.contains(51)); + BOOST_CHECK(m.contains(200)); + BOOST_CHECK(!m.contains(2)); + BOOST_CHECK(!m.contains(7)); + BOOST_CHECK(!m.contains(17)); + BOOST_CHECK(!m.contains(258)); +} + +BOOST_AUTO_TEST_CASE(empty_union) +{ + using RM = RangeMask; + using Range = pair; + RM m(Range(0, 2000)); + m.unionWith(Range(3, 6)); + BOOST_CHECK_EQUAL(m.size(), 3); + m.unionWith(Range(50, 50)); + BOOST_CHECK_EQUAL(m.size(), 3); + m.unionWith(Range(0, 0)); + BOOST_CHECK_EQUAL(m.size(), 3); + m.unionWith(Range(1, 1)); + BOOST_CHECK_EQUAL(m.size(), 3); + m.unionWith(Range(2, 2)); + BOOST_CHECK_EQUAL(m.size(), 3); + m.unionWith(Range(3, 3)); + BOOST_CHECK_EQUAL(m.size(), 3); +} + +BOOST_AUTO_TEST_CASE(overlapping_unions) +{ + using RM = RangeMask; + using Range = pair; + RM m(Range(0, 2000)); + m.unionWith(Range(10, 20)); + BOOST_CHECK_EQUAL(10, m.size()); + m.unionWith(Range(30, 40)); + BOOST_CHECK_EQUAL(20, m.size()); + m.unionWith(Range(15, 30)); + BOOST_CHECK_EQUAL(40 - 10, m.size()); + m.unionWith(Range(50, 60)); + m.unionWith(Range(45, 55)); + // [40, 45) still missing here + BOOST_CHECK_EQUAL(60 - 10 - 5, m.size()); + m.unionWith(Range(15, 56)); + BOOST_CHECK_EQUAL(60 - 10, m.size()); + m.unionWith(Range(15, 65)); + BOOST_CHECK_EQUAL(65 - 10, m.size()); + m.unionWith(Range(5, 70)); + BOOST_CHECK_EQUAL(70 - 5, m.size()); +} + +BOOST_AUTO_TEST_CASE(complement) +{ + using RM = RangeMask; + using Range = pair; + RM m(Range(0, 2000)); + m.unionWith(7).unionWith(9); + m = ~m; + m.unionWith(7).unionWith(9); + m = ~m; + BOOST_CHECK(m.empty()); + + m += Range(0, 10); + m += Range(1000, 2000); + m.invert(); + BOOST_CHECK_EQUAL(m.size(), 1000 - 10); +} + +BOOST_AUTO_TEST_CASE(iterator) +{ + using RM = RangeMask; + using Range = pair; + RM m(Range(0, 2000)); + m.unionWith(Range(7, 9)); + m.unionWith(11); + m.unionWith(Range(200, 205)); + + vector elements; + copy(m.begin(), m.end(), back_inserter(elements)); + BOOST_CHECK(elements == (vector{7, 8, 11, 200, 201, 202, 203, 204})); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} diff --git a/test/libethereum/gaspricer.cpp b/test/libethereum/gaspricer.cpp index 105f4c2d7..ce49a4a20 100644 --- a/test/libethereum/gaspricer.cpp +++ b/test/libethereum/gaspricer.cpp @@ -20,10 +20,9 @@ */ #include -#include #include -#include -#include +#include +#include #include "../TestHelper.h" using namespace std; diff --git a/test/libethereum/transactionqueue.cpp b/test/libethereum/transactionqueue.cpp new file mode 100644 index 000000000..ab85350a5 --- /dev/null +++ b/test/libethereum/transactionqueue.cpp @@ -0,0 +1,64 @@ +/* + 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 transactionqueue.cpp + * @author Christoph Jentzsch + * @date 2015 + * TransactionQueue test functions. + */ + +#include +#include "../TestHelper.h" + +using namespace std; +using namespace dev; +using namespace dev::eth; + +BOOST_AUTO_TEST_SUITE(TransactionQueue) + +BOOST_AUTO_TEST_CASE(maxNonce) +{ + + dev::eth::TransactionQueue txq; + + // from a94f5374fce5edbc8e2a8697c15331677e6ebf0b + const u256 gasCost = 10 * szabo; + const u256 gas = 25000; + Address dest = Address("0x095e7baea6a6c7c4c2dfeb977efac326af552d87"); + Address to = Address("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"); + Secret sec = Secret("0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"); + Transaction tx0(0, gasCost, gas, dest, bytes(), 0, sec ); + Transaction tx0_1(1, gasCost, gas, dest, bytes(), 0, sec ); + Transaction tx1(0, gasCost, gas, dest, bytes(), 1, sec ); + Transaction tx2(0, gasCost, gas, dest, bytes(), 2, sec ); + Transaction tx9(0, gasCost, gas, dest, bytes(), 9, sec ); + + txq.import(tx0); + BOOST_CHECK(1 == txq.maxNonce(to)); + txq.import(tx0); + BOOST_CHECK(1 == txq.maxNonce(to)); + txq.import(tx0_1); + BOOST_CHECK(1 == txq.maxNonce(to)); + txq.import(tx1); + BOOST_CHECK(2 == txq.maxNonce(to)); + txq.import(tx9); + BOOST_CHECK(10 == txq.maxNonce(to)); + txq.import(tx2); + BOOST_CHECK(10 == txq.maxNonce(to)); + +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libweb3jsonrpc/webthreestubclient.h b/test/libweb3jsonrpc/webthreestubclient.h index 175f1958c..fbef7a62a 100644 --- a/test/libweb3jsonrpc/webthreestubclient.h +++ b/test/libweb3jsonrpc/webthreestubclient.h @@ -166,43 +166,43 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - std::string eth_getBlockTransactionCountByHash(const std::string& param1) throw (jsonrpc::JsonRpcException) + Json::Value eth_getBlockTransactionCountByHash(const std::string& param1) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); Json::Value result = this->CallMethod("eth_getBlockTransactionCountByHash",p); - if (result.isString()) - return result.asString(); + if (result.isObject()) + return result; else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - std::string eth_getBlockTransactionCountByNumber(const std::string& param1) throw (jsonrpc::JsonRpcException) + Json::Value eth_getBlockTransactionCountByNumber(const std::string& param1) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); Json::Value result = this->CallMethod("eth_getBlockTransactionCountByNumber",p); - if (result.isString()) - return result.asString(); + if (result.isObject()) + return result; else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - std::string eth_getUncleCountByBlockHash(const std::string& param1) throw (jsonrpc::JsonRpcException) + Json::Value eth_getUncleCountByBlockHash(const std::string& param1) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); Json::Value result = this->CallMethod("eth_getUncleCountByBlockHash",p); - if (result.isString()) - return result.asString(); + if (result.isObject()) + return result; else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - std::string eth_getUncleCountByBlockNumber(const std::string& param1) throw (jsonrpc::JsonRpcException) + Json::Value eth_getUncleCountByBlockNumber(const std::string& param1) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); Json::Value result = this->CallMethod("eth_getUncleCountByBlockNumber",p); - if (result.isString()) - return result.asString(); + if (result.isObject()) + return result; else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } diff --git a/test/libwhisper/bloomFilter.cpp b/test/libwhisper/bloomFilter.cpp index 814990d52..197a7258d 100644 --- a/test/libwhisper/bloomFilter.cpp +++ b/test/libwhisper/bloomFilter.cpp @@ -28,7 +28,7 @@ using namespace dev; using namespace dev::shh; using TopicBloomFilterShort = TopicBloomFilterBase<4>; -using TopicBloomFilterTest = TopicBloomFilterBase; +using TopicBloomFilterTest = TopicBloomFilterBase; void testAddNonExisting(TopicBloomFilterShort& _f, AbridgedTopic const& _h) { @@ -61,7 +61,7 @@ void testRemoveExistingBloom(TopicBloomFilterShort& _f, AbridgedTopic const& _h) double calculateExpected(TopicBloomFilterTest const& f, int n) { int const m = f.size * 8; // number of bits in the bloom - int const k = f.BitsPerBloom; // number of hash functions (e.g. bits set to 1 in every bloom) + int const k = BitsPerBloom; // number of hash functions (e.g. bits set to 1 in every bloom) double singleBitSet = 1.0 / m; // probability of any bit being set after inserting a single bit double singleBitNotSet = (1.0 - singleBitSet); @@ -244,4 +244,55 @@ BOOST_AUTO_TEST_CASE(bloomFilterRaw) BOOST_REQUIRE(!f.contains(b00110111)); } +static const unsigned DistributionTestSize = 8; +static const unsigned TestArrSize = 8 * DistributionTestSize; + +void updateDistribution(FixedHash const& _h, array& _distribution) +{ + unsigned bits = 0; + for (unsigned i = 0; i < DistributionTestSize; ++i) + if (_h[i]) + for (unsigned j = 0; j < 8; ++j) + if (_h[i] & c_powerOfTwoBitMmask[j]) + { + _distribution[i * 8 + j]++; + if (++bits >= BitsPerBloom) + return; + } +} + +BOOST_AUTO_TEST_CASE(distributionRate) +{ + cnote << "Testing Bloom Filter Distribution Rate..."; + + array distribution; + for (unsigned i = 0; i < TestArrSize; ++i) + distribution[i] = 0; + + Topic x(0xC0FFEE); // deterministic pseudorandom value + + for (unsigned i = 0; i < 22000; ++i) + { + x = sha3(x); + FixedHash h = x.template bloomPart(); + updateDistribution(h, distribution); + } + + unsigned average = 0; + for (unsigned i = 0; i < TestArrSize; ++i) + average += distribution[i]; + + average /= TestArrSize; + unsigned deviation = average / 10; // approx. 10% + unsigned maxAllowed = average + deviation; + unsigned minAllowed = average - deviation; + + for (unsigned i = 0; i < TestArrSize; ++i) + { + //cnote << i << ":" << distribution[i]; + BOOST_REQUIRE(distribution[i] > minAllowed); + BOOST_REQUIRE(distribution[i] < maxAllowed); + } +} + BOOST_AUTO_TEST_SUITE_END()