subtly
10 years ago
141 changed files with 5660 additions and 2769 deletions
@ -0,0 +1,7 @@ |
|||
#!/bin/bash |
|||
|
|||
cd ethereumjs |
|||
export PATH=$PATH:$1:$2 |
|||
npm install |
|||
npm run-script build |
|||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,37 @@ |
|||
var assert = require('assert'); |
|||
var abi = require('../lib/abi.js'); |
|||
|
|||
describe('abi', function() { |
|||
describe('inputParser', function() { |
|||
it('should parse ...', function() { |
|||
|
|||
var desc = [{ |
|||
"name": "multiply", |
|||
"inputs": [ |
|||
{ |
|||
"name": "a", |
|||
"type": "uint256" |
|||
} |
|||
], |
|||
"outputs": [ |
|||
{ |
|||
"name": "d", |
|||
"type": "uint256" |
|||
} |
|||
] |
|||
}]; |
|||
|
|||
var iParser = abi.inputParser(desc); |
|||
assert.equal(iParser.multiply(1), "0x000000000000000000000000000000000000000000000000000000000000000001"); |
|||
|
|||
}); |
|||
}); |
|||
|
|||
|
|||
describe('outputParser', function() { |
|||
it('parse ...', function() { |
|||
|
|||
}); |
|||
}); |
|||
}); |
|||
|
@ -0,0 +1,18 @@ |
|||
require('es6-promise').polyfill(); |
|||
|
|||
var assert = require('assert'); |
|||
var web3 = require('../index.js'); |
|||
var u = require('./utils.js'); |
|||
web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080')); // TODO: create some mock provider
|
|||
|
|||
describe('web3', function() { |
|||
describe('db', function() { |
|||
it('should have all methods implemented', function() { |
|||
u.methodExists(web3.db, 'put'); |
|||
u.methodExists(web3.db, 'get'); |
|||
u.methodExists(web3.db, 'putString'); |
|||
u.methodExists(web3.db, 'getString'); |
|||
}); |
|||
}); |
|||
}); |
|||
|
@ -0,0 +1,42 @@ |
|||
require('es6-promise').polyfill(); |
|||
|
|||
var assert = require('assert'); |
|||
var web3 = require('../index.js'); |
|||
var u = require('./utils.js'); |
|||
web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080')); // TODO: create some mock provider
|
|||
|
|||
describe('web3', function() { |
|||
describe('eth', function() { |
|||
it('should have all methods implemented', function() { |
|||
u.methodExists(web3.eth, 'balanceAt'); |
|||
u.methodExists(web3.eth, 'stateAt'); |
|||
u.methodExists(web3.eth, 'storageAt'); |
|||
u.methodExists(web3.eth, 'countAt'); |
|||
u.methodExists(web3.eth, 'codeAt'); |
|||
u.methodExists(web3.eth, 'transact'); |
|||
u.methodExists(web3.eth, 'call'); |
|||
u.methodExists(web3.eth, 'block'); |
|||
u.methodExists(web3.eth, 'transaction'); |
|||
u.methodExists(web3.eth, 'uncle'); |
|||
u.methodExists(web3.eth, 'compilers'); |
|||
u.methodExists(web3.eth, 'lll'); |
|||
u.methodExists(web3.eth, 'solidity'); |
|||
u.methodExists(web3.eth, 'serpent'); |
|||
u.methodExists(web3.eth, 'logs'); |
|||
}); |
|||
|
|||
it('should have all properties implemented', function () { |
|||
u.propertyExists(web3.eth, 'coinbase'); |
|||
u.propertyExists(web3.eth, 'listening'); |
|||
u.propertyExists(web3.eth, 'mining'); |
|||
u.propertyExists(web3.eth, 'gasPrice'); |
|||
u.propertyExists(web3.eth, 'account'); |
|||
u.propertyExists(web3.eth, 'accounts'); |
|||
u.propertyExists(web3.eth, 'peerCount'); |
|||
u.propertyExists(web3.eth, 'defaultBlock'); |
|||
u.propertyExists(web3.eth, 'number'); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
|
@ -0,0 +1,2 @@ |
|||
--reporter Spec |
|||
|
@ -0,0 +1,19 @@ |
|||
require('es6-promise').polyfill(); |
|||
|
|||
var assert = require('assert'); |
|||
var web3 = require('../index.js'); |
|||
var u = require('./utils.js'); |
|||
web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080')); // TODO: create some mock provider
|
|||
|
|||
describe('web3', function() { |
|||
describe('shh', function() { |
|||
it('should have all methods implemented', function() { |
|||
u.methodExists(web3.shh, 'post'); |
|||
u.methodExists(web3.shh, 'newIdentity'); |
|||
u.methodExists(web3.shh, 'haveIdentity'); |
|||
u.methodExists(web3.shh, 'newGroup'); |
|||
u.methodExists(web3.shh, 'addToGroup'); |
|||
}); |
|||
}); |
|||
}); |
|||
|
@ -0,0 +1,15 @@ |
|||
var assert = require('assert'); |
|||
|
|||
var methodExists = function (object, method) { |
|||
assert.equal('function', typeof object[method], 'method ' + method + ' is not implemented'); |
|||
}; |
|||
|
|||
var propertyExists = function (object, property) { |
|||
assert.equal('object', typeof object[property], 'property ' + property + ' is not implemented'); |
|||
}; |
|||
|
|||
module.exports = { |
|||
methodExists: methodExists, |
|||
propertyExists: propertyExists |
|||
}; |
|||
|
@ -0,0 +1,18 @@ |
|||
require('es6-promise').polyfill(); |
|||
|
|||
var assert = require('assert'); |
|||
var web3 = require('../index.js'); |
|||
var u = require('./utils.js'); |
|||
web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080')); // TODO: create some mock provider
|
|||
|
|||
describe('web3', function() { |
|||
it('should have all methods implemented', function() { |
|||
u.methodExists(web3, 'sha3'); |
|||
u.methodExists(web3, 'toAscii'); |
|||
u.methodExists(web3, 'fromAscii'); |
|||
u.methodExists(web3, 'toFixed'); |
|||
u.methodExists(web3, 'fromFixed'); |
|||
u.methodExists(web3, 'offset'); |
|||
}); |
|||
}); |
|||
|
@ -1,7 +1,7 @@ |
|||
<RCC version="1.0"> |
|||
<qresource prefix="/js"> |
|||
<file>es6-promise-2.0.0.js</file> |
|||
<file>setup.js</file> |
|||
<file alias="ethereum.js">ethereumjs/dist/ethereum.js</file> |
|||
</qresource> |
|||
<RCC> |
|||
<qresource prefix="/js"> |
|||
<file>es6-promise-2.0.0.js</file> |
|||
<file>setup.js</file> |
|||
<file alias="webthree.js">ethereumjs/dist/ethereum.js</file> |
|||
</qresource> |
|||
</RCC> |
|||
|
@ -0,0 +1,665 @@ |
|||
/*
|
|||
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::LocalisedLogEntry 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)); |
|||
res["number"] = _e.number; |
|||
return res; |
|||
} |
|||
|
|||
static Json::Value toJson(dev::eth::LocalisedLogEntries const& _es) // commented to avoid warning. Uncomment once in use @ poC-7.
|
|||
{ |
|||
Json::Value res; |
|||
for (dev::eth::LocalisedLogEntry 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; |
|||
} |
|||
|
|||
Json::Value WebThreeStubServerBase::eth_changed(int const& _id) |
|||
{ |
|||
return toJson(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; |
|||
} |
|||
|
@ -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; |
|||
struct 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 Json::Value 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
|
@ -0,0 +1,4 @@ |
|||
#!/bin/bash |
|||
|
|||
git checkout master && git merge --no-ff $1+ && git push && git tag -f $1 && git push --tags -f |
|||
|
@ -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); |
|||
} |
|||
|
|||
} |
|||
} |
@ -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); |
|||
}; |
|||
|
|||
} |
|||
} |
@ -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; |
|||
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) |
|||
{ |
|||
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; |
|||
} |
|||
|
@ -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; |
|||
}; |
|||
|
|||
} |
|||
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue