Browse Source

Removed dependency on WebThreeDirect form Web3Server

Mix Client Model
cl-refactor
arkpar 10 years ago
parent
commit
23c82cccac
  1. 2
      libdevcore/Exceptions.h
  2. 637
      libweb3jsonrpc/WebThreeStubServer.cpp
  3. 105
      libweb3jsonrpc/WebThreeStubServer.h
  4. 664
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  5. 138
      libweb3jsonrpc/WebThreeStubServerBase.h
  6. 72
      libwebthree/WebThree.h
  7. 39
      mix/AppContext.cpp
  8. 12
      mix/AppContext.h
  9. 169
      mix/AssemblyDebuggerControl.cpp
  10. 42
      mix/AssemblyDebuggerControl.h
  11. 119
      mix/AssemblyDebuggerModel.cpp
  12. 76
      mix/AssemblyDebuggerModel.h
  13. 222
      mix/ClientModel.cpp
  14. 113
      mix/ClientModel.h
  15. 3
      mix/CodeEditorExtensionManager.cpp
  16. 2
      mix/CodeModel.cpp
  17. 8
      mix/CodeModel.h
  18. 2
      mix/ConstantCompilationControl.cpp
  19. 5
      mix/ContractCallDataEncoder.cpp
  20. 8
      mix/ContractCallDataEncoder.h
  21. 40
      mix/DebuggingStateWrapper.h
  22. 5
      mix/FileIo.cpp
  23. 4
      mix/FileIo.h
  24. 5
      mix/MixApplication.cpp
  25. 336
      mix/MixClient.cpp
  26. 125
      mix/MixClient.h
  27. 4
      mix/QFunctionDefinition.cpp
  28. 3
      mix/QFunctionDefinition.h
  29. 55
      mix/Web3Server.cpp
  30. 56
      mix/Web3Server.h
  31. 1
      mix/qml.qrc
  32. 2
      mix/qml/CodeEditorView.qml
  33. 7
      mix/qml/ProjectModel.qml
  34. 2
      mix/qml/StateDialog.qml
  35. 8
      mix/qml/StateList.qml
  36. 81
      mix/qml/WebPreview.qml
  37. 2
      mix/qml/js/ProjectModel.js
  38. 6
      mix/qml/main.qml

2
libdevcore/Exceptions.h

@ -22,6 +22,7 @@
#pragma once
#include <exception>
#include <string>
#include <boost/exception/all.hpp>
#include <boost/throw_exception.hpp>
#include "CommonData.h"
@ -40,6 +41,7 @@ struct NoNetworking: virtual Exception {};
struct NoUPnPDevice: virtual Exception {};
struct RootNotFound: virtual Exception {};
struct FileError: virtual Exception {};
struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): m_f("Interface " + _f + " not supported.") {} virtual const char* what() const noexcept { return m_f.c_str(); } private: std::string m_f; };
// error information to be added to exceptions
typedef boost::error_info<struct tag_invalidSymbol, char> errinfo_invalidSymbol;

637
libweb3jsonrpc/WebThreeStubServer.cpp

