/* 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 WebThreeStubServerBase.cpp * @authors: * Gav Wood * Marek Kotewicz * @date 2014 */ #include "WebThreeStubServerBase.h" // Make sure boost/asio.hpp is included before windows.h. #include #include #include #if ETH_SOLIDITY || !ETH_TRUE #include #include #include #endif #include #include #include #include #include #include #include #if ETH_SERPENT || !ETH_TRUE #include #endif #include "AccountHolder.h" #include "JsonHelper.h" using namespace std; using namespace jsonrpc; using namespace dev; using namespace eth; using namespace shh; #if ETH_DEBUG const unsigned dev::SensibleHttpThreads = 1; #else const unsigned dev::SensibleHttpThreads = 4; #endif const unsigned dev::SensibleHttpPort = 8545; WebThreeStubServerBase::WebThreeStubServerBase(AbstractServerConnector& _conn, std::shared_ptr const& _ethAccounts, vector const& _sshAccounts): AbstractWebThreeStubServer(_conn), m_ethAccounts(_ethAccounts) { setIdentities(_sshAccounts); } void WebThreeStubServerBase::setIdentities(vector const& _ids) { m_shhIds.clear(); for (auto i: _ids) m_shhIds[i.pub()] = i.secret(); } string WebThreeStubServerBase::web3_sha3(string const& _param1) { return toJS(sha3(jsToBytes(_param1))); } string WebThreeStubServerBase::net_peerCount() { return toJS(network()->peerCount()); } bool WebThreeStubServerBase::net_listening() { return network()->isNetworkStarted(); } string WebThreeStubServerBase::eth_protocolVersion() { return toJS(eth::c_protocolVersion); } string WebThreeStubServerBase::eth_coinbase() { return toJS(client()->address()); } string WebThreeStubServerBase::eth_hashrate() { return toJS(client()->hashrate()); } bool WebThreeStubServerBase::eth_mining() { return client()->isMining(); } string WebThreeStubServerBase::eth_gasPrice() { return toJS(10 * dev::eth::szabo); } Json::Value WebThreeStubServerBase::eth_accounts() { Json::Value ret(Json::arrayValue); for (auto const& i: m_ethAccounts->allAccounts()) ret.append(toJS(i)); return ret; } string WebThreeStubServerBase::eth_blockNumber() { return toJS(client()->number()); } string WebThreeStubServerBase::eth_getBalance(string const& _address, string const& _blockNumber) { try { return toJS(client()->balanceAt(jsToAddress(_address), jsToBlockNumber(_blockNumber))); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } string WebThreeStubServerBase::eth_getStorageAt(string const& _address, string const& _position, string const& _blockNumber) { try { return toJS(client()->stateAt(jsToAddress(_address), jsToU256(_position), jsToBlockNumber(_blockNumber))); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } string WebThreeStubServerBase::eth_getTransactionCount(string const& _address, string const& _blockNumber) { try { return toJS(client()->countAt(jsToAddress(_address), jsToBlockNumber(_blockNumber))); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } string WebThreeStubServerBase::eth_getBlockTransactionCountByHash(string const& _blockHash) { try { return toJS(client()->transactionCount(jsToFixed<32>(_blockHash))); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } string WebThreeStubServerBase::eth_getBlockTransactionCountByNumber(string const& _blockNumber) { try { return toJS(client()->transactionCount(jsToBlockNumber(_blockNumber))); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } string WebThreeStubServerBase::eth_getUncleCountByBlockHash(string const& _blockHash) { try { return toJS(client()->uncleCount(jsToFixed<32>(_blockHash))); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } string WebThreeStubServerBase::eth_getUncleCountByBlockNumber(string const& _blockNumber) { try { return toJS(client()->uncleCount(jsToBlockNumber(_blockNumber))); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } string WebThreeStubServerBase::eth_getCode(string const& _address, string const& _blockNumber) { try { return toJS(client()->codeAt(jsToAddress(_address), jsToBlockNumber(_blockNumber))); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } string WebThreeStubServerBase::eth_sendTransaction(Json::Value const& _json) { try { string ret; TransactionSkeleton t = toTransactionSkeleton(_json); if (!t.from) t.from = m_ethAccounts->defaultTransactAccount(); if (t.creation) ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));; if (t.gasPrice == UndefinedU256) t.gasPrice = 10 * dev::eth::szabo; // TODO: should be determined by user somehow. if (t.gas == UndefinedU256) t.gas = min(client()->gasLimitRemaining() / 5, client()->balanceAt(t.from) / t.gasPrice); m_ethAccounts->authenticate(t); return ret; } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } string WebThreeStubServerBase::eth_signTransaction(Json::Value const& _json) { try { string ret; TransactionSkeleton t = toTransactionSkeleton(_json); if (!t.from) t.from = m_ethAccounts->defaultTransactAccount(); if (t.creation) ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));; if (t.gasPrice == UndefinedU256) t.gasPrice = 10 * dev::eth::szabo; // TODO: should be determined by user somehow. if (t.gas == UndefinedU256) t.gas = min(client()->gasLimitRemaining() / 5, client()->balanceAt(t.from) / t.gasPrice); m_ethAccounts->authenticate(t); return toJS((t.creation ? Transaction(t.value, t.gasPrice, t.gas, t.data) : Transaction(t.value, t.gasPrice, t.gas, t.to, t.data)).sha3(WithoutSignature)); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } Json::Value WebThreeStubServerBase::eth_inspectTransaction(std::string const& _rlp) { try { return toJson(Transaction(jsToBytes(_rlp), CheckTransaction::Everything)); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } bool WebThreeStubServerBase::eth_injectTransaction(std::string const& _rlp) { try { return client()->injectTransaction(jsToBytes(_rlp)) == ImportResult::Success; } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } string WebThreeStubServerBase::eth_call(Json::Value const& _json, string const& _blockNumber) { try { TransactionSkeleton t = toTransactionSkeleton(_json); if (!t.from) t.from = m_ethAccounts->defaultTransactAccount(); // if (!m_accounts->isRealAccount(t.from)) // return ret; if (t.gasPrice == UndefinedU256) t.gasPrice = 10 * dev::eth::szabo; if (t.gas == UndefinedU256) t.gas = client()->gasLimitRemaining(); return toJS(client()->call(t.from, t.value, t.to, t.data, t.gas, t.gasPrice, jsToBlockNumber(_blockNumber), FudgeFactor::Lenient).output); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } bool WebThreeStubServerBase::eth_flush() { client()->flushTransactions(); return true; } Json::Value WebThreeStubServerBase::eth_getBlockByHash(string const& _blockHash, bool _includeTransactions) { try { auto h = jsToFixed<32>(_blockHash); if (_includeTransactions) return toJson(client()->blockInfo(h), client()->blockDetails(h), client()->uncleHashes(h), client()->transactions(h)); else return toJson(client()->blockInfo(h), client()->blockDetails(h), client()->uncleHashes(h), client()->transactionHashes(h)); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } Json::Value WebThreeStubServerBase::eth_getBlockByNumber(string const& _blockNumber, bool _includeTransactions) { try { auto h = jsToBlockNumber(_blockNumber); if (_includeTransactions) return toJson(client()->blockInfo(h), client()->blockDetails(h), client()->uncleHashes(h), client()->transactions(h)); else return toJson(client()->blockInfo(h), client()->blockDetails(h), client()->uncleHashes(h), client()->transactionHashes(h)); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } Json::Value WebThreeStubServerBase::eth_getTransactionByHash(string const& _transactionHash) { try { h256 h = jsToFixed<32>(_transactionHash); auto l = client()->transactionLocation(h); return toJson(client()->transaction(h), l, client()->numberFromHash(l.first)); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } Json::Value WebThreeStubServerBase::eth_getTransactionByBlockHashAndIndex(string const& _blockHash, string const& _transactionIndex) { try { h256 bh = jsToFixed<32>(_blockHash); unsigned ti = jsToInt(_transactionIndex); Transaction t = client()->transaction(bh, ti); return toJson(t, make_pair(bh, ti), client()->numberFromHash(bh)); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } Json::Value WebThreeStubServerBase::eth_getTransactionByBlockNumberAndIndex(string const& _blockNumber, string const& _transactionIndex) { try { BlockNumber bn = jsToBlockNumber(_blockNumber); unsigned ti = jsToInt(_transactionIndex); Transaction t = client()->transaction(bn, ti); return toJson(t, make_pair(client()->hashFromNumber(bn), ti), bn); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } Json::Value WebThreeStubServerBase::eth_getUncleByBlockHashAndIndex(string const& _blockHash, string const& _uncleIndex) { try { return toJson(client()->uncle(jsToFixed<32>(_blockHash), jsToInt(_uncleIndex))); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } Json::Value WebThreeStubServerBase::eth_getUncleByBlockNumberAndIndex(string const& _blockNumber, string const& _uncleIndex) { try { return toJson(client()->uncle(jsToBlockNumber(_blockNumber), jsToInt(_uncleIndex))); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } Json::Value WebThreeStubServerBase::eth_getCompilers() { Json::Value ret(Json::arrayValue); ret.append("lll"); #if ETH_SOLIDITY || !TRUE ret.append("solidity"); #endif #if ETH_SERPENT || !TRUE ret.append("serpent"); #endif return ret; } string WebThreeStubServerBase::eth_compileLLL(string const& _source) { // TODO throw here jsonrpc errors string res; vector errors; res = toJS(dev::eth::compileLLL(_source, true, &errors)); cwarn << "LLL compilation errors: " << errors; return res; } string WebThreeStubServerBase::eth_compileSerpent(string const& _source) { // TODO throw here jsonrpc errors string res; #if ETH_SERPENT || !ETH_TRUE try { res = toJS(dev::asBytes(::compile(_source))); } catch (string err) { cwarn << "Serpent compilation error: " << err; } catch (...) { cwarn << "Uncought serpent compilation exception"; } #else (void)_source; #endif return res; } #define ADMIN requires(_session, Priviledge::Admin) bool WebThreeStubServerBase::admin_web3_setVerbosity(int _v, string const& _session) { ADMIN; g_logVerbosity = _v; return true; } bool WebThreeStubServerBase::admin_net_start(std::string const& _session) { ADMIN; network()->startNetwork(); return true; } bool WebThreeStubServerBase::admin_net_stop(std::string const& _session) { ADMIN; network()->stopNetwork(); return true; } bool WebThreeStubServerBase::admin_net_connect(std::string const& _node, std::string const& _session) { ADMIN; p2p::NodeId id; bi::tcp::endpoint ep; if (_node.substr(0, 8) == "enode://" && _node.find('@') == 136) { id = p2p::NodeId(_node.substr(8, 128)); ep = p2p::Network::resolveHost(_node.substr(137)); } else ep = p2p::Network::resolveHost(_node); network()->requirePeer(id, ep); return true; } Json::Value WebThreeStubServerBase::admin_net_peers(std::string const& _session) { ADMIN; Json::Value ret; for (p2p::PeerSessionInfo const& i: network()->peers()) ret.append(toJson(i)); return ret; } bool WebThreeStubServerBase::admin_eth_setMining(bool _on, std::string const& _session) { ADMIN; if (_on) client()->startMining(); else client()->stopMining(); return true; } Json::Value WebThreeStubServerBase::eth_compileSolidity(string const& _source) { // TOOD throw here jsonrpc errors Json::Value res(Json::objectValue); #if ETH_SOLIDITY || !ETH_TRUE dev::solidity::CompilerStack compiler; try { compiler.addSource("source", _source); compiler.compile(); for (string const& name: compiler.getContractNames()) { Json::Value contract(Json::objectValue); contract["code"] = toJS(compiler.getBytecode(name)); Json::Value info(Json::objectValue); info["source"] = _source; info["language"] = ""; info["languageVersion"] = ""; info["compilerVersion"] = ""; Json::Reader reader; reader.parse(compiler.getInterface(name), info["abiDefinition"]); reader.parse(compiler.getMetadata(name, dev::solidity::DocumentationType::NatspecUser), info["userDoc"]); reader.parse(compiler.getMetadata(name, dev::solidity::DocumentationType::NatspecDev), info["developerDoc"]); contract["info"] = info; res[name] = contract; } } catch (dev::Exception const& exception) { ostringstream error; solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); cwarn << "Solidity compilation error: " << error.str(); return Json::Value(Json::objectValue); } catch (...) { cwarn << "Uncought solidity compilation exception"; return Json::Value(Json::objectValue); } #else (void)_source; #endif return res; } string WebThreeStubServerBase::eth_newFilter(Json::Value const& _json) { try { return toJS(client()->installWatch(toLogFilter(_json, *client()))); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } string WebThreeStubServerBase::eth_newFilterEx(Json::Value const& _json) { try { return toJS(client()->installWatch(toLogFilter(_json))); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } string WebThreeStubServerBase::eth_newBlockFilter() { h256 filter = dev::eth::ChainChangedFilter; return toJS(client()->installWatch(filter)); } string WebThreeStubServerBase::eth_newPendingTransactionFilter() { h256 filter = dev::eth::PendingChangedFilter; return toJS(client()->installWatch(filter)); } bool WebThreeStubServerBase::eth_uninstallFilter(string const& _filterId) { try { return client()->uninstallWatch(jsToInt(_filterId)); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } Json::Value WebThreeStubServerBase::eth_getFilterChanges(string const& _filterId) { try { int id = jsToInt(_filterId); auto entries = client()->checkWatch(id); if (entries.size()) cnote << "FIRING WATCH" << id << entries.size(); return toJson(entries); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } Json::Value WebThreeStubServerBase::eth_getFilterChangesEx(string const& _filterId) { try { int id = jsToInt(_filterId); auto entries = client()->checkWatch(id); if (entries.size()) cnote << "FIRING WATCH" << id << entries.size(); return toJson(entries); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } Json::Value WebThreeStubServerBase::eth_getFilterLogs(string const& _filterId) { try { return toJson(client()->logs(jsToInt(_filterId))); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } Json::Value WebThreeStubServerBase::eth_getFilterLogsEx(string const& _filterId) { try { return toJson(client()->logs(jsToInt(_filterId))); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } Json::Value WebThreeStubServerBase::eth_getLogs(Json::Value const& _json) { try { return toJson(client()->logs(toLogFilter(_json))); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } Json::Value WebThreeStubServerBase::eth_getWork() { Json::Value ret(Json::arrayValue); auto r = client()->getWork(); ret.append(toJS(r.headerHash)); ret.append(toJS(r.seedHash)); ret.append(toJS(r.boundary)); return ret; } bool WebThreeStubServerBase::eth_submitWork(string const& _nonce, string const&, string const& _mixHash) { try { return client()->submitWork(ProofOfWork::Solution{jsToFixed(_nonce), jsToFixed<32>(_mixHash)}); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } string WebThreeStubServerBase::eth_register(string const& _address) { try { return toJS(m_ethAccounts->addProxyAccount(jsToAddress(_address))); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } bool WebThreeStubServerBase::eth_unregister(string const& _accountId) { try { return m_ethAccounts->removeProxyAccount(jsToInt(_accountId)); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } Json::Value WebThreeStubServerBase::eth_fetchQueuedTransactions(string const& _accountId) { try { auto id = jsToInt(_accountId); Json::Value ret(Json::arrayValue); // TODO: throw an error on no account with given id for (TransactionSkeleton const& t: m_ethAccounts->queuedTransactions(id)) ret.append(toJson(t)); m_ethAccounts->clearQueue(id); return ret; } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } bool WebThreeStubServerBase::db_put(string const& _name, string const& _key, string const& _value) { db()->put(_name, _key,_value); return true; } string WebThreeStubServerBase::db_get(string const& _name, string const& _key) { return db()->get(_name, _key);; } bool WebThreeStubServerBase::shh_post(Json::Value const& _json) { try { shh::Message m = toMessage(_json); Secret from; if (m.from() && m_shhIds.count(m.from())) { cwarn << "Silently signing message from identity" << m.from() << ": User validation hook goes here."; // TODO: insert validification hook here. from = m_shhIds[m.from()]; } face()->inject(toSealed(_json, m, from)); return true; } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } string WebThreeStubServerBase::shh_newIdentity() { KeyPair kp = KeyPair::create(); m_shhIds[kp.pub()] = kp.secret(); return toJS(kp.pub()); } bool WebThreeStubServerBase::shh_hasIdentity(string const& _identity) { try { return m_shhIds.count(jsToPublic(_identity)) > 0; } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } string WebThreeStubServerBase::shh_newGroup(string const& _id, string const& _who) { (void)_id; (void)_who; return ""; } string WebThreeStubServerBase::shh_addToGroup(string const& _group, string const& _who) { (void)_group; (void)_who; return ""; } string WebThreeStubServerBase::shh_newFilter(Json::Value const& _json) { try { pair w = toWatch(_json); auto ret = face()->installWatch(w.first); m_shhWatches.insert(make_pair(ret, w.second)); return toJS(ret); } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } bool WebThreeStubServerBase::shh_uninstallFilter(string const& _filterId) { try { face()->uninstallWatch(jsToInt(_filterId)); return true; } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } Json::Value WebThreeStubServerBase::shh_getFilterChanges(string const& _filterId) { try { Json::Value ret(Json::arrayValue); int id = jsToInt(_filterId); auto pub = m_shhWatches[id]; if (!pub || m_shhIds.count(pub)) for (h256 const& h: face()->checkWatch(id)) { auto e = face()->envelope(h); shh::Message m; if (pub) { cwarn << "Silently decrypting message from identity" << pub << ": User validation hook goes here."; m = e.open(face()->fullTopics(id), m_shhIds[pub]); } else m = e.open(face()->fullTopics(id)); if (!m) continue; ret.append(toJson(h, e, m)); } return ret; } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } } Json::Value WebThreeStubServerBase::shh_getMessages(string const& _filterId) { try { Json::Value ret(Json::arrayValue); int id = jsToInt(_filterId); auto pub = m_shhWatches[id]; if (!pub || m_shhIds.count(pub)) for (h256 const& h: face()->watchMessages(id)) { auto e = face()->envelope(h); shh::Message m; if (pub) { cwarn << "Silently decrypting message from identity" << pub << ": User validation hook goes here."; m = e.open(face()->fullTopics(id), m_shhIds[pub]); } else m = e.open(face()->fullTopics(id)); if (!m) continue; ret.append(toJson(h, e, m)); } return ret; } catch (...) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } }