Browse Source

made contract accessible for ethereum.js api

cl-refactor
arkpar 10 years ago
parent
commit
4c4deb3dd8
  1. 2
      libjsqrc/ethereumjs/dist/ethereum.js
  2. 14
      mix/CMakeLists.txt
  3. 33
      mix/ClientModel.cpp
  4. 21
      mix/ClientModel.h
  5. 14
      mix/CodeModel.cpp
  6. 6
      mix/CodeModel.h
  7. 6
      mix/MixClient.cpp
  8. 1
      mix/MixClient.h
  9. 22
      mix/qml/MainContent.qml
  10. 31
      mix/qml/WebPreview.qml
  11. 10
      mix/qml/html/WebContainer.html
  12. 13
      mix/qml/main.qml

2
libjsqrc/ethereumjs/dist/ethereum.js

@ -1465,10 +1465,8 @@ var WebSocketProvider = function(host) {
/// Response for the call will be received by ws.onmessage
/// @param payload is inner message object
WebSocketProvider.prototype.send = function(payload) {
console.log("wsSend");
if (this.ready) {
var data = JSON.stringify(payload);
console.log(payload);
this.ws.send(data);
} else {

14
mix/CMakeLists.txt

@ -20,10 +20,10 @@ file(GLOB HEADERS "*.h")
set(EXECUTABLE mix)
if ("${Qt5WebEngine_VERSION_STRING}" VERSION_GREATER "5.3.0")
set (ETH_HAVE_WEBENGINE TRUE)
qt5_add_resources(UI_RESOURCES web.qrc)
set (ETH_HAVE_WEBENGINE TRUE)
qt5_add_resources(UI_RESOURCES web.qrc)
else()
qt5_add_resources(UI_RESOURCES noweb.qrc)
qt5_add_resources(UI_RESOURCES noweb.qrc)
endif()
@ -47,12 +47,12 @@ target_link_libraries(${EXECUTABLE} lll)
target_link_libraries(${EXECUTABLE} solidity)
target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} jsqrc)
target_link_libraries(${EXECUTABLE} web3jsonrpc)
if (${ETH_HAVE_WEBENGINE})
add_definitions(-DETH_HAVE_WEBENGINE)
target_link_libraries(${EXECUTABLE} Qt5::WebEngine)
target_link_libraries(${EXECUTABLE} jsqrc)
target_link_libraries(${EXECUTABLE} web3jsonrpc)
add_definitions(-DETH_HAVE_WEBENGINE)
target_link_libraries(${EXECUTABLE} Qt5::WebEngine)
endif()
# eth_install_executable is defined in cmake/EthExecutableHelper.cmake

33
mix/ClientModel.cpp

@ -24,7 +24,7 @@
#include <QQmlApplicationEngine>
#include <libdevcore/CommonJS.h>
#include <libethereum/Transaction.h>
#include "ClientModel.h"
#include <libqwebthree/QWebThree.h>
#include "AppContext.h"
#include "DebuggingStateWrapper.h"
#include "QContractDefinition.h"
@ -33,13 +33,15 @@
#include "CodeModel.h"
#include "ClientModel.h"
#include "QEther.h"
#include "Web3Server.h"
#include "ClientModel.h"
using namespace dev;
using namespace dev::eth;
using namespace dev::mix;
ClientModel::ClientModel(AppContext* _context):
m_context(_context), m_running(false)
m_context(_context), m_running(false), m_qWebThree(nullptr)
{
qRegisterMetaType<QBigInt*>("QBigInt*");
qRegisterMetaType<QEther*>("QEther*");
@ -53,9 +55,30 @@ ClientModel::ClientModel(AppContext* _context):
connect(this, &ClientModel::dataAvailable, this, &ClientModel::showDebugger, Qt::QueuedConnection);
m_client.reset(new MixClient());
m_qWebThree = new QWebThree(this);
m_qWebThreeConnector.reset(new QWebThreeConnector());
m_qWebThreeConnector->setQWeb(m_qWebThree);
m_web3Server.reset(new Web3Server(*m_qWebThreeConnector.get(), std::vector<dev::KeyPair> { m_client->userAccount() }, m_client.get()));
connect(m_qWebThree, &QWebThree::response, this, &ClientModel::apiResponse);
_context->appEngine()->rootContext()->setContextProperty("clientModel", this);
}
ClientModel::~ClientModel()
{
}
void ClientModel::apiRequest(const QString& _message)
{
m_qWebThree->postMessage(_message);
}
QString ClientModel::contractAddress() const
{
QString address = QString::fromStdString(dev::toJS(m_client->lastContractAddress()));
return address;
}
void ClientModel::debugDeployment()
{
executeSequence(std::vector<TransactionSettings>(), 10000000 * ether);
@ -195,9 +218,11 @@ ExecutionResult ClientModel::deployContract(bytes const& _code)
u256 gas = 125000;
u256 amount = 100;
Address contractAddress = m_client->transact(m_client->userAccount().secret(), amount, _code, gas, gasPrice);
Address lastAddress = m_client->lastContractAddress();
Address newAddress = m_client->transact(m_client->userAccount().secret(), amount, _code, gas, gasPrice);
ExecutionResult r = m_client->lastExecutionResult();
r.contractAddress = contractAddress;
if (newAddress != lastAddress)
contractAddressChanged();
return r;
}

21
mix/ClientModel.h

@ -32,12 +32,16 @@ using AssemblyDebuggerData = std::tuple<QList<QObject*>, dev::mix::QQMLMap*>;
Q_DECLARE_METATYPE(AssemblyDebuggerData)
Q_DECLARE_METATYPE(dev::mix::ExecutionResult)
class QWebThree;
class QWebThreeConnector;
namespace dev
{
namespace mix
{
class AppContext;
class Web3Server;
/// Backend transaction config class
struct TransactionSettings
@ -67,10 +71,16 @@ class ClientModel: public QObject
public:
ClientModel(AppContext* _context);
~ClientModel();
/// @returns true if currently executing contract code
Q_PROPERTY(bool running MEMBER m_running NOTIFY stateChanged)
/// @returns address of the last executed contract
Q_PROPERTY(QString contractAddress READ contractAddress NOTIFY contractAddressChanged)
public slots:
/// ethereum.js RPC request entry point
/// @param _message RPC request in Json format
void apiRequest(QString const& _message);
/// Run the contract constructor and show debugger window.
void debugDeployment();
/// Setup state, run transaction sequence, show debugger for the last transaction
@ -91,15 +101,21 @@ signals:
/// Transaction execution completed with error
/// @param _message Error message
void runFailed(QString const& _message);
/// Contract address changed
void contractAddressChanged();
/// Execution state changed
void stateChanged();
/// Show debugger window request
void showDebuggerWindow();
/// ethereum.js RPC response ready
/// @param _message RPC response in Json format
void apiResponse(QString const& _message);
/// 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:
QString contractAddress() const;
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);
@ -107,6 +123,9 @@ private:
AppContext* m_context;
std::atomic<bool> m_running;
std::unique_ptr<MixClient> m_client;
QWebThree* m_qWebThree;
std::unique_ptr<QWebThreeConnector> m_qWebThreeConnector;
std::unique_ptr<Web3Server> m_web3Server;
};
}