@ -21,198 +21,19 @@
* @date 2014
*/
#include <libsolidity/CompilerStack.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/SourceReferenceFormatter.h>
#include "WebThreeStubServer.h"
#include <libevmcore/Instruction.h>
#include <liblll/Compiler.h>
#include <libethereum/Client.h>
#include <libwebthree/WebThree.h>
#include <libdevcore/CommonJS.h>
#include <boost/filesystem.hpp>
#include <libwebthree/WebThree.h>
#include <libdevcrypto/FileSystem.h>
#include <libwhisper/Message.h>
#include <libwhisper/WhisperHost.h>
#include <libserpent/funcs.h>
#include "WebThreeStubServer.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
static Json::Value toJson(dev::eth::BlockInfo const& _bi)
{
Json::Value res;
res["hash"] = boost::lexical_cast<string>(_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"] = (int)_bi.number;
res["gasLimit"] = (int)_bi.gasLimit;
res["timestamp"] = (int)_bi.timestamp;
res["extraData"] = jsFromBinary(_bi.extraData);
res["nonce"] = toJS(_bi.nonce);
return res;
}
static Json::Value toJson(dev::eth::Transaction const& _t)
{
Json::Value res;
res["hash"] = toJS(_t.sha3());
res["input"] = jsFromBinary(_t.data());
res["to"] = toJS(_t.receiveAddress());
res["from"] = toJS(_t.sender());
res["gas"] = (int)_t.gas();
res["gasPrice"] = toJS(_t.gasPrice());
res["nonce"] = toJS(_t.nonce());
res["value"] = toJS(_t.value());
return res;
}
static Json::Value toJson(dev::eth::LogEntry const& _e)
{
Json::Value res;
res["data"] = jsFromBinary(_e.data);
res["address"] = toJS(_e.address);
for (auto const& t: _e.topics)
res["topics"].append(toJS(t));
return res;
}
static Json::Value toJson(dev::eth::LogEntries const& _es) // commented to avoid warning. Uncomment once in use @ poC-7.
{
Json::Value res;
for (dev::eth::LogEntry const& e: _es)
res.append(toJson(e));
return res;
}
static Json::Value toJson(std::map<u256, u256> 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) // commented to avoid warning. Uncomment once in use @ PoC-7.
{
dev::eth::LogFilter filter;
if (!_json.isObject() || _json.empty())
return filter;
if (_json["earliest"].isInt())
filter.withEarliest(_json["earliest"].asInt());
if (_json["latest"].isInt())
filter.withLatest(_json["lastest"].asInt());
if (_json["max"].isInt())
filter.withMax(_json["max"].asInt());
if (_json["skip"].isInt())
filter.withSkip(_json["skip"].asInt());
if (!_json["address"].empty())
{
if (_json["address"].isArray())
{
for (auto i : _json["address"])
if (i.isString())
filter.address(jsToAddress(i.asString()));
}
else if (_json["address"].isString())
filter.address(jsToAddress(_json["address"].asString()));
}
if (!_json["topics"].empty())
{
if (_json["topics"].isArray())
{
for (auto i: _json["topics"])
if (i.isString())
filter.topic(jsToU256(i.asString()));
}
else if(_json["topics"].isString())
filter.topic(jsToU256(_json["topics"].asString()));
}
return filter;
}
static shh::Message toMessage(Json::Value const& _json)
{
shh::Message ret;
if (_json["from"].isString())
ret.setFrom(jsToPublic(_json["from"].asString()));
if (_json["to"].isString())
ret.setTo(jsToPublic(_json["to"].asString()));
if (_json["payload"].isString())
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"].isInt())
ttl = _json["ttl"].asInt();
if (_json["workToProve"].isInt())
workToProve = _json["workToProve"].asInt();
if (!_json["topic"].empty())
{
if (_json["topic"].isString())
bt.shift(jsToBytes(_json["topic"].asString()));
else if (_json["topic"].isArray())
for (auto i: _json["topic"])
if (i.isString())
bt.shift(jsToBytes(i.asString()));
}
return _m.seal(_from, bt, ttl, workToProve);
}
static pair<shh::TopicMask, Public> toWatch(Json::Value const& _json)
{
shh::BuildTopicMask bt;
Public to;
if (_json["to"].isString())
to = jsToPublic(_json["to"].asString());
if (!_json["topic"].empty())
{
if (_json["topic"].isString())
bt.shift(jsToBytes(_json["topic"].asString()));
else if (_json["topic"].isArray())
for (auto i: _json["topic"])
if (i.isString())
bt.shift(jsToBytes(i.asString()));
}
return make_pair(bt.toTopicMask(), 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"] = (int)_e.expiry();
res["sent"] = (int)_e.sent();
res["ttl"] = (int)_e.ttl();
res["workProved"] = (int)_e.workProved();
for (auto const& t: _e.topics())
res["topics"].append(toJS(t));
res["payload"] = toJS(_m.payload());
res["from"] = toJS(_m.from());
res["to"] = toJS(_m.to());
return res;
}
WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts):
AbstractWebThreeStubServer(_conn),
WebThreeStubServerBase(_conn, _accounts),
m_web3(_web3)
{
setAccounts(_accounts);
auto path = getDataDir() + "/.web3";
boost::filesystem::create_directories(path);
ldb::Options o;
@ -220,181 +41,27 @@ WebThreeStubServer::WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn,
ldb::DB::Open(o, path, &m_db);
}
void WebThreeStubServer::setAccounts(std::vector<dev::KeyPair> const& _accounts)
{
m_accounts.clear();
for (auto i: _accounts)
m_accounts[i.address()] = i.secret();
}
void WebThreeStubServer::setIdentities(std::vector<dev::KeyPair> const& _ids)
{
m_ids.clear();
for (auto i: _ids)
m_ids[i.pub()] = i.secret();
}
dev::eth::Interface* WebThreeStubServer::client() const
dev::eth::Interface* WebThreeStubServer::client()
{
return m_web3.ethereum();
}
std::shared_ptr<dev::shh::Interface> WebThreeStubServer::face() const
std::shared_ptr<dev::shh::Interface> WebThreeStubServer::face()
{
return m_web3.whisper();
}
std::string WebThreeStubServer::web3_sha3(std::string const& _param1)
dev::WebThreeNetworkFace* WebThreeStubServer::network()
{
return toJS(sha3(jsToBytes(_param1)));
return &m_web3;
}
Json::Value WebThreeStubServer::eth_accounts()
dev::WebThreeStubDatabaseFace* WebThreeStubServer::db()
{
Json::Value ret(Json::arrayValue);
for (auto i: m_accounts)
ret.append(toJS(i.first));
return ret;
}
std::string WebThreeStubServer::shh_addToGroup(std::string const& _group, std::string const& _who)
{
(void)_group;
(void)_who;
return "";
return this;
}
std::string WebThreeStubServer::eth_balanceAt(string const& _address)
{
return toJS(client()->balanceAt(jsToAddress(_address), client()->getDefault()));
}
Json::Value WebThreeStubServer::eth_blockByHash(std::string const& _hash)
{
return toJson(client()->blockInfo(jsToFixed<32>(_hash)));
}
Json::Value WebThreeStubServer::eth_blockByNumber(int const& _number)
{
return toJson(client()->blockInfo(client()->hashFromNumber(_number)));
}
static TransactionSkeleton toTransaction(Json::Value const& _json)
{
TransactionSkeleton ret;
if (!_json.isObject() || _json.empty())
return ret;
if (_json["from"].isString())
ret.from = jsToAddress(_json["from"].asString());
if (_json["to"].isString())
ret.to = jsToAddress(_json["to"].asString());
if (!_json["value"].empty())
{
if (_json["value"].isString())
ret.value = jsToU256(_json["value"].asString());
else if (_json["value"].isInt())
ret.value = u256(_json["value"].asInt());
}
if (!_json["gas"].empty())
{
if (_json["gas"].isString())
ret.gas = jsToU256(_json["gas"].asString());
else if (_json["gas"].isInt())
ret.gas = u256(_json["gas"].asInt());
}
if (!_json["gasPrice"].empty())
{
if (_json["gasPrice"].isString())
ret.gasPrice = jsToU256(_json["gasPrice"].asString());
else if (_json["gasPrice"].isInt())
ret.gas = u256(_json["gas"].asInt());
}
if (!_json["data"].empty())
{
if (_json["data"].isString()) // ethereum.js has preconstructed the data array
ret.data = jsToBytes(_json["data"].asString());
else if (_json["data"].isArray()) // old style: array of 32-byte-padded values. TODO: remove PoC-8
for (auto i: _json["data"])
dev::operator +=(ret.data, padded(jsToBytes(i.asString()), 32));
}
if (_json["code"].isString())
ret.data = jsToBytes(_json["code"].asString());
return ret;
}
std::string WebThreeStubServer::eth_call(Json::Value const& _json)
{
std::string ret;
TransactionSkeleton t = toTransaction(_json);
if (!t.from && m_accounts.size())
{
auto b = m_accounts.begin()->first;
for (auto a: m_accounts)
if (client()->balanceAt(a.first) > client()->balanceAt(b))
b = a.first;
t.from = b;
}
if (!m_accounts.count(t.from))
return ret;
if (!t.gasPrice)
t.gasPrice = 10 * dev::eth::szabo;
if (!t.gas)
t.gas = min<u256>(client()->gasLimitRemaining(), client()->balanceAt(t.from) / t.gasPrice);
ret = toJS(client()->call(m_accounts[t.from].secret(), t.value, t.to, t.data, t.gas, t.gasPrice));
return ret;
}
bool WebThreeStubServer::eth_changed(int const& _id)
{
return client()->checkWatch(_id);
}
std::string WebThreeStubServer::eth_codeAt(string const& _address)
{
return jsFromBinary(client()->codeAt(jsToAddress(_address), client()->getDefault()));
}
std::string WebThreeStubServer::eth_coinbase()
{
return toJS(client()->address());
}
double WebThreeStubServer::eth_countAt(string const& _address)
{
return (double)(uint64_t)client()->countAt(jsToAddress(_address), client()->getDefault());
}
int WebThreeStubServer::eth_defaultBlock()
{
return client()->getDefault();
}
std::string WebThreeStubServer::eth_gasPrice()
{
return toJS(10 * dev::eth::szabo);
}
std::string WebThreeStubServer::db_get(std::string const& _name, std::string const& _key)
{
bytes k = sha3(_name).asBytes() + sha3(_key).asBytes();
string ret;
m_db->Get(m_readOptions, ldb::Slice((char const*)k.data(), k.size()), &ret);
return toJS(dev::asBytes(ret));
}
Json::Value WebThreeStubServer::eth_filterLogs(int const& _id)
{
return toJson(client()->logs(_id));
}
Json::Value WebThreeStubServer::eth_logs(Json::Value const& _json)
{
return toJson(client()->logs(toLogFilter(_json)));
}
std::string WebThreeStubServer::db_getString(std::string const& _name, std::string const& _key)
std::string WebThreeStubServer::get(std::string const& _name, std::string const& _key)
{
bytes k = sha3(_name).asBytes() + sha3(_key).asBytes();
string ret;
@ -402,289 +69,9 @@ std::string WebThreeStubServer::db_getString(std::string const& _name, std::stri
return ret;
}
bool WebThreeStubServer::shh_haveIdentity(std::string const& _id)
{
return m_ids.count(jsToPublic(_id)) > 0;
}
bool WebThreeStubServer::eth_listening()
{
return m_web3.isNetworkStarted();
}
bool WebThreeStubServer::eth_mining()
{
return client()->isMining();
}
int WebThreeStubServer::eth_newFilter(Json::Value const& _json)
{
unsigned ret = -1;
ret = client()->installWatch(toLogFilter(_json));
return ret;
}
int WebThreeStubServer::eth_newFilterString(std::string const& _filter)
{
unsigned ret = -1;
if (_filter.compare("chain") == 0)
ret = client()->installWatch(dev::eth::ChainChangedFilter);
else if (_filter.compare("pending") == 0)
ret = client()->installWatch(dev::eth::PendingChangedFilter);
return ret;
}
std::string WebThreeStubServer::shh_newGroup(std::string const& _id, std::string const& _who)
{
(void)_id;
(void)_who;
return "";
}
std::string WebThreeStubServer::shh_newIdentity()
{
// cnote << this << m_ids;
KeyPair kp = KeyPair::create();
m_ids[kp.pub()] = kp.secret();
return toJS(kp.pub());
}
Json::Value WebThreeStubServer::eth_compilers()
{
Json::Value ret(Json::arrayValue);
ret.append("lll");
ret.append("solidity");
ret.append("serpent");
return ret;
}
std::string WebThreeStubServer::eth_lll(std::string const& _code)
{
string res;
vector<string> errors;
res = toJS(dev::eth::compileLLL(_code, true, &errors));
cwarn << "LLL compilation errors: " << errors;
return res;
}
std::string WebThreeStubServer::eth_serpent(std::string const& _code)
{
string res;
try
{
res = toJS(dev::asBytes(::compile(_code)));
}
catch (string err)
{
cwarn << "Solidity compilation error: " << err;
}
catch (...)
{
cwarn << "Uncought serpent compilation exception";
}
return res;
}
std::string WebThreeStubServer::eth_solidity(std::string const& _code)
{
string res;
dev::solidity::CompilerStack compiler;
try
{
res = toJS(compiler.compile(_code, true));
}
catch (dev::Exception const& exception)
{
ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler);
cwarn << "Solidity compilation error: " << error.str();
}
catch (...)
{
cwarn << "Uncought solidity compilation exception";
}
return res;
}
int WebThreeStubServer::eth_number()
{
return client()->number() + 1;
}
int WebThreeStubServer::eth_peerCount()
{
return m_web3.peerCount();
}
bool WebThreeStubServer::shh_post(Json::Value const& _json)
{
shh::Message m = toMessage(_json);
Secret from;
if (m.from() && m_ids.count(m.from()))
{
cwarn << "Silently signing message from identity" << m.from().abridged() << ": User validation hook goes here.";
// TODO: insert validification hook here.
from = m_ids[m.from()];
}
face()->inject(toSealed(_json, m, from));
return true;
}
bool WebThreeStubServer::db_put(std::string const& _name, std::string const& _key, std::string const& _value)
void WebThreeStubServer::put(std::string const& _name, std::string const& _key, std::string const& _value)
{
bytes k = sha3(_name).asBytes() + sha3(_key).asBytes();
bytes v = jsToBytes(_value);
m_db->Put(m_writeOptions, ldb::Slice((char const*)k.data(), k.size()), ldb::Slice((char const*)v.data(), v.size()));
return true;
}
bool WebThreeStubServer::db_putString(std::string const& _name, std::string const& _key, std::string const& _value)
{
bytes k = sha3(_name).asBytes() + sha3(_key).asBytes();
string v = _value;
m_db->Put(m_writeOptions, ldb::Slice((char const*)k.data(), k.size()), ldb::Slice((char const*)v.data(), v.size()));
return true;
}
bool WebThreeStubServer::eth_setCoinbase(std::string const& _address)
{
client()->setAddress(jsToAddress(_address));
return true;
}
bool WebThreeStubServer::eth_setDefaultBlock(int const& _block)
{
client()->setDefault(_block);
return true;
}
bool WebThreeStubServer::eth_setListening(bool const& _listening)
{
if (_listening)
m_web3.startNetwork();
else
m_web3.stopNetwork();
return true;
}
bool WebThreeStubServer::eth_setMining(bool const& _mining)
{
if (_mining)
client()->startMining();
else
client()->stopMining();
return true;
}
Json::Value WebThreeStubServer::shh_changed(int const& _id)
{
Json::Value ret(Json::arrayValue);
auto pub = m_shhWatches[_id];
if (!pub || m_ids.count(pub))
for (h256 const& h: face()->checkWatch(_id))
{
auto e = face()->envelope(h);
shh::Message m;
if (pub)
{
cwarn << "Silently decrypting message from identity" << pub.abridged() << ": User validation hook goes here.";
m = e.open(m_ids[pub]);
if (!m)
continue;
}
else
m = e.open();
ret.append(toJson(h, e, m));
}
return ret;
}
int WebThreeStubServer::shh_newFilter(Json::Value const& _json)
{
auto w = toWatch(_json);
auto ret = face()->installWatch(w.first);
m_shhWatches.insert(make_pair(ret, w.second));
return ret;
}
bool WebThreeStubServer::shh_uninstallFilter(int const& _id)
{
face()->uninstallWatch(_id);
return true;
}
std::string WebThreeStubServer::eth_stateAt(string const& _address, string const& _storage)
{
return toJS(client()->stateAt(jsToAddress(_address), jsToU256(_storage), client()->getDefault()));
}
Json::Value WebThreeStubServer::eth_storageAt(string const& _address)
{
return toJson(client()->storageAt(jsToAddress(_address)));
}
std::string WebThreeStubServer::eth_transact(Json::Value const& _json)
{
std::string ret;
TransactionSkeleton t = toTransaction(_json);
if (!t.from && m_accounts.size())
{
auto b = m_accounts.begin()->first;
for (auto a: m_accounts)
if (client()->balanceAt(a.first) > client()->balanceAt(b))
b = a.first;
t.from = b;
}
if (!m_accounts.count(t.from))
return ret;
if (!t.gasPrice)
t.gasPrice = 10 * dev::eth::szabo;
if (!t.gas)
t.gas = min<u256>(client()->gasLimitRemaining(), client()->balanceAt(t.from) / t.gasPrice);
if (authenticate(t))
{
if (t.to)
// TODO: from qethereum, insert validification hook here.
client()->transact(m_accounts[t.from].secret(), t.value, t.to, t.data, t.gas, t.gasPrice);
else
ret = toJS(client()->transact(m_accounts[t.from].secret(), t.value, t.data, t.gas, t.gasPrice));
client()->flushTransactions();
}
return ret;
}
bool WebThreeStubServer::authenticate(TransactionSkeleton const& _t) const
{
cwarn << "Silently signing transaction from address" << _t.from.abridged() << ": User validation hook goes here.";
return true;
}
Json::Value WebThreeStubServer::eth_transactionByHash(std::string const& _hash, int const& _i)
{
return toJson(client()->transaction(jsToFixed<32>(_hash), _i));
}
Json::Value WebThreeStubServer::eth_transactionByNumber(int const& _number, int const& _i)
{
return toJson(client()->transaction(client()->hashFromNumber(_number), _i));
}
Json::Value WebThreeStubServer::eth_uncleByHash(std::string const& _hash, int const& _i)
{
return toJson(client()->uncle(jsToFixed<32>(_hash), _i));
}
Json::Value WebThreeStubServer::eth_uncleByNumber(int const& _number, int const& _i)
{
return toJson(client()->uncle(client()->hashFromNumber(_number), _i));
}
bool WebThreeStubServer::eth_uninstallFilter(int const& _id)
{
client()->uninstallWatch(_id);
return true;
m_db->Put(m_writeOptions, ldb::Slice((char const*)k.data(), k.size()), ldb::Slice((char const*)_value.data(), _value.size()));
}

105
libweb3jsonrpc/WebThreeStubServer.h

@ -28,111 +28,32 @@
#include <leveldb/db.h>
#pragma warning(pop)
#include <iostream>
#include <jsonrpccpp/server.h>
#include <libdevcrypto/Common.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include "abstractwebthreestubserver.h"
#pragma GCC diagnostic pop
namespace ldb = leveldb;
#include "WebThreeStubServerBase.h"
namespace dev
{
class WebThreeDirect;
class KeyPair;
class TransactionSkeleton;
namespace eth
{
class Interface;
}
namespace shh
{
class Interface;
}
}
/**
* @brief JSON-RPC api implementation
* @todo filters should work on unsigned instead of int
* unsigned are not supported in json-rpc-cpp and there are bugs with double in json-rpc-cpp version 0.2.1
* @todo split these up according to subprotocol (eth, shh, db, p2p, web3) and make it /very/ clear about how to add other subprotocols.
* @todo modularise everything so additional subprotocols don't need to change this file.
* @brief JSON-RPC api implementation for WebThreeDirect
*/
class WebThreeStubServer: public AbstractWebThreeStubServer
class WebThreeStubServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace
{
public:
WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts);
virtual std::string web3_sha3(std::string const& _param1);
virtual Json::Value eth_accounts();
virtual std::string eth_balanceAt(std::string const& _address);
virtual Json::Value eth_blockByHash(std::string const& _hash);
virtual Json::Value eth_blockByNumber(int const& _number);
virtual std::string eth_call(Json::Value const& _json);
virtual bool eth_changed(int const& _id);
virtual std::string eth_codeAt(std::string const& _address);
virtual std::string eth_coinbase();
virtual Json::Value eth_compilers();
virtual double eth_countAt(std::string const& _address);
virtual int eth_defaultBlock();
virtual std::string eth_gasPrice();
virtual Json::Value eth_filterLogs(int const& _id);
virtual Json::Value eth_logs(Json::Value const& _json);
virtual bool eth_listening();
virtual bool eth_mining();
virtual int eth_newFilter(Json::Value const& _json);
virtual int eth_newFilterString(std::string const& _filter);
virtual int eth_number();
virtual int eth_peerCount();
virtual bool eth_setCoinbase(std::string const& _address);
virtual bool eth_setDefaultBlock(int const& _block);
virtual bool eth_setListening(bool const& _listening);
virtual std::string eth_lll(std::string const& _s);
virtual std::string eth_serpent(std::string const& _s);
virtual bool eth_setMining(bool const& _mining);
virtual std::string eth_solidity(std::string const& _code);
virtual std::string eth_stateAt(std::string const& _address, std::string const& _storage);
virtual Json::Value eth_storageAt(std::string const& _address);
virtual std::string eth_transact(Json::Value const& _json);
virtual Json::Value eth_transactionByHash(std::string const& _hash, int const& _i);
virtual Json::Value eth_transactionByNumber(int const& _number, int const& _i);
virtual Json::Value eth_uncleByHash(std::string const& _hash, int const& _i);
virtual Json::Value eth_uncleByNumber(int const& _number, int const& _i);
virtual bool eth_uninstallFilter(int const& _id);
virtual std::string db_get(std::string const& _name, std::string const& _key);
virtual std::string db_getString(std::string const& _name, std::string const& _key);
virtual bool db_put(std::string const& _name, std::string const& _key, std::string const& _value);
virtual bool db_putString(std::string const& _name, std::string const& _key, std::string const& _value);
virtual std::string shh_addToGroup(std::string const& _group, std::string const& _who);
virtual Json::Value shh_changed(int const& _id);
virtual bool shh_haveIdentity(std::string const& _id);
virtual int shh_newFilter(Json::Value const& _json);
virtual std::string shh_newGroup(std::string const& _id, std::string const& _who);
virtual std::string shh_newIdentity();
virtual bool shh_post(Json::Value const& _json);
virtual bool shh_uninstallFilter(int const& _id);
void setAccounts(std::vector<dev::KeyPair> const& _accounts);
void setIdentities(std::vector<dev::KeyPair> const& _ids);
std::map<dev::Public, dev::Secret> const& ids() const { return m_ids; }
protected:
virtual bool authenticate(dev::TransactionSkeleton const& _t) const;
private:
dev::eth::Interface* client() override;
std::shared_ptr<dev::shh::Interface> face() override;
dev::WebThreeNetworkFace* network() override;
dev::WebThreeStubDatabaseFace* db() override;
std::string get(std::string const& _name, std::string const& _key) override;
void put(std::string const& _name, std::string const& _key, std::string const& _value) override;
private:
dev::eth::Interface* client() const;
std::shared_ptr<dev::shh::Interface> face() const;
dev::WebThreeDirect& m_web3;
std::map<dev::Address, dev::KeyPair> m_accounts;
ldb::ReadOptions m_readOptions;
ldb::WriteOptions m_writeOptions;
ldb::DB* m_db;
std::map<dev::Public, dev::Secret> m_ids;
std::map<unsigned, dev::Public> m_shhWatches;
leveldb::ReadOptions m_readOptions;
leveldb::WriteOptions m_writeOptions;
leveldb::DB* m_db;
};

