diff --git a/CMakeLists.txt b/CMakeLists.txt index 71d12353c..729f95ed6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -430,9 +430,9 @@ if (TOOLS) endif() -if (NCURSES) - add_subdirectory(neth) -endif () +#if (NCURSES) +# add_subdirectory(neth) +#endif () if (GUI) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index e88086e0d..848020cbc 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -212,7 +212,7 @@ Main::Main(QWidget *parent) : m_httpConnector.reset(new jsonrpc::HttpServer(SensibleHttpPort, "", "", dev::SensibleHttpThreads)); auto w3ss = new OurWebThreeStubServer(*m_httpConnector, this); m_server.reset(w3ss); - auto sessionKey = w3ss->newSession({true}); + auto sessionKey = w3ss->newSession(SessionPermissions{{Priviledge::Admin}}); connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString))); m_server->setIdentities(keysAsVector(owned())); m_server->StartListening(); @@ -1922,7 +1922,7 @@ void Main::on_clearPending_triggered() void Main::on_retryUnknown_triggered() { - ethereum()->retryUnkonwn(); + ethereum()->retryUnknown(); } void Main::on_killBlockchain_triggered() @@ -1941,11 +1941,7 @@ void Main::on_net_triggered() { ui->port->setEnabled(!ui->net->isChecked()); ui->clientName->setEnabled(!ui->net->isChecked()); - string n = string("AlethZero/v") + dev::Version; - if (ui->clientName->text().size()) - n += "/" + ui->clientName->text().toStdString(); - n += "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM); - web3()->setClientVersion(n); + web3()->setClientVersion(WebThreeDirect::composeClientVersion("AlethZero", ui->clientName->text().toStdString())); if (ui->net->isChecked()) { web3()->setIdealPeerCount(ui->idealPeers->value()); diff --git a/alethzero/OurWebThreeStubServer.cpp b/alethzero/OurWebThreeStubServer.cpp index 39ab69a19..e18cb55d5 100644 --- a/alethzero/OurWebThreeStubServer.cpp +++ b/alethzero/OurWebThreeStubServer.cpp @@ -33,7 +33,7 @@ OurWebThreeStubServer::OurWebThreeStubServer( jsonrpc::AbstractServerConnector& _conn, Main* _main ): - WebThreeStubServer(_conn, *_main->web3(), make_shared(_main), _main->owned().toVector().toStdVector(), _main->keyManager()), + WebThreeStubServer(_conn, *_main->web3(), make_shared(_main), _main->owned().toVector().toStdVector(), _main->keyManager(), *static_cast(_main->ethereum()->gasPricer().get())), m_main(_main) { } diff --git a/alethzero/OurWebThreeStubServer.h b/alethzero/OurWebThreeStubServer.h index 43985b640..cc950027e 100644 --- a/alethzero/OurWebThreeStubServer.h +++ b/alethzero/OurWebThreeStubServer.h @@ -59,7 +59,7 @@ private: Main* m_main; }; -class OurWebThreeStubServer: public QObject, public WebThreeStubServer +class OurWebThreeStubServer: public QObject, public dev::WebThreeStubServer { Q_OBJECT diff --git a/eth/main.cpp b/eth/main.cpp index 4b23ef62a..382858ae7 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -685,9 +685,8 @@ int main(int argc, char** argv) VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); - std::string clientImplString = "++eth/" + clientName + "v" + dev::Version + "-" + string(DEV_QUOTED(ETH_COMMIT_HASH)).substr(0, 8) + (ETH_CLEAN_REPO ? "" : "*") + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""); dev::WebThreeDirect web3( - clientImplString, + WebThreeDirect::composeClientVersion("++eth", clientName), dbPath, killChain, nodeMode == NodeMode::Full ? set{"eth"/*, "shh"*/} : set(), @@ -800,7 +799,7 @@ int main(int argc, char** argv) // 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(clientImplString, dev::Version); + StructuredLogger::starting(WebThreeDirect::composeClientVersion("++eth", clientName), dev::Version); if (c) { c->setGasPricer(gasPricer); @@ -827,12 +826,12 @@ int main(int argc, char** argv) if (jsonrpc > -1) { jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); - jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){return web3.ethereum();}, getAccountPassword, keyManager), vector(), keyManager)); + jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector(), keyManager, *gasPricer)); jsonrpcServer->StartListening(); if (jsonAdmin.empty()) - jsonAdmin = jsonrpcServer->newSession(SessionPermissions{true}); + jsonAdmin = jsonrpcServer->newSession(SessionPermissions{{Priviledge::Admin}}); else - jsonrpcServer->addSession(jsonAdmin, SessionPermissions{true}); + jsonrpcServer->addSession(jsonAdmin, SessionPermissions{{Priviledge::Admin}}); cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl; } #endif @@ -982,12 +981,12 @@ int main(int argc, char** argv) if (jsonrpc < 0) jsonrpc = SensibleHttpPort; jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); - jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector(), keyManager)); + jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){ return web3.ethereum(); }, getAccountPassword, keyManager), vector(), keyManager, *gasPricer)); jsonrpcServer->StartListening(); if (jsonAdmin.empty()) - jsonAdmin = jsonrpcServer->newSession(SessionPermissions{true}); + jsonAdmin = jsonrpcServer->newSession(SessionPermissions{{Priviledge::Admin}}); else - jsonrpcServer->addSession(jsonAdmin, SessionPermissions{true}); + jsonrpcServer->addSession(jsonAdmin, SessionPermissions{{Priviledge::Admin}}); cout << "JSONRPC Admin Session Key: " << jsonAdmin << endl; } else if (cmd == "jsonstop") @@ -1083,7 +1082,7 @@ int main(int argc, char** argv) } else if (c && cmd == "retryunknown") { - c->retryUnkonwn(); + c->retryUnknown(); } else if (cmd == "peers") { @@ -1520,7 +1519,7 @@ int main(int argc, char** argv) ofstream f; f.open(filename); - dev::eth::State state =c->state(index + 1,c->blockChain().numberHash(block)); + dev::eth::State state = c->state(index + 1,c->blockChain().numberHash(block)); if (index < state.pending().size()) { Executive e(state, c->blockChain(), 0); @@ -1712,7 +1711,7 @@ int main(int argc, char** argv) while (!g_exit) this_thread::sleep_for(chrono::milliseconds(1000)); - StructuredLogger::stopping(clientImplString, dev::Version); + StructuredLogger::stopping(WebThreeDirect::composeClientVersion("++eth", clientName), dev::Version); auto netData = web3.saveNetwork(); if (!netData.empty()) writeFile((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp", netData); diff --git a/ethminer/MinerAux.h b/ethminer/MinerAux.h index 6de3913e4..3351b90de 100644 --- a/ethminer/MinerAux.h +++ b/ethminer/MinerAux.h @@ -410,7 +410,6 @@ private: } catch (...) { - cout << "Error phoning home. ET is sad." << endl; } } #endif diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index 2c743f33b..ebf8c5615 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -108,7 +108,10 @@ bool Ethash::verify(BlockInfo const& _header) bool pre = preVerify(_header); #if !ETH_DEBUG if (!pre) + { + cwarn << "Fail on preVerify"; return false; + } #endif auto result = EthashAux::eval(_header); diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index bd6996a45..195f65f1d 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -1072,7 +1072,7 @@ VerifiedBlockRef BlockChain::verifyBlock(bytes const& _block, functionisConversing()) { _peer->setIdle(); - if (_peer->isCriticalSyncing()) +// if (_peer->isCriticalSyncing()) _peer->setRude(); continueSync(); } diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 7a30f1ad9..b876bf019 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -48,7 +48,6 @@ EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i, Cap EthereumPeer::~EthereumPeer() { - clog(NetMessageSummary) << "Aborting Sync :-("; abortSync(); } @@ -57,8 +56,15 @@ bool EthereumPeer::isRude() const return repMan().isRude(*session(), name()); } +unsigned EthereumPeer::askOverride() const +{ + bytes const& d = repMan().data(*session(), name()); + return d.empty() ? c_maxBlocksAsk : RLP(d).toInt(RLP::LaisezFaire); +} + void EthereumPeer::setRude() { + repMan().setData(*session(), name(), rlp(askOverride() / 2 + 1)); repMan().noteRude(*session(), name()); session()->addNote("manners", "RUDE"); } @@ -140,7 +146,7 @@ void EthereumPeer::requestHashes(h256 const& _lastHash) void EthereumPeer::requestBlocks() { setAsking(Asking::Blocks); - auto blocks = m_sub.nextFetch(isRude() ? 1 : c_maxBlocksAsk); + auto blocks = m_sub.nextFetch(askOverride()); if (blocks.size()) { RLPStream s; diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h index a12b7a197..e9d29322f 100644 --- a/libethereum/EthereumPeer.h +++ b/libethereum/EthereumPeer.h @@ -91,6 +91,9 @@ public: private: using p2p::Capability::sealAndSend; + /// Figure out the amount of blocks we should be asking for. + unsigned askOverride() const; + /// Interpret an incoming message. virtual bool interpret(unsigned _id, RLP const& _r); diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 4976ce03a..41a8d1961 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -53,6 +53,8 @@ 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); }; } + private: bool m_showMnemonics = false; std::vector m_lastInst; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index a4b784b6f..f56c12c91 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -115,21 +115,19 @@ State::State(OverlayDB const& _db, BaseState _bs, Address _coinbaseAddress): paranoia("end of normal construction.", true); } -State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h, ImportRequirements::value _ir): - m_db(_db), - m_state(&m_db), - m_blockReward(c_blockReward) +PopulationStatistics State::populateFromChain(BlockChain const& _bc, h256 const& _h, ImportRequirements::value _ir) { - auto b = _bc.block(_h); - BlockInfo bi(b); + PopulationStatistics ret; - if (!bi) + if (!_bc.isKnown(_h)) { // Might be worth throwing here. cwarn << "Invalid block given for state population: " << _h; - return; + return ret; } + auto b = _bc.block(_h); + BlockInfo bi(b); if (bi.number) { // Non-genesis: @@ -143,10 +141,10 @@ State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h, ImportRequire m_ourAddress = bi.coinbaseAddress; boost::timer t; auto vb = BlockChain::verifyBlock(b); - cnote << "verifyBlock:" << t.elapsed(); + ret.verify = t.elapsed(); t.restart(); enact(vb, _bc, _ir); - cnote << "enact:" << t.elapsed(); + ret.enact = t.elapsed(); } else { @@ -155,6 +153,8 @@ State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h, ImportRequire m_state.init(); sync(_bc, _h, bi, _ir); } + + return ret; } State::State(State const& _s): @@ -600,7 +600,7 @@ string State::vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequire { StandardTrace st; st.setShowMnemonics(); - execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, [&](uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM) { st(_steps, _inst, _newMemSize, _gasCost, _gas, _vm, _extVM); }); + execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, st.onOp()); ret += (ret.empty() ? "[" : ",") + st.json(); RLPStream receiptRLP; diff --git a/libethereum/State.h b/libethereum/State.h index 771cdb6bf..3eac63d04 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -123,6 +123,12 @@ enum class Permanence Committed }; +struct PopulationStatistics +{ + double verify; + double enact; +}; + /** * @brief Model of the current state of the ledger. * Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block). @@ -146,9 +152,6 @@ public: /// You can also set the coinbase address. explicit State(OverlayDB const& _db, BaseState _bs = BaseState::PreExisting, Address _coinbaseAddress = Address()); - /// Construct state object from arbitrary point in blockchain. - State(OverlayDB const& _db, BlockChain const& _bc, h256 _hash, ImportRequirements::value _ir = ImportRequirements::Default); - /// Copy state object. State(State const& _s); @@ -157,6 +160,9 @@ public: ~State(); + /// Construct state object from arbitrary point in blockchain. + PopulationStatistics populateFromChain(BlockChain const& _bc, h256 const& _hash, ImportRequirements::value _ir = ImportRequirements::Default); + /// Set the coinbase address for any transactions we do. /// This causes a complete reset of current block. void setAddress(Address _coinbaseAddress) { m_ourAddress = _coinbaseAddress; resetCurrent(); } diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 357292853..f6cab376a 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -63,12 +63,16 @@ using LogEntries = std::vector; struct LocalisedLogEntry: public LogEntry { LocalisedLogEntry() {} - explicit LocalisedLogEntry(LogEntry const& _le): LogEntry(_le) {}; + explicit LocalisedLogEntry(LogEntry const& _le): LogEntry(_le) {} explicit LocalisedLogEntry( LogEntry const& _le, h256 _special - ): LogEntry(_le), special(_special) {}; + ): + LogEntry(_le), + isSpecial(true), + special(_special) + {} explicit LocalisedLogEntry( LogEntry const& _le, @@ -76,15 +80,24 @@ struct LocalisedLogEntry: public LogEntry h256 _th, unsigned _ti, unsigned _li - ): LogEntry(_le), blockHash(_bi.hash()), blockNumber((BlockNumber)_bi.number), transactionHash(_th), transactionIndex(_ti), logIndex(_li), mined(true) {}; - - h256 blockHash = h256(); + ): + LogEntry(_le), + blockHash(_bi.hash()), + blockNumber((BlockNumber)_bi.number), + transactionHash(_th), + transactionIndex(_ti), + logIndex(_li), + mined(true) + {} + + h256 blockHash; BlockNumber blockNumber = 0; - h256 transactionHash = h256(); + h256 transactionHash; unsigned transactionIndex = 0; unsigned logIndex = 0; bool mined = false; - h256 special = h256(); + bool isSpecial = false; + h256 special; }; using LocalisedLogEntries = std::vector; diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 55389ed1b..1482719c6 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -79,6 +79,25 @@ bool ReputationManager::isRude(Session const& _s, std::string const& _sub) const return false; } +void ReputationManager::setData(Session const& _s, std::string const& _sub, bytes const& _data) +{ + DEV_WRITE_GUARDED(x_nodes) + m_nodes[make_pair(_s.id(), _s.info().clientVersion)].subs[_sub].data = _data; +} + +bytes ReputationManager::data(Session const& _s, std::string const& _sub) const +{ + DEV_READ_GUARDED(x_nodes) + { + auto nit = m_nodes.find(make_pair(_s.id(), _s.info().clientVersion)); + if (nit == m_nodes.end()) + return bytes(); + auto sit = nit->second.subs.find(_sub); + return sit == nit->second.subs.end() ? bytes() : sit->second.data; + } + return bytes(); +} + Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bytesConstRef _restoreNetwork): Worker("p2p", 0), m_restoreNetwork(_restoreNetwork.toBytes()), diff --git a/libp2p/Host.h b/libp2p/Host.h index e9cae509c..9523d0cca 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -83,6 +83,7 @@ struct SubReputation { bool isRude = false; int utility = 0; + bytes data; }; struct Reputation @@ -97,6 +98,8 @@ public: void noteRude(Session const& _s, std::string const& _sub = std::string()); bool isRude(Session const& _s, std::string const& _sub = std::string()) const; + void setData(Session const& _s, std::string const& _sub, bytes const& _data); + bytes data(Session const& _s, std::string const& _subs) const; private: std::unordered_map, Reputation> m_nodes; ///< Nodes that were impolite while syncing. We avoid syncing from these if possible. diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 5be23b7c8..dbeec858e 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -488,7 +488,7 @@ string FunctionDefinition::externalSignature() const bool VariableDeclaration::isLValue() const { // External function parameters and constant declared variables are Read-Only - return !isExternalFunctionParameter() && !m_isConstant; + return !isExternalCallableParameter() && !m_isConstant; } void VariableDeclaration::checkTypeRequirements() @@ -516,39 +516,41 @@ void VariableDeclaration::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Assignment necessary for type detection.")); m_value->checkTypeRequirements(nullptr); - TypePointer type = m_value->getType(); - if (type->getCategory() == Type::Category::IntegerConstant) - { - auto intType = dynamic_pointer_cast(type)->getIntegerType(); - if (!intType) - BOOST_THROW_EXCEPTION(m_value->createTypeError("Invalid integer constant " + type->toString() + ".")); - type = intType; - } + TypePointer const& type = m_value->getType(); + if ( + type->getCategory() == Type::Category::IntegerConstant && + !dynamic_pointer_cast(type)->getIntegerType() + ) + BOOST_THROW_EXCEPTION(m_value->createTypeError("Invalid integer constant " + type->toString() + ".")); else if (type->getCategory() == Type::Category::Void) BOOST_THROW_EXCEPTION(createTypeError("Variable cannot have void type.")); - m_type = type; + m_type = type->mobileType(); } if (m_isStateVariable && getVisibility() >= Visibility::Public && !FunctionType(*this).externalType()) BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables.")); } -bool VariableDeclaration::isFunctionParameter() const +bool VariableDeclaration::isCallableParameter() const { - auto const* function = dynamic_cast(getScope()); - if (!function) + auto const* callable = dynamic_cast(getScope()); + if (!callable) return false; - for (auto const& variable: function->getParameters() + function->getReturnParameters()) + for (auto const& variable: callable->getParameters()) if (variable.get() == this) return true; + if (callable->getReturnParameterList()) + for (auto const& variable: callable->getReturnParameterList()->getParameters()) + if (variable.get() == this) + return true; return false; } -bool VariableDeclaration::isExternalFunctionParameter() const +bool VariableDeclaration::isExternalCallableParameter() const { - auto const* function = dynamic_cast(getScope()); - if (!function || function->getVisibility() != Declaration::Visibility::External) + auto const* callable = dynamic_cast(getScope()); + if (!callable || callable->getVisibility() != Declaration::Visibility::External) return false; - for (auto const& variable: function->getParameters()) + for (auto const& variable: callable->getParameters()) if (variable.get() == this) return true; return false; diff --git a/libsolidity/AST.h b/libsolidity/AST.h index b3984840f..ff0d708f5 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -406,13 +406,43 @@ private: std::vector> m_parameters; }; -class FunctionDefinition: public Declaration, public VariableScope, public Documented, public ImplementationOptional +/** + * Base class for all nodes that define function-like objects, i.e. FunctionDefinition, + * EventDefinition and ModifierDefinition. + */ +class CallableDeclaration: public Declaration, public VariableScope +{ +public: + CallableDeclaration( + SourceLocation const& _location, + ASTPointer const& _name, + Declaration::Visibility _visibility, + ASTPointer const& _parameters, + ASTPointer const& _returnParameters = ASTPointer() + ): + Declaration(_location, _name, _visibility), + m_parameters(_parameters), + m_returnParameters(_returnParameters) + { + } + + std::vector> const& getParameters() const { return m_parameters->getParameters(); } + ParameterList const& getParameterList() const { return *m_parameters; } + ASTPointer const& getReturnParameterList() const { return m_returnParameters; } + +protected: + ASTPointer m_parameters; + ASTPointer m_returnParameters; +}; + +class FunctionDefinition: public CallableDeclaration, public Documented, public ImplementationOptional { public: FunctionDefinition( SourceLocation const& _location, ASTPointer const& _name, - Declaration::Visibility _visibility, bool _isConstructor, + Declaration::Visibility _visibility, + bool _isConstructor, ASTPointer const& _documentation, ASTPointer const& _parameters, bool _isDeclaredConst, @@ -420,14 +450,12 @@ public: ASTPointer const& _returnParameters, ASTPointer const& _body ): - Declaration(_location, _name, _visibility), + CallableDeclaration(_location, _name, _visibility, _parameters, _returnParameters), Documented(_documentation), ImplementationOptional(_body != nullptr), m_isConstructor(_isConstructor), - m_parameters(_parameters), m_isDeclaredConst(_isDeclaredConst), m_functionModifiers(_modifiers), - m_returnParameters(_returnParameters), m_body(_body) {} @@ -437,10 +465,7 @@ public: bool isConstructor() const { return m_isConstructor; } bool isDeclaredConst() const { return m_isDeclaredConst; } std::vector> const& getModifiers() const { return m_functionModifiers; } - std::vector> const& getParameters() const { return m_parameters->getParameters(); } - ParameterList const& getParameterList() const { return *m_parameters; } std::vector> const& getReturnParameters() const { return m_returnParameters->getParameters(); } - ASTPointer const& getReturnParameterList() const { return m_returnParameters; } Block const& getBody() const { return *m_body; } virtual bool isVisibleInContract() const override @@ -460,10 +485,8 @@ public: private: bool m_isConstructor; - ASTPointer m_parameters; bool m_isDeclaredConst; std::vector> m_functionModifiers; - ASTPointer m_returnParameters; ASTPointer m_body; }; @@ -512,9 +535,9 @@ public: void checkTypeRequirements(); bool isLocalVariable() const { return !!dynamic_cast(getScope()); } /// @returns true if this variable is a parameter or return parameter of a function. - bool isFunctionParameter() const; + bool isCallableParameter() const; /// @returns true if this variable is a parameter (not return parameter) of an external function. - bool isExternalFunctionParameter() const; + bool isExternalCallableParameter() const; bool isStateVariable() const { return m_isStateVariable; } bool isIndexed() const { return m_isIndexed; } bool isConstant() const { return m_isConstant; } @@ -537,22 +560,25 @@ private: /** * Definition of a function modifier. */ -class ModifierDefinition: public Declaration, public VariableScope, public Documented +class ModifierDefinition: public CallableDeclaration, public Documented { public: - ModifierDefinition(SourceLocation const& _location, - ASTPointer const& _name, - ASTPointer const& _documentation, - ASTPointer const& _parameters, - ASTPointer const& _body): - Declaration(_location, _name), Documented(_documentation), - m_parameters(_parameters), m_body(_body) {} + ModifierDefinition( + SourceLocation const& _location, + ASTPointer const& _name, + ASTPointer const& _documentation, + ASTPointer const& _parameters, + ASTPointer const& _body + ): + CallableDeclaration(_location, _name, Visibility::Default, _parameters), + Documented(_documentation), + m_body(_body) + { + } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - std::vector> const& getParameters() const { return m_parameters->getParameters(); } - ParameterList const& getParameterList() const { return *m_parameters; } Block const& getBody() const { return *m_body; } virtual TypePointer getType(ContractDefinition const* = nullptr) const override; @@ -560,7 +586,6 @@ public: void checkTypeRequirements(); private: - ASTPointer m_parameters; ASTPointer m_body; }; @@ -591,7 +616,7 @@ private: /** * Definition of a (loggable) event. */ -class EventDefinition: public Declaration, public VariableScope, public Documented +class EventDefinition: public CallableDeclaration, public Documented { public: EventDefinition( @@ -601,16 +626,15 @@ public: ASTPointer const& _parameters, bool _anonymous = false ): - Declaration(_location, _name), + CallableDeclaration(_location, _name, Visibility::Default, _parameters), Documented(_documentation), - m_parameters(_parameters), - m_anonymous(_anonymous){} + m_anonymous(_anonymous) + { + } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - std::vector> const& getParameters() const { return m_parameters->getParameters(); } - ParameterList const& getParameterList() const { return *m_parameters; } bool isAnonymous() const { return m_anonymous; } virtual TypePointer getType(ContractDefinition const* = nullptr) const override @@ -621,7 +645,6 @@ public: void checkTypeRequirements(); private: - ASTPointer m_parameters; bool m_anonymous = false; }; diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index b2b8bfa4c..b55ae7d36 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -170,11 +170,17 @@ void Compiler::appendConstructor(FunctionDefinition const& _constructor) if (argumentSize > 0) { - m_context << u256(argumentSize); + CompilerUtils(m_context).fetchFreeMemoryPointer(); + m_context << u256(argumentSize) << eth::Instruction::DUP1; m_context.appendProgramSize(); - m_context << u256(CompilerUtils::dataStartOffset); // copy it to byte four as expected for ABI calls - m_context << eth::Instruction::CODECOPY; - appendCalldataUnpacker(FunctionType(_constructor).getParameterTypes(), true); + m_context << eth::Instruction::DUP4 << eth::Instruction::CODECOPY; + m_context << eth::Instruction::ADD; + CompilerUtils(m_context).storeFreeMemoryPointer(); + appendCalldataUnpacker( + FunctionType(_constructor).getParameterTypes(), + true, + CompilerUtils::freeMemoryPointer + 0x20 + ); } _constructor.accept(*this); } @@ -232,26 +238,35 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) } } -void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory) +void Compiler::appendCalldataUnpacker( + TypePointers const& _typeParameters, + bool _fromMemory, + u256 _startOffset +) { // We do not check the calldata size, everything is zero-paddedd - m_context << u256(CompilerUtils::dataStartOffset); + if (_startOffset == u256(-1)) + _startOffset = u256(CompilerUtils::dataStartOffset); + + m_context << _startOffset; for (TypePointer const& type: _typeParameters) { - if (type->getCategory() == Type::Category::Array) + switch (type->getCategory()) + { + case Type::Category::Array: { auto const& arrayType = dynamic_cast(*type); if (arrayType.location() == ReferenceType::Location::CallData) { + solAssert(!_fromMemory, ""); if (type->isDynamicallySized()) { // put on stack: data_pointer length CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory); // stack: data_offset next_pointer //@todo once we support nested arrays, this offset needs to be dynamic. - m_context << eth::Instruction::SWAP1 << u256(CompilerUtils::dataStartOffset); - m_context << eth::Instruction::ADD; + m_context << eth::Instruction::SWAP1 << _startOffset << eth::Instruction::ADD; // stack: next_pointer data_pointer // retrieve length CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true); @@ -268,13 +283,15 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool else { solAssert(arrayType.location() == ReferenceType::Location::Memory, ""); - CompilerUtils(m_context).fetchFreeMemoryPointer(); - CompilerUtils(m_context).storeInMemoryDynamic(*type); - CompilerUtils(m_context).storeFreeMemoryPointer(); + // compute data pointer + m_context << eth::Instruction::DUP1 << _startOffset << eth::Instruction::ADD; + if (!_fromMemory) + solAssert(false, "Not yet implemented."); + m_context << eth::Instruction::SWAP1 << u256(0x20) << eth::Instruction::ADD; } + break; } - else - { + default: solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true); } diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index 670c74673..60ca00e83 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -73,7 +73,12 @@ private: void appendFunctionSelector(ContractDefinition const& _contract); /// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers. /// From memory if @a _fromMemory is true, otherwise from call data. - void appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false); + /// Expects source offset on the stack. + void appendCalldataUnpacker( + TypePointers const& _typeParameters, + bool _fromMemory = false, + u256 _startOffset = u256(-1) + ); void appendReturnValuePacker(TypePointers const& _typeParameters); void registerStateVariables(ContractDefinition const& _contract); diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index d9b6da14e..811ee60ec 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -204,7 +204,7 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con } else if (targetTypeCategory == Type::Category::Enum) // just clean - appendTypeConversion(_typeOnStack, *_typeOnStack.getRealType(), true); + appendTypeConversion(_typeOnStack, *_typeOnStack.mobileType(), true); else { solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, ""); @@ -232,6 +232,25 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con } } break; + case Type::Category::Array: + //@TODO + break; + case Type::Category::Struct: + { + solAssert(targetTypeCategory == stackTypeCategory, ""); + auto& targetType = dynamic_cast(_targetType); + auto& stackType = dynamic_cast(_typeOnStack); + solAssert( + targetType.location() == ReferenceType::Location::Storage && + stackType.location() == ReferenceType::Location::Storage, + "Non-storage structs not yet implemented." + ); + solAssert( + targetType.isPointer(), + "Type conversion to non-pointer struct requested." + ); + break; + } default: // All other types should not be convertible to non-equal types. solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); @@ -771,7 +790,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) TypeType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); solAssert( !type.getMembers().membersByName(_memberAccess.getMemberName()).empty(), - "Invalid member access to " + type.toString() + "Invalid member access to " + type.toString(false) ); if (dynamic_cast(type.getActualType().get())) @@ -1101,7 +1120,7 @@ void ExpressionCompiler::appendExternalFunctionCall( bool manualFunctionId = (funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode) && !_arguments.empty() && - _arguments.front()->getType()->getRealType()->getCalldataEncodedSize(false) == + _arguments.front()->getType()->mobileType()->getCalldataEncodedSize(false) == CompilerUtils::dataStartOffset; if (manualFunctionId) { @@ -1225,7 +1244,7 @@ void ExpressionCompiler::encodeToMemory( TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes; solAssert(targetTypes.size() == _givenTypes.size(), ""); for (TypePointer& t: targetTypes) - t = t->getRealType()->externalType(); + t = t->mobileType()->externalType(); // Stack during operation: // ... ... @@ -1325,7 +1344,7 @@ void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, appendTypeMoveToMemory(_expectedType); } else - appendTypeMoveToMemory(*_expression.getType()->getRealType()); + appendTypeMoveToMemory(*_expression.getType()->mobileType()); } void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression) diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index a9d54cdc6..23b7ff82f 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -96,7 +96,7 @@ unique_ptr InterfaceHandler::getABIInterface(ContractDefinition const& _ { Json::Value input; input["name"] = p->getName(); - input["type"] = p->getType()->toString(); + input["type"] = p->getType()->toString(true); input["indexed"] = p->isIndexed(); params.append(input); } diff --git a/libsolidity/LValue.cpp b/libsolidity/LValue.cpp index b684e55a1..1acf0a3e8 100644 --- a/libsolidity/LValue.cpp +++ b/libsolidity/LValue.cpp @@ -198,7 +198,11 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc // stack layout: source_ref source_offset target_ref target_offset // note that we have structs, so offsets should be zero and are ignored auto const& structType = dynamic_cast(m_dataType); - solAssert(structType == _sourceType, "Struct assignment with conversion."); + solAssert( + structType.structDefinition() == + dynamic_cast(_sourceType).structDefinition(), + "Struct assignment with conversion." + ); for (auto const& member: structType.getMembers()) { // assign each member that is not a mapping diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 22232014f..e60797967 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -431,7 +431,7 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable) // They default to memory for function parameters and storage for local variables. if (auto ref = dynamic_cast(type.get())) { - if (_variable.isExternalFunctionParameter()) + if (_variable.isExternalCallableParameter()) { // force location of external function parameters (not return) to calldata if (loc != Location::Default) @@ -439,9 +439,9 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable) "Location has to be calldata for external functions " "(remove the \"memory\" or \"storage\" keyword)." )); - type = ref->copyForLocation(ReferenceType::Location::CallData); + type = ref->copyForLocation(ReferenceType::Location::CallData, true); } - else if (_variable.isFunctionParameter() && _variable.getScope()->isPublic()) + else if (_variable.isCallableParameter() && _variable.getScope()->isPublic()) { // force locations of public or external function (return) parameters to memory if (loc == VariableDeclaration::Location::Storage) @@ -449,16 +449,18 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable) "Location has to be memory for publicly visible functions " "(remove the \"storage\" keyword)." )); - type = ref->copyForLocation(ReferenceType::Location::Memory); + type = ref->copyForLocation(ReferenceType::Location::Memory, true); } else { if (loc == Location::Default) - loc = _variable.isFunctionParameter() ? Location::Memory : Location::Storage; + loc = _variable.isCallableParameter() ? Location::Memory : Location::Storage; + bool isPointer = !_variable.isStateVariable(); type = ref->copyForLocation( loc == Location::Memory ? ReferenceType::Location::Memory : - ReferenceType::Location::Storage + ReferenceType::Location::Storage, + isPointer ); } } diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 6f16f5193..bedd2e7b0 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -179,6 +179,8 @@ TypePointer Type::fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType TypePointer valueType = _valueType.toType(); if (!valueType) BOOST_THROW_EXCEPTION(_valueType.createTypeError("Invalid type name.")); + // Convert value type to storage reference. + valueType = ReferenceType::copyForLocationIfReference(ReferenceType::Location::Storage, valueType); return make_shared(keyType, valueType); } @@ -288,7 +290,7 @@ bool IntegerType::operator==(Type const& _other) const return other.m_bits == m_bits && other.m_modifier == m_modifier; } -string IntegerType::toString() const +string IntegerType::toString(bool) const { if (isAddress()) return "address"; @@ -488,7 +490,7 @@ bool IntegerConstantType::operator==(Type const& _other) const return m_value == dynamic_cast(_other).m_value; } -string IntegerConstantType::toString() const +string IntegerConstantType::toString(bool) const { return "int_const " + m_value.str(); } @@ -508,10 +510,10 @@ u256 IntegerConstantType::literalValue(Literal const*) const return value; } -TypePointer IntegerConstantType::getRealType() const +TypePointer IntegerConstantType::mobileType() const { auto intType = getIntegerType(); - solAssert(!!intType, "getRealType called with invalid integer constant " + toString()); + solAssert(!!intType, "mobileType called with invalid integer constant " + toString(false)); return intType; } @@ -668,21 +670,67 @@ TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const return _operator == Token::Delete ? make_shared() : TypePointer(); } +TypePointer ReferenceType::copyForLocationIfReference(Location _location, TypePointer const& _type) +{ + if (auto type = dynamic_cast(_type.get())) + return type->copyForLocation(_location, false); + return _type; +} + +TypePointer ReferenceType::copyForLocationIfReference(TypePointer const& _type) const +{ + return copyForLocationIfReference(m_location, _type); +} + +string ReferenceType::stringForReferencePart() const +{ + switch (m_location) + { + case Location::Storage: + return string("storage ") + (m_isPointer ? "pointer" : "ref"); + case Location::CallData: + return "calldata"; + case Location::Memory: + return "memory"; + } + solAssert(false, ""); + return ""; +} + bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const { if (_convertTo.getCategory() != getCategory()) return false; auto& convertTo = dynamic_cast(_convertTo); - // let us not allow assignment to memory arrays for now - if (convertTo.location() != Location::Storage) - return false; if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString()) return false; - if (!getBaseType()->isImplicitlyConvertibleTo(*convertTo.getBaseType())) + // memory/calldata to storage can be converted, but only to a direct storage reference + if (convertTo.location() == Location::Storage && location() != Location::Storage && convertTo.isPointer()) return false; - if (convertTo.isDynamicallySized()) + if (convertTo.location() == Location::CallData && location() != convertTo.location()) + return false; + if (convertTo.location() == Location::Storage && !convertTo.isPointer()) + { + // Less restrictive conversion, since we need to copy anyway. + if (!getBaseType()->isImplicitlyConvertibleTo(*convertTo.getBaseType())) + return false; + if (convertTo.isDynamicallySized()) + return true; + return !isDynamicallySized() && convertTo.getLength() >= getLength(); + } + else + { + // Require that the base type is the same, not only convertible. + // This disallows assignment of nested arrays from storage to memory for now. + if (*getBaseType() != *convertTo.getBaseType()) + return false; + if (isDynamicallySized() != convertTo.isDynamicallySized()) + return false; + // We also require that the size is the same. + if (!isDynamicallySized() && getLength() != convertTo.getLength()) + return false; return true; - return !isDynamicallySized() && convertTo.getLength() >= getLength(); + } } TypePointer ArrayType::unaryOperatorResult(Token::Value _operator) const @@ -698,7 +746,7 @@ bool ArrayType::operator==(Type const& _other) const return false; ArrayType const& other = dynamic_cast(_other); if ( - other.m_location != m_location || + !ReferenceType::operator==(other) || other.isByteArray() != isByteArray() || other.isString() != isString() || other.isDynamicallySized() != isDynamicallySized() @@ -751,16 +799,23 @@ unsigned ArrayType::getSizeOnStack() const return 1; } -string ArrayType::toString() const +string ArrayType::toString(bool _short) const { + string ret; if (isString()) - return "string"; + ret = "string"; else if (isByteArray()) - return "bytes"; - string ret = getBaseType()->toString() + "["; - if (!isDynamicallySized()) - ret += getLength().str(); - return ret + "]"; + ret = "bytes"; + else + { + ret = getBaseType()->toString(_short) + "["; + if (!isDynamicallySized()) + ret += getLength().str(); + ret += "]"; + } + if (!_short) + ret += " " + stringForReferencePart(); + return ret; } TypePointer ArrayType::externalType() const @@ -778,14 +833,12 @@ TypePointer ArrayType::externalType() const return std::make_shared(Location::CallData, m_baseType->externalType(), m_length); } -TypePointer ArrayType::copyForLocation(ReferenceType::Location _location) const +TypePointer ArrayType::copyForLocation(ReferenceType::Location _location, bool _isPointer) const { auto copy = make_shared(_location); + copy->m_isPointer = _isPointer; copy->m_arrayKind = m_arrayKind; - if (auto ref = dynamic_cast(m_baseType.get())) - copy->m_baseType = ref->copyForLocation(_location); - else - copy->m_baseType = m_baseType; + copy->m_baseType = copy->copyForLocationIfReference(m_baseType); copy->m_hasDynamicLength = m_hasDynamicLength; copy->m_length = m_length; return copy; @@ -801,7 +854,7 @@ bool ContractType::operator==(Type const& _other) const return other.m_contract == m_contract && other.m_super == m_super; } -string ContractType::toString() const +string ContractType::toString(bool) const { return "contract " + string(m_super ? "super " : "") + m_contract.getName(); } @@ -890,6 +943,19 @@ vector> ContractType::getState return variablesAndOffsets; } +bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const +{ + if (_convertTo.getCategory() != getCategory()) + return false; + auto& convertTo = dynamic_cast(_convertTo); + // memory/calldata to storage can be converted, but only to a direct storage reference + if (convertTo.location() == Location::Storage && location() != Location::Storage && convertTo.isPointer()) + return false; + if (convertTo.location() == Location::CallData && location() != convertTo.location()) + return false; + return this->m_struct == convertTo.m_struct; +} + TypePointer StructType::unaryOperatorResult(Token::Value _operator) const { return _operator == Token::Delete ? make_shared() : TypePointer(); @@ -900,7 +966,7 @@ bool StructType::operator==(Type const& _other) const if (_other.getCategory() != getCategory()) return false; StructType const& other = dynamic_cast(_other); - return other.m_struct == m_struct; + return ReferenceType::operator==(other) && other.m_struct == m_struct; } u256 StructType::getStorageSize() const @@ -916,9 +982,12 @@ bool StructType::canLiveOutsideStorage() const return true; } -string StructType::toString() const +string StructType::toString(bool _short) const { - return string("struct ") + m_struct.getName(); + string ret = "struct " + m_struct.getName(); + if (!_short) + ret += " " + stringForReferencePart(); + return ret; } MemberList const& StructType::getMembers() const @@ -928,16 +997,23 @@ MemberList const& StructType::getMembers() const { MemberList::MemberMap members; for (ASTPointer const& variable: m_struct.getMembers()) - members.push_back(MemberList::Member(variable->getName(), variable->getType(), variable.get())); + { + members.push_back(MemberList::Member( + variable->getName(), + copyForLocationIfReference(variable->getType()), + variable.get()) + ); + } m_members.reset(new MemberList(members)); } return *m_members; } -TypePointer StructType::copyForLocation(ReferenceType::Location _location) const +TypePointer StructType::copyForLocation(ReferenceType::Location _location, bool _isPointer) const { auto copy = make_shared(m_struct); copy->m_location = _location; + copy->m_isPointer = _isPointer; return copy; } @@ -970,7 +1046,7 @@ unsigned EnumType::getStorageBytes() const return dev::bytesRequired(elements - 1); } -string EnumType::toString() const +string EnumType::toString(bool) const { return string("enum ") + m_enum.getName(); } @@ -1114,14 +1190,14 @@ bool FunctionType::operator==(Type const& _other) const return true; } -string FunctionType::toString() const +string FunctionType::toString(bool _short) const { string name = "function ("; for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it) - name += (*it)->toString() + (it + 1 == m_parameterTypes.end() ? "" : ","); + name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ","); name += ") returns ("; for (auto it = m_returnParameterTypes.begin(); it != m_returnParameterTypes.end(); ++it) - name += (*it)->toString() + (it + 1 == m_returnParameterTypes.end() ? "" : ","); + name += (*it)->toString(_short) + (it + 1 == m_returnParameterTypes.end() ? "" : ","); return name + ")"; } @@ -1289,7 +1365,7 @@ string FunctionType::externalSignature(std::string const& _name) const for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it) { solAssert(!!(*it), "Parameter should have external type"); - ret += (*it)->toString() + (it + 1 == externalParameterTypes.cend() ? "" : ","); + ret += (*it)->toString(true) + (it + 1 == externalParameterTypes.cend() ? "" : ","); } return ret + ")"; @@ -1327,7 +1403,7 @@ vector const FunctionType::getParameterTypeNames() const { vector names; for (TypePointer const& t: m_parameterTypes) - names.push_back(t->toString()); + names.push_back(t->toString(true)); return names; } @@ -1336,7 +1412,7 @@ vector const FunctionType::getReturnParameterTypeNames() const { vector names; for (TypePointer const& t: m_returnParameterTypes) - names.push_back(t->toString()); + names.push_back(t->toString(true)); return names; } @@ -1358,9 +1434,9 @@ bool MappingType::operator==(Type const& _other) const return *other.m_keyType == *m_keyType && *other.m_valueType == *m_valueType; } -string MappingType::toString() const +string MappingType::toString(bool _short) const { - return "mapping(" + getKeyType()->toString() + " => " + getValueType()->toString() + ")"; + return "mapping(" + getKeyType()->toString(_short) + " => " + getValueType()->toString(_short) + ")"; } u256 VoidType::getStorageSize() const @@ -1445,11 +1521,11 @@ bool ModifierType::operator==(Type const& _other) const return true; } -string ModifierType::toString() const +string ModifierType::toString(bool _short) const { string name = "modifier ("; for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it) - name += (*it)->toString() + (it + 1 == m_parameterTypes.end() ? "" : ","); + name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ","); return name + ")"; } @@ -1496,7 +1572,7 @@ bool MagicType::operator==(Type const& _other) const return other.m_kind == m_kind; } -string MagicType::toString() const +string MagicType::toString(bool) const { switch (m_kind) { diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 17d30ea6c..0f86ac95f 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -198,19 +198,24 @@ public: /// i.e. it behaves differently in lvalue context and in value context. virtual bool isValueType() const { return false; } virtual unsigned getSizeOnStack() const { return 1; } - /// @returns the real type of some types, like e.g: IntegerConstant - virtual TypePointer getRealType() const { return shared_from_this(); } + /// @returns the mobile (in contrast to static) type corresponding to the given type. + /// This returns the corresponding integer type for IntegerConstantTypes and the pointer type + /// for storage reference types. + virtual TypePointer mobileType() const { return shared_from_this(); } /// Returns the list of all members of this type. Default implementation: no members. virtual MemberList const& getMembers() const { return EmptyMemberList; } /// Convenience method, returns the type of the given named member or an empty pointer if no such member exists. TypePointer getMemberType(std::string const& _name) const { return getMembers().getMemberType(_name); } - virtual std::string toString() const = 0; + virtual std::string toString(bool _short) const = 0; + std::string toString() const { return toString(false); } virtual u256 literalValue(Literal const*) const { - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Literal value requested " - "for type without literals.")); + BOOST_THROW_EXCEPTION( + InternalCompilerError() << + errinfo_comment("Literal value requested for type without literals.") + ); } /// @returns a type suitable for outside of Solidity, i.e. for contract types it returns address. @@ -249,7 +254,7 @@ public: virtual MemberList const& getMembers() const override { return isAddress() ? AddressMemberList : EmptyMemberList; } - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; virtual TypePointer externalType() const override { return shared_from_this(); } @@ -287,9 +292,9 @@ public: virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned getSizeOnStack() const override { return 1; } - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; virtual u256 literalValue(Literal const* _literal) const override; - virtual TypePointer getRealType() const override; + virtual TypePointer mobileType() const override; /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. std::shared_ptr getIntegerType() const; @@ -322,7 +327,7 @@ public: virtual unsigned getStorageBytes() const override { return m_bytes; } virtual bool isValueType() const override { return true; } - virtual std::string toString() const override { return "bytes" + dev::toString(m_bytes); } + virtual std::string toString(bool) const override { return "bytes" + dev::toString(m_bytes); } virtual u256 literalValue(Literal const* _literal) const override; virtual TypePointer externalType() const override { return shared_from_this(); } @@ -348,27 +353,51 @@ public: virtual unsigned getStorageBytes() const override { return 1; } virtual bool isValueType() const override { return true; } - virtual std::string toString() const override { return "bool"; } + virtual std::string toString(bool) const override { return "bool"; } virtual u256 literalValue(Literal const* _literal) const override; virtual TypePointer externalType() const override { return shared_from_this(); } }; /** - * Trait used by types which are not value types and can be stored either in storage, memory + * Base class used by types which are not value types and can be stored either in storage, memory * or calldata. This is currently used by arrays and structs. */ -class ReferenceType +class ReferenceType: public Type { public: enum class Location { Storage, CallData, Memory }; explicit ReferenceType(Location _location): m_location(_location) {} Location location() const { return m_location; } - /// @returns a copy of this type with location (recursively) changed to @a _location. - virtual TypePointer copyForLocation(Location _location) const = 0; + /// @returns a copy of this type with location (recursively) changed to @a _location, + /// whereas isPointer is only shallowly changed - the deep copy is always a bound reference. + virtual TypePointer copyForLocation(Location _location, bool _isPointer) const = 0; + + virtual TypePointer mobileType() const override { return copyForLocation(m_location, true); } + + /// Storage references can be pointers or bound references. In general, local variables are of + /// pointer type, state variables are bound references. Assignments to pointers or deleting + /// them will not modify storage (that will only change the pointer). Assignment from + /// non-storage objects to a variable of storage pointer type is not possible. + bool isPointer() const { return m_isPointer; } + + bool operator==(ReferenceType const& _other) const + { + return location() == _other.location() && isPointer() == _other.isPointer(); + } + + /// @returns a copy of @a _type having the same location as this (and is not a pointer type) + /// if _type is a reference type and an unmodified copy of _type otherwise. + /// This function is mostly useful to modify inner types appropriately. + static TypePointer copyForLocationIfReference(Location _location, TypePointer const& _type); protected: + TypePointer copyForLocationIfReference(TypePointer const& _type) const; + /// @returns a human-readable description of the reference part of the type. + std::string stringForReferencePart() const; + Location m_location = Location::Storage; + bool m_isPointer = true; }; /** @@ -378,10 +407,9 @@ protected: * one slot). Dynamically sized arrays (including byte arrays) start with their size as a uint and * thus start on their own slot. */ -class ArrayType: public Type, public ReferenceType +class ArrayType: public ReferenceType { public: - virtual Category getCategory() const override { return Category::Array; } /// Constructor for a byte array ("bytes") and string. @@ -389,16 +417,18 @@ public: ReferenceType(_location), m_arrayKind(_isString ? ArrayKind::String : ArrayKind::Bytes), m_baseType(std::make_shared(1)) - {} + { + } /// Constructor for a dynamically sized array type ("type[]") - ArrayType(Location _location, const TypePointer &_baseType): + ArrayType(Location _location, TypePointer const& _baseType): ReferenceType(_location), - m_baseType(_baseType) - {} + m_baseType(copyForLocationIfReference(_baseType)) + { + } /// Constructor for a fixed-size array type ("type[20]") - ArrayType(Location _location, const TypePointer &_baseType, u256 const& _length): + ArrayType(Location _location, TypePointer const& _baseType, u256 const& _length): ReferenceType(_location), - m_baseType(_baseType), + m_baseType(copyForLocationIfReference(_baseType)), m_hasDynamicLength(false), m_length(_length) {} @@ -410,7 +440,7 @@ public: virtual bool isDynamicallySized() const override { return m_hasDynamicLength; } virtual u256 getStorageSize() const override; virtual unsigned getSizeOnStack() const override; - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; virtual MemberList const& getMembers() const override { return isString() ? EmptyMemberList : s_arrayTypeMemberList; @@ -424,7 +454,7 @@ public: TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;} u256 const& getLength() const { return m_length; } - TypePointer copyForLocation(Location _location) const override; + TypePointer copyForLocation(Location _location, bool _isPointer) const override; private: /// String is interpreted as a subtype of Bytes. @@ -460,7 +490,7 @@ public: virtual unsigned getStorageBytes() const override { return 20; } virtual bool canLiveOutsideStorage() const override { return true; } virtual bool isValueType() const override { return true; } - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; virtual MemberList const& getMembers() const override; virtual TypePointer externalType() const override @@ -497,26 +527,29 @@ private: /** * The type of a struct instance, there is one distinct type per struct definition. */ -class StructType: public Type, public ReferenceType +class StructType: public ReferenceType { public: virtual Category getCategory() const override { return Category::Struct; } explicit StructType(StructDefinition const& _struct): //@todo only storage until we have non-storage structs ReferenceType(Location::Storage), m_struct(_struct) {} + virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(Type const& _other) const override; virtual u256 getStorageSize() const override; virtual bool canLiveOutsideStorage() const override; virtual unsigned getSizeOnStack() const override { return 2; } - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; virtual MemberList const& getMembers() const override; - TypePointer copyForLocation(Location _location) const override; + TypePointer copyForLocation(Location _location, bool _isPointer) const override; std::pair const& getStorageOffsetsOfMember(std::string const& _name) const; + StructDefinition const& structDefinition() const { return m_struct; } + private: StructDefinition const& m_struct; /// List of member types, will be lazy-initialized because of recursive references. @@ -540,7 +573,7 @@ public: virtual unsigned getSizeOnStack() const override { return 1; } virtual unsigned getStorageBytes() const override; virtual bool canLiveOutsideStorage() const override { return true; } - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; virtual bool isValueType() const override { return true; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; @@ -649,7 +682,7 @@ public: std::vector const getReturnParameterTypeNames() const; virtual bool operator==(Type const& _other) const override; - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; virtual bool canBeStored() const override { return false; } virtual u256 getStorageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } @@ -721,7 +754,7 @@ public: m_keyType(_keyType), m_valueType(_valueType) {} virtual bool operator==(Type const& _other) const override; - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; virtual unsigned getSizeOnStack() const override { return 2; } virtual bool canLiveOutsideStorage() const override { return false; } @@ -744,7 +777,7 @@ public: VoidType() {} virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } - virtual std::string toString() const override { return "void"; } + virtual std::string toString(bool) const override { return "void"; } virtual bool canBeStored() const override { return false; } virtual u256 getStorageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } @@ -769,7 +802,7 @@ public: virtual u256 getStorageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned getSizeOnStack() const override { return 0; } - virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; } + virtual std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } virtual MemberList const& getMembers() const override; private: @@ -796,7 +829,7 @@ public: virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned getSizeOnStack() const override { return 0; } virtual bool operator==(Type const& _other) const override; - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; private: TypePointers m_parameterTypes; @@ -826,7 +859,7 @@ public: virtual unsigned getSizeOnStack() const override { return 0; } virtual MemberList const& getMembers() const override { return m_members; } - virtual std::string toString() const override; + virtual std::string toString(bool _short) const override; private: Kind m_kind; diff --git a/libtestutils/FixedClient.cpp b/libtestutils/FixedClient.cpp index 052141039..4237415ed 100644 --- a/libtestutils/FixedClient.cpp +++ b/libtestutils/FixedClient.cpp @@ -28,5 +28,7 @@ using namespace dev::test; eth::State FixedClient::asOf(h256 const& _h) const { ReadGuard l(x_stateDB); - return State(m_state.db(), bc(), _h); + State ret(m_state.db()); + ret.populateFromChain(bc(), _h); + return ret; } diff --git a/libweb3jsonrpc/JsonHelper.cpp b/libweb3jsonrpc/JsonHelper.cpp new file mode 100644 index 000000000..0b6cd4a1d --- /dev/null +++ b/libweb3jsonrpc/JsonHelper.cpp @@ -0,0 +1,427 @@ +/* + 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 JsonHelper.cpp + * @authors: + * Gav Wood + * @date 2014 + */ + +#include "JsonHelper.h" + +#include +#include +#include +#include +#include +#include +#include +using namespace std; +using namespace dev; +using namespace eth; + +namespace dev +{ + +Json::Value toJson(unordered_map const& _storage) +{ + Json::Value res(Json::objectValue); + for (auto i: _storage) + res[toJS(i.first)] = toJS(i.second); + return res; +} + +Json::Value toJson(map const& _storage) +{ + Json::Value res(Json::objectValue); + for (auto i: _storage) + res[toJS(i.first)] = toJS(i.second); + return res; +} + +// //////////////////////////////////////////////////////////////////////////////// +// p2p +// //////////////////////////////////////////////////////////////////////////////// +namespace p2p +{ + +Json::Value toJson(p2p::PeerSessionInfo const& _p) +{ + Json::Value ret; + ret["id"] = _p.id.hex(); + ret["clientVersion"] = _p.clientVersion; + ret["host"] = _p.host; + ret["port"] = _p.port; + ret["lastPing"] = (int)chrono::duration_cast(_p.lastPing).count(); + for (auto const& i: _p.notes) + ret["notes"][i.first] = i.second; + for (auto const& i: _p.caps) + ret["caps"][i.first] = (unsigned)i.second; + return ret; +} + +} + +// //////////////////////////////////////////////////////////////////////////////// +// eth +// //////////////////////////////////////////////////////////////////////////////// + +namespace eth +{ + +Json::Value toJson(dev::eth::BlockInfo const& _bi) +{ + Json::Value res; + if (_bi) + { + res["hash"] = toJS(_bi.hash()); + res["parentHash"] = toJS(_bi.parentHash); + res["sha3Uncles"] = toJS(_bi.sha3Uncles); + res["miner"] = toJS(_bi.coinbaseAddress); + res["stateRoot"] = toJS(_bi.stateRoot); + res["transactionsRoot"] = toJS(_bi.transactionsRoot); + res["difficulty"] = toJS(_bi.difficulty); + res["number"] = toJS(_bi.number); + res["gasUsed"] = toJS(_bi.gasUsed); + res["gasLimit"] = toJS(_bi.gasLimit); + res["timestamp"] = toJS(_bi.timestamp); + res["extraData"] = toJS(_bi.extraData); + res["nonce"] = toJS(_bi.nonce); + res["logsBloom"] = toJS(_bi.logBloom); + + res["seedHash"] = toJS(_bi.seedHash()); + res["target"] = toJS(_bi.boundary()); + } + return res; +} + +Json::Value toJson(dev::eth::Transaction const& _t, std::pair _location, BlockNumber _blockNumber) +{ + Json::Value res; + if (_t) + { + res["hash"] = toJS(_t.sha3()); + res["input"] = toJS(_t.data()); + res["to"] = _t.isCreation() ? Json::Value() : toJS(_t.receiveAddress()); + res["from"] = toJS(_t.safeSender()); + res["gas"] = toJS(_t.gas()); + res["gasPrice"] = toJS(_t.gasPrice()); + res["nonce"] = toJS(_t.nonce()); + res["value"] = toJS(_t.value()); + res["blockHash"] = toJS(_location.first); + res["transactionIndex"] = toJS(_location.second); + res["blockNumber"] = toJS(_blockNumber); + } + return res; +} + +Json::Value toJson(dev::eth::BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, Transactions const& _ts) +{ + Json::Value res = toJson(_bi); + if (_bi) + { + res["totalDifficulty"] = toJS(_bd.totalDifficulty); + res["uncles"] = Json::Value(Json::arrayValue); + for (h256 h: _us) + res["uncles"].append(toJS(h)); + res["transactions"] = Json::Value(Json::arrayValue); + for (unsigned i = 0; i < _ts.size(); i++) + res["transactions"].append(toJson(_ts[i], std::make_pair(_bi.hash(), i), (BlockNumber)_bi.number)); + } + return res; +} + +Json::Value toJson(dev::eth::BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, TransactionHashes const& _ts) +{ + Json::Value res = toJson(_bi); + if (_bi) + { + res["totalDifficulty"] = toJS(_bd.totalDifficulty); + res["uncles"] = Json::Value(Json::arrayValue); + for (h256 h: _us) + res["uncles"].append(toJS(h)); + res["transactions"] = Json::Value(Json::arrayValue); + for (h256 const& t: _ts) + res["transactions"].append(toJS(t)); + } + return res; +} + +Json::Value toJson(dev::eth::TransactionSkeleton const& _t) +{ + Json::Value res; + res["to"] = _t.creation ? Json::Value() : toJS(_t.to); + res["from"] = toJS(_t.from); + res["gas"] = toJS(_t.gas); + res["gasPrice"] = toJS(_t.gasPrice); + res["value"] = toJS(_t.value); + res["data"] = toJS(_t.data, 32); + return res; +} + +Json::Value toJson(dev::eth::TransactionReceipt const& _t) +{ + Json::Value res; + res["stateRoot"] = toJS(_t.stateRoot()); + res["gasUsed"] = toJS(_t.gasUsed()); + res["bloom"] = toJS(_t.bloom()); + res["log"] = dev::toJson(_t.log()); + return res; +} + +Json::Value toJson(dev::eth::Transaction const& _t) +{ + Json::Value res; + res["to"] = _t.isCreation() ? Json::Value() : toJS(_t.to()); + res["from"] = toJS(_t.from()); + res["gas"] = toJS(_t.gas()); + res["gasPrice"] = toJS(_t.gasPrice()); + res["value"] = toJS(_t.value()); + res["data"] = toJS(_t.data(), 32); + res["nonce"] = toJS(_t.nonce()); + res["hash"] = toJS(_t.sha3(WithSignature)); + res["sighash"] = toJS(_t.sha3(WithoutSignature)); + res["r"] = toJS(_t.signature().r); + res["s"] = toJS(_t.signature().s); + res["v"] = toJS(_t.signature().v); + return res; +} + +Json::Value toJson(dev::eth::LocalisedLogEntry const& _e) +{ + Json::Value res; + + if (_e.isSpecial) + res = toJS(_e.special); + else + { + res = toJson(static_cast(_e)); + if (_e.mined) + { + res["type"] = "mined"; + res["blockNumber"] = _e.blockNumber; + res["blockHash"] = toJS(_e.blockHash); + res["logIndex"] = _e.logIndex; + res["transactionHash"] = toJS(_e.transactionHash); + res["transactionIndex"] = _e.transactionIndex; + } + else + { + res["type"] = "pending"; + res["blockNumber"] = Json::Value(Json::nullValue); + res["blockHash"] = Json::Value(Json::nullValue); + res["logIndex"] = Json::Value(Json::nullValue); + res["transactionHash"] = Json::Value(Json::nullValue); + res["transactionIndex"] = Json::Value(Json::nullValue); + } + } + return res; +} + +Json::Value toJson(dev::eth::LogEntry const& _e) +{ + Json::Value res; + res["data"] = toJS(_e.data); + res["address"] = toJS(_e.address); + res["topics"] = Json::Value(Json::arrayValue); + for (auto const& t: _e.topics) + res["topics"].append(toJS(t)); + return res; +} + +TransactionSkeleton toTransactionSkeleton(Json::Value const& _json) +{ + TransactionSkeleton ret; + if (!_json.isObject() || _json.empty()) + return ret; + + if (!_json["from"].empty()) + ret.from = jsToAddress(_json["from"].asString()); + if (!_json["to"].empty() && _json["to"].asString() != "0x") + ret.to = jsToAddress(_json["to"].asString()); + else + ret.creation = true; + + if (!_json["value"].empty()) + ret.value = jsToU256(_json["value"].asString()); + + if (!_json["gas"].empty()) + ret.gas = jsToU256(_json["gas"].asString()); + + if (!_json["gasPrice"].empty()) + ret.gasPrice = jsToU256(_json["gasPrice"].asString()); + + if (!_json["data"].empty()) // ethereum.js has preconstructed the data array + ret.data = jsToBytes(_json["data"].asString()); + + if (!_json["code"].empty()) + ret.data = jsToBytes(_json["code"].asString()); + return ret; +} + +dev::eth::LogFilter toLogFilter(Json::Value const& _json) +{ + dev::eth::LogFilter filter; + if (!_json.isObject() || _json.empty()) + return filter; + + // check only !empty. it should throw exceptions if input params are incorrect + if (!_json["fromBlock"].empty()) + filter.withEarliest(jsToFixed<32>(_json["fromBlock"].asString())); + if (!_json["toBlock"].empty()) + filter.withLatest(jsToFixed<32>(_json["toBlock"].asString())); + if (!_json["address"].empty()) + { + if (_json["address"].isArray()) + for (auto i : _json["address"]) + filter.address(jsToAddress(i.asString())); + else + filter.address(jsToAddress(_json["address"].asString())); + } + if (!_json["topics"].empty()) + for (unsigned i = 0; i < _json["topics"].size(); i++) + { + if (_json["topics"][i].isArray()) + { + for (auto t: _json["topics"][i]) + if (!t.isNull()) + filter.topic(i, jsToFixed<32>(t.asString())); + } + else if (!_json["topics"][i].isNull()) // if it is anything else then string, it should and will fail + filter.topic(i, jsToFixed<32>(_json["topics"][i].asString())); + } + return filter; +} + +// TODO: this should be removed once we decide to remove backward compatibility with old log filters +dev::eth::LogFilter toLogFilter(Json::Value const& _json, Interface const& _client) // commented to avoid warning. Uncomment once in use @ PoC-7. +{ + dev::eth::LogFilter filter; + if (!_json.isObject() || _json.empty()) + return filter; + + // check only !empty. it should throw exceptions if input params are incorrect + if (!_json["fromBlock"].empty()) + filter.withEarliest(_client.hashFromNumber(jsToBlockNumber(_json["fromBlock"].asString()))); + if (!_json["toBlock"].empty()) + filter.withLatest(_client.hashFromNumber(jsToBlockNumber(_json["toBlock"].asString()))); + if (!_json["address"].empty()) + { + if (_json["address"].isArray()) + for (auto i : _json["address"]) + filter.address(jsToAddress(i.asString())); + else + filter.address(jsToAddress(_json["address"].asString())); + } + if (!_json["topics"].empty()) + for (unsigned i = 0; i < _json["topics"].size(); i++) + { + if (_json["topics"][i].isArray()) + { + for (auto t: _json["topics"][i]) + if (!t.isNull()) + filter.topic(i, jsToFixed<32>(t.asString())); + } + else if (!_json["topics"][i].isNull()) // if it is anything else then string, it should and will fail + filter.topic(i, jsToFixed<32>(_json["topics"][i].asString())); + } + return filter; +} + +} + +// //////////////////////////////////////////////////////////////////////////////////// +// shh +// //////////////////////////////////////////////////////////////////////////////////// + +namespace shh +{ + +Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message const& _m) +{ + Json::Value res; + res["hash"] = toJS(_h); + res["expiry"] = toJS(_e.expiry()); + res["sent"] = toJS(_e.sent()); + res["ttl"] = toJS(_e.ttl()); + res["workProved"] = toJS(_e.workProved()); + res["topics"] = Json::Value(Json::arrayValue); + for (auto const& t: _e.topic()) + res["topics"].append(toJS(t)); + res["payload"] = toJS(_m.payload()); + res["from"] = toJS(_m.from()); + res["to"] = toJS(_m.to()); + return res; +} + +shh::Message toMessage(Json::Value const& _json) +{ + shh::Message ret; + if (!_json["from"].empty()) + ret.setFrom(jsToPublic(_json["from"].asString())); + if (!_json["to"].empty()) + ret.setTo(jsToPublic(_json["to"].asString())); + if (!_json["payload"].empty()) + ret.setPayload(jsToBytes(_json["payload"].asString())); + return ret; +} + +shh::Envelope toSealed(Json::Value const& _json, shh::Message const& _m, Secret _from) +{ + unsigned ttl = 50; + unsigned workToProve = 50; + shh::BuildTopic bt; + + if (!_json["ttl"].empty()) + ttl = jsToInt(_json["ttl"].asString()); + + if (!_json["workToProve"].empty()) + workToProve = jsToInt(_json["workToProve"].asString()); + + if (!_json["topics"].empty()) + for (auto i: _json["topics"]) + { + if (i.isArray()) + { + for (auto j: i) + if (!j.isNull()) + bt.shift(jsToBytes(j.asString())); + } + else if (!i.isNull()) // if it is anything else then string, it should and will fail + bt.shift(jsToBytes(i.asString())); + } + + return _m.seal(_from, bt, ttl, workToProve); +} + +pair toWatch(Json::Value const& _json) +{ + shh::BuildTopic bt; + Public to; + + if (!_json["to"].empty()) + to = jsToPublic(_json["to"].asString()); + + if (!_json["topics"].empty()) + for (auto i: _json["topics"]) + bt.shift(jsToBytes(i.asString())); + + return make_pair(bt, to); +} + +} + +} diff --git a/libweb3jsonrpc/JsonHelper.h b/libweb3jsonrpc/JsonHelper.h new file mode 100644 index 000000000..d445ad0dc --- /dev/null +++ b/libweb3jsonrpc/JsonHelper.h @@ -0,0 +1,104 @@ +/* + 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 JsonHelper.h + * @authors: + * Gav Wood + * @date 2015 + */ +#pragma once + +#include +#include +#include +#include + +namespace dev +{ + +Json::Value toJson(std::map const& _storage); +Json::Value toJson(std::unordered_map const& _storage); + +namespace p2p +{ + +Json::Value toJson(PeerSessionInfo const& _p); + +} + +namespace eth +{ + +class Transaction; +class BlockDetails; +class Interface; +using Transactions = std::vector; +using UncleHashes = h256s; +using TransactionHashes = h256s; + +Json::Value toJson(BlockInfo const& _bi); +Json::Value toJson(Transaction const& _t, std::pair _location, BlockNumber _blockNumber); +Json::Value toJson(BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, Transactions const& _ts); +Json::Value toJson(BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, TransactionHashes const& _ts); +Json::Value toJson(TransactionSkeleton const& _t); +Json::Value toJson(Transaction const& _t); +Json::Value toJson(TransactionReceipt const& _t); +Json::Value toJson(LocalisedLogEntry const& _e); +Json::Value toJson(LogEntry const& _e); +TransactionSkeleton toTransactionSkeleton(Json::Value const& _json); +LogFilter toLogFilter(Json::Value const& _json); +LogFilter toLogFilter(Json::Value const& _json, Interface const& _client); // commented to avoid warning. Uncomment once in use @ PoC-7. + +} + +namespace shh +{ + +Json::Value toJson(h256 const& _h, Envelope const& _e, Message const& _m); +Message toMessage(Json::Value const& _json); +Envelope toSealed(Json::Value const& _json, Message const& _m, Secret _from); +std::pair toWatch(Json::Value const& _json); + +} + +template +Json::Value toJson(std::vector const& _es) +{ + Json::Value res(Json::arrayValue); + for (auto const& e: _es) + res.append(toJson(e)); + return res; +} + +template +Json::Value toJson(std::unordered_set const& _es) +{ + Json::Value res(Json::arrayValue); + for (auto const& e: _es) + res.append(toJson(e)); + return res; +} + +template +Json::Value toJson(std::set const& _es) +{ + Json::Value res(Json::arrayValue); + for (auto const& e: _es) + res.append(toJson(e)); + return res; +} + +} diff --git a/libweb3jsonrpc/WebThreeStubServer.cpp b/libweb3jsonrpc/WebThreeStubServer.cpp index 1bef71b59..adef51033 100644 --- a/libweb3jsonrpc/WebThreeStubServer.cpp +++ b/libweb3jsonrpc/WebThreeStubServer.cpp @@ -21,21 +21,39 @@ * @date 2014 */ +#include "WebThreeStubServer.h" // Make sure boost/asio.hpp is included before windows.h. #include #include #include +#include #include +#include #include -#include "WebThreeStubServer.h" +#include "JsonHelper.h" using namespace std; using namespace dev; using namespace dev::eth; -WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, shared_ptr const& _ethAccounts, std::vector const& _shhAccounts, KeyManager& _keyMan): +bool isHex(std::string const& _s) +{ + unsigned i = (_s.size() >= 2 && _s.substr(0, 2) == "0x") ? 2 : 0; + for (; i < _s.size(); ++i) + if (fromHex(_s[i], WhenError::DontThrow) == -1) + return false; + return true; +} + +template bool isHash(std::string const& _hash) +{ + return (_hash.size() == T::size * 2 || (_hash.size() == T::size * 2 + 2 && _hash.substr(0, 2) == "0x")) && isHex(_hash); +} + +WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, shared_ptr const& _ethAccounts, std::vector const& _shhAccounts, KeyManager& _keyMan, dev::eth::TrivialGasPricer& _gp): WebThreeStubServerBase(_conn, _ethAccounts, _shhAccounts), m_web3(_web3), - m_keyMan(_keyMan) + m_keyMan(_keyMan), + m_gp(_gp) { auto path = getDataDir() + "/.web3"; boost::filesystem::create_directories(path); @@ -57,23 +75,231 @@ bool WebThreeStubServer::eth_notePassword(string const& _password) return true; } +#define ADMIN requires(_session, Priviledge::Admin) + Json::Value WebThreeStubServer::admin_eth_blockQueueStatus(string const& _session) { + ADMIN; Json::Value ret; - if (isAdmin(_session)) + BlockQueueStatus bqs = m_web3.ethereum()->blockQueue().status(); + ret["importing"] = (int)bqs.importing; + ret["verified"] = (int)bqs.verified; + ret["verifying"] = (int)bqs.verifying; + ret["unverified"] = (int)bqs.unverified; + ret["future"] = (int)bqs.future; + ret["unknown"] = (int)bqs.unknown; + ret["bad"] = (int)bqs.bad; + return ret; +} + +bool WebThreeStubServer::admin_eth_setAskPrice(std::string const& _wei, std::string const& _session) +{ + ADMIN; + m_gp.setAsk(jsToU256(_wei)); + return true; +} + +bool WebThreeStubServer::admin_eth_setBidPrice(std::string const& _wei, std::string const& _session) +{ + ADMIN; + m_gp.setBid(jsToU256(_wei)); + return true; +} + +dev::eth::CanonBlockChain const& WebThreeStubServer::bc() const +{ + return m_web3.ethereum()->blockChain(); +} + +dev::eth::BlockQueue const& WebThreeStubServer::bq() const +{ + return m_web3.ethereum()->blockQueue(); +} + +Json::Value WebThreeStubServer::admin_eth_findBlock(std::string const& _blockHash, std::string const& _session) +{ + ADMIN; + h256 h(_blockHash); + if (bc().isKnown(h)) + return toJson(bc().info(h)); + switch(bq().blockStatus(h)) { - BlockQueueStatus bqs = m_web3.ethereum()->blockQueue().status(); - ret["importing"] = (int)bqs.importing; - ret["verified"] = (int)bqs.verified; - ret["verifying"] = (int)bqs.verifying; - ret["unverified"] = (int)bqs.unverified; - ret["future"] = (int)bqs.future; - ret["unknown"] = (int)bqs.unknown; - ret["bad"] = (int)bqs.bad; + case QueueStatus::Ready: + return "ready"; + case QueueStatus::Importing: + return "importing"; + case QueueStatus::UnknownParent: + return "unknown parent"; + case QueueStatus::Bad: + return "bad"; + default: + return "unknown"; } +} + +std::string WebThreeStubServer::admin_eth_blockQueueFirstUnknown(std::string const& _session) +{ + ADMIN; + return bq().firstUnknown().hex(); +} + +bool WebThreeStubServer::admin_eth_blockQueueRetryUnknown(std::string const& _session) +{ + ADMIN; + m_web3.ethereum()->retryUnknown(); + return true; +} + +Json::Value WebThreeStubServer::admin_eth_allAccounts(std::string const& _session) +{ + ADMIN; + Json::Value ret; + u256 total = 0; + u256 pendingtotal = 0; + Address beneficiary; + for (auto const& i: m_keyMan.accountDetails()) + { + auto pending = m_web3.ethereum()->balanceAt(i.first, PendingBlock); + auto latest = m_web3.ethereum()->balanceAt(i.first, LatestBlock); + Json::Value a; + if (i.first == beneficiary) + a["beneficiary"] = true; + a["address"] = toJS(i.first); + a["balance"] = toJS(latest); + a["nicebalance"] = formatBalance(latest); + a["pending"] = toJS(pending); + a["nicepending"] = formatBalance(pending); + ret["accounts"][i.second.first] = a; + total += latest; + pendingtotal += pending; + } + ret["total"] = toJS(total); + ret["nicetotal"] = formatBalance(total); + ret["pendingtotal"] = toJS(pendingtotal); + ret["nicependingtotal"] = formatBalance(pendingtotal); return ret; } +Json::Value WebThreeStubServer::admin_eth_newAccount(Json::Value const& _info, std::string const& _session) +{ + ADMIN; + if (!_info.isMember("name")) + throw jsonrpc::JsonRpcException("No member found: name"); + string name = _info["name"].asString(); + auto s = Secret::random(); + h128 uuid; + if (_info.isMember("password")) + { + string password = _info["password"].asString(); + string hint = _info["passwordHint"].asString(); + uuid = m_keyMan.import(s, name, password, hint); + } + else + uuid = m_keyMan.import(s, name); + Json::Value ret; + ret["account"] = toJS(toAddress(s)); + ret["uuid"] = toUUID(uuid); + return ret; +} + +bool WebThreeStubServer::admin_eth_setMiningBenefactor(std::string const& _uuidOrAddress, std::string const& _session) +{ + ADMIN; + (void)_uuidOrAddress; + return true; +} + +Json::Value WebThreeStubServer::admin_eth_inspect(std::string const& _address, std::string const& _session) +{ + ADMIN; + if (!isHash
(_address)) + throw jsonrpc::JsonRpcException("Invalid address given."); + + Json::Value ret; + auto h = Address(fromHex(_address)); + ret["storage"] = toJson(m_web3.ethereum()->storageAt(h, PendingBlock)); + ret["balance"] = toJS(m_web3.ethereum()->balanceAt(h, PendingBlock)); + ret["nonce"] = toJS(m_web3.ethereum()->countAt(h, PendingBlock)); + ret["code"] = toJS(m_web3.ethereum()->codeAt(h, PendingBlock)); + return ret; +} + +h256 WebThreeStubServer::blockHash(std::string const& _blockNumberOrHash) const +{ + if (isHash(_blockNumberOrHash)) + return h256(_blockNumberOrHash.substr(_blockNumberOrHash.size() - 64, 64)); + try + { + return bc().numberHash(stoul(_blockNumberOrHash)); + } + catch (...) + { + throw jsonrpc::JsonRpcException("Invalid argument"); + } +} + +Json::Value WebThreeStubServer::admin_eth_reprocess(std::string const& _blockNumberOrHash, std::string const& _session) +{ + ADMIN; + Json::Value ret; + PopulationStatistics ps; + m_web3.ethereum()->state(blockHash(_blockNumberOrHash), &ps); + ret["enact"] = ps.enact; + ret["verify"] = ps.verify; + ret["total"] = ps.verify + ps.enact; + return ret; +} + +Json::Value WebThreeStubServer::admin_eth_vmTrace(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session) +{ + ADMIN; + + Json::Value ret; + + auto c = m_web3.ethereum(); + State state = c->state(_txIndex + 1, blockHash(_blockNumberOrHash)); + + if (_txIndex < 0) + throw jsonrpc::JsonRpcException("Negative index"); + + if ((unsigned)_txIndex < state.pending().size()) + { + Executive e(state, bc(), 0); + Transaction t = state.pending()[_txIndex]; + state = state.fromPending(_txIndex); + try + { + StandardTrace st; + st.setShowMnemonics(); + e.initialize(t); + if (!e.execute()) + e.go(st.onOp()); + e.finalize(); + Json::Reader().parse(st.json(), ret); + } + catch(Exception const& _e) + { + cwarn << diagnostic_information(_e); + } + } + + return ret; +} + +Json::Value WebThreeStubServer::admin_eth_getReceiptByHashAndIndex(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session) +{ + ADMIN; + if (_txIndex < 0) + throw jsonrpc::JsonRpcException("Negative index"); + auto h = blockHash(_blockNumberOrHash); + if (!bc().isKnown(h)) + throw jsonrpc::JsonRpcException("Invalid/unknown block."); + auto rs = bc().receipts(h); + if ((unsigned)_txIndex >= rs.receipts.size()) + throw jsonrpc::JsonRpcException("Index too large."); + return toJson(rs.receipts[_txIndex]); +} + std::string WebThreeStubServer::web3_clientVersion() { return m_web3.clientVersion(); diff --git a/libweb3jsonrpc/WebThreeStubServer.h b/libweb3jsonrpc/WebThreeStubServer.h index 8bdeb6b7a..ecf8acca0 100644 --- a/libweb3jsonrpc/WebThreeStubServer.h +++ b/libweb3jsonrpc/WebThreeStubServer.h @@ -32,16 +32,19 @@ namespace dev { + class WebThreeDirect; namespace eth { class KeyManager; -} +class TrivialGasPricer; +class CanonBlockChain; +class BlockQueue; } struct SessionPermissions { - bool admin; + std::unordered_set priviledges; }; /** @@ -50,7 +53,7 @@ struct SessionPermissions class WebThreeStubServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace { public: - WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::shared_ptr const& _ethAccounts, std::vector const& _shhAccounts, dev::eth::KeyManager& _keyMan); + WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::shared_ptr const& _ethAccounts, std::vector const& _shhAccounts, dev::eth::KeyManager& _keyMan, dev::eth::TrivialGasPricer& _gp); virtual std::string web3_clientVersion() override; @@ -58,7 +61,7 @@ public: void addSession(std::string const& _session, SessionPermissions const& _p) { m_sessions[_session] = _p; } private: - bool isAdmin(std::string const& _session) const override { auto it = m_sessions.find(_session); return it != m_sessions.end() && it->second.admin; } + virtual bool hasPriviledgeLevel(std::string const& _session, Priviledge _l) const override { auto it = m_sessions.find(_session); return it != m_sessions.end() && it->second.priviledges.count(_l); } virtual dev::eth::Interface* client() override; virtual std::shared_ptr face() override; @@ -68,15 +71,37 @@ private: virtual std::string get(std::string const& _name, std::string const& _key) override; virtual void put(std::string const& _name, std::string const& _key, std::string const& _value) override; - virtual bool eth_notePassword(std::string const& _password); - virtual Json::Value admin_eth_blockQueueStatus(std::string const& _session); + virtual bool eth_notePassword(std::string const& _password) override; + virtual Json::Value admin_eth_blockQueueStatus(std::string const& _session) override; + virtual bool admin_eth_setAskPrice(std::string const& _wei, std::string const& _session) override; + virtual bool admin_eth_setBidPrice(std::string const& _wei, std::string const& _session) override; + + virtual Json::Value admin_eth_findBlock(std::string const& _blockHash, std::string const& _session) override; + virtual std::string admin_eth_blockQueueFirstUnknown(std::string const& _session) override; + virtual bool admin_eth_blockQueueRetryUnknown(std::string const& _session) override; + + virtual Json::Value admin_eth_allAccounts(std::string const& _session) override; + virtual Json::Value admin_eth_newAccount(const Json::Value& _info, std::string const& _session) override; + virtual bool admin_eth_setMiningBenefactor(std::string const& _uuidOrAddress, std::string const& _session) override; + virtual Json::Value admin_eth_inspect(std::string const& _address, std::string const& _session) override; + virtual Json::Value admin_eth_reprocess(std::string const& _blockNumberOrHash, std::string const& _session) override; + virtual Json::Value admin_eth_vmTrace(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session) override; + virtual Json::Value admin_eth_getReceiptByHashAndIndex(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session) override; private: + h256 blockHash(std::string const& _blockNumberOrHash) const; + + dev::eth::CanonBlockChain const& bc() const; + dev::eth::BlockQueue const& bq() const; + dev::WebThreeDirect& m_web3; dev::eth::KeyManager& m_keyMan; + dev::eth::TrivialGasPricer& m_gp; leveldb::ReadOptions m_readOptions; leveldb::WriteOptions m_writeOptions; leveldb::DB* m_db; std::unordered_map m_sessions; }; + +} diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index f88443ee5..99c7e3425 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -21,6 +21,8 @@ * @date 2014 */ +#include "WebThreeStubServerBase.h" + // Make sure boost/asio.hpp is included before windows.h. #include @@ -41,13 +43,13 @@ #if ETH_SERPENT || !ETH_TRUE #include #endif -#include "WebThreeStubServerBase.h" #include "AccountHolder.h" - +#include "JsonHelper.h" using namespace std; using namespace jsonrpc; using namespace dev; -using namespace dev::eth; +using namespace eth; +using namespace shh; #if ETH_DEBUG const unsigned dev::SensibleHttpThreads = 1; @@ -56,302 +58,6 @@ const unsigned dev::SensibleHttpThreads = 4; #endif const unsigned dev::SensibleHttpPort = 8545; -static Json::Value toJson(dev::eth::BlockInfo const& _bi) -{ - Json::Value res; - if (_bi) - { - res["hash"] = toJS(_bi.hash()); - res["parentHash"] = toJS(_bi.parentHash); - res["sha3Uncles"] = toJS(_bi.sha3Uncles); - res["miner"] = toJS(_bi.coinbaseAddress); - res["stateRoot"] = toJS(_bi.stateRoot); - res["transactionsRoot"] = toJS(_bi.transactionsRoot); - res["difficulty"] = toJS(_bi.difficulty); - res["number"] = toJS(_bi.number); - res["gasUsed"] = toJS(_bi.gasUsed); - res["gasLimit"] = toJS(_bi.gasLimit); - res["timestamp"] = toJS(_bi.timestamp); - res["extraData"] = toJS(_bi.extraData); - res["nonce"] = toJS(_bi.nonce); - res["logsBloom"] = toJS(_bi.logBloom); - } - return res; -} - -static Json::Value toJson(dev::eth::Transaction const& _t, std::pair _location, BlockNumber _blockNumber) -{ - Json::Value res; - if (_t) - { - res["hash"] = toJS(_t.sha3()); - res["input"] = toJS(_t.data()); - res["to"] = _t.isCreation() ? Json::Value() : toJS(_t.receiveAddress()); - res["from"] = toJS(_t.safeSender()); - res["gas"] = toJS(_t.gas()); - res["gasPrice"] = toJS(_t.gasPrice()); - res["nonce"] = toJS(_t.nonce()); - res["value"] = toJS(_t.value()); - res["blockHash"] = toJS(_location.first); - res["transactionIndex"] = toJS(_location.second); - res["blockNumber"] = toJS(_blockNumber); - } - return res; -} - -static Json::Value toJson(dev::eth::BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, Transactions const& _ts) -{ - Json::Value res = toJson(_bi); - if (_bi) - { - res["totalDifficulty"] = toJS(_bd.totalDifficulty); - res["uncles"] = Json::Value(Json::arrayValue); - for (h256 h: _us) - res["uncles"].append(toJS(h)); - res["transactions"] = Json::Value(Json::arrayValue); - for (unsigned i = 0; i < _ts.size(); i++) - res["transactions"].append(toJson(_ts[i], std::make_pair(_bi.hash(), i), (BlockNumber)_bi.number)); - } - return res; -} - -static Json::Value toJson(dev::eth::BlockInfo const& _bi, BlockDetails const& _bd, UncleHashes const& _us, TransactionHashes const& _ts) -{ - Json::Value res = toJson(_bi); - if (_bi) - { - res["totalDifficulty"] = toJS(_bd.totalDifficulty); - res["uncles"] = Json::Value(Json::arrayValue); - for (h256 h: _us) - res["uncles"].append(toJS(h)); - res["transactions"] = Json::Value(Json::arrayValue); - for (h256 const& t: _ts) - res["transactions"].append(toJS(t)); - } - return res; -} - -static Json::Value toJson(dev::eth::TransactionSkeleton const& _t) -{ - Json::Value res; - res["to"] = _t.creation ? Json::Value() : toJS(_t.to); - res["from"] = toJS(_t.from); - res["gas"] = toJS(_t.gas); - res["gasPrice"] = toJS(_t.gasPrice); - res["value"] = toJS(_t.value); - res["data"] = toJS(_t.data, 32); - return res; -} - -static Json::Value toJson(dev::eth::Transaction const& _t) -{ - Json::Value res; - res["to"] = _t.isCreation() ? Json::Value() : toJS(_t.to()); - res["from"] = toJS(_t.from()); - res["gas"] = toJS(_t.gas()); - res["gasPrice"] = toJS(_t.gasPrice()); - res["value"] = toJS(_t.value()); - res["data"] = toJS(_t.data(), 32); - res["nonce"] = toJS(_t.nonce()); - res["hash"] = toJS(_t.sha3(WithSignature)); - res["sighash"] = toJS(_t.sha3(WithoutSignature)); - res["r"] = toJS(_t.signature().r); - res["s"] = toJS(_t.signature().s); - res["v"] = toJS(_t.signature().v); - return res; -} - -static Json::Value toJson(dev::eth::LocalisedLogEntry const& _e) -{ - Json::Value res; - if (_e.topics.size() > 0) - { - res["data"] = toJS(_e.data); - res["address"] = toJS(_e.address); - res["topics"] = Json::Value(Json::arrayValue); - for (auto const& t: _e.topics) - res["topics"].append(toJS(t)); - if (_e.mined) - { - res["type"] = "mined"; - res["blockNumber"] = _e.blockNumber; - res["blockHash"] = toJS(_e.blockHash); - res["logIndex"] = _e.logIndex; - res["transactionHash"] = toJS(_e.transactionHash); - res["transactionIndex"] = _e.transactionIndex; - } - else - { - res["type"] = "pending"; - res["blockNumber"] = Json::Value(Json::nullValue); - res["blockHash"] = Json::Value(Json::nullValue); - res["logIndex"] = Json::Value(Json::nullValue); - res["transactionHash"] = Json::Value(Json::nullValue); - res["transactionIndex"] = Json::Value(Json::nullValue); - } - } else { - res = toJS(_e.special); - } - return res; -} - -static Json::Value toJson(dev::eth::LocalisedLogEntries const& _es) -{ - Json::Value res(Json::arrayValue); - for (dev::eth::LocalisedLogEntry const& e: _es) - res.append(toJson(e)); - return res; -} - -static Json::Value toJson(map const& _storage) -{ - Json::Value res(Json::objectValue); - for (auto i: _storage) - res[toJS(i.first)] = toJS(i.second); - return res; -} - -static dev::eth::LogFilter toLogFilter(Json::Value const& _json) -{ - dev::eth::LogFilter filter; - if (!_json.isObject() || _json.empty()) - return filter; - - // check only !empty. it should throw exceptions if input params are incorrect - if (!_json["fromBlock"].empty()) - filter.withEarliest(jsToFixed<32>(_json["fromBlock"].asString())); - if (!_json["toBlock"].empty()) - filter.withLatest(jsToFixed<32>(_json["toBlock"].asString())); - if (!_json["address"].empty()) - { - if (_json["address"].isArray()) - for (auto i : _json["address"]) - filter.address(jsToAddress(i.asString())); - else - filter.address(jsToAddress(_json["address"].asString())); - } - if (!_json["topics"].empty()) - for (unsigned i = 0; i < _json["topics"].size(); i++) - { - if (_json["topics"][i].isArray()) - { - for (auto t: _json["topics"][i]) - if (!t.isNull()) - filter.topic(i, jsToFixed<32>(t.asString())); - } - else if (!_json["topics"][i].isNull()) // if it is anything else then string, it should and will fail - filter.topic(i, jsToFixed<32>(_json["topics"][i].asString())); - } - return filter; -} - -// TODO: this should be removed once we decide to remove backward compatibility with old log filters -static dev::eth::LogFilter toLogFilter(Json::Value const& _json, Interface const& _client) // commented to avoid warning. Uncomment once in use @ PoC-7. -{ - dev::eth::LogFilter filter; - if (!_json.isObject() || _json.empty()) - return filter; - - // check only !empty. it should throw exceptions if input params are incorrect - if (!_json["fromBlock"].empty()) - filter.withEarliest(_client.hashFromNumber(jsToBlockNumber(_json["fromBlock"].asString()))); - if (!_json["toBlock"].empty()) - filter.withLatest(_client.hashFromNumber(jsToBlockNumber(_json["toBlock"].asString()))); - if (!_json["address"].empty()) - { - if (_json["address"].isArray()) - for (auto i : _json["address"]) - filter.address(jsToAddress(i.asString())); - else - filter.address(jsToAddress(_json["address"].asString())); - } - if (!_json["topics"].empty()) - for (unsigned i = 0; i < _json["topics"].size(); i++) - { - if (_json["topics"][i].isArray()) - { - for (auto t: _json["topics"][i]) - if (!t.isNull()) - filter.topic(i, jsToFixed<32>(t.asString())); - } - else if (!_json["topics"][i].isNull()) // if it is anything else then string, it should and will fail - filter.topic(i, jsToFixed<32>(_json["topics"][i].asString())); - } - return filter; -} - -static shh::Message toMessage(Json::Value const& _json) -{ - shh::Message ret; - if (!_json["from"].empty()) - ret.setFrom(jsToPublic(_json["from"].asString())); - if (!_json["to"].empty()) - ret.setTo(jsToPublic(_json["to"].asString())); - if (!_json["payload"].empty()) - ret.setPayload(jsToBytes(_json["payload"].asString())); - return ret; -} - -static shh::Envelope toSealed(Json::Value const& _json, shh::Message const& _m, Secret _from) -{ - unsigned ttl = 50; - unsigned workToProve = 50; - shh::BuildTopic bt; - - if (!_json["ttl"].empty()) - ttl = jsToInt(_json["ttl"].asString()); - - if (!_json["workToProve"].empty()) - workToProve = jsToInt(_json["workToProve"].asString()); - - if (!_json["topics"].empty()) - for (auto i: _json["topics"]) - { - if (i.isArray()) - { - for (auto j: i) - if (!j.isNull()) - bt.shift(jsToBytes(j.asString())); - } - else if (!i.isNull()) // if it is anything else then string, it should and will fail - bt.shift(jsToBytes(i.asString())); - } - - return _m.seal(_from, bt, ttl, workToProve); -} - -static pair toWatch(Json::Value const& _json) -{ - shh::BuildTopic bt; - Public to; - - if (!_json["to"].empty()) - to = jsToPublic(_json["to"].asString()); - - if (!_json["topics"].empty()) - for (auto i: _json["topics"]) - bt.shift(jsToBytes(i.asString())); - - return make_pair(bt, to); -} - -static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message const& _m) -{ - Json::Value res; - res["hash"] = toJS(_h); - res["expiry"] = toJS(_e.expiry()); - res["sent"] = toJS(_e.sent()); - res["ttl"] = toJS(_e.ttl()); - res["workProved"] = toJS(_e.workProved()); - res["topics"] = Json::Value(Json::arrayValue); - for (auto const& t: _e.topic()) - res["topics"].append(toJS(t)); - res["payload"] = toJS(_m.payload()); - res["from"] = toJS(_m.from()); - res["to"] = toJS(_m.to()); - return res; -} - WebThreeStubServerBase::WebThreeStubServerBase(AbstractServerConnector& _conn, std::shared_ptr const& _ethAccounts, vector const& _sshAccounts): AbstractWebThreeStubServer(_conn), m_ethAccounts(_ethAccounts) @@ -468,7 +174,6 @@ string WebThreeStubServerBase::eth_getBlockTransactionCountByHash(string const& } } - string WebThreeStubServerBase::eth_getBlockTransactionCountByNumber(string const& _blockNumber) { try @@ -517,42 +222,12 @@ string WebThreeStubServerBase::eth_getCode(string const& _address, string const& } } -static TransactionSkeleton toTransaction(Json::Value const& _json) -{ - TransactionSkeleton ret; - if (!_json.isObject() || _json.empty()) - return ret; - - if (!_json["from"].empty()) - ret.from = jsToAddress(_json["from"].asString()); - if (!_json["to"].empty() && _json["to"].asString() != "0x") - ret.to = jsToAddress(_json["to"].asString()); - else - ret.creation = true; - - if (!_json["value"].empty()) - ret.value = jsToU256(_json["value"].asString()); - - if (!_json["gas"].empty()) - ret.gas = jsToU256(_json["gas"].asString()); - - if (!_json["gasPrice"].empty()) - ret.gasPrice = jsToU256(_json["gasPrice"].asString()); - - if (!_json["data"].empty()) // ethereum.js has preconstructed the data array - ret.data = jsToBytes(_json["data"].asString()); - - if (!_json["code"].empty()) - ret.data = jsToBytes(_json["code"].asString()); - return ret; -} - string WebThreeStubServerBase::eth_sendTransaction(Json::Value const& _json) { try { string ret; - TransactionSkeleton t = toTransaction(_json); + TransactionSkeleton t = toTransactionSkeleton(_json); if (!t.from) t.from = m_ethAccounts->defaultTransactAccount(); @@ -578,7 +253,7 @@ string WebThreeStubServerBase::eth_signTransaction(Json::Value const& _json) try { string ret; - TransactionSkeleton t = toTransaction(_json); + TransactionSkeleton t = toTransactionSkeleton(_json); if (!t.from) t.from = m_ethAccounts->defaultTransactAccount(); @@ -627,7 +302,7 @@ string WebThreeStubServerBase::eth_call(Json::Value const& _json, string const& { try { - TransactionSkeleton t = toTransaction(_json); + TransactionSkeleton t = toTransactionSkeleton(_json); if (!t.from) t.from = m_ethAccounts->defaultTransactAccount(); // if (!m_accounts->isRealAccount(t.from)) @@ -798,34 +473,32 @@ string WebThreeStubServerBase::eth_compileSerpent(string const& _source) return res; } +#define ADMIN requires(_session, Priviledge::Admin) + bool WebThreeStubServerBase::admin_web3_setVerbosity(int _v, string const& _session) { - if (!isAdmin(_session)) - return false; + ADMIN; g_logVerbosity = _v; return true; } bool WebThreeStubServerBase::admin_net_start(std::string const& _session) { - if (!isAdmin(_session)) - return false; + ADMIN; network()->startNetwork(); return true; } bool WebThreeStubServerBase::admin_net_stop(std::string const& _session) { - if (!isAdmin(_session)) - return false; + ADMIN; network()->stopNetwork(); return true; } bool WebThreeStubServerBase::admin_net_connect(std::string const& _node, std::string const& _session) { - if (!isAdmin(_session)) - return false; + ADMIN; p2p::NodeId id; bi::tcp::endpoint ep; if (_node.substr(0, 8) == "enode://" && _node.find('@') == 136) @@ -839,25 +512,9 @@ bool WebThreeStubServerBase::admin_net_connect(std::string const& _node, std::st return true; } -Json::Value toJson(p2p::PeerSessionInfo const& _p) -{ - Json::Value ret; - ret["id"] = _p.id.hex(); - ret["clientVersion"] = _p.clientVersion; - ret["host"] = _p.host; - ret["port"] = _p.port; - ret["lastPing"] = (int)chrono::duration_cast(_p.lastPing).count(); - for (auto const& i: _p.notes) - ret["notes"][i.first] = i.second; - for (auto const& i: _p.caps) - ret["caps"][i.first] = (unsigned)i.second; - return ret; -} - Json::Value WebThreeStubServerBase::admin_net_peers(std::string const& _session) { - if (!isAdmin(_session)) - return false; + ADMIN; Json::Value ret; for (p2p::PeerSessionInfo const& i: network()->peers()) ret.append(toJson(i)); @@ -866,8 +523,7 @@ Json::Value WebThreeStubServerBase::admin_net_peers(std::string const& _session) bool WebThreeStubServerBase::admin_eth_setMining(bool _on, std::string const& _session) { - if (!isAdmin(_session)) - return false; + ADMIN; if (_on) client()->startMining(); else @@ -970,8 +626,6 @@ bool WebThreeStubServerBase::eth_uninstallFilter(string const& _filterId) { BOOST_THROW_EXCEPTION(JsonRpcException(Errors::ERROR_RPC_INVALID_PARAMS)); } - - } Json::Value WebThreeStubServerBase::eth_getFilterChanges(string const& _filterId) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index aaf0a6096..d3e16d0f4 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" @@ -58,6 +59,26 @@ public: virtual void put(std::string const& _name, std::string const& _key, std::string const& _value) = 0; }; +enum class Priviledge +{ + Admin +}; + +} + +namespace std +{ + +template<> struct hash +{ + size_t operator()(dev::Priviledge _value) const { return (size_t)_value; } +}; + +} + +namespace dev +{ + /** * @brief JSON-RPC api implementation * @todo split these up according to subprotocol (eth, shh, db, p2p, web3) and make it /very/ clear about how to add other subprotocols. @@ -147,25 +168,28 @@ public: virtual Json::Value admin_eth_blockQueueStatus(std::string const& _session) { (void)_session; return Json::Value(); } virtual bool admin_eth_setAskPrice(std::string const& _wei, std::string const& _session) { (void)_wei; (void)_session; return false; } virtual bool admin_eth_setBidPrice(std::string const& _wei, std::string const& _session) { (void)_wei; (void)_session; return false; } - virtual bool admin_eth_setReferencePrice(std::string const& _wei, std::string const& _session) { (void)_wei; (void)_session; return false; } - virtual bool admin_eth_setPriority(int _percent, std::string const& _session) { (void)_percent; (void)_session; return false; } virtual Json::Value admin_eth_findBlock(std::string const& _blockHash, std::string const& _session) { (void)_blockHash; (void)_session; return Json::Value(); } virtual std::string admin_eth_blockQueueFirstUnknown(std::string const& _session) { (void)_session; return ""; } virtual bool admin_eth_blockQueueRetryUnknown(std::string const& _session) { (void)_session; return false; } virtual Json::Value admin_eth_allAccounts(std::string const& _session) { (void)_session; return Json::Value(); } virtual Json::Value admin_eth_newAccount(const Json::Value& _info, std::string const& _session) { (void)_info; (void)_session; return Json::Value(); } - virtual bool admin_eth_setSigningKey(std::string const& _uuidOrAddress, std::string const& _session) { (void)_uuidOrAddress; (void)_session; return false; } virtual bool admin_eth_setMiningBenefactor(std::string const& _uuidOrAddress, std::string const& _session) { (void)_uuidOrAddress; (void)_session; return false; } virtual Json::Value admin_eth_inspect(std::string const& _address, std::string const& _session) { (void)_address; (void)_session; return Json::Value(); } virtual Json::Value admin_eth_reprocess(std::string const& _blockNumberOrHash, std::string const& _session) { (void)_blockNumberOrHash; (void)_session; return Json::Value(); } - virtual Json::Value admin_eth_vmTrace(std::string const& _blockNumberOrHash, std::string const& _txIndex, std::string const& _session) { (void)_blockNumberOrHash; (void)_txIndex; (void)_session; return Json::Value(); } - virtual Json::Value admin_eth_getReceiptByHashAndIndex(std::string const& _blockNumberOrHash, std::string const& _txIndex, std::string const& _session) { (void)_blockNumberOrHash; (void)_txIndex; (void)_session; return Json::Value(); } + virtual Json::Value admin_eth_vmTrace(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session) { (void)_blockNumberOrHash; (void)_txIndex; (void)_session; return Json::Value(); } + virtual Json::Value admin_eth_getReceiptByHashAndIndex(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session) { (void)_blockNumberOrHash; (void)_txIndex; (void)_session; return Json::Value(); } + + // TODO REMOVE + virtual bool admin_eth_setReferencePrice(std::string const& _wei, std::string const& _session) { (void)_wei; (void)_session; return false; } + virtual bool admin_eth_setPriority(int _percent, std::string const& _session) { (void)_percent; (void)_session; return false; } + virtual bool admin_eth_setSigningKey(std::string const& _uuidOrAddress, std::string const& _session) { (void)_uuidOrAddress; (void)_session; return false; } void setIdentities(std::vector const& _ids); std::map const& ids() const { return m_shhIds; } protected: - virtual bool isAdmin(std::string const& _session) const { (void)_session; return false; } + void requires(std::string const& _session, Priviledge _l) const { if (!hasPriviledgeLevel(_session, _l)) throw jsonrpc::JsonRpcException("Invalid priviledges"); } + virtual bool hasPriviledgeLevel(std::string const& _session, Priviledge _l) const { (void)_session; (void)_l; return false; } virtual dev::eth::Interface* client() = 0; virtual std::shared_ptr face() = 0; diff --git a/libweb3jsonrpc/abstractwebthreestubserver.h b/libweb3jsonrpc/abstractwebthreestubserver.h index 6b94c31da..82864a61a 100644 --- a/libweb3jsonrpc/abstractwebthreestubserver.h +++ b/libweb3jsonrpc/abstractwebthreestubserver.h @@ -96,8 +96,8 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerbindAndAddMethod(jsonrpc::Procedure("admin_eth_setMiningBenefactor", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_setMiningBenefactorI); this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_inspect", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_inspectI); this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_reprocess", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_reprocessI); - this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_vmTrace", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_vmTraceI); - this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_getReceiptByHashAndIndex", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_getReceiptByHashAndIndexI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_vmTrace", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_vmTraceI); + this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_getReceiptByHashAndIndex", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::admin_eth_getReceiptByHashAndIndexI); } inline virtual void web3_sha3I(const Json::Value &request, Json::Value &response) @@ -455,11 +455,11 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServeradmin_eth_vmTrace(request[0u].asString(), request[1u].asString(), request[2u].asString()); + response = this->admin_eth_vmTrace(request[0u].asString(), request[1u].asInt(), request[2u].asString()); } inline virtual void admin_eth_getReceiptByHashAndIndexI(const Json::Value &request, Json::Value &response) { - response = this->admin_eth_getReceiptByHashAndIndex(request[0u].asString(), request[1u].asString(), request[2u].asString()); + response = this->admin_eth_getReceiptByHashAndIndex(request[0u].asString(), request[1u].asInt(), request[2u].asString()); } virtual std::string web3_sha3(const std::string& param1) = 0; virtual std::string web3_clientVersion() = 0; @@ -545,8 +545,8 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer #include #include +#include "BuildInfo.h" using namespace std; using namespace dev; using namespace dev::p2p; @@ -72,6 +73,16 @@ WebThreeDirect::~WebThreeDirect() m_ethereum.reset(); } +std::string WebThreeDirect::composeClientVersion(std::string const& _client, std::string const& _clientName) +{ +#if ETH_EVMJIT + char const* jit = "-JIT"; +#else + char const* jit = ""; +#endif + return _client + "v" + dev::Version + "-" + string(DEV_QUOTED(ETH_COMMIT_HASH)).substr(0, 8) + (ETH_CLEAN_REPO ? "" : "*") + "/" + _clientName + "/" DEV_QUOTED(ETH_BUILD_TYPE) "-" DEV_QUOTED(ETH_BUILD_PLATFORM) + jit; +} + p2p::NetworkPreferences const& WebThreeDirect::networkPreferences() const { return m_net.networkPreferences(); diff --git a/libwebthree/WebThree.h b/libwebthree/WebThree.h index ece83abb8..6eefa6a4b 100644 --- a/libwebthree/WebThree.h +++ b/libwebthree/WebThree.h @@ -129,6 +129,8 @@ public: // Misc stuff: + static std::string composeClientVersion(std::string const& _client, std::string const& _name); + std::string const& clientVersion() const { return m_clientVersion; } void setClientVersion(std::string const& _name) { m_clientVersion = _name; } diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index e2e554cb9..befd1d056 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -218,6 +218,8 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas")); case TransactionException::BlockGasLimitReached: BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Block gas limit reached")); + case TransactionException::BadJumpDestination: + BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Solidity exception (bad jump)")); case TransactionException::OutOfStack: BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Out of stack")); case TransactionException::StackUnderflow: @@ -225,7 +227,6 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c //these should not happen in mix case TransactionException::Unknown: case TransactionException::BadInstruction: - case TransactionException::BadJumpDestination: case TransactionException::InvalidSignature: case TransactionException::InvalidNonce: case TransactionException::BadRLP: @@ -296,7 +297,9 @@ ExecutionResult MixClient::execution(unsigned _index) const State MixClient::asOf(h256 const& _block) const { ReadGuard l(x_state); - return State(m_stateDB, bc(), _block); + State ret(m_stateDB); + ret.populateFromChain(bc(), _block); + return ret; } void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, bool _gasAuto) diff --git a/neth/main.cpp b/neth/main.cpp index a6e661d2e..a55c3d559 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -577,10 +577,11 @@ int main(int argc, char** argv) #if ETH_JSONRPC || !ETH_TRUE shared_ptr jsonrpcServer; unique_ptr jsonrpcConnector; + KeyManager km; if (jsonrpc > -1) { jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); - jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){ return web3.ethereum(); }, vector({us})), vector({us}))); + jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, make_shared([&](){ return web3.ethereum(); }, vector({us})), vector({us})), km); jsonrpcServer->setIdentities({us}); jsonrpcServer->StartListening(); } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 3948a4a23..fced12848 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1889,6 +1889,93 @@ BOOST_AUTO_TEST_CASE(storage_location_local_variables) BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); } +BOOST_AUTO_TEST_CASE(assignment_mem_to_local_storage_variable) +{ + char const* sourceCode = R"( + contract C { + uint[] data; + function f(uint[] x) { + var dataRef = data; + dataRef = x; + } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(storage_assign_to_different_local_variable) +{ + char const* sourceCode = R"( + contract C { + uint[] data; + uint8[] otherData; + function f() { + uint8[] storage x = otherData; + uint[] storage y = data; + y = x; + // note that data = otherData works + } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(assignment_mem_storage_variable_directly) +{ + char const* sourceCode = R"( + contract C { + uint[] data; + function f(uint[] x) { + data = x; + } + } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(function_argument_mem_to_storage) +{ + char const* sourceCode = R"( + contract C { + function f(uint[] storage x) private { + } + function g(uint[] x) { + f(x); + } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(function_argument_storage_to_mem) +{ + char const* sourceCode = R"( + contract C { + function f(uint[] storage x) private { + g(x); + } + function g(uint[] x) { + } + } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(mem_array_assignment_changes_base_type) +{ + // Such an assignment is possible in storage, but not in memory + // (because it would incur an otherwise unnecessary copy). + // This requirement might be lifted, though. + char const* sourceCode = R"( + contract C { + function f(uint8[] memory x) private { + uint[] memory y = x; + } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libweb3jsonrpc/webthreestubclient.h b/test/libweb3jsonrpc/webthreestubclient.h index 5c74e5a46..175f1958c 100644 --- a/test/libweb3jsonrpc/webthreestubclient.h +++ b/test/libweb3jsonrpc/webthreestubclient.h @@ -884,7 +884,7 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - Json::Value admin_eth_vmTrace(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException) + Json::Value admin_eth_vmTrace(const std::string& param1, int param2, const std::string& param3) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); @@ -896,7 +896,7 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - Json::Value admin_eth_getReceiptByHashAndIndex(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException) + Json::Value admin_eth_getReceiptByHashAndIndex(const std::string& param1, int param2, const std::string& param3) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1);