14
mix/CodeModel.cpp

@ -26,6 +26,7 @@
#include <QtQml>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/SourceReferenceFormatter.h>
#include <libsolidity/InterfaceHandler.h>
#include <libevmcore/Instruction.h>
#include "QContractDefinition.h"
#include "QFunctionDefinition.h"
@ -55,9 +56,12 @@ CompilationResult::CompilationResult(const dev::solidity::CompilerStack& _compil
{
if (!_compiler.getContractNames().empty())
{
m_contract.reset(new QContractDefinition(&_compiler.getContractDefinition(std::string())));
auto const& contractDefinition = _compiler.getContractDefinition(std::string());
m_contract.reset(new QContractDefinition(&contractDefinition));
m_bytes = _compiler.getBytecode();
m_assemblyCode = QString::fromStdString(dev::eth::disassemble(m_bytes));
dev::solidity::InterfaceHandler interfaceHandler;
m_contractDefinition = QString::fromStdString(*interfaceHandler.getABIInterface(contractDefinition));
}
else
m_contract.reset(new QContractDefinition());
@ -71,6 +75,7 @@ CompilationResult::CompilationResult(CompilationResult const& _prev, QString con
m_compilerMessage(_compilerMessage),
m_bytes(_prev.m_bytes),
m_assemblyCode(_prev.m_assemblyCode),
m_contractDefinition(_prev.m_contractDefinition),
m_codeHighlighter(_prev.m_codeHighlighter)
{}
@ -156,14 +161,19 @@ void CodeModel::runCompilationJob(int _jobId, QString const& _code)
emit compilationCompleteInternal(result.release());
}
void CodeModel::onCompilationComplete(CompilationResult*_newResult)
void CodeModel::onCompilationComplete(CompilationResult* _newResult)
{
m_compiling = false;
bool contractChanged = m_result->contractDefinition() != _newResult->contractDefinition();
m_result.reset(_newResult);
emit compilationComplete();
emit stateChanged();
if (m_result->successful())
{
emit codeChanged();
if (contractChanged)
emit contractDefinitionChanged();
}
}
bool CodeModel::hasContract() const

6
mix/CodeModel.h

@ -67,6 +67,7 @@ class CompilationResult: public QObject
Q_PROPERTY(QContractDefinition* contract READ contract)
Q_PROPERTY(QString compilerMessage READ compilerMessage CONSTANT)
Q_PROPERTY(bool successful READ successful CONSTANT)
Q_PROPERTY(QString contractDefinition READ contractDefinition CONSTANT)
public:
/// Empty compilation result constructor
@ -88,6 +89,8 @@ public:
dev::bytes const& bytes() const { return m_bytes; }
/// @returns contract bytecode in human-readable form
QString assemblyCode() const { return m_assemblyCode; }
/// @returns contract definition in JSON format
QString contractDefinition() const { return m_contractDefinition; }
/// Get code highlighter
std::shared_ptr<CodeHighlighter> codeHighlighter() { return m_codeHighlighter; }
@ -98,6 +101,7 @@ private:
QString m_compilerMessage; ///< @todo: use some structure here
dev::bytes m_bytes;
QString m_assemblyCode;
QString m_contractDefinition;
std::shared_ptr<CodeHighlighter> m_codeHighlighter;
friend class CodeModel;
@ -137,6 +141,8 @@ signals:
void scheduleCompilationJob(int _jobId, QString const& _content);
/// Emitted if there are any changes in the code model
void codeChanged();
/// Emitted if there are any changes in the contract interface
void contractDefinitionChanged();
/// Emitted on compilation complete. Internal
void compilationCompleteInternal(CompilationResult* _newResult);

6
mix/MixClient.cpp

@ -88,6 +88,7 @@ void MixClient::executeTransaction(bytesConstRef _rlp, State& _state)
d.executionData = data;
d.contentAvailable = true;
d.message = "ok";
d.contractAddress = m_lastExecutionResult.contractAddress;
m_lastExecutionResult = d;
}
@ -113,7 +114,9 @@ Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init,
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())));
Address address = right160(sha3(rlpList(t.sender(), t.nonce())));
m_lastExecutionResult.contractAddress = address;
return address;
}
void MixClient::inject(bytesConstRef _rlp)
@ -128,7 +131,6 @@ 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;
{

1
mix/MixClient.h

@ -70,6 +70,7 @@ public:
void resetState(u256 _balance);
const KeyPair& userAccount() const { return m_userAccount; }
const ExecutionResult lastExecutionResult() const { ReadGuard l(x_state); return m_lastExecutionResult; }
const Address lastContractAddress() const { ReadGuard l(x_state); return m_lastExecutionResult.contractAddress; }
//dev::eth::Interface
void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override;

22
mix/qml/MainContent.qml

@ -17,26 +17,30 @@ Rectangle {
anchors.fill: parent
id: root
function toggleRightView()
{
property alias rightViewVisible : rightView.visible
property alias webViewVisible : webPreview.visible
function toggleRightView() {
if (!rightView.visible)
rightView.show();
else
rightView.hide();
}
function ensureRightView()
{
function ensureRightView() {
if (!rightView.visible)
rightView.show();
}
function hideRightView()
{
function hideRightView() {
if (rightView.visible)
rightView.hide();
}
function toggleWebPreview() {
webPreview.visible = !webPreview.visible;
}
CodeEditorExtensionManager {
headerView: headerPaneTabs;
rightView: rightPaneTabs;
@ -102,11 +106,13 @@ Rectangle {
CodeEditorView {
height: parent.height * 0.6
anchors.top: parent.top
width: parent.width
Layout.fillWidth: true
Layout.fillHeight: true
}
WebPreview {
id: webPreview
height: parent.height * 0.4
width: parent.width
Layout.fillWidth: true
}
}
}

31
mix/qml/WebPreview.qml

@ -5,7 +5,7 @@ import QtQuick.Controls 1.0
import QtQuick.Controls.Styles 1.1
import QtWebEngine 1.0
import Qt.WebSockets 1.0
import QtWebEngine.experimental 1.0
//import QtWebEngine.experimental 1.0
Item {
id: webPreview
@ -17,14 +17,20 @@ Item {
pendingPageUrl = url;
else {
pendingPageUrl = "";
updateContract();
webView.runJavaScript("loadPage(\"" + url + "\")");
}
}
function reload() {
updateContract();
webView.runJavaScript("reloadPage()");
}
function updateContract() {
webView.runJavaScript("updateContract(\"" + clientModel.contractAddress + "\", " + codeModel.code.contractDefinition + ")");
}
function reloadOnSave() {
if (autoReloadOnSave.checked)
reload();
@ -53,13 +59,21 @@ Item {
}
}
Connections {
target: clientModel
onContractAddressChanged: reload();
}
Connections {
target: codeModel
onContractDefinitionChanged: reload();
}
Connections {
target: projectModel
onProjectSaved : reloadOnSave();
onDocumentSaved: reloadOnSave();
onDocumentAdded: {
console.log("added")
console.log(documentId)
var document = projectModel.getDocument(documentId)
if (document.isHtml)
pageListModel.append(document);
@ -98,7 +112,12 @@ Item {
onClientConnected:
{
webSocket.onTextMessageReceived.connect(function(message) {
console.log("rpc: " + message);
console.log("rpc_request: " + message);
clientModel.apiRequest(message);
});
clientModel.onApiResponse.connect(function(message) {
console.log("rpc_response: " + message);
webSocket.sendTextMessage(message);
});
}
}
@ -134,8 +153,8 @@ Item {
Layout.fillWidth: true
Layout.fillHeight: true
id: webView
experimental.settings.localContentCanAccessFileUrls: true
experimental.settings.localContentCanAccessRemoteUrls: true
//experimental.settings.localContentCanAccessFileUrls: true
//experimental.settings.localContentCanAccessRemoteUrls: true
onJavaScriptConsoleMessage: {
console.log(sourceID + ":" + lineNumber + ":" + message);
}

10
mix/qml/html/WebContainer.html

@ -16,11 +16,19 @@ reloadPage = function() {
preview.contentWindow.location.reload();
};
updateContract = function(address, definition) {
if (window.web3) {
window.contractAddress = address;
window.contractDefinition = definition;
window.contract = window.web3.eth.contract(address, definition);
}
};
init = function(url) {
web3 = require('web3');
web3.setProvider(new web3.providers.WebSocketProvider(url));
window.web3 = web3;
}
};
</script>

13
mix/qml/main.qml

@ -88,10 +88,21 @@ ApplicationWindow {
onTriggered: clientModel.resetState();
}
Action {
id: toggleWebPreview
text: "Show/Hide web view"
shortcut: "F2"
checkable: true
checked: mainContent.webViewVisible
onTriggered: mainContent.toggleWebPreview();
}
Action {
id: showHideRightPanel
text: "Show/Hide right view"
text: "Show right view"
shortcut: "F7"
checkable: true
checked: mainContent.rightViewVisible
onTriggered: mainContent.toggleRightView();
}

Loading…
Cancel
Save