664
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -0,0 +1,664 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file WebThreeStubServerBase.cpp
* @authors:
* Gav Wood <i@gavwood.com>
* Marek Kotewicz <marek@ethdev.com>
* @date 2014
*/
#include <libsolidity/CompilerStack.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/SourceReferenceFormatter.h>
#include <libevmcore/Instruction.h>
#include <liblll/Compiler.h>
#include <libethereum/Client.h>
#include <libwebthree/WebThree.h>
#include <libdevcore/CommonJS.h>
#include <libwhisper/Message.h>
#include <libwhisper/WhisperHost.h>
#include <libserpent/funcs.h>
#include "WebThreeStubServerBase.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
static Json::Value toJson(dev::eth::BlockInfo const& _bi)
{
Json::Value res;
res["hash"] = boost::lexical_cast<string>(_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"] = (int)_bi.number;
res["gasLimit"] = (int)_bi.gasLimit;
res["timestamp"] = (int)_bi.timestamp;
res["extraData"] = jsFromBinary(_bi.extraData);
res["nonce"] = toJS(_bi.nonce);
return res;
}
static Json::Value toJson(dev::eth::Transaction const& _t)
{
Json::Value res;
res["hash"] = toJS(_t.sha3());
res["input"] = jsFromBinary(_t.data());
res["to"] = toJS(_t.receiveAddress());
res["from"] = toJS(_t.sender());
res["gas"] = (int)_t.gas();
res["gasPrice"] = toJS(_t.gasPrice());
res["nonce"] = toJS(_t.nonce());
res["value"] = toJS(_t.value());
return res;
}
static Json::Value toJson(dev::eth::LogEntry const& _e)
{
Json::Value res;
res["data"] = jsFromBinary(_e.data);
res["address"] = toJS(_e.address);
for (auto const& t: _e.topics)
res["topics"].append(toJS(t));
return res;
}
static Json::Value toJson(dev::eth::LogEntries const& _es) // commented to avoid warning. Uncomment once in use @ poC-7.
{
Json::Value res;
for (dev::eth::LogEntry const& e: _es)
res.append(toJson(e));
return res;
}
static Json::Value toJson(std::map<u256, u256> 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) // commented to avoid warning. Uncomment once in use @ PoC-7.
{
dev::eth::LogFilter filter;
if (!_json.isObject() || _json.empty())
return filter;
if (_json["earliest"].isInt())
filter.withEarliest(_json["earliest"].asInt());
if (_json["latest"].isInt())
filter.withLatest(_json["lastest"].asInt());
if (_json["max"].isInt())
filter.withMax(_json["max"].asInt());
if (_json["skip"].isInt())
filter.withSkip(_json["skip"].asInt());
if (!_json["address"].empty())
{
if (_json["address"].isArray())
{
for (auto i : _json["address"])
if (i.isString())
filter.address(jsToAddress(i.asString()));
}
else if (_json["address"].isString())
filter.address(jsToAddress(_json["address"].asString()));
}
if (!_json["topics"].empty())
{
if (_json["topics"].isArray())
{
for (auto i: _json["topics"])
if (i.isString())
filter.topic(jsToU256(i.asString()));
}
else if(_json["topics"].isString())
filter.topic(jsToU256(_json["topics"].asString()));
}
return filter;
}
static shh::Message toMessage(Json::Value const& _json)
{
shh::Message ret;
if (_json["from"].isString())
ret.setFrom(jsToPublic(_json["from"].asString()));
if (_json["to"].isString())
ret.setTo(jsToPublic(_json["to"].asString()));
if (_json["payload"].isString())
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"].isInt())
ttl = _json["ttl"].asInt();
if (_json["workToProve"].isInt())
workToProve = _json["workToProve"].asInt();
if (!_json["topic"].empty())
{
if (_json["topic"].isString())
bt.shift(jsToBytes(_json["topic"].asString()));
else if (_json["topic"].isArray())
for (auto i: _json["topic"])
if (i.isString())
bt.shift(jsToBytes(i.asString()));
}
return _m.seal(_from, bt, ttl, workToProve);
}
static pair<shh::TopicMask, Public> toWatch(Json::Value const& _json)
{
shh::BuildTopicMask bt;
Public to;
if (_json["to"].isString())
to = jsToPublic(_json["to"].asString());
if (!_json["topic"].empty())
{
if (_json["topic"].isString())
bt.shift(jsToBytes(_json["topic"].asString()));
else if (_json["topic"].isArray())
for (auto i: _json["topic"])
if (i.isString())
bt.shift(jsToBytes(i.asString()));
}
return make_pair(bt.toTopicMask(), 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"] = (int)_e.expiry();
res["sent"] = (int)_e.sent();
res["ttl"] = (int)_e.ttl();
res["workProved"] = (int)_e.workProved();
for (auto const& t: _e.topics())
res["topics"].append(toJS(t));
res["payload"] = toJS(_m.payload());
res["from"] = toJS(_m.from());
res["to"] = toJS(_m.to());
return res;
}
WebThreeStubServerBase::WebThreeStubServerBase(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _accounts):
AbstractWebThreeStubServer(_conn)
{
setAccounts(_accounts);
}
void WebThreeStubServerBase::setAccounts(std::vector<dev::KeyPair> const& _accounts)
{
m_accounts.clear();
for (auto i: _accounts)
m_accounts[i.address()] = i.secret();
}
void WebThreeStubServerBase::setIdentities(std::vector<dev::KeyPair> const& _ids)
{
m_ids.clear();
for (auto i: _ids)
m_ids[i.pub()] = i.secret();
}
std::string WebThreeStubServerBase::web3_sha3(std::string const& _param1)
{
return toJS(sha3(jsToBytes(_param1)));
}
Json::Value WebThreeStubServerBase::eth_accounts()
{
Json::Value ret(Json::arrayValue);
for (auto i: m_accounts)
ret.append(toJS(i.first));
return ret;
}
std::string WebThreeStubServerBase::shh_addToGroup(std::string const& _group, std::string const& _who)
{
(void)_group;
(void)_who;
return "";
}
std::string WebThreeStubServerBase::eth_balanceAt(string const& _address)
{
return toJS(client()->balanceAt(jsToAddress(_address), client()->getDefault()));
}
Json::Value WebThreeStubServerBase::eth_blockByHash(std::string const& _hash)
{
return toJson(client()->blockInfo(jsToFixed<32>(_hash)));
}
Json::Value WebThreeStubServerBase::eth_blockByNumber(int const& _number)
{
return toJson(client()->blockInfo(client()->hashFromNumber(_number)));
}
static TransactionSkeleton toTransaction(Json::Value const& _json)
{
TransactionSkeleton ret;
if (!_json.isObject() || _json.empty())
return ret;
if (_json["from"].isString())
ret.from = jsToAddress(_json["from"].asString());
if (_json["to"].isString())
ret.to = jsToAddress(_json["to"].asString());
if (!_json["value"].empty())
{
if (_json["value"].isString())
ret.value = jsToU256(_json["value"].asString());
else if (_json["value"].isInt())
ret.value = u256(_json["value"].asInt());
}
if (!_json["gas"].empty())
{
if (_json["gas"].isString())
ret.gas = jsToU256(_json["gas"].asString());
else if (_json["gas"].isInt())
ret.gas = u256(_json["gas"].asInt());
}
if (!_json["gasPrice"].empty())
{
if (_json["gasPrice"].isString())
ret.gasPrice = jsToU256(_json["gasPrice"].asString());
else if (_json["gasPrice"].isInt())
ret.gas = u256(_json["gas"].asInt());
}
if (!_json["data"].empty())
{
if (_json["data"].isString()) // ethereum.js has preconstructed the data array
ret.data = jsToBytes(_json["data"].asString());
else if (_json["data"].isArray()) // old style: array of 32-byte-padded values. TODO: remove PoC-8
for (auto i: _json["data"])
dev::operator +=(ret.data, padded(jsToBytes(i.asString()), 32));
}
if (_json["code"].isString())
ret.data = jsToBytes(_json["code"].asString());
return ret;
}
std::string WebThreeStubServerBase::eth_call(Json::Value const& _json)
{
std::string ret;
TransactionSkeleton t = toTransaction(_json);
if (!t.from && m_accounts.size())
{
auto b = m_accounts.begin()->first;
for (auto a: m_accounts)
if (client()->balanceAt(a.first) > client()->balanceAt(b))
b = a.first;
t.from = b;
}
if (!m_accounts.count(t.from))
return ret;
if (!t.gasPrice)
t.gasPrice = 10 * dev::eth::szabo;
if (!t.gas)
t.gas = min<u256>(client()->gasLimitRemaining(), client()->balanceAt(t.from) / t.gasPrice);
ret = toJS(client()->call(m_accounts[t.from].secret(), t.value, t.to, t.data, t.gas, t.gasPrice));
return ret;
}
bool WebThreeStubServerBase::eth_changed(int const& _id)
{
return client()->checkWatch(_id);
}
std::string WebThreeStubServerBase::eth_codeAt(string const& _address)
{
return jsFromBinary(client()->codeAt(jsToAddress(_address), client()->getDefault()));
}
std::string WebThreeStubServerBase::eth_coinbase()
{
return toJS(client()->address());
}
double WebThreeStubServerBase::eth_countAt(string const& _address)
{
return (double)(uint64_t)client()->countAt(jsToAddress(_address), client()->getDefault());
}
int WebThreeStubServerBase::eth_defaultBlock()
{
return client()->getDefault();
}
std::string WebThreeStubServerBase::eth_gasPrice()
{
return toJS(10 * dev::eth::szabo);
}
std::string WebThreeStubServerBase::db_get(std::string const& _name, std::string const& _key)
{
string ret = db()->get(_name, _key);
return toJS(dev::asBytes(ret));
}
Json::Value WebThreeStubServerBase::eth_filterLogs(int const& _id)
{
return toJson(client()->logs(_id));
}
Json::Value WebThreeStubServerBase::eth_logs(Json::Value const& _json)
{
return toJson(client()->logs(toLogFilter(_json)));
}
std::string WebThreeStubServerBase::db_getString(std::string const& _name, std::string const& _key)
{
return db()->get(_name, _key);;
}
bool WebThreeStubServerBase::shh_haveIdentity(std::string const& _id)
{
return m_ids.count(jsToPublic(_id)) > 0;
}
bool WebThreeStubServerBase::eth_listening()
{
return network()->isNetworkStarted();
}
bool WebThreeStubServerBase::eth_mining()
{
return client()->isMining();
}
int WebThreeStubServerBase::eth_newFilter(Json::Value const& _json)
{
unsigned ret = -1;
ret = client()->installWatch(toLogFilter(_json));
return ret;
}
int WebThreeStubServerBase::eth_newFilterString(std::string const& _filter)
{
unsigned ret = -1;
if (_filter.compare("chain") == 0)
ret = client()->installWatch(dev::eth::ChainChangedFilter);
else if (_filter.compare("pending") == 0)
ret = client()->installWatch(dev::eth::PendingChangedFilter);
return ret;
}
std::string WebThreeStubServerBase::shh_newGroup(std::string const& _id, std::string const& _who)
{
(void)_id;
(void)_who;
return "";
}
std::string WebThreeStubServerBase::shh_newIdentity()
{
// cnote << this << m_ids;
KeyPair kp = KeyPair::create();
m_ids[kp.pub()] = kp.secret();
return toJS(kp.pub());
}
Json::Value WebThreeStubServerBase::eth_compilers()
{
Json::Value ret(Json::arrayValue);
ret.append("lll");
ret.append("solidity");
ret.append("serpent");
return ret;
}
std::string WebThreeStubServerBase::eth_lll(std::string const& _code)
{
string res;
vector<string> errors;
res = toJS(dev::eth::compileLLL(_code, true, &errors));
cwarn << "LLL compilation errors: " << errors;
return res;
}
std::string WebThreeStubServerBase::eth_serpent(std::string const& _code)
{
string res;
try
{
res = toJS(dev::asBytes(::compile(_code)));
}
catch (string err)
{
cwarn << "Solidity compilation error: " << err;
}
catch (...)
{
cwarn << "Uncought serpent compilation exception";
}
return res;
}
std::string WebThreeStubServerBase::eth_solidity(std::string const& _code)
{
string res;
dev::solidity::CompilerStack compiler;
try
{
res = toJS(compiler.compile(_code, true));
}
catch (dev::Exception const& exception)
{
ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler);
cwarn << "Solidity compilation error: " << error.str();
}
catch (...)
{
cwarn << "Uncought solidity compilation exception";
}
return res;
}
int WebThreeStubServerBase::eth_number()
{
return client()->number() + 1;
}
int WebThreeStubServerBase::eth_peerCount()
{
return network()->peerCount();
}
bool WebThreeStubServerBase::shh_post(Json::Value const& _json)
{
shh::Message m = toMessage(_json);
Secret from;
if (m.from() && m_ids.count(m.from()))
{
cwarn << "Silently signing message from identity" << m.from().abridged() << ": User validation hook goes here.";
// TODO: insert validification hook here.
from = m_ids[m.from()];
}
face()->inject(toSealed(_json, m, from));
return true;
}
bool WebThreeStubServerBase::db_put(std::string const& _name, std::string const& _key, std::string const& _value)
{
string v = asString(jsToBytes(_value));
db()->put(_name, _key, v);
return true;
}
bool WebThreeStubServerBase::db_putString(std::string const& _name, std::string const& _key, std::string const& _value)
{
db()->put(_name, _key,_value);
return true;
}
bool WebThreeStubServerBase::eth_setCoinbase(std::string const& _address)
{
client()->setAddress(jsToAddress(_address));
return true;
}
bool WebThreeStubServerBase::eth_setDefaultBlock(int const& _block)
{
client()->setDefault(_block);
return true;
}
bool WebThreeStubServerBase::eth_setListening(bool const& _listening)
{
if (_listening)
network()->startNetwork();
else
network()->stopNetwork();
return true;
}
bool WebThreeStubServerBase::eth_setMining(bool const& _mining)
{
if (_mining)
client()->startMining();
else
client()->stopMining();
return true;
}
Json::Value WebThreeStubServerBase::shh_changed(int const& _id)
{
Json::Value ret(Json::arrayValue);
auto pub = m_shhWatches[_id];
if (!pub || m_ids.count(pub))
for (h256 const& h: face()->checkWatch(_id))
{
auto e = face()->envelope(h);
shh::Message m;
if (pub)
{
cwarn << "Silently decrypting message from identity" << pub.abridged() << ": User validation hook goes here.";
m = e.open(m_ids[pub]);
if (!m)
continue;
}
else
m = e.open();
ret.append(toJson(h, e, m));
}
return ret;
}
int WebThreeStubServerBase::shh_newFilter(Json::Value const& _json)
{
auto w = toWatch(_json);
auto ret = face()->installWatch(w.first);
m_shhWatches.insert(make_pair(ret, w.second));
return ret;
}
bool WebThreeStubServerBase::shh_uninstallFilter(int const& _id)
{
face()->uninstallWatch(_id);
return true;
}
std::string WebThreeStubServerBase::eth_stateAt(string const& _address, string const& _storage)
{
return toJS(client()->stateAt(jsToAddress(_address), jsToU256(_storage), client()->getDefault()));
}
Json::Value WebThreeStubServerBase::eth_storageAt(string const& _address)
{
return toJson(client()->storageAt(jsToAddress(_address)));
}
std::string WebThreeStubServerBase::eth_transact(Json::Value const& _json)
{
std::string ret;
TransactionSkeleton t = toTransaction(_json);
if (!t.from && m_accounts.size())
{
auto b = m_accounts.begin()->first;
for (auto a: m_accounts)
if (client()->balanceAt(a.first) > client()->balanceAt(b))
b = a.first;
t.from = b;
}
if (!m_accounts.count(t.from))
return ret;
if (!t.gasPrice)
t.gasPrice = 10 * dev::eth::szabo;
if (!t.gas)
t.gas = min<u256>(client()->gasLimitRemaining(), client()->balanceAt(t.from) / t.gasPrice);
if (authenticate(t))
{
if (t.to)
// TODO: from qethereum, insert validification hook here.
client()->transact(m_accounts[t.from].secret(), t.value, t.to, t.data, t.gas, t.gasPrice);
else
ret = toJS(client()->transact(m_accounts[t.from].secret(), t.value, t.data, t.gas, t.gasPrice));
client()->flushTransactions();
}
return ret;
}
bool WebThreeStubServerBase::authenticate(TransactionSkeleton const& _t) const
{
cwarn << "Silently signing transaction from address" << _t.from.abridged() << ": User validation hook goes here.";
return true;
}
Json::Value WebThreeStubServerBase::eth_transactionByHash(std::string const& _hash, int const& _i)
{
return toJson(client()->transaction(jsToFixed<32>(_hash), _i));
}
Json::Value WebThreeStubServerBase::eth_transactionByNumber(int const& _number, int const& _i)
{
return toJson(client()->transaction(client()->hashFromNumber(_number), _i));
}
Json::Value WebThreeStubServerBase::eth_uncleByHash(std::string const& _hash, int const& _i)
{
return toJson(client()->uncle(jsToFixed<32>(_hash), _i));
}
Json::Value WebThreeStubServerBase::eth_uncleByNumber(int const& _number, int const& _i)
{
return toJson(client()->uncle(client()->hashFromNumber(_number), _i));
}
bool WebThreeStubServerBase::eth_uninstallFilter(int const& _id)
{
client()->uninstallWatch(_id);
return true;
}

138
libweb3jsonrpc/WebThreeStubServerBase.h

@ -0,0 +1,138 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file WebThreeStubServer.h
* @authors:
* Gav Wood <i@gavwood.com>
* Marek Kotewicz <marek@ethdev.com>
* @date 2014
*/
#pragma once
#include <iostream>
#include <jsonrpccpp/server.h>
#include <libdevcrypto/Common.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include "abstractwebthreestubserver.h"
#pragma GCC diagnostic pop
namespace dev
{
class WebThreeNetworkFace;
class KeyPair;
class TransactionSkeleton;
namespace eth
{
class Interface;
}
namespace shh
{
class Interface;
}
class WebThreeStubDatabaseFace
{
public:
virtual std::string get(std::string const& _name, std::string const& _key) = 0;
virtual void put(std::string const& _name, std::string const& _key, std::string const& _value) = 0;
};
/**
* @brief JSON-RPC api implementation
* @todo filters should work on unsigned instead of int
* unsigned are not supported in json-rpc-cpp and there are bugs with double in json-rpc-cpp version 0.2.1
* @todo split these up according to subprotocol (eth, shh, db, p2p, web3) and make it /very/ clear about how to add other subprotocols.
* @todo modularise everything so additional subprotocols don't need to change this file.
*/
class WebThreeStubServerBase: public AbstractWebThreeStubServer
{
public:
WebThreeStubServerBase(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _accounts);
virtual std::string web3_sha3(std::string const& _param1);
virtual Json::Value eth_accounts();
virtual std::string eth_balanceAt(std::string const& _address);
virtual Json::Value eth_blockByHash(std::string const& _hash);
virtual Json::Value eth_blockByNumber(int const& _number);
virtual std::string eth_call(Json::Value const& _json);
virtual bool eth_changed(int const& _id);
virtual std::string eth_codeAt(std::string const& _address);
virtual std::string eth_coinbase();
virtual Json::Value eth_compilers();
virtual double eth_countAt(std::string const& _address);
virtual int eth_defaultBlock();
virtual std::string eth_gasPrice();
virtual Json::Value eth_filterLogs(int const& _id);
virtual Json::Value eth_logs(Json::Value const& _json);
virtual bool eth_listening();
virtual bool eth_mining();
virtual int eth_newFilter(Json::Value const& _json);
virtual int eth_newFilterString(std::string const& _filter);
virtual int eth_number();
virtual int eth_peerCount();
virtual bool eth_setCoinbase(std::string const& _address);
virtual bool eth_setDefaultBlock(int const& _block);
virtual bool eth_setListening(bool const& _listening);
virtual std::string eth_lll(std::string const& _s);
virtual std::string eth_serpent(std::string const& _s);
virtual bool eth_setMining(bool const& _mining);
virtual std::string eth_solidity(std::string const& _code);
virtual std::string eth_stateAt(std::string const& _address, std::string const& _storage);
virtual Json::Value eth_storageAt(std::string const& _address);
virtual std::string eth_transact(Json::Value const& _json);
virtual Json::Value eth_transactionByHash(std::string const& _hash, int const& _i);
virtual Json::Value eth_transactionByNumber(int const& _number, int const& _i);
virtual Json::Value eth_uncleByHash(std::string const& _hash, int const& _i);
virtual Json::Value eth_uncleByNumber(int const& _number, int const& _i);
virtual bool eth_uninstallFilter(int const& _id);
virtual std::string db_get(std::string const& _name, std::string const& _key);
virtual std::string db_getString(std::string const& _name, std::string const& _key);
virtual bool db_put(std::string const& _name, std::string const& _key, std::string const& _value);
virtual bool db_putString(std::string const& _name, std::string const& _key, std::string const& _value);
virtual std::string shh_addToGroup(std::string const& _group, std::string const& _who);
virtual Json::Value shh_changed(int const& _id);
virtual bool shh_haveIdentity(std::string const& _id);
virtual int shh_newFilter(Json::Value const& _json);
virtual std::string shh_newGroup(std::string const& _id, std::string const& _who);
virtual std::string shh_newIdentity();
virtual bool shh_post(Json::Value const& _json);
virtual bool shh_uninstallFilter(int const& _id);
void setAccounts(std::vector<dev::KeyPair> const& _accounts);
void setIdentities(std::vector<dev::KeyPair> const& _ids);
std::map<dev::Public, dev::Secret> const& ids() const { return m_ids; }
protected:
virtual bool authenticate(dev::TransactionSkeleton const& _t) const;
protected:
virtual dev::eth::Interface* client() = 0;
virtual std::shared_ptr<dev::shh::Interface> face() = 0;
virtual dev::WebThreeNetworkFace* network() = 0;
virtual dev::WebThreeStubDatabaseFace* db() = 0;
std::map<dev::Address, dev::KeyPair> m_accounts;
std::map<dev::Public, dev::Secret> m_ids;
std::map<unsigned, dev::Public> m_shhWatches;
};
} //namespace dev

72
libwebthree/WebThree.h

@ -38,8 +38,6 @@
namespace dev
{
class InterfaceNotSupported: public Exception { public: InterfaceNotSupported(std::string _f): m_f(_f) {} virtual const char* what() const noexcept { return ("Interface " + m_f + " not supported.").c_str(); } private: std::string m_f; };
enum WorkState
{
Active = 0,
@ -51,6 +49,48 @@ namespace eth { class Interface; }
namespace shh { class Interface; }
namespace bzz { class Interface; }
class WebThreeNetworkFace
{
public:
/// Get information on the current peer set.
virtual std::vector<p2p::PeerInfo> peers() = 0;
/// Same as peers().size(), but more efficient.
virtual size_t peerCount() const = 0;
/// Connect to a particular peer.
virtual void connect(std::string const& _seedHost, unsigned short _port) = 0;
/// Save peers
virtual dev::bytes saveNodes() = 0;
/// Restore peers
virtual void restoreNodes(bytesConstRef _saved) = 0;
/// Sets the ideal number of peers.
virtual void setIdealPeerCount(size_t _n) = 0;
virtual bool haveNetwork() const = 0;
virtual void setNetworkPreferences(p2p::NetworkPreferences const& _n) = 0;
virtual p2p::NodeId id() const = 0;
/// Gets the nodes.
virtual p2p::Nodes nodes() const = 0;
/// Start the network subsystem.
virtual void startNetwork() = 0;
/// Stop the network subsystem.
virtual void stopNetwork() = 0;
/// Is network working? there may not be any peers yet.
virtual bool isNetworkStarted() const = 0;
};
/**
* @brief Main API hub for interfacing with Web 3 components. This doesn't do any local multiplexing, so you can only have one
* running on any given machine for the provided DB path.
@ -61,7 +101,7 @@ namespace bzz { class Interface; }
*
* Provides a baseline for the multiplexed multi-protocol session class, WebThree.
*/
class WebThreeDirect
class WebThreeDirect : public WebThreeNetworkFace
{
public:
/// Constructor for private instance. If there is already another process on the machine using @a _dbPath, then this will throw an exception.
@ -84,40 +124,40 @@ public:
// Network stuff:
/// Get information on the current peer set.
std::vector<p2p::PeerInfo> peers();
std::vector<p2p::PeerInfo> peers() override;
/// Same as peers().size(), but more efficient.
size_t peerCount() const;
size_t peerCount() const override;
/// Connect to a particular peer.
void connect(std::string const& _seedHost, unsigned short _port = 30303);
void connect(std::string const& _seedHost, unsigned short _port = 30303) override;
/// Save peers
dev::bytes saveNodes();
dev::bytes saveNodes() override;
/// Restore peers
void restoreNodes(bytesConstRef _saved);
void restoreNodes(bytesConstRef _saved) override;
/// Sets the ideal number of peers.
void setIdealPeerCount(size_t _n);
void setIdealPeerCount(size_t _n) override;
bool haveNetwork() const { return m_net.isStarted(); }
bool haveNetwork() const override { return m_net.isStarted(); }
void setNetworkPreferences(p2p::NetworkPreferences const& _n);
void setNetworkPreferences(p2p::NetworkPreferences const& _n) override;
p2p::NodeId id() const { return m_net.id(); }
p2p::NodeId id() const override { return m_net.id(); }
/// Gets the nodes.
p2p::Nodes nodes() const { return m_net.nodes(); }
p2p::Nodes nodes() const override { return m_net.nodes(); }
/// Start the network subsystem.
void startNetwork() { m_net.start(); }
void startNetwork() override { m_net.start(); }
/// Stop the network subsystem.
void stopNetwork() { m_net.stop(); }
void stopNetwork() override { m_net.stop(); }
/// Is network working? there may not be any peers yet.
bool isNetworkStarted() { return m_net.isStarted(); }
bool isNetworkStarted() const override { return m_net.isStarted(); }
private:
std::string m_clientVersion; ///< Our end-application client's name/version.

39
mix/AppContext.cpp

@ -27,13 +27,10 @@
#include <QQmlComponent>
#include <QQmlContext>
#include <QQmlApplicationEngine>
#include <QStandardPaths>
#include <QFile>
#include <QDir>
#include <libdevcrypto/FileSystem.h>
#include <libwebthree/WebThree.h>
#include "CodeModel.h"
#include "FileIo.h"
#include "ClientModel.h"
#include "AppContext.h"
@ -47,7 +44,8 @@ AppContext::AppContext(QQmlApplicationEngine* _engine)
{
m_applicationEngine = _engine;
//m_webThree = std::unique_ptr<dev::WebThreeDirect>(new WebThreeDirect(std::string("Mix/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/Mix", false, {"eth", "shh"}));
m_codeModel = std::unique_ptr<CodeModel>(new CodeModel(this));
m_codeModel.reset(new CodeModel(this));
m_clientModel.reset(new ClientModel(this));
m_fileIo.reset(new FileIo());
m_applicationEngine->rootContext()->setContextProperty("appContext", this);
qmlRegisterType<FileIo>("org.ethereum.qml", 1, 0, "FileIo");
@ -61,21 +59,6 @@ AppContext::~AppContext()
{
}
void AppContext::loadProject()
{
QString path = QStandardPaths::locate(QStandardPaths::DataLocation, c_projectFileName);
if (!path.isEmpty())
{
QFile file(path);
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QTextStream stream(&file);
QString json = stream.readAll();
emit projectLoaded(json);
}
}
}
QQmlApplicationEngine* AppContext::appEngine()
{
return m_applicationEngine;
@ -93,19 +76,3 @@ void AppContext::displayMessageDialog(QString _title, QString _message)
dialogWin->findChild<QObject*>("messageContent", Qt::FindChildrenRecursively)->setProperty("text", _message);
QMetaObject::invokeMethod(dialogWin, "open");
}
void AppContext::saveProject(QString const& _json)
{
QDir dirPath(QStandardPaths::writableLocation(QStandardPaths::DataLocation));
QString path = QDir(dirPath).filePath(c_projectFileName);
if (!path.isEmpty())
{
dirPath.mkpath(dirPath.path());
QFile file(path);
if (file.open(QIODevice::WriteOnly | QIODevice::Text))
{
QTextStream stream(&file);
stream << _json;
}
}
}

12
mix/AppContext.h

@ -47,6 +47,7 @@ namespace mix
{
class CodeModel;
class ClientModel;
class FileIo;
/**
* @brief Provides access to application scope variable.
@ -63,24 +64,25 @@ public:
QQmlApplicationEngine* appEngine();
/// Get code model
CodeModel* codeModel() { return m_codeModel.get(); }
/// Get client model
ClientModel* clientModel() { return m_clientModel.get(); }
/// Display an alert message.
void displayMessageDialog(QString _title, QString _message);
/// Load project settings
void loadProject();
signals:
void projectLoaded(QString const& _json);
/// Triggered once components have been loaded
void appLoaded();
private:
QQmlApplicationEngine* m_applicationEngine; //owned by app
std::unique_ptr<dev::WebThreeDirect> m_webThree;
std::unique_ptr<CodeModel> m_codeModel;
std::unique_ptr<ClientModel> m_clientModel;
std::unique_ptr<FileIo> m_fileIo;
public slots:
/// Delete the current instance when application quit.
void quitApplication() {}
/// Write json to a settings file
void saveProject(QString const& _json);
};
}

169
mix/AssemblyDebuggerControl.cpp

@ -17,53 +17,19 @@
* display opcode debugging.
*/
#include <QtConcurrent/QtConcurrent>
#include <QDebug>
#include <QQmlContext>
#include <QQmlApplicationEngine>
#include <QModelIndex>
#include <libdevcore/CommonJS.h>
#include <libethereum/Transaction.h>
#include "AssemblyDebuggerModel.h"
#include "AssemblyDebuggerControl.h"
#include "AppContext.h"
#include "DebuggingStateWrapper.h"
#include "QContractDefinition.h"
#include "QVariableDeclaration.h"
#include "ContractCallDataEncoder.h"
#include "CodeModel.h"
#include "ClientModel.h"
#include "AssemblyDebuggerControl.h"
using namespace dev::eth;
using namespace dev::mix;
/// @todo Move this to QML
dev::u256 fromQString(QString const& _s)
{
return dev::jsToU256(_s.toStdString());
}
/// @todo Move this to QML
QString toQString(dev::u256 _value)
{
std::ostringstream s;
s << _value;
return QString::fromStdString(s.str());
}
AssemblyDebuggerControl::AssemblyDebuggerControl(AppContext* _context):
Extension(_context, ExtensionDisplayBehavior::ModalDialog), m_running(false)
Extension(_context, ExtensionDisplayBehavior::ModalDialog)
{
qRegisterMetaType<QVariableDefinition*>("QVariableDefinition*");
qRegisterMetaType<QVariableDefinitionList*>("QVariableDefinitionList*");
qRegisterMetaType<QList<QVariableDefinition*>>("QList<QVariableDefinition*>");
qRegisterMetaType<QList<QVariableDeclaration*>>("QList<QVariableDeclaration*>");
qRegisterMetaType<QVariableDeclaration*>("QVariableDeclaration*");
qRegisterMetaType<AssemblyDebuggerData>("AssemblyDebuggerData");
connect(this, &AssemblyDebuggerControl::dataAvailable, this, &AssemblyDebuggerControl::showDebugger, Qt::QueuedConnection);
m_modelDebugger = std::unique_ptr<AssemblyDebuggerModel>(new AssemblyDebuggerModel);
_context->appEngine()->rootContext()->setContextProperty("debugModel", this);
connect(_context->clientModel(), &ClientModel::showDebuggerWindow, this, &AssemblyDebuggerControl::showDebugger, Qt::QueuedConnection);
}
QString AssemblyDebuggerControl::contentUrl() const
@ -80,132 +46,7 @@ void AssemblyDebuggerControl::start() const
{
}
void AssemblyDebuggerControl::debugDeployment()
{
executeSequence(std::vector<TransactionSettings>(), 0);
}
void AssemblyDebuggerControl::debugState(QVariantMap _state)
{
u256 balance = fromQString(_state.value("balance").toString());
QVariantList transactions = _state.value("transactions").toList();
std::vector<TransactionSettings> transactionSequence;
for (auto const& t: transactions)
{
QVariantMap transaction = t.toMap();
QString functionId = transaction.value("functionId").toString();
u256 value = fromQString(transaction.value("value").toString());
u256 gas = fromQString(transaction.value("gas").toString());
u256 gasPrice = fromQString(transaction.value("gasPrice").toString());
QVariantMap params = transaction.value("parameters").toMap();
TransactionSettings transactionSettings(functionId, value, gas, gasPrice);
for (auto p = params.cbegin(); p != params.cend(); ++p)
transactionSettings.parameterValues.insert(std::make_pair(p.key(), fromQString(p.value().toString())));
transactionSequence.push_back(transactionSettings);
}
executeSequence(transactionSequence, balance);
}
void AssemblyDebuggerControl::executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance)
{
if (m_running)
throw (std::logic_error("debugging already running"));
auto compilerRes = m_ctx->codeModel()->code();
std::shared_ptr<QContractDefinition> contractDef = compilerRes->sharedContract();
m_running = true;
emit runStarted();
emit stateChanged();
//run sequence
QtConcurrent::run([=]()
{
try
{
bytes contractCode = compilerRes->bytes();
std::vector<dev::bytes> transactonData;
QFunctionDefinition* f;
ContractCallDataEncoder c;
//encode data for all transactions
for (auto const& t: _sequence)
{
f = nullptr;
for (int tf = 0; tf < contractDef->functionsList().size(); tf++)
{
if (contractDef->functionsList().at(tf)->name() == t.functionId)
{
f = contractDef->functionsList().at(tf);
break;
}
}
if (!f)
throw std::runtime_error("function " + t.functionId.toStdString() + " not found");
c.encode(f->index());
for (int p = 0; p < f->parametersList().size(); p++)
{
QVariableDeclaration* var = (QVariableDeclaration*)f->parametersList().at(p);
u256 value = 0;
auto v = t.parameterValues.find(var->name());
if (v != t.parameterValues.cend())
value = v->second;
c.encode(var, value);
}
transactonData.emplace_back(c.encodedData());
}
//run contract creation first
m_modelDebugger->resetState(_balance);
DebuggingContent debuggingContent = m_modelDebugger->deployContract(contractCode);
Address address = debuggingContent.contractAddress;
for (unsigned i = 0; i < _sequence.size(); ++i)
debuggingContent = m_modelDebugger->callContract(address, transactonData.at(i), _sequence.at(i));
if (f)
debuggingContent.returnParameters = c.decode(f->returnParameters(), debuggingContent.returnValue);
//we need to wrap states in a QObject before sending to QML.
QList<QObject*> wStates;
for (int i = 0; i < debuggingContent.machineStates.size(); i++)
{
QPointer<DebuggingStateWrapper> s(new DebuggingStateWrapper(debuggingContent.executionCode, debuggingContent.executionData.toBytes()));
s->setState(debuggingContent.machineStates.at(i));
wStates.append(s);
}
//collect states for last transaction
AssemblyDebuggerData code = DebuggingStateWrapper::getHumanReadableCode(debuggingContent.executionCode);
emit dataAvailable(debuggingContent.returnParameters, wStates, code);
emit runComplete();
}
catch(boost::exception const&)
{
emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information()));
}
catch(std::exception const& e)
{
emit runFailed(e.what());
}
m_running = false;
emit stateChanged();
});
}
void AssemblyDebuggerControl::showDebugger(QList<QVariableDefinition*> const& _returnParam, QList<QObject*> const& _wStates, AssemblyDebuggerData const& _code)
void AssemblyDebuggerControl::showDebugger()
{
m_appEngine->rootContext()->setContextProperty("debugStates", QVariant::fromValue(_wStates));
m_appEngine->rootContext()->setContextProperty("humanReadableExecutionCode", QVariant::fromValue(std::get<0>(_code)));
m_appEngine->rootContext()->setContextProperty("bytesCodeMapping", QVariant::fromValue(std::get<1>(_code)));
m_appEngine->rootContext()->setContextProperty("contractCallReturnParameters", QVariant::fromValue(new QVariableDefinitionList(_returnParam)));
this->addContentOn(this);
}
void AssemblyDebuggerControl::showDebugError(QString const& _error)
{
m_ctx->displayMessageDialog(QApplication::tr("Debugger"), _error);
}

42
mix/AssemblyDebuggerControl.h

@ -20,14 +20,7 @@
#pragma once
#include <atomic>
#include <QKeySequence>
#include "Extension.h"
#include "AssemblyDebuggerModel.h"
using AssemblyDebuggerData = std::tuple<QList<QObject*>, dev::mix::QQMLMap*>;
Q_DECLARE_METATYPE(AssemblyDebuggerData)
Q_DECLARE_METATYPE(dev::mix::DebuggingContent)
class AppContext;
@ -37,7 +30,7 @@ namespace mix
{
/**
* @brief Extension which display transaction creation or transaction call debugging. handle: F5 to deploy contract, F6 to reset state.
* @brief Extension which display transaction creation or transaction call debugging.
*/
class AssemblyDebuggerControl: public Extension
{
@ -50,40 +43,9 @@ public:
QString title() const override;
QString contentUrl() const override;
Q_PROPERTY(bool running MEMBER m_running NOTIFY stateChanged)
private:
void executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance);
std::unique_ptr<AssemblyDebuggerModel> m_modelDebugger;
std::atomic<bool> m_running;
public slots:
/// Run the contract constructor and show debugger window.
void debugDeployment();
/// Setup state, run transaction sequence, show debugger for the last transaction
/// @param _state JS object with state configuration
void debugState(QVariantMap _state);
private slots:
/// Update UI with machine states result. Display a modal dialog.
void showDebugger(QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData());
/// Update UI with transaction run error.
void showDebugError(QString const& _error);
signals:
/// Transaction execution started
void runStarted();
/// Transaction execution completed successfully
void runComplete();
/// Transaction execution completed with error
/// @param _message Error message
void runFailed(QString const& _message);
/// Execution state changed
void stateChanged();
/// Emited when machine states are available.
void dataAvailable(QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData());
void showDebugger();
};
}

119
mix/AssemblyDebuggerModel.cpp

@ -1,119 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file AssemblyDebuggerModel.cpp
* @author Yann yann@ethdev.com
* @date 2014
* used as a model to debug contract assembly code.
*/
#include <QApplication>
#include <libdevcore/Common.h>
#include <libevm/VM.h>
#include <libethereum/Executive.h>
#include <libethereum/Transaction.h>
#include <libethereum/ExtVM.h>
#include "AssemblyDebuggerModel.h"
#include "AppContext.h"
#include "DebuggingStateWrapper.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
namespace dev
{
namespace mix
{
AssemblyDebuggerModel::AssemblyDebuggerModel():
m_userAccount(KeyPair::create())
{
resetState(10000000 * ether);
}
DebuggingContent AssemblyDebuggerModel::executeTransaction(bytesConstRef const& _rawTransaction)
{
QList<DebuggingState> machineStates;
eth::Executive execution(m_executiveState, LastHashes(), 0);
execution.setup(_rawTransaction);
std::vector<DebuggingState const*> levels;
bytes code;
bytesConstRef data;
bool firstIteration = true;
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt)
{
VM& vm = *(VM*)voidVM;
ExtVM const& ext = *(ExtVM const*)voidExt;
if (firstIteration)
{
code = ext.code;
data = ext.data;
firstIteration = false;
}
if (levels.size() < ext.depth)
levels.push_back(&machineStates.back());
else
levels.resize(ext.depth);
machineStates.append(DebuggingState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(),
vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels}));
};
execution.go(onOp);
execution.finalize();
DebuggingContent d;
d.returnValue = execution.out().toVector();
d.machineStates = machineStates;
d.executionCode = code;
d.executionData = data;
d.contentAvailable = true;
d.message = "ok";
return d;
}
DebuggingContent AssemblyDebuggerModel::deployContract(bytes const& _code)
{
u256 gasPrice = 10000000000000;
u256 gas = 1000000;
u256 amount = 100;
Transaction _tr(amount, gasPrice, min(gas, m_executiveState.gasLimitRemaining()), _code, m_executiveState.transactionsFrom(dev::toAddress(m_userAccount.secret())), m_userAccount.secret());
bytes b = _tr.rlp();
dev::bytesConstRef bytesRef = &b;
DebuggingContent d = executeTransaction(bytesRef);
h256 th = sha3(rlpList(_tr.sender(), _tr.nonce()));
d.contractAddress = right160(th);
return d;
}
DebuggingContent AssemblyDebuggerModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr)
{
Transaction tr = Transaction(_tr.value, _tr.gasPrice, min(_tr.gas, m_executiveState.gasLimitRemaining()), _contract, _data, m_executiveState.transactionsFrom(dev::toAddress(m_userAccount.secret())), m_userAccount.secret());
bytes b = tr.rlp();
dev::bytesConstRef bytesRef = &b;
DebuggingContent d = executeTransaction(bytesRef);
d.contractAddress = tr.receiveAddress();
return d;
}
void AssemblyDebuggerModel::resetState(u256 _balance)
{
m_executiveState = eth::State(Address(), m_overlayDB, BaseState::Empty);
m_executiveState.addBalance(m_userAccount.address(), _balance);
}
}
}

76
mix/AssemblyDebuggerModel.h

@ -1,76 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file AssemblyDebuggerModel.h
* @author Yann yann@ethdev.com
* @date 2014
* Used as a model to debug contract assembly code.
*/
#pragma once
#include <QObject>
#include <QList>
#include <libdevcore/Common.h>
#include <libdevcrypto/Common.h>
#include <libethereum/State.h>
#include <libethereum/Executive.h>
#include "DebuggingStateWrapper.h"
namespace dev
{
namespace mix
{
/// Backend transaction config class
struct TransactionSettings
{
TransactionSettings(QString const& _functionId, u256 _value, u256 _gas, u256 _gasPrice):
functionId(_functionId), value(_value), gas(_gas), gasPrice(_gasPrice) {}
/// Contract function name
QString functionId;
/// Transaction value
u256 value;
/// Gas
u256 gas;
/// Gas price
u256 gasPrice;
/// Mapping from contract function parameter name to value
std::map<QString, u256> parameterValues;
};
/**
* @brief Long-life object for managing all executions.
*/
class AssemblyDebuggerModel
{
public:
AssemblyDebuggerModel();
/// Call function in a already deployed contract.
DebuggingContent callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
/// Deploy the contract described by _code.
DebuggingContent deployContract(bytes const& _code);
/// Reset state to the empty state with given balance.
void resetState(u256 _balance);
private:
KeyPair m_userAccount;
OverlayDB m_overlayDB;
eth::State m_executiveState;
DebuggingContent executeTransaction(dev::bytesConstRef const& _rawTransaction);
};
}
}

222
mix/ClientModel.cpp

@ -0,0 +1,222 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ClientModel.cpp
* @author Yann yann@ethdev.com
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#include <QtConcurrent/QtConcurrent>
#include <QDebug>
#include <QQmlContext>
#include <QQmlApplicationEngine>
#include <libdevcore/CommonJS.h>
#include <libethereum/Transaction.h>
#include "ClientModel.h"
#include "AppContext.h"
#include "DebuggingStateWrapper.h"
#include "QContractDefinition.h"
#include "QVariableDeclaration.h"
#include "ContractCallDataEncoder.h"
#include "CodeModel.h"
#include "ClientModel.h"
using namespace dev::eth;
using namespace dev::mix;
/// @todo Move this to QML
dev::u256 fromQString(QString const& _s)
{
return dev::jsToU256(_s.toStdString());
}
/// @todo Move this to QML
QString toQString(dev::u256 _value)
{
std::ostringstream s;
s << _value;
return QString::fromStdString(s.str());
}
ClientModel::ClientModel(AppContext* _context):
m_context(_context), m_running(false)
{
qRegisterMetaType<QVariableDefinition*>("QVariableDefinition*");
qRegisterMetaType<QVariableDefinitionList*>("QVariableDefinitionList*");
qRegisterMetaType<QList<QVariableDefinition*>>("QList<QVariableDefinition*>");
qRegisterMetaType<QList<QVariableDeclaration*>>("QList<QVariableDeclaration*>");
qRegisterMetaType<QVariableDeclaration*>("QVariableDeclaration*");
qRegisterMetaType<AssemblyDebuggerData>("AssemblyDebuggerData");
connect(this, &ClientModel::dataAvailable, this, &ClientModel::showDebugger, Qt::QueuedConnection);
m_client.reset(new MixClient());
_context->appEngine()->rootContext()->setContextProperty("clientModel", this);
}
void ClientModel::debugDeployment()
{
executeSequence(std::vector<TransactionSettings>(), 10000000 * ether);
}
void ClientModel::debugState(QVariantMap _state)
{
u256 balance = fromQString(_state.value("balance").toString());
QVariantList transactions = _state.value("transactions").toList();
std::vector<TransactionSettings> transactionSequence;
for (auto const& t: transactions)
{
QVariantMap transaction = t.toMap();
QString functionId = transaction.value("functionId").toString();
u256 value = fromQString(transaction.value("value").toString());
u256 gas = fromQString(transaction.value("gas").toString());
u256 gasPrice = fromQString(transaction.value("gasPrice").toString());
QVariantMap params = transaction.value("parameters").toMap();
TransactionSettings transactionSettings(functionId, value, gas, gasPrice);
for (auto p = params.cbegin(); p != params.cend(); ++p)
transactionSettings.parameterValues.insert(std::make_pair(p.key(), fromQString(p.value().toString())));
transactionSequence.push_back(transactionSettings);
}
executeSequence(transactionSequence, balance);
}
void ClientModel::executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance)
{
if (m_running)
throw (std::logic_error("debugging already running"));
auto compilerRes = m_context->codeModel()->code();
std::shared_ptr<QContractDefinition> contractDef = compilerRes->sharedContract();
m_running = true;
emit runStarted();
emit stateChanged();
//run sequence
QtConcurrent::run([=]()
{
try
{
bytes contractCode = compilerRes->bytes();
std::vector<dev::bytes> transactonData;
QFunctionDefinition* f;
ContractCallDataEncoder c;
//encode data for all transactions
for (auto const& t: _sequence)
{
f = nullptr;
for (int tf = 0; tf < contractDef->functionsList().size(); tf++)
{
if (contractDef->functionsList().at(tf)->name() == t.functionId)
{
f = contractDef->functionsList().at(tf);
break;
}
}
if (!f)
throw std::runtime_error("function " + t.functionId.toStdString() + " not found");
c.encode(f);
for (int p = 0; p < f->parametersList().size(); p++)
{
QVariableDeclaration* var = (QVariableDeclaration*)f->parametersList().at(p);
u256 value = 0;
auto v = t.parameterValues.find(var->name());
if (v != t.parameterValues.cend())
value = v->second;
c.encode(var, value);
}
transactonData.emplace_back(c.encodedData());
}
//run contract creation first
m_client->resetState(_balance);
ExecutionResult debuggingContent = deployContract(contractCode);
Address address = debuggingContent.contractAddress;
for (unsigned i = 0; i < _sequence.size(); ++i)
debuggingContent = callContract(address, transactonData.at(i), _sequence.at(i));
QList<QVariableDefinition*> returnParameters;
if (f)
returnParameters = c.decode(f->returnParameters(), debuggingContent.returnValue);
//we need to wrap states in a QObject before sending to QML.
QList<QObject*> wStates;
for (unsigned i = 0; i < debuggingContent.machineStates.size(); i++)
{
QPointer<DebuggingStateWrapper> s(new DebuggingStateWrapper(debuggingContent.executionCode, debuggingContent.executionData.toBytes()));
s->setState(debuggingContent.machineStates[i]);
wStates.append(s);
}
//collect states for last transaction
AssemblyDebuggerData code = DebuggingStateWrapper::getHumanReadableCode(debuggingContent.executionCode);
emit dataAvailable(returnParameters, wStates, code);
emit runComplete();
}
catch(boost::exception const&)
{
emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information()));
}
catch(std::exception const& e)
{
emit runFailed(e.what());
}
m_running = false;
emit stateChanged();
});
}
void ClientModel::showDebugger(QList<QVariableDefinition*> const& _returnParam, QList<QObject*> const& _wStates, AssemblyDebuggerData const& _code)
{
m_context->appEngine()->rootContext()->setContextProperty("debugStates", QVariant::fromValue(_wStates));
m_context->appEngine()->rootContext()->setContextProperty("humanReadableExecutionCode", QVariant::fromValue(std::get<0>(_code)));
m_context->appEngine()->rootContext()->setContextProperty("bytesCodeMapping", QVariant::fromValue(std::get<1>(_code)));
m_context->appEngine()->rootContext()->setContextProperty("contractCallReturnParameters", QVariant::fromValue(new QVariableDefinitionList(_returnParam)));
showDebuggerWindow();
}
void ClientModel::showDebugError(QString const& _error)
{
//TODO: change that to a signal
m_context->displayMessageDialog(tr("Debugger"), _error);
}
ExecutionResult ClientModel::deployContract(bytes const& _code)
{
u256 gasPrice = 10000000000000;
u256 gas = 125000;
u256 amount = 100;
Address contractAddress = m_client->transact(m_client->userAccount().secret(), amount, _code, gas, gasPrice);
ExecutionResult r = m_client->lastExecutionResult();
r.contractAddress = contractAddress;
return r;
}
ExecutionResult ClientModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr)
{
//bytes call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override;
m_client->transact(m_client->userAccount().secret(), _tr.value, _contract, _data, _tr.gas, _tr.gasPrice);
ExecutionResult r = m_client->lastExecutionResult();
r.contractAddress = _contract;
return r;
}

113
mix/ClientModel.h

@ -0,0 +1,113 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ClientModel.h
* @author Yann yann@ethdev.com
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#pragma once
#include <atomic>
#include "DebuggingStateWrapper.h"
#include "MixClient.h"
using AssemblyDebuggerData = std::tuple<QList<QObject*>, dev::mix::QQMLMap*>;
Q_DECLARE_METATYPE(AssemblyDebuggerData)
Q_DECLARE_METATYPE(dev::mix::ExecutionResult)
namespace dev
{
namespace mix
{
class AppContext;
/// Backend transaction config class
struct TransactionSettings
{
TransactionSettings(QString const& _functionId, u256 _value, u256 _gas, u256 _gasPrice):
functionId(_functionId), value(_value), gas(_gas), gasPrice(_gasPrice) {}
/// Contract function name
QString functionId;
/// Transaction value
u256 value;
/// Gas
u256 gas;
/// Gas price
u256 gasPrice;
/// Mapping from contract function parameter name to value
std::map<QString, u256> parameterValues;
};
/**
* @brief Ethereum state control
*/
class ClientModel: public QObject
{
Q_OBJECT
public:
ClientModel(AppContext* _context);
Q_PROPERTY(bool running MEMBER m_running NOTIFY stateChanged)
public slots:
/// Run the contract constructor and show debugger window.
void debugDeployment();
/// Setup state, run transaction sequence, show debugger for the last transaction
/// @param _state JS object with state configuration
void debugState(QVariantMap _state);
private slots:
/// Update UI with machine states result. Display a modal dialog.
void showDebugger(QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData());
/// Update UI with transaction run error.
void showDebugError(QString const& _error);
signals:
/// Transaction execution started
void runStarted();
/// Transaction execution completed successfully
void runComplete();
/// Transaction execution completed with error
/// @param _message Error message
void runFailed(QString const& _message);
/// Execution state changed
void stateChanged();
/// Show debugger window request
void showDebuggerWindow();
/// Emited when machine states are available.
void dataAvailable(QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData());
private:
void executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance);
ExecutionResult deployContract(bytes const& _code);
ExecutionResult callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
AppContext* m_context;
std::atomic<bool> m_running;
std::unique_ptr<MixClient> m_client;
};
}
}

3
mix/CodeEditorExtensionManager.cpp

@ -31,6 +31,7 @@
#include "AppContext.h"
#include "MixApplication.h"
#include "CodeModel.h"
#include "ClientModel.h"
#include "CodeHighlighter.h"
#include "CodeEditorExtensionManager.h"
@ -58,7 +59,7 @@ void CodeEditorExtensionManager::initExtensions()
std::shared_ptr<ConstantCompilationControl> output = std::make_shared<ConstantCompilationControl>(m_appContext);
std::shared_ptr<AssemblyDebuggerControl> debug = std::make_shared<AssemblyDebuggerControl>(m_appContext);
std::shared_ptr<StateListView> stateList = std::make_shared<StateListView>(m_appContext);
QObject::connect(debug.get(), &AssemblyDebuggerControl::runFailed, output.get(), &ConstantCompilationControl::displayError);
QObject::connect(m_appContext->clientModel(), &ClientModel::runFailed, output.get(), &ConstantCompilationControl::displayError);
QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight);
initExtension(output);

2
mix/CodeModel.cpp

@ -162,7 +162,7 @@ void CodeModel::onCompilationComplete(CompilationResult*_newResult)
m_result.reset(_newResult);
emit compilationComplete();
emit stateChanged();
if (m_result->successfull())
if (m_result->successful())
emit codeChanged();
}

8
mix/CodeModel.h

@ -69,7 +69,7 @@ class CompilationResult: public QObject
public:
/// Empty compilation result constructor
CompilationResult();
/// Successfull compilation result constructor
/// Successful compilation result constructor
CompilationResult(solidity::CompilerStack const& _compiler);
/// Failed compilation result constructor
CompilationResult(CompilationResult const& _prev, QString const& _compilerMessage);
@ -78,9 +78,9 @@ public:
QContractDefinition* contract() { return m_contract.get(); }
/// @returns contract definition
std::shared_ptr<QContractDefinition> sharedContract() { return m_contract; }
/// Indicates if the compilation was successfull
bool successfull() const { return m_successful; }
/// @returns compiler error message in case of unsuccessfull compilation
/// Indicates if the compilation was successful
bool successful() const { return m_successful; }
/// @returns compiler error message in case of unsuccessful compilation
QString compilerMessage() const { return m_compilerMessage; }
/// @returns contract bytecode
dev::bytes const& bytes() const { return m_bytes; }

2
mix/ConstantCompilationControl.cpp

@ -61,7 +61,7 @@ void ConstantCompilationControl::update()
QObject* status = m_view->findChild<QObject*>("status", Qt::FindChildrenRecursively);
QObject* content = m_view->findChild<QObject*>("content", Qt::FindChildrenRecursively);
if (result->successfull())
if (result->successful())
{
status->setProperty("text", "succeeded");
status->setProperty("color", "green");

5
mix/ContractCallDataEncoder.cpp

@ -27,6 +27,7 @@
#include <libsolidity/AST.h>
#include "QVariableDeclaration.h"
#include "QVariableDefinition.h"
#include "QFunctionDefinition.h"
#include "ContractCallDataEncoder.h"
using namespace dev;
using namespace dev::solidity;
@ -37,9 +38,9 @@ bytes ContractCallDataEncoder::encodedData()
return m_encodedData;
}
void ContractCallDataEncoder::encode(int _functionIndex)
void ContractCallDataEncoder::encode(QFunctionDefinition const* _function)
{
bytes i = jsToBytes(std::to_string(_functionIndex));
bytes i = _function->hash().asBytes();
m_encodedData.insert(m_encodedData.end(), i.begin(), i.end());
}

8
mix/ContractCallDataEncoder.h

@ -30,6 +30,10 @@ namespace dev
namespace mix
{
class QFunctionDefinition;
class QVariableDeclaration;
class QVariableDefinition;
/**
* @brief Encode/Decode data to be sent to a transaction or to be displayed in a view.
*/
@ -43,8 +47,8 @@ public:
void encode(QVariableDeclaration const* _dec, u256 _value);
/// Encode variable in order to be sent as parameter.
void encode(QVariableDeclaration const* _dec, bool _value);
/// Encode index of the function to call.
void encode(int _functionIndex);
/// Encode hash of the function to call.
void encode(QFunctionDefinition const* _function);
/// Decode variable in order to be sent to QML view.
QList<QVariableDefinition*> decode(QList<QVariableDeclaration*> _dec, bytes _value);
/// Get all encoded data encoded by encode function.

40
mix/DebuggingStateWrapper.h

@ -28,45 +28,13 @@
#include <libethereum/State.h>
#include <libethereum/Executive.h>
#include "QVariableDefinition.h"
#include "MixClient.h"
namespace dev
{
namespace mix
{
/**
* @brief Store information about a machine state.
*/
struct DebuggingState
{
uint64_t steps;
dev::Address cur;
dev::u256 curPC;
dev::eth::Instruction inst;
dev::bigint newMemSize;
dev::u256 gas;
dev::u256s stack;
dev::bytes memory;
dev::bigint gasCost;
std::map<dev::u256, dev::u256> storage;
std::vector<DebuggingState const*> levels;
};
/**
* @brief Store information about a machine states.
*/
struct DebuggingContent
{
QList<DebuggingState> machineStates;
bytes executionCode;
bytesConstRef executionData;
Address contractAddress;
bool contentAvailable;
QString message;
bytes returnValue;
QList<QVariableDefinition*> returnParameters;
};
/**
* @brief Contains the line nb of the assembly code and the corresponding index in the code bytes array.
*/
@ -151,14 +119,14 @@ public:
/// Get all previous steps.
QStringList levels();
/// Get the current processed machine state.
DebuggingState state() { return m_state; }
MachineState state() { return m_state; }
/// Set the current processed machine state.
void setState(DebuggingState _state) { m_state = _state; }
void setState(MachineState _state) { m_state = _state; }
/// Convert all machine state in human readable code.
static std::tuple<QList<QObject*>, QQMLMap*> getHumanReadableCode(bytes const& _code);
private:
DebuggingState m_state;
MachineState m_state;
bytes m_code;
bytes m_data;
};

5
mix/FileIo.cpp

@ -73,3 +73,8 @@ void FileIo::copyFile(QString const& _sourceUrl, QString const& _destUrl)
if (!QFile::copy(sourceUrl.path(), destUrl.path()))
error(tr("Error copying file %1 to %2").arg(_sourceUrl).arg(_destUrl));
}
QString FileIo::getHomePath() const
{
return QDir::homePath();
}

4
mix/FileIo.h

@ -33,6 +33,7 @@ namespace mix
class FileIo : public QObject
{
Q_OBJECT
Q_PROPERTY(QString homePath READ getHomePath CONSTANT);
signals:
/// Signalled in case of IO error
@ -47,6 +48,9 @@ public:
Q_INVOKABLE void writeFile(QString const& _url, QString const& _data);
/// Copy a file from _sourcePath to _destPath. Signals on failure.
Q_INVOKABLE void copyFile(QString const& _sourceUrl, QString const& _destUrl);
private:
QString getHomePath() const;
};
}

5
mix/MixApplication.cpp

@ -33,10 +33,9 @@ MixApplication::MixApplication(int _argc, char* _argv[]):
QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine()), m_appContext(new AppContext(m_engine.get()))
{
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
//QObject::connect(this, SIGNAL(lastWindowClosed()), context(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff
QObject::connect(this, SIGNAL(lastWindowClosed()), context(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff
m_engine->load(QUrl("qrc:/qml/main.qml"));
//m_engine->load(QUrl("qrc:/qml/ProjectModel.qml"));
m_appContext->loadProject();
m_appContext->appLoaded();
}
MixApplication::~MixApplication()

336
mix/MixClient.cpp

@ -0,0 +1,336 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file MixClient.cpp
* @author Yann yann@ethdev.com
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#include <vector>
#include <libdevcore/Exceptions.h>
#include <libethereum/BlockChain.h>
#include <libethereum/Transaction.h>
#include <libethereum/Executive.h>
#include <libethereum/ExtVM.h>
#include <libevm/VM.h>
#include "MixClient.h"
using namespace dev;
using namespace dev::eth;
using namespace dev::mix;
MixClient::MixClient():
m_userAccount(KeyPair::create())
{
resetState(10000000 * ether);
}
void MixClient::resetState(u256 _balance)
{
WriteGuard l(x_state);
m_state = eth::State(Address(), m_stateDB, BaseState::Empty);
m_state.addBalance(m_userAccount.address(), _balance);
}
void MixClient::executeTransaction(bytesConstRef _rlp, State& _state)
{
Executive execution(_state, LastHashes(), 0);
execution.setup(_rlp);
bytes code;
bytesConstRef data;
bool firstIteration = true;
std::vector<MachineState> machineStates;
std::vector<MachineState const*> levels;
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt)
{
VM& vm = *(VM*)voidVM;
ExtVM const& ext = *(ExtVM const*)voidExt;
if (firstIteration)
{
code = ext.code;
data = ext.data;
firstIteration = false;
}
if (levels.size() < ext.depth)
levels.push_back(&machineStates.back());
else
levels.resize(ext.depth);
machineStates.push_back(MachineState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(),
vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels}));
};
execution.go(onOp);
execution.finalize();
ExecutionResult d;
d.returnValue = execution.out().toVector();
d.machineStates = machineStates;
d.executionCode = code;
d.executionData = data;
d.contentAvailable = true;
d.message = "ok";
m_lastExecutionResult = d;
}
void MixClient::validateBlock(int _block) const
{
//TODO: throw exception here if _block != 0
(void)_block;
}
void MixClient::transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
{
WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret));
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
bytes rlp = t.rlp();
executeTransaction(&rlp, m_state);
}
Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice)
{
WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret));
eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret);
bytes rlp = t.rlp();
executeTransaction(&rlp, m_state);
return right160(sha3(rlpList(t.sender(), t.nonce())));
}
void MixClient::inject(bytesConstRef _rlp)
{
WriteGuard l(x_state);
executeTransaction(_rlp, m_state);
}
void MixClient::flushTransactions()
{
}
bytes MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
{
bytes out;
u256 n;
State temp;
{
ReadGuard lr(x_state);
temp = m_state;
n = temp.transactionsFrom(toAddress(_secret));
}
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
bytes rlp = t.rlp();
WriteGuard lw(x_state); //TODO: lock is required only for last executoin state
executeTransaction(&rlp, temp);
return m_lastExecutionResult.returnValue;
}
u256 MixClient::balanceAt(Address _a, int _block) const
{
validateBlock(_block);
ReadGuard l(x_state);
return m_state.balance(_a);
}
u256 MixClient::countAt(Address _a, int _block) const
{
validateBlock(_block);
ReadGuard l(x_state);
return m_state.transactionsFrom(_a);
}
u256 MixClient::stateAt(Address _a, u256 _l, int _block) const
{
validateBlock(_block);
ReadGuard l(x_state);
return m_state.storage(_a, _l);
}
bytes MixClient::codeAt(Address _a, int _block) const
{
validateBlock(_block);
ReadGuard l(x_state);
return m_state.code(_a);
}
std::map<u256, u256> MixClient::storageAt(Address _a, int _block) const
{
validateBlock(_block);
ReadGuard l(x_state);
return m_state.storage(_a);
}
eth::LogEntries MixClient::logs(unsigned _watchId) const
{
(void)_watchId;
return LogEntries();
}
eth::LogEntries MixClient::logs(eth::LogFilter const& _filter) const
{
(void)_filter;
return LogEntries();
}
unsigned MixClient::installWatch(eth::LogFilter const& _filter)
{
(void)_filter;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::installWatch"));
}
unsigned MixClient::installWatch(h256 _filterId)
{
(void)_filterId;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::installWatch"));
}
void MixClient::uninstallWatch(unsigned _watchId)
{
(void)_watchId;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::uninstallWatch"));
}
bool MixClient::peekWatch(unsigned _watchId) const
{
(void)_watchId;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::peekWatch"));
}
bool MixClient::checkWatch(unsigned _watchId)
{
(void)_watchId;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::checkWatch"));
}
h256 MixClient::hashFromNumber(unsigned _number) const
{
(void)_number;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::hashFromNumber"));
}
eth::BlockInfo MixClient::blockInfo(h256 _hash) const
{
(void)_hash;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::blockInfo"));
}
eth::BlockDetails MixClient::blockDetails(h256 _hash) const
{
(void)_hash;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::blockDetails"));
}
eth::Transaction MixClient::transaction(h256 _blockHash, unsigned _i) const
{
(void)_blockHash;
(void)_i;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::transaction"));
}
eth::BlockInfo MixClient::uncle(h256 _blockHash, unsigned _i) const
{
(void)_blockHash;
(void)_i;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::uncle"));
}
unsigned MixClient::number() const
{
return 0;
}
eth::Transactions MixClient::pending() const
{
return eth::Transactions();
}
eth::StateDiff MixClient::diff(unsigned _txi, h256 _block) const
{
(void)_txi;
(void)_block;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::diff"));
}
eth::StateDiff MixClient::diff(unsigned _txi, int _block) const
{
(void)_txi;
(void)_block;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::diff"));
}
Addresses MixClient::addresses(int _block) const
{
validateBlock(_block);
ReadGuard l(x_state);
Addresses ret;
for (auto const& i: m_state.addresses())
ret.push_back(i.first);
return ret;
}
u256 MixClient::gasLimitRemaining() const
{
ReadGuard l(x_state);
return m_state.gasLimitRemaining();
}
void MixClient::setAddress(Address _us)
{
WriteGuard l(x_state);
m_state.setAddress(_us);
}
Address MixClient::address() const
{
ReadGuard l(x_state);
return m_state.address();
}
void MixClient::setMiningThreads(unsigned _threads)
{
(void)_threads;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::setMiningThreads"));
}
unsigned MixClient::miningThreads() const
{
return 0;
}
void MixClient::startMining()
{
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::startMining"));
}
void MixClient::stopMining()
{
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::stopMining"));
}
bool MixClient::isMining()
{
return false;
}
eth::MineProgress MixClient::miningProgress() const
{
return eth::MineProgress();
}

125
mix/MixClient.h

@ -0,0 +1,125 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file MixClient.h
* @author Yann yann@ethdev.com
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#pragma once
#include <libethereum/Interface.h>
namespace dev
{
namespace mix
{
/**
* @brief Store information about a machine state.
*/
struct MachineState
{
uint64_t steps;
dev::Address cur;
dev::u256 curPC;
dev::eth::Instruction inst;
dev::bigint newMemSize;
dev::u256 gas;
dev::u256s stack;
dev::bytes memory;
dev::bigint gasCost;
std::map<dev::u256, dev::u256> storage;
std::vector<MachineState const*> levels;
};
/**
* @brief Store information about a machine states.
*/
struct ExecutionResult
{
std::vector<MachineState> machineStates;
bytes executionCode;
bytesConstRef executionData;
Address contractAddress;
bool contentAvailable;
std::string message;
bytes returnValue;
};
class MixClient: public dev::eth::Interface
{
public:
MixClient();
/// Reset state to the empty state with given balance.
void resetState(u256 _balance);
const KeyPair& userAccount() const { return m_userAccount; }
const ExecutionResult lastExecutionResult() const { ReadGuard l(x_state); return m_lastExecutionResult; }
//dev::eth::Interface
void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override;
Address transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) override;
void inject(bytesConstRef _rlp) override;
void flushTransactions() override;
bytes call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override;
u256 balanceAt(Address _a, int _block) const override;
u256 countAt(Address _a, int _block) const override;
u256 stateAt(Address _a, u256 _l, int _block) const override;
bytes codeAt(Address _a, int _block) const override;
std::map<u256, u256> storageAt(Address _a, int _block) const override;
eth::LogEntries logs(unsigned _watchId) const override;
eth::LogEntries logs(eth::LogFilter const& _filter) const override;
unsigned installWatch(eth::LogFilter const& _filter) override;
unsigned installWatch(h256 _filterId) override;
void uninstallWatch(unsigned _watchId) override;
bool peekWatch(unsigned _watchId) const override;
bool checkWatch(unsigned _watchId) override;
h256 hashFromNumber(unsigned _number) const override;
eth::BlockInfo blockInfo(h256 _hash) const override;
eth::BlockDetails blockDetails(h256 _hash) const override;
eth::Transaction transaction(h256 _blockHash, unsigned _i) const override;
eth::BlockInfo uncle(h256 _blockHash, unsigned _i) const override;
unsigned number() const override;
eth::Transactions pending() const override;
eth::StateDiff diff(unsigned _txi, h256 _block) const override;
eth::StateDiff diff(unsigned _txi, int _block) const override;
Addresses addresses(int _block) const override;
u256 gasLimitRemaining() const override;
void setAddress(Address _us) override;
Address address() const override;
void setMiningThreads(unsigned _threads) override;
unsigned miningThreads() const override;
void startMining() override;
void stopMining() override;
bool isMining() override;
eth::MineProgress miningProgress() const override;
private:
void executeTransaction(bytesConstRef _rlp, eth::State& _state);
void validateBlock(int _block) const;
KeyPair m_userAccount;
eth::State m_state;
OverlayDB m_stateDB;
mutable boost::shared_mutex x_state;
ExecutionResult m_lastExecutionResult;
};
}
}

4
mix/QFunctionDefinition.cpp

@ -20,12 +20,14 @@
*/
#include <libsolidity/AST.h>
#include <libdevcrypto/SHA3.h>
#include "QVariableDeclaration.h"
#include "QFunctionDefinition.h"
using namespace dev::solidity;
using namespace dev::mix;
QFunctionDefinition::QFunctionDefinition(dev::solidity::FunctionDefinition const* _f, int _index): QBasicNodeDefinition(_f), m_index(_index)
QFunctionDefinition::QFunctionDefinition(dev::solidity::FunctionDefinition const* _f, int _index): QBasicNodeDefinition(_f), m_index(_index), m_hash(dev::sha3(_f->getCanonicalSignature()))
{
std::vector<std::shared_ptr<VariableDeclaration>> parameters = _f->getParameterList().getParameters();
for (unsigned i = 0; i < parameters.size(); i++)

3
mix/QFunctionDefinition.h

@ -49,9 +49,12 @@ public:
QList<QVariableDeclaration*> returnParameters() const { return m_returnParameters; }
/// Get the index of this function on the contract ABI.
int index() const { return m_index; }
/// Get the hash of this function declaration on the contract ABI.
FixedHash<4> hash() const { return m_hash; }
private:
int m_index;
FixedHash<4> m_hash;
QList<QVariableDeclaration*> m_parameters;
QList<QVariableDeclaration*> m_returnParameters;
void initQParameters();

55
mix/Web3Server.cpp

@ -0,0 +1,55 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Web3Server.h.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <libdevcore/Exceptions.h>
#include "Web3Server.h"
using namespace dev::mix;
Web3Server::Web3Server(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _accounts, dev::eth::Interface* _client):
WebThreeStubServerBase(_conn, _accounts),
m_client(_client)
{
}
std::shared_ptr<dev::shh::Interface> Web3Server::face()
{
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::shh::Interface"));
}
dev::WebThreeNetworkFace* Web3Server::network()
{
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::WebThreeNetworkFace"));
}
std::string Web3Server::get(std::string const& _name, std::string const& _key)
{
std::string k(_name + "/" + _key);
return m_db[k];
}
void Web3Server::put(std::string const& _name, std::string const& _key, std::string const& _value)
{
std::string k(_name + "/" + _key);
m_db[k] = _value;
}

56
mix/Web3Server.h

@ -0,0 +1,56 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Web3Server.h
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#pragma once
#include <map>
#include <string>
#include <libweb3jsonrpc/WebThreeStubServerBase.h>
namespace dev
{
namespace mix
{
class Web3Server: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace
{
public:
Web3Server(jsonrpc::AbstractServerConnector& _conn, std::vector<dev::KeyPair> const& _accounts, dev::eth::Interface* _client);
private:
dev::eth::Interface* client() override { return m_client; }
std::shared_ptr<dev::shh::Interface> face() override;
dev::WebThreeNetworkFace* network() override;
dev::WebThreeStubDatabaseFace* db() override { return this; }
std::string get(std::string const& _name, std::string const& _key) override;
void put(std::string const& _name, std::string const& _key, std::string const& _value) override;
private:
dev::eth::Interface* m_client;
std::map<std::string, std::string> m_db;
};
}
}

1
mix/qml.qrc

@ -18,5 +18,6 @@
<file>qml/CodeEditor.qml</file>
<file>qml/CodeEditorView.qml</file>
<file>qml/js/ProjectModel.js</file>
<file>qml/WebPreview.qml</file>
</qresource>
</RCC>

2
mix/qml/CodeEditorView.qml

@ -44,7 +44,7 @@ Item {
onDocumentOpened: {
openDocument(document);
}
onProjectSaved: {
onProjectSaving: {
for (var i = 0; i < editorListModel.count; i++)
fileIo.writeFile(editorListModel.get(i).path, editors.itemAt(i).item.getText());
}

7
mix/qml/ProjectModel.qml

@ -15,7 +15,7 @@ Item {
signal projectClosed
signal projectLoaded
signal documentOpened(var document)
signal projectSaved
signal projectSaving(var projectData)
property bool isEmpty: (projectData === null)
readonly property string projectFileName: ".mix"
@ -35,10 +35,13 @@ Item {
function addExistingFile() { ProjectModelCode.addExistingFile(); }
function openDocument(documentId) { ProjectModelCode.openDocument(documentId); }
Component.onCompleted: {
Connections {
target: appContext
onAppLoaded: {
if (projectSettings.lastProjectPath)
loadProject(projectSettings.lastProjectPath)
}
}
NewProjectDialog {
id: newProjectDialog

2
mix/qml/StateDialog.qml

@ -122,7 +122,7 @@ Window {
var item = {
value: "0",
functionId: "",
gas: "1000000000000",
gas: "125000",
gasPrice: "100000"
};

8
mix/qml/StateList.qml

@ -29,6 +29,9 @@ Rectangle {
stateList.push(items[i])
}
}
onProjectSaving: {
projectData.states = stateList;
}
}
ListView {
@ -78,7 +81,7 @@ Rectangle {
function runState(index) {
var item = stateList[index];
debugModel.debugState(item);
clientModel.debugState(item);
}
function deleteState(index) {
@ -88,7 +91,6 @@ Rectangle {
}
function save() {
console.log(parent.id);
ProjectModel.saveProject();
}
}
@ -131,7 +133,7 @@ Rectangle {
id: addStateAction
text: "&Add State"
shortcut: "Ctrl+T"
enabled: codeModel.hasContract && !debugModel.running;
enabled: codeModel.hasContract && !clientModel.running;
onTriggered: stateListModel.addState();
}
}

81
mix/qml/WebPreview.qml

@ -0,0 +1,81 @@
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0
import QtQuick.Controls.Styles 1.1
Component {
Item {
signal editorTextChanged
function setText(text) {
codeEditor.text = text;
}
function getText() {
return codeEditor.text;
}
anchors.fill: parent
id: contentView
width: parent.width
height: parent.height * 0.7
Rectangle {
id: lineColumn
property int rowHeight: codeEditor.font.pixelSize + 3
color: "#202020"
width: 50
height: parent.height
Column {
y: -codeEditor.flickableItem.contentY + 4
width: parent.width
Repeater {
model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight))
delegate: Text {
id: text
color: codeEditor.textColor
font: codeEditor.font
width: lineColumn.width - 4
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
height: lineColumn.rowHeight
renderType: Text.NativeRendering
text: index + 1
}
}
}
}
TextArea {
id: codeEditor
textColor: "#EEE8D5"
style: TextAreaStyle {
backgroundColor: "#002B36"
}
anchors.left: lineColumn.right
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
wrapMode: TextEdit.NoWrap
frameVisible: false
height: parent.height
font.family: "Monospace"
font.pointSize: 12
width: parent.width
tabChangesFocus: false
Keys.onPressed: {
if (event.key === Qt.Key_Tab) {
codeEditor.insert(codeEditor.cursorPosition, "\t");
event.accepted = true;
}
}
onTextChanged: {
editorTextChanged();
}
}
}
}

2
mix/qml/js/ProjectModel.js

@ -22,7 +22,6 @@
function saveAll() {
saveProject();
projectSaved();
}
function createProject() {
@ -44,6 +43,7 @@ function closeProject() {
function saveProject() {
if (!isEmpty) {
projectSaving(projectData);
var json = JSON.stringify(projectData);
var projectFile = projectPath + projectFileName;
fileIo.writeFile(projectFile, json);

6
mix/qml/main.qml

@ -63,15 +63,15 @@ ApplicationWindow {
id: debugRunAction
text: "&Run"
shortcut: "F5"
enabled: codeModel.hasContract && !debugModel.running;
onTriggered: debugModel.debugDeployment();
enabled: codeModel.hasContract && !clientModel.running;
onTriggered: clientModel.debugDeployment();
}
Action {
id: debugResetStateAction
text: "Reset &State"
shortcut: "F6"
onTriggered: debugModel.resetState();
onTriggered: clientModel.resetState();
}
Action {

Loading…
Cancel
Save