From 36bedf6a7998b0dbd48800998e9179789d0a3e1c Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Sat, 21 Feb 2015 13:41:25 +0100 Subject: [PATCH 001/228] checking jsonrpccpp version when looking for library. version 0.4 exact required --- cmake/EthDependencies.cmake | 9 +----- ...sonRpcCpp.cmake => Findjson_rpc_cpp.cmake} | 29 +++++++++++++++++-- 2 files changed, 27 insertions(+), 11 deletions(-) rename cmake/{FindJsonRpcCpp.cmake => Findjson_rpc_cpp.cmake} (65%) diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index 3f6cbcd77..1b21280cb 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -45,16 +45,9 @@ find_package (Jsoncpp 0.60 REQUIRED) message(" - Jsoncpp header: ${JSONCPP_INCLUDE_DIRS}") message(" - Jsoncpp lib : ${JSONCPP_LIBRARIES}") -# TODO the JsonRpcCpp package does not yet check for correct version number -# json-rpc-cpp support is currently not mandatory -# TODO make headless client optional # TODO get rid of -DETH_JSONRPC if (JSONRPC) - - find_package (JsonRpcCpp 0.3.2) - if (NOT JSON_RPC_CPP_FOUND) - message (FATAL_ERROR "JSONRPC 0.3.2. not found") - endif() + find_package (json_rpc_cpp 0.4 EXACT REQUIRED) message (" - json-rpc-cpp header: ${JSON_RPC_CPP_INCLUDE_DIRS}") message (" - json-rpc-cpp lib : ${JSON_RPC_CPP_LIBRARIES}") add_definitions(-DETH_JSONRPC) diff --git a/cmake/FindJsonRpcCpp.cmake b/cmake/Findjson_rpc_cpp.cmake similarity index 65% rename from cmake/FindJsonRpcCpp.cmake rename to cmake/Findjson_rpc_cpp.cmake index 2ca176f68..8b705aeeb 100644 --- a/cmake/FindJsonRpcCpp.cmake +++ b/cmake/Findjson_rpc_cpp.cmake @@ -10,6 +10,11 @@ # JSON_RPC_CPP_SERVER_LIBRARIES, the libraries needed to use json-rpc-cpp-server # JSON_RPC_CPP_CLIENT_LIBRARIES, the libraries needed to use json-rpc-cpp-client # JSON_RCP_CPP_FOUND, If false, do not try to use json-rpc-cpp. +# JSON_RPC_CPP_VERSION, version of library +# JSON_RPC_CPP_VERSION_MAJOR +# JSON_RPC_CPP_VERSION_MINOR +# JSON_RPC_CPP_VERSION_PATCH + # only look in default directories find_path( @@ -90,10 +95,28 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") endif() +if (JSON_RPC_CPP_INCLUDE_DIR) + set (JSON_RPC_CPP_VERSION_HEADER "${JSON_RPC_CPP_INCLUDE_DIR}/jsonrpccpp/version.h") + if (EXISTS ${JSON_RPC_CPP_VERSION_HEADER}) + file (STRINGS ${JSON_RPC_CPP_VERSION_HEADER} JSON_RPC_CPP_VERSION_MAJOR REGEX "^#define JSONRPC_CPP_MAJOR_VERSION[ \t]+[0-9]+$") + file (STRINGS ${JSON_RPC_CPP_VERSION_HEADER} JSON_RPC_CPP_VERSION_MINOR REGEX "^#define JSONRPC_CPP_MINOR_VERSION[ \t]+[0-9]+$") + file (STRINGS ${JSON_RPC_CPP_VERSION_HEADER} JSON_RPC_CPP_VERSION_PATCH REGEX "^#define JSONRPC_CPP_PATCH_VERSION[ \t]+[0-9]+$") + string (REGEX REPLACE "^#define JSONRPC_CPP_MAJOR_VERSION[ \t]+([0-9]+)" "\\1" JSON_RPC_CPP_VERSION_MAJOR ${JSON_RPC_CPP_VERSION_MAJOR}) + string (REGEX REPLACE "^#define JSONRPC_CPP_MINOR_VERSION[ \t]+([0-9]+)" "\\1" JSON_RPC_CPP_VERSION_MINOR ${JSON_RPC_CPP_VERSION_MINOR}) + string (REGEX REPLACE "^#define JSONRPC_CPP_PATCH_VERSION[ \t]+([0-9]+)" "\\1" JSON_RPC_CPP_VERSION_PATCH ${JSON_RPC_CPP_VERSION_PATCH}) + set (JSON_RPC_CPP_VERSION ${JSON_RPC_CPP_VERSION_MAJOR}.${JSON_RPC_CPP_VERSION_MINOR}.${JSON_RPC_CPP_VERSION_PATCH}) + endif() +endif() + # handle the QUIETLY and REQUIRED arguments and set JSON_RPC_CPP_FOUND to TRUE # if all listed variables are TRUE, hide their existence from configuration view include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(json_rpc_cpp DEFAULT_MSG - JSON_RPC_CPP_COMMON_LIBRARY JSON_RPC_CPP_SERVER_LIBRARY JSON_RPC_CPP_CLIENT_LIBRARY JSON_RPC_CPP_INCLUDE_DIR) -mark_as_advanced (JSON_RPC_CPP_COMMON_LIBRARY JSON_RPC_CPP_SERVER_LIBRARY JSON_RPC_CPP_CLIENT_LIBRARY JSON_RPC_CPP_INCLUDE_DIR) + +find_package_handle_standard_args( + json_rpc_cpp + REQUIRED_VARS JSON_RPC_CPP_INCLUDE_DIR JSON_RPC_CPP_COMMON_LIBRARY JSON_RPC_CPP_SERVER_LIBRARY JSON_RPC_CPP_CLIENT_LIBRARY + VERSION_VAR JSON_RPC_CPP_VERSION +) + +mark_as_advanced (JSON_RPC_CPP_INCLUDE_DIR JSON_RPC_CPP_COMMON_LIBRARY JSON_RPC_CPP_SERVER_LIBRARY JSON_RPC_CPP_CLIENT_LIBRARY) From 5a9042640fca39c5ad43ecccd974d4c59ab09b42 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 19 Mar 2015 17:32:21 +0100 Subject: [PATCH 002/228] Error annotation --- mix/qml/Debugger.qml | 2 +- mix/qml/WebCodeEditor.qml | 13 ++++++ mix/qml/html/cm/solarized.css | 5 ++ mix/qml/html/codeeditor.js | 71 +++++++++++++++++++++-------- mix/qml/js/ErrorLocationFormater.js | 5 +- 5 files changed, 75 insertions(+), 21 deletions(-) diff --git a/mix/qml/Debugger.qml b/mix/qml/Debugger.qml index c4b6502b0..1a823e276 100644 --- a/mix/qml/Debugger.qml +++ b/mix/qml/Debugger.qml @@ -40,7 +40,7 @@ Rectangle { var errorInfo = ErrorLocationFormater.extractErrorInfo(compilationErrorMessage, false); errorLocation.text = errorInfo.errorLocation; errorDetail.text = errorInfo.errorDetail; - errorLine.text = errorInfo.errorLine; + errorLine.text = errorInfo.line; } function update(data, giveFocus) diff --git a/mix/qml/WebCodeEditor.qml b/mix/qml/WebCodeEditor.qml index c424eeddd..e77da0f45 100644 --- a/mix/qml/WebCodeEditor.qml +++ b/mix/qml/WebCodeEditor.qml @@ -4,6 +4,7 @@ import QtQuick.Layouts 1.0 import QtQuick.Controls.Styles 1.1 import QtWebEngine 1.0 import QtWebEngine.experimental 1.0 +import "js/ErrorLocationFormater.js" as ErrorLocationFormater Item { signal editorTextChanged @@ -83,7 +84,19 @@ Item { runJavaScript("getTextChanged()", function(result) { }); pollTimer.running = true; syncClipboard(); + if (currentMode === "solidity") + { + codeModel.onCompilationComplete.connect(function(){ + runJavaScript("compilationComplete()", function(result) { }); + }); + + codeModel.onCompilationError.connect(function(error){ + var errorInfo = ErrorLocationFormater.extractErrorInfo(error, false); + runJavaScript("compilationError('" + errorInfo.line + "', '" + errorInfo.column + "', '" + errorInfo.errorDetail + "')", function(result) { }); + }); + } parent.changeGeneration(); + } } diff --git a/mix/qml/html/cm/solarized.css b/mix/qml/html/cm/solarized.css index f07b8b43b..9f324241a 100644 --- a/mix/qml/html/cm/solarized.css +++ b/mix/qml/html/cm/solarized.css @@ -169,3 +169,8 @@ view-port background: rgba(255, 255, 255, 0.10); } +/* Error annotation */ +.CodeMirror-errorannotation { + background: red; +} + diff --git a/mix/qml/html/codeeditor.js b/mix/qml/html/codeeditor.js index 25c20ab2d..ea04f891b 100644 --- a/mix/qml/html/codeeditor.js +++ b/mix/qml/html/codeeditor.js @@ -22,23 +22,23 @@ editor.on("change", function(eMirror, object) { var mac = /Mac/.test(navigator.platform); if (mac === true) { -editor.setOption("extraKeys", { - "Cmd-V": function(cm) { - cm.replaceSelection(clipboard); - }, - "Cmd-X": function(cm) { - window.document.execCommand("cut"); - }, - "Cmd-C": function(cm) { - window.document.execCommand("copy"); - }}); + editor.setOption("extraKeys", { + "Cmd-V": function(cm) { + cm.replaceSelection(clipboard); + }, + "Cmd-X": function(cm) { + window.document.execCommand("cut"); + }, + "Cmd-C": function(cm) { + window.document.execCommand("copy"); + }}); } makeMarker = function() { - var marker = document.createElement("div"); - marker.style.color = "#822"; - marker.innerHTML = "●"; - return marker; + var marker = document.createElement("div"); + marker.style.color = "#822"; + marker.innerHTML = "●"; + return marker; }; toggleBreakpointLine = function(n) { @@ -77,9 +77,9 @@ getBreakpoints = function() { if (line.gutterMarkers && line.gutterMarkers["breakpoints"]) { var l = doc.getLineNumber(line); locations.push({ - start: editor.indexFromPos({ line: l, ch: 0}), - end: editor.indexFromPos({ line: l + 1, ch: 0}) - });; + start: editor.indexFromPos({ line: l, ch: 0}), + end: editor.indexFromPos({ line: l + 1, ch: 0}) + });; } }); return locations; @@ -101,13 +101,13 @@ setMode = function(mode) { if (mode === "javascript") { CodeMirror.commands.autocomplete = function(cm) { - CodeMirror.showHint(cm, CodeMirror.hint.anyword); // TODO change to a proper JavaScript language completion + CodeMirror.showHint(cm, CodeMirror.hint.anyword); // TODO change to a proper JavaScript language completion } } else if (mode === "solidity") { CodeMirror.commands.autocomplete = function(cm) { - CodeMirror.showHint(cm, CodeMirror.hint.anyword); + CodeMirror.showHint(cm, CodeMirror.hint.anyword); } } }; @@ -133,3 +133,36 @@ isClean = function() { return editor.isClean(changeId); } + +var errorMark; +var compilationCompleteBool = true; +compilationError = function(line, column, content) +{ + compilationCompleteBool = false; + window.setTimeout(function(){ + if (errorMark) + errorMark.clear(); + if (compilationCompleteBool) + return; + line = parseInt(line); + column = parseInt(column); + if (line > 0) + line = line - 1; + if (column > 0) + column = column - 1; + + var separators = [' ', '\\\+', '-', ';', '\\\(', '\\\{', '\\\}', '\\\)', '\\*', '/', ':', '\\\?']; + var errorPart = editor.getLine(line).substring(column); + var incrMark = column + errorPart.split(new RegExp(separators.join('|'), 'g'))[0].length; + if (incrMark === column) + incrMark = column + 1; + errorMark = editor.markText({ line: line, ch: column }, { line: line, ch: incrMark }, { className: "CodeMirror-errorannotation" }); + }, 1000) +} + +compilationComplete = function() +{ + if (errorMark) + errorMark.clear(); + compilationCompleteBool = true; +} diff --git a/mix/qml/js/ErrorLocationFormater.js b/mix/qml/js/ErrorLocationFormater.js index 8c83e6b15..cb92f298b 100644 --- a/mix/qml/js/ErrorLocationFormater.js +++ b/mix/qml/js/ErrorLocationFormater.js @@ -16,12 +16,15 @@ function extractErrorInfo(raw, shortMessage) { _return.errorLocation = ErrorLocationFormater.formatLocation(reg[0], shortMessage); _return.errorDetail = detail.replace(reg[0], ""); + _return.line = reg[0].split(':')[1]; + _return.column = reg[0].split(':')[2]; } else { _return.errorLocation = ""; _return.errorDetail = detail; + _return.line = ""; + _return.column = ""; } - _return.errorLine = raw.split('\n')[1]; return _return; } From e329364af4eff20f1ddee47a94ea54cff25067f3 Mon Sep 17 00:00:00 2001 From: Ali Mashatan Date: Fri, 20 Mar 2015 01:46:39 +0330 Subject: [PATCH 003/228] Adding StatusComboBox --- mix/qml/StatesComboBox.qml | 185 ++++++++++++++++++++++++++++++++++++ mix/qml/TransactionLog.qml | 21 +++- mix/qml/img/edit_combox.png | Bin 0 -> 406 bytes mix/res.qrc | 2 + 4 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 mix/qml/StatesComboBox.qml create mode 100644 mix/qml/img/edit_combox.png diff --git a/mix/qml/StatesComboBox.qml b/mix/qml/StatesComboBox.qml new file mode 100644 index 000000000..c89df5de5 --- /dev/null +++ b/mix/qml/StatesComboBox.qml @@ -0,0 +1,185 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file StatesComboBox.qml + * @author Ali Mashatan ali@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +import QtQuick 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.1 + +Rectangle { + id:statesComboBox + + width:200; + height: 20; + + Component.onCompleted: + { + var top = dropDownList; + while (top.parent) + { + top = top.parent + if (top.objectName == "debugPanel") + break; + } + var coordinates = dropDownList.mapToItem(top, 0, 0) + dropDownList.parent = top; + dropDownList.x = coordinates.x + dropDownList.y = coordinates.y + } + + signal selectItem(real item); + signal editItem(real item); + + property variant items; + property alias selectedItem: chosenItemText.text; + property alias selectedIndex: listView.currentRow; + signal comboClicked; + + property variant colorItem; + property variant colorSelect; + + smooth:true; + Rectangle { + id:chosenItem + width:parent.width; + height:statesComboBox.height; + color: statesComboBox.color; + smooth:true; + Text { + anchors.top: parent.top; + anchors.left: parent.left; + anchors.margins: 2; + color: statesComboBox.colorItem; + id:chosenItemText + text:statesComboBox.items.get(0).title; + smooth:true + } + + MouseArea { + anchors.fill: parent; + onClicked: { + statesComboBox.state = statesComboBox.state==="dropDown"?"":"dropDown" + } + } + } + + Rectangle { + id:dropDownList + width:statesComboBox.width; + height:0; + clip:true; + radius:4; + anchors.top: chosenItem.bottom; + anchors.margins: 2; + color: statesComboBox.color + + TableView { + id:listView + height:500; + width:statesComboBox.width; + model: statesComboBox.items + currentRow: 0 + headerVisible: false; + backgroundVisible: false + alternatingRowColors : false; + frameVisible: false + + TableViewColumn { + role: "title" + title: "" + width: statesComboBox.width; + delegate: mainItemDelegate + //elideMode: Text.ElideNone + } + + Component { + id: mainItemDelegate + Item{ + id: itemDelegate + width:statesComboBox.width; + height: statesComboBox.height; + Text { + id: textItemid + text: styleData.value + color: statesComboBox.colorItem; + anchors.top: parent.top; + anchors.left: parent.left; + anchors.margins: 5; + + } + Rectangle + { + id: spaceItemid + anchors.top: textItemid.top; + anchors.left: textItemid.right + width: parent.width - textItemid.width - imageItemid.width - textItemid.anchors.margins- textItemid.anchors.margins + } + Image { + id: imageItemid + height:20 + width:20; + visible: false; + fillMode: Image.PreserveAspectFit + source: "img/edit_combox.png" + anchors.top: spaceItemid.top; + anchors.left: spaceItemid.right; + } + + MouseArea { + anchors.fill: parent; + hoverEnabled : true + + onEntered: { + imageItemid.visible = true; + textItemid.color = statesComboBox.colorSelect; + } + onExited: { + imageItemid.visible = false; + textItemid.color = statesComboBox.colorItem; + } + onClicked: { + if (mouseX > imageItemid.x && mouseX < imageItemid.x+ imageItemid.width + && mouseY > imageItemid.y && mouseY < imageItemid.y+ imageItemid.height) + statesComboBox.editItem(styleData.row); + else { + statesComboBox.state = "" + var prevSelection = chosenItemText.text + chosenItemText.text = modelData + if(chosenItemText.text != prevSelection) + statesComboBox.comboClicked(); + listView.currentRow = styleData.row; + statesComboBox.selectItem(styleData.row); + } + } + } + }//Item + }//Component + } + } + states: State { + name: "dropDown"; + PropertyChanges { target: dropDownList; height:20*statesComboBox.items.count } + } + + transitions: Transition { + NumberAnimation { target: dropDownList; properties: "height"; easing.type: Easing.OutExpo; duration: 1000 } + } +} diff --git a/mix/qml/TransactionLog.qml b/mix/qml/TransactionLog.qml index 315028c74..d7bda8fc4 100644 --- a/mix/qml/TransactionLog.qml +++ b/mix/qml/TransactionLog.qml @@ -61,7 +61,7 @@ Item { } } - ComboBox { + /*ComboBox { id: statesCombo model: projectModel.stateListModel width: 150 @@ -77,7 +77,24 @@ Item { statesCombo.currentIndex = index; } } - } + }*/ + StatesComboBox + { + id: statesCombo + items:projectModel.stateListModel + onSelectItem: console.log("Combobox Select Item: " + item ) + onEditItem: console.log("Combobox Edit Item: " + item ) + colorItem: "black" + colorSelect: "yellow" + color: "gray" + Connections { + target: projectModel.stateListModel + onStateRun: { + if (statesCombo.selectedIndex !== index) + statesCombo.selectedIndex = index; + } + } + } Button { anchors.rightMargin: 9 diff --git a/mix/qml/img/edit_combox.png b/mix/qml/img/edit_combox.png new file mode 100644 index 0000000000000000000000000000000000000000..be3b359429d9191aee8366978092ddbbe37be67b GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX0wgC|rfC8xmUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lweBoc6VX;-`;;_Kaj^> z;_2(kexFf>&z!H+DD?|aXr8BwV~EA+J{d9U}xl-c#Q+N3{xm--e>HF>))CL_vxqQs|r zwi;2M<$qR`Wo5=c(R41^vJgnCar>}edXmAO{WbKscMO9L`h0wNvc(HQ7VvP zFfuSS)ip5GHL?gXG_f)@wK6o*HZZg@FqmUJ^9PEC-29Zxv`Q=*OduMnJ{sf#H86O( L`njxgN@xNAav_Q1 literal 0 HcmV?d00001 diff --git a/mix/res.qrc b/mix/res.qrc index fa50d6dd8..51f2b49c6 100644 --- a/mix/res.qrc +++ b/mix/res.qrc @@ -9,8 +9,10 @@ qml/ProjectList.qml qml/StateDialog.qml qml/StateList.qml + qml/StatesComboBox.qml qml/StateListModel.qml qml/img/dappProjectIcon.png + qml/img/edit_combox.png qml/img/jumpintoback.png qml/img/jumpintoforward.png qml/img/jumpoutback.png From 28ccaf0cfa672b06ee5f354234d42c5c53f96748 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 20 Mar 2015 15:46:29 +0100 Subject: [PATCH 004/228] error annotation widget --- mix/qml/html/cm/errorannotation.js | 46 ++++++++++++++++++++++++++++++ mix/qml/html/cm/solarized.css | 11 ++++++- mix/qml/html/codeeditor.html | 1 + mix/qml/html/codeeditor.js | 22 ++++++-------- mix/web.qrc | 1 + 5 files changed, 67 insertions(+), 14 deletions(-) create mode 100644 mix/qml/html/cm/errorannotation.js diff --git a/mix/qml/html/cm/errorannotation.js b/mix/qml/html/cm/errorannotation.js new file mode 100644 index 000000000..4da65ffa0 --- /dev/null +++ b/mix/qml/html/cm/errorannotation.js @@ -0,0 +1,46 @@ +function ErrorAnnotation(editor, line, col, content) +{ + this.opened = false; + this.line = line; + this.col = col; + this.content = content; + this.editor = editor; + this.errorMark = null; + this.lineWidget = null; + this.init(); + this.open(); +} + +ErrorAnnotation.prototype.init = function() +{ + var separators = [' ', '\\\+', '-', ';', '\\\(', '\\\{', '\\\}', '\\\)', '\\*', '/', ':', '\\\?']; + var errorPart = editor.getLine(this.line).substring(this.col); + var incrMark = this.col + errorPart.split(new RegExp(separators.join('|'), 'g'))[0].length; + if (incrMark === this.col) + incrMark = this.col + 1; + this.errorMark = editor.markText({ line: this.line, ch: this.col }, { line: this.line, ch: incrMark }, { className: "CodeMirror-errorannotation", inclusiveRight: true }); +} + +ErrorAnnotation.prototype.open = function() +{ + var node = document.createElement("div"); + node.id = "annotation" + node.innerHTML = this.content; + node.className = "CodeMirror-errorannotation-context" + this.lineWidget = this.editor.addLineWidget(this.errorMark.find().from.line, node, { coverGutter: true }); + this.opened = true; +} + +ErrorAnnotation.prototype.close = function() +{ + this.lineWidget.clear(); + this.opened = false; +} + +ErrorAnnotation.prototype.detroy = function() +{ + if (this.opened) + this.close(); + if (this.errorMark) + this.errorMark.clear(); +} diff --git a/mix/qml/html/cm/solarized.css b/mix/qml/html/cm/solarized.css index 9f324241a..8a15b731f 100644 --- a/mix/qml/html/cm/solarized.css +++ b/mix/qml/html/cm/solarized.css @@ -171,6 +171,15 @@ view-port /* Error annotation */ .CodeMirror-errorannotation { - background: red; + background: rgb(255, 255, 170); + color: red !important; +} + +.CodeMirror-errorannotation-context { + font-family: monospace; + font-size: small; + color: red; + background: rgb(255, 255, 170); + padding: 2px; } diff --git a/mix/qml/html/codeeditor.html b/mix/qml/html/codeeditor.html index 4545b6239..5a8d5c462 100644 --- a/mix/qml/html/codeeditor.html +++ b/mix/qml/html/codeeditor.html @@ -22,6 +22,7 @@ + diff --git a/mix/qml/html/codeeditor.js b/mix/qml/html/codeeditor.js index ea04f891b..a549d0e68 100644 --- a/mix/qml/html/codeeditor.js +++ b/mix/qml/html/codeeditor.js @@ -134,14 +134,12 @@ isClean = function() return editor.isClean(changeId); } -var errorMark; +var annotation = null; var compilationCompleteBool = true; compilationError = function(line, column, content) { compilationCompleteBool = false; window.setTimeout(function(){ - if (errorMark) - errorMark.clear(); if (compilationCompleteBool) return; line = parseInt(line); @@ -150,19 +148,17 @@ compilationError = function(line, column, content) line = line - 1; if (column > 0) column = column - 1; - - var separators = [' ', '\\\+', '-', ';', '\\\(', '\\\{', '\\\}', '\\\)', '\\*', '/', ':', '\\\?']; - var errorPart = editor.getLine(line).substring(column); - var incrMark = column + errorPart.split(new RegExp(separators.join('|'), 'g'))[0].length; - if (incrMark === column) - incrMark = column + 1; - errorMark = editor.markText({ line: line, ch: column }, { line: line, ch: incrMark }, { className: "CodeMirror-errorannotation" }); - }, 1000) + if (annotation == null) + annotation = new ErrorAnnotation(editor, line, column, content); + }, 500) } compilationComplete = function() { - if (errorMark) - errorMark.clear(); + if (annotation !== null) + { + annotation.detroy(); + annotation = null; + } compilationCompleteBool = true; } diff --git a/mix/web.qrc b/mix/web.qrc index 118177d07..10cd26764 100644 --- a/mix/web.qrc +++ b/mix/web.qrc @@ -27,5 +27,6 @@ qml/html/cm/closebrackets.js qml/html/cm/solidityToken.js qml/html/cm/javascript-hint.js + qml/html/cm/errorannotation.js From 433f47b109b8c9a43dd80c50158ebf11aeff3d75 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 20 Mar 2015 16:57:48 +0100 Subject: [PATCH 005/228] small changes --- mix/qml/html/cm/errorannotation.js | 18 +++++++++--------- mix/qml/html/cm/solarized.css | 8 ++++---- mix/qml/html/codeeditor.js | 8 +++++++- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/mix/qml/html/cm/errorannotation.js b/mix/qml/html/cm/errorannotation.js index 4da65ffa0..0830d2b9e 100644 --- a/mix/qml/html/cm/errorannotation.js +++ b/mix/qml/html/cm/errorannotation.js @@ -1,8 +1,8 @@ -function ErrorAnnotation(editor, line, col, content) +function ErrorAnnotation(editor, line, column, content) { this.opened = false; this.line = line; - this.col = col; + this.column = column; this.content = content; this.editor = editor; this.errorMark = null; @@ -14,11 +14,11 @@ function ErrorAnnotation(editor, line, col, content) ErrorAnnotation.prototype.init = function() { var separators = [' ', '\\\+', '-', ';', '\\\(', '\\\{', '\\\}', '\\\)', '\\*', '/', ':', '\\\?']; - var errorPart = editor.getLine(this.line).substring(this.col); - var incrMark = this.col + errorPart.split(new RegExp(separators.join('|'), 'g'))[0].length; - if (incrMark === this.col) - incrMark = this.col + 1; - this.errorMark = editor.markText({ line: this.line, ch: this.col }, { line: this.line, ch: incrMark }, { className: "CodeMirror-errorannotation", inclusiveRight: true }); + var errorPart = editor.getLine(this.line).substring(this.column); + var incrMark = this.column + errorPart.split(new RegExp(separators.join('|'), 'g'))[0].length; + if (incrMark === this.column) + incrMark = this.column + 1; + this.errorMark = editor.markText({ line: this.line, ch: this.column }, { line: this.line, ch: incrMark }, { className: "CodeMirror-errorannotation", inclusiveRight: true }); } ErrorAnnotation.prototype.open = function() @@ -26,7 +26,7 @@ ErrorAnnotation.prototype.open = function() var node = document.createElement("div"); node.id = "annotation" node.innerHTML = this.content; - node.className = "CodeMirror-errorannotation-context" + node.className = "CodeMirror-errorannotation-context"; this.lineWidget = this.editor.addLineWidget(this.errorMark.find().from.line, node, { coverGutter: true }); this.opened = true; } @@ -37,7 +37,7 @@ ErrorAnnotation.prototype.close = function() this.opened = false; } -ErrorAnnotation.prototype.detroy = function() +ErrorAnnotation.prototype.destroy = function() { if (this.opened) this.close(); diff --git a/mix/qml/html/cm/solarized.css b/mix/qml/html/cm/solarized.css index 8a15b731f..0f4764c30 100644 --- a/mix/qml/html/cm/solarized.css +++ b/mix/qml/html/cm/solarized.css @@ -171,15 +171,15 @@ view-port /* Error annotation */ .CodeMirror-errorannotation { - background: rgb(255, 255, 170); - color: red !important; + background: #b58900; + color: #dc322f !important; } .CodeMirror-errorannotation-context { font-family: monospace; font-size: small; - color: red; - background: rgb(255, 255, 170); + color: #dc322f; + background: #b58900; padding: 2px; } diff --git a/mix/qml/html/codeeditor.js b/mix/qml/html/codeeditor.js index a549d0e68..f42e62c94 100644 --- a/mix/qml/html/codeeditor.js +++ b/mix/qml/html/codeeditor.js @@ -148,8 +148,14 @@ compilationError = function(line, column, content) line = line - 1; if (column > 0) column = column - 1; + if (annotation == null) annotation = new ErrorAnnotation(editor, line, column, content); + else if (annotation.line !== line || annotation.column !== column || annotation.content !== content) + { + annotation.destroy(); + annotation = new ErrorAnnotation(editor, line, column, content); + } }, 500) } @@ -157,7 +163,7 @@ compilationComplete = function() { if (annotation !== null) { - annotation.detroy(); + annotation.destroy(); annotation = null; } compilationCompleteBool = true; From 2d7fc88e7cd99b3c8f19554d693ee4fd6d659bf6 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 20 Mar 2015 17:23:25 +0100 Subject: [PATCH 006/228] ui changes --- mix/qml/html/cm/errorannotation.js | 6 +++--- mix/qml/html/cm/solarized.css | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/mix/qml/html/cm/errorannotation.js b/mix/qml/html/cm/errorannotation.js index 0830d2b9e..345d389b2 100644 --- a/mix/qml/html/cm/errorannotation.js +++ b/mix/qml/html/cm/errorannotation.js @@ -3,7 +3,7 @@ function ErrorAnnotation(editor, line, column, content) this.opened = false; this.line = line; this.column = column; - this.content = content; + this.content = content.replace("Contract Error:", ""); this.editor = editor; this.errorMark = null; this.lineWidget = null; @@ -13,7 +13,7 @@ function ErrorAnnotation(editor, line, column, content) ErrorAnnotation.prototype.init = function() { - var separators = [' ', '\\\+', '-', ';', '\\\(', '\\\{', '\\\}', '\\\)', '\\*', '/', ':', '\\\?']; + var separators = ['\\\+', '-', ';', '\\\(', '\\\{', '\\\}', '\\\)', '\\*', '/', ':', '\\\?']; var errorPart = editor.getLine(this.line).substring(this.column); var incrMark = this.column + errorPart.split(new RegExp(separators.join('|'), 'g'))[0].length; if (incrMark === this.column) @@ -27,7 +27,7 @@ ErrorAnnotation.prototype.open = function() node.id = "annotation" node.innerHTML = this.content; node.className = "CodeMirror-errorannotation-context"; - this.lineWidget = this.editor.addLineWidget(this.errorMark.find().from.line, node, { coverGutter: true }); + this.lineWidget = this.editor.addLineWidget(this.errorMark.find().from.line, node, { coverGutter: false }); this.opened = true; } diff --git a/mix/qml/html/cm/solarized.css b/mix/qml/html/cm/solarized.css index 0f4764c30..ece98a95c 100644 --- a/mix/qml/html/cm/solarized.css +++ b/mix/qml/html/cm/solarized.css @@ -171,14 +171,13 @@ view-port /* Error annotation */ .CodeMirror-errorannotation { - background: #b58900; - color: #dc322f !important; + border-bottom: 1px solid #b58900; } .CodeMirror-errorannotation-context { font-family: monospace; font-size: small; - color: #dc322f; + color: #586e75; background: #b58900; padding: 2px; } From 04bb0ff2634c8deb37707ffebd4563dae8b37807 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 20 Mar 2015 17:28:14 +0100 Subject: [PATCH 007/228] small changes --- mix/qml/html/cm/errorannotation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix/qml/html/cm/errorannotation.js b/mix/qml/html/cm/errorannotation.js index 345d389b2..03e36c927 100644 --- a/mix/qml/html/cm/errorannotation.js +++ b/mix/qml/html/cm/errorannotation.js @@ -13,7 +13,7 @@ function ErrorAnnotation(editor, line, column, content) ErrorAnnotation.prototype.init = function() { - var separators = ['\\\+', '-', ';', '\\\(', '\\\{', '\\\}', '\\\)', '\\*', '/', ':', '\\\?']; + var separators = [';', ',', '\\\(', '\\\{', '\\\}', '\\\)', ':']; var errorPart = editor.getLine(this.line).substring(this.column); var incrMark = this.column + errorPart.split(new RegExp(separators.join('|'), 'g'))[0].length; if (incrMark === this.column) From 99a5df6b268d4c417ed6a835f5442ea8e06318a8 Mon Sep 17 00:00:00 2001 From: Ali Mashatan Date: Sun, 22 Mar 2015 12:59:19 +0430 Subject: [PATCH 008/228] - Bug fix: When a state was selected, the state is not being showed in the combobox. - Removing animation --- mix/qml/StatesComboBox.qml | 21 ++- mix/qml/TransactionLog.qml | 334 ++++++++++++++++++------------------- 2 files changed, 170 insertions(+), 185 deletions(-) diff --git a/mix/qml/StatesComboBox.qml b/mix/qml/StatesComboBox.qml index c89df5de5..24bd57866 100644 --- a/mix/qml/StatesComboBox.qml +++ b/mix/qml/StatesComboBox.qml @@ -49,8 +49,14 @@ Rectangle { signal editItem(real item); property variant items; - property alias selectedItem: chosenItemText.text; - property alias selectedIndex: listView.currentRow; + readonly property alias selectedItem: chosenItemText.text; + readonly property alias selectedIndex: listView.currentRow; + function setSelectedIndex(index) + { + listView.currentRow = index; + chosenItemText.text = statesComboBox.items.get(0).title; + } + signal comboClicked; property variant colorItem; @@ -64,12 +70,12 @@ Rectangle { color: statesComboBox.color; smooth:true; Text { + id:chosenItemText anchors.top: parent.top; anchors.left: parent.left; anchors.margins: 2; color: statesComboBox.colorItem; - id:chosenItemText - text:statesComboBox.items.get(0).title; + text:"" smooth:true } @@ -96,7 +102,7 @@ Rectangle { height:500; width:statesComboBox.width; model: statesComboBox.items - currentRow: 0 + currentRow: -1 headerVisible: false; backgroundVisible: false alternatingRowColors : false; @@ -162,7 +168,7 @@ Rectangle { else { statesComboBox.state = "" var prevSelection = chosenItemText.text - chosenItemText.text = modelData + chosenItemText.text = styleData.value if(chosenItemText.text != prevSelection) statesComboBox.comboClicked(); listView.currentRow = styleData.row; @@ -179,7 +185,4 @@ Rectangle { PropertyChanges { target: dropDownList; height:20*statesComboBox.items.count } } - transitions: Transition { - NumberAnimation { target: dropDownList; properties: "height"; easing.type: Easing.OutExpo; duration: 1000 } - } } diff --git a/mix/qml/TransactionLog.qml b/mix/qml/TransactionLog.qml index d7bda8fc4..d0386706e 100644 --- a/mix/qml/TransactionLog.qml +++ b/mix/qml/TransactionLog.qml @@ -7,202 +7,184 @@ import org.ethereum.qml.RecordLogEntry 1.0 Item { - property ListModel fullModel: ListModel{} - property ListModel transactionModel: ListModel{} - property ListModel callModel: ListModel{} + property ListModel fullModel: ListModel{} + property ListModel transactionModel: ListModel{} + property ListModel callModel: ListModel{} - Action { - id: addStateAction - text: "Add State" - shortcut: "Ctrl+Alt+T" - enabled: codeModel.hasContract && !clientModel.running; - onTriggered: projectModel.stateListModel.addState(); - } - Action { - id: editStateAction - text: "Edit State" - shortcut: "Ctrl+Alt+T" - enabled: codeModel.hasContract && !clientModel.running && statesCombo.currentIndex >= 0 && projectModel.stateListModel.count > 0; - onTriggered: projectModel.stateListModel.editState(statesCombo.currentIndex); - } + Action { + id: addStateAction + text: "Add State" + shortcut: "Ctrl+Alt+T" + enabled: codeModel.hasContract && !clientModel.running; + onTriggered: projectModel.stateListModel.addState(); + } + Action { + id: editStateAction + text: "Edit State" + shortcut: "Ctrl+Alt+T" + enabled: codeModel.hasContract && !clientModel.running && statesCombo.selectedIndex >= 0 && projectModel.stateListModel.count > 0; + onTriggered: projectModel.stateListModel.editState(statesCombo.selectedIndex); + } - ColumnLayout { - anchors.fill: parent - RowLayout { + ColumnLayout { + anchors.fill: parent + RowLayout { - Connections - { - id: compilationStatus - target: codeModel - property bool compilationComplete: false - onCompilationComplete: compilationComplete = true - onCompilationError: compilationComplete = false - } - - Connections - { - target: projectModel - onProjectSaved: - { - if (projectModel.appIsClosing) - return; - if (compilationStatus.compilationComplete && codeModel.hasContract && !clientModel.running) - projectModel.stateListModel.debugDefaultState(); - } - onProjectClosed: - { - fullModel.clear(); - transactionModel.clear(); - callModel.clear(); - } - onContractSaved: { - if (compilationStatus.compilationComplete && codeModel.hasContract && !clientModel.running) - projectModel.stateListModel.debugDefaultState(); - } - } + Connections + { + id: compilationStatus + target: codeModel + property bool compilationComplete: false + onCompilationComplete: compilationComplete = true + onCompilationError: compilationComplete = false + } - /*ComboBox { - id: statesCombo - model: projectModel.stateListModel - width: 150 - editable: false - textRole: "title" - onActivated: { - model.runState(index); - } - Connections { - target: projectModel.stateListModel - onStateRun: { - if (statesCombo.currentIndex !== index) - statesCombo.currentIndex = index; - } - } - }*/ - StatesComboBox + Connections + { + target: projectModel + onProjectSaved: + { + if (projectModel.appIsClosing) + return; + if (compilationStatus.compilationComplete && codeModel.hasContract && !clientModel.running) + projectModel.stateListModel.debugDefaultState(); + } + onProjectClosed: + { + fullModel.clear(); + transactionModel.clear(); + callModel.clear(); + } + onContractSaved: { + if (compilationStatus.compilationComplete && codeModel.hasContract && !clientModel.running) + projectModel.stateListModel.debugDefaultState(); + } + } + StatesComboBox { id: statesCombo items:projectModel.stateListModel onSelectItem: console.log("Combobox Select Item: " + item ) - onEditItem: console.log("Combobox Edit Item: " + item ) + onEditItem: projectModel.stateListModel.editState(item) colorItem: "black" - colorSelect: "yellow" - color: "gray" + colorSelect: "blue" + color: "white" Connections { target: projectModel.stateListModel onStateRun: { if (statesCombo.selectedIndex !== index) - statesCombo.selectedIndex = index; + statesCombo.setSelectedIndex( index ); } } } - Button - { - anchors.rightMargin: 9 - anchors.verticalCenter: parent.verticalCenter - action: editStateAction - } - Button - { - anchors.rightMargin: 9 - anchors.verticalCenter: parent.verticalCenter - action: addStateAction - } - Button - { - anchors.rightMargin: 9 - anchors.verticalCenter: parent.verticalCenter - action: mineAction - } + Button + { + anchors.rightMargin: 9 + anchors.verticalCenter: parent.verticalCenter + action: editStateAction + } + Button + { + anchors.rightMargin: 9 + anchors.verticalCenter: parent.verticalCenter + action: addStateAction + } + Button + { + anchors.rightMargin: 9 + anchors.verticalCenter: parent.verticalCenter + action: mineAction + } - ComboBox { - id: itemFilter + ComboBox { + id: itemFilter - function getCurrentModel() - { - return currentIndex === 0 ? fullModel : currentIndex === 1 ? transactionModel : currentIndex === 2 ? callModel : fullModel; - } + function getCurrentModel() + { + return currentIndex === 0 ? fullModel : currentIndex === 1 ? transactionModel : currentIndex === 2 ? callModel : fullModel; + } - model: ListModel { - ListElement { text: qsTr("Calls and Transactions"); value: 0; } - ListElement { text: qsTr("Only Transactions"); value: 1; } - ListElement { text: qsTr("Only Calls"); value: 2; } - } + model: ListModel { + ListElement { text: qsTr("Calls and Transactions"); value: 0; } + ListElement { text: qsTr("Only Transactions"); value: 1; } + ListElement { text: qsTr("Only Calls"); value: 2; } + } - onCurrentIndexChanged: - { - logTable.model = itemFilter.getCurrentModel(); - } - } - } - TableView { - id: logTable - Layout.fillWidth: true - Layout.fillHeight: true - model: fullModel + onCurrentIndexChanged: + { + logTable.model = itemFilter.getCurrentModel(); + } + } + } + TableView { + id: logTable + Layout.fillWidth: true + Layout.fillHeight: true + model: fullModel - TableViewColumn { - role: "transactionIndex" - title: qsTr("#") - width: 40 - } - TableViewColumn { - role: "contract" - title: qsTr("Contract") - width: 100 - } - TableViewColumn { - role: "function" - title: qsTr("Function") - width: 120 - } - TableViewColumn { - role: "value" - title: qsTr("Value") - width: 60 - } - TableViewColumn { - role: "address" - title: qsTr("Destination") - width: 130 - } - TableViewColumn { - role: "returned" - title: qsTr("Returned") - width: 120 - } - onActivated: { - var item = logTable.model.get(row); - if (item.type === RecordLogEntry.Transaction) - clientModel.debugRecord(item.recordIndex); - else - clientModel.emptyRecord(); - } - Keys.onPressed: { - if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logTable.model.count) { - var item = logTable.model.get(currentRow); - appContext.toClipboard(item.returned); - } - } - } - } + TableViewColumn { + role: "transactionIndex" + title: qsTr("#") + width: 40 + } + TableViewColumn { + role: "contract" + title: qsTr("Contract") + width: 100 + } + TableViewColumn { + role: "function" + title: qsTr("Function") + width: 120 + } + TableViewColumn { + role: "value" + title: qsTr("Value") + width: 60 + } + TableViewColumn { + role: "address" + title: qsTr("Destination") + width: 130 + } + TableViewColumn { + role: "returned" + title: qsTr("Returned") + width: 120 + } + onActivated: { + var item = logTable.model.get(row); + if (item.type === RecordLogEntry.Transaction) + clientModel.debugRecord(item.recordIndex); + else + clientModel.emptyRecord(); + } + Keys.onPressed: { + if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logTable.model.count) { + var item = logTable.model.get(currentRow); + appContext.toClipboard(item.returned); + } + } + } + } - Connections { - target: clientModel - onStateCleared: { - fullModel.clear(); - transactionModel.clear(); - callModel.clear(); - } - onNewRecord: { - fullModel.append(_r); - if (!_r.call) - transactionModel.append(_r); - else - callModel.append(_r); - } - onMiningComplete: { - fullModel.append(clientModel.lastBlock); - transactionModel.append(clientModel.lastBlock); - } - } + Connections { + target: clientModel + onStateCleared: { + fullModel.clear(); + transactionModel.clear(); + callModel.clear(); + } + onNewRecord: { + fullModel.append(_r); + if (!_r.call) + transactionModel.append(_r); + else + callModel.append(_r); + } + onMiningComplete: { + fullModel.append(clientModel.lastBlock); + transactionModel.append(clientModel.lastBlock); + } + } } From f64bf8f17a1b4e18441048587a31023d245faf9a Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 22 Mar 2015 22:37:12 +0100 Subject: [PATCH 009/228] Bug fix. Add destination node to expected discovery ping instead of ourselves. --- libp2p/NodeTable.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 0d6dda214..ae9e2cdef 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -87,7 +87,8 @@ shared_ptr NodeTable::addNode(Node const& _node) // ping address if nodeid is empty if (!_node.id) { - m_pubkDiscoverPings[m_node.endpoint.udp.address()] = std::chrono::steady_clock::now(); + clog(NodeTableConnect) << "Sending public key discovery Ping to" << _node.endpoint.udp << "(Advertising:" << m_node.endpoint.udp << ")"; + m_pubkDiscoverPings[_node.endpoint.udp.address()] = std::chrono::steady_clock::now(); PingNode p(_node.endpoint.udp, m_node.endpoint.udp.address().to_string(), m_node.endpoint.udp.port()); p.sign(m_secret); m_socketPointer->send(p); From 5c5c01a6e916683b2470055d0492db93ebf9ccdc Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 23 Mar 2015 00:24:36 +0100 Subject: [PATCH 010/228] fix public-key discovery ping --- libp2p/NodeTable.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index ae9e2cdef..912754c39 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -443,7 +443,10 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes n->pending = false; } else if (m_pubkDiscoverPings.count(_from.address())) + { m_pubkDiscoverPings.erase(_from.address()); + addNode(nodeid, _from, bi::tcp::endpoint(_from.address(), _from.port())); + } else return; // unsolicited pong; don't note node as active From 8bce0525375ced5e5145dfe15383c5faa7175e1b Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 23 Mar 2015 11:49:15 +0100 Subject: [PATCH 011/228] Do not hightlight the whole document when debugging. --- mix/qml/html/codeeditor.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mix/qml/html/codeeditor.js b/mix/qml/html/codeeditor.js index 102ba97a6..daf1286d8 100644 --- a/mix/qml/html/codeeditor.js +++ b/mix/qml/html/codeeditor.js @@ -132,6 +132,8 @@ var executionMark; highlightExecution = function(start, end) { if (executionMark) executionMark.clear(); + if (start === 0 && end + 1 === editor.getValue().length) + return; // Do not hightlight the whole document. executionMark = editor.markText(editor.posFromIndex(start), editor.posFromIndex(end), { className: "CodeMirror-exechighlight" }); } From a00c76944e2a10021c3ffd0915a6ae39734467e4 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 23 Mar 2015 12:07:57 +0100 Subject: [PATCH 012/228] Removing string as a token. - The string keyword is reserved for future use but should not be a token in the code since it can cause trigger internal compiler assertions. - fixes #1384 --- libsolidity/ExpressionCompiler.h | 1 - libsolidity/Token.h | 1 - 2 files changed, 2 deletions(-) diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index e6caad747..2577d21b5 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -42,7 +42,6 @@ class CompilerContext; class Type; class IntegerType; class ArrayType; -class StaticStringType; /** * Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream diff --git a/libsolidity/Token.h b/libsolidity/Token.h index b2951e939..6ffbe2194 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -290,7 +290,6 @@ namespace solidity K(Byte, "byte", 0) \ K(Address, "address", 0) \ K(Bool, "bool", 0) \ - K(StringType, "string", 0) \ K(Real, "real", 0) \ K(UReal, "ureal", 0) \ T(TypesEnd, NULL, 0) /* used as type enum end marker */ \ From 7c34bac9db621936356dbc9358c254e39c09d01b Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 23 Mar 2015 12:33:48 +0100 Subject: [PATCH 013/228] Ensure line nb and column nb are set. --- mix/qml/WebCodeEditor.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mix/qml/WebCodeEditor.qml b/mix/qml/WebCodeEditor.qml index 8ef3cc40c..7e5945fc7 100644 --- a/mix/qml/WebCodeEditor.qml +++ b/mix/qml/WebCodeEditor.qml @@ -92,7 +92,10 @@ Item { codeModel.onCompilationError.connect(function(error){ var errorInfo = ErrorLocationFormater.extractErrorInfo(error, false); - runJavaScript("compilationError('" + errorInfo.line + "', '" + errorInfo.column + "', '" + errorInfo.errorDetail + "')", function(result) { }); + if (errorInfo.line && errorInfo.column) + runJavaScript("compilationError('" + errorInfo.line + "', '" + errorInfo.column + "', '" + errorInfo.errorDetail + "')", function(result) { }); + else + runJavaScript("compilationComplete()", function(result) { }); }); } parent.changeGeneration(); From e11d04a79e7b61978001e295134ba6500c679d70 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 23 Mar 2015 12:58:34 +0100 Subject: [PATCH 014/228] Fix visible/hidden 'Step Out Back' and 'Run Forward' buttons. --- mix/qml/Debugger.qml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mix/qml/Debugger.qml b/mix/qml/Debugger.qml index b1f910ccb..358750b24 100644 --- a/mix/qml/Debugger.qml +++ b/mix/qml/Debugger.qml @@ -242,7 +242,6 @@ Rectangle { height: 30 buttonShortcut: "Ctrl+Shift+F11" buttonTooltip: qsTr("Step Out Back") - visible: false } StepActionImage @@ -315,9 +314,8 @@ Rectangle { height: 30 buttonShortcut: "Ctrl+F5" buttonTooltip: qsTr("Run Forward") + visible: false } - - } } From 74335368ff3da5a1bdfd0be51e0b8399e92548eb Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 23 Mar 2015 14:15:04 +0100 Subject: [PATCH 015/228] add version to packet payload --- libp2p/Host.cpp | 2 +- libp2p/NodeTable.h | 4 ++-- libp2p/RLPxHandshake.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 13ea87ba6..b946d9b3e 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -560,7 +560,7 @@ void Host::run(boost::system::error_code const&) { RecursiveGuard l(x_sessions); for (auto p: m_peers) - if (p.second->shouldReconnect()) + if (p.second->shouldReconnect() && !havePeerSession(p.second->id)) toConnect.push_back(p.second); } diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 58fa722c1..562942f2f 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -321,8 +321,8 @@ struct PingNode: RLPXDatagram unsigned port; unsigned expiration; - void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << port << expiration; } - void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); ipAddress = r[0].toString(); port = r[1].toInt(); expiration = r[2].toInt(); } + void streamRLP(RLPStream& _s) const { _s.appendList(4); _s << dev::p2p::c_protocolVersion << ipAddress << port << expiration; } + void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); version = r[0].toInt(); ipAddress = r[1].toString(); port = r[2].toInt(); expiration = r[3].toInt(); } }; /** diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index 1e42995ee..16bfe0fb1 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -252,7 +252,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) } clog(NetNote) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: success. starting session."; - RLP rlp(frame.cropped(1)); + RLP rlp(frame.cropped(1), RLP::ThrowOnFail | RLP::FailIfTooSmall); m_host->startPeerSession(m_remote, rlp, m_io, m_socket->remoteEndpoint()); } }); From 887cdf4bfb584e368ff573ff39e2adc2d531d93b Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 23 Mar 2015 15:26:48 +0100 Subject: [PATCH 016/228] gracefully handle old pingnode packet --- libp2p/NodeTable.h | 2 +- libp2p/UDP.h | 2 +- test/net.cpp | 11 +++++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 562942f2f..655ce7d43 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -316,7 +316,7 @@ struct PingNode: RLPXDatagram static const uint8_t type = 1; - unsigned version = dev::p2p::c_protocolVersion; + unsigned version = 0; std::string ipAddress; unsigned port; unsigned expiration; diff --git a/libp2p/UDP.h b/libp2p/UDP.h index 091f3cc2a..338100c78 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -77,7 +77,7 @@ template struct RLPXDatagram: public RLPXDatagramFace { RLPXDatagram(bi::udp::endpoint const& _ep): RLPXDatagramFace(_ep) {} - static T fromBytesConstRef(bi::udp::endpoint const& _ep, bytesConstRef _bytes) { T t(_ep); t.interpretRLP(_bytes); return std::move(t); } + static T fromBytesConstRef(bi::udp::endpoint const& _ep, bytesConstRef _bytes) { try { T t(_ep); t.interpretRLP(_bytes); return std::move(t); } catch(...) { T t(_ep); return std::move(t); } } uint8_t packetType() { return T::type; } }; diff --git a/test/net.cpp b/test/net.cpp index 23ad0fb4a..2ecfafb82 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -145,6 +145,17 @@ public: bool success = false; }; +BOOST_AUTO_TEST_CASE(badPingNodePacket) +{ + // test old versino of pingNode packet w/new + RLPStream s; + s.appendList(3); s << "1.1.1.1" << 30303 << std::chrono::duration_cast((std::chrono::system_clock::now() + chrono::seconds(60)).time_since_epoch()).count(); + + PingNode p((bi::udp::endpoint())); + BOOST_REQUIRE_NO_THROW(p = PingNode::fromBytesConstRef(bi::udp::endpoint(), bytesConstRef(&s.out()))); + BOOST_REQUIRE(p.version = 0); +} + BOOST_AUTO_TEST_CASE(test_neighbours_packet) { KeyPair k = KeyPair::create(); From c63b38e362682fd276d2b9b4d7453d1ddb16dcd7 Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 23 Mar 2015 15:31:03 +0100 Subject: [PATCH 017/228] fix test typo --- test/net.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/net.cpp b/test/net.cpp index 2ecfafb82..7ba2d8a12 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -153,7 +153,7 @@ BOOST_AUTO_TEST_CASE(badPingNodePacket) PingNode p((bi::udp::endpoint())); BOOST_REQUIRE_NO_THROW(p = PingNode::fromBytesConstRef(bi::udp::endpoint(), bytesConstRef(&s.out()))); - BOOST_REQUIRE(p.version = 0); + BOOST_REQUIRE(p.version == 0); } BOOST_AUTO_TEST_CASE(test_neighbours_packet) From 237b6da0ca9ea5507dc8caebcec26867a30f9d49 Mon Sep 17 00:00:00 2001 From: winsvega Date: Mon, 23 Mar 2015 17:52:34 +0300 Subject: [PATCH 018/228] Transaction Data Must Be Array +libsecp256k1test --- libethereum/Transaction.cpp | 4 ++++ test/ttTransactionTestFiller.json | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index 31a7f74a9..9a5e7fb3f 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -68,6 +68,10 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig) m_type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall; m_receiveAddress = rlp[field = 3].isEmpty() ? Address() : rlp[field = 3].toHash
(RLP::VeryStrict); m_value = rlp[field = 4].toInt(); + + if (rlp[field = 5].isList()) + BOOST_THROW_EXCEPTION(BadRLP() << errinfo_comment("transaction data RLP must be an array")); + m_data = rlp[field = 5].toBytes(); byte v = rlp[field = 6].toInt() - 27; h256 r = rlp[field = 7].toInt(); diff --git a/test/ttTransactionTestFiller.json b/test/ttTransactionTestFiller.json index 105b64abc..4f8005c5f 100644 --- a/test/ttTransactionTestFiller.json +++ b/test/ttTransactionTestFiller.json @@ -511,6 +511,7 @@ "s" : "0x8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3" } }, + "unpadedRValue": { "transaction": { "nonce": "13", @@ -523,5 +524,19 @@ "v": "28", "value": "" } + }, + + "libsecp256k1test": { + "transaction": { + "nonce": "", + "gasPrice": "0x09184e72a000", + "gasLimit": "0x1388", + "to": "", + "data": "", + "r": "44", + "s": "4", + "v": "27", + "value": "" + } } } From d12f6540d193f212b181cc33b458a298d3d8723b Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 23 Mar 2015 13:09:15 +0100 Subject: [PATCH 019/228] Adding keywords for future use section in Token.h --- libsolidity/Token.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 6ffbe2194..57d71c1fe 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -143,7 +143,6 @@ namespace solidity \ /* Keywords */ \ K(Break, "break", 0) \ - K(Case, "case", 0) \ K(Const, "constant", 0) \ K(Anonymous, "anonymous", 0) \ K(Continue, "continue", 0) \ @@ -168,7 +167,6 @@ namespace solidity K(Return, "return", 0) \ K(Returns, "returns", 0) \ K(Struct, "struct", 0) \ - K(Switch, "switch", 0) \ K(Var, "var", 0) \ K(While, "while", 0) \ K(Enum, "enum", 0) \ @@ -305,6 +303,13 @@ namespace solidity /* Identifiers (not keywords or future reserved words). */ \ T(Identifier, NULL, 0) \ \ + /* Keywords reserved for future. use*/ \ + T(String, "string", 0) \ + K(Case, "case", 0) \ + K(Switch, "switch", 0) \ + K(Throw, "throw", 0) \ + K(Try, "try", 0) \ + K(Catch, "catch", 0) \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ From 22b45721d8c1d91b9431e1761c5105adadb92709 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 23 Mar 2015 19:22:14 +0100 Subject: [PATCH 020/228] Better NodeTable error detection. --- libp2p/NodeTable.cpp | 26 ++++++++++++++++++++++++++ libp2p/NodeTable.h | 6 ++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 912754c39..7fe531762 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -578,3 +578,29 @@ void NodeTable::doRefreshBuckets(boost::system::error_code const& _ec) m_bucketRefreshTimer.async_wait(runcb); } +void PingNode::streamRLP(RLPStream& _s) const +{ + _s.appendList(4); + _s << dev::p2p::c_protocolVersion << ipAddress << port << expiration; +} + +void PingNode::interpretRLP(bytesConstRef _bytes) +{ + RLP r(_bytes); + if (r.itemCountStrict() == 3) + { + version = 2; + ipAddress = r[0].toString(); + port = r[1].toInt(RLP::Strict); + expiration = r[2].toInt(RLP::Strict); + } + else if (r.itemCountStrict() == 4) + { + version = r[0].toInt(RLP::Strict); + ipAddress = r[1].toString(); + port = r[2].toInt(RLP::Strict); + expiration = r[3].toInt(RLP::Strict); + } + else + BOOST_THROW_EXCEPTION(InvalidRLP()); +} diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 655ce7d43..6a167930d 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -288,6 +288,8 @@ inline std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable) return _out; } +struct InvalidRLP: public Exception {}; + /** * Ping packet: Sent to check if node is alive. * PingNode is cached and regenerated after expiration - t, where t is timeout. @@ -321,8 +323,8 @@ struct PingNode: RLPXDatagram unsigned port; unsigned expiration; - void streamRLP(RLPStream& _s) const { _s.appendList(4); _s << dev::p2p::c_protocolVersion << ipAddress << port << expiration; } - void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); version = r[0].toInt(); ipAddress = r[1].toString(); port = r[2].toInt(); expiration = r[3].toInt(); } + void streamRLP(RLPStream& _s) const override; + void interpretRLP(bytesConstRef _bytes) override; }; /** From e904462675c7dae5eeabd119996f17d824b509f3 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 23 Mar 2015 19:22:24 +0100 Subject: [PATCH 021/228] Remove numberOf. --- libethereum/Client.cpp | 12 +----------- libethereum/Client.h | 3 --- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 883b7f615..d2730fb98 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -729,16 +729,6 @@ void Client::doWork() } } -unsigned Client::numberOf(int _n) const -{ - if (_n > 0) - return _n; - else if (_n == GenesisBlock) - return 0; - else - return m_bc.details().number + max(-(int)m_bc.details().number, 1 + _n); -} - State Client::asOf(unsigned _h) const { ReadGuard l(x_stateDB); @@ -747,7 +737,7 @@ State Client::asOf(unsigned _h) const else if (_h == LatestBlock) return m_preMine; else - return State(m_stateDB, m_bc, m_bc.numberHash(numberOf(_h))); + return State(m_stateDB, m_bc, m_bc.numberHash(_h)); } State Client::state(unsigned _txi, h256 _block) const diff --git a/libethereum/Client.h b/libethereum/Client.h index f34eea5bf..6e34dc925 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -380,9 +380,6 @@ private: virtual bool turbo() const { return m_turboMining; } virtual bool force() const { return m_forceMining; } - /// Return the actual block number of the block with the given int-number (positive is the same, INT_MIN is genesis block, < 0 is negative age, thus -1 is most recently mined, 0 is pending. - unsigned numberOf(int _b) const; - /// Returns the state object for the full block (i.e. the terminal state) for index _h. /// Works properly with LatestBlock and PendingBlock. State asOf(unsigned _h) const; From 029b94a6ca5519458fc09098d70559f551327e00 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 23 Mar 2015 21:08:32 +0100 Subject: [PATCH 022/228] Revert balance transfer on exception. --- libethereum/Executive.cpp | 22 +++++++++++++--------- libethereum/ExtVM.h | 6 +++++- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index b44caf4ee..ddfd20956 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -120,9 +120,8 @@ bool Executive::setup() bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress) { m_isCreation = false; + m_excepted = TransactionException::None; // cnote << "Transferring" << formatBalance(_value) << "to receiver."; - m_s.addBalance(_receiveAddress, _value); - auto it = !(_codeAddress & ~h160(0xffffffff)) ? precompiled().find((unsigned)(u160)_codeAddress) : precompiled().end(); if (it != precompiled().end()) { @@ -147,6 +146,10 @@ bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _sen } else m_endGas = _gas; + + if (m_excepted == TransactionException::None) + m_s.addBalance(_receiveAddress, _value); + return !m_ext; } @@ -158,20 +161,21 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g // we delete it explicitly if we decide we need to revert. m_newAddress = right160(sha3(rlpList(_sender, m_s.transactionsFrom(_sender) - 1))); - // Set up new account... + // Execute _init. + if (!_init.empty()) + { + m_vm = VMFactory::create(_gas); + m_ext = make_shared(m_s, m_lastHashes, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_depth); + } + m_s.m_cache[m_newAddress] = Account(m_s.balance(m_newAddress) + _endowment, Account::ContractConception); - // Execute _init. if (_init.empty()) { m_s.m_cache[m_newAddress].setCode({}); m_endGas = _gas; } - else - { - m_vm = VMFactory::create(_gas); - m_ext = make_shared(m_s, m_lastHashes, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_depth); - } + return !m_ext; } diff --git a/libethereum/ExtVM.h b/libethereum/ExtVM.h index d63cd943a..8807bcd58 100644 --- a/libethereum/ExtVM.h +++ b/libethereum/ExtVM.h @@ -82,7 +82,11 @@ public: /// Revert any changes made (by any of the other calls). /// @TODO check call site for the parent manifest being discarded. - virtual void revert() override final { m_s.m_cache = m_origCache; sub.clear(); } + virtual void revert() override final + { + m_s.m_cache = m_origCache; + sub.clear(); + } State& state() const { return m_s; } From 4a436715cfa38b699a1f58c077d8e3717f233f6b Mon Sep 17 00:00:00 2001 From: winsvega Date: Mon, 23 Mar 2015 23:50:04 +0300 Subject: [PATCH 023/228] Exceptions --- libethereum/Transaction.cpp | 2 +- test/transaction.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index 9a5e7fb3f..502f089fb 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -69,7 +69,7 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig) m_receiveAddress = rlp[field = 3].isEmpty() ? Address() : rlp[field = 3].toHash
(RLP::VeryStrict); m_value = rlp[field = 4].toInt(); - if (rlp[field = 5].isList()) + if (!rlp[field = 5].isData()) BOOST_THROW_EXCEPTION(BadRLP() << errinfo_comment("transaction data RLP must be an array")); m_data = rlp[field = 5].toBytes(); diff --git a/test/transaction.cpp b/test/transaction.cpp index 77f6ecdaf..4c57326ba 100644 --- a/test/transaction.cpp +++ b/test/transaction.cpp @@ -48,6 +48,13 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin) if (!txFromRlp.signature().isValid()) BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") ); } + catch(Exception const& _e) + { + cnote << i.first; + cnote << "Transaction Exception: " << diagnostic_information(_e); + BOOST_CHECK_MESSAGE(o.count("transaction") == 0, "A transaction object should not be defined because the RLP is invalid!"); + continue; + } catch(...) { BOOST_CHECK_MESSAGE(o.count("transaction") == 0, "A transaction object should not be defined because the RLP is invalid!"); From 19d09366b05876a2f49a36abc89fc22103f7e06a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 24 Mar 2015 08:56:21 +0100 Subject: [PATCH 024/228] Avoid confusing Gustav. --- libethcore/Ethasher.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/libethcore/Ethasher.cpp b/libethcore/Ethasher.cpp index 44a555fde..1e6f0acef 100644 --- a/libethcore/Ethasher.cpp +++ b/libethcore/Ethasher.cpp @@ -112,15 +112,19 @@ bool Ethasher::verify(BlockInfo const& _header) h256 boundary = u256((bigint(1) << 256) / _header.difficulty); - // should be equivalent to: - auto r = eval(_header); - return r.mixHash == _header.mixHash && r.value <= boundary; - - return ethash_quick_check_difficulty( + bool ret = ethash_quick_check_difficulty( _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_header.nonce, _header.mixHash.data(), boundary.data()); + +#if ETH_DEBUG + // should be equivalent to: + auto result = eval(_header); + assert((result.mixHash == _header.mixHash && result.value <= boundary) == ret); +#endif + + return ret; } Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce) From 59b676b42be803ef01807177a05eebf850ff08ed Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 24 Mar 2015 08:57:09 +0100 Subject: [PATCH 025/228] Avoid confusing Gustav. --- libethcore/Ethasher.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/libethcore/Ethasher.cpp b/libethcore/Ethasher.cpp index 44a555fde..1e6f0acef 100644 --- a/libethcore/Ethasher.cpp +++ b/libethcore/Ethasher.cpp @@ -112,15 +112,19 @@ bool Ethasher::verify(BlockInfo const& _header) h256 boundary = u256((bigint(1) << 256) / _header.difficulty); - // should be equivalent to: - auto r = eval(_header); - return r.mixHash == _header.mixHash && r.value <= boundary; - - return ethash_quick_check_difficulty( + bool ret = ethash_quick_check_difficulty( _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_header.nonce, _header.mixHash.data(), boundary.data()); + +#if ETH_DEBUG + // should be equivalent to: + auto result = eval(_header); + assert((result.mixHash == _header.mixHash && result.value <= boundary) == ret); +#endif + + return ret; } Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce) From f3d4351dbfc2b4ea9b7d0db7b3c624262e472442 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 24 Mar 2015 08:57:45 +0100 Subject: [PATCH 026/228] Make value transfers symmetric. --- libethereum/Executive.cpp | 33 +++++++++++++++++++-------------- libethereum/State.h | 8 ++++++++ libevm/VM.cpp | 6 ------ 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index ddfd20956..c23047870 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -80,27 +80,31 @@ bool Executive::setup() } // Check gas cost is enough. - auto gasCost = Interface::txGas(m_t.data()); + auto gasRequired = Interface::txGas(m_t.data()); - if (m_t.gas() < gasCost) + if (m_t.gas() < gasRequired) { - clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << gasCost << " Got" << m_t.gas(); - BOOST_THROW_EXCEPTION(OutOfGas() << RequirementError((bigint)gasCost, (bigint)m_t.gas())); + clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << gasRequired << " Got" << m_t.gas(); + m_excepted = TransactionException::OutOfGas; + BOOST_THROW_EXCEPTION(OutOfGas() << RequirementError((bigint)gasRequired, (bigint)m_t.gas())); } - bigint cost = m_t.value() + (bigint)m_t.gas() * m_t.gasPrice(); + bigint gasCost = (bigint)m_t.gas() * m_t.gasPrice(); + bigint totalCost = m_t.value() + gasCost; // Avoid unaffordable transactions. - if (m_s.balance(m_t.sender()) < cost) + if (m_s.balance(m_t.sender()) < totalCost) { - clog(StateDetail) << "Not enough cash: Require >" << cost << " Got" << m_s.balance(m_t.sender()); - BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(cost, (bigint)m_s.balance(m_t.sender()))); + clog(StateDetail) << "Not enough cash: Require >" << totalCost << " Got" << m_s.balance(m_t.sender()); + m_excepted = TransactionException::NotEnoughCash; + BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(totalCost, (bigint)m_s.balance(m_t.sender()))); } u256 startGasUsed = m_s.gasUsed(); if (startGasUsed + (bigint)m_t.gas() > m_s.m_currentBlock.gasLimit) { clog(StateDetail) << "Too much gas used in this block: Require <" << (m_s.m_currentBlock.gasLimit - startGasUsed) << " Got" << m_t.gas(); + m_excepted = TransactionException::BlockGasLimitReached; BOOST_THROW_EXCEPTION(BlockGasLimitReached() << RequirementError((bigint)(m_s.m_currentBlock.gasLimit - startGasUsed), (bigint)m_t.gas())); } @@ -108,8 +112,8 @@ bool Executive::setup() m_s.noteSending(m_t.sender()); // Pay... - clog(StateDetail) << "Paying" << formatBalance(u256(cost)) << "from sender (includes" << m_t.gas() << "gas at" << formatBalance(m_t.gasPrice()) << ")"; - m_s.subBalance(m_t.sender(), cost); + clog(StateDetail) << "Paying" << formatBalance(u256(gasCost)) << "from sender for gas (" << m_t.gas() << "gas at" << formatBalance(m_t.gasPrice()) << ")"; + m_s.subBalance(m_t.sender(), gasCost); if (m_t.isCreation()) return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)gasCost, &m_t.data(), m_t.sender()); @@ -120,7 +124,6 @@ bool Executive::setup() bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress) { m_isCreation = false; - m_excepted = TransactionException::None; // cnote << "Transferring" << formatBalance(_value) << "to receiver."; auto it = !(_codeAddress & ~h160(0xffffffff)) ? precompiled().find((unsigned)(u160)_codeAddress) : precompiled().end(); if (it != precompiled().end()) @@ -130,6 +133,8 @@ bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _sen { m_endGas = 0; m_excepted = TransactionException::OutOfGasBase; + // Bail from exception. + return true; // true actually means "all finished - nothing more to be done regarding go(). } else { @@ -147,8 +152,7 @@ bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _sen else m_endGas = _gas; - if (m_excepted == TransactionException::None) - m_s.addBalance(_receiveAddress, _value); + m_s.transferBalance(_senderAddress, _receiveAddress, _value); return !m_ext; } @@ -168,7 +172,8 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g m_ext = make_shared(m_s, m_lastHashes, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_depth); } - m_s.m_cache[m_newAddress] = Account(m_s.balance(m_newAddress) + _endowment, Account::ContractConception); + m_s.m_cache[m_newAddress] = Account(m_s.balance(m_newAddress), Account::ContractConception); + m_s.transferBalance(_sender, m_newAddress, _endowment); if (_init.empty()) { diff --git a/libethereum/State.h b/libethereum/State.h index 36d3e7c09..46a111a9b 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -219,6 +219,14 @@ public: */ void subBalance(Address _id, bigint _value); + /** + * @brief Transfers "the balance @a _value between two accounts. + * @param _from Account from which @a _value will be deducted. + * @param _to Account to which @a _value will be added. + * @param _value Amount to be transferred. + */ + void transferBalance(Address _from, Address _to, u256 _value) { subBalance(_from, _value); addBalance(_to, _value); } + /// Get the root of the storage of an account. h256 storageRoot(Address _contract) const; diff --git a/libevm/VM.cpp b/libevm/VM.cpp index 2c1627db9..33c943938 100644 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -614,10 +614,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) m_stack.pop_back(); if (_ext.balance(_ext.myAddress) >= endowment && _ext.depth < 1024) - { - _ext.subBalance(endowment); m_stack.push_back((u160)_ext.create(endowment, m_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp)); - } else m_stack.push_back(0); break; @@ -644,10 +641,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) m_stack.pop_back(); if (_ext.balance(_ext.myAddress) >= value && _ext.depth < 1024) - { - _ext.subBalance(value); m_stack.push_back(_ext.call(inst == Instruction::CALL ? receiveAddress : _ext.myAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), gas, bytesRef(m_temp.data() + outOff, outSize), _onOp, {}, receiveAddress)); - } else m_stack.push_back(0); From 210278dc822276cb3bd2eb37ade1bfee5ce58567 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 24 Mar 2015 09:17:28 +0100 Subject: [PATCH 027/228] Protocol bump. --- libethcore/Common.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethcore/Common.cpp b/libethcore/Common.cpp index d26ac0260..d3ecbcc5f 100644 --- a/libethcore/Common.cpp +++ b/libethcore/Common.cpp @@ -34,7 +34,7 @@ namespace eth { const unsigned c_ethashVersion = c_ethashRevision; -const unsigned c_protocolVersion = 58; +const unsigned c_protocolVersion = 59; const unsigned c_databaseBaseVersion = 8; #if ETH_FATDB const unsigned c_databaseVersionModifier = 1; From 43e9fa3afde876fee57f5b3d2d4d37b77b8a6127 Mon Sep 17 00:00:00 2001 From: Jan Willem Penterman Date: Tue, 24 Mar 2015 10:03:20 +0100 Subject: [PATCH 028/228] Win32/MSVC requires ios::binary flag on ofstream to correctly write dag file --- libdevcore/CommonIO.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp index 4fa132073..46d6e3a6b 100644 --- a/libdevcore/CommonIO.cpp +++ b/libdevcore/CommonIO.cpp @@ -112,6 +112,6 @@ string dev::contentsString(std::string const& _file) void dev::writeFile(std::string const& _file, bytesConstRef _data) { - ofstream(_file, ios::trunc).write((char const*)_data.data(), _data.size()); + ofstream(_file, ios::trunc|ios::binary).write((char const*)_data.data(), _data.size()); } From a5269863bb3116b5f62c9becf5cc624c0250b1a6 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 24 Mar 2015 11:27:17 +0100 Subject: [PATCH 029/228] autocompletion redesign --- mix/qml/html/cm/anyword-hint.js | 39 +++++++++++++++++++++----------- mix/qml/html/cm/show-hint.css | 34 ++++++++++++++++++++++------ mix/qml/html/cm/solidityToken.js | 12 ++++++++++ 3 files changed, 65 insertions(+), 20 deletions(-) diff --git a/mix/qml/html/cm/anyword-hint.js b/mix/qml/html/cm/anyword-hint.js index c76044212..5da878fd8 100644 --- a/mix/qml/html/cm/anyword-hint.js +++ b/mix/qml/html/cm/anyword-hint.js @@ -13,6 +13,19 @@ var curWord = start != end && curLine.slice(start, end); var list = [], seen = {}; + + if (editor.getMode().name === "solidity") + { + list = addSolToken(curWord, list, seen, solCurrency(), solCurrency); + list = addSolToken(curWord, list, seen, solKeywords(), solKeywords); + list = addSolToken(curWord, list, seen, solStdContract(), solStdContract); + list = addSolToken(curWord, list, seen, solTime(), solTime); + list = addSolToken(curWord, list, seen, solTypes(), solTypes); + list = addSolToken(curWord, list, seen, solMisc(), solMisc); + } + + seen = solKeywords(); + var re = new RegExp(word.source, "g"); for (var dir = -1; dir <= 1; dir += 2) { var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir; @@ -28,32 +41,32 @@ } } - if (editor.getMode().name === "solidity") - { - list = addSolToken(curWord, list, solCurrency(), solCurrency); - list = addSolToken(curWord, list, solKeywords(), solKeywords); - list = addSolToken(curWord, list, solStdContract(), solStdContract); - list = addSolToken(curWord, list, solTime(), solTime); - list = addSolToken(curWord, list, solTypes(), solTypes); - list = addSolToken(curWord, list, solMisc(), solMisc); - } - return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)}; }); })(); -function addSolToken(curWord, list, tokens, type) +function addSolToken(curWord, list, seen, tokens, type) { + var keywordsTypeName = keywordsName(); for (var key in tokens) { + seen[key] = true; if (curWord === false || key.indexOf(curWord, 0) === 0) { var token = { text: key }; token.render = function(elt, data, cur) { - elt.className = elt.className + " " + type.name.toLowerCase(); - elt.appendChild(document.createTextNode(cur.displayText || cur.text)); + var container = document.createElement("div"); + var word = document.createElement("div"); + word.className = type.name.toLowerCase() + " solToken"; + word.appendChild(document.createTextNode(cur.displayText || cur.text)); + var typeDiv = document.createElement("type"); + typeDiv.appendChild(document.createTextNode(keywordsTypeName[type.name.toLowerCase()])); + typeDiv.className = "solTokenType"; + container.appendChild(word); + container.appendChild(typeDiv); + elt.appendChild(container); } list.push(token); } diff --git a/mix/qml/html/cm/show-hint.css b/mix/qml/html/cm/show-hint.css index 663ac1223..82b99d39a 100644 --- a/mix/qml/html/cm/show-hint.css +++ b/mix/qml/html/cm/show-hint.css @@ -31,33 +31,53 @@ white-space: pre; color: black; cursor: pointer; + padding-left: 20px; + padding-top: 3px; + padding-bottom: 3px; } .CodeMirror-hint-active { background: #4a90e2; - color: white; + color: white !important; +} + +.CodeMirror-hint-active .solToken, +.CodeMirror-hint-active .solTokenType +{ + color: white !important; } .solcurrency { - color: red; + color: #b58900; } .solkeywords { - color: brown; + color: #cb4b16; } .solstdcontract { - color: blue; + color: #d33682; } .soltime { - color: green; + color: #268bd2; } .soltypes { - color: orange; + color: #2aa198; } .solMisc { - color: grey; + color: #859900; +} + +.solToken { + float: left; +} + +.solTokenType +{ + font-style: italic; + color: #808080; + float: right; } diff --git a/mix/qml/html/cm/solidityToken.js b/mix/qml/html/cm/solidityToken.js index 68bd203c7..d8e588a10 100644 --- a/mix/qml/html/cm/solidityToken.js +++ b/mix/qml/html/cm/solidityToken.js @@ -27,3 +27,15 @@ function solMisc() { return { "true": true, "false": true, "null": true }; } + +function keywordsName() +{ + var keywords = {}; + keywords[solCurrency.name.toLowerCase()] = "Currency"; + keywords[solKeywords.name.toLowerCase()] = "Keyword"; + keywords[solStdContract.name.toLowerCase()] = "Contract"; + keywords[solTime.name.toLowerCase()] = "Time"; + keywords[solTypes.name.toLowerCase()] = "Type"; + keywords[solMisc.name.toLowerCase()] = "Misc"; + return keywords; +} From 386627340f242687d6fa6e55c593f2ccadb1f383 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 24 Mar 2015 12:06:36 +0100 Subject: [PATCH 030/228] Fixed all around... --- alethzero/MainWin.cpp | 2 +- libethereum/Client.cpp | 1 + libethereum/Executive.cpp | 8 ++++++-- libethereum/Transaction.h | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index d5e81a4f5..d566882f3 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -907,7 +907,7 @@ void Main::on_nameReg_textChanged() void Main::on_preview_triggered() { - ethereum()->setDefault(ui->preview->isChecked() ? 0 : -1); + ethereum()->setDefault(ui->preview->isChecked() ? PendingBlock : LatestBlock); refreshAll(); } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index d2730fb98..9e8a611c6 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -547,6 +547,7 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256 { ReadGuard l(x_stateDB); temp = m_postMine; + temp.addBalance(Address(), _value + _gasPrice * _gas); } Executive e(temp, LastHashes(), 0); if (!e.call(_dest, _dest, Address(), _value, _gasPrice, &_data, _gas, Address())) diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index c23047870..13b6df884 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -116,9 +116,9 @@ bool Executive::setup() m_s.subBalance(m_t.sender(), gasCost); if (m_t.isCreation()) - return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)gasCost, &m_t.data(), m_t.sender()); + return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)gasRequired, &m_t.data(), m_t.sender()); else - return call(m_t.receiveAddress(), m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)gasCost, m_t.sender()); + return call(m_t.receiveAddress(), m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)gasRequired, m_t.sender()); } bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress) @@ -152,7 +152,11 @@ bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _sen else m_endGas = _gas; + cdebug << m_s; + State prev = m_s; m_s.transferBalance(_senderAddress, _receiveAddress, _value); + cdebug << m_s; + cdebug << m_s.diff(prev); return !m_ext; } diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index 561ec6269..1287be64e 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -76,7 +76,7 @@ struct ExecutionResult { ExecutionResult() = default; ExecutionResult(u256 _gasUsed, TransactionException _excepted, Address _newAddress, bytesConstRef _output, CodeDeposit _codeDeposit): gasUsed(_gasUsed), excepted(_excepted), newAddress(_newAddress), output(_output.toBytes()), codeDeposit(_codeDeposit) {} - u256 gasUsed; + u256 gasUsed = 0; TransactionException excepted = TransactionException::Unknown; Address newAddress; bytes output; From a48ce877d23bc9f205bfc6fab1567ed211489287 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 24 Mar 2015 12:07:45 +0100 Subject: [PATCH 031/228] Reduce verbosity. --- libethereum/Executive.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 13b6df884..597506821 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -152,11 +152,7 @@ bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _sen else m_endGas = _gas; - cdebug << m_s; - State prev = m_s; m_s.transferBalance(_senderAddress, _receiveAddress, _value); - cdebug << m_s; - cdebug << m_s.diff(prev); return !m_ext; } From 9e10137d33d505a78091bc7d07963facfd336b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 24 Mar 2015 13:54:05 +0100 Subject: [PATCH 032/228] Increase executable stack size to 16MB on Windows Fixes ethereum/cpp-ethereum#1313 --- cmake/EthCompilerSettings.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index a0d5a50c1..7a5c94e6b 100755 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -43,7 +43,8 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221") # warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification # warning LNK4099: pdb was not found with lib - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099,4075") + # stack size 16MB + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099,4075 /STACK:16777216") # windows likes static set(ETH_STATIC 1) From a86fa46e894b2fde39bfefcbda24464917d2db05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 24 Mar 2015 13:57:03 +0100 Subject: [PATCH 033/228] Do not subbalance twice VM does not need to subbalance a caller. Balance tranfer is taken care on higher level. --- evmjit/libevmjit-cpp/Env.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/evmjit/libevmjit-cpp/Env.cpp b/evmjit/libevmjit-cpp/Env.cpp index fae320c28..b89aca726 100644 --- a/evmjit/libevmjit-cpp/Env.cpp +++ b/evmjit/libevmjit-cpp/Env.cpp @@ -52,7 +52,6 @@ extern "C" auto endowment = llvm2eth(*_endowment); if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024) { - _env->subBalance(endowment); u256 gas = *io_gas; h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, {}), h256::AlignRight); *io_gas = static_cast(gas); @@ -89,10 +88,7 @@ extern "C" auto ret = false; auto callGas = u256{_callGas}; if (_env->balance(_env->myAddress) >= value && _env->depth < 1024) - { - _env->subBalance(value); ret = _env->call(receiveAddress, value, {_inBeg, _inSize}, callGas, {_outBeg, _outSize}, {}, {}, codeAddress); - } *io_gas += static_cast(callGas); // it is never more than initial _callGas return ret; From 1b18c5b35ae42b13663741568800b2a565c16088 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 24 Mar 2015 14:01:16 +0100 Subject: [PATCH 034/228] Manage functions and contracts declaration in autocompletion. --- mix/qml/html/cm/anyword-hint.js | 46 +++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/mix/qml/html/cm/anyword-hint.js b/mix/qml/html/cm/anyword-hint.js index 5da878fd8..d1034ed8c 100644 --- a/mix/qml/html/cm/anyword-hint.js +++ b/mix/qml/html/cm/anyword-hint.js @@ -24,8 +24,7 @@ list = addSolToken(curWord, list, seen, solMisc(), solMisc); } - seen = solKeywords(); - + var previousWord = ""; var re = new RegExp(word.source, "g"); for (var dir = -1; dir <= 1; dir += 2) { var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir; @@ -35,12 +34,15 @@ if (line == cur.line && m[0] === curWord) continue; if ((!curWord || m[0].lastIndexOf(curWord, 0) === 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) { seen[m[0]] = true; - list.push({ text: m[0] }); + var w = { text: m[0] }; + checkDeclaration(previousWord, "Contract", w); + checkDeclaration(previousWord, "Function", w); + list.push(w); } + previousWord = m[0]; } } } - return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)}; }); })(); @@ -57,19 +59,35 @@ function addSolToken(curWord, list, seen, tokens, type) var token = { text: key }; token.render = function(elt, data, cur) { - var container = document.createElement("div"); - var word = document.createElement("div"); - word.className = type.name.toLowerCase() + " solToken"; - word.appendChild(document.createTextNode(cur.displayText || cur.text)); - var typeDiv = document.createElement("type"); - typeDiv.appendChild(document.createTextNode(keywordsTypeName[type.name.toLowerCase()])); - typeDiv.className = "solTokenType"; - container.appendChild(word); - container.appendChild(typeDiv); - elt.appendChild(container); + render(elt, data, cur, type.name.toLowerCase(), keywordsTypeName[type.name.toLowerCase()]); } list.push(token); } } return list; } + +function render(elt, data, cur, csstype, type) +{ + var container = document.createElement("div"); + var word = document.createElement("div"); + word.className = csstype + " solToken"; + word.appendChild(document.createTextNode(cur.displayText || cur.text)); + var typeDiv = document.createElement("type"); + typeDiv.appendChild(document.createTextNode(type)); + typeDiv.className = "solTokenType"; + container.appendChild(word); + container.appendChild(typeDiv); + elt.appendChild(container); +} + +function checkDeclaration(previousToken, target, current) +{ + if (previousToken.toLowerCase() === target.toLowerCase()) + { + current.render = function(elt, data, cur) + { + render(elt, data, cur, "sol" + target, target); + } + } +} From 2f935d722c5c7847d55bd8bb63c7ec5d440d456c Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 17 Mar 2015 22:50:01 +0100 Subject: [PATCH 035/228] Optimizer interface. --- libevmcore/Assembly.cpp | 18 ++++- libevmcore/CommonSubexpressionEliminator.cpp | 43 ++++++++++++ libevmcore/CommonSubexpressionEliminator.h | 70 ++++++++++++++++++++ 3 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 libevmcore/CommonSubexpressionEliminator.cpp create mode 100644 libevmcore/CommonSubexpressionEliminator.h diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index 68f326bfd..6ff6768d3 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -20,10 +20,9 @@ */ #include "Assembly.h" - #include - #include +#include using namespace std; using namespace dev; @@ -411,6 +410,21 @@ Assembly& Assembly::optimise(bool _enable) copt << *this; + copt << "Performing common subexpression elimination..."; + AssemblyItems optimizedItems; + for (auto iter = m_items.begin(); iter != m_items.end(); ++iter) + { + CommonSubexpressionEliminator eliminator; + iter = eliminator.feedItems(iter, m_items.end()); + optimizedItems += eliminator.getOptimizedItems(); + if (iter != m_items.end()) + optimizedItems.push_back(*iter); + } + copt << "Old size: " << m_items.size() << ", new size: " << optimizedItems.size(); + swap(m_items, optimizedItems); + + copt << *this; + unsigned total = 0; for (unsigned count = 1; count > 0; total += count) { diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp new file mode 100644 index 000000000..e3215e28d --- /dev/null +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -0,0 +1,43 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @file CommonSubexpressionEliminator.cpp + * @author Christian + * @date 2015 + * Optimizer step for common subexpression elimination and stack reorganisation. + */ + +#include +#include + +using namespace std; +using namespace dev; +using namespace dev::eth; + +vector CommonSubexpressionEliminator::getOptimizedItems() const +{ + return vector(); +} + +bool CommonSubexpressionEliminator::breaksBasicBlock(AssemblyItem const&) +{ + return true; +} + +void CommonSubexpressionEliminator::feedItem(AssemblyItem const&) +{ +} diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h new file mode 100644 index 000000000..ab04d2a8e --- /dev/null +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -0,0 +1,70 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @file CommonSubexpressionEliminator.h + * @author Christian + * @date 2015 + * Optimizer step for common subexpression elimination and stack reorganisation. + */ + +#pragma once + +#include + +namespace dev +{ +namespace eth +{ + +class AssemblyItem; + +/** + * Optimizer step that performs common subexpression elimination and stack reorginasation, + * i.e. it tries to infer equality among expressions and compute the values of two expressions + * known to be equal only once. + */ +class CommonSubexpressionEliminator +{ +public: + /// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first + /// item that must be fed into a new instance of the eliminator. + template + _AssemblyItemIterator feedItems(_AssemblyItemIterator _iterator, _AssemblyItemIterator _end); + + /// @returns the resulting items after optimization. + std::vector getOptimizedItems() const; + +private: + /// @returns true if the given items starts a new basic block + bool breaksBasicBlock(AssemblyItem const& _item); + /// Feeds the item into the system for analysis. + void feedItem(AssemblyItem const& _item); +}; + +template +_AssemblyItemIterator CommonSubexpressionEliminator::feedItems( + _AssemblyItemIterator _iterator, + _AssemblyItemIterator _end +) +{ + while (_iterator != _end && !breaksBasicBlock(*_iterator)) + feedItem(*_iterator); + return _iterator; +} + +} +} From 9b021fc77c8bb9e475fa15240ff663a07a9633c3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 18 Mar 2015 12:51:20 +0100 Subject: [PATCH 036/228] Analyzing part of the optimizer. --- libevmcore/Assembly.cpp | 2 +- libevmcore/CommonSubexpressionEliminator.cpp | 197 ++++++++++++++++++- libevmcore/CommonSubexpressionEliminator.h | 40 +++- libevmcore/Exceptions.h | 1 + 4 files changed, 235 insertions(+), 5 deletions(-) diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index 6ff6768d3..ad464721b 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -421,7 +421,7 @@ Assembly& Assembly::optimise(bool _enable) optimizedItems.push_back(*iter); } copt << "Old size: " << m_items.size() << ", new size: " << optimizedItems.size(); - swap(m_items, optimizedItems); +// swap(m_items, optimizedItems); copt << *this; diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index e3215e28d..117f823ce 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -21,6 +21,7 @@ * Optimizer step for common subexpression elimination and stack reorganisation. */ +#include #include #include @@ -30,14 +31,204 @@ using namespace dev::eth; vector CommonSubexpressionEliminator::getOptimizedItems() const { + auto streamEquivalenceClass = [this](ostream& _out, EquivalenceClass _id) + { + auto const& eqClass = m_equivalenceClasses[_id]; + _out << " " << _id << ": " << *eqClass.first; + _out << "("; + for (EquivalenceClass arg: eqClass.second) + _out << dec << arg << ","; + _out << ")" << endl; + }; + + cout << dec; + cout << "Optimizer results:" << endl; + cout << "Final stack height: " << m_stackHeight << endl; + cout << "Stack elements: " << endl; + for (auto const& it: m_stackElements) + { + cout + << " " << dec << it.first.first << "(" << it.first.second << ") = "; + streamEquivalenceClass(cout, it.second); + } + cout << "Equivalence classes: " << endl; + for (EquivalenceClass eqClass = 0; eqClass < m_equivalenceClasses.size(); ++eqClass) + streamEquivalenceClass(cout, eqClass); + cout << "----------------------------" << endl; + + if (m_stackElements.size() == 0) + { + + } + int stackHeight; +// m_stackElements + // for all stack elements from most neg to most pos: + // return vector(); } -bool CommonSubexpressionEliminator::breaksBasicBlock(AssemblyItem const&) +bool CommonSubexpressionEliminator::breaksBasicBlock(AssemblyItem const& _item) +{ + switch (_item.type()) + { + case UndefinedItem: + case Tag: + return true; + case Push: + case PushString: + case PushTag: + case PushSub: + case PushSubSize: + case PushProgramSize: + case PushData: + return false; + case Operation: + return instructionInfo(_item.instruction()).sideEffects; + } +} + +void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) +{ + cout << _item << endl; + if (_item.type() != Operation) + { + if (_item.deposit() != 1) + BOOST_THROW_EXCEPTION(InvalidDeposit()); + setStackElement(++m_stackHeight, getClass(_item, {})); + } + else + { + Instruction instruction = _item.instruction(); + InstructionInfo info = instructionInfo(instruction); + if (Instruction::DUP1 <= instruction && instruction <= Instruction::DUP16) + setStackElement( + m_stackHeight + 1, + getStackElement(m_stackHeight - int(instruction) + int(Instruction::DUP1)) + ); + else if (Instruction::SWAP1 <= instruction && instruction <= Instruction::SWAP16) + swapStackElements( + m_stackHeight, + m_stackHeight - 1 - int(instruction) + int(Instruction::SWAP1) + ); + else if (instruction != Instruction::POP) + { + vector arguments(info.args); + for (int i = 0; i < info.args; ++i) + arguments[i] = getStackElement(m_stackHeight - i); + setStackElement(m_stackHeight + info.ret - info.args, getClass(_item, arguments)); + } + m_stackHeight += info.ret - info.args; + } +} + +void CommonSubexpressionEliminator::setStackElement(int _stackHeight, EquivalenceClass _class) +{ + unsigned nextSequence = getNextStackElementSequence(_stackHeight); + m_stackElements[make_pair(_stackHeight, nextSequence)] = _class; +} + +void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _stackHeightB) +{ + if (_stackHeightA == _stackHeightB) + BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Swap on same stack elements.")); + EquivalenceClass classA = getStackElement(_stackHeightA); + EquivalenceClass classB = getStackElement(_stackHeightB); + + unsigned nextSequenceA = getNextStackElementSequence(_stackHeightA); + unsigned nextSequenceB = getNextStackElementSequence(_stackHeightB); + m_stackElements[make_pair(_stackHeightA, nextSequenceA)] = classB; + m_stackElements[make_pair(_stackHeightB, nextSequenceB)] = classA; +} + +CommonSubexpressionEliminator::EquivalenceClass CommonSubexpressionEliminator::getStackElement(int _stackHeight) +{ + // retrieve class by last sequence number + unsigned nextSequence = getNextStackElementSequence(_stackHeight); + if (nextSequence > 0) + return m_stackElements[make_pair(_stackHeight, nextSequence - 1)]; + + // Stack element not found (not assigned yet), create new equivalence class. + if (_stackHeight > 0) + BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Stack element accessed before assignment.")); + if (_stackHeight <= -16) + BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Stack too deep.")); + // This is a special assembly item that refers to elements pre-existing on the initial stack. + m_spareAssemblyItem.push_back(make_shared(dupInstruction(1 - _stackHeight))); + m_equivalenceClasses.push_back(make_pair(m_spareAssemblyItem.back().get(), EquivalenceClasses())); + return m_stackElements[make_pair(_stackHeight, nextSequence)] = EquivalenceClass(m_equivalenceClasses.size() - 1); +} + +CommonSubexpressionEliminator::EquivalenceClass CommonSubexpressionEliminator::getClass( + const AssemblyItem& _item, + EquivalenceClasses const& _arguments +) { - return true; + // do a clever search, i.e. + // - check for the presence of constants in the argument classes and do arithmetic + // - check whether the two items are equal for a SUB instruction + // - check whether 0 or 1 is in one of the classes for a MUL + // - for commutative opcodes, sort the arguments before searching + for (EquivalenceClass c = 0; c < m_equivalenceClasses.size(); ++c) + { + AssemblyItem const& classItem = *m_equivalenceClasses[c].first; + if (classItem != _item) + continue; + if (_arguments.size() != m_equivalenceClasses[c].second.size()) + BOOST_THROW_EXCEPTION( + OptimizerException() << + errinfo_comment("Equal assembly items with different number of arguments.") + ); + if (equal(_arguments.begin(), _arguments.end(), m_equivalenceClasses[c].second.begin())) + return c; + } + if (_item.type() == Operation && _arguments.size() == 2 && all_of( + _arguments.begin(), + _arguments.end(), + [this](EquivalenceClass eqc) { return m_equivalenceClasses[eqc].first->match(Push); })) + { + map> const arithmetics = + { + //@todo these are not correct (e.g. for div by zero) + { Instruction::SUB, [](u256 a, u256 b)->u256{return a - b;} }, + { Instruction::DIV, [](u256 a, u256 b)->u256{return a / b;} }, + { Instruction::SDIV, [](u256 a, u256 b)->u256{return s2u(u2s(a) / u2s(b));} }, + { Instruction::MOD, [](u256 a, u256 b)->u256{return a % b;} }, + { Instruction::SMOD, [](u256 a, u256 b)->u256{return s2u(u2s(a) % u2s(b));} }, + { Instruction::EXP, [](u256 a, u256 b)->u256{return (u256)boost::multiprecision::powm((bigint)a, (bigint)b, bigint(1) << 256);} }, + //{ Instruction::SIGNEXTEND, signextend }, + { Instruction::LT, [](u256 a, u256 b)->u256{return a < b ? 1 : 0;} }, + { Instruction::GT, [](u256 a, u256 b)->u256{return a > b ? 1 : 0;} }, + { Instruction::SLT, [](u256 a, u256 b)->u256{return u2s(a) < u2s(b) ? 1 : 0;} }, + { Instruction::SGT, [](u256 a, u256 b)->u256{return u2s(a) > u2s(b) ? 1 : 0;} }, + { Instruction::EQ, [](u256 a, u256 b)->u256{return a == b ? 1 : 0;} }, + { Instruction::ADD, [](u256 a, u256 b)->u256{return a + b;} }, + { Instruction::MUL, [](u256 a, u256 b)->u256{return a * b;} }, + { Instruction::AND, [](u256 a, u256 b)->u256{return a & b;} }, + { Instruction::OR, [](u256 a, u256 b)->u256{return a | b;} }, + { Instruction::XOR, [](u256 a, u256 b)->u256{return a ^ b;} }, + }; + if (arithmetics.count(_item.instruction())) + { + u256 result = arithmetics.at(_item.instruction())( + m_equivalenceClasses[_arguments[0]].first->data(), + m_equivalenceClasses[_arguments[1]].first->data() + ); + m_spareAssemblyItem.push_back(make_shared(result)); + return getClass(*m_spareAssemblyItem.back()); + } + } + m_equivalenceClasses.push_back(make_pair(&_item, _arguments)); + return m_equivalenceClasses.size() - 1; } -void CommonSubexpressionEliminator::feedItem(AssemblyItem const&) +unsigned CommonSubexpressionEliminator::getNextStackElementSequence(int _stackHeight) { + auto it = m_stackElements.upper_bound(make_pair(_stackHeight, unsigned(-1))); + if (it == m_stackElements.begin()) + return 0; + --it; + if (it->first.first == _stackHeight) + return it->first.second + 1; + else + return 0; } diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h index ab04d2a8e..2f932ba49 100644 --- a/libevmcore/CommonSubexpressionEliminator.h +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -24,6 +24,8 @@ #pragma once #include +#include +#include namespace dev { @@ -36,6 +38,14 @@ class AssemblyItem; * Optimizer step that performs common subexpression elimination and stack reorginasation, * i.e. it tries to infer equality among expressions and compute the values of two expressions * known to be equal only once. + * + * The general workings are that for each assembly item that is fed into the eliminator, an + * equivalence class is derived from the operation and the equivalence class of its arguments and + * it is assigned to the next sequence number of a stack item. DUPi, SWAPi and some arithmetic + * instructions are used to infer equivalences while these classes are determined. + * + * When the list of optimized items is requested, they are generated in a bottom-up fashion, + * adding code for equivalence classes that were not yet computed. */ class CommonSubexpressionEliminator { @@ -49,10 +59,37 @@ public: std::vector getOptimizedItems() const; private: + using EquivalenceClass = unsigned; + using EquivalenceClasses = std::vector; + /// @returns true if the given items starts a new basic block bool breaksBasicBlock(AssemblyItem const& _item); /// Feeds the item into the system for analysis. void feedItem(AssemblyItem const& _item); + + /// Assigns a new equivalence class to the next sequence number of the given stack element. + void setStackElement(int _stackHeight, EquivalenceClass _class); + /// Swaps the given stack elements in their next sequence number. + void swapStackElements(int _stackHeightA, int _stackHeightB); + /// Retrieves the current equivalence class fo the given stack element (or generates a new + /// one if it does not exist yet). + EquivalenceClass getStackElement(int _stackHeight); + /// Retrieves the equivalence class resulting from the given item applied to the given classes, + /// might also create a new one. + EquivalenceClass getClass(AssemblyItem const& _item, EquivalenceClasses const& _arguments = {}); + + /// @returns the next sequence number of the given stack element. + unsigned getNextStackElementSequence(int _stackHeight); + + /// Current stack height, can be negative. + int m_stackHeight = 0; + /// Mapping (stack height, sequence number) -> equivalence class + std::map, EquivalenceClass> m_stackElements; + /// Vector of equivalence class representatives - we only store one item of an equivalence + /// class and the index is used as identifier. + std::vector> m_equivalenceClasses; + /// List of items generated during analysis. + std::vector> m_spareAssemblyItem; }; template @@ -61,7 +98,8 @@ _AssemblyItemIterator CommonSubexpressionEliminator::feedItems( _AssemblyItemIterator _end ) { - while (_iterator != _end && !breaksBasicBlock(*_iterator)) + std::cout << "---------------Feeding items to the CSE engine:--------------" << std::endl; + for (; _iterator != _end && !breaksBasicBlock(*_iterator); ++_iterator) feedItem(*_iterator); return _iterator; } diff --git a/libevmcore/Exceptions.h b/libevmcore/Exceptions.h index 57e1ac9c6..fad972179 100644 --- a/libevmcore/Exceptions.h +++ b/libevmcore/Exceptions.h @@ -31,6 +31,7 @@ namespace eth struct AssemblyException: virtual Exception {}; struct InvalidDeposit: virtual AssemblyException {}; struct InvalidOpcode: virtual AssemblyException {}; +struct OptimizerException: virtual AssemblyException {}; } } From c97447fbd8480d0532ee8227bf0c1811b4947da2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 18 Mar 2015 16:50:59 +0100 Subject: [PATCH 037/228] code generator stub --- libevmcore/CommonSubexpressionEliminator.cpp | 58 ++++++++++++-------- libevmcore/CommonSubexpressionEliminator.h | 39 ++++++++++--- 2 files changed, 65 insertions(+), 32 deletions(-) diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index 117f823ce..c442425a4 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -29,14 +29,14 @@ using namespace std; using namespace dev; using namespace dev::eth; -vector CommonSubexpressionEliminator::getOptimizedItems() const +vector CommonSubexpressionEliminator::getOptimizedItems() { - auto streamEquivalenceClass = [this](ostream& _out, EquivalenceClass _id) + auto streamEquivalenceClass = [this](ostream& _out, EquivalenceClassId _id) { auto const& eqClass = m_equivalenceClasses[_id]; _out << " " << _id << ": " << *eqClass.first; _out << "("; - for (EquivalenceClass arg: eqClass.second) + for (EquivalenceClassId arg: eqClass.second) _out << dec << arg << ","; _out << ")" << endl; }; @@ -52,19 +52,20 @@ vector CommonSubexpressionEliminator::getOptimizedItems() const streamEquivalenceClass(cout, it.second); } cout << "Equivalence classes: " << endl; - for (EquivalenceClass eqClass = 0; eqClass < m_equivalenceClasses.size(); ++eqClass) + for (EquivalenceClassId eqClass = 0; eqClass < m_equivalenceClasses.size(); ++eqClass) streamEquivalenceClass(cout, eqClass); cout << "----------------------------" << endl; - if (m_stackElements.size() == 0) - { + map targetStackContents; + int minStackHeight = m_stackHeight; + if (m_stackElements.size() > 0) + minStackHeight = min(minStackHeight, m_stackElements.begin()->first.first); + for (int stackHeight = minStackHeight; stackHeight <= m_stackHeight; ++stackHeight) + targetStackContents[stackHeight] = getStackElement(stackHeight); - } - int stackHeight; -// m_stackElements - // for all stack elements from most neg to most pos: - // - return vector(); + CSECodeGenerator generator; + + return generator.generateCode(m_stackHeight, targetStackContents, m_equivalenceClasses); } bool CommonSubexpressionEliminator::breaksBasicBlock(AssemblyItem const& _item) @@ -112,7 +113,7 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) ); else if (instruction != Instruction::POP) { - vector arguments(info.args); + vector arguments(info.args); for (int i = 0; i < info.args; ++i) arguments[i] = getStackElement(m_stackHeight - i); setStackElement(m_stackHeight + info.ret - info.args, getClass(_item, arguments)); @@ -121,7 +122,7 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) } } -void CommonSubexpressionEliminator::setStackElement(int _stackHeight, EquivalenceClass _class) +void CommonSubexpressionEliminator::setStackElement(int _stackHeight, EquivalenceClassId _class) { unsigned nextSequence = getNextStackElementSequence(_stackHeight); m_stackElements[make_pair(_stackHeight, nextSequence)] = _class; @@ -131,8 +132,8 @@ void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _st { if (_stackHeightA == _stackHeightB) BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Swap on same stack elements.")); - EquivalenceClass classA = getStackElement(_stackHeightA); - EquivalenceClass classB = getStackElement(_stackHeightB); + EquivalenceClassId classA = getStackElement(_stackHeightA); + EquivalenceClassId classB = getStackElement(_stackHeightB); unsigned nextSequenceA = getNextStackElementSequence(_stackHeightA); unsigned nextSequenceB = getNextStackElementSequence(_stackHeightB); @@ -140,7 +141,7 @@ void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _st m_stackElements[make_pair(_stackHeightB, nextSequenceB)] = classA; } -CommonSubexpressionEliminator::EquivalenceClass CommonSubexpressionEliminator::getStackElement(int _stackHeight) +EquivalenceClassId CommonSubexpressionEliminator::getStackElement(int _stackHeight) { // retrieve class by last sequence number unsigned nextSequence = getNextStackElementSequence(_stackHeight); @@ -154,13 +155,13 @@ CommonSubexpressionEliminator::EquivalenceClass CommonSubexpressionEliminator::g BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Stack too deep.")); // This is a special assembly item that refers to elements pre-existing on the initial stack. m_spareAssemblyItem.push_back(make_shared(dupInstruction(1 - _stackHeight))); - m_equivalenceClasses.push_back(make_pair(m_spareAssemblyItem.back().get(), EquivalenceClasses())); - return m_stackElements[make_pair(_stackHeight, nextSequence)] = EquivalenceClass(m_equivalenceClasses.size() - 1); + m_equivalenceClasses.push_back(make_pair(m_spareAssemblyItem.back().get(), EquivalenceClassIds())); + return m_stackElements[make_pair(_stackHeight, nextSequence)] = EquivalenceClassId(m_equivalenceClasses.size() - 1); } -CommonSubexpressionEliminator::EquivalenceClass CommonSubexpressionEliminator::getClass( +EquivalenceClassId CommonSubexpressionEliminator::getClass( const AssemblyItem& _item, - EquivalenceClasses const& _arguments + EquivalenceClassIds const& _arguments ) { // do a clever search, i.e. @@ -168,7 +169,7 @@ CommonSubexpressionEliminator::EquivalenceClass CommonSubexpressionEliminator::g // - check whether the two items are equal for a SUB instruction // - check whether 0 or 1 is in one of the classes for a MUL // - for commutative opcodes, sort the arguments before searching - for (EquivalenceClass c = 0; c < m_equivalenceClasses.size(); ++c) + for (EquivalenceClassId c = 0; c < m_equivalenceClasses.size(); ++c) { AssemblyItem const& classItem = *m_equivalenceClasses[c].first; if (classItem != _item) @@ -184,7 +185,7 @@ CommonSubexpressionEliminator::EquivalenceClass CommonSubexpressionEliminator::g if (_item.type() == Operation && _arguments.size() == 2 && all_of( _arguments.begin(), _arguments.end(), - [this](EquivalenceClass eqc) { return m_equivalenceClasses[eqc].first->match(Push); })) + [this](EquivalenceClassId eqc) { return m_equivalenceClasses[eqc].first->match(Push); })) { map> const arithmetics = { @@ -232,3 +233,14 @@ unsigned CommonSubexpressionEliminator::getNextStackElementSequence(int _stackHe else return 0; } + + +AssemblyItems CSECodeGenerator::generateCode( + int _targetStackHeight, + map const& _targetStackContents, + vector> const& _equivalenceClasses) +{ + m_generatedItems.clear(); + m_classRequestCount.clear(); + return m_generatedItems; +} diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h index 2f932ba49..c15b3efad 100644 --- a/libevmcore/CommonSubexpressionEliminator.h +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -33,6 +33,10 @@ namespace eth { class AssemblyItem; +using AssemblyItems = std::vector; + +using EquivalenceClassId = unsigned; +using EquivalenceClassIds = std::vector; /** * Optimizer step that performs common subexpression elimination and stack reorginasation, @@ -56,27 +60,24 @@ public: _AssemblyItemIterator feedItems(_AssemblyItemIterator _iterator, _AssemblyItemIterator _end); /// @returns the resulting items after optimization. - std::vector getOptimizedItems() const; + AssemblyItems getOptimizedItems(); private: - using EquivalenceClass = unsigned; - using EquivalenceClasses = std::vector; - /// @returns true if the given items starts a new basic block bool breaksBasicBlock(AssemblyItem const& _item); /// Feeds the item into the system for analysis. void feedItem(AssemblyItem const& _item); /// Assigns a new equivalence class to the next sequence number of the given stack element. - void setStackElement(int _stackHeight, EquivalenceClass _class); + void setStackElement(int _stackHeight, EquivalenceClassId _class); /// Swaps the given stack elements in their next sequence number. void swapStackElements(int _stackHeightA, int _stackHeightB); /// Retrieves the current equivalence class fo the given stack element (or generates a new /// one if it does not exist yet). - EquivalenceClass getStackElement(int _stackHeight); + EquivalenceClassId getStackElement(int _stackHeight); /// Retrieves the equivalence class resulting from the given item applied to the given classes, /// might also create a new one. - EquivalenceClass getClass(AssemblyItem const& _item, EquivalenceClasses const& _arguments = {}); + EquivalenceClassId getClass(AssemblyItem const& _item, EquivalenceClassIds const& _arguments = {}); /// @returns the next sequence number of the given stack element. unsigned getNextStackElementSequence(int _stackHeight); @@ -84,12 +85,32 @@ private: /// Current stack height, can be negative. int m_stackHeight = 0; /// Mapping (stack height, sequence number) -> equivalence class - std::map, EquivalenceClass> m_stackElements; + std::map, EquivalenceClassId> m_stackElements; /// Vector of equivalence class representatives - we only store one item of an equivalence /// class and the index is used as identifier. - std::vector> m_equivalenceClasses; + std::vector> m_equivalenceClasses; /// List of items generated during analysis. std::vector> m_spareAssemblyItem; + +}; + +class CSECodeGenerator +{ +public: + /// @returns the assembly items generated from the given requirements + /// @param _targetStackHeight target stack height starting from an assumed initial stack height of zero + /// @param _targetStackContents final contents of the stack, by stack height relative to initial + /// @param _equivalenceClasses equivalence classes as expressions of how to compute them + AssemblyItems generateCode( + int _targetStackHeight, + std::map const& _targetStackContents, + std::vector> const& _equivalenceClasses + ); + +private: + AssemblyItems m_generatedItems; + /// Number of requests for an equivalence class, used as a reference counter. + std::map m_classRequestCount; }; template From 6f54f1046ab3fe00acefe2e9d4ee683be1b569cb Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 18 Mar 2015 20:04:30 +0100 Subject: [PATCH 038/228] Code generation part 1. --- libevmcore/CommonSubexpressionEliminator.cpp | 167 ++++++++++++++++++- libevmcore/CommonSubexpressionEliminator.h | 39 ++++- 2 files changed, 194 insertions(+), 12 deletions(-) diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index c442425a4..711b6d016 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -22,6 +22,7 @@ */ #include +#include #include #include @@ -56,16 +57,19 @@ vector CommonSubexpressionEliminator::getOptimizedItems() streamEquivalenceClass(cout, eqClass); cout << "----------------------------" << endl; + map currentStackContents; map targetStackContents; int minStackHeight = m_stackHeight; if (m_stackElements.size() > 0) minStackHeight = min(minStackHeight, m_stackElements.begin()->first.first); for (int stackHeight = minStackHeight; stackHeight <= m_stackHeight; ++stackHeight) + { + if (stackHeight <= 0) + currentStackContents[stackHeight] = getClass(AssemblyItem(dupInstruction(1 - stackHeight))); targetStackContents[stackHeight] = getStackElement(stackHeight); + } - CSECodeGenerator generator; - - return generator.generateCode(m_stackHeight, targetStackContents, m_equivalenceClasses); + return CSECodeGenerator().generateCode(currentStackContents, targetStackContents, m_equivalenceClasses); } bool CommonSubexpressionEliminator::breaksBasicBlock(AssemblyItem const& _item) @@ -234,13 +238,160 @@ unsigned CommonSubexpressionEliminator::getNextStackElementSequence(int _stackHe return 0; } - AssemblyItems CSECodeGenerator::generateCode( - int _targetStackHeight, + map const& _currentStack, map const& _targetStackContents, - vector> const& _equivalenceClasses) + vector> const& _equivalenceClasses +) { - m_generatedItems.clear(); - m_classRequestCount.clear(); + // reset + *this = move(CSECodeGenerator()); + m_stack = _currentStack; + m_equivalenceClasses = _equivalenceClasses; + for (auto const& item: m_stack) + m_classPositions[item.second] = item.first; + + // generate the dependency graph + for (auto const& stackContent: _targetStackContents) + { + m_finalClasses.insert(stackContent.second); + addDependencies(stackContent.second); + } + + for (auto const& cid: m_finalClasses) + generateClassElement(cid); + + // @TODO shuffle and copy the elements + + cout << "--------------- generated code: ---------------" << endl; + for (auto const& it: m_generatedItems) + cout << it << endl; + cout << "-----------------------------" << endl; + return m_generatedItems; } + +void CSECodeGenerator::addDependencies(EquivalenceClassId _c) +{ + if (m_neededBy.count(_c)) + return; + for (EquivalenceClassId argument: m_equivalenceClasses[_c].second) + { + addDependencies(argument); + m_neededBy.insert(make_pair(argument, _c)); + } +} + +int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) +{ + if (m_classPositions.count(_c)) + return m_classPositions[_c]; + assertThrow( + m_classPositions[_c] != c_invalidPosition, + OptimizerException, + "Element already removed but still needed." + ); + EquivalenceClassIds const& arguments = m_equivalenceClasses[_c].second; + for (EquivalenceClassId arg: boost::adaptors::reverse(arguments)) + generateClassElement(arg); + + if (arguments.size() == 1) + { + if (canBeRemoved(arguments[0], _c)) + appendSwap(generateClassElement(arguments[0])); + else + appendDup(generateClassElement(arguments[0])); + } + else if (arguments.size() == 2) + { + if (canBeRemoved(arguments[1], _c)) + { + appendSwap(generateClassElement(arguments[1])); + if (arguments[0] == arguments[1]) + appendDup(m_stackHeight); + else if (canBeRemoved(arguments[0], _c)) + { + appendSwap(m_stackHeight - 1); + appendSwap(generateClassElement(arguments[1])); + } + else + appendDup(generateClassElement(arguments[1])); + } + else + { + if (arguments[0] == arguments[1]) + { + appendDup(generateClassElement(arguments[0])); + appendDup(m_stackHeight); + } + else if (canBeRemoved(arguments[0], _c)) + { + appendSwap(generateClassElement(arguments[0])); + appendDup(generateClassElement(arguments[1])); + appendSwap(m_stackHeight - 1); + } + else + { + appendDup(generateClassElement(arguments[1])); + appendDup(generateClassElement(arguments[0])); + } + } + } + else + assertThrow( + arguments.size() <= 2, + OptimizerException, + "Opcodes with more than two arguments not implemented yet." + ); + for (auto arg: arguments) + if (canBeRemoved(arg, _c)) + m_classPositions[arguments[1]] = c_invalidPosition; + appendItem(*m_equivalenceClasses[_c].first); + m_stack[m_stackHeight] = _c; + return m_classPositions[_c] = m_stackHeight; +} + +bool CSECodeGenerator::canBeRemoved(EquivalenceClassId _element, EquivalenceClassId _result) +{ + // Returns false if _element is finally needed or is needed by a class that has not been + // computed yet. Note that m_classPositions also includes classes that were deleted in the meantime. + if (m_finalClasses.count(_element)) + return false; + + auto range = m_neededBy.equal_range(_element); + for (auto it = range.first; it != range.second; ++it) + if (it->second != _result && !m_classPositions.count(it->second)) + return false; + return true; +} + +void CSECodeGenerator::appendDup(int _fromPosition) +{ + m_generatedItems.push_back(AssemblyItem(swapInstruction(1 + m_stackHeight - _fromPosition))); + int nr = 1 + m_stackHeight - _fromPosition; + assertThrow(1 <= nr && nr <= 16, OptimizerException, "Stack too deep."); + m_generatedItems.push_back(AssemblyItem(dupInstruction(nr))); + m_stackHeight++; + m_stack[m_stackHeight] = m_stack[_fromPosition]; +} + +void CSECodeGenerator::appendSwap(int _fromPosition) +{ + if (_fromPosition == m_stackHeight) + return; + int nr = m_stackHeight - _fromPosition; + assertThrow(1 <= nr && nr <= 16, OptimizerException, "Stack too deep."); + m_generatedItems.push_back(AssemblyItem(swapInstruction(nr))); + // only update if they are the "canonical" positions + if (m_classPositions[m_stack[m_stackHeight]] == m_stackHeight) + m_classPositions[m_stack[m_stackHeight]] = _fromPosition; + if (m_classPositions[m_stack[_fromPosition]] == _fromPosition) + m_classPositions[m_stack[_fromPosition]] = m_stackHeight; + swap(m_stack[m_stackHeight], m_stack[_fromPosition]); +} + +void CSECodeGenerator::appendItem(AssemblyItem const& _item) +{ + m_generatedItems.push_back(_item); + m_stackHeight += _item.deposit(); +} diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h index c15b3efad..e93ccfaad 100644 --- a/libevmcore/CommonSubexpressionEliminator.h +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -26,6 +26,7 @@ #include #include #include +#include namespace dev { @@ -98,19 +99,49 @@ class CSECodeGenerator { public: /// @returns the assembly items generated from the given requirements - /// @param _targetStackHeight target stack height starting from an assumed initial stack height of zero + /// @param _currentStack current contents of the stack (up to stack height of zero) /// @param _targetStackContents final contents of the stack, by stack height relative to initial /// @param _equivalenceClasses equivalence classes as expressions of how to compute them AssemblyItems generateCode( - int _targetStackHeight, + std::map const& _currentStack, std::map const& _targetStackContents, std::vector> const& _equivalenceClasses ); private: + /// Recursively discovers all dependencies to @a m_requests. + void addDependencies(EquivalenceClassId _c); + + /// Produce code that generates the given element if it is not yet present. + /// @returns the stack position of the element. + int generateClassElement(EquivalenceClassId _c); + + /// @returns true if @a _element can be removed while computing @a _result. + bool canBeRemoved(EquivalenceClassId _element, EquivalenceClassId _result); + + /// Appends a dup instruction to m_generatedItems to retrieve the element at the given stack position. + void appendDup(int _fromPosition); + /// Appends a swap instruction to m_generatedItems to retrieve the element at the given stack position. + void appendSwap(int _fromPosition); + /// Appends the given assembly item. + void appendItem(AssemblyItem const& _item); + + static const int c_invalidPosition = std::numeric_limits::min(); + AssemblyItems m_generatedItems; - /// Number of requests for an equivalence class, used as a reference counter. - std::map m_classRequestCount; + /// Current height of the stack relative to the start. + int m_stackHeight = 0; + /// If (b, a) is in m_requests then b is needed to compute a. + std::multimap m_neededBy; + /// Current content of the stack. + std::map m_stack; + /// Current positions of equivalence classes, equal to c_invalidPosition if already deleted. + std::map m_classPositions; + + /// The actual eqivalence class items and how to compute them. + std::vector> m_equivalenceClasses; + /// The set of equivalence classes that should be present on the stack at the end. + std::set m_finalClasses; }; template From e8463940d63e59806969cc9795fd707ef383227e Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 19 Mar 2015 18:11:58 +0100 Subject: [PATCH 039/228] Code generation part 2. --- libevmcore/Assembly.cpp | 114 ++----- libevmcore/CommonSubexpressionEliminator.cpp | 332 +++++++++++++------ libevmcore/CommonSubexpressionEliminator.h | 37 ++- test/SolidityOptimizer.cpp | 163 +++++++-- 4 files changed, 428 insertions(+), 218 deletions(-) diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index ad464721b..ab9d76911 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -288,12 +288,6 @@ inline bool matches(AssemblyItemsConstRef _a, AssemblyItemsConstRef _b) return true; } -inline bool popCountIncreased(AssemblyItemsConstRef _pre, AssemblyItems const& _post) -{ - auto isPop = [](AssemblyItem const& _item) -> bool { return _item.match(AssemblyItem(Instruction::POP)); }; - return count_if(begin(_post), end(_post), isPop) > count_if(begin(_pre), end(_pre), isPop); -} - //@todo this has to move to a special optimizer class soon template unsigned bytesRequiredBySlice(Iterator _begin, Iterator _end) @@ -313,29 +307,6 @@ Assembly& Assembly::optimise(bool _enable) { if (!_enable) return *this; - auto signextend = [](u256 a, u256 b) -> u256 - { - if (a >= 31) - return b; - unsigned testBit = unsigned(a) * 8 + 7; - u256 mask = (u256(1) << testBit) - 1; - return boost::multiprecision::bit_test(b, testBit) ? b | ~mask : b & mask; - }; - map> const c_simple = - { - { Instruction::SUB, [](u256 a, u256 b)->u256{return a - b;} }, - { Instruction::DIV, [](u256 a, u256 b)->u256{return a / b;} }, - { Instruction::SDIV, [](u256 a, u256 b)->u256{return s2u(u2s(a) / u2s(b));} }, - { Instruction::MOD, [](u256 a, u256 b)->u256{return a % b;} }, - { Instruction::SMOD, [](u256 a, u256 b)->u256{return s2u(u2s(a) % u2s(b));} }, - { Instruction::EXP, [](u256 a, u256 b)->u256{return (u256)boost::multiprecision::powm((bigint)a, (bigint)b, bigint(1) << 256);} }, - { Instruction::SIGNEXTEND, signextend }, - { Instruction::LT, [](u256 a, u256 b)->u256{return a < b ? 1 : 0;} }, - { Instruction::GT, [](u256 a, u256 b)->u256{return a > b ? 1 : 0;} }, - { Instruction::SLT, [](u256 a, u256 b)->u256{return u2s(a) < u2s(b) ? 1 : 0;} }, - { Instruction::SGT, [](u256 a, u256 b)->u256{return u2s(a) > u2s(b) ? 1 : 0;} }, - { Instruction::EQ, [](u256 a, u256 b)->u256{return a == b ? 1 : 0;} }, - }; map> const c_associative = { { Instruction::ADD, [](u256 a, u256 b)->u256{return a + b;} }, @@ -358,8 +329,6 @@ Assembly& Assembly::optimise(bool _enable) { { Instruction::ISZERO, Instruction::ISZERO }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, }; - for (auto const& i: c_simple) - rules.push_back({ { Push, Push, i.first }, [&](AssemblyItemsConstRef m) -> AssemblyItems { return { i.second(m[1].data(), m[0].data()) }; } }); for (auto const& i: c_associative) { rules.push_back({ { Push, Push, i.first }, [&](AssemblyItemsConstRef m) -> AssemblyItems { return { i.second(m[1].data(), m[0].data()) }; } }); @@ -371,64 +340,33 @@ Assembly& Assembly::optimise(bool _enable) // jump to next instruction rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }}); - // pop optimization, do not compute values that are popped again anyway - rules.push_back({ { AssemblyItem(UndefinedItem), Instruction::POP }, [](AssemblyItemsConstRef m) -> AssemblyItems - { - if (m[0].type() != Operation) - return m.toVector(); - Instruction instr = m[0].instruction(); - if (Instruction::DUP1 <= instr && instr <= Instruction::DUP16) - return {}; - InstructionInfo info = instructionInfo(instr); - if (info.sideEffects || info.additional != 0 || info.ret != 1) - return m.toVector(); - return AssemblyItems(info.args, Instruction::POP); - } }); - // compute constants close to powers of two by expressions - auto computeConstants = [](AssemblyItemsConstRef m) -> AssemblyItems - { - u256 const& c = m[0].data(); - unsigned const minBits = 4 * 8; - if (c < (bigint(1) << minBits)) - return m.toVector(); // we need at least "PUSH1 PUSH1 <2> EXP" - if (c == u256(-1)) - return {u256(0), Instruction::NOT}; - for (unsigned bits = minBits; bits < 256; ++bits) - { - bigint const diff = c - (bigint(1) << bits); - if (abs(diff) > 0xff) - continue; - AssemblyItems powerOfTwo{u256(bits), u256(2), Instruction::EXP}; - if (diff == 0) - return powerOfTwo; - return AssemblyItems{u256(abs(diff))} + powerOfTwo + - AssemblyItems{diff > 0 ? Instruction::ADD : Instruction::SUB}; - } - return m.toVector(); - }; - rules.push_back({{Push}, computeConstants}); - - copt << *this; - - copt << "Performing common subexpression elimination..."; - AssemblyItems optimizedItems; - for (auto iter = m_items.begin(); iter != m_items.end(); ++iter) - { - CommonSubexpressionEliminator eliminator; - iter = eliminator.feedItems(iter, m_items.end()); - optimizedItems += eliminator.getOptimizedItems(); - if (iter != m_items.end()) - optimizedItems.push_back(*iter); - } - copt << "Old size: " << m_items.size() << ", new size: " << optimizedItems.size(); -// swap(m_items, optimizedItems); - copt << *this; unsigned total = 0; for (unsigned count = 1; count > 0; total += count) { count = 0; + + copt << "Performing common subexpression elimination..."; + for (auto iter = m_items.begin(); iter != m_items.end();) + { + CommonSubexpressionEliminator eliminator; + auto orig = iter; + iter = eliminator.feedItems(iter, m_items.end()); + AssemblyItems optItems = eliminator.getOptimizedItems(); + copt << "Old size: " << (iter - orig) << ", new size: " << optItems.size(); + if (optItems.size() < size_t(iter - orig)) + { + // replace items + count++; + for (auto moveIter = optItems.begin(); moveIter != optItems.end(); ++orig, ++moveIter) + *orig = move(*moveIter); + iter = m_items.erase(orig, iter); + } + if (iter != m_items.end()) + ++iter; + } + for (unsigned i = 0; i < m_items.size(); ++i) { for (auto const& r: rules) @@ -437,12 +375,10 @@ Assembly& Assembly::optimise(bool _enable) if (matches(vr, &r.first)) { auto rw = r.second(vr); - unsigned const vrSizeInBytes = bytesRequiredBySlice(vr.begin(), vr.end()); - unsigned const rwSizeInBytes = bytesRequiredBySlice(rw.begin(), rw.end()); - if (rwSizeInBytes < vrSizeInBytes || (rwSizeInBytes == vrSizeInBytes && popCountIncreased(vr, rw))) + if (rw.size() < vr.size()) { - copt << vr << "matches" << AssemblyItemsConstRef(&r.first) << "becomes..."; - copt << AssemblyItemsConstRef(&rw); + copt << "Rule " << vr << " matches " << AssemblyItemsConstRef(&r.first) << " becomes..."; + copt << AssemblyItemsConstRef(&rw) << "\n"; if (rw.size() > vr.size()) { // create hole in the vector @@ -456,7 +392,7 @@ Assembly& Assembly::optimise(bool _enable) copy(rw.begin(), rw.end(), m_items.begin() + i); count++; - copt << "Now:\n" << m_items; + copt << "Now:" << m_items; } } } diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index 711b6d016..05d3d8dc4 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -31,10 +31,37 @@ using namespace dev; using namespace dev::eth; vector CommonSubexpressionEliminator::getOptimizedItems() +{ + map currentStackContents; + map targetStackContents; + int minHeight = m_stackHeight + 1; + if (!m_stackElements.empty()) + minHeight = min(minHeight, m_stackElements.begin()->first.first); + for (int height = minHeight; height <= max(0, m_stackHeight); ++height) + { + // make sure it is created + EquivalenceClassId c = getStackElement(height); + if (height <= 0) + currentStackContents[height] = getClass(AssemblyItem(dupInstruction(1 - height))); + if (height <= m_stackHeight) + targetStackContents[height] = c; + } + + // Debug info: + //stream(cout, currentStackContents, targetStackContents); + + return CSECodeGenerator().generateCode(currentStackContents, targetStackContents, m_equivalenceClasses); +} + +ostream& CommonSubexpressionEliminator::stream( + ostream& _out, + map _currentStack, + map _targetStack +) const { auto streamEquivalenceClass = [this](ostream& _out, EquivalenceClassId _id) { - auto const& eqClass = m_equivalenceClasses[_id]; + auto const& eqClass = m_equivalenceClasses.at(_id); _out << " " << _id << ": " << *eqClass.first; _out << "("; for (EquivalenceClassId arg: eqClass.second) @@ -42,59 +69,36 @@ vector CommonSubexpressionEliminator::getOptimizedItems() _out << ")" << endl; }; - cout << dec; - cout << "Optimizer results:" << endl; - cout << "Final stack height: " << m_stackHeight << endl; - cout << "Stack elements: " << endl; + _out << "Optimizer analysis:" << endl; + _out << "Final stack height: " << dec << m_stackHeight << endl; + _out << "Stack elements: " << endl; for (auto const& it: m_stackElements) { - cout - << " " << dec << it.first.first << "(" << it.first.second << ") = "; - streamEquivalenceClass(cout, it.second); + _out << " " << dec << it.first.first << "(" << it.first.second << ") = "; + streamEquivalenceClass(_out, it.second); } - cout << "Equivalence classes: " << endl; + _out << "Equivalence classes: " << endl; for (EquivalenceClassId eqClass = 0; eqClass < m_equivalenceClasses.size(); ++eqClass) - streamEquivalenceClass(cout, eqClass); - cout << "----------------------------" << endl; + streamEquivalenceClass(_out, eqClass); - map currentStackContents; - map targetStackContents; - int minStackHeight = m_stackHeight; - if (m_stackElements.size() > 0) - minStackHeight = min(minStackHeight, m_stackElements.begin()->first.first); - for (int stackHeight = minStackHeight; stackHeight <= m_stackHeight; ++stackHeight) + _out << "Current stack: " << endl; + for (auto const& it: _currentStack) { - if (stackHeight <= 0) - currentStackContents[stackHeight] = getClass(AssemblyItem(dupInstruction(1 - stackHeight))); - targetStackContents[stackHeight] = getStackElement(stackHeight); + _out << " " << dec << it.first << ": "; + streamEquivalenceClass(_out, it.second); } - - return CSECodeGenerator().generateCode(currentStackContents, targetStackContents, m_equivalenceClasses); -} - -bool CommonSubexpressionEliminator::breaksBasicBlock(AssemblyItem const& _item) -{ - switch (_item.type()) + _out << "Target stack: " << endl; + for (auto const& it: _targetStack) { - case UndefinedItem: - case Tag: - return true; - case Push: - case PushString: - case PushTag: - case PushSub: - case PushSubSize: - case PushProgramSize: - case PushData: - return false; - case Operation: - return instructionInfo(_item.instruction()).sideEffects; + _out << " " << dec << it.first << ": "; + streamEquivalenceClass(_out, it.second); } + + return _out; } void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) { - cout << _item << endl; if (_item.type() != Operation) { if (_item.deposit() != 1) @@ -105,12 +109,12 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) { Instruction instruction = _item.instruction(); InstructionInfo info = instructionInfo(instruction); - if (Instruction::DUP1 <= instruction && instruction <= Instruction::DUP16) + if (SemanticInformation::isDupInstruction(_item)) setStackElement( m_stackHeight + 1, getStackElement(m_stackHeight - int(instruction) + int(Instruction::DUP1)) ); - else if (Instruction::SWAP1 <= instruction && instruction <= Instruction::SWAP16) + else if (SemanticInformation::isSwapInstruction(_item)) swapStackElements( m_stackHeight, m_stackHeight - 1 - int(instruction) + int(Instruction::SWAP1) @@ -168,61 +172,75 @@ EquivalenceClassId CommonSubexpressionEliminator::getClass( EquivalenceClassIds const& _arguments ) { - // do a clever search, i.e. + // TODO: do a clever search, i.e. // - check for the presence of constants in the argument classes and do arithmetic // - check whether the two items are equal for a SUB instruction // - check whether 0 or 1 is in one of the classes for a MUL - // - for commutative opcodes, sort the arguments before searching + + EquivalenceClassIds args = _arguments; + if (SemanticInformation::isCommutativeOperation(_item)) + sort(args.begin(), args.end()); + + //@todo use a better data structure for search here for (EquivalenceClassId c = 0; c < m_equivalenceClasses.size(); ++c) { - AssemblyItem const& classItem = *m_equivalenceClasses[c].first; + AssemblyItem const& classItem = *m_equivalenceClasses.at(c).first; if (classItem != _item) continue; - if (_arguments.size() != m_equivalenceClasses[c].second.size()) - BOOST_THROW_EXCEPTION( - OptimizerException() << - errinfo_comment("Equal assembly items with different number of arguments.") - ); - if (equal(_arguments.begin(), _arguments.end(), m_equivalenceClasses[c].second.begin())) + + assertThrow( + args.size() == m_equivalenceClasses.at(c).second.size(), + OptimizerException, + "Equal assembly items with different number of arguments." + ); + if (equal(args.begin(), args.end(), m_equivalenceClasses.at(c).second.begin())) return c; } - if (_item.type() == Operation && _arguments.size() == 2 && all_of( - _arguments.begin(), - _arguments.end(), - [this](EquivalenceClassId eqc) { return m_equivalenceClasses[eqc].first->match(Push); })) + // constant folding + if (_item.type() == Operation && args.size() == 2 && all_of( + args.begin(), + args.end(), + [this](EquivalenceClassId eqc) { return m_equivalenceClasses.at(eqc).first->match(Push); })) { + auto signextend = [](u256 a, u256 b) -> u256 + { + if (a >= 31) + return b; + unsigned testBit = unsigned(a) * 8 + 7; + u256 mask = (u256(1) << testBit) - 1; + return boost::multiprecision::bit_test(b, testBit) ? b | ~mask : b & mask; + }; map> const arithmetics = { - //@todo these are not correct (e.g. for div by zero) - { Instruction::SUB, [](u256 a, u256 b)->u256{return a - b;} }, - { Instruction::DIV, [](u256 a, u256 b)->u256{return a / b;} }, - { Instruction::SDIV, [](u256 a, u256 b)->u256{return s2u(u2s(a) / u2s(b));} }, - { Instruction::MOD, [](u256 a, u256 b)->u256{return a % b;} }, - { Instruction::SMOD, [](u256 a, u256 b)->u256{return s2u(u2s(a) % u2s(b));} }, - { Instruction::EXP, [](u256 a, u256 b)->u256{return (u256)boost::multiprecision::powm((bigint)a, (bigint)b, bigint(1) << 256);} }, - //{ Instruction::SIGNEXTEND, signextend }, - { Instruction::LT, [](u256 a, u256 b)->u256{return a < b ? 1 : 0;} }, - { Instruction::GT, [](u256 a, u256 b)->u256{return a > b ? 1 : 0;} }, - { Instruction::SLT, [](u256 a, u256 b)->u256{return u2s(a) < u2s(b) ? 1 : 0;} }, - { Instruction::SGT, [](u256 a, u256 b)->u256{return u2s(a) > u2s(b) ? 1 : 0;} }, - { Instruction::EQ, [](u256 a, u256 b)->u256{return a == b ? 1 : 0;} }, - { Instruction::ADD, [](u256 a, u256 b)->u256{return a + b;} }, - { Instruction::MUL, [](u256 a, u256 b)->u256{return a * b;} }, - { Instruction::AND, [](u256 a, u256 b)->u256{return a & b;} }, - { Instruction::OR, [](u256 a, u256 b)->u256{return a | b;} }, - { Instruction::XOR, [](u256 a, u256 b)->u256{return a ^ b;} }, + { Instruction::SUB, [](u256 a, u256 b) -> u256 {return a - b; } }, + { Instruction::DIV, [](u256 a, u256 b) -> u256 {return b == 0 ? 0 : a / b; } }, + { Instruction::SDIV, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : s2u(u2s(a) / u2s(b)); } }, + { Instruction::MOD, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : a % b; } }, + { Instruction::SMOD, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : s2u(u2s(a) % u2s(b)); } }, + { Instruction::EXP, [](u256 a, u256 b) -> u256 { return (u256)boost::multiprecision::powm(bigint(a), bigint(b), bigint(1) << 256); } }, + { Instruction::SIGNEXTEND, signextend }, + { Instruction::LT, [](u256 a, u256 b) -> u256 { return a < b ? 1 : 0; } }, + { Instruction::GT, [](u256 a, u256 b) -> u256 { return a > b ? 1 : 0; } }, + { Instruction::SLT, [](u256 a, u256 b) -> u256 { return u2s(a) < u2s(b) ? 1 : 0; } }, + { Instruction::SGT, [](u256 a, u256 b) -> u256 { return u2s(a) > u2s(b) ? 1 : 0; } }, + { Instruction::EQ, [](u256 a, u256 b) -> u256 { return a == b ? 1 : 0; } }, + { Instruction::ADD, [](u256 a, u256 b) -> u256 { return a + b; } }, + { Instruction::MUL, [](u256 a, u256 b) -> u256 { return a * b; } }, + { Instruction::AND, [](u256 a, u256 b) -> u256 { return a & b; } }, + { Instruction::OR, [](u256 a, u256 b) -> u256 { return a | b; } }, + { Instruction::XOR, [](u256 a, u256 b) -> u256 { return a ^ b; } }, }; if (arithmetics.count(_item.instruction())) { u256 result = arithmetics.at(_item.instruction())( - m_equivalenceClasses[_arguments[0]].first->data(), - m_equivalenceClasses[_arguments[1]].first->data() + m_equivalenceClasses.at(args[0]).first->data(), + m_equivalenceClasses.at(args[1]).first->data() ); m_spareAssemblyItem.push_back(make_shared(result)); return getClass(*m_spareAssemblyItem.back()); } } - m_equivalenceClasses.push_back(make_pair(&_item, _arguments)); + m_equivalenceClasses.push_back(make_pair(&_item, args)); return m_equivalenceClasses.size() - 1; } @@ -238,6 +256,64 @@ unsigned CommonSubexpressionEliminator::getNextStackElementSequence(int _stackHe return 0; } +bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item) +{ + switch (_item.type()) + { + case UndefinedItem: + case Tag: + return true; + case Push: + case PushString: + case PushTag: + case PushSub: + case PushSubSize: + case PushProgramSize: + case PushData: + return false; + case Operation: + { + if (isSwapInstruction(_item) || isDupInstruction(_item)) + return false; + InstructionInfo info = instructionInfo(_item.instruction()); + // the second requirement will be lifted once it is implemented + return info.sideEffects || info.args > 2; + } + } +} + +bool SemanticInformation::isCommutativeOperation(AssemblyItem const& _item) +{ + if (_item.type() != Operation) + return false; + switch (_item.instruction()) + { + case Instruction::ADD: + case Instruction::MUL: + case Instruction::EQ: + case Instruction::AND: + case Instruction::OR: + case Instruction::XOR: + return true; + default: + return false; + } +} + +bool SemanticInformation::isDupInstruction(AssemblyItem const& _item) +{ + if (_item.type() != Operation) + return false; + return Instruction::DUP1 <= _item.instruction() && _item.instruction() <= Instruction::DUP16; +} + +bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item) +{ + if (_item.type() != Operation) + return false; + return Instruction::SWAP1 <= _item.instruction() && _item.instruction() <= Instruction::SWAP16; +} + AssemblyItems CSECodeGenerator::generateCode( map const& _currentStack, map const& _targetStackContents, @@ -249,24 +325,48 @@ AssemblyItems CSECodeGenerator::generateCode( m_stack = _currentStack; m_equivalenceClasses = _equivalenceClasses; for (auto const& item: m_stack) - m_classPositions[item.second] = item.first; + if (!m_classPositions.count(item.second)) + m_classPositions[item.second] = item.first; + + // @todo: provide information about the positions of copies of class elements // generate the dependency graph - for (auto const& stackContent: _targetStackContents) + for (auto const& targetItem: _targetStackContents) { - m_finalClasses.insert(stackContent.second); - addDependencies(stackContent.second); + m_finalClasses.insert(targetItem.second); + addDependencies(targetItem.second); } - for (auto const& cid: m_finalClasses) - generateClassElement(cid); + // generate the actual elements + for (auto const& targetItem: _targetStackContents) + { + removeStackTopIfPossible(); + int position = generateClassElement(targetItem.second); + if (position == targetItem.first) + continue; + if (position < targetItem.first) + // it is already at its target, we need another copy + appendDup(position); + else + appendSwap(position); + appendSwap(targetItem.first); + } - // @TODO shuffle and copy the elements + // remove surplus elements + while (removeStackTopIfPossible()) + { + // no-op + } - cout << "--------------- generated code: ---------------" << endl; - for (auto const& it: m_generatedItems) - cout << it << endl; - cout << "-----------------------------" << endl; + // check validity + int finalHeight = 0; + if (!_targetStackContents.empty()) + finalHeight = (--_targetStackContents.end())->first; + else if (!_currentStack.empty()) + finalHeight = _currentStack.begin()->first - 1; + else + finalHeight = 0; + assertThrow(finalHeight == m_stackHeight, OptimizerException, "Incorrect final stack height."); return m_generatedItems; } @@ -275,7 +375,7 @@ void CSECodeGenerator::addDependencies(EquivalenceClassId _c) { if (m_neededBy.count(_c)) return; - for (EquivalenceClassId argument: m_equivalenceClasses[_c].second) + for (EquivalenceClassId argument: m_equivalenceClasses.at(_c).second) { addDependencies(argument); m_neededBy.insert(make_pair(argument, _c)); @@ -285,13 +385,15 @@ void CSECodeGenerator::addDependencies(EquivalenceClassId _c) int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) { if (m_classPositions.count(_c)) + { + assertThrow( + m_classPositions[_c] != c_invalidPosition, + OptimizerException, + "Element already removed but still needed." + ); return m_classPositions[_c]; - assertThrow( - m_classPositions[_c] != c_invalidPosition, - OptimizerException, - "Element already removed but still needed." - ); - EquivalenceClassIds const& arguments = m_equivalenceClasses[_c].second; + } + EquivalenceClassIds const& arguments = m_equivalenceClasses.at(_c).second; for (EquivalenceClassId arg: boost::adaptors::reverse(arguments)) generateClassElement(arg); @@ -312,10 +414,10 @@ int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) else if (canBeRemoved(arguments[0], _c)) { appendSwap(m_stackHeight - 1); - appendSwap(generateClassElement(arguments[1])); + appendSwap(generateClassElement(arguments[0])); } else - appendDup(generateClassElement(arguments[1])); + appendDup(generateClassElement(arguments[0])); } else { @@ -343,10 +445,20 @@ int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) OptimizerException, "Opcodes with more than two arguments not implemented yet." ); + for (size_t i = 0; i < arguments.size(); ++i) + assertThrow(m_stack[m_stackHeight - i] == arguments[i], OptimizerException, "Expected arguments not present." ); + + AssemblyItem const& item = *m_equivalenceClasses.at(_c).first; + while (SemanticInformation::isCommutativeOperation(item) && + !m_generatedItems.empty() && + m_generatedItems.back() == AssemblyItem(Instruction::SWAP1)) + appendSwap(m_stackHeight - 1); for (auto arg: arguments) if (canBeRemoved(arg, _c)) - m_classPositions[arguments[1]] = c_invalidPosition; - appendItem(*m_equivalenceClasses[_c].first); + m_classPositions[arg] = c_invalidPosition; + for (size_t i = 0; i < arguments.size(); ++i) + m_stack.erase(m_stackHeight - i); + appendItem(*m_equivalenceClasses.at(_c).first); m_stack[m_stackHeight] = _c; return m_classPositions[_c] = m_stackHeight; } @@ -365,9 +477,22 @@ bool CSECodeGenerator::canBeRemoved(EquivalenceClassId _element, EquivalenceClas return true; } +bool CSECodeGenerator::removeStackTopIfPossible() +{ + if (m_stack.empty()) + return false; + assertThrow(m_stack.count(m_stackHeight), OptimizerException, ""); + EquivalenceClassId top = m_stack[m_stackHeight]; + if (!canBeRemoved(top)) + return false; + m_generatedItems.push_back(AssemblyItem(Instruction::POP)); + m_stack.erase(m_stackHeight); + m_stackHeight--; + return true; +} + void CSECodeGenerator::appendDup(int _fromPosition) { - m_generatedItems.push_back(AssemblyItem(swapInstruction(1 + m_stackHeight - _fromPosition))); int nr = 1 + m_stackHeight - _fromPosition; assertThrow(1 <= nr && nr <= 16, OptimizerException, "Stack too deep."); m_generatedItems.push_back(AssemblyItem(dupInstruction(nr))); @@ -388,6 +513,13 @@ void CSECodeGenerator::appendSwap(int _fromPosition) if (m_classPositions[m_stack[_fromPosition]] == _fromPosition) m_classPositions[m_stack[_fromPosition]] = m_stackHeight; swap(m_stack[m_stackHeight], m_stack[_fromPosition]); + if (m_generatedItems.size() >= 2 && + SemanticInformation::isSwapInstruction(m_generatedItems.back()) && + *(m_generatedItems.end() - 2) == m_generatedItems.back()) + { + m_generatedItems.pop_back(); + m_generatedItems.pop_back(); + } } void CSECodeGenerator::appendItem(AssemblyItem const& _item) diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h index e93ccfaad..44891149d 100644 --- a/libevmcore/CommonSubexpressionEliminator.h +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -63,9 +64,14 @@ public: /// @returns the resulting items after optimization. AssemblyItems getOptimizedItems(); + /// Streams debugging information to @a _out. + std::ostream& stream( + std::ostream& _out, + std::map _currentStack = {}, + std::map _targetStack = {} + ) const; + private: - /// @returns true if the given items starts a new basic block - bool breaksBasicBlock(AssemblyItem const& _item); /// Feeds the item into the system for analysis. void feedItem(AssemblyItem const& _item); @@ -92,9 +98,26 @@ private: std::vector> m_equivalenceClasses; /// List of items generated during analysis. std::vector> m_spareAssemblyItem; +}; +/** + * Helper functions to provide context-independent information about assembly items. + */ +struct SemanticInformation +{ + /// @returns true if the given items starts a new basic block + static bool breaksBasicBlock(AssemblyItem const& _item); + /// @returns true if the item is a two-argument operation whose value does not depend on the + /// order of its arguments. + static bool isCommutativeOperation(AssemblyItem const& _item); + static bool isDupInstruction(AssemblyItem const& _item); + static bool isSwapInstruction(AssemblyItem const& _item); }; +/** + * Unit that generates code from current stack layout, target stack layout and information about + * the equivalence classes. + */ class CSECodeGenerator { public: @@ -116,8 +139,11 @@ private: /// @returns the stack position of the element. int generateClassElement(EquivalenceClassId _c); - /// @returns true if @a _element can be removed while computing @a _result. - bool canBeRemoved(EquivalenceClassId _element, EquivalenceClassId _result); + /// @returns true if @a _element can be removed - in general or, if given, while computing @a _result. + bool canBeRemoved(EquivalenceClassId _element, EquivalenceClassId _result = EquivalenceClassId(-1)); + + /// Appends code to remove the topmost stack element if it can be removed. + bool removeStackTopIfPossible(); /// Appends a dup instruction to m_generatedItems to retrieve the element at the given stack position. void appendDup(int _fromPosition); @@ -150,8 +176,7 @@ _AssemblyItemIterator CommonSubexpressionEliminator::feedItems( _AssemblyItemIterator _end ) { - std::cout << "---------------Feeding items to the CSE engine:--------------" << std::endl; - for (; _iterator != _end && !breaksBasicBlock(*_iterator); ++_iterator) + for (; _iterator != _end && !SemanticInformation::breaksBasicBlock(*_iterator); ++_iterator) feedItem(*_iterator); return _iterator; } diff --git a/test/SolidityOptimizer.cpp b/test/SolidityOptimizer.cpp index 85b77d210..9c6a4e361 100644 --- a/test/SolidityOptimizer.cpp +++ b/test/SolidityOptimizer.cpp @@ -26,8 +26,11 @@ #include #include #include +#include +#include using namespace std; +using namespace dev::eth; namespace dev { @@ -41,16 +44,21 @@ class OptimizerTestFramework: public ExecutionFramework public: OptimizerTestFramework() { } /// Compiles the source code with and without optimizing. - void compileBothVersions(unsigned _expectedSizeDecrease, std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "") { + void compileBothVersions( + std::string const& _sourceCode, + u256 const& _value = 0, + std::string const& _contractName = "" + ) + { m_optimize = false; bytes nonOptimizedBytecode = compileAndRun(_sourceCode, _value, _contractName); m_nonOptimizedContract = m_contractAddress; m_optimize = true; bytes optimizedBytecode = compileAndRun(_sourceCode, _value, _contractName); - int sizeDiff = nonOptimizedBytecode.size() - optimizedBytecode.size(); - BOOST_CHECK_MESSAGE(sizeDiff == int(_expectedSizeDecrease), "Bytecode shrank by " - + boost::lexical_cast(sizeDiff) + " bytes, expected: " - + boost::lexical_cast(_expectedSizeDecrease)); + BOOST_CHECK_MESSAGE( + nonOptimizedBytecode.size() > optimizedBytecode.size(), + "Optimizer did not reduce bytecode size." + ); m_optimizedContract = m_contractAddress; } @@ -81,24 +89,11 @@ BOOST_AUTO_TEST_CASE(smoke_test) return a; } })"; - compileBothVersions(29, sourceCode); + compileBothVersions(sourceCode); compareVersions("f(uint256)", u256(7)); } -BOOST_AUTO_TEST_CASE(large_integers) -{ - char const* sourceCode = R"( - contract test { - function f() returns (uint a, uint b) { - a = 0x234234872642837426347000000; - b = 0x10000000000000000000000002; - } - })"; - compileBothVersions(36, sourceCode); - compareVersions("f()"); -} - -BOOST_AUTO_TEST_CASE(invariants) +BOOST_AUTO_TEST_CASE(identities) { char const* sourceCode = R"( contract test { @@ -106,7 +101,7 @@ BOOST_AUTO_TEST_CASE(invariants) return int(0) | (int(1) * (int(0) ^ (0 + a))); } })"; - compileBothVersions(41, sourceCode); + compileBothVersions(sourceCode); compareVersions("f(uint256)", u256(0x12334664)); } @@ -120,7 +115,7 @@ BOOST_AUTO_TEST_CASE(unused_expressions) data; } })"; - compileBothVersions(36, sourceCode); + compileBothVersions(sourceCode); compareVersions("f()"); } @@ -135,10 +130,132 @@ BOOST_AUTO_TEST_CASE(constant_folding_both_sides) return 98 ^ (7 * ((1 | (x | 1000)) * 40) ^ 102); } })"; - compileBothVersions(37, sourceCode); + compileBothVersions(sourceCode); + compareVersions("f(uint256)"); +} + +BOOST_AUTO_TEST_CASE(storage_access) +{ + char const* sourceCode = R"( + contract test { + uint8[40] data; + function f(uint x) returns (uint y) { + data[2] = data[7] = uint8(x); + data[4] = data[2] * 10 + data[3]; + } + } + )"; + compileBothVersions(sourceCode); compareVersions("f(uint256)"); } +BOOST_AUTO_TEST_CASE(array_copy) +{ + char const* sourceCode = R"( + contract test { + bytes2[] data1; + bytes5[] data2; + function f(uint x) returns (uint l, uint y) { + for (uint i = 0; i < msg.data.length; ++i) + data1[i] = msg.data[i]; + data2 = data1; + l = data2.length; + y = uint(data2[x]); + } + } + )"; + compileBothVersions(sourceCode); + compareVersions("f(uint256)", 0); + compareVersions("f(uint256)", 10); + compareVersions("f(uint256)", 36); +} + +BOOST_AUTO_TEST_CASE(function_calls) +{ + char const* sourceCode = R"( + contract test { + function f1(uint x) returns (uint) { return x*x; } + function f(uint x) returns (uint) { return f1(7+x) - this.f1(x**9); } + } + )"; + compileBothVersions(sourceCode); + compareVersions("f(uint256)", 0); + compareVersions("f(uint256)", 10); + compareVersions("f(uint256)", 36); +} + +BOOST_AUTO_TEST_CASE(cse_intermediate_swap) +{ + eth::CommonSubexpressionEliminator cse; + AssemblyItems input{ + Instruction::SWAP1, Instruction::POP, Instruction::ADD, u256(0), Instruction::SWAP1, + Instruction::SLOAD, Instruction::SWAP1, u256(100), Instruction::EXP, Instruction::SWAP1, + Instruction::DIV, u256(0xff), Instruction::AND + }; + BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end()); + AssemblyItems output = cse.getOptimizedItems(); + BOOST_CHECK(!output.empty()); +} + +BOOST_AUTO_TEST_CASE(cse_negative_stack_access) +{ + eth::CommonSubexpressionEliminator cse; + AssemblyItems input{AssemblyItem(Instruction::DUP2), AssemblyItem(u256(0))}; + BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end()); + AssemblyItems output = cse.getOptimizedItems(); + BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); +} + +BOOST_AUTO_TEST_CASE(cse_negative_stack_end) +{ + eth::CommonSubexpressionEliminator cse; + AssemblyItems input{ + AssemblyItem(Instruction::ADD) + }; + BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end()); + AssemblyItems output = cse.getOptimizedItems(); + BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); +} + +BOOST_AUTO_TEST_CASE(cse_intermediate_negative_stack) +{ + eth::CommonSubexpressionEliminator cse; + AssemblyItems input{ + AssemblyItem(Instruction::ADD), + AssemblyItem(u256(1)), + AssemblyItem(Instruction::DUP2) + }; + BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end()); + AssemblyItems output = cse.getOptimizedItems(); + BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); +} + +BOOST_AUTO_TEST_CASE(cse_pop) +{ + eth::CommonSubexpressionEliminator cse; + AssemblyItems input{ + AssemblyItem(Instruction::POP) + }; + BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end()); + AssemblyItems output = cse.getOptimizedItems(); + BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); +} + +BOOST_AUTO_TEST_CASE(cse_unneeded_items) +{ + eth::CommonSubexpressionEliminator cse; + AssemblyItems input{ + AssemblyItem(Instruction::ADD), + AssemblyItem(Instruction::SWAP1), + AssemblyItem(Instruction::POP), + AssemblyItem(u256(7)), + AssemblyItem(u256(8)), + }; + BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end()); + AssemblyItems output = cse.getOptimizedItems(); + BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); +} + BOOST_AUTO_TEST_SUITE_END() } From 1603c1c4e3d436db0ca04c1254cd06dbc40b8bc1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 23 Mar 2015 16:53:28 +0100 Subject: [PATCH 040/228] Comments and renames. --- libevmcore/CommonSubexpressionEliminator.cpp | 96 +++++++++++--------- libevmcore/CommonSubexpressionEliminator.h | 12 ++- 2 files changed, 61 insertions(+), 47 deletions(-) diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index 05d3d8dc4..aa532b2ff 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -32,7 +32,7 @@ using namespace dev::eth; vector CommonSubexpressionEliminator::getOptimizedItems() { - map currentStackContents; + map initialStackContents; map targetStackContents; int minHeight = m_stackHeight + 1; if (!m_stackElements.empty()) @@ -42,7 +42,7 @@ vector CommonSubexpressionEliminator::getOptimizedItems() // make sure it is created EquivalenceClassId c = getStackElement(height); if (height <= 0) - currentStackContents[height] = getClass(AssemblyItem(dupInstruction(1 - height))); + initialStackContents[height] = getClass(AssemblyItem(dupInstruction(1 - height))); if (height <= m_stackHeight) targetStackContents[height] = c; } @@ -50,7 +50,7 @@ vector CommonSubexpressionEliminator::getOptimizedItems() // Debug info: //stream(cout, currentStackContents, targetStackContents); - return CSECodeGenerator().generateCode(currentStackContents, targetStackContents, m_equivalenceClasses); + return CSECodeGenerator().generateCode(initialStackContents, targetStackContents, m_equivalenceClasses); } ostream& CommonSubexpressionEliminator::stream( @@ -124,9 +124,9 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) vector arguments(info.args); for (int i = 0; i < info.args; ++i) arguments[i] = getStackElement(m_stackHeight - i); - setStackElement(m_stackHeight + info.ret - info.args, getClass(_item, arguments)); + setStackElement(m_stackHeight + _item.deposit(), getClass(_item, arguments)); } - m_stackHeight += info.ret - info.args; + m_stackHeight += _item.deposit(); } } @@ -202,33 +202,33 @@ EquivalenceClassId CommonSubexpressionEliminator::getClass( args.end(), [this](EquivalenceClassId eqc) { return m_equivalenceClasses.at(eqc).first->match(Push); })) { - auto signextend = [](u256 a, u256 b) -> u256 + auto signextend = [](u256 const& _a, u256 const& _b) -> u256 { - if (a >= 31) - return b; - unsigned testBit = unsigned(a) * 8 + 7; + if (_a >= 31) + return _b; + unsigned testBit = unsigned(_a) * 8 + 7; u256 mask = (u256(1) << testBit) - 1; - return boost::multiprecision::bit_test(b, testBit) ? b | ~mask : b & mask; + return boost::multiprecision::bit_test(_b, testBit) ? _b | ~mask : _b & mask; }; - map> const arithmetics = + map> const arithmetics = { - { Instruction::SUB, [](u256 a, u256 b) -> u256 {return a - b; } }, - { Instruction::DIV, [](u256 a, u256 b) -> u256 {return b == 0 ? 0 : a / b; } }, - { Instruction::SDIV, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : s2u(u2s(a) / u2s(b)); } }, - { Instruction::MOD, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : a % b; } }, - { Instruction::SMOD, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : s2u(u2s(a) % u2s(b)); } }, - { Instruction::EXP, [](u256 a, u256 b) -> u256 { return (u256)boost::multiprecision::powm(bigint(a), bigint(b), bigint(1) << 256); } }, + { Instruction::SUB, [](u256 const& _a, u256 const& _b) -> u256 {return _a - _b; } }, + { Instruction::DIV, [](u256 const& _a, u256 const& _b) -> u256 {return _b == 0 ? 0 : _a / _b; } }, + { Instruction::SDIV, [](u256 const& _a, u256 const& _b) -> u256 { return _b == 0 ? 0 : s2u(u2s(_a) / u2s(_b)); } }, + { Instruction::MOD, [](u256 const& _a, u256 const& _b) -> u256 { return _b == 0 ? 0 : _a % _b; } }, + { Instruction::SMOD, [](u256 const& _a, u256 const& _b) -> u256 { return _b == 0 ? 0 : s2u(u2s(_a) % u2s(_b)); } }, + { Instruction::EXP, [](u256 const& _a, u256 const& _b) -> u256 { return (u256)boost::multiprecision::powm(bigint(_a), bigint(_b), bigint(1) << 256); } }, { Instruction::SIGNEXTEND, signextend }, - { Instruction::LT, [](u256 a, u256 b) -> u256 { return a < b ? 1 : 0; } }, - { Instruction::GT, [](u256 a, u256 b) -> u256 { return a > b ? 1 : 0; } }, - { Instruction::SLT, [](u256 a, u256 b) -> u256 { return u2s(a) < u2s(b) ? 1 : 0; } }, - { Instruction::SGT, [](u256 a, u256 b) -> u256 { return u2s(a) > u2s(b) ? 1 : 0; } }, - { Instruction::EQ, [](u256 a, u256 b) -> u256 { return a == b ? 1 : 0; } }, - { Instruction::ADD, [](u256 a, u256 b) -> u256 { return a + b; } }, - { Instruction::MUL, [](u256 a, u256 b) -> u256 { return a * b; } }, - { Instruction::AND, [](u256 a, u256 b) -> u256 { return a & b; } }, - { Instruction::OR, [](u256 a, u256 b) -> u256 { return a | b; } }, - { Instruction::XOR, [](u256 a, u256 b) -> u256 { return a ^ b; } }, + { Instruction::LT, [](u256 const& _a, u256 const& _b) -> u256 { return _a < _b ? 1 : 0; } }, + { Instruction::GT, [](u256 const& _a, u256 const& _b) -> u256 { return _a > _b ? 1 : 0; } }, + { Instruction::SLT, [](u256 const& _a, u256 const& _b) -> u256 { return u2s(_a) < u2s(_b) ? 1 : 0; } }, + { Instruction::SGT, [](u256 const& _a, u256 const& _b) -> u256 { return u2s(_a) > u2s(_b) ? 1 : 0; } }, + { Instruction::EQ, [](u256 const& _a, u256 const& _b) -> u256 { return _a == _b ? 1 : 0; } }, + { Instruction::ADD, [](u256 const& _a, u256 const& _b) -> u256 { return _a + _b; } }, + { Instruction::MUL, [](u256 const& _a, u256 const& _b) -> u256 { return _a * _b; } }, + { Instruction::AND, [](u256 const& _a, u256 const& _b) -> u256 { return _a & _b; } }, + { Instruction::OR, [](u256 const& _a, u256 const& _b) -> u256 { return _a | _b; } }, + { Instruction::XOR, [](u256 const& _a, u256 const& _b) -> u256 { return _a ^ _b; } }, }; if (arithmetics.count(_item.instruction())) { @@ -260,6 +260,7 @@ bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item) { switch (_item.type()) { + default: case UndefinedItem: case Tag: return true; @@ -275,6 +276,8 @@ bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item) { if (isSwapInstruction(_item) || isDupInstruction(_item)) return false; + if (_item.instruction() == Instruction::GAS || _item.instruction() == Instruction::PC) + return true; // GAS and PC assume a specific order of opcodes InstructionInfo info = instructionInfo(_item.instruction()); // the second requirement will be lifted once it is implemented return info.sideEffects || info.args > 2; @@ -315,14 +318,14 @@ bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item) } AssemblyItems CSECodeGenerator::generateCode( - map const& _currentStack, + map const& _initialStack, map const& _targetStackContents, vector> const& _equivalenceClasses ) { // reset *this = move(CSECodeGenerator()); - m_stack = _currentStack; + m_stack = _initialStack; m_equivalenceClasses = _equivalenceClasses; for (auto const& item: m_stack) if (!m_classPositions.count(item.second)) @@ -348,8 +351,8 @@ AssemblyItems CSECodeGenerator::generateCode( // it is already at its target, we need another copy appendDup(position); else - appendSwap(position); - appendSwap(targetItem.first); + appendSwapOrRemove(position); + appendSwapOrRemove(targetItem.first); } // remove surplus elements @@ -361,10 +364,13 @@ AssemblyItems CSECodeGenerator::generateCode( // check validity int finalHeight = 0; if (!_targetStackContents.empty()) + // have target stack, so its height should be the final height finalHeight = (--_targetStackContents.end())->first; - else if (!_currentStack.empty()) - finalHeight = _currentStack.begin()->first - 1; + else if (!_initialStack.empty()) + // no target stack, only erase the initial stack + finalHeight = _initialStack.begin()->first - 1; else + // neither initial no target stack, no change in height finalHeight = 0; assertThrow(finalHeight == m_stackHeight, OptimizerException, "Incorrect final stack height."); @@ -397,10 +403,14 @@ int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) for (EquivalenceClassId arg: boost::adaptors::reverse(arguments)) generateClassElement(arg); + // The arguments are somewhere on the stack now, so it remains to move them at the correct place. + // This is quite difficult as sometimes, the values also have to removed in this process + // (if canBeRemoved() returns true) and the two arguments can be equal. For now, this is + // implemented for every single case for combinations of up to two arguments manually. if (arguments.size() == 1) { if (canBeRemoved(arguments[0], _c)) - appendSwap(generateClassElement(arguments[0])); + appendSwapOrRemove(generateClassElement(arguments[0])); else appendDup(generateClassElement(arguments[0])); } @@ -408,13 +418,13 @@ int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) { if (canBeRemoved(arguments[1], _c)) { - appendSwap(generateClassElement(arguments[1])); + appendSwapOrRemove(generateClassElement(arguments[1])); if (arguments[0] == arguments[1]) appendDup(m_stackHeight); else if (canBeRemoved(arguments[0], _c)) { - appendSwap(m_stackHeight - 1); - appendSwap(generateClassElement(arguments[0])); + appendSwapOrRemove(m_stackHeight - 1); + appendSwapOrRemove(generateClassElement(arguments[0])); } else appendDup(generateClassElement(arguments[0])); @@ -428,9 +438,9 @@ int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) } else if (canBeRemoved(arguments[0], _c)) { - appendSwap(generateClassElement(arguments[0])); + appendSwapOrRemove(generateClassElement(arguments[0])); appendDup(generateClassElement(arguments[1])); - appendSwap(m_stackHeight - 1); + appendSwapOrRemove(m_stackHeight - 1); } else { @@ -452,7 +462,8 @@ int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) while (SemanticInformation::isCommutativeOperation(item) && !m_generatedItems.empty() && m_generatedItems.back() == AssemblyItem(Instruction::SWAP1)) - appendSwap(m_stackHeight - 1); + // this will not append a swap but remove the one that is already there + appendSwapOrRemove(m_stackHeight - 1); for (auto arg: arguments) if (canBeRemoved(arg, _c)) m_classPositions[arg] = c_invalidPosition; @@ -500,14 +511,15 @@ void CSECodeGenerator::appendDup(int _fromPosition) m_stack[m_stackHeight] = m_stack[_fromPosition]; } -void CSECodeGenerator::appendSwap(int _fromPosition) +void CSECodeGenerator::appendSwapOrRemove(int _fromPosition) { if (_fromPosition == m_stackHeight) return; int nr = m_stackHeight - _fromPosition; assertThrow(1 <= nr && nr <= 16, OptimizerException, "Stack too deep."); m_generatedItems.push_back(AssemblyItem(swapInstruction(nr))); - // only update if they are the "canonical" positions + // The value of a class can be present in multiple locations on the stack. We only update the + // "canonical" one that is tracked by m_classPositions if (m_classPositions[m_stack[m_stackHeight]] == m_stackHeight) m_classPositions[m_stack[m_stackHeight]] = _fromPosition; if (m_classPositions[m_stack[_fromPosition]] == _fromPosition) diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h index 44891149d..72dcfdf99 100644 --- a/libevmcore/CommonSubexpressionEliminator.h +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -41,7 +41,7 @@ using EquivalenceClassId = unsigned; using EquivalenceClassIds = std::vector; /** - * Optimizer step that performs common subexpression elimination and stack reorginasation, + * Optimizer step that performs common subexpression elimination and stack reorganisation, * i.e. it tries to infer equality among expressions and compute the values of two expressions * known to be equal only once. * @@ -122,11 +122,12 @@ class CSECodeGenerator { public: /// @returns the assembly items generated from the given requirements - /// @param _currentStack current contents of the stack (up to stack height of zero) + /// @param _initialStack current contents of the stack (up to stack height of zero) /// @param _targetStackContents final contents of the stack, by stack height relative to initial /// @param _equivalenceClasses equivalence classes as expressions of how to compute them + /// @note resuts the state of the object for each call. AssemblyItems generateCode( - std::map const& _currentStack, + std::map const& _initialStack, std::map const& _targetStackContents, std::vector> const& _equivalenceClasses ); @@ -148,11 +149,12 @@ private: /// Appends a dup instruction to m_generatedItems to retrieve the element at the given stack position. void appendDup(int _fromPosition); /// Appends a swap instruction to m_generatedItems to retrieve the element at the given stack position. - void appendSwap(int _fromPosition); + /// @note this might also remove the last item if it exactly the same swap instruction. + void appendSwapOrRemove(int _fromPosition); /// Appends the given assembly item. void appendItem(AssemblyItem const& _item); - static const int c_invalidPosition = std::numeric_limits::min(); + static const int c_invalidPosition = -0x7fffffff; AssemblyItems m_generatedItems; /// Current height of the stack relative to the start. From 859c68d7767aac23c39b6e4bd1546e158f4f1576 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 23 Mar 2015 17:24:59 +0100 Subject: [PATCH 041/228] Compiler error fixes. --- libevmcore/CommonSubexpressionEliminator.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h index 72dcfdf99..edf1e3f38 100644 --- a/libevmcore/CommonSubexpressionEliminator.h +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -67,8 +67,8 @@ public: /// Streams debugging information to @a _out. std::ostream& stream( std::ostream& _out, - std::map _currentStack = {}, - std::map _targetStack = {} + std::map _currentStack = std::map(), + std::map _targetStack = std::map() ) const; private: From 8926a979ed227ca5366b8b32035063994051a54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 24 Mar 2015 15:22:29 +0100 Subject: [PATCH 042/228] Select list-burr LLVM instruction scheduler Workaround for LLVM bug https://llvm.org/bugs/show_bug.cgi?id=22304 in source scheduler. --- evmjit/libevmjit/ExecutionEngine.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/evmjit/libevmjit/ExecutionEngine.cpp b/evmjit/libevmjit/ExecutionEngine.cpp index f9588fbea..c00691f3e 100644 --- a/evmjit/libevmjit/ExecutionEngine.cpp +++ b/evmjit/libevmjit/ExecutionEngine.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include "preprocessor/llvm_includes_start.h" #include @@ -82,7 +84,19 @@ void parseOptions() { static llvm::llvm_shutdown_obj shutdownObj{}; cl::AddExtraVersionPrinter(printVersion); - cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler"); + //cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler"); + + // FIXME: LLVM workaround: + // Manually select instruction scheduler other than "source". + // "source" scheduler has a bug: http://llvm.org/bugs/show_bug.cgi?id=22304 + auto envLine = std::getenv("EVMJIT"); + auto commandLine = std::string{"evmjit "} + (envLine ? envLine : "") + " -pre-RA-sched=list-burr\0"; + char const* argv[100] = {nullptr, }; + auto arg = std::strtok(&*commandLine.begin(), " "); + auto i = 0; + for (; i < sizeof(argv) / sizeof(argv[0]) && arg; ++i, arg = std::strtok(nullptr, " ")) + argv[i] = arg; + cl::ParseCommandLineOptions(i, argv, "Ethereum EVM JIT Compiler"); } } From fdd2959fff4815b0bce45019c22adf8825ab0192 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 24 Mar 2015 19:04:41 +0100 Subject: [PATCH 043/228] Adding some more reserved keywords --- libsolidity/Token.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 57d71c1fe..1435dcc57 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -310,6 +310,9 @@ namespace solidity K(Throw, "throw", 0) \ K(Try, "try", 0) \ K(Catch, "catch", 0) \ + K(Using, "using", 0) \ + K(Type, "type", 0) \ + K(TypeOf, "typeof", 0) \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ From c905a66b3e101f51fa054957f8e8fb6276f31062 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Tue, 24 Mar 2015 20:40:22 +0100 Subject: [PATCH 044/228] fixed numeber of http server threads, fixed #1147 --- alethzero/MainWin.cpp | 6 +++++- eth/main.cpp | 12 ++++++++++-- neth/main.cpp | 12 ++++++++++-- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index d566882f3..034952ecd 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -164,7 +164,11 @@ Main::Main(QWidget *parent) : bytesConstRef network((byte*)m_networkConfig.data(), m_networkConfig.size()); m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), false, {"eth", "shh"}, p2p::NetworkPreferences(), network)); - m_httpConnector.reset(new jsonrpc::HttpServer(8080)); +#if ETH_DEBUG + m_httpConnector.reset(new jsonrpc::HttpServer(8080, "", "", 1)); +#else + m_httpConnector.reset(new jsonrpc::HttpServer(8080, "", "", 4)); +#endif m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), keysAsVector(m_myKeys), this)); connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString))); m_server->setIdentities(keysAsVector(owned())); diff --git a/eth/main.cpp b/eth/main.cpp index eda6e12ae..e89a1250f 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -457,7 +457,11 @@ int main(int argc, char** argv) unique_ptr jsonrpcConnector; if (jsonrpc > -1) { - jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc)); +#if ETH_DEBUG + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 1)); +#else + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 4)); +#endif jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({us}))); jsonrpcServer->setIdentities({us}); jsonrpcServer->StartListening(); @@ -583,7 +587,11 @@ int main(int argc, char** argv) { if (jsonrpc < 0) jsonrpc = 8080; - jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc)); +#if ETH_DEBUG + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 1)); +#else + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 4)); +#endif jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({us}))); jsonrpcServer->setIdentities({us}); jsonrpcServer->StartListening(); diff --git a/neth/main.cpp b/neth/main.cpp index 4888f784d..fc4c8c9c6 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -474,7 +474,11 @@ int main(int argc, char** argv) unique_ptr jsonrpcConnector; if (jsonrpc > -1) { - jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc)); +#if ETH_DEBUG + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 1)); +#else + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 4)); +#endif jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({us}))); jsonrpcServer->setIdentities({us}); jsonrpcServer->StartListening(); @@ -625,7 +629,11 @@ int main(int argc, char** argv) { if (jsonrpc < 0) jsonrpc = 8080; - jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc)); +#if ETH_DEBUG + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 1)); +#else + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 4)); +#endif jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({us}))); jsonrpcServer->setIdentities({us}); jsonrpcServer->StartListening(); From b9a7f0815f49237030a06df42b4eac64f6f13a74 Mon Sep 17 00:00:00 2001 From: Ali Mashatan Date: Wed, 25 Mar 2015 02:47:15 +0430 Subject: [PATCH 045/228] Adding "create state" minor correction --- mix/qml/StatesComboBox.qml | 169 ++++++++++++++++++++++--------------- mix/qml/TransactionLog.qml | 7 +- 2 files changed, 104 insertions(+), 72 deletions(-) diff --git a/mix/qml/StatesComboBox.qml b/mix/qml/StatesComboBox.qml index 24bd57866..ada5e00d5 100644 --- a/mix/qml/StatesComboBox.qml +++ b/mix/qml/StatesComboBox.qml @@ -47,6 +47,7 @@ Rectangle { signal selectItem(real item); signal editItem(real item); + signal selectCreate(); property variant items; readonly property alias selectedItem: chosenItemText.text; @@ -86,7 +87,7 @@ Rectangle { } } } - +//ToDo: We need scrollbar for items Rectangle { id:dropDownList width:statesComboBox.width; @@ -96,93 +97,123 @@ Rectangle { anchors.top: chosenItem.bottom; anchors.margins: 2; color: statesComboBox.color + ColumnLayout{ + spacing: 2 + TableView { + id:listView + height:20; + implicitHeight: 0 + width:statesComboBox.width; + model: statesComboBox.items + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff; + currentRow: -1 + headerVisible: false; + backgroundVisible: false + alternatingRowColors : false; + frameVisible: false + + TableViewColumn { + role: "title" + title: "" + width: statesComboBox.width; + delegate: mainItemDelegate + } + + Component { + id: mainItemDelegate + Item{ + id: itemDelegate + width:statesComboBox.width; + height: statesComboBox.height; + Text { + id: textItemid + text: styleData.value + color: statesComboBox.colorItem; + anchors.top: parent.top; + anchors.left: parent.left; + anchors.margins: 5; - TableView { - id:listView - height:500; - width:statesComboBox.width; - model: statesComboBox.items - currentRow: -1 - headerVisible: false; - backgroundVisible: false - alternatingRowColors : false; - frameVisible: false - - TableViewColumn { - role: "title" - title: "" - width: statesComboBox.width; - delegate: mainItemDelegate - //elideMode: Text.ElideNone - } + } + Rectangle + { + id: spaceItemid + anchors.top: textItemid.top; + anchors.left: textItemid.right + width: parent.width - textItemid.width - imageItemid.width - textItemid.anchors.margins- textItemid.anchors.margins + } + Image { + id: imageItemid + height:20 + width:20; + visible: false; + fillMode: Image.PreserveAspectFit + source: "img/edit_combox.png" + anchors.top: spaceItemid.top; + anchors.left: spaceItemid.right; + } - Component { - id: mainItemDelegate - Item{ - id: itemDelegate + MouseArea { + anchors.fill: parent; + hoverEnabled : true + + onEntered: { + imageItemid.visible = true; + textItemid.color = statesComboBox.colorSelect; + } + onExited: { + imageItemid.visible = false; + textItemid.color = statesComboBox.colorItem; + } + onClicked: { + if (mouseX > imageItemid.x && mouseX < imageItemid.x+ imageItemid.width + && mouseY > imageItemid.y && mouseY < imageItemid.y+ imageItemid.height) + statesComboBox.editItem(styleData.row); + else { + statesComboBox.state = "" + var prevSelection = chosenItemText.text + chosenItemText.text = styleData.value + listView.currentRow = styleData.row; + statesComboBox.selectItem(styleData.row); + } + } + } + }//Item + }//Component + }//Table View + RowLayout{ + Rectangle{ + width: 1 + } + Text{ + id:createStateText width:statesComboBox.width; height: statesComboBox.height; - Text { - id: textItemid - text: styleData.value - color: statesComboBox.colorItem; - anchors.top: parent.top; - anchors.left: parent.left; - anchors.margins: 5; - - } - Rectangle + font.pointSize: 10 + text:"Create State ..." + MouseArea { - id: spaceItemid - anchors.top: textItemid.top; - anchors.left: textItemid.right - width: parent.width - textItemid.width - imageItemid.width - textItemid.anchors.margins- textItemid.anchors.margins - } - Image { - id: imageItemid - height:20 - width:20; - visible: false; - fillMode: Image.PreserveAspectFit - source: "img/edit_combox.png" - anchors.top: spaceItemid.top; - anchors.left: spaceItemid.right; - } - - MouseArea { anchors.fill: parent; hoverEnabled : true onEntered: { - imageItemid.visible = true; - textItemid.color = statesComboBox.colorSelect; + createStateText.color = statesComboBox.colorSelect; } onExited: { - imageItemid.visible = false; - textItemid.color = statesComboBox.colorItem; + createStateText.color = statesComboBox.colorItem; } onClicked: { - if (mouseX > imageItemid.x && mouseX < imageItemid.x+ imageItemid.width - && mouseY > imageItemid.y && mouseY < imageItemid.y+ imageItemid.height) - statesComboBox.editItem(styleData.row); - else { - statesComboBox.state = "" - var prevSelection = chosenItemText.text - chosenItemText.text = styleData.value - if(chosenItemText.text != prevSelection) - statesComboBox.comboClicked(); - listView.currentRow = styleData.row; - statesComboBox.selectItem(styleData.row); - } + statesComboBox.state = "" + statesComboBox.selectCreate(); } } - }//Item - }//Component + } + } } } states: State { name: "dropDown"; - PropertyChanges { target: dropDownList; height:20*statesComboBox.items.count } + PropertyChanges { target: dropDownList; height:(20*(statesComboBox.items.count+1)) } + PropertyChanges { target:listView; height:20; implicitHeight: (20*(statesComboBox.items.count)) } } } diff --git a/mix/qml/TransactionLog.qml b/mix/qml/TransactionLog.qml index d0386706e..e0aec1a32 100644 --- a/mix/qml/TransactionLog.qml +++ b/mix/qml/TransactionLog.qml @@ -64,7 +64,8 @@ Item { { id: statesCombo items:projectModel.stateListModel - onSelectItem: console.log("Combobox Select Item: " + item ) + //onSelectItem: console.log("Combobox Select Item: " + item ) + onSelectCreate: projectModel.stateListModel.addState(); onEditItem: projectModel.stateListModel.editState(item) colorItem: "black" colorSelect: "blue" @@ -77,7 +78,7 @@ Item { } } } - Button + /*Button { anchors.rightMargin: 9 anchors.verticalCenter: parent.verticalCenter @@ -88,7 +89,7 @@ Item { anchors.rightMargin: 9 anchors.verticalCenter: parent.verticalCenter action: addStateAction - } + }*/ Button { anchors.rightMargin: 9 From 7567cf72ec9bb11319eef9c218dd28459eb7c45e Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 09:31:43 +0100 Subject: [PATCH 046/228] split #1351, cmake changes --- cmake/EthDependencies.cmake | 3 +++ cmake/EthUtils.cmake | 40 +++++++++++++++++++++++++++++++++++++ cmake/scripts/runtest.cmake | 15 ++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 cmake/scripts/runtest.cmake diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index ea272da56..05893715e 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -30,6 +30,9 @@ if (APPLE) set (CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "/usr/local/opt/qt5") endif() +find_program(CTEST_COMMAND ctest) +message(STATUS "ctest path: ${CTEST_COMMAND}") + # Dependencies must have a version number, to ensure reproducible build. The version provided here is the one that is in the extdep repository. If you use system libraries, version numbers may be different. find_package (CryptoPP 5.6.2 EXACT REQUIRED) diff --git a/cmake/EthUtils.cmake b/cmake/EthUtils.cmake index c6fd43ed4..69690156a 100644 --- a/cmake/EthUtils.cmake +++ b/cmake/EthUtils.cmake @@ -22,3 +22,43 @@ macro(replace_if_different SOURCE DST) endif() endmacro() +macro(eth_add_test NAME) + + # parse arguments here + set(commands) + set(current_command "") + foreach (arg ${ARGN}) + if (arg STREQUAL "ARGS") + if (current_command) + list(APPEND commands ${current_command}) + endif() + set(current_command "") + else () + set(current_command "${current_command} ${arg}") + endif() + endforeach(arg) + list(APPEND commands ${current_command}) + + message(STATUS "test: ${NAME} | ${commands}") + + # create tests + set(index 0) + list(LENGTH commands count) + while (index LESS count) + list(GET commands ${index} test_arguments) + + set(run_test "--run_test=${NAME}") + add_test(NAME "${NAME}.${index}" COMMAND testeth ${run_test} ${test_arguments}) + + math(EXPR index "${index} + 1") + endwhile(index LESS count) + + # add target to run them + add_custom_target("test.${NAME}" + DEPENDS testeth + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -DETH_TEST_NAME="${NAME}" -DCTEST_COMMAND="${CTEST_COMMAND}" -P "${ETH_SCRIPTS_DIR}/runtest.cmake" + ) + +endmacro() + diff --git a/cmake/scripts/runtest.cmake b/cmake/scripts/runtest.cmake new file mode 100644 index 000000000..15f7409ef --- /dev/null +++ b/cmake/scripts/runtest.cmake @@ -0,0 +1,15 @@ +# Should be used to run ctest +# +# example usage: +# cmake -DETH_TEST_NAME=TestInterfaceStub -DCTEST_COMMAND=/path/to/ctest -P scripts/runtest.cmake + +if (NOT CTEST_COMMAND) + message(FATAL_ERROR "ctest could not be found!") +endif() + +# verbosity is off, cause BOOST_MESSAGE is not thread safe and output is a trash +# see https://codecrafter.wordpress.com/2012/11/01/c-unit-test-framework-adapter-part-3/ +# +# output might not be usefull cause of thread safety issue +execute_process(COMMAND ${CTEST_COMMAND} --force-new-ctest-process -C Debug --output-on-failure -j 4 -R "${ETH_TEST_NAME}[.].*") + From c4a4b8014b23fdbac8483eec487528f961eba4f3 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 09:41:47 +0100 Subject: [PATCH 047/228] fixed dependency on windows.h include order when using std::min && std::max --- cmake/EthCompilerSettings.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index a0d5a50c1..bbc8473e6 100755 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -38,7 +38,8 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # disable decorated name length exceeded, name was truncated (4503) # disable warning C4535: calling _set_se_translator() requires /EHa (for boost tests) # declare Windows XP requirement - add_compile_options(/MP /EHsc /wd4068 /wd4996 /wd4503 -D_WIN32_WINNT=0x0501) + # undefine windows.h MAX && MIN macros cause it cause conflicts with std::min && std::max functions + add_compile_options(/MP /EHsc /wd4068 /wd4996 /wd4503 -D_WIN32_WINNT=0x0501 /DNOMINMAX) # disable empty object file warning set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221") # warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification From 3d009ae161dc6b5d82709731e2e21837c6f32af8 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 10:10:28 +0100 Subject: [PATCH 048/228] ClientBase --- libethereum/BlockChain.h | 6 +- libethereum/Client.cpp | 375 ++--------------------------------- libethereum/Client.h | 135 ++----------- libethereum/ClientBase.cpp | 393 +++++++++++++++++++++++++++++++++++++ libethereum/ClientBase.h | 164 ++++++++++++++++ libethereum/Interface.h | 5 +- libethereum/State.h | 3 +- mix/MixClient.cpp | 326 +++--------------------------- mix/MixClient.h | 70 +++---- 9 files changed, 652 insertions(+), 825 deletions(-) create mode 100644 libethereum/ClientBase.cpp create mode 100644 libethereum/ClientBase.h diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 9676b4a78..235a39267 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -65,6 +65,7 @@ ldb::Slice toSlice(h256 _h, unsigned _sub = 0); using BlocksHash = std::map; using TransactionHashes = h256s; +using UncleHashes = h256s; enum { ExtraDetails = 0, @@ -130,7 +131,10 @@ public: TransactionHashes transactionHashes(h256 _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[1]) ret.push_back(sha3(t.data())); return ret; } TransactionHashes transactionHashes() const { return transactionHashes(currentHash()); } - /// Get a list of transaction hashes for a given block. Thread-safe. + /// Get a list of uncle hashes for a given block. Thread-safe. + UncleHashes uncleHashes(h256 _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[2]) ret.push_back(sha3(t.data())); return ret; } + UncleHashes uncleHashes() const { return uncleHashes(currentHash()); } + h256 numberHash(u256 _index) const { if (!_index) return genesisHash(); return queryExtras(h256(_index), m_blockHashes, x_blockHashes, NullBlockHash).value; } /** Get the block blooms for a number of blocks. Thread-safe. diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 9e8a611c6..f1ef6df71 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -186,11 +186,6 @@ void Client::doneWorking() m_postMine = m_preMine; } -void Client::flushTransactions() -{ - doWork(); -} - void Client::killChain() { bool wasMining = isMining(); @@ -249,61 +244,6 @@ void Client::clearPending() noteChanged(changeds); } -unsigned Client::installWatch(h256 _h, Reaping _r) -{ - unsigned ret; - { - Guard l(m_filterLock); - ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0; - m_watches[ret] = ClientWatch(_h, _r); - cwatch << "+++" << ret << _h.abridged(); - } - auto ch = logs(ret); - if (ch.empty()) - ch.push_back(InitialChange); - { - Guard l(m_filterLock); - swap(m_watches[ret].changes, ch); - } - return ret; -} - -unsigned Client::installWatch(LogFilter const& _f, Reaping _r) -{ - h256 h = _f.sha3(); - { - Guard l(m_filterLock); - if (!m_filters.count(h)) - { - cwatch << "FFF" << _f << h.abridged(); - m_filters.insert(make_pair(h, _f)); - } - } - return installWatch(h, _r); -} - -bool Client::uninstallWatch(unsigned _i) -{ - cwatch << "XXX" << _i; - - Guard l(m_filterLock); - - auto it = m_watches.find(_i); - if (it == m_watches.end()) - return false; - auto id = it->second.id; - m_watches.erase(it); - - auto fit = m_filters.find(id); - if (fit != m_filters.end()) - if (!--fit->second.refCount) - { - cwatch << "*X*" << fit->first << ":" << fit->second.filter; - m_filters.erase(fit); - } - return true; -} - void Client::noteChanged(h256Set const& _filters) { Guard l(m_filterLock); @@ -324,39 +264,6 @@ void Client::noteChanged(h256Set const& _filters) i.second.changes.clear(); } -LocalisedLogEntries Client::peekWatch(unsigned _watchId) const -{ - Guard l(m_filterLock); - -#if ETH_DEBUG - cdebug << "peekWatch" << _watchId; -#endif - auto& w = m_watches.at(_watchId); -#if ETH_DEBUG - cdebug << "lastPoll updated to " << chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); -#endif - w.lastPoll = chrono::system_clock::now(); - return w.changes; -} - -LocalisedLogEntries Client::checkWatch(unsigned _watchId) -{ - Guard l(m_filterLock); - LocalisedLogEntries ret; - -#if ETH_DEBUG && 0 - cdebug << "checkWatch" << _watchId; -#endif - auto& w = m_watches.at(_watchId); -#if ETH_DEBUG && 0 - cdebug << "lastPoll updated to " << chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); -#endif - std::swap(ret, w.changes); - w.lastPoll = chrono::system_clock::now(); - - return ret; -} - void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Set& io_changed, h256 _transactionHash) { Guard l(m_filterLock); @@ -475,68 +382,6 @@ void Client::setupState(State& _s) _s.commitToMine(m_bc); } -void Client::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) -{ - startWorking(); - - u256 n; - { - ReadGuard l(x_stateDB); - n = m_postMine.transactionsFrom(toAddress(_secret)); - } - Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); -// cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); - StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged()); - cnote << "New transaction " << t; - m_tq.attemptImport(t.rlp()); -} - -ExecutionResult Client::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) -{ - ExecutionResult ret; - try - { - u256 n; - State temp; - // cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); - { - ReadGuard l(x_stateDB); - temp = asOf(_blockNumber); - n = temp.transactionsFrom(toAddress(_secret)); - } - Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); - ret = temp.execute(m_bc, t.rlp(), Permanence::Reverted); - } - catch (...) - { - // TODO: Some sort of notification of failure. - } - return ret; -} - -ExecutionResult Client::create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) -{ - ExecutionResult ret; - try - { - u256 n; - State temp; - // cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); - { - ReadGuard l(x_stateDB); - temp = asOf(_blockNumber); - n = temp.transactionsFrom(toAddress(_secret)); - } - Transaction t(_value, _gasPrice, _gas, _data, n, _secret); - ret = temp.execute(m_bc, t.rlp(), Permanence::Reverted); - } - catch (...) - { - // TODO: Some sort of notification of failure. - } - return ret; -} - ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256 _value, u256 _gasPrice) { ExecutionResult ret; @@ -561,28 +406,6 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256 return ret; } -Address Client::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) -{ - startWorking(); - - u256 n; - { - ReadGuard l(x_stateDB); - n = m_postMine.transactionsFrom(toAddress(_secret)); - } - Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret); - cnote << "New transaction " << t; - m_tq.attemptImport(t.rlp()); - return right160(sha3(rlpList(t.sender(), t.nonce()))); -} - -void Client::inject(bytesConstRef _rlp) -{ - startWorking(); - - m_tq.attemptImport(_rlp); -} - pair Client::getWork() { Guard l(x_remoteMiner); @@ -730,15 +553,26 @@ void Client::doWork() } } -State Client::asOf(unsigned _h) const +State Client::asOf(BlockNumber _h) const { ReadGuard l(x_stateDB); if (_h == PendingBlock) return m_postMine; else if (_h == LatestBlock) return m_preMine; - else - return State(m_stateDB, m_bc, m_bc.numberHash(_h)); + + return State(m_stateDB, bc(), bc().numberHash(_h)); +} + +State Client::asOf(h256 _block) const +{ + ReadGuard l(x_stateDB); + return State(m_stateDB, bc(), _block); +} + +void Client::prepareForTransaction() +{ + startWorking(); } State Client::state(unsigned _txi, h256 _block) const @@ -759,183 +593,14 @@ eth::State Client::state(unsigned _txi) const return m_postMine.fromPending(_txi); } -StateDiff Client::diff(unsigned _txi, BlockNumber _block) const -{ - State st = asOf(_block); - return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); -} - -StateDiff Client::diff(unsigned _txi, h256 _block) const -{ - State st = state(_block); - return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); -} - -std::vector
Client::addresses(BlockNumber _block) const -{ - vector
ret; - for (auto const& i: asOf(_block).addresses()) - ret.push_back(i.first); - return ret; -} - -u256 Client::balanceAt(Address _a, BlockNumber _block) const -{ - return asOf(_block).balance(_a); -} - -std::map Client::storageAt(Address _a, BlockNumber _block) const -{ - return asOf(_block).storage(_a); -} - -u256 Client::countAt(Address _a, BlockNumber _block) const -{ - return asOf(_block).transactionsFrom(_a); -} - -u256 Client::stateAt(Address _a, u256 _l, BlockNumber _block) const -{ - return asOf(_block).storage(_a, _l); -} - -bytes Client::codeAt(Address _a, BlockNumber _block) const -{ - return asOf(_block).code(_a); -} - -Transaction Client::transaction(h256 _transactionHash) const -{ - return Transaction(m_bc.transaction(_transactionHash), CheckSignature::Range); -} - -Transaction Client::transaction(h256 _blockHash, unsigned _i) const -{ - auto bl = m_bc.block(_blockHash); - RLP b(bl); - if (_i < b[1].itemCount()) - return Transaction(b[1][_i].data(), CheckSignature::Range); - else - return Transaction(); -} - -BlockInfo Client::uncle(h256 _blockHash, unsigned _i) const -{ - auto bl = m_bc.block(_blockHash); - RLP b(bl); - if (_i < b[2].itemCount()) - return BlockInfo::fromHeader(b[2][_i].data()); - else - return BlockInfo(); -} - -unsigned Client::transactionCount(h256 _blockHash) const -{ - auto bl = m_bc.block(_blockHash); - RLP b(bl); - return b[1].itemCount(); -} - -unsigned Client::uncleCount(h256 _blockHash) const -{ - auto bl = m_bc.block(_blockHash); - RLP b(bl); - return b[2].itemCount(); -} - -Transactions Client::transactions(h256 _blockHash) const -{ - auto bl = m_bc.block(_blockHash); - RLP b(bl); - Transactions res; - for (unsigned i = 0; i < b[1].itemCount(); i++) - res.emplace_back(b[1][i].data(), CheckSignature::Range); - return res; -} - -TransactionHashes Client::transactionHashes(h256 _blockHash) const -{ - return m_bc.transactionHashes(_blockHash); -} - -LocalisedLogEntries Client::logs(unsigned _watchId) const +void Client::inject(bytesConstRef _rlp) { - LogFilter f; - try - { - Guard l(m_filterLock); - f = m_filters.at(m_watches.at(_watchId).id).filter; - } - catch (...) - { - return LocalisedLogEntries(); - } - return logs(f); + startWorking(); + + m_tq.attemptImport(_rlp); } -LocalisedLogEntries Client::logs(LogFilter const& _f) const +void Client::flushTransactions() { - LocalisedLogEntries ret; - unsigned begin = min(m_bc.number() + 1, (unsigned)_f.latest()); - unsigned end = min(m_bc.number(), min(begin, (unsigned)_f.earliest())); - - // Handle pending transactions differently as they're not on the block chain. - if (begin > m_bc.number()) - { - ReadGuard l(x_stateDB); - for (unsigned i = 0; i < m_postMine.pending().size(); ++i) - { - // Might have a transaction that contains a matching log. - TransactionReceipt const& tr = m_postMine.receipt(i); - auto sha3 = m_postMine.pending()[i].sha3(); - LogEntries le = _f.matches(tr); - if (le.size()) - for (unsigned j = 0; j < le.size(); ++j) - ret.insert(ret.begin(), LocalisedLogEntry(le[j], begin, sha3)); - } - begin = m_bc.number(); - } - - set matchingBlocks; - for (auto const& i: _f.bloomPossibilities()) - for (auto u: m_bc.withBlockBloom(i, end, begin)) - matchingBlocks.insert(u); - -#if ETH_DEBUG - unsigned falsePos = 0; -#endif - for (auto n: matchingBlocks) - { -#if ETH_DEBUG - int total = 0; -#endif - auto h = m_bc.numberHash(n); - auto receipts = m_bc.receipts(h).receipts; - for (size_t i = 0; i < receipts.size(); i++) - { - TransactionReceipt receipt = receipts[i]; - if (_f.matches(receipt.bloom())) - { - auto info = m_bc.info(h); - auto h = transaction(info.hash, i).sha3(); - LogEntries le = _f.matches(receipt); - if (le.size()) - { -#if ETH_DEBUG - total += le.size(); -#endif - for (unsigned j = 0; j < le.size(); ++j) - ret.insert(ret.begin(), LocalisedLogEntry(le[j], n, h)); - } - } -#if ETH_DEBUG - if (!total) - falsePos++; -#endif - } - } -#if ETH_DEBUG - cdebug << matchingBlocks.size() << "searched from" << (end - begin) << "skipped; " << falsePos << "false +ves"; -#endif - return ret; + doWork(); } diff --git a/libethereum/Client.h b/libethereum/Client.h index 6e34dc925..c49181a13 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -40,9 +40,8 @@ #include "TransactionQueue.h" #include "State.h" #include "CommonNet.h" -#include "LogFilter.h" #include "Miner.h" -#include "Interface.h" +#include "ClientBase.h" namespace dev { @@ -74,40 +73,6 @@ private: static const int GenesisBlock = INT_MIN; -struct InstalledFilter -{ - InstalledFilter(LogFilter const& _f): filter(_f) {} - - LogFilter filter; - unsigned refCount = 1; - LocalisedLogEntries changes; -}; - -static const h256 PendingChangedFilter = u256(0); -static const h256 ChainChangedFilter = u256(1); - -static const LogEntry SpecialLogEntry = LogEntry(Address(), h256s(), bytes()); -static const LocalisedLogEntry InitialChange(SpecialLogEntry, 0); - -struct ClientWatch -{ - ClientWatch(): lastPoll(std::chrono::system_clock::now()) {} - explicit ClientWatch(h256 _id, Reaping _r): id(_id), lastPoll(_r == Reaping::Automatic ? std::chrono::system_clock::now() : std::chrono::system_clock::time_point::max()) {} - - h256 id; - LocalisedLogEntries changes = LocalisedLogEntries{ InitialChange }; - mutable std::chrono::system_clock::time_point lastPoll = std::chrono::system_clock::now(); -}; - -struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 7; }; -#define cwatch dev::LogOutputStream() -struct WorkInChannel: public LogChannel { static const char* name() { return ">W>"; } static const int verbosity = 16; }; -struct WorkOutChannel: public LogChannel { static const char* name() { return "() -#define cworkin dev::LogOutputStream() -#define cworkout dev::LogOutputStream() - template struct ABISerialiser {}; template struct ABISerialiser> { static bytes serialise(FixedHash const& _t) { static_assert(N <= 32, "Cannot serialise hash > 32 bytes."); static_assert(N > 0, "Cannot serialise zero-length hash."); return bytes(32 - N, 0) + _t.asBytes(); } }; template <> struct ABISerialiser { static bytes serialise(u256 const& _t) { return h256(_t).asBytes(); } }; @@ -182,7 +147,7 @@ private: /** * @brief Main API hub for interfacing with Ethereum. */ -class Client: public MinerHost, public Interface, Worker +class Client: public MinerHost, public ClientBase, Worker { friend class Miner; @@ -211,88 +176,20 @@ public: /// Resets the gas pricer to some other object. void setGasPricer(std::shared_ptr _gp) { m_gp = _gp; } - /// Submits the given message-call transaction. - virtual void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override; - - /// Submits a new contract-creation transaction. - /// @returns the new contract's address (assuming it all goes through). - virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override; - /// Injects the RLP-encoded transaction given by the _rlp into the transaction queue directly. - virtual void inject(bytesConstRef _rlp) override; + virtual void inject(bytesConstRef _rlp); /// Blocks until all pending transactions have been processed. virtual void flushTransactions() override; - /// Makes the given call. Nothing is recorded into the state. - virtual ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock) override; - - /// Does the given creation. Nothing is recorded into the state. - /// @returns the pair of the Address of the created contract together with its code. - virtual ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock) override; - + using Interface::call; // to remove warning about hiding virtual function /// Makes the given call. Nothing is recorded into the state. This cheats by creating a null address and endowing it with a lot of ETH. ExecutionResult call(Address _dest, bytes const& _data = bytes(), u256 _gas = 125000, u256 _value = 0, u256 _gasPrice = 1 * ether); - // Informational stuff - - // [NEW API] - - using Interface::balanceAt; - using Interface::countAt; - using Interface::stateAt; - using Interface::codeAt; - using Interface::storageAt; - - virtual u256 balanceAt(Address _a, BlockNumber _block) const; - virtual u256 countAt(Address _a, BlockNumber _block) const; - virtual u256 stateAt(Address _a, u256 _l, BlockNumber _block) const; - virtual bytes codeAt(Address _a, BlockNumber _block) const; - virtual std::map storageAt(Address _a, BlockNumber _block) const; - - virtual unsigned installWatch(LogFilter const& _filter, Reaping _r = Reaping::Automatic) override; - virtual unsigned installWatch(h256 _filterId, Reaping _r = Reaping::Automatic) override; - virtual bool uninstallWatch(unsigned _watchId) override; - virtual LocalisedLogEntries peekWatch(unsigned _watchId) const; - virtual LocalisedLogEntries checkWatch(unsigned _watchId); - - virtual LocalisedLogEntries logs(unsigned _watchId) const; - virtual LocalisedLogEntries logs(LogFilter const& _filter) const; - - // [EXTRA API]: - - /// @returns the length of the chain. - virtual unsigned number() const { return m_bc.number(); } - - /// Get the list of pending transactions. - /// @TODO: Remove in favour of transactions(). - virtual Transactions pending() const { return m_postMine.pending(); } - - virtual h256 hashFromNumber(unsigned _number) const { return m_bc.numberHash(_number); } - virtual BlockInfo blockInfo(h256 _hash) const { return BlockInfo(m_bc.block(_hash)); } - virtual BlockDetails blockDetails(h256 _hash) const { return m_bc.details(_hash); } - virtual Transaction transaction(h256 _transactionHash) const; - virtual Transaction transaction(h256 _blockHash, unsigned _i) const; - virtual BlockInfo uncle(h256 _blockHash, unsigned _i) const; - virtual unsigned transactionCount(h256 _blockHash) const; - virtual unsigned uncleCount(h256 _blockHash) const; - virtual Transactions transactions(h256 _blockHash) const; - virtual TransactionHashes transactionHashes(h256 _blockHash) const; - - /// Differences between transactions. - using Interface::diff; - virtual StateDiff diff(unsigned _txi, h256 _block) const; - virtual StateDiff diff(unsigned _txi, BlockNumber _block) const; - - /// Get a list of all active addresses. - using Interface::addresses; - virtual std::vector
addresses(BlockNumber _block) const; - /// Get the remaining gas limit in this block. virtual u256 gasLimitRemaining() const { return m_postMine.gasLimitRemaining(); } // [PRIVATE API - only relevant for base clients, not available in general] - dev::eth::State state(unsigned _txi, h256 _block) const; dev::eth::State state(h256 _block) const; dev::eth::State state(unsigned _txi) const; @@ -317,10 +214,6 @@ public: /// Enable/disable fast mining. void setTurboMining(bool _enable = true) { m_turboMining = _enable; } - /// Set the coinbase address. - virtual void setAddress(Address _us) { m_preMine.setAddress(_us); } - /// Get the coinbase address. - virtual Address address() const { return m_preMine.address(); } /// Stops mining and sets the number of mining threads (0 for automatic). virtual void setMiningThreads(unsigned _threads = 0); /// Get the effective number of mining threads. @@ -356,6 +249,17 @@ public: void killChain(); protected: + /// InterfaceStub methods + virtual BlockChain const& bc() const override { return m_bc; } + + /// Returns the state object for the full block (i.e. the terminal state) for index _h. + /// Works properly with LatestBlock and PendingBlock. + virtual State asOf(BlockNumber _h) const override; + virtual State asOf(h256 _block) const override; + virtual State preMine() const override { ReadGuard l(x_stateDB); return m_preMine; } + virtual State postMine() const override { ReadGuard l(x_stateDB); return m_postMine; } + virtual void prepareForTransaction() override; + /// Collate the changed filters for the bloom filter of the given pending transaction. /// Insert any filters that are activated into @a o_changed. void appendFromNewPending(TransactionReceipt const& _receipt, h256Set& io_changed, h256 _sha3); @@ -380,13 +284,8 @@ private: virtual bool turbo() const { return m_turboMining; } virtual bool force() const { return m_forceMining; } - /// Returns the state object for the full block (i.e. the terminal state) for index _h. - /// Works properly with LatestBlock and PendingBlock. - State asOf(unsigned _h) const; - VersionChecker m_vc; ///< Dummy object to check & update the protocol version. CanonBlockChain m_bc; ///< Maintains block database. - TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported). std::shared_ptr m_gp; ///< The gas pricer. @@ -407,10 +306,6 @@ private: bool m_forceMining = false; ///< Mine even when there are no transactions pending? bool m_verifyOwnBlocks = true; ///< Should be verify blocks that we mined? - mutable Mutex m_filterLock; - std::map m_filters; - std::map m_watches; - mutable std::chrono::system_clock::time_point m_lastGarbageCollection; }; diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp new file mode 100644 index 000000000..03e74de58 --- /dev/null +++ b/libethereum/ClientBase.cpp @@ -0,0 +1,393 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file ClientBase.cpp + * @author Gav Wood + * @author Marek Kotewicz + * @date 2015 + */ + +#include +#include "ClientBase.h" +#include "BlockChain.h" +#include "Executive.h" + +using namespace std; +using namespace dev; +using namespace dev::eth; + +void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) +{ + prepareForTransaction(); + + u256 n = postMine().transactionsFrom(toAddress(_secret)); + Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); + m_tq.attemptImport(t.rlp()); + + StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged()); + cnote << "New transaction " << t; +} + +Address ClientBase::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) +{ + prepareForTransaction(); + + u256 n = postMine().transactionsFrom(toAddress(_secret)); + Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret); + m_tq.attemptImport(t.rlp()); + + StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged()); + cnote << "New transaction " << t; + + return right160(sha3(rlpList(t.sender(), t.nonce()))); +} + +// TODO: remove try/catch, allow exceptions +ExecutionResult ClientBase::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) +{ + ExecutionResult ret; + try + { + State temp = asOf(_blockNumber); + u256 n = temp.transactionsFrom(toAddress(_secret)); + Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); + ret = temp.execute(bc(), t.rlp(), Permanence::Reverted); + } + catch (...) + { + // TODO: Some sort of notification of failure. + } + return ret; +} + +ExecutionResult ClientBase::create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) +{ + ExecutionResult ret; + try + { + State temp = asOf(_blockNumber); + u256 n = temp.transactionsFrom(toAddress(_secret)); + // cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); + + Transaction t(_value, _gasPrice, _gas, _data, n, _secret); + ret = temp.execute(bc(), t.rlp(), Permanence::Reverted); + } + catch (...) + { + // TODO: Some sort of notification of failure. + } + return ret; +} + +u256 ClientBase::balanceAt(Address _a, BlockNumber _block) const +{ + return asOf(_block).balance(_a); +} + +u256 ClientBase::countAt(Address _a, BlockNumber _block) const +{ + return asOf(_block).transactionsFrom(_a); +} + +u256 ClientBase::stateAt(Address _a, u256 _l, BlockNumber _block) const +{ + return asOf(_block).storage(_a, _l); +} + +bytes ClientBase::codeAt(Address _a, BlockNumber _block) const +{ + return asOf(_block).code(_a); +} + +map ClientBase::storageAt(Address _a, BlockNumber _block) const +{ + return asOf(_block).storage(_a); +} + +// TODO: remove try/catch, allow exceptions +LocalisedLogEntries ClientBase::logs(unsigned _watchId) const +{ + LogFilter f; + try + { + Guard l(m_filterLock); + f = m_filters.at(m_watches.at(_watchId).id).filter; + } + catch (...) + { + return LocalisedLogEntries(); + } + return logs(f); +} + +LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const +{ + LocalisedLogEntries ret; + unsigned begin = min(bc().number() + 1, (unsigned)_f.latest()); + unsigned end = min(bc().number(), min(begin, (unsigned)_f.earliest())); + + // Handle pending transactions differently as they're not on the block chain. + if (begin > bc().number()) + { + State temp = postMine(); + for (unsigned i = 0; i < temp.pending().size(); ++i) + { + // Might have a transaction that contains a matching log. + TransactionReceipt const& tr = temp.receipt(i); + auto th = temp.pending()[i].sha3(); + LogEntries le = _f.matches(tr); + if (le.size()) + for (unsigned j = 0; j < le.size(); ++j) + ret.insert(ret.begin(), LocalisedLogEntry(le[j], begin, th)); + } + begin = bc().number(); + } + + set matchingBlocks; + for (auto const& i: _f.bloomPossibilities()) + for (auto u: bc().withBlockBloom(i, end, begin)) + matchingBlocks.insert(u); + + unsigned falsePos = 0; + for (auto n: matchingBlocks) + { + int total = 0; + auto h = bc().numberHash(n); + auto receipts = bc().receipts(h).receipts; + for (size_t i = 0; i < receipts.size(); i++) + { + TransactionReceipt receipt = receipts[i]; + if (_f.matches(receipt.bloom())) + { + auto info = bc().info(h); + auto th = transaction(info.hash, i).sha3(); + LogEntries le = _f.matches(receipt); + if (le.size()) + { + total += le.size(); + for (unsigned j = 0; j < le.size(); ++j) + ret.insert(ret.begin(), LocalisedLogEntry(le[j], n, th)); + } + } + + if (!total) + falsePos++; + } + } + + cdebug << matchingBlocks.size() << "searched from" << (end - begin) << "skipped; " << falsePos << "false +ves"; + return ret; +} + +unsigned ClientBase::installWatch(LogFilter const& _f, Reaping _r) +{ + h256 h = _f.sha3(); + { + Guard l(m_filterLock); + if (!m_filters.count(h)) + { + cwatch << "FFF" << _f << h.abridged(); + m_filters.insert(make_pair(h, _f)); + } + } + return installWatch(h, _r); +} + +unsigned ClientBase::installWatch(h256 _h, Reaping _r) +{ + unsigned ret; + { + Guard l(m_filterLock); + ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0; + m_watches[ret] = ClientWatch(_h, _r); + cwatch << "+++" << ret << _h.abridged(); + } + auto ch = logs(ret); + if (ch.empty()) + ch.push_back(InitialChange); + { + Guard l(m_filterLock); + swap(m_watches[ret].changes, ch); + } + return ret; +} + +bool ClientBase::uninstallWatch(unsigned _i) +{ + cwatch << "XXX" << _i; + + Guard l(m_filterLock); + + auto it = m_watches.find(_i); + if (it == m_watches.end()) + return false; + auto id = it->second.id; + m_watches.erase(it); + + auto fit = m_filters.find(id); + if (fit != m_filters.end()) + if (!--fit->second.refCount) + { + cwatch << "*X*" << fit->first << ":" << fit->second.filter; + m_filters.erase(fit); + } + return true; +} + +LocalisedLogEntries ClientBase::peekWatch(unsigned _watchId) const +{ + Guard l(m_filterLock); + + cwatch << "peekWatch" << _watchId; + auto& w = m_watches.at(_watchId); + cwatch << "lastPoll updated to " << chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); + w.lastPoll = chrono::system_clock::now(); + return w.changes; +} + +LocalisedLogEntries ClientBase::checkWatch(unsigned _watchId) +{ + Guard l(m_filterLock); + LocalisedLogEntries ret; + + cwatch << "checkWatch" << _watchId; + auto& w = m_watches.at(_watchId); + cwatch << "lastPoll updated to " << chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); + std::swap(ret, w.changes); + w.lastPoll = chrono::system_clock::now(); + + return ret; +} + +h256 ClientBase::hashFromNumber(unsigned _number) const +{ + return bc().numberHash(_number); +} + +BlockInfo ClientBase::blockInfo(h256 _hash) const +{ + return BlockInfo(bc().block(_hash)); +} + +BlockDetails ClientBase::blockDetails(h256 _hash) const +{ + return bc().details(_hash); +} + +Transaction ClientBase::transaction(h256 _transactionHash) const +{ + return Transaction(bc().transaction(_transactionHash), CheckSignature::Range); +} + +Transaction ClientBase::transaction(h256 _blockHash, unsigned _i) const +{ + auto bl = bc().block(_blockHash); + RLP b(bl); + if (_i < b[1].itemCount()) + return Transaction(b[1][_i].data(), CheckSignature::Range); + else + return Transaction(); +} + +Transactions ClientBase::transactions(h256 _blockHash) const +{ + auto bl = bc().block(_blockHash); + RLP b(bl); + Transactions res; + for (unsigned i = 0; i < b[1].itemCount(); i++) + res.emplace_back(b[1][i].data(), CheckSignature::Range); + return res; +} + +TransactionHashes ClientBase::transactionHashes(h256 _blockHash) const +{ + return bc().transactionHashes(_blockHash); +} + +BlockInfo ClientBase::uncle(h256 _blockHash, unsigned _i) const +{ + auto bl = bc().block(_blockHash); + RLP b(bl); + if (_i < b[2].itemCount()) + return BlockInfo::fromHeader(b[2][_i].data()); + else + return BlockInfo(); +} + +UncleHashes ClientBase::uncleHashes(h256 _blockHash) const +{ + return bc().uncleHashes(_blockHash); +} + +unsigned ClientBase::transactionCount(h256 _blockHash) const +{ + auto bl = bc().block(_blockHash); + RLP b(bl); + return b[1].itemCount(); +} + +unsigned ClientBase::uncleCount(h256 _blockHash) const +{ + auto bl = bc().block(_blockHash); + RLP b(bl); + return b[2].itemCount(); +} + +unsigned ClientBase::number() const +{ + return bc().number(); +} + +Transactions ClientBase::pending() const +{ + return postMine().pending(); +} + + +StateDiff ClientBase::diff(unsigned _txi, h256 _block) const +{ + State st = asOf(_block); + return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); +} + +StateDiff ClientBase::diff(unsigned _txi, BlockNumber _block) const +{ + State st = asOf(_block); + return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); +} + +Addresses ClientBase::addresses(BlockNumber _block) const +{ + Addresses ret; + for (auto const& i: asOf(_block).addresses()) + ret.push_back(i.first); + return ret; +} + +u256 ClientBase::gasLimitRemaining() const +{ + return postMine().gasLimitRemaining(); +} + +void ClientBase::setAddress(Address _us) +{ + preMine().setAddress(_us); +} + +Address ClientBase::address() const +{ + return preMine().address(); +} diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h new file mode 100644 index 000000000..662f170ba --- /dev/null +++ b/libethereum/ClientBase.h @@ -0,0 +1,164 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file ClientBase.h + * @author Gav Wood + * @author Marek Kotewicz + * @date 2015 + */ + +#pragma once + +#include +#include "Interface.h" +#include "LogFilter.h" + +namespace dev { + +namespace eth { + +struct InstalledFilter +{ + InstalledFilter(LogFilter const& _f): filter(_f) {} + + LogFilter filter; + unsigned refCount = 1; + LocalisedLogEntries changes; +}; + +static const h256 PendingChangedFilter = u256(0); +static const h256 ChainChangedFilter = u256(1); + +static const LogEntry SpecialLogEntry = LogEntry(Address(), h256s(), bytes()); +static const LocalisedLogEntry InitialChange(SpecialLogEntry, 0); + +struct ClientWatch +{ + ClientWatch(): lastPoll(std::chrono::system_clock::now()) {} + explicit ClientWatch(h256 _id, Reaping _r): id(_id), lastPoll(_r == Reaping::Automatic ? std::chrono::system_clock::now() : std::chrono::system_clock::time_point::max()) {} + + h256 id; + LocalisedLogEntries changes = LocalisedLogEntries{ InitialChange }; + mutable std::chrono::system_clock::time_point lastPoll = std::chrono::system_clock::now(); +}; + +struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 7; }; +#define cwatch dev::LogOutputStream() +struct WorkInChannel: public LogChannel { static const char* name() { return ">W>"; } static const int verbosity = 16; }; +struct WorkOutChannel: public LogChannel { static const char* name() { return "() +#define cworkin dev::LogOutputStream() +#define cworkout dev::LogOutputStream() + +class ClientBase: public dev::eth::Interface +{ +public: + ClientBase() {} + virtual ~ClientBase() {} + + /// Submits the given message-call transaction. + virtual void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override; + + /// Submits a new contract-creation transaction. + /// @returns the new contract's address (assuming it all goes through). + virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override; + + /// Makes the given call. Nothing is recorded into the state. + virtual ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock) override; + + virtual ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock) override; + + using Interface::balanceAt; + using Interface::countAt; + using Interface::stateAt; + using Interface::codeAt; + using Interface::storageAt; + + virtual u256 balanceAt(Address _a, BlockNumber _block) const override; + virtual u256 countAt(Address _a, BlockNumber _block) const override; + virtual u256 stateAt(Address _a, u256 _l, BlockNumber _block) const override; + virtual bytes codeAt(Address _a, BlockNumber _block) const override; + virtual std::map storageAt(Address _a, BlockNumber _block) const override; + + virtual LocalisedLogEntries logs(unsigned _watchId) const override; + virtual LocalisedLogEntries logs(LogFilter const& _filter) const override; + + /// Install, uninstall and query watches. + virtual unsigned installWatch(LogFilter const& _filter, Reaping _r = Reaping::Automatic) override; + virtual unsigned installWatch(h256 _filterId, Reaping _r = Reaping::Automatic) override; + virtual bool uninstallWatch(unsigned _watchId) override; + virtual LocalisedLogEntries peekWatch(unsigned _watchId) const override; + virtual LocalisedLogEntries checkWatch(unsigned _watchId) override; + + virtual h256 hashFromNumber(unsigned _number) const override; + virtual eth::BlockInfo blockInfo(h256 _hash) const override; + virtual eth::BlockDetails blockDetails(h256 _hash) const override; + virtual eth::Transaction transaction(h256 _transactionHash) const override; + virtual eth::Transaction transaction(h256 _blockHash, unsigned _i) const override; + virtual eth::Transactions transactions(h256 _blockHash) const override; + virtual eth::TransactionHashes transactionHashes(h256 _blockHash) const override; + virtual eth::BlockInfo uncle(h256 _blockHash, unsigned _i) const override; + virtual eth::UncleHashes uncleHashes(h256 _blockHash) const override; + virtual unsigned transactionCount(h256 _blockHash) const override; + virtual unsigned uncleCount(h256 _blockHash) const override; + virtual unsigned number() const override; + virtual eth::Transactions pending() const override; + + using Interface::diff; + virtual StateDiff diff(unsigned _txi, h256 _block) const override; + virtual StateDiff diff(unsigned _txi, BlockNumber _block) const override; + + using Interface::addresses; + virtual Addresses addresses(BlockNumber _block) const override; + virtual u256 gasLimitRemaining() const override; + + // Set the coinbase address + virtual void setAddress(Address _us) override; + + /// Get the coinbase address + virtual Address address() const override; + + /// TODO: consider moving it to a separate interface + + virtual void setMiningThreads(unsigned _threads) override { (void)_threads; BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::setMiningThreads")); } + virtual unsigned miningThreads() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::miningThreads")); } + virtual void startMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::startMining")); } + virtual void stopMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::stopMining")); } + virtual bool isMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::isMining")); } + virtual eth::MineProgress miningProgress() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::miningProgress")); } + virtual std::pair getWork() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::getWork")); } + virtual bool submitWork(eth::ProofOfWork::Proof const&) override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::submitWork")); } + +protected: + + virtual BlockChain const& bc() const = 0; + virtual State asOf(BlockNumber _h) const = 0; + virtual State asOf(h256 _h) const = 0; + virtual State preMine() const = 0; + virtual State postMine() const = 0; + virtual void prepareForTransaction() = 0; + + TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. + + // filters + mutable Mutex m_filterLock; + std::map m_filters; + std::map m_watches; + +}; + +}} diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 68eb4b094..7252abb73 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -38,6 +38,7 @@ namespace eth { using TransactionHashes = h256s; +using UncleHashes = h256s; enum class Reaping { @@ -66,9 +67,6 @@ public: /// @returns the new contract's address (assuming it all goes through). virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) = 0; - /// Injects the RLP-encoded transaction given by the _rlp into the transaction queue directly. - virtual void inject(bytesConstRef _rlp) = 0; - /// Blocks until all pending transactions have been processed. virtual void flushTransactions() = 0; @@ -118,6 +116,7 @@ public: virtual Transaction transaction(h256 _transactionHash) const = 0; virtual Transaction transaction(h256 _blockHash, unsigned _i) const = 0; virtual BlockInfo uncle(h256 _blockHash, unsigned _i) const = 0; + virtual UncleHashes uncleHashes(h256 _blockHash) const = 0; virtual unsigned transactionCount(h256 _blockHash) const = 0; virtual unsigned uncleCount(h256 _blockHash) const = 0; virtual Transactions transactions(h256 _blockHash) const = 0; diff --git a/libethereum/State.h b/libethereum/State.h index 46a111a9b..033942c12 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -41,7 +41,7 @@ namespace dev { -namespace test { class ImportTest; } +namespace test { class ImportTest; class StateLoader; } namespace eth { @@ -99,6 +99,7 @@ class State { friend class ExtVM; friend class dev::test::ImportTest; + friend class dev::test::StateLoader; friend class Executive; public: diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index fd7b2263d..b6a9a45b1 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -44,27 +44,18 @@ namespace mix const Secret c_defaultUserAccountSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074"); const u256 c_mixGenesisDifficulty = c_minimumDifficulty; //TODO: make it lower for Mix somehow - -class MixBlockChain: public dev::eth::BlockChain + +bytes MixBlockChain::createGenesisBlock(h256 _stateRoot) { -public: - MixBlockChain(std::string const& _path, h256 _stateRoot): - BlockChain(createGenesisBlock(_stateRoot), _path, true) - { - } - - static bytes createGenesisBlock(h256 _stateRoot) - { - RLPStream block(3); - block.appendList(15) - << h256() << EmptyListSHA3 << h160() << _stateRoot << EmptyTrie << EmptyTrie - << LogBloom() << c_mixGenesisDifficulty << 0 << c_genesisGasLimit << 0 << (unsigned)0 - << std::string() << h256() << h64(u64(42)); - block.appendRaw(RLPEmptyList); - block.appendRaw(RLPEmptyList); - return block.out(); - } -}; + RLPStream block(3); + block.appendList(15) + << h256() << EmptyListSHA3 << h160() << _stateRoot << EmptyTrie << EmptyTrie + << LogBloom() << c_mixGenesisDifficulty << 0 << c_genesisGasLimit << 0 << (unsigned)0 + << std::string() << h256() << h64(u64(42)); + block.appendRaw(RLPEmptyList); + block.appendRaw(RLPEmptyList); + return block.out(); +} MixClient::MixClient(std::string const& _dbPath): m_dbPath(_dbPath), m_minigThreads(0) @@ -243,15 +234,21 @@ ExecutionResult MixClient::execution(unsigned _index) const return m_executions.at(_index); } -State MixClient::asOf(int _block) const +State MixClient::asOf(BlockNumber _block) const { ReadGuard l(x_state); - if (_block == 0) + if (_block == PendingBlock) return m_state; - else if (_block == -1) + else if (_block == LatestBlock) return m_startState; - else - return State(m_stateDB, bc(), bc().numberHash(_block)); + + return State(m_stateDB, bc(), bc().numberHash(_block)); +} + +State MixClient::asOf(h256 _block) const +{ + ReadGuard l(x_state); + return State(m_stateDB, bc(), _block); } void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) @@ -272,26 +269,11 @@ Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes cons return address; } -void MixClient::inject(bytesConstRef _rlp) -{ - WriteGuard l(x_state); - eth::Transaction t(_rlp, CheckSignature::None); - executeTransaction(t, m_state, false); -} - -void MixClient::flushTransactions() -{ -} - dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) { - u256 n; - State temp; - { - ReadGuard lr(x_state); - temp = asOf(_blockNumber); - n = temp.transactionsFrom(toAddress(_secret)); - } + + State temp = asOf(_blockNumber); + u256 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 execution state @@ -315,122 +297,6 @@ dev::eth::ExecutionResult MixClient::create(Secret _secret, u256 _value, bytes c return lastExecution().result; } -u256 MixClient::balanceAt(Address _a, BlockNumber _block) const -{ - return asOf(_block).balance(_a); -} - -u256 MixClient::countAt(Address _a, BlockNumber _block) const -{ - return asOf(_block).transactionsFrom(_a); -} - -u256 MixClient::stateAt(Address _a, u256 _l, BlockNumber _block) const -{ - return asOf(_block).storage(_a, _l); -} - -bytes MixClient::codeAt(Address _a, BlockNumber _block) const -{ - return asOf(_block).code(_a); -} - -std::map MixClient::storageAt(Address _a, BlockNumber _block) const -{ - return asOf(_block).storage(_a); -} - -eth::LocalisedLogEntries MixClient::logs(unsigned _watchId) const -{ - Guard l(m_filterLock); - h256 h = m_watches.at(_watchId).id; - auto filterIter = m_filters.find(h); - if (filterIter != m_filters.end()) - return logs(filterIter->second.filter); - return eth::LocalisedLogEntries(); -} - -eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const -{ - LocalisedLogEntries ret; - unsigned lastBlock = bc().number(); - unsigned block = std::min(lastBlock, (unsigned)_f.latest()); - unsigned end = std::min(lastBlock, std::min(block, (unsigned)_f.earliest())); - // Pending transactions - if (block > bc().number()) - { - ReadGuard l(x_state); - for (unsigned i = 0; i < m_state.pending().size(); ++i) - { - // Might have a transaction that contains a matching log. - TransactionReceipt const& tr = m_state.receipt(i); - LogEntries logEntries = _f.matches(tr); - for (unsigned entry = 0; entry < logEntries.size(); ++entry) - ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block)); - } - block = bc().number(); - } - - // The rest - auto h = bc().numberHash(block); - for (; ret.size() != block && block != end; block--) - { - if (_f.matches(bc().info(h).logBloom)) - for (TransactionReceipt receipt: bc().receipts(h).receipts) - if (_f.matches(receipt.bloom())) - for (auto const& e: _f.matches(receipt)) - ret.insert(ret.begin(), LocalisedLogEntry(e, block)); - h = bc().details(h).parent; - } - return ret; -} - -unsigned MixClient::installWatch(h256 _h, eth::Reaping _r) -{ - unsigned ret; - { - Guard l(m_filterLock); - ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0; - m_watches[ret] = ClientWatch(_h, _r); - } - auto ch = logs(ret); - if (ch.empty()) - ch.push_back(eth::InitialChange); - { - Guard l(m_filterLock); - swap(m_watches[ret].changes, ch); - } - return ret; -} - -unsigned MixClient::installWatch(eth::LogFilter const& _f, eth::Reaping _r) -{ - h256 h = _f.sha3(); - { - Guard l(m_filterLock); - m_filters.insert(std::make_pair(h, _f)); - } - return installWatch(h, _r); -} - -bool MixClient::uninstallWatch(unsigned _i) -{ - Guard l(m_filterLock); - - auto it = m_watches.find(_i); - if (it == m_watches.end()) - return false; - auto id = it->second.id; - m_watches.erase(it); - - auto fit = m_filters.find(id); - if (fit != m_filters.end()) - if (!--fit->second.refCount) - m_filters.erase(fit); - - return true; -} - void MixClient::noteChanged(h256Set const& _filters) { for (auto& i: m_watches) @@ -445,160 +311,18 @@ void MixClient::noteChanged(h256Set const& _filters) i.second.changes.clear(); } -LocalisedLogEntries MixClient::peekWatch(unsigned _watchId) const -{ - Guard l(m_filterLock); - if (_watchId < m_watches.size()) - return m_watches.at(_watchId).changes; - return LocalisedLogEntries(); -} - -LocalisedLogEntries MixClient::checkWatch(unsigned _watchId) -{ - Guard l(m_filterLock); - LocalisedLogEntries ret; - if (_watchId < m_watches.size()) - std::swap(ret, m_watches.at(_watchId).changes); - return ret; -} - -h256 MixClient::hashFromNumber(unsigned _number) const -{ - ReadGuard l(x_state); - return bc().numberHash(_number); -} - -eth::BlockInfo MixClient::blockInfo(h256 _hash) const -{ - ReadGuard l(x_state); - return BlockInfo(bc().block(_hash)); - -} - eth::BlockInfo MixClient::blockInfo() const { ReadGuard l(x_state); return BlockInfo(bc().block()); } -eth::BlockDetails MixClient::blockDetails(h256 _hash) const -{ - ReadGuard l(x_state); - return bc().details(_hash); -} - -Transaction MixClient::transaction(h256 _transactionHash) const -{ - ReadGuard l(x_state); - return Transaction(bc().transaction(_transactionHash), CheckSignature::Range); -} - -eth::Transaction MixClient::transaction(h256 _blockHash, unsigned _i) const -{ - ReadGuard l(x_state); - auto bl = bc().block(_blockHash); - RLP b(bl); - if (_i < b[1].itemCount()) - return Transaction(b[1][_i].data(), CheckSignature::Range); - else - return Transaction(); -} - -eth::BlockInfo MixClient::uncle(h256 _blockHash, unsigned _i) const -{ - ReadGuard l(x_state); - auto bl = bc().block(_blockHash); - RLP b(bl); - if (_i < b[2].itemCount()) - return BlockInfo::fromHeader(b[2][_i].data()); - else - return BlockInfo(); -} - -unsigned MixClient::transactionCount(h256 _blockHash) const -{ - ReadGuard l(x_state); - auto bl = bc().block(_blockHash); - RLP b(bl); - return b[1].itemCount(); -} - -unsigned MixClient::uncleCount(h256 _blockHash) const -{ - ReadGuard l(x_state); - auto bl = bc().block(_blockHash); - RLP b(bl); - return b[2].itemCount(); -} - -Transactions MixClient::transactions(h256 _blockHash) const -{ - ReadGuard l(x_state); - auto bl = bc().block(_blockHash); - RLP b(bl); - Transactions res; - for (unsigned i = 0; i < b[1].itemCount(); i++) - res.emplace_back(b[1][i].data(), CheckSignature::Range); - return res; -} - -TransactionHashes MixClient::transactionHashes(h256 _blockHash) const -{ - ReadGuard l(x_state); - return bc().transactionHashes(_blockHash); -} - -unsigned MixClient::number() const -{ - ReadGuard l(x_state); - return bc().number(); -} - -eth::Transactions MixClient::pending() const -{ - ReadGuard l(x_state); - return m_state.pending(); -} - -eth::StateDiff MixClient::diff(unsigned _txi, h256 _block) const -{ - ReadGuard l(x_state); - State st(m_stateDB, bc(), _block); - return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); -} - -eth::StateDiff MixClient::diff(unsigned _txi, BlockNumber _block) const -{ - State st = asOf(_block); - return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); -} - -Addresses MixClient::addresses(BlockNumber _block) const -{ - Addresses ret; - for (auto const& i: asOf(_block).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) { m_minigThreads = _threads; diff --git a/mix/MixClient.h b/mix/MixClient.h index e1085a20e..1a81f10cc 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -25,8 +25,7 @@ #include #include -#include -#include +#include #include #include "MachineStates.h" @@ -35,9 +34,15 @@ namespace dev namespace mix { -class MixBlockChain; +class MixBlockChain: public dev::eth::BlockChain +{ +public: + MixBlockChain(std::string const& _path, h256 _stateRoot): BlockChain(createGenesisBlock(_stateRoot), _path, true) {} + + static bytes createGenesisBlock(h256 _stateRoot); +}; -class MixClient: public dev::eth::Interface +class MixClient: public dev::eth::ClientBase { public: MixClient(std::string const& _dbPath); @@ -48,43 +53,12 @@ public: ExecutionResult lastExecution() const; ExecutionResult execution(unsigned _index) const; - //dev::eth::Interface void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override; Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) override; - void inject(bytesConstRef _rlp) override; - void flushTransactions() override; - dev::eth::ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber) override; - dev::eth::ExecutionResult create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber) override; - u256 balanceAt(Address _a, eth::BlockNumber _block) const override; - u256 countAt(Address _a, eth::BlockNumber _block) const override; - u256 stateAt(Address _a, u256 _l, eth::BlockNumber _block) const override; - bytes codeAt(Address _a, eth::BlockNumber _block) const override; - std::map storageAt(Address _a, eth::BlockNumber _block) const override; - eth::LocalisedLogEntries logs(unsigned _watchId) const override; - eth::LocalisedLogEntries logs(eth::LogFilter const& _filter) const override; - unsigned installWatch(eth::LogFilter const& _filter, eth::Reaping _r = eth::Reaping::Automatic) override; - unsigned installWatch(h256 _filterId, eth::Reaping _r = eth::Reaping::Automatic) override; - bool uninstallWatch(unsigned _watchId) override; - eth::LocalisedLogEntries peekWatch(unsigned _watchId) const override; - eth::LocalisedLogEntries 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 _transactionHash) const override; - eth::Transaction transaction(h256 _blockHash, unsigned _i) const override; - eth::BlockInfo uncle(h256 _blockHash, unsigned _i) const override; - unsigned transactionCount(h256 _blockHash) const override; - unsigned uncleCount(h256 _blockHash) const override; - eth::Transactions transactions(h256 _blockHash) const override; - eth::TransactionHashes transactionHashes(h256 _blockHash) 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, eth::BlockNumber _block) const override; - Addresses addresses(eth::BlockNumber _block) const override; - u256 gasLimitRemaining() const override; + dev::eth::ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber = eth::PendingBlock) override; + dev::eth::ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * eth::szabo, eth::BlockNumber _blockNumber = eth::PendingBlock) override; + void setAddress(Address _us) override; - Address address() const override; void setMiningThreads(unsigned _threads) override; unsigned miningThreads() const override; void startMining() override; @@ -93,16 +67,27 @@ public: eth::MineProgress miningProgress() const override; std::pair getWork() override { return std::pair(); } bool submitWork(eth::ProofOfWork::Proof const&) override { return false; } + virtual void flushTransactions() override {} + /// @returns the last mined block information + using Interface::blockInfo; // to remove warning about hiding virtual function eth::BlockInfo blockInfo() const; std::vector userAccounts() { return m_userAccounts; } +protected: + virtual dev::eth::BlockChain& bc() { return *m_bc; } + + /// InterfaceStub methods + virtual dev::eth::State asOf(eth::BlockNumber _block) const override; + virtual dev::eth::State asOf(h256 _block) const override; + virtual dev::eth::BlockChain const& bc() const override { return *m_bc; } + virtual dev::eth::State preMine() const override { ReadGuard l(x_state); return m_startState; } + virtual dev::eth::State postMine() const override { ReadGuard l(x_state); return m_state; } + virtual void prepareForTransaction() override {} + private: void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call); void noteChanged(h256Set const& _filters); - dev::eth::State asOf(int _block) const; - MixBlockChain& bc() { return *m_bc; } - MixBlockChain const& bc() const { return *m_bc; } std::vector m_userAccounts; eth::State m_state; @@ -111,9 +96,6 @@ private: std::auto_ptr m_bc; mutable boost::shared_mutex x_state; mutable boost::shared_mutex x_executions; - mutable std::mutex m_filterLock; - std::map m_filters; - std::map m_watches; ExecutionResults m_executions; std::string m_dbPath; unsigned m_minigThreads; From a70b84cb3df614377e6b79e6e53ca74731d95bef Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 10:35:12 +0100 Subject: [PATCH 049/228] libtestutils --- CMakeLists.txt | 1 + libtestutils/BlockChainLoader.cpp | 101 ++++++++++++++++++++++++++ libtestutils/BlockChainLoader.h | 51 +++++++++++++ libtestutils/CMakeLists.txt | 44 +++++++++++ libtestutils/Common.cpp | 80 ++++++++++++++++++++ libtestutils/Common.h | 44 +++++++++++ libtestutils/FixedClient.cpp | 41 +++++++++++ libtestutils/FixedClient.h | 55 ++++++++++++++ libtestutils/FixedWebThreeServer.cpp | 2 + libtestutils/FixedWebThreeServer.h | 32 ++++++++ libtestutils/ShortLivingDirectory.cpp | 44 +++++++++++ libtestutils/ShortLivingDirectory.h | 45 ++++++++++++ libtestutils/StateLoader.cpp | 57 +++++++++++++++ libtestutils/StateLoader.h | 42 +++++++++++ 14 files changed, 639 insertions(+) create mode 100644 libtestutils/BlockChainLoader.cpp create mode 100644 libtestutils/BlockChainLoader.h create mode 100644 libtestutils/CMakeLists.txt create mode 100644 libtestutils/Common.cpp create mode 100644 libtestutils/Common.h create mode 100644 libtestutils/FixedClient.cpp create mode 100644 libtestutils/FixedClient.h create mode 100644 libtestutils/FixedWebThreeServer.cpp create mode 100644 libtestutils/FixedWebThreeServer.h create mode 100644 libtestutils/ShortLivingDirectory.cpp create mode 100644 libtestutils/ShortLivingDirectory.h create mode 100644 libtestutils/StateLoader.cpp create mode 100644 libtestutils/StateLoader.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e7e89a5a7..70cd3cb66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -195,6 +195,7 @@ add_subdirectory(libevm) add_subdirectory(libethereum) add_subdirectory(libwebthree) +add_subdirectory(libtestutils) add_subdirectory(test) if (NOT JUSTTESTS) diff --git a/libtestutils/BlockChainLoader.cpp b/libtestutils/BlockChainLoader.cpp new file mode 100644 index 000000000..224e40ec5 --- /dev/null +++ b/libtestutils/BlockChainLoader.cpp @@ -0,0 +1,101 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file BlockChainLoader.cpp + * @author Marek Kotewicz + * @date 2015 + */ + +#include "BlockChainLoader.h" +#include "StateLoader.h" +#include "Common.h" + +using namespace std; +using namespace dev; +using namespace dev::test; +using namespace dev::eth; + +namespace dev +{ +namespace test +{ +dev::eth::BlockInfo toBlockInfo(Json::Value const& _json); +bytes toGenesisBlock(Json::Value const& _json); + +} +} + +dev::eth::BlockInfo dev::test::toBlockInfo(Json::Value const& _json) +{ + RLPStream rlpStream; + auto size = _json.getMemberNames().size(); + rlpStream.appendList(_json["hash"].empty() ? size : (size - 1)); + rlpStream << fromHex(_json["parentHash"].asString()); + rlpStream << fromHex(_json["uncleHash"].asString()); + rlpStream << fromHex(_json["coinbase"].asString()); + rlpStream << fromHex(_json["stateRoot"].asString()); + rlpStream << fromHex(_json["transactionsTrie"].asString()); + rlpStream << fromHex(_json["receiptTrie"].asString()); + rlpStream << fromHex(_json["bloom"].asString()); + rlpStream << bigint(_json["difficulty"].asString()); + rlpStream << bigint(_json["number"].asString()); + rlpStream << bigint(_json["gasLimit"].asString()); + rlpStream << bigint(_json["gasUsed"].asString()); + rlpStream << bigint(_json["timestamp"].asString()); + rlpStream << fromHex(_json["extraData"].asString()); + rlpStream << fromHex(_json["mixHash"].asString()); + rlpStream << fromHex(_json["nonce"].asString()); + + BlockInfo result; + RLP rlp(rlpStream.out()); + result.populateFromHeader(rlp, IgnoreNonce); + return result; +} + +bytes dev::test::toGenesisBlock(Json::Value const& _json) +{ + BlockInfo bi = toBlockInfo(_json); + RLPStream rlpStream; + bi.streamRLP(rlpStream, WithNonce); + + RLPStream fullStream(3); + fullStream.appendRaw(rlpStream.out()); + fullStream.appendRaw(RLPEmptyList); + fullStream.appendRaw(RLPEmptyList); + bi.verifyInternals(&fullStream.out()); + + return fullStream.out(); +} + +BlockChainLoader::BlockChainLoader(Json::Value const& _json) +{ + // load pre state + StateLoader sl(_json["pre"]); + m_state = sl.state(); + + // load genesisBlock + m_bc.reset(new BlockChain(toGenesisBlock(_json["genesisBlockHeader"]), m_dir.path(), true)); + + // load blocks + for (auto const& block: _json["blocks"]) + { + bytes rlp = fromHex(block["rlp"].asString()); + m_bc->import(rlp, m_state.db()); + } + + // sync state + m_state.sync(*m_bc); +} diff --git a/libtestutils/BlockChainLoader.h b/libtestutils/BlockChainLoader.h new file mode 100644 index 000000000..a3528045c --- /dev/null +++ b/libtestutils/BlockChainLoader.h @@ -0,0 +1,51 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file BlockChainLoader.h + * @author Marek Kotewicz + * @date 2015 + */ + +#pragma once +#include +#include +#include +#include +#include "ShortLivingDirectory.h" + +namespace dev +{ +namespace test +{ + +/** + * @brief - loads the blockchain from json, creates temporary directory to store it, removes this temporary directory on dealloc + */ +class BlockChainLoader +{ +public: + BlockChainLoader(Json::Value const& _json); + eth::BlockChain& bc() { return *m_bc; } + eth::State state() { return m_state; } + +private: + ShortLivingDirectory m_dir; + std::auto_ptr m_bc; + eth::State m_state; +}; + +} +} diff --git a/libtestutils/CMakeLists.txt b/libtestutils/CMakeLists.txt new file mode 100644 index 000000000..f79daa47c --- /dev/null +++ b/libtestutils/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_policy(SET CMP0015 NEW) +# this policy was introduced in cmake 3.0 +# remove if, once 3.0 will be used on unix +if (${CMAKE_MAJOR_VERSION} GREATER 2) + # old policy do not use MACOSX_RPATH + cmake_policy(SET CMP0042 OLD) +endif() +set(CMAKE_AUTOMOC OFF) + +aux_source_directory(. SRC_LIST) + +include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) +include_directories(BEFORE ..) +include_directories(${MHD_INCLUDE_DIRS}) +include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) +include_directories(${LEVELDB_INCLUDE_DIRS}) +include_directories(${Boost_INCLUDE_DIRS}) + +set(EXECUTABLE testutils) + +file(GLOB HEADERS "*.h") + +if (ETH_STATIC) + add_library(${EXECUTABLE} STATIC ${SRC_LIST} ${HEADERS}) +else() + add_library(${EXECUTABLE} SHARED ${SRC_LIST} ${HEADERS}) +endif() + +target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES}) +target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES}) +target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_SERVER_LIBRARIES}) +target_link_libraries(${EXECUTABLE} ${MHD_LIBRARIES}) + +target_link_libraries(${EXECUTABLE} webthree) +target_link_libraries(${EXECUTABLE} secp256k1) +target_link_libraries(${EXECUTABLE} solidity) + +if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) + target_link_libraries(${EXECUTABLE} serpent) +endif() + +install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) + diff --git a/libtestutils/Common.cpp b/libtestutils/Common.cpp new file mode 100644 index 000000000..f15a2da12 --- /dev/null +++ b/libtestutils/Common.cpp @@ -0,0 +1,80 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file Common.cpp + * @author Marek Kotewicz + * @date 2015 + */ + +#include +#include +#include +#include +#include "Common.h" + +using namespace std; +using namespace dev; +using namespace dev::test; + +std::string dev::test::getTestPath() +{ + string testPath; + const char* ptestPath = getenv("ETHEREUM_TEST_PATH"); + + if (ptestPath == NULL) + { + ctest << " could not find environment variable ETHEREUM_TEST_PATH \n"; + testPath = "../../../tests"; + } + else + testPath = ptestPath; + + return testPath; +} + +int dev::test::randomNumber() +{ + static std::mt19937 randomGenerator(time(0)); + randomGenerator.seed(std::random_device()()); + return std::uniform_int_distribution(1)(randomGenerator); +} + +Json::Value dev::test::loadJsonFromFile(std::string const& _path) +{ + Json::Reader reader; + Json::Value result; + string s = asString(dev::contents(_path)); + if (!s.length()) + ctest << "Contents of " + _path + " is empty. Have you cloned the 'tests' repo branch develop and set ETHEREUM_TEST_PATH to its path?"; + else + ctest << "FIXTURE: loaded test from file: " << _path; + + reader.parse(s, result); + return result; +} + +std::string dev::test::toTestFilePath(std::string const& _filename) +{ + return getTestPath() + "/" + _filename + ".json"; +} + +std::string dev::test::getRandomPath() +{ + std::stringstream stream; + stream << getDataDir() << "/EthereumTests/" << randomNumber(); + return stream.str(); +} + diff --git a/libtestutils/Common.h b/libtestutils/Common.h new file mode 100644 index 000000000..4757a3b7a --- /dev/null +++ b/libtestutils/Common.h @@ -0,0 +1,44 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file Common.h + * @author Marek Kotewicz + * @date 2015 + */ + +#pragma once + +#include +#include +#include + +namespace dev +{ +namespace test +{ + +struct TestChannel: public LogChannel { static const char* name() { return "TEST"; } }; +#define ctest dev::LogOutputStream() + +std::string getTestPath(); +int randomNumber(); +Json::Value loadJsonFromFile(std::string const& _path); +std::string toTestFilePath(std::string const& _filename); +std::string getRandomPath(); + +} + +} diff --git a/libtestutils/FixedClient.cpp b/libtestutils/FixedClient.cpp new file mode 100644 index 000000000..a58d4ae0e --- /dev/null +++ b/libtestutils/FixedClient.cpp @@ -0,0 +1,41 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file FixedClient.cpp + * @author Marek Kotewicz + * @date 2015 + */ + +#include "FixedClient.h" + +using namespace dev; +using namespace dev::eth; +using namespace dev::test; + +eth::State FixedClient::asOf(BlockNumber _h) const +{ + ReadGuard l(x_stateDB); + if (_h == PendingBlock || _h == LatestBlock) + return m_state; + + return State(m_state.db(), bc(), bc().numberHash(_h)); +} + +eth::State FixedClient::asOf(h256 _h) const +{ + ReadGuard l(x_stateDB); + return State(m_state.db(), bc(), _h); +} diff --git a/libtestutils/FixedClient.h b/libtestutils/FixedClient.h new file mode 100644 index 000000000..ce628c651 --- /dev/null +++ b/libtestutils/FixedClient.h @@ -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 . + */ +/** @file FixedClient.h + * @author Marek Kotewicz + * @date 2015 + */ + +#pragma once + +#include +#include +#include + +namespace dev +{ +namespace test +{ + +class FixedClient: public dev::eth::ClientBase +{ +public: + FixedClient(eth::BlockChain const& _bc, eth::State _state) : m_bc(_bc), m_state(_state) {} + virtual ~FixedClient() {} + + // stub + virtual void flushTransactions() override {} + virtual eth::BlockChain const& bc() const override { return m_bc; } + virtual eth::State asOf(eth::BlockNumber _h) const override; + virtual eth::State asOf(h256 _h) const override; + virtual eth::State preMine() const override { ReadGuard l(x_stateDB); return m_state; } + virtual eth::State postMine() const override { ReadGuard l(x_stateDB); return m_state; } + virtual void prepareForTransaction() override {} + +private: + eth::BlockChain const& m_bc; + eth::State m_state; + mutable SharedMutex x_stateDB; ///< Lock on the state DB, effectively a lock on m_postMine. +}; + +} +} diff --git a/libtestutils/FixedWebThreeServer.cpp b/libtestutils/FixedWebThreeServer.cpp new file mode 100644 index 000000000..6a1b13b11 --- /dev/null +++ b/libtestutils/FixedWebThreeServer.cpp @@ -0,0 +1,2 @@ + +#include "FixedWebThreeServer.h" diff --git a/libtestutils/FixedWebThreeServer.h b/libtestutils/FixedWebThreeServer.h new file mode 100644 index 000000000..aab12c461 --- /dev/null +++ b/libtestutils/FixedWebThreeServer.h @@ -0,0 +1,32 @@ + + +#pragma once + +#include +#include + +class FixedWebThreeServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace +{ +public: + FixedWebThreeServer(jsonrpc::AbstractServerConnector& _conn, std::vector const& _accounts, dev::eth::Interface* _client): WebThreeStubServerBase(_conn, _accounts), m_client(_client) {}; + +private: + dev::eth::Interface* client() override { return m_client; } + std::shared_ptr face() override { BOOST_THROW_EXCEPTION(dev::InterfaceNotSupported("dev::shh::Interface")); } + dev::WebThreeNetworkFace* network() override { BOOST_THROW_EXCEPTION(dev::InterfaceNotSupported("dev::WebThreeNetworkFace")); } + dev::WebThreeStubDatabaseFace* db() override { return this; } + std::string get(std::string const& _name, std::string const& _key) override + { + std::string k(_name + "/" + _key); + return m_db[k]; + } + void put(std::string const& _name, std::string const& _key, std::string const& _value) override + { + std::string k(_name + "/" + _key); + m_db[k] = _value; + } + +private: + dev::eth::Interface* m_client; + std::map m_db; +}; diff --git a/libtestutils/ShortLivingDirectory.cpp b/libtestutils/ShortLivingDirectory.cpp new file mode 100644 index 000000000..56fc101a7 --- /dev/null +++ b/libtestutils/ShortLivingDirectory.cpp @@ -0,0 +1,44 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file ShortLivingDirectory.cpp + * @author Marek Kotewicz + * @date 2015 + */ + +#include +#include "ShortLivingDirectory.h" +#include "Common.h" + +using namespace std; +using namespace dev; +using namespace dev::test; + +ShortLivingDirectory::ShortLivingDirectory() +{ + m_path = getRandomPath(); + boost::filesystem::create_directories(m_path); +} + +ShortLivingDirectory::ShortLivingDirectory(std::string const& _path) : m_path(_path) +{ + boost::filesystem::create_directories(m_path); +} + +ShortLivingDirectory::~ShortLivingDirectory() +{ + boost::filesystem::remove_all(m_path); +} diff --git a/libtestutils/ShortLivingDirectory.h b/libtestutils/ShortLivingDirectory.h new file mode 100644 index 000000000..6f572c2bc --- /dev/null +++ b/libtestutils/ShortLivingDirectory.h @@ -0,0 +1,45 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file ShortLivingDirectory.h + * @author Marek Kotewicz + * @date 2015 + */ + +#pragma once + +#include + +namespace dev +{ +namespace test +{ + +class ShortLivingDirectory +{ +public: + ShortLivingDirectory(); + ShortLivingDirectory(std::string const& _path); + ~ShortLivingDirectory(); + + std::string path(){ return m_path; } + +private: + std::string m_path; +}; + +} +} diff --git a/libtestutils/StateLoader.cpp b/libtestutils/StateLoader.cpp new file mode 100644 index 000000000..0561e40f1 --- /dev/null +++ b/libtestutils/StateLoader.cpp @@ -0,0 +1,57 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file StateLoader.cpp + * @author Marek Kotewicz + * @date 2015 + */ + +#include "StateLoader.h" + +using namespace std; +using namespace dev; +using namespace dev::eth; +using namespace dev::test; + +StateLoader::StateLoader(Json::Value const& _json) +: m_state(Address(), OverlayDB(), BaseState::Empty) +{ + for (string const& name: _json.getMemberNames()) + { + Json::Value o = _json[name]; + + Address address = Address(name); + bytes code = fromHex(o["code"].asString().substr(2)); + + if (code.size()) + { + m_state.m_cache[address] = Account(u256(o["balance"].asString()), Account::ContractConception); + m_state.m_cache[address].setCode(code); + } + else + m_state.m_cache[address] = Account(u256(o["balance"].asString()), Account::NormalCreation); + + for (string const& j: o["storage"].getMemberNames()) + m_state.setStorage(address, u256(j), u256(o["storage"][j].asString())); + + for (auto i = 0; i < u256(o["nonce"].asString()); ++i) + m_state.noteSending(address); + + m_state.ensureCached(address, false, false); + } + + m_state.commit(); +} diff --git a/libtestutils/StateLoader.h b/libtestutils/StateLoader.h new file mode 100644 index 000000000..90f806714 --- /dev/null +++ b/libtestutils/StateLoader.h @@ -0,0 +1,42 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file StateLoader.h + * @author Marek Kotewicz + * @date 2015 + */ + +#pragma once + +#include +#include + +namespace dev +{ +namespace test +{ + +class StateLoader +{ +public: + StateLoader(Json::Value const& _json); + eth::State state() { return m_state; } + +private: + eth::State m_state; +}; +} +} From f69dd0a4ba29e88b82508c7e724b7c8bc3076496 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 10:41:01 +0100 Subject: [PATCH 050/228] ethrpctest executable --- CMakeLists.txt | 1 + ethrpctest/CMakeLists.txt | 35 ++++++++ ethrpctest/CommandLineInterface.cpp | 129 ++++++++++++++++++++++++++++ ethrpctest/CommandLineInterface.h | 46 ++++++++++ ethrpctest/main.cpp | 34 ++++++++ 5 files changed, 245 insertions(+) create mode 100644 ethrpctest/CMakeLists.txt create mode 100644 ethrpctest/CommandLineInterface.cpp create mode 100644 ethrpctest/CommandLineInterface.h create mode 100644 ethrpctest/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 70cd3cb66..59fdde07e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -178,6 +178,7 @@ endif() if (JSONRPC) add_subdirectory(libweb3jsonrpc) + add_subdirectory(ethrpctest) endif() add_subdirectory(secp256k1) diff --git a/ethrpctest/CMakeLists.txt b/ethrpctest/CMakeLists.txt new file mode 100644 index 000000000..5d3fef542 --- /dev/null +++ b/ethrpctest/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_policy(SET CMP0015 NEW) +set(CMAKE_AUTOMOC OFF) + +aux_source_directory(. SRC_LIST) + +include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) +include_directories(BEFORE ..) +include_directories(${Boost_INCLUDE_DIRS}) +include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) + +set(EXECUTABLE ethrpctest) + +file(GLOB HEADERS "*.h") + +add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) + +add_dependencies(${EXECUTABLE} BuildInfo.h) + +target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES}) + +if (READLINE_FOUND) + target_link_libraries(${EXECUTABLE} ${READLINE_LIBRARIES}) +endif() + +target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARIES}) +target_link_libraries(${EXECUTABLE} ${Boost_PROGRAM_OPTIONS_LIBRARIES}) +target_link_libraries(${EXECUTABLE} testutils) +target_link_libraries(${EXECUTABLE} web3jsonrpc) + +if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW) + add_custom_command(TARGET ${EXECUTABLE} POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS -E copy ${MHD_DLL_RELEASE} "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}") +endif() + +install( TARGETS ${EXECUTABLE} DESTINATION bin ) + diff --git a/ethrpctest/CommandLineInterface.cpp b/ethrpctest/CommandLineInterface.cpp new file mode 100644 index 000000000..c4b3d8f27 --- /dev/null +++ b/ethrpctest/CommandLineInterface.cpp @@ -0,0 +1,129 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file CommandLineInterface.cpp + * @author Marek Kotewicz + * @date 2015 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "CommandLineInterface.h" +#include "BuildInfo.h" + +using namespace std; +using namespace dev; +using namespace dev::eth; +using namespace dev::test; +namespace po = boost::program_options; + +bool CommandLineInterface::parseArguments(int argc, char** argv) +{ + // Declare the supported options. + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "Show help message and exit") + ("json", po::value>()->required(), "input file") + ("test", po::value>()->required(), "test case name"); + + // All positional options should be interpreted as input files + po::positional_options_description p; + + // parse the compiler arguments + try + { + po::store(po::command_line_parser(argc, argv).options(desc).positional(p).allow_unregistered().run(), m_args); + + if (m_args.count("help")) + { + cout << desc; + return false; + } + + po::notify(m_args); + } + catch (po::error const& _exception) + { + cout << _exception.what() << endl; + return false; + } + + return true; +} + +bool CommandLineInterface::processInput() +{ + string infile = m_args["json"].as>()[0]; + + auto path = boost::filesystem::path(infile); + if (!boost::filesystem::exists(path)) + { + cout << "Non existant input file \"" << infile << "\"" << endl; + return false; + } + + string test = m_args["test"].as>()[0]; + Json::Value j = dev::test::loadJsonFromFile(path.string()); + + if (j[test].empty()) + { + cout << "Non existant test case \"" << infile << "\"" << endl; + return false; + } + + if (!j[test].isObject()) + { + cout << "Incorrect JSON file \"" << infile << "\"" << endl; + return false; + } + + m_json = j[test]; + + return true; +} + +bool g_exit = false; + +void sighandler(int) +{ + g_exit = true; +} + +void CommandLineInterface::actOnInput() +{ + BlockChainLoader bcl(m_json); + FixedClient client(bcl.bc(), bcl.state()); + unique_ptr jsonrpcServer; + auto server = new jsonrpc::HttpServer(8080, "", "", 2); + jsonrpcServer.reset(new FixedWebThreeServer(*server, {}, &client)); + jsonrpcServer->StartListening(); + + signal(SIGABRT, &sighandler); + signal(SIGTERM, &sighandler); + signal(SIGINT, &sighandler); + + while (!g_exit) + this_thread::sleep_for(chrono::milliseconds(1000)); +} diff --git a/ethrpctest/CommandLineInterface.h b/ethrpctest/CommandLineInterface.h new file mode 100644 index 000000000..b57e1df5e --- /dev/null +++ b/ethrpctest/CommandLineInterface.h @@ -0,0 +1,46 @@ +/* + 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 . +*/ +/** CommandLineInterface.h + * @author Marek Kotewicz + * @date 2015 + */ +#pragma once + +#include +#include + +class CommandLineInterface +{ +public: + CommandLineInterface() {} + + /// Parse command line arguments and return false if we should not continue + bool parseArguments(int argc, char** argv); + /// Parse input file and check if test exists + bool processInput(); + /// Start FixedJsonRpcServer + void actOnInput(); + +private: + + /// Compiler arguments variable map + boost::program_options::variables_map m_args; + + /// loaded json test case + Json::Value m_json; +}; + diff --git a/ethrpctest/main.cpp b/ethrpctest/main.cpp new file mode 100644 index 000000000..3d710dd5b --- /dev/null +++ b/ethrpctest/main.cpp @@ -0,0 +1,34 @@ +/* + 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 . +*/ +/** main.cpp + * @author Marek Kotewicz + * @date 2015 + */ + +#include "CommandLineInterface.h" + +int main(int argc, char** argv) +{ + CommandLineInterface cli; + if (!cli.parseArguments(argc, argv)) + return 1; + if (!cli.processInput()) + return 1; + cli.actOnInput(); + + return 0; +} From 169ac0d4d0a30dada11ee4c3e39776b7bf0a89cf Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 10:59:46 +0100 Subject: [PATCH 051/228] ClientBase tests --- test/CMakeLists.txt | 24 ++++++ test/ClientBase.cpp | 202 ++++++++++++++++++++++++++++++++++++++++++++ test/TestHelper.cpp | 16 ---- test/TestHelper.h | 2 +- test/TestUtils.cpp | 130 ++++++++++++++++++++++++++++ test/TestUtils.h | 82 ++++++++++++++++++ 6 files changed, 439 insertions(+), 17 deletions(-) create mode 100644 test/ClientBase.cpp create mode 100644 test/TestUtils.cpp create mode 100644 test/TestUtils.h diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ef292c2bb..66b95adad 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -30,6 +30,7 @@ target_link_libraries(testeth ethereum) target_link_libraries(testeth ethcore) target_link_libraries(testeth secp256k1) target_link_libraries(testeth solidity) +target_link_libraries(testeth testutils) if (NOT HEADLESS AND NOT JUSTTESTS) target_link_libraries(testeth webthree) target_link_libraries(testeth natspec) @@ -42,13 +43,36 @@ endif() target_link_libraries(createRandomVMTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) target_link_libraries(createRandomVMTest ethereum) target_link_libraries(createRandomVMTest ethcore) +target_link_libraries(createRandomVMTest testutils) target_link_libraries(createRandomStateTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) target_link_libraries(createRandomStateTest ethereum) target_link_libraries(createRandomStateTest ethcore) +target_link_libraries(createRandomStateTest testutils) target_link_libraries(checkRandomVMTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) target_link_libraries(checkRandomVMTest ethereum) target_link_libraries(checkRandomVMTest ethcore) +target_link_libraries(checkRandomVMTest testutils) target_link_libraries(checkRandomStateTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) target_link_libraries(checkRandomStateTest ethereum) target_link_libraries(checkRandomStateTest ethcore) +target_link_libraries(checkRandomStateTest testutils) + +enable_testing() +set(CTEST_OUTPUT_ON_FAILURE TRUE) + +include(EthUtils) + +eth_add_test(ClientBase + ARGS --eth_testfile=BlockTests/bcJS_API_Test --eth_threads=1 + ARGS --eth_testfile=BlockTests/bcJS_API_Test --eth_threads=3 + ARGS --eth_testfile=BlockTests/bcJS_API_Test --eth_threads=10 + ARGS --eth_testfile=BlockTests/bcValidBlockTest --eth_threads=1 + ARGS --eth_testfile=BlockTests/bcValidBlockTest --eth_threads=3 + ARGS --eth_testfile=BlockTests/bcValidBlockTest --eth_threads=10 +) + +eth_add_test(JsonRpc + ARGS --eth_testfile=BlockTests/bcJS_API_Test + ARGS --eth_testfile=BlockTests/bcValidBlockTest +) diff --git a/test/ClientBase.cpp b/test/ClientBase.cpp new file mode 100644 index 000000000..2197ac830 --- /dev/null +++ b/test/ClientBase.cpp @@ -0,0 +1,202 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file ClientBase.cpp + * @author Marek Kotewicz + * @date 2015 + */ + +#include +#include +#include "TestUtils.h" + +using namespace std; +using namespace dev; +using namespace dev::eth; +using namespace dev::test; + +BOOST_FIXTURE_TEST_SUITE(ClientBase, ParallelClientBaseFixture) + +BOOST_AUTO_TEST_CASE(blocks) +{ + enumerateClients([](Json::Value const& _json, dev::eth::ClientBase& _client) -> void + { + for (string const& name: _json["postState"].getMemberNames()) + { + Json::Value o = _json["postState"][name]; + Address address(name); + + // balanceAt + u256 expectedBalance = u256(o["balance"].asString()); + u256 balance = _client.balanceAt(address); + ETH_CHECK_EQUAL(expectedBalance, balance); + + // countAt + u256 expectedCount = u256(o["nonce"].asString()); + u256 count = _client.countAt(address); + ETH_CHECK_EQUAL(expectedCount, count); + + // stateAt + for (string const& pos: o["storage"].getMemberNames()) + { + u256 expectedState = u256(o["storage"][pos].asString()); + u256 state = _client.stateAt(address, u256(pos)); + ETH_CHECK_EQUAL(expectedState, state); + } + + // codeAt + bytes expectedCode = fromHex(o["code"].asString()); + bytes code = _client.codeAt(address); + ETH_CHECK_EQUAL_COLLECTIONS(expectedCode.begin(), expectedCode.end(), + code.begin(), code.end()); + } + + // number + unsigned expectedNumber = _json["blocks"].size(); + unsigned number = _client.number(); + ETH_CHECK_EQUAL(expectedNumber, number); + + u256 totalDifficulty = u256(_json["genesisBlockHeader"]["difficulty"].asString()); + for (Json::Value const& block: _json["blocks"]) + { + Json::Value blockHeader = block["blockHeader"]; + Json::Value uncles = block["uncleHeaders"]; + Json::Value transactions = block["transactions"]; + h256 blockHash = h256(fromHex(blockHeader["hash"].asString())); + + // just update the difficulty + for (Json::Value const& uncle: uncles) + { + totalDifficulty += u256(uncle["difficulty"].asString()); + } + + // hashFromNumber + h256 expectedHashFromNumber = h256(fromHex(blockHeader["hash"].asString())); + h256 hashFromNumber = _client.hashFromNumber(jsToInt(blockHeader["number"].asString())); + ETH_CHECK_EQUAL(expectedHashFromNumber, hashFromNumber); + + // blockInfo + auto compareBlockInfos = [](Json::Value const& _b, BlockInfo _blockInfo) -> void + { + LogBloom expectedBlockInfoBloom = LogBloom(fromHex(_b["bloom"].asString())); + Address expectedBlockInfoCoinbase = Address(fromHex(_b["coinbase"].asString())); + u256 expectedBlockInfoDifficulty = u256(_b["difficulty"].asString()); + bytes expectedBlockInfoExtraData = fromHex(_b["extraData"].asString()); + u256 expectedBlockInfoGasLimit = u256(_b["gasLimit"].asString()); + u256 expectedBlockInfoGasUsed = u256(_b["gasUsed"].asString()); + h256 expectedBlockInfoHash = h256(fromHex(_b["hash"].asString())); + h256 expectedBlockInfoMixHash = h256(fromHex(_b["mixHash"].asString())); + Nonce expectedBlockInfoNonce = Nonce(fromHex(_b["nonce"].asString())); + u256 expectedBlockInfoNumber = u256(_b["number"].asString()); + h256 expectedBlockInfoParentHash = h256(fromHex(_b["parentHash"].asString())); + h256 expectedBlockInfoReceiptsRoot = h256(fromHex(_b["receiptTrie"].asString())); + u256 expectedBlockInfoTimestamp = u256(_b["timestamp"].asString()); + h256 expectedBlockInfoTransactionsRoot = h256(fromHex(_b["transactionsTrie"].asString())); + h256 expectedBlockInfoUncldeHash = h256(fromHex(_b["uncleHash"].asString())); + ETH_CHECK_EQUAL(expectedBlockInfoBloom, _blockInfo.logBloom); + ETH_CHECK_EQUAL(expectedBlockInfoCoinbase, _blockInfo.coinbaseAddress); + ETH_CHECK_EQUAL(expectedBlockInfoDifficulty, _blockInfo.difficulty); + ETH_CHECK_EQUAL_COLLECTIONS(expectedBlockInfoExtraData.begin(), expectedBlockInfoExtraData.end(), + _blockInfo.extraData.begin(), _blockInfo.extraData.end()); + ETH_CHECK_EQUAL(expectedBlockInfoGasLimit, _blockInfo.gasLimit); + ETH_CHECK_EQUAL(expectedBlockInfoGasUsed, _blockInfo.gasUsed); + ETH_CHECK_EQUAL(expectedBlockInfoHash, _blockInfo.hash); + ETH_CHECK_EQUAL(expectedBlockInfoMixHash, _blockInfo.mixHash); + ETH_CHECK_EQUAL(expectedBlockInfoNonce, _blockInfo.nonce); + ETH_CHECK_EQUAL(expectedBlockInfoNumber, _blockInfo.number); + ETH_CHECK_EQUAL(expectedBlockInfoParentHash, _blockInfo.parentHash); + ETH_CHECK_EQUAL(expectedBlockInfoReceiptsRoot, _blockInfo.receiptsRoot); + ETH_CHECK_EQUAL(expectedBlockInfoTimestamp, _blockInfo.timestamp); + ETH_CHECK_EQUAL(expectedBlockInfoTransactionsRoot, _blockInfo.transactionsRoot); + ETH_CHECK_EQUAL(expectedBlockInfoUncldeHash, _blockInfo.sha3Uncles); + }; + + BlockInfo blockInfo = _client.blockInfo(blockHash); + compareBlockInfos(blockHeader, blockInfo); + + // blockDetails + unsigned expectedBlockDetailsNumber = jsToInt(blockHeader["number"].asString()); + totalDifficulty += u256(blockHeader["difficulty"].asString()); + BlockDetails blockDetails = _client.blockDetails(blockHash); + ETH_CHECK_EQUAL(expectedBlockDetailsNumber, blockDetails.number); + ETH_CHECK_EQUAL(totalDifficulty, blockDetails.totalDifficulty); + + auto compareTransactions = [](Json::Value const& _t, Transaction _transaction) -> void + { + bytes expectedTransactionData = fromHex(_t["data"].asString()); + u256 expectedTransactionGasLimit = u256(_t["gasLimit"].asString()); + u256 expectedTransactionGasPrice = u256(_t["gasPrice"].asString()); + u256 expectedTransactionNonce = u256(_t["nonce"].asString()); + u256 expectedTransactionSignatureR = h256(fromHex(_t["r"].asString())); + u256 expectedTransactionSignatureS = h256(fromHex(_t["s"].asString())); +// unsigned expectedTransactionSignatureV = jsToInt(t["v"].asString()); + + ETH_CHECK_EQUAL_COLLECTIONS(expectedTransactionData.begin(), expectedTransactionData.end(), + _transaction.data().begin(), _transaction.data().end()); + ETH_CHECK_EQUAL(expectedTransactionGasLimit, _transaction.gas()); + ETH_CHECK_EQUAL(expectedTransactionGasPrice, _transaction.gasPrice()); + ETH_CHECK_EQUAL(expectedTransactionNonce, _transaction.nonce()); + ETH_CHECK_EQUAL(expectedTransactionSignatureR, _transaction.signature().r); + ETH_CHECK_EQUAL(expectedTransactionSignatureS, _transaction.signature().s); +// ETH_CHECK_EQUAL(expectedTransactionSignatureV, _transaction.signature().v); // 27 === 0x0, 28 === 0x1, not sure why + }; + + Transactions ts = _client.transactions(blockHash); + TransactionHashes tHashes = _client.transactionHashes(blockHash); + unsigned tsCount = _client.transactionCount(blockHash); + + ETH_REQUIRE(transactions.size() == ts.size()); + ETH_REQUIRE(transactions.size() == tHashes.size()); + + // transactionCount + ETH_CHECK_EQUAL(transactions.size(), tsCount); + + for (unsigned i = 0; i < tsCount; i++) + { + Json::Value t = transactions[i]; + + // transaction (by block hash and transaction index) + Transaction transaction = _client.transaction(blockHash, i); + compareTransactions(t, transaction); + + // transaction (by hash) + Transaction transactionByHash = _client.transaction(transaction.sha3()); + compareTransactions(t, transactionByHash); + + // transactions + compareTransactions(t, ts[i]); + + // transactionHashes + ETH_CHECK_EQUAL(transaction.sha3(), tHashes[i]); + } + + // uncleCount + unsigned usCount = _client.uncleCount(blockHash); + ETH_CHECK_EQUAL(uncles.size(), usCount); + + for (unsigned i = 0; i < usCount; i++) + { + Json::Value u = uncles[i]; + + // uncle (by hash) + BlockInfo uncle = _client.uncle(blockHash, i); + compareBlockInfos(u, uncle); + } + } + }); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index b29c5dc3a..7dc001498 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -374,22 +374,6 @@ void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _e } } -std::string getTestPath() -{ - string testPath; - const char* ptestPath = getenv("ETHEREUM_TEST_PATH"); - - if (ptestPath == NULL) - { - cnote << " could not find environment variable ETHEREUM_TEST_PATH \n"; - testPath = "../../../tests"; - } - else - testPath = ptestPath; - - return testPath; -} - void userDefinedTest(string testTypeFlag, std::function doTests) { for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) diff --git a/test/TestHelper.h b/test/TestHelper.h index ade20f5e4..022c715f8 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -28,6 +28,7 @@ #include "JsonSpiritHeaders.h" #include #include +#include namespace dev { @@ -138,7 +139,6 @@ void checkLog(eth::LogEntries _resultLogs, eth::LogEntries _expectedLogs); void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _expectedCallCreates); void executeTests(const std::string& _name, const std::string& _testPathAppendix, std::function doTests); -std::string getTestPath(); void userDefinedTest(std::string testTypeFlag, std::function doTests); RLPStream createRLPStreamFromTransactionFields(json_spirit::mObject& _tObj); eth::LastHashes lastHashes(u256 _currentBlockNumber); diff --git a/test/TestUtils.cpp b/test/TestUtils.cpp new file mode 100644 index 000000000..71eeadb3a --- /dev/null +++ b/test/TestUtils.cpp @@ -0,0 +1,130 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file TestUtils.cpp + * @author Marek Kotewicz + * @date 2015 + */ + +#include +#include +#include +#include +#include +#include +#include +#include "TestUtils.h" + +// used methods from TestHelper: +// getTestPath +#include "TestHelper.h" + +using namespace std; +using namespace dev; +using namespace dev::eth; +using namespace dev::test; + +namespace dev +{ +namespace test +{ + +bool getCommandLineOption(std::string const& _name); +std::string getCommandLineArgument(std::string const& _name, bool _require = false); + +} +} + +bool dev::test::getCommandLineOption(string const& _name) +{ + auto argc = boost::unit_test::framework::master_test_suite().argc; + auto argv = boost::unit_test::framework::master_test_suite().argv; + bool result = false; + for (auto i = 0; !result && i < argc; ++i) + result = _name == argv[i]; + return result; +} + +std::string dev::test::getCommandLineArgument(string const& _name, bool _require) +{ + auto argc = boost::unit_test::framework::master_test_suite().argc; + auto argv = boost::unit_test::framework::master_test_suite().argv; + for (auto i = 1; i < argc; ++i) + { + string str = argv[i]; + if (_name == str.substr(0, _name.size())) + return str.substr(str.find("=") + 1); + } + if (_require) + BOOST_ERROR("Failed getting command line argument: " << _name << " from: " << argv); + return ""; +} + +bool LoadTestFileFixture::m_loaded = false; +Json::Value LoadTestFileFixture::m_json; + +LoadTestFileFixture::LoadTestFileFixture() +{ + if (!m_loaded) + { + m_json = loadJsonFromFile(toTestFilePath(getCommandLineArgument("--eth_testfile"))); + m_loaded = true; + } +} + +void ParallelFixture::enumerateThreads(std::function callback) +{ + size_t threadsCount = std::stoul(getCommandLineArgument("--eth_threads"), nullptr, 10); + + vector workers; + for (size_t i = 0; i < threadsCount; i++) + workers.emplace_back(callback); + + for_each(workers.begin(), workers.end(), [](thread &t) + { + t.join(); + }); +} + +void BlockChainFixture::enumerateBlockchains(std::function callback) +{ + for (string const& name: m_json.getMemberNames()) + { + BlockChainLoader bcl(m_json[name]); + callback(m_json[name], bcl.bc(), bcl.state()); + } +} + +void ClientBaseFixture::enumerateClients(std::function callback) +{ + enumerateBlockchains([&callback](Json::Value const& _json, BlockChain& _bc, State _state) -> void + { + FixedClient client(_bc, _state); + callback(_json, client); + }); +} + +void ParallelClientBaseFixture::enumerateClients(std::function callback) +{ + ClientBaseFixture::enumerateClients([this, &callback](Json::Value const& _json, dev::eth::ClientBase& _client) -> void + { + // json is being copied here + enumerateThreads([callback, _json, &_client]() -> void + { + callback(_json, _client); + }); + }); +} diff --git a/test/TestUtils.h b/test/TestUtils.h new file mode 100644 index 000000000..fef10e982 --- /dev/null +++ b/test/TestUtils.h @@ -0,0 +1,82 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file TestUtils.h + * @author Marek Kotewicz + * @date 2015 + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace dev +{ +namespace test +{ + +// should be used for multithread tests +static SharedMutex x_boostTest; +#define ETH_CHECK_EQUAL(x, y) { dev::WriteGuard(x_boostTest); BOOST_CHECK_EQUAL(x, y); } +#define ETH_CHECK_EQUAL_COLLECTIONS(xb, xe, yb, ye) { dev::WriteGuard(x_boostTest); BOOST_CHECK_EQUAL_COLLECTIONS(xb, xe, yb, ye); } +#define ETH_REQUIRE(x) { dev::WriteGuard(x_boostTest); BOOST_REQUIRE(x); } + +struct LoadTestFileFixture +{ + LoadTestFileFixture(); + + static bool m_loaded; + static Json::Value m_json; +}; + +struct ParallelFixture +{ + void enumerateThreads(std::function callback); +}; + +struct BlockChainFixture: public LoadTestFileFixture +{ + void enumerateBlockchains(std::function callback); +}; + +struct ClientBaseFixture: public BlockChainFixture +{ + void enumerateClients(std::function callback); +}; + +// important BOOST TEST do have problems with thread safety!!! +// BOOST_CHECK is not thread safe +// BOOST_MESSAGE is not thread safe +// http://boost.2283326.n4.nabble.com/Is-boost-test-thread-safe-td3471644.html +// http://lists.boost.org/boost-users/2010/03/57691.php +// worth reading +// https://codecrafter.wordpress.com/2012/11/01/c-unit-test-framework-adapter-part-3/ +struct ParallelClientBaseFixture: public ClientBaseFixture, public ParallelFixture +{ + void enumerateClients(std::function callback); +}; + +struct JsonRpcFixture: public ClientBaseFixture +{ + +}; + +} +} From bd1e66dd1117701aecdea8406ae5e9e9ec4ed7ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 25 Mar 2015 11:52:24 +0100 Subject: [PATCH 052/228] Code cleanup --- evmjit/libevmjit/ExecutionEngine.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/evmjit/libevmjit/ExecutionEngine.cpp b/evmjit/libevmjit/ExecutionEngine.cpp index c00691f3e..ce1f530d7 100644 --- a/evmjit/libevmjit/ExecutionEngine.cpp +++ b/evmjit/libevmjit/ExecutionEngine.cpp @@ -91,10 +91,11 @@ void parseOptions() // "source" scheduler has a bug: http://llvm.org/bugs/show_bug.cgi?id=22304 auto envLine = std::getenv("EVMJIT"); auto commandLine = std::string{"evmjit "} + (envLine ? envLine : "") + " -pre-RA-sched=list-burr\0"; - char const* argv[100] = {nullptr, }; + static const auto c_maxArgs = 20; + char const* argv[c_maxArgs] = {nullptr, }; auto arg = std::strtok(&*commandLine.begin(), " "); auto i = 0; - for (; i < sizeof(argv) / sizeof(argv[0]) && arg; ++i, arg = std::strtok(nullptr, " ")) + for (; i < c_maxArgs && arg; ++i, arg = std::strtok(nullptr, " ")) argv[i] = arg; cl::ParseCommandLineOptions(i, argv, "Ethereum EVM JIT Compiler"); } From 36e2c9dbeee9c7f5a3b27ac320e6d9e558da532f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 25 Mar 2015 12:32:34 +0100 Subject: [PATCH 053/228] Hide accounts/contracts when we're not compiled with FATDB. Fixes #1403 --- alethzero/Main.ui | 4 ++-- alethzero/MainWin.cpp | 25 +++++++++++++++++++------ libethereum/Interface.h | 1 + libethereum/State.cpp | 4 ++++ libethereum/State.h | 1 + 5 files changed, 27 insertions(+), 8 deletions(-) diff --git a/alethzero/Main.ui b/alethzero/Main.ui index da5ee6c4a..34f240fbf 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -208,7 +208,7 @@ - + QDockWidget::DockWidgetFeatureMask @@ -566,7 +566,7 @@ - + QDockWidget::DockWidgetFeatureMask diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index d566882f3..f63cd74f0 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -135,6 +135,11 @@ Main::Main(QWidget *parent) : // ui->log->addItem(QString::fromStdString(s)); }; +#if !ETH_FATDB + ui->dockWidgetAccounts->hide(); + ui->dockWidgetContracts->hide(); +#endif + #if ETH_DEBUG m_servers.append("localhost:30300"); #endif @@ -1036,6 +1041,7 @@ void Main::refreshPending() void Main::refreshAccounts() { +#if ETH_FATDB cwatch << "refreshAccounts()"; ui->accounts->clear(); ui->contracts->clear(); @@ -1053,6 +1059,7 @@ void Main::refreshAccounts() ->setData(Qt::UserRole, QByteArray((char const*)i.data(), Address::size)); } } +#endif } void Main::refreshBlockCount() @@ -1626,16 +1633,22 @@ void Main::on_ourAccounts_doubleClicked() void Main::on_accounts_doubleClicked() { - auto hba = ui->accounts->currentItem()->data(Qt::UserRole).toByteArray(); - auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); - qApp->clipboard()->setText(QString::fromStdString(toHex(h.asArray()))); + if (!ui->accounts->isEmpty()) + { + auto hba = ui->accounts->currentItem()->data(Qt::UserRole).toByteArray(); + auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); + qApp->clipboard()->setText(QString::fromStdString(toHex(h.asArray()))); + } } void Main::on_contracts_doubleClicked() { - auto hba = ui->contracts->currentItem()->data(Qt::UserRole).toByteArray(); - auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); - qApp->clipboard()->setText(QString::fromStdString(toHex(h.asArray()))); + if (!ui->contracts->isEmpty()) + { + auto hba = ui->contracts->currentItem()->data(Qt::UserRole).toByteArray(); + auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); + qApp->clipboard()->setText(QString::fromStdString(toHex(h.asArray()))); + } } static shh::FullTopic topicFromText(QString _s) diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 68eb4b094..b78106ae1 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -138,6 +138,7 @@ public: virtual StateDiff diff(unsigned _txi, BlockNumber _block) const = 0; /// Get a list of all active addresses. + /// NOTE: This only works when compiled with ETH_FATDB; otherwise will throw InterfaceNotSupported. virtual Addresses addresses() const { return addresses(m_default); } virtual Addresses addresses(BlockNumber _block) const = 0; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index c5732116d..486abde12 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -342,6 +342,7 @@ u256 State::enactOn(bytesConstRef _block, BlockInfo const& _bi, BlockChain const map State::addresses() const { +#if ETH_FATDB map ret; for (auto i: m_cache) if (i.second.isAlive()) @@ -350,6 +351,9 @@ map State::addresses() const if (m_cache.find(i.first) == m_cache.end()) ret[i.first] = RLP(i.second)[1].toInt(); return ret; +#else + throw InterfaceNotSupported("State::addresses()"); +#endif } void State::resetCurrent() diff --git a/libethereum/State.h b/libethereum/State.h index 46a111a9b..3883a4897 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -127,6 +127,7 @@ public: OverlayDB const& db() const { return m_db; } /// @returns the set containing all addresses currently in use in Ethereum. + /// @throws InterfaceNotSupported if compiled without ETH_FATDB. std::map addresses() const; /// Get the header information on the present block. From 2321f2914ba2b5023332ae80a7346352bcc7220b Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 25 Mar 2015 12:58:06 +0100 Subject: [PATCH 054/228] - bugfix: #1397. - Debugger panel: UI bug fixes. --- mix/ContractCallDataEncoder.cpp | 16 ++++++++++------ mix/qml/QHashTypeView.qml | 1 - mix/qml/QIntTypeView.qml | 1 - mix/qml/QStringTypeView.qml | 1 - mix/qml/StructView.qml | 11 ++++++----- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index fc5dcee03..6553244dc 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -76,18 +76,22 @@ unsigned ContractCallDataEncoder::encodeSingleItem(QVariant const& _data, Solidi unsigned const alignSize = 32; QString src = _data.toString(); bytes result; - if (src.length() >= 2 && ((src.startsWith("\"") && src.endsWith("\"")) || (src.startsWith("\'") && src.endsWith("\'")))) - { + + if ((src.startsWith("\"") && src.endsWith("\"")) || (src.startsWith("\'") && src.endsWith("\'"))) src = src.remove(src.length() - 1, 1).remove(0, 1); - QByteArray bytesAr = src.toLocal8Bit(); - result = bytes(bytesAr.begin(), bytesAr.end()); - } - else if (src.startsWith("0x")) + + QRegExp rx("[a-z]+"); + if (src.startsWith("0x")) { result = fromHex(src.toStdString().substr(2)); if (_type.type != SolidityType::Type::Bytes) result = padded(result, alignSize); } + else if (rx.indexIn(src.toLower(), 0) != -1) + { + QByteArray bytesAr = src.toLocal8Bit(); + result = bytes(bytesAr.begin(), bytesAr.end()); + } else { bigint i(src.toStdString()); diff --git a/mix/qml/QHashTypeView.qml b/mix/qml/QHashTypeView.qml index 77da45365..a097c22dd 100644 --- a/mix/qml/QHashTypeView.qml +++ b/mix/qml/QHashTypeView.qml @@ -15,7 +15,6 @@ Item Rectangle { anchors.fill: parent radius: 4 - color: "#f7f7f7" TextInput { id: textinput text: value diff --git a/mix/qml/QIntTypeView.qml b/mix/qml/QIntTypeView.qml index 206b641f3..8adb46846 100644 --- a/mix/qml/QIntTypeView.qml +++ b/mix/qml/QIntTypeView.qml @@ -16,7 +16,6 @@ Item Rectangle { anchors.fill: parent radius: 4 - color: "#f7f7f7" TextInput { id: textinput text: value diff --git a/mix/qml/QStringTypeView.qml b/mix/qml/QStringTypeView.qml index 0cde9b662..ffbde734c 100644 --- a/mix/qml/QStringTypeView.qml +++ b/mix/qml/QStringTypeView.qml @@ -15,7 +15,6 @@ Item Rectangle { anchors.fill: parent radius: 4 - color: "#f7f7f7" TextInput { id: textinput text: value diff --git a/mix/qml/StructView.qml b/mix/qml/StructView.qml index 312a24804..045b2eabc 100644 --- a/mix/qml/StructView.qml +++ b/mix/qml/StructView.qml @@ -24,24 +24,24 @@ Column height: 20 id: typeLabel text: modelData.type.name - Layout.preferredWidth: 60 + anchors.verticalCenter: parent.verticalCenter } DefaultLabel { id: nameLabel text: modelData.name - Layout.preferredWidth: 100 + anchors.verticalCenter: parent.verticalCenter } DefaultLabel { id: equalLabel text: "=" - Layout.preferredWidth: 15 + anchors.verticalCenter: parent.verticalCenter } Loader { id: typeLoader - Layout.preferredWidth: 150 + anchors.verticalCenter: parent.verticalCenter sourceComponent: { var t = modelData.type.category; @@ -63,7 +63,8 @@ Column var ptype = members[index].type; var pname = members[index].name; var vals = value; - if (ptype.category === QSolidityType.Struct && !item.members) { + if (ptype.category === QSolidityType.Struct && !item.members) + { item.value = getValue(); item.members = ptype.members; } From e26de2489e1751405372c18360c52b138fa9de75 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 25 Mar 2015 13:10:30 +0100 Subject: [PATCH 055/228] Fix AZ accounts/contracts panes. Added initial ABI tool. --- CMakeLists.txt | 3 +- abi/CMakeLists.txt | 16 +++++ abi/main.cpp | 137 ++++++++++++++++++++++++++++++++++++++++++ alethzero/MainWin.cpp | 8 +-- 4 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 abi/CMakeLists.txt create mode 100644 abi/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e7e89a5a7..40a3e8e63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -160,7 +160,6 @@ if (EVMJIT) endif() add_subdirectory(libdevcore) -add_subdirectory(rlp) add_subdirectory(libevmcore) add_subdirectory(liblll) @@ -199,6 +198,8 @@ add_subdirectory(test) if (NOT JUSTTESTS) + add_subdirectory(rlp) + add_subdirectory(abi) add_subdirectory(eth) if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug") diff --git a/abi/CMakeLists.txt b/abi/CMakeLists.txt new file mode 100644 index 000000000..82c7c4240 --- /dev/null +++ b/abi/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_policy(SET CMP0015 NEW) +set(CMAKE_AUTOMOC OFF) + +aux_source_directory(. SRC_LIST) + +include_directories(BEFORE ..) +include_directories(${LEVELDB_INCLUDE_DIRS}) + +set(EXECUTABLE abi) + +add_executable(${EXECUTABLE} ${SRC_LIST}) + +target_link_libraries(${EXECUTABLE} ethereum) + +install( TARGETS ${EXECUTABLE} DESTINATION bin) + diff --git a/abi/main.cpp b/abi/main.cpp new file mode 100644 index 000000000..7bb00e390 --- /dev/null +++ b/abi/main.cpp @@ -0,0 +1,137 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file main.cpp + * @author Gav Wood + * @date 2014 + * RLP tool. + */ +#include +#include +#include +#include "../test/JsonSpiritHeaders.h" +#include +#include +#include +#include +using namespace std; +using namespace dev; +namespace js = json_spirit; + +void help() +{ + cout + << "Usage abi enc (, (, ... ))" << endl + << " abi enc -a (, (, ... ))" << endl + << " abi dec -a [ | ]" << endl + << "Options:" << endl + << " -a,--abi-file Specify the JSON ABI file." << endl + << "Output options (dec mode):" << endl + << " -i,--index When decoding, output only the nth (counting from 0) return value." << endl + << " -d,--decimal All data should be displayed as decimal." << endl + << " -x,--hex Display all data as hex." << endl + << " -b,--binary Display all data as binary." << endl + << " -p,--prefix Prefix by a base identifier." << endl + << " -z,--no-zeroes Remove any leading zeroes from the data." << endl + << " -n,--no-nulls Remove any trailing nulls from the data." << endl + << "General options:" << endl + << " -h,--help Print this help message and exit." << endl + << " -V,--version Show the version and exit." << endl + ; + exit(0); +} + +void version() +{ + cout << "abi version " << dev::Version << endl; + exit(0); +} + +enum class Mode { + Encode, + Decode +}; + +enum class Encoding { + Auto, + Decimal, + Hex, + Binary, +}; + +int main(int argc, char** argv) +{ + Encoding encoding = Encoding::Auto; + Mode mode = Mode::Encode; + string abiFile; + string methodName; + bool outputPrefix = false; + bool clearZeroes = false; + bool clearNulls = false; + int outputIndex = -1; + + for (int i = 1; i < argc; ++i) + { + string arg = argv[i]; + if (arg == "-h" || arg == "--help") + help(); + else if (arg == "enc" && i == 1) + mode = Mode::Encode; + else if (arg == "dec" && i == 1) + mode = Mode::Decode; + else if ((arg == "-a" || arg == "--abi") && argc > i) + abiFile = argv[++i]; + else if ((arg == "-i" || arg == "--index") && argc > i) + outputIndex = atoi(argv[++i]); + else if (arg == "-p" || arg == "--prefix") + outputPrefix = true; + else if (arg == "-z" || arg == "--no-zeroes") + clearZeroes = true; + else if (arg == "-n" || arg == "--no-nulls") + clearNulls = true; + else if (arg == "-x" || arg == "--hex") + encoding = Encoding::Hex; + else if (arg == "-d" || arg == "--decimal" || arg == "--dec") + encoding = Encoding::Decimal; + else if (arg == "-b" || arg == "--binary" || arg == "--bin") + encoding = Encoding::Binary; + else if (arg == "-V" || arg == "--version") + version(); + else + methodName = arg; + } + + string abi; + if (abiFile == "--") + for (int i = cin.get(); i != -1; i = cin.get()) + abi.push_back((char)i); + else if (!abiFile.empty()) + abi = contentsString(abiFile); + + if (mode == Mode::Encode) + { + (void)encoding; + (void)outputPrefix; + (void)clearZeroes; + (void)clearNulls; + (void)outputIndex; + } + else if (mode == Mode::Decode) + { + } + + return 0; +} diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index f63cd74f0..4b9af1666 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -136,8 +136,8 @@ Main::Main(QWidget *parent) : }; #if !ETH_FATDB - ui->dockWidgetAccounts->hide(); - ui->dockWidgetContracts->hide(); + delete ui->dockWidget_accounts; + delete ui->dockWidget_contracts; #endif #if ETH_DEBUG @@ -1633,7 +1633,7 @@ void Main::on_ourAccounts_doubleClicked() void Main::on_accounts_doubleClicked() { - if (!ui->accounts->isEmpty()) + if (ui->accounts->count()) { auto hba = ui->accounts->currentItem()->data(Qt::UserRole).toByteArray(); auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); @@ -1643,7 +1643,7 @@ void Main::on_accounts_doubleClicked() void Main::on_contracts_doubleClicked() { - if (!ui->contracts->isEmpty()) + if (ui->contracts->count()) { auto hba = ui->contracts->currentItem()->data(Qt::UserRole).toByteArray(); auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); From e78b641290963eac4462d46a41b5eb90dfa79764 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 13:37:43 +0100 Subject: [PATCH 056/228] fixed link dependencies for libtestutils --- libtestutils/CMakeLists.txt | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/libtestutils/CMakeLists.txt b/libtestutils/CMakeLists.txt index f79daa47c..202b9823c 100644 --- a/libtestutils/CMakeLists.txt +++ b/libtestutils/CMakeLists.txt @@ -11,9 +11,7 @@ aux_source_directory(. SRC_LIST) include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) include_directories(BEFORE ..) -include_directories(${MHD_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) -include_directories(${LEVELDB_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS}) set(EXECUTABLE testutils) @@ -26,18 +24,10 @@ else() add_library(${EXECUTABLE} SHARED ${SRC_LIST} ${HEADERS}) endif() -target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES}) +target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES}) -target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_SERVER_LIBRARIES}) -target_link_libraries(${EXECUTABLE} ${MHD_LIBRARIES}) - -target_link_libraries(${EXECUTABLE} webthree) -target_link_libraries(${EXECUTABLE} secp256k1) -target_link_libraries(${EXECUTABLE} solidity) - -if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) - target_link_libraries(${EXECUTABLE} serpent) -endif() +target_link_libraries(${EXECUTABLE} ethereum) +target_link_libraries(${EXECUTABLE} web3jsonrpc) install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) From 77b8e7796ac1fb4ef1cf1a19f17da80dbb2dd454 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 13:47:10 +0100 Subject: [PATCH 057/228] added missing headers --- libtestutils/FixedWebThreeServer.cpp | 20 ++++++++++++++++++++ libtestutils/FixedWebThreeServer.h | 19 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/libtestutils/FixedWebThreeServer.cpp b/libtestutils/FixedWebThreeServer.cpp index 6a1b13b11..c72a106c6 100644 --- a/libtestutils/FixedWebThreeServer.cpp +++ b/libtestutils/FixedWebThreeServer.cpp @@ -1,2 +1,22 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file FixedWebThreeStubServer.cpp + * @author Marek Kotewicz + * @date 2015 + */ #include "FixedWebThreeServer.h" diff --git a/libtestutils/FixedWebThreeServer.h b/libtestutils/FixedWebThreeServer.h index aab12c461..53db2f2b2 100644 --- a/libtestutils/FixedWebThreeServer.h +++ b/libtestutils/FixedWebThreeServer.h @@ -1,4 +1,23 @@ +/* + This file is part of cpp-ethereum. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file FixedWebThreeStubServer.h + * @author Marek Kotewicz + * @date 2015 + */ #pragma once From 2a892a60da6f99dfa94625dcbbc666e9cade4da7 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 13:57:07 +0100 Subject: [PATCH 058/228] pr fixes for ShortLivingDirectory --- libtestutils/ShortLivingDirectory.cpp | 13 ++++++------- libtestutils/ShortLivingDirectory.h | 6 +++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/libtestutils/ShortLivingDirectory.cpp b/libtestutils/ShortLivingDirectory.cpp index 56fc101a7..48e1d643d 100644 --- a/libtestutils/ShortLivingDirectory.cpp +++ b/libtestutils/ShortLivingDirectory.cpp @@ -20,21 +20,20 @@ */ #include +#include #include "ShortLivingDirectory.h" -#include "Common.h" using namespace std; using namespace dev; using namespace dev::test; -ShortLivingDirectory::ShortLivingDirectory() -{ - m_path = getRandomPath(); - boost::filesystem::create_directories(m_path); -} - ShortLivingDirectory::ShortLivingDirectory(std::string const& _path) : m_path(_path) { + // we never ever want to delete a directory (including all its contents) that we did not create ourselves. + if (boost::filesystem::exists(m_path)) { + BOOST_THROW_EXCEPTION(FileError()); + } + boost::filesystem::create_directories(m_path); } diff --git a/libtestutils/ShortLivingDirectory.h b/libtestutils/ShortLivingDirectory.h index 6f572c2bc..434120e04 100644 --- a/libtestutils/ShortLivingDirectory.h +++ b/libtestutils/ShortLivingDirectory.h @@ -22,6 +22,7 @@ #pragma once #include +#include "Common.h" namespace dev { @@ -31,11 +32,10 @@ namespace test class ShortLivingDirectory { public: - ShortLivingDirectory(); - ShortLivingDirectory(std::string const& _path); + ShortLivingDirectory(std::string const& _path = getRandomPath()); ~ShortLivingDirectory(); - std::string path(){ return m_path; } + std::string const& path() { return m_path; } private: std::string m_path; From 42b3160f13d8c50de0a8fbac2dbaaaa4df37464d Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 13:59:02 +0100 Subject: [PATCH 059/228] pr fixes for StateLoader --- libtestutils/StateLoader.cpp | 3 +-- libtestutils/StateLoader.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/libtestutils/StateLoader.cpp b/libtestutils/StateLoader.cpp index 0561e40f1..b56475b5a 100644 --- a/libtestutils/StateLoader.cpp +++ b/libtestutils/StateLoader.cpp @@ -26,8 +26,7 @@ using namespace dev; using namespace dev::eth; using namespace dev::test; -StateLoader::StateLoader(Json::Value const& _json) -: m_state(Address(), OverlayDB(), BaseState::Empty) +StateLoader::StateLoader(Json::Value const& _json) : m_state(Address(), OverlayDB(), BaseState::Empty) { for (string const& name: _json.getMemberNames()) { diff --git a/libtestutils/StateLoader.h b/libtestutils/StateLoader.h index 90f806714..10d7251cc 100644 --- a/libtestutils/StateLoader.h +++ b/libtestutils/StateLoader.h @@ -33,7 +33,7 @@ class StateLoader { public: StateLoader(Json::Value const& _json); - eth::State state() { return m_state; } + eth::State const& state() { return m_state; } private: eth::State m_state; From 42177bc187df8ebeb8a10ec891ee73d12624d82f Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Tue, 17 Mar 2015 14:21:22 +0100 Subject: [PATCH 060/228] added getABIType() to types --- libsolidity/Types.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/libsolidity/Types.h b/libsolidity/Types.h index c90aabda1..3d3a263d5 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -187,6 +187,9 @@ public: "for type without literals.")); } + /// Returns address of type for ABI interface + virtual TypePointer ABIType() const { return TypePointer(); } + protected: /// Convenience object used when returning an empty member list. static const MemberList EmptyMemberList; @@ -217,10 +220,12 @@ public: virtual unsigned getStorageBytes() const override { return m_bits / 8; } virtual bool isValueType() const override { return true; } - virtual MemberList const& getMembers() const { return isAddress() ? AddressMemberList : EmptyMemberList; } + virtual MemberList const& getMembers() const override { return isAddress() ? AddressMemberList : EmptyMemberList; } virtual std::string toString() const override; + virtual TypePointer ABIType() const override { return shared_from_this(); } + int getNumBits() const { return m_bits; } bool isAddress() const { return m_modifier == Modifier::Address; } bool isSigned() const { return m_modifier == Modifier::Signed; } @@ -258,6 +263,7 @@ public: virtual std::string toString() const override; virtual u256 literalValue(Literal const* _literal) const override; virtual TypePointer getRealType() const override; + virtual TypePointer ABIType() const override { return shared_from_this(); } /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. std::shared_ptr getIntegerType() const; @@ -311,7 +317,7 @@ public: virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; - virtual unsigned getCalldataEncodedSize(bool _padded) const { return _padded ? 32 : 1; } + virtual unsigned getCalldataEncodedSize(bool _padded) const override{ return _padded ? 32 : 1; } virtual unsigned getStorageBytes() const override { return 1; } virtual bool isValueType() const override { return true; } @@ -361,7 +367,7 @@ public: virtual unsigned getSizeOnStack() const override; virtual std::string toString() const override; virtual MemberList const& getMembers() const override { return s_arrayTypeMemberList; } - + virtual TypePointer ABIType() const override { return (m_baseType->ABIType() ? m_baseType->ABIType() : ABIType()); } Location getLocation() const { return m_location; } bool isByteArray() const { return m_isByteArray; } TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;} @@ -400,6 +406,7 @@ public: virtual std::string toString() const override; virtual MemberList const& getMembers() const override; + virtual TypePointer ABIType() const override { return std::make_shared(160, IntegerType::Modifier::Address); } bool isSuper() const { return m_super; } ContractDefinition const& getContractDefinition() const { return m_contract; } @@ -468,6 +475,7 @@ public: virtual bool isValueType() const override { return true; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual TypePointer ABIType() const override { return std::make_shared(8 * int(getStorageBytes())); } EnumDefinition const& getEnumDefinition() const { return m_enum; } /// @returns the value that the string has in the Enum From 3d18c02f3697d4399d50962f31fd11c2523c0371 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Thu, 19 Mar 2015 16:48:54 +0100 Subject: [PATCH 061/228] added externalType for ArrayType --- libsolidity/Types.cpp | 17 +++++++++++++++++ libsolidity/Types.h | 13 +++++++------ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 016f0b236..6fe8a6410 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -742,6 +742,23 @@ string ArrayType::toString() const return ret + "]"; } +TypePointer ArrayType::externalType() const +{ + if (m_location != Location::CallData) + return TypePointer(); + if (m_isByteArray) + return shared_from_this(); + if (!(m_baseType->externalType())) + return TypePointer(); + if (dynamic_cast(m_baseType.get()) && m_baseType->isDynamicallySized()) + return TypePointer(); + + if (m_baseType->isDynamicallySized()) + return std::make_shared(Location::CallData, m_baseType->externalType()); + else + return std::make_shared(Location::CallData, m_baseType->externalType(), m_length); +} + shared_ptr ArrayType::copyForLocation(ArrayType::Location _location) const { auto copy = make_shared(_location); diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 3d3a263d5..013c65893 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -188,7 +188,7 @@ public: } /// Returns address of type for ABI interface - virtual TypePointer ABIType() const { return TypePointer(); } + virtual TypePointer externalType() const { return TypePointer(); } protected: /// Convenience object used when returning an empty member list. @@ -224,7 +224,7 @@ public: virtual std::string toString() const override; - virtual TypePointer ABIType() const override { return shared_from_this(); } + virtual TypePointer externalType() const override { return shared_from_this(); } int getNumBits() const { return m_bits; } bool isAddress() const { return m_modifier == Modifier::Address; } @@ -263,7 +263,7 @@ public: virtual std::string toString() const override; virtual u256 literalValue(Literal const* _literal) const override; virtual TypePointer getRealType() const override; - virtual TypePointer ABIType() const override { return shared_from_this(); } + virtual TypePointer externalType() const override { return shared_from_this(); } /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. std::shared_ptr getIntegerType() const; @@ -367,7 +367,8 @@ public: virtual unsigned getSizeOnStack() const override; virtual std::string toString() const override; virtual MemberList const& getMembers() const override { return s_arrayTypeMemberList; } - virtual TypePointer ABIType() const override { return (m_baseType->ABIType() ? m_baseType->ABIType() : ABIType()); } + virtual TypePointer externalType() const override; + Location getLocation() const { return m_location; } bool isByteArray() const { return m_isByteArray; } TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;} @@ -406,7 +407,7 @@ public: virtual std::string toString() const override; virtual MemberList const& getMembers() const override; - virtual TypePointer ABIType() const override { return std::make_shared(160, IntegerType::Modifier::Address); } + virtual TypePointer externalType() const override { return std::make_shared(160, IntegerType::Modifier::Address); } bool isSuper() const { return m_super; } ContractDefinition const& getContractDefinition() const { return m_contract; } @@ -475,7 +476,7 @@ public: virtual bool isValueType() const override { return true; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer ABIType() const override { return std::make_shared(8 * int(getStorageBytes())); } + virtual TypePointer externalType() const override { return std::make_shared(8 * int(getStorageBytes())); } EnumDefinition const& getEnumDefinition() const { return m_enum; } /// @returns the value that the string has in the Enum From a7eccfaa978b8f1e5ea7d3eb4d89b276bdf626d7 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Thu, 19 Mar 2015 17:33:10 +0100 Subject: [PATCH 062/228] added check for valid externalType to checkTypeRequirements for function --- libsolidity/AST.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index d05eaf83e..3ecf79eb6 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -305,8 +305,12 @@ TypePointer FunctionDefinition::getType(ContractDefinition const*) const void FunctionDefinition::checkTypeRequirements() { for (ASTPointer const& var: getParameters() + getReturnParameters()) + { if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); + if (!var->getType()->externalType() && getVisibility() >= Visibility::Internal) + BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to have an external address.")); + } for (ASTPointer const& modifier: m_functionModifiers) modifier->checkTypeRequirements(isConstructor() ? dynamic_cast(*getScope()).getBaseContracts() : @@ -653,6 +657,10 @@ void MemberAccess::checkTypeRequirements() if (!m_type) BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not " "visible in " + type.toString())); + //todo check for visibility +// else if (!m_type->externalType()) +// BOOST_THROW_EXCEPTION(createTypeError("Type is required to have an external address.")); + // This should probably move somewhere else. if (type.getCategory() == Type::Category::Struct) m_isLValue = true; From 1d15c09e5fa457d201968aa1a85b517113a7f689 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Fri, 20 Mar 2015 13:30:00 +0100 Subject: [PATCH 063/228] - added externalType to BooleanType. - fixed the error message --- libsolidity/AST.cpp | 6 +++--- libsolidity/Types.cpp | 2 ++ libsolidity/Types.h | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 3ecf79eb6..0827f7941 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -308,8 +308,8 @@ void FunctionDefinition::checkTypeRequirements() { if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); - if (!var->getType()->externalType() && getVisibility() >= Visibility::Internal) - BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to have an external address.")); + if (!var->getType()->externalType() && getVisibility() >= Visibility::Public) + BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for function with external visibility")); } for (ASTPointer const& modifier: m_functionModifiers) modifier->checkTypeRequirements(isConstructor() ? @@ -659,7 +659,7 @@ void MemberAccess::checkTypeRequirements() "visible in " + type.toString())); //todo check for visibility // else if (!m_type->externalType()) -// BOOST_THROW_EXCEPTION(createTypeError("Type is required to have an external address.")); +// BOOST_THROW_EXCEPTION(createTypeError("Internal type not allowed for function with external visibility")); // This should probably move somewhere else. if (type.getCategory() == Type::Category::Struct) diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 6fe8a6410..82c28a394 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -749,7 +749,9 @@ TypePointer ArrayType::externalType() const if (m_isByteArray) return shared_from_this(); if (!(m_baseType->externalType())) + { return TypePointer(); + } if (dynamic_cast(m_baseType.get()) && m_baseType->isDynamicallySized()) return TypePointer(); diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 013c65893..5941646aa 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -263,7 +263,6 @@ public: virtual std::string toString() const override; virtual u256 literalValue(Literal const* _literal) const override; virtual TypePointer getRealType() const override; - virtual TypePointer externalType() const override { return shared_from_this(); } /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. std::shared_ptr getIntegerType() const; @@ -298,6 +297,7 @@ public: virtual std::string toString() const override { return "bytes" + dev::toString(m_bytes); } virtual u256 literalValue(Literal const* _literal) const override; + virtual TypePointer externalType() const override { return shared_from_this(); } int getNumBytes() const { return m_bytes; } @@ -323,6 +323,7 @@ public: virtual std::string toString() const override { return "bool"; } virtual u256 literalValue(Literal const* _literal) const override; + virtual TypePointer externalType() const override { return shared_from_this(); } }; /** From 44c7da426293fef7a816c72e05ee07b5d287cdc6 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Fri, 20 Mar 2015 17:02:24 +0100 Subject: [PATCH 064/228] added check for events and stat variables --- libsolidity/AST.cpp | 13 +++++++++---- libsolidity/Types.cpp | 8 +++----- libsolidity/Types.h | 3 ++- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 0827f7941..63f3d772b 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -346,8 +346,11 @@ void VariableDeclaration::checkTypeRequirements() if (!m_value) return; if (m_type) + { m_value->expectType(*m_type); - else + if (m_isStateVariable && !m_type->externalType() && getVisibility() >= Visibility::Public) + BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for state variables.")); + } else { // no type declared and no previous assignment, infer the type m_value->checkTypeRequirements(); @@ -426,6 +429,8 @@ void EventDefinition::checkTypeRequirements() numIndexed++; if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); + if (!var->getType()->externalType() && getVisibility() >= Visibility::Public) + BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for Events")); } if (numIndexed > 3) BOOST_THROW_EXCEPTION(createTypeError("More than 3 indexed arguments for event.")); @@ -657,9 +662,9 @@ void MemberAccess::checkTypeRequirements() if (!m_type) BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not " "visible in " + type.toString())); - //todo check for visibility -// else if (!m_type->externalType()) -// BOOST_THROW_EXCEPTION(createTypeError("Internal type not allowed for function with external visibility")); +// if (auto f = dynamic_cast(m_expression->getType().get())) +// if (f->getLocation() == FunctionType::Location::External && !m_type->externalType()) +// BOOST_THROW_EXCEPTION(createTypeError(*m_memberName + " member has an internal type.")); // This should probably move somewhere else. if (type.getCategory() == Type::Category::Struct) diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 82c28a394..7b4d1de26 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -748,14 +748,12 @@ TypePointer ArrayType::externalType() const return TypePointer(); if (m_isByteArray) return shared_from_this(); - if (!(m_baseType->externalType())) - { + if (!m_baseType->externalType()) return TypePointer(); - } - if (dynamic_cast(m_baseType.get()) && m_baseType->isDynamicallySized()) + if (m_baseType->getCategory() == Category::Array && m_baseType->isDynamicallySized()) return TypePointer(); - if (m_baseType->isDynamicallySized()) + if (isDynamicallySized()) return std::make_shared(Location::CallData, m_baseType->externalType()); else return std::make_shared(Location::CallData, m_baseType->externalType(), m_length); diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 5941646aa..17264dc59 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -187,7 +187,8 @@ public: "for type without literals.")); } - /// Returns address of type for ABI interface + /// @returns a type suitable for outside of Solidity, i.e. for contract types it returns address. + /// If there is no such type, returns an empty shared pointer. virtual TypePointer externalType() const { return TypePointer(); } protected: From 0ca313ec85d8a094eeab175c20603175b0d1c26c Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Mon, 23 Mar 2015 18:08:45 +0100 Subject: [PATCH 065/228] renamed getCanonicalSignature added externalTypes instead of types for interface functions added simple test todo testing --- libsolidity/AST.cpp | 11 ++++---- libsolidity/AST.h | 2 +- libsolidity/ExpressionCompiler.cpp | 2 +- libsolidity/InterfaceHandler.cpp | 4 +-- libsolidity/Types.cpp | 8 +++--- libsolidity/Types.h | 4 +-- mix/QFunctionDefinition.cpp | 2 +- test/SolidityNameAndTypeResolution.cpp | 36 +++++++++++++++++++++++--- test/SolidityTypes.cpp | 1 + 9 files changed, 52 insertions(+), 18 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 63f3d772b..2e24d4f9d 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -88,7 +88,7 @@ void ContractDefinition::checkTypeRequirements() if (hashes.count(hash)) BOOST_THROW_EXCEPTION(createTypeError( std::string("Function signature hash collision for ") + - it.second->getCanonicalSignature())); + it.second->externalTypes())); hashes.insert(hash); } } @@ -192,7 +192,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn if (functionsSeen.count(f->getName()) == 0 && f->isPartOfExternalInterface()) { functionsSeen.insert(f->getName()); - FixedHash<4> hash(dev::sha3(f->getCanonicalSignature())); + FixedHash<4> hash(dev::sha3(f->externalTypes())); m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*f, false))); } @@ -200,8 +200,9 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn if (functionsSeen.count(v->getName()) == 0 && v->isPartOfExternalInterface()) { FunctionType ftype(*v); + solAssert(v->getType().get(), ""); functionsSeen.insert(v->getName()); - FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(v->getName()))); + FixedHash<4> hash(dev::sha3(ftype.externalTypes(v->getName()))); m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*v))); } } @@ -319,9 +320,9 @@ void FunctionDefinition::checkTypeRequirements() m_body->checkTypeRequirements(); } -string FunctionDefinition::getCanonicalSignature() const +string FunctionDefinition::externalTypes() const { - return FunctionType(*this).getCanonicalSignature(getName()); + return FunctionType(*this).externalTypes(getName()); } bool VariableDeclaration::isLValue() const diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 335b9a880..c5cd2e5b1 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -424,7 +424,7 @@ public: /// @returns the canonical signature of the function /// That consists of the name of the function followed by the types of the /// arguments separated by commas all enclosed in parentheses without any spaces. - std::string getCanonicalSignature() const; + std::string externalTypes() const; private: bool m_isConstructor; diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index da762147a..288af3983 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -544,7 +544,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } if (!event.isAnonymous()) { - m_context << u256(h256::Arith(dev::sha3(function.getCanonicalSignature(event.getName())))); + m_context << u256(h256::Arith(dev::sha3(function.externalTypes(event.getName())))); ++numIndexed; } solAssert(numIndexed <= 4, "Too many indexed arguments."); diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 406d1e24a..e09e55cbc 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -129,7 +129,7 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefi if (!m_notice.empty()) {// since @notice is the only user tag if missing function should not appear user["notice"] = Json::Value(m_notice); - methods[it.second->getCanonicalSignature()] = user; + methods[it.second->externalTypes()] = user; } } } @@ -185,7 +185,7 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin method["return"] = m_return; if (!method.empty()) // add the function, only if we have any documentation to add - methods[it.second->getCanonicalSignature()] = method; + methods[it.second->externalTypes()] = method; } } doc["methods"] = methods; diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 7b4d1de26..28a3af33a 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -1127,7 +1127,7 @@ MemberList const& FunctionType::getMembers() const } } -string FunctionType::getCanonicalSignature(std::string const& _name) const +string FunctionType::externalTypes(std::string const& _name) const { std::string funcName = _name; if (_name == "") @@ -1138,8 +1138,10 @@ string FunctionType::getCanonicalSignature(std::string const& _name) const string ret = funcName + "("; for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it) - ret += (*it)->toString() + (it + 1 == m_parameterTypes.cend() ? "" : ","); - + { + solAssert(!!(*it)->externalType(), "Parameter should have external type"); + ret += (*it)->externalType()->toString() + (it + 1 == m_parameterTypes.cend() ? "" : ","); + } return ret + ")"; } diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 17264dc59..599d80cc6 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -550,10 +550,10 @@ public: virtual MemberList const& getMembers() const override; Location const& getLocation() const { return m_location; } - /// @returns the canonical signature of this function type given the function name + /// @returns the external type of this function type given the function name /// If @a _name is not provided (empty string) then the @c m_declaration member of the /// function type is used - std::string getCanonicalSignature(std::string const& _name = "") const; + std::string externalTypes(std::string const& _name = "") const; Declaration const& getDeclaration() const { solAssert(m_declaration, "Requested declaration from a FunctionType that has none"); diff --git a/mix/QFunctionDefinition.cpp b/mix/QFunctionDefinition.cpp index fed4e8add..d8cc63dee 100644 --- a/mix/QFunctionDefinition.cpp +++ b/mix/QFunctionDefinition.cpp @@ -28,7 +28,7 @@ using namespace dev::solidity; using namespace dev::mix; -QFunctionDefinition::QFunctionDefinition(QObject* _parent, dev::solidity::FunctionTypePointer const& _f): QBasicNodeDefinition(_parent, &_f->getDeclaration()), m_hash(dev::sha3(_f->getCanonicalSignature())) +QFunctionDefinition::QFunctionDefinition(QObject* _parent, dev::solidity::FunctionTypePointer const& _f): QBasicNodeDefinition(_parent, &_f->getDeclaration()), m_hash(dev::sha3(_f->externalTypes())) { auto paramNames = _f->getParameterNames(); auto paramTypes = _f->getParameterTypes(); diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 591cf053a..4e8166963 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -359,7 +359,7 @@ BOOST_AUTO_TEST_CASE(function_canonical_signature) if (ContractDefinition* contract = dynamic_cast(node.get())) { auto functions = contract->getDefinedFunctions(); - BOOST_CHECK_EQUAL("foo(uint256,uint64,bool)", functions[0]->getCanonicalSignature()); + BOOST_CHECK_EQUAL("foo(uint256,uint64,bool)", functions[0]->externalTypes()); } } @@ -376,10 +376,41 @@ BOOST_AUTO_TEST_CASE(function_canonical_signature_type_aliases) if (ContractDefinition* contract = dynamic_cast(node.get())) { auto functions = contract->getDefinedFunctions(); - BOOST_CHECK_EQUAL("boo(uint256,bytes32,address)", functions[0]->getCanonicalSignature()); + BOOST_CHECK_EQUAL("boo(uint256,bytes32,address)", functions[0]->externalTypes()); } } +BOOST_AUTO_TEST_CASE(function_external_types) +{ + ASTPointer sourceUnit; + char const* text = R"( + contract Test { + function boo(uint arg2, bool arg3, bytes8 arg4) returns (uint ret) { + ret = 5; + } + })"; + ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed"); + for (ASTPointer const& node: sourceUnit->getNodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + { + auto functions = contract->getDefinedFunctions(); + BOOST_CHECK_EQUAL("boo(uint256,bool,bytes8)", functions[0]->externalTypes()); + } +} + +//todo should check arrays and contract. also event +//BOOST_AUTO_TEST_CASE(function_external_types_throw) +//{ +// ASTPointer sourceUnit; +// char const* text = R"( +// contract Test { +// function boo(uint32[] arg5) returns (uint ret) { +// ret = 5; +// } +// })"; +// BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +//} + BOOST_AUTO_TEST_CASE(hash_collision_in_interface) { char const* text = "contract test {\n" @@ -635,7 +666,6 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) "mapping(uint=>bytes4) public map;\n" "mapping(uint=>mapping(uint=>bytes4)) public multiple_map;\n" "}\n"; - ASTPointer source; ContractDefinition const* contract; ETH_TEST_CHECK_NO_THROW(source = parseTextAndResolveNames(text), "Parsing and Resolving names failed"); diff --git a/test/SolidityTypes.cpp b/test/SolidityTypes.cpp index 6b6306479..4133ce7b7 100644 --- a/test/SolidityTypes.cpp +++ b/test/SolidityTypes.cpp @@ -86,6 +86,7 @@ BOOST_AUTO_TEST_CASE(storage_layout_arrays) BOOST_CHECK(ArrayType(ArrayType::Location::Storage, make_shared(32), 9).getStorageSize() == 9); } + BOOST_AUTO_TEST_SUITE_END() } From ba8d0f615c2fee7b08f83cac07b4bacc23f6fd5d Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Tue, 24 Mar 2015 11:11:27 +0100 Subject: [PATCH 066/228] renamed externalTypes to externalSignature --- libsolidity/AST.cpp | 12 ++++++------ libsolidity/AST.h | 6 +++--- libsolidity/ExpressionCompiler.cpp | 2 +- libsolidity/InterfaceHandler.cpp | 4 ++-- libsolidity/Types.cpp | 2 +- libsolidity/Types.h | 4 ++-- mix/QFunctionDefinition.cpp | 2 +- test/SolidityNameAndTypeResolution.cpp | 27 ++++++++++++++++++++++---- test/SolidityTypes.cpp | 1 - 9 files changed, 39 insertions(+), 21 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 2e24d4f9d..9a2c1be07 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -88,7 +88,7 @@ void ContractDefinition::checkTypeRequirements() if (hashes.count(hash)) BOOST_THROW_EXCEPTION(createTypeError( std::string("Function signature hash collision for ") + - it.second->externalTypes())); + it.second->externalSignature())); hashes.insert(hash); } } @@ -192,7 +192,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn if (functionsSeen.count(f->getName()) == 0 && f->isPartOfExternalInterface()) { functionsSeen.insert(f->getName()); - FixedHash<4> hash(dev::sha3(f->externalTypes())); + FixedHash<4> hash(dev::sha3(f->externalSignature())); m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*f, false))); } @@ -202,7 +202,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn FunctionType ftype(*v); solAssert(v->getType().get(), ""); functionsSeen.insert(v->getName()); - FixedHash<4> hash(dev::sha3(ftype.externalTypes(v->getName()))); + FixedHash<4> hash(dev::sha3(ftype.externalSignature(v->getName()))); m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*v))); } } @@ -320,9 +320,9 @@ void FunctionDefinition::checkTypeRequirements() m_body->checkTypeRequirements(); } -string FunctionDefinition::externalTypes() const +string FunctionDefinition::externalSignature() const { - return FunctionType(*this).externalTypes(getName()); + return FunctionType(*this).externalSignature(getName()); } bool VariableDeclaration::isLValue() const @@ -430,7 +430,7 @@ void EventDefinition::checkTypeRequirements() numIndexed++; if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); - if (!var->getType()->externalType() && getVisibility() >= Visibility::Public) + if (!var->getType()->externalType()) BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for Events")); } if (numIndexed > 3) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index c5cd2e5b1..937c2ceab 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -421,10 +421,10 @@ public: /// Checks that all parameters have allowed types and calls checkTypeRequirements on the body. void checkTypeRequirements(); - /// @returns the canonical signature of the function - /// That consists of the name of the function followed by the types of the + /// @returns the external signature of the function + /// That consists of the name of the function followed by the external types of the /// arguments separated by commas all enclosed in parentheses without any spaces. - std::string externalTypes() const; + std::string externalSignature() const; private: bool m_isConstructor; diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 288af3983..90568767b 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -544,7 +544,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } if (!event.isAnonymous()) { - m_context << u256(h256::Arith(dev::sha3(function.externalTypes(event.getName())))); + m_context << u256(h256::Arith(dev::sha3(function.externalSignature(event.getName())))); ++numIndexed; } solAssert(numIndexed <= 4, "Too many indexed arguments."); diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index e09e55cbc..2f35a96f6 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -129,7 +129,7 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefi if (!m_notice.empty()) {// since @notice is the only user tag if missing function should not appear user["notice"] = Json::Value(m_notice); - methods[it.second->externalTypes()] = user; + methods[it.second->externalSignature()] = user; } } } @@ -185,7 +185,7 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin method["return"] = m_return; if (!method.empty()) // add the function, only if we have any documentation to add - methods[it.second->externalTypes()] = method; + methods[it.second->externalSignature()] = method; } } doc["methods"] = methods; diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 28a3af33a..6344d128d 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -1127,7 +1127,7 @@ MemberList const& FunctionType::getMembers() const } } -string FunctionType::externalTypes(std::string const& _name) const +string FunctionType::externalSignature(std::string const& _name) const { std::string funcName = _name; if (_name == "") diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 599d80cc6..e00e6b983 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -550,10 +550,10 @@ public: virtual MemberList const& getMembers() const override; Location const& getLocation() const { return m_location; } - /// @returns the external type of this function type given the function name + /// @returns the external signature of this function type given the function name /// If @a _name is not provided (empty string) then the @c m_declaration member of the /// function type is used - std::string externalTypes(std::string const& _name = "") const; + std::string externalSignature(std::string const& _name = "") const; Declaration const& getDeclaration() const { solAssert(m_declaration, "Requested declaration from a FunctionType that has none"); diff --git a/mix/QFunctionDefinition.cpp b/mix/QFunctionDefinition.cpp index d8cc63dee..13dbd4821 100644 --- a/mix/QFunctionDefinition.cpp +++ b/mix/QFunctionDefinition.cpp @@ -28,7 +28,7 @@ using namespace dev::solidity; using namespace dev::mix; -QFunctionDefinition::QFunctionDefinition(QObject* _parent, dev::solidity::FunctionTypePointer const& _f): QBasicNodeDefinition(_parent, &_f->getDeclaration()), m_hash(dev::sha3(_f->externalTypes())) +QFunctionDefinition::QFunctionDefinition(QObject* _parent, dev::solidity::FunctionTypePointer const& _f): QBasicNodeDefinition(_parent, &_f->getDeclaration()), m_hash(dev::sha3(_f->externalSignature())) { auto paramNames = _f->getParameterNames(); auto paramTypes = _f->getParameterTypes(); diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 4e8166963..a6d1920a1 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -359,7 +359,7 @@ BOOST_AUTO_TEST_CASE(function_canonical_signature) if (ContractDefinition* contract = dynamic_cast(node.get())) { auto functions = contract->getDefinedFunctions(); - BOOST_CHECK_EQUAL("foo(uint256,uint64,bool)", functions[0]->externalTypes()); + BOOST_CHECK_EQUAL("foo(uint256,uint64,bool)", functions[0]->externalSignature()); } } @@ -376,7 +376,7 @@ BOOST_AUTO_TEST_CASE(function_canonical_signature_type_aliases) if (ContractDefinition* contract = dynamic_cast(node.get())) { auto functions = contract->getDefinedFunctions(); - BOOST_CHECK_EQUAL("boo(uint256,bytes32,address)", functions[0]->externalTypes()); + BOOST_CHECK_EQUAL("boo(uint256,bytes32,address)", functions[0]->externalSignature()); } } @@ -385,7 +385,7 @@ BOOST_AUTO_TEST_CASE(function_external_types) ASTPointer sourceUnit; char const* text = R"( contract Test { - function boo(uint arg2, bool arg3, bytes8 arg4) returns (uint ret) { + function boo(uint arg2, bool arg3, bytes8 arg4, bool[2] pairs) public returns (uint ret) { ret = 5; } })"; @@ -394,10 +394,28 @@ BOOST_AUTO_TEST_CASE(function_external_types) if (ContractDefinition* contract = dynamic_cast(node.get())) { auto functions = contract->getDefinedFunctions(); - BOOST_CHECK_EQUAL("boo(uint256,bool,bytes8)", functions[0]->externalTypes()); + BOOST_CHECK_EQUAL("boo(uint256,bool,bytes8)", functions[0]->externalSignature()); } } +//BOOST_AUTO_TEST_CASE(function_external_types_throw) +//{ +// ASTPointer sourceUnit; +// char const* text = R"( +// contract ArrayContract { +// bool[2][] m_pairsOfFlags; +// function setAllFlagPairs(bool[2][] newPairs) { +// // assignment to array replaces the complete array +// m_pairsOfFlags = newPairs; +// } +// })"; +// ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed"); +// for (ASTPointer const& node: sourceUnit->getNodes()) +// if (ContractDefinition* contract = dynamic_cast(node.get())) +// { +// auto functions = contract->getDefinedFunctions(); +// BOOST_CHECK_EQUAL("boo(uint256,bool,bytes8)", functions[0]->externalSigniture()); +// } //todo should check arrays and contract. also event //BOOST_AUTO_TEST_CASE(function_external_types_throw) //{ @@ -666,6 +684,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) "mapping(uint=>bytes4) public map;\n" "mapping(uint=>mapping(uint=>bytes4)) public multiple_map;\n" "}\n"; + ASTPointer source; ContractDefinition const* contract; ETH_TEST_CHECK_NO_THROW(source = parseTextAndResolveNames(text), "Parsing and Resolving names failed"); diff --git a/test/SolidityTypes.cpp b/test/SolidityTypes.cpp index 4133ce7b7..6b6306479 100644 --- a/test/SolidityTypes.cpp +++ b/test/SolidityTypes.cpp @@ -86,7 +86,6 @@ BOOST_AUTO_TEST_CASE(storage_layout_arrays) BOOST_CHECK(ArrayType(ArrayType::Location::Storage, make_shared(32), 9).getStorageSize() == 9); } - BOOST_AUTO_TEST_SUITE_END() } From f3e8d2b7e9bf54f1f0f6a7895924ecdeddd4cd9d Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Wed, 25 Mar 2015 13:58:15 +0100 Subject: [PATCH 067/228] tests for external types --- libsolidity/AST.cpp | 14 ++--- libsolidity/AST.h | 4 +- libsolidity/ExpressionCompiler.cpp | 2 +- libsolidity/Types.cpp | 7 ++- libsolidity/Types.h | 3 +- test/SolidityNameAndTypeResolution.cpp | 83 ++++++++++++++++---------- 6 files changed, 65 insertions(+), 48 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 9a2c1be07..9e81c2cb2 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -192,7 +192,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn if (functionsSeen.count(f->getName()) == 0 && f->isPartOfExternalInterface()) { functionsSeen.insert(f->getName()); - FixedHash<4> hash(dev::sha3(f->externalSignature())); + FixedHash<4> hash(dev::sha3(f->externalSignature(true))); m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*f, false))); } @@ -202,7 +202,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn FunctionType ftype(*v); solAssert(v->getType().get(), ""); functionsSeen.insert(v->getName()); - FixedHash<4> hash(dev::sha3(ftype.externalSignature(v->getName()))); + FixedHash<4> hash(dev::sha3(ftype.externalSignature(true, v->getName()))); m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*v))); } } @@ -309,7 +309,7 @@ void FunctionDefinition::checkTypeRequirements() { if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); - if (!var->getType()->externalType() && getVisibility() >= Visibility::Public) + if (!(var->getType()->externalType()) && getVisibility() >= Visibility::Public) BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for function with external visibility")); } for (ASTPointer const& modifier: m_functionModifiers) @@ -320,9 +320,9 @@ void FunctionDefinition::checkTypeRequirements() m_body->checkTypeRequirements(); } -string FunctionDefinition::externalSignature() const +string FunctionDefinition::externalSignature(bool isExternalCall) const { - return FunctionType(*this).externalSignature(getName()); + return FunctionType(*this).externalSignature(isExternalCall, getName()); } bool VariableDeclaration::isLValue() const @@ -663,10 +663,6 @@ void MemberAccess::checkTypeRequirements() if (!m_type) BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not " "visible in " + type.toString())); -// if (auto f = dynamic_cast(m_expression->getType().get())) -// if (f->getLocation() == FunctionType::Location::External && !m_type->externalType()) -// BOOST_THROW_EXCEPTION(createTypeError(*m_memberName + " member has an internal type.")); - // This should probably move somewhere else. if (type.getCategory() == Type::Category::Struct) m_isLValue = true; diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 937c2ceab..0d3ef857a 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -422,9 +422,9 @@ public: void checkTypeRequirements(); /// @returns the external signature of the function - /// That consists of the name of the function followed by the external types of the + /// That consists of the name of the function followed by the types(external types if isExternalCall) of the /// arguments separated by commas all enclosed in parentheses without any spaces. - std::string externalSignature() const; + std::string externalSignature(bool isExternalCall = false) const; private: bool m_isConstructor; diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 90568767b..ddc347e68 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -544,7 +544,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } if (!event.isAnonymous()) { - m_context << u256(h256::Arith(dev::sha3(function.externalSignature(event.getName())))); + m_context << u256(h256::Arith(dev::sha3(function.externalSignature(false, event.getName())))); ++numIndexed; } solAssert(numIndexed <= 4, "Too many indexed arguments."); diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 6344d128d..1776413a0 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -1127,7 +1127,7 @@ MemberList const& FunctionType::getMembers() const } } -string FunctionType::externalSignature(std::string const& _name) const +string FunctionType::externalSignature(bool isExternalCall, std::string const& _name) const { std::string funcName = _name; if (_name == "") @@ -1139,8 +1139,9 @@ string FunctionType::externalSignature(std::string const& _name) const for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it) { - solAssert(!!(*it)->externalType(), "Parameter should have external type"); - ret += (*it)->externalType()->toString() + (it + 1 == m_parameterTypes.cend() ? "" : ","); + if (isExternalCall) + solAssert(!!(*it)->externalType(), "Parameter should have external type"); + ret += (isExternalCall ? (*it)->externalType()->toString() : (*it)->toString()) + (it + 1 == m_parameterTypes.cend() ? "" : ","); } return ret + ")"; } diff --git a/libsolidity/Types.h b/libsolidity/Types.h index e00e6b983..c075ff07f 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -553,7 +553,8 @@ public: /// @returns the external signature of this function type given the function name /// If @a _name is not provided (empty string) then the @c m_declaration member of the /// function type is used - std::string externalSignature(std::string const& _name = "") const; + /// @a isExternalCall shows if it is external function call + std::string externalSignature(bool isExternalCall = false, std::string const& _name = "") const; Declaration const& getDeclaration() const { solAssert(m_declaration, "Requested declaration from a FunctionType that has none"); diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index a6d1920a1..75a912244 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "TestHelper.h" using namespace std; @@ -48,16 +49,28 @@ ASTPointer parseTextAndResolveNames(std::string const& _source) ASTPointer sourceUnit = parser.parse(std::make_shared(CharStream(_source))); NameAndTypeResolver resolver({}); resolver.registerDeclarations(*sourceUnit); + std::shared_ptr globalContext = make_shared(); + for (ASTPointer const& node: sourceUnit->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) + { + globalContext->setCurrentContract(*contract); + resolver.updateDeclaration(*globalContext->getCurrentThis()); + resolver.updateDeclaration(*globalContext->getCurrentSuper()); resolver.resolveNamesAndTypes(*contract); + } for (ASTPointer const& node: sourceUnit->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) + { + globalContext->setCurrentContract(*contract); + resolver.updateDeclaration(*globalContext->getCurrentThis()); resolver.checkTypeRequirements(*contract); + } return sourceUnit; } + static ContractDefinition const* retrieveContract(ASTPointer _source, unsigned index) { ContractDefinition* contract; @@ -376,6 +389,8 @@ BOOST_AUTO_TEST_CASE(function_canonical_signature_type_aliases) if (ContractDefinition* contract = dynamic_cast(node.get())) { auto functions = contract->getDefinedFunctions(); + if (functions.empty()) + continue; BOOST_CHECK_EQUAL("boo(uint256,bytes32,address)", functions[0]->externalSignature()); } } @@ -384,8 +399,11 @@ BOOST_AUTO_TEST_CASE(function_external_types) { ASTPointer sourceUnit; char const* text = R"( + contract C { + uint a; + } contract Test { - function boo(uint arg2, bool arg3, bytes8 arg4, bool[2] pairs) public returns (uint ret) { + function boo(uint arg2, bool arg3, bytes8 arg4, bool[2] pairs, uint[] dynamic, C carg) external returns (uint ret) { ret = 5; } })"; @@ -394,40 +412,41 @@ BOOST_AUTO_TEST_CASE(function_external_types) if (ContractDefinition* contract = dynamic_cast(node.get())) { auto functions = contract->getDefinedFunctions(); - BOOST_CHECK_EQUAL("boo(uint256,bool,bytes8)", functions[0]->externalSignature()); + if (functions.empty()) + continue; + BOOST_CHECK_EQUAL("boo(uint256,bool,bytes8,bool[2],uint256[],address)", functions[0]->externalSignature(true)); } } -//BOOST_AUTO_TEST_CASE(function_external_types_throw) -//{ -// ASTPointer sourceUnit; -// char const* text = R"( -// contract ArrayContract { -// bool[2][] m_pairsOfFlags; -// function setAllFlagPairs(bool[2][] newPairs) { -// // assignment to array replaces the complete array -// m_pairsOfFlags = newPairs; -// } -// })"; -// ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed"); -// for (ASTPointer const& node: sourceUnit->getNodes()) -// if (ContractDefinition* contract = dynamic_cast(node.get())) -// { -// auto functions = contract->getDefinedFunctions(); -// BOOST_CHECK_EQUAL("boo(uint256,bool,bytes8)", functions[0]->externalSigniture()); -// } -//todo should check arrays and contract. also event -//BOOST_AUTO_TEST_CASE(function_external_types_throw) -//{ -// ASTPointer sourceUnit; -// char const* text = R"( -// contract Test { -// function boo(uint32[] arg5) returns (uint ret) { -// ret = 5; -// } -// })"; -// BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); -//} +BOOST_AUTO_TEST_CASE(function_external_call_conversion) +{ + char const* sourceCode = R"( + contract C {} + contract Test { + function externalCall() { + address arg; + this.g(arg); + } + function g (C c) external {} + })"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(function_internal_call_conversion) +{ + char const* text = R"( + contract C { + uint a; + } + contract Test { + address a; + function g (C c) {} + function internalCall() { + g(a); + } + })"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} BOOST_AUTO_TEST_CASE(hash_collision_in_interface) { From 14115d96d57b8d469151f0ed60aa6fe5188d8bba Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 14:19:09 +0100 Subject: [PATCH 068/228] brief docs for libtestutils classes --- libtestutils/BlockChainLoader.h | 3 ++- libtestutils/FixedClient.h | 4 ++++ libtestutils/FixedWebThreeServer.h | 6 ++++++ libtestutils/ShortLivingDirectory.h | 5 +++++ libtestutils/StateLoader.h | 3 +++ 5 files changed, 20 insertions(+), 1 deletion(-) diff --git a/libtestutils/BlockChainLoader.h b/libtestutils/BlockChainLoader.h index a3528045c..ed110ff6f 100644 --- a/libtestutils/BlockChainLoader.h +++ b/libtestutils/BlockChainLoader.h @@ -32,7 +32,8 @@ namespace test { /** - * @brief - loads the blockchain from json, creates temporary directory to store it, removes this temporary directory on dealloc + * @brief Should be used to load test blockchain from json file + * Loads the blockchain from json, creates temporary directory to store it, removes the directory on dealloc */ class BlockChainLoader { diff --git a/libtestutils/FixedClient.h b/libtestutils/FixedClient.h index ce628c651..f0a7c54f6 100644 --- a/libtestutils/FixedClient.h +++ b/libtestutils/FixedClient.h @@ -30,6 +30,10 @@ namespace dev namespace test { +/** + * @brief mvp implementation of ClientBase + * Doesn't support mining interface + */ class FixedClient: public dev::eth::ClientBase { public: diff --git a/libtestutils/FixedWebThreeServer.h b/libtestutils/FixedWebThreeServer.h index 53db2f2b2..33ccbf71e 100644 --- a/libtestutils/FixedWebThreeServer.h +++ b/libtestutils/FixedWebThreeServer.h @@ -24,6 +24,12 @@ #include #include +/** + * @brief dummy JSON-RPC api implementation + * Should be used for test purposes only + * Supports eth && db interfaces + * Doesn't support shh && net interfaces + */ class FixedWebThreeServer: public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace { public: diff --git a/libtestutils/ShortLivingDirectory.h b/libtestutils/ShortLivingDirectory.h index 434120e04..bab6a1c20 100644 --- a/libtestutils/ShortLivingDirectory.h +++ b/libtestutils/ShortLivingDirectory.h @@ -29,6 +29,11 @@ namespace dev namespace test { +/** + * @brief temporary directory implementation + * It creates temporary directory in the given path. On dealloc it removes the directory + * @throws if the given path already exists, throws an exception + */ class ShortLivingDirectory { public: diff --git a/libtestutils/StateLoader.h b/libtestutils/StateLoader.h index 10d7251cc..e8f955440 100644 --- a/libtestutils/StateLoader.h +++ b/libtestutils/StateLoader.h @@ -29,6 +29,9 @@ namespace dev namespace test { +/** + * @brief Friend of State, loads State from given JSON object + */ class StateLoader { public: From 742002b05ade24c7e5399b3f6d9e36e090385681 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 20 Mar 2015 17:50:26 +0100 Subject: [PATCH 069/228] Parsing of not fully implemented functions - Adding the possibility of omitting a function body by simply ending a function definition with a semicolon - Such a function is marked as not fully implemented and any contract that contains such a function is considered a not fully implemented contract --- libsolidity/AST.cpp | 4 +- libsolidity/AST.h | 85 +++++++++++++++++--------- libsolidity/AST_accept.h | 6 +- libsolidity/Parser.cpp | 33 ++++++++-- test/SolidityNameAndTypeResolution.cpp | 15 +++++ test/SolidityParser.cpp | 8 +++ 6 files changed, 112 insertions(+), 39 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index d05eaf83e..71af3286f 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -311,8 +311,8 @@ void FunctionDefinition::checkTypeRequirements() modifier->checkTypeRequirements(isConstructor() ? dynamic_cast(*getScope()).getBaseContracts() : vector>()); - - m_body->checkTypeRequirements(); + if (m_body) + m_body->checkTypeRequirements(); } string FunctionDefinition::getCanonicalSignature() const diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 335b9a880..fa0172b10 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -196,6 +196,21 @@ protected: ASTPointer m_documentation; }; +/** + * Abstract class that is added to AST nodes that can be marked as not being fully implemented + */ +class ImplementationOptional +{ +public: + explicit ImplementationOptional(bool _implemented): m_implemented(_implemented) {} + + /// @return whether this node is fully implemented or not + bool isFullyImplemented() const { return m_implemented; } + +protected: + bool m_implemented; +}; + /// @} /** @@ -203,20 +218,25 @@ protected: * document order. It first visits all struct declarations, then all variable declarations and * finally all function declarations. */ -class ContractDefinition: public Declaration, public Documented +class ContractDefinition: public Declaration, public Documented, public ImplementationOptional { public: - ContractDefinition(SourceLocation const& _location, - ASTPointer const& _name, - ASTPointer const& _documentation, - std::vector> const& _baseContracts, - std::vector> const& _definedStructs, - std::vector> const& _definedEnums, - std::vector> const& _stateVariables, - std::vector> const& _definedFunctions, - std::vector> const& _functionModifiers, - std::vector> const& _events): - Declaration(_location, _name), Documented(_documentation), + ContractDefinition( + SourceLocation const& _location, + ASTPointer const& _name, + ASTPointer const& _documentation, + std::vector> const& _baseContracts, + std::vector> const& _definedStructs, + std::vector> const& _definedEnums, + std::vector> const& _stateVariables, + std::vector> const& _definedFunctions, + std::vector> const& _functionModifiers, + std::vector> const& _events, + bool _isFullyImplemented + ): + Declaration(_location, _name), + Documented(_documentation), + ImplementationOptional(_isFullyImplemented), m_baseContracts(_baseContracts), m_definedStructs(_definedStructs), m_definedEnums(_definedEnums), @@ -224,7 +244,7 @@ public: m_definedFunctions(_definedFunctions), m_functionModifiers(_functionModifiers), m_events(_events) - {} + {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; @@ -378,25 +398,30 @@ private: std::vector> m_parameters; }; -class FunctionDefinition: public Declaration, public VariableScope, public Documented +class FunctionDefinition: public Declaration, public VariableScope, public Documented, public ImplementationOptional { public: - FunctionDefinition(SourceLocation const& _location, ASTPointer const& _name, - Declaration::Visibility _visibility, bool _isConstructor, - ASTPointer const& _documentation, - ASTPointer const& _parameters, - bool _isDeclaredConst, - std::vector> const& _modifiers, - ASTPointer const& _returnParameters, - ASTPointer const& _body): - Declaration(_location, _name, _visibility), Documented(_documentation), - m_isConstructor(_isConstructor), - m_parameters(_parameters), - m_isDeclaredConst(_isDeclaredConst), - m_functionModifiers(_modifiers), - m_returnParameters(_returnParameters), - m_body(_body) - {} + FunctionDefinition( + SourceLocation const& _location, + ASTPointer const& _name, + Declaration::Visibility _visibility, bool _isConstructor, + ASTPointer const& _documentation, + ASTPointer const& _parameters, + bool _isDeclaredConst, + std::vector> const& _modifiers, + ASTPointer const& _returnParameters, + ASTPointer const& _body + ): + Declaration(_location, _name, _visibility), + Documented(_documentation), + ImplementationOptional(_body != nullptr), + m_isConstructor(_isConstructor), + m_parameters(_parameters), + m_isDeclaredConst(_isDeclaredConst), + m_functionModifiers(_modifiers), + m_returnParameters(_returnParameters), + m_body(_body) + {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; diff --git a/libsolidity/AST_accept.h b/libsolidity/AST_accept.h index 81ede8fc9..3557f8779 100644 --- a/libsolidity/AST_accept.h +++ b/libsolidity/AST_accept.h @@ -175,7 +175,8 @@ void FunctionDefinition::accept(ASTVisitor& _visitor) if (m_returnParameters) m_returnParameters->accept(_visitor); listAccept(m_functionModifiers, _visitor); - m_body->accept(_visitor); + if (m_body) + m_body->accept(_visitor); } _visitor.endVisit(*this); } @@ -188,7 +189,8 @@ void FunctionDefinition::accept(ASTConstVisitor& _visitor) const if (m_returnParameters) m_returnParameters->accept(_visitor); listAccept(m_functionModifiers, _visitor); - m_body->accept(_visitor); + if (m_body) + m_body->accept(_visitor); } _visitor.endVisit(*this); } diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 393d2734e..3dfedf6d2 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -116,6 +116,7 @@ ASTPointer Parser::parseContractDefinition() { ASTNodeFactory nodeFactory(*this); ASTPointer docString; + bool contractFullyImplemented = true; if (m_scanner->getCurrentCommentLiteral() != "") docString = make_shared(m_scanner->getCurrentCommentLiteral()); expectToken(Token::Contract); @@ -141,7 +142,12 @@ ASTPointer Parser::parseContractDefinition() if (currentToken == Token::RBrace) break; else if (currentToken == Token::Function) - functions.push_back(parseFunctionDefinition(name.get())); + { + ASTPointer func = parseFunctionDefinition(name.get()); + functions.push_back(func); + if (!func->isFullyImplemented()) + contractFullyImplemented = false; + } else if (currentToken == Token::Struct) structs.push_back(parseStructDefinition()); else if (currentToken == Token::Enum) @@ -164,8 +170,18 @@ ASTPointer Parser::parseContractDefinition() } nodeFactory.markEndPosition(); expectToken(Token::RBrace); - return nodeFactory.createNode(name, docString, baseContracts, structs, enums, - stateVariables, functions, modifiers, events); + return nodeFactory.createNode( + name, + docString, + baseContracts, + structs, + enums, + stateVariables, + functions, + modifiers, + events, + contractFullyImplemented + ); } ASTPointer Parser::parseInheritanceSpecifier() @@ -247,8 +263,15 @@ ASTPointer Parser::parseFunctionDefinition(ASTString const* } else returnParameters = createEmptyParameterList(); - ASTPointer block = parseBlock(); - nodeFactory.setEndPositionFromNode(block); + ASTPointer block = ASTPointer(); + nodeFactory.markEndPosition(); + if (m_scanner->getCurrentToken() != Token::Semicolon) + { + block = parseBlock(); + nodeFactory.setEndPositionFromNode(block); + } + else + m_scanner->next(); // just consume the ';' bool const c_isConstructor = (_contractName && *name == *_contractName); return nodeFactory.createNode(name, visibility, c_isConstructor, docstring, parameters, isDeclaredConst, modifiers, diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 591cf053a..3c088358c 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -346,6 +346,21 @@ BOOST_AUTO_TEST_CASE(comparison_bitop_precedence) ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); } +BOOST_AUTO_TEST_CASE(function_no_implementation) +{ + ASTPointer sourceUnit; + char const* text = "contract test {\n" + " function functionName(bytes32 input) returns (bytes32 out);\n" + "}\n"; + ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed"); + ContractDefinition* contract; + for (ASTPointer const& node: sourceUnit->getNodes()) + contract = dynamic_cast(node.get()); + BOOST_CHECK(contract); + BOOST_CHECK(!contract->isFullyImplemented()); + BOOST_CHECK(!contract->getDefinedFunctions()[0]->isFullyImplemented()); +} + BOOST_AUTO_TEST_CASE(function_canonical_signature) { ASTPointer sourceUnit; diff --git a/test/SolidityParser.cpp b/test/SolidityParser.cpp index 392d9ac47..7640f91ad 100644 --- a/test/SolidityParser.cpp +++ b/test/SolidityParser.cpp @@ -108,6 +108,14 @@ BOOST_AUTO_TEST_CASE(single_function_param) ETH_TEST_CHECK_NO_THROW(parseText(text), "Parsing failed."); } +BOOST_AUTO_TEST_CASE(function_no_body) +{ + char const* text = "contract test {\n" + " function functionName(bytes32 input) returns (bytes32 out);\n" + "}\n"; + ETH_TEST_CHECK_NO_THROW(parseText(text), "Parsing failed."); +} + BOOST_AUTO_TEST_CASE(missing_parameter_name_in_named_args) { char const* text = "contract test {\n" From 42e6a291444e2b3e0b1075302ef3f65d0fcba6d3 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 25 Mar 2015 14:45:50 +0100 Subject: [PATCH 070/228] More towards an implementation. --- abi/main.cpp | 75 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 9 deletions(-) diff --git a/abi/main.cpp b/abi/main.cpp index 7bb00e390..34187acc7 100644 --- a/abi/main.cpp +++ b/abi/main.cpp @@ -39,8 +39,10 @@ void help() << " abi dec -a [ | ]" << endl << "Options:" << endl << " -a,--abi-file Specify the JSON ABI file." << endl + << "Input options (enc mode):" << endl + << " -p,--prefix Require all arguments to be prefixed 0x (hex), . (decimal), # (binary)." << endl << "Output options (dec mode):" << endl - << " -i,--index When decoding, output only the nth (counting from 0) return value." << endl + << " -i,--index Output only the nth (counting from 0) return value." << endl << " -d,--decimal All data should be displayed as decimal." << endl << " -x,--hex Display all data as hex." << endl << " -b,--binary Display all data as binary." << endl @@ -72,16 +74,57 @@ enum class Encoding { Binary, }; +struct InvalidUserString: public Exception {}; + +pair fromUser(std::string const& _arg, bool _requirePrefix) +{ + if (_requirePrefix) + { + if (_arg.substr(0, 2) == "0x") + return make_pair(fromHex(_arg), false); + if (_arg.substr(0, 1) == ".") + return make_pair(toCompactBigEndian(bigint(_arg.substr(1))), false); + if (_arg.substr(0, 1) == "#") + return make_pair(asBytes(_arg.substr(1)), true); + throw InvalidUserString(); + } + else + { + if (_arg.substr(0, 2) == "0x") + return make_pair(fromHex(_arg), false); + if (_arg.find_first_not_of("0123456789")) + return make_pair(toCompactBigEndian(bigint(_arg)), false); + return make_pair(asBytes(_arg), true); + } +} + +bytes aligned(bytes const& _b, bool _left, unsigned _length) +{ + bytes ret = _b; + while (ret.size() < _length) + if (_left) + ret.push_back(0); + else + ret.insert(ret.begin(), 0); + while (ret.size() > _length) + if (_left) + ret.pop_back(); + else + ret.erase(ret.begin()); + return ret; +} + int main(int argc, char** argv) { Encoding encoding = Encoding::Auto; Mode mode = Mode::Encode; string abiFile; - string methodName; - bool outputPrefix = false; + string method; + bool prefix = false; bool clearZeroes = false; bool clearNulls = false; int outputIndex = -1; + vector> args; for (int i = 1; i < argc; ++i) { @@ -97,7 +140,7 @@ int main(int argc, char** argv) else if ((arg == "-i" || arg == "--index") && argc > i) outputIndex = atoi(argv[++i]); else if (arg == "-p" || arg == "--prefix") - outputPrefix = true; + prefix = true; else if (arg == "-z" || arg == "--no-zeroes") clearZeroes = true; else if (arg == "-n" || arg == "--no-nulls") @@ -110,8 +153,10 @@ int main(int argc, char** argv) encoding = Encoding::Binary; else if (arg == "-V" || arg == "--version") version(); + else if (method.empty()) + method = arg; else - methodName = arg; + args.push_back(fromUser(arg, prefix)); } string abi; @@ -122,16 +167,28 @@ int main(int argc, char** argv) abi = contentsString(abiFile); if (mode == Mode::Encode) + { + if (abi.empty()) + { + bytes ret; + if (!method.empty()) + ret = FixedHash<32>(sha3(method)).asBytes(); + if (method.empty()) + for (pair const& arg: args) + ret += aligned(arg.first, arg.second, 32); + } + else + { + // TODO: read abi. + } + } + else if (mode == Mode::Decode) { (void)encoding; - (void)outputPrefix; (void)clearZeroes; (void)clearNulls; (void)outputIndex; } - else if (mode == Mode::Decode) - { - } return 0; } From 5b18f853b7d645734884545ecf849030d1ad12d6 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 25 Mar 2015 15:28:23 +0100 Subject: [PATCH 071/228] - bug fix: #1399 --- mix/MixClient.cpp | 11 ++++++----- mix/MixClient.h | 2 +- mix/qml/html/WebContainer.html | 6 +++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index fd7b2263d..0272cc129 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -89,6 +89,7 @@ void MixClient::resetState(std::map _accounts) SecureTrieDB accountState(&m_stateDB); accountState.init(); + m_userAccounts.clear(); std::map genesisState; for (auto account: _accounts) { @@ -243,15 +244,15 @@ ExecutionResult MixClient::execution(unsigned _index) const return m_executions.at(_index); } -State MixClient::asOf(int _block) const +State MixClient::asOf(BlockNumber _h) const { ReadGuard l(x_state); - if (_block == 0) - return m_state; - else if (_block == -1) + if (_h == PendingBlock) return m_startState; + else if (_h == LatestBlock) + return m_state; else - return State(m_stateDB, bc(), bc().numberHash(_block)); + return State(m_stateDB, bc(), bc().numberHash(_h)); } void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) diff --git a/mix/MixClient.h b/mix/MixClient.h index e1085a20e..4c45e3162 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -100,7 +100,7 @@ public: private: void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call); void noteChanged(h256Set const& _filters); - dev::eth::State asOf(int _block) const; + dev::eth::State asOf(eth::BlockNumber _block) const; MixBlockChain& bc() { return *m_bc; } MixBlockChain const& bc() const { return *m_bc; } diff --git a/mix/qml/html/WebContainer.html b/mix/qml/html/WebContainer.html index fb716507f..dfce64979 100644 --- a/mix/qml/html/WebContainer.html +++ b/mix/qml/html/WebContainer.html @@ -23,9 +23,9 @@ updateContracts = function(contracts) { var contractProto = window.web3.eth.contract(contracts[c].interface); var contract = new contractProto(contracts[c].address); window.contracts[c] = { - address: c.address, - interface: c.interface, - contract: contract, + address: contracts[c].address, + interface: contracts[c].interface, + contract: contract }; } } From c226dc2451ef4f4571b4fbabde35c237758b6861 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 16:05:50 +0100 Subject: [PATCH 072/228] fixed bracers --- libtestutils/ShortLivingDirectory.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libtestutils/ShortLivingDirectory.cpp b/libtestutils/ShortLivingDirectory.cpp index 48e1d643d..f60e12a86 100644 --- a/libtestutils/ShortLivingDirectory.cpp +++ b/libtestutils/ShortLivingDirectory.cpp @@ -30,9 +30,8 @@ using namespace dev::test; ShortLivingDirectory::ShortLivingDirectory(std::string const& _path) : m_path(_path) { // we never ever want to delete a directory (including all its contents) that we did not create ourselves. - if (boost::filesystem::exists(m_path)) { + if (boost::filesystem::exists(m_path)) BOOST_THROW_EXCEPTION(FileError()); - } boost::filesystem::create_directories(m_path); } From 1cb510dbd0d71af3ba0e2f8d24cb7757092d2c2c Mon Sep 17 00:00:00 2001 From: subtly Date: Wed, 25 Mar 2015 17:01:05 +0100 Subject: [PATCH 073/228] Fallback to 0.0.0.0 if bind fails. Bump handshake timeout until we switch back to secp256k1. --- libp2p/RLPxHandshake.h | 2 +- libp2p/UDP.h | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index 5e51aa4f8..47f6afb57 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -95,7 +95,7 @@ protected: void transition(boost::system::error_code _ech = boost::system::error_code()); /// Timeout for remote to respond to transition events. Enforced by m_idleTimer and refreshed by transition(). - boost::posix_time::milliseconds const c_timeout = boost::posix_time::milliseconds(1000); + boost::posix_time::milliseconds const c_timeout = boost::posix_time::milliseconds(1800); State m_nextState = New; ///< Current or expected state of transition. bool m_cancel = false; ///< Will be set to true if connection was canceled. diff --git a/libp2p/UDP.h b/libp2p/UDP.h index 338100c78..68160d053 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -163,7 +163,14 @@ void UDPSocket::connect() return; m_socket.open(bi::udp::v4()); - m_socket.bind(m_endpoint); + try + { + m_socket.bind(m_endpoint); + } + catch (...) + { + m_socket.bind(bi::udp::endpoint(bi::udp::v4(), m_endpoint.port())); + } // clear write queue so reconnect doesn't send stale messages Guard l(x_sendQ); From bc5a238159df27c31bbcbeb8f1d736c9e8813ced Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 18:18:54 +0100 Subject: [PATCH 074/228] jsonrpc fixes --- libweb3jsonrpc/WebThreeStubServerBase.cpp | 24 +++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 9e4ffe174..7fe043291 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -50,7 +50,7 @@ using namespace dev::eth; static Json::Value toJson(dev::eth::BlockInfo const& _bi) { Json::Value res; - res["hash"] = boost::lexical_cast(_bi.hash); + res["hash"] = toJS(_bi.hash); res["parentHash"] = toJS(_bi.parentHash); res["sha3Uncles"] = toJS(_bi.sha3Uncles); res["miner"] = toJS(_bi.coinbaseAddress); @@ -58,10 +58,12 @@ static Json::Value toJson(dev::eth::BlockInfo const& _bi) res["transactionsRoot"] = toJS(_bi.transactionsRoot); res["difficulty"] = toJS(_bi.difficulty); res["number"] = toJS(_bi.number); + res["gasUsed"] = toJS(_bi.gasUsed); res["gasLimit"] = toJS(_bi.gasLimit); res["timestamp"] = toJS(_bi.timestamp); res["extraData"] = toJS(_bi.extraData); res["nonce"] = toJS(_bi.nonce); + res["logsBloom"] = toJS(_bi.logBloom); return res; } @@ -79,18 +81,24 @@ static Json::Value toJson(dev::eth::Transaction const& _t) return res; } -static Json::Value toJson(dev::eth::BlockInfo const& _bi, Transactions const& _ts) +static Json::Value toJson(dev::eth::BlockInfo const& _bi, UncleHashes const& _us, Transactions const& _ts) { Json::Value res = toJson(_bi); + res["uncles"] = Json::Value(Json::arrayValue); + for (h256 h: _us) + res["uncles"].append(toJS(h)); res["transactions"] = Json::Value(Json::arrayValue); for (Transaction const& t: _ts) res["transactions"].append(toJson(t)); return res; } -static Json::Value toJson(dev::eth::BlockInfo const& _bi, TransactionHashes const& _ts) +static Json::Value toJson(dev::eth::BlockInfo const& _bi, UncleHashes const& _us, TransactionHashes const& _ts) { Json::Value res = toJson(_bi); + res["uncles"] = Json::Value(Json::arrayValue); + for (h256 h: _us) + res["uncles"].append(toJS(h)); res["transactions"] = Json::Value(Json::arrayValue); for (h256 const& t: _ts) res["transactions"].append(toJS(t)); @@ -387,7 +395,7 @@ string WebThreeStubServerBase::eth_getCode(string const& _address, string const& { try { - return toJS(client()->codeAt(jsToAddress(_address), toBlockNumber(_blockNumber))); + return toJS(client()->codeAt(jsToAddress(_address), toBlockNumber(_blockNumber)), 1); } catch (...) { @@ -498,9 +506,9 @@ Json::Value WebThreeStubServerBase::eth_getBlockByHash(string const& _blockHash, { auto h = jsToFixed<32>(_blockHash); if (_includeTransactions) - return toJson(client()->blockInfo(h), client()->transactions(h)); + return toJson(client()->blockInfo(h), client()->uncleHashes(h), client()->transactions(h)); else - return toJson(client()->blockInfo(h), client()->transactionHashes(h)); + return toJson(client()->blockInfo(h), client()->uncleHashes(h), client()->transactionHashes(h)); } catch (...) { @@ -514,9 +522,9 @@ Json::Value WebThreeStubServerBase::eth_getBlockByNumber(string const& _blockNum { auto h = client()->hashFromNumber(jsToInt(_blockNumber)); if (_includeTransactions) - return toJson(client()->blockInfo(h), client()->transactions(h)); + return toJson(client()->blockInfo(h), client()->uncleHashes(h), client()->transactions(h)); else - return toJson(client()->blockInfo(h), client()->transactionHashes(h)); + return toJson(client()->blockInfo(h), client()->uncleHashes(h), client()->transactionHashes(h)); } catch (...) { From d71bec193f7991d70e96fa2b4819652546bae21b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 25 Mar 2015 18:33:20 +0100 Subject: [PATCH 075/228] Clean up some code redundancy. Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop --- alethzero/MainWin.cpp | 6 +++++- cmake/EthCompilerSettings.cmake | 3 ++- eth/main.cpp | 12 ++++++++++-- libethereum/Transaction.cpp | 4 ++++ libsolidity/ExpressionCompiler.h | 1 - libsolidity/Token.h | 13 ++++++++++--- neth/main.cpp | 12 ++++++++++-- test/transaction.cpp | 7 +++++++ test/ttTransactionTestFiller.json | 15 +++++++++++++++ 9 files changed, 63 insertions(+), 10 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 4b9af1666..d0cfafd7d 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -169,7 +169,11 @@ Main::Main(QWidget *parent) : bytesConstRef network((byte*)m_networkConfig.data(), m_networkConfig.size()); m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), false, {"eth", "shh"}, p2p::NetworkPreferences(), network)); - m_httpConnector.reset(new jsonrpc::HttpServer(8080)); +#if ETH_DEBUG + m_httpConnector.reset(new jsonrpc::HttpServer(8080, "", "", 1)); +#else + m_httpConnector.reset(new jsonrpc::HttpServer(8080, "", "", 4)); +#endif m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), keysAsVector(m_myKeys), this)); connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString))); m_server->setIdentities(keysAsVector(owned())); diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index a0d5a50c1..7a5c94e6b 100755 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -43,7 +43,8 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221") # warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification # warning LNK4099: pdb was not found with lib - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099,4075") + # stack size 16MB + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099,4075 /STACK:16777216") # windows likes static set(ETH_STATIC 1) diff --git a/eth/main.cpp b/eth/main.cpp index eda6e12ae..e89a1250f 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -457,7 +457,11 @@ int main(int argc, char** argv) unique_ptr jsonrpcConnector; if (jsonrpc > -1) { - jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc)); +#if ETH_DEBUG + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 1)); +#else + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 4)); +#endif jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({us}))); jsonrpcServer->setIdentities({us}); jsonrpcServer->StartListening(); @@ -583,7 +587,11 @@ int main(int argc, char** argv) { if (jsonrpc < 0) jsonrpc = 8080; - jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc)); +#if ETH_DEBUG + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 1)); +#else + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 4)); +#endif jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({us}))); jsonrpcServer->setIdentities({us}); jsonrpcServer->StartListening(); diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index 31a7f74a9..502f089fb 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -68,6 +68,10 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig) m_type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall; m_receiveAddress = rlp[field = 3].isEmpty() ? Address() : rlp[field = 3].toHash
(RLP::VeryStrict); m_value = rlp[field = 4].toInt(); + + if (!rlp[field = 5].isData()) + BOOST_THROW_EXCEPTION(BadRLP() << errinfo_comment("transaction data RLP must be an array")); + m_data = rlp[field = 5].toBytes(); byte v = rlp[field = 6].toInt() - 27; h256 r = rlp[field = 7].toInt(); diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index e6caad747..2577d21b5 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -42,7 +42,6 @@ class CompilerContext; class Type; class IntegerType; class ArrayType; -class StaticStringType; /** * Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream diff --git a/libsolidity/Token.h b/libsolidity/Token.h index b2951e939..1435dcc57 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -143,7 +143,6 @@ namespace solidity \ /* Keywords */ \ K(Break, "break", 0) \ - K(Case, "case", 0) \ K(Const, "constant", 0) \ K(Anonymous, "anonymous", 0) \ K(Continue, "continue", 0) \ @@ -168,7 +167,6 @@ namespace solidity K(Return, "return", 0) \ K(Returns, "returns", 0) \ K(Struct, "struct", 0) \ - K(Switch, "switch", 0) \ K(Var, "var", 0) \ K(While, "while", 0) \ K(Enum, "enum", 0) \ @@ -290,7 +288,6 @@ namespace solidity K(Byte, "byte", 0) \ K(Address, "address", 0) \ K(Bool, "bool", 0) \ - K(StringType, "string", 0) \ K(Real, "real", 0) \ K(UReal, "ureal", 0) \ T(TypesEnd, NULL, 0) /* used as type enum end marker */ \ @@ -306,6 +303,16 @@ namespace solidity /* Identifiers (not keywords or future reserved words). */ \ T(Identifier, NULL, 0) \ \ + /* Keywords reserved for future. use*/ \ + T(String, "string", 0) \ + K(Case, "case", 0) \ + K(Switch, "switch", 0) \ + K(Throw, "throw", 0) \ + K(Try, "try", 0) \ + K(Catch, "catch", 0) \ + K(Using, "using", 0) \ + K(Type, "type", 0) \ + K(TypeOf, "typeof", 0) \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ diff --git a/neth/main.cpp b/neth/main.cpp index 4888f784d..fc4c8c9c6 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -474,7 +474,11 @@ int main(int argc, char** argv) unique_ptr jsonrpcConnector; if (jsonrpc > -1) { - jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc)); +#if ETH_DEBUG + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 1)); +#else + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 4)); +#endif jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({us}))); jsonrpcServer->setIdentities({us}); jsonrpcServer->StartListening(); @@ -625,7 +629,11 @@ int main(int argc, char** argv) { if (jsonrpc < 0) jsonrpc = 8080; - jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc)); +#if ETH_DEBUG + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 1)); +#else + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 4)); +#endif jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({us}))); jsonrpcServer->setIdentities({us}); jsonrpcServer->StartListening(); diff --git a/test/transaction.cpp b/test/transaction.cpp index 77f6ecdaf..4c57326ba 100644 --- a/test/transaction.cpp +++ b/test/transaction.cpp @@ -48,6 +48,13 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin) if (!txFromRlp.signature().isValid()) BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") ); } + catch(Exception const& _e) + { + cnote << i.first; + cnote << "Transaction Exception: " << diagnostic_information(_e); + BOOST_CHECK_MESSAGE(o.count("transaction") == 0, "A transaction object should not be defined because the RLP is invalid!"); + continue; + } catch(...) { BOOST_CHECK_MESSAGE(o.count("transaction") == 0, "A transaction object should not be defined because the RLP is invalid!"); diff --git a/test/ttTransactionTestFiller.json b/test/ttTransactionTestFiller.json index 0058feac1..c04b76848 100644 --- a/test/ttTransactionTestFiller.json +++ b/test/ttTransactionTestFiller.json @@ -571,6 +571,7 @@ "s" : "0x8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3" } }, + "unpadedRValue": { "transaction": { "nonce": "13", @@ -583,5 +584,19 @@ "v": "28", "value": "" } + }, + + "libsecp256k1test": { + "transaction": { + "nonce": "", + "gasPrice": "0x09184e72a000", + "gasLimit": "0x1388", + "to": "", + "data": "", + "r": "44", + "s": "4", + "v": "27", + "value": "" + } } } From 60da83fb0acf3b3a77a3aaa099c4e4793eec9233 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 25 Mar 2015 18:51:08 +0100 Subject: [PATCH 076/228] Record and detail gas refund. --- alethzero/MainWin.cpp | 6 +----- alethzero/Transact.cpp | 4 ++-- eth/main.cpp | 18 +++++------------- libethereum/Executive.cpp | 5 +++++ libethereum/Executive.h | 2 +- libethereum/Transaction.h | 3 ++- libweb3jsonrpc/WebThreeStubServerBase.cpp | 7 +++++++ libweb3jsonrpc/WebThreeStubServerBase.h | 3 +++ 8 files changed, 26 insertions(+), 22 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index d0cfafd7d..0ee2961a1 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -169,11 +169,7 @@ Main::Main(QWidget *parent) : bytesConstRef network((byte*)m_networkConfig.data(), m_networkConfig.size()); m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), false, {"eth", "shh"}, p2p::NetworkPreferences(), network)); -#if ETH_DEBUG - m_httpConnector.reset(new jsonrpc::HttpServer(8080, "", "", 1)); -#else - m_httpConnector.reset(new jsonrpc::HttpServer(8080, "", "", 4)); -#endif + m_httpConnector.reset(new jsonrpc::HttpServer(SensibleHttpPort, "", "", dev::SensibleHttpThreads)); m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), keysAsVector(m_myKeys), this)); connect(&*m_server, SIGNAL(onNewId(QString)), SLOT(addNewId(QString))); m_server->setIdentities(keysAsVector(owned())); diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index 7248ee4a6..29eafff03 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -328,8 +328,8 @@ void Transact::rejigData() to = m_context->fromString(ui->destination->currentText()); er = ethereum()->call(s, value(), to, m_data, ethereum()->gasLimitRemaining(), gasPrice()); } - gasNeeded = (qint64)er.gasUsed; - htmlInfo = QString("
INFO Gas required: %1 total = %2 base, %3 exec
").arg(gasNeeded).arg(baseGas).arg(gasNeeded - baseGas) + htmlInfo; + gasNeeded = (qint64)(er.gasUsed + er.gasRefunded); + htmlInfo = QString("
INFO Gas required: %1 total = %2 base, %3 exec [%4 refunded later]
").arg(gasNeeded).arg(baseGas).arg(gasNeeded - baseGas).arg(er.gasRefunded) + htmlInfo; if (er.excepted != TransactionException::None) { diff --git a/eth/main.cpp b/eth/main.cpp index e89a1250f..03c54a5b1 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -117,7 +117,7 @@ void help() << " -i,--interactive Enter interactive mode (default: non-interactive)." << endl #if ETH_JSONRPC << " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl - << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: 8080)." << endl + << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl #endif << " -K,--kill-blockchain First kill the blockchain." << endl << " -l,--listen Listen on the given port for incoming connected (default: 30303)." << endl @@ -370,7 +370,7 @@ int main(int argc, char** argv) interactive = true; #if ETH_JSONRPC else if ((arg == "-j" || arg == "--json-rpc")) - jsonrpc = jsonrpc == -1 ? 8080 : jsonrpc; + jsonrpc = jsonrpc == -1 ? SensibleHttpPort : jsonrpc; else if (arg == "--json-rpc-port" && i + 1 < argc) jsonrpc = atoi(argv[++i]); #endif @@ -457,11 +457,7 @@ int main(int argc, char** argv) unique_ptr jsonrpcConnector; if (jsonrpc > -1) { -#if ETH_DEBUG - jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 1)); -#else - jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 4)); -#endif + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({us}))); jsonrpcServer->setIdentities({us}); jsonrpcServer->StartListening(); @@ -586,12 +582,8 @@ int main(int argc, char** argv) else if (cmd == "jsonstart") { if (jsonrpc < 0) - jsonrpc = 8080; -#if ETH_DEBUG - jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 1)); -#else - jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 4)); -#endif + jsonrpc = SensibleHttpPort; + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({us}))); jsonrpcServer->setIdentities({us}); jsonrpcServer->StartListening(); diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 597506821..c574fa650 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -44,6 +44,11 @@ u256 Executive::gasUsed() const return m_t.gas() - m_endGas; } +ExecutionResult Executive::executionResult() const +{ + return ExecutionResult(gasUsed(), m_excepted, m_newAddress, m_out, m_codeDeposit, m_ext ? m_ext->sub.refunds : 0); +} + void Executive::accrueSubState(SubState& _parentContext) { if (m_ext) diff --git a/libethereum/Executive.h b/libethereum/Executive.h index d04a39da8..eb0c27ad2 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -104,7 +104,7 @@ public: bool excepted() const { return m_excepted != TransactionException::None; } /// Get the above in an amalgamated fashion. - ExecutionResult executionResult() const { return ExecutionResult(gasUsed(), m_excepted, m_newAddress, m_out, m_codeDeposit); } + ExecutionResult executionResult() const; private: bool setup(); diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index 1287be64e..bed291868 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -75,12 +75,13 @@ TransactionException toTransactionException(VMException const& _e); struct ExecutionResult { ExecutionResult() = default; - ExecutionResult(u256 _gasUsed, TransactionException _excepted, Address _newAddress, bytesConstRef _output, CodeDeposit _codeDeposit): gasUsed(_gasUsed), excepted(_excepted), newAddress(_newAddress), output(_output.toBytes()), codeDeposit(_codeDeposit) {} + ExecutionResult(u256 _gasUsed, TransactionException _excepted, Address _newAddress, bytesConstRef _output, CodeDeposit _codeDeposit, u256 _gasRefund): gasUsed(_gasUsed), excepted(_excepted), newAddress(_newAddress), output(_output.toBytes()), codeDeposit(_codeDeposit), gasRefunded(_gasRefund) {} u256 gasUsed = 0; TransactionException excepted = TransactionException::Unknown; Address newAddress; bytes output; CodeDeposit codeDeposit = CodeDeposit::None; + u256 gasRefunded = 0; }; std::ostream& operator<<(std::ostream& _out, ExecutionResult const& _er); diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 9e4ffe174..806df6b30 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -47,6 +47,13 @@ using namespace jsonrpc; using namespace dev; using namespace dev::eth; +#if ETH_DEBUG +const unsigned dev::SensibleHttpThreads = 1; +#else +const unsigned dev::SensibleHttpThreads = 4; +#endif +const unsigned dev::SensibleHttpPort = 8080; + static Json::Value toJson(dev::eth::BlockInfo const& _bi) { Json::Value res; diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index 214573e0d..40265ac10 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -48,6 +48,9 @@ namespace shh class Interface; } +extern const unsigned SensibleHttpThreads; +extern const unsigned SensibleHttpPort; + class WebThreeStubDatabaseFace { public: From 7f915a1949067846be46a373a73eadea836371a3 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 25 Mar 2015 18:52:21 +0100 Subject: [PATCH 077/228] Minor compile fix. --- alethzero/Transact.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index 29eafff03..534f18a69 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -329,7 +329,7 @@ void Transact::rejigData() er = ethereum()->call(s, value(), to, m_data, ethereum()->gasLimitRemaining(), gasPrice()); } gasNeeded = (qint64)(er.gasUsed + er.gasRefunded); - htmlInfo = QString("
INFO Gas required: %1 total = %2 base, %3 exec [%4 refunded later]
").arg(gasNeeded).arg(baseGas).arg(gasNeeded - baseGas).arg(er.gasRefunded) + htmlInfo; + htmlInfo = QString("
INFO Gas required: %1 total = %2 base, %3 exec [%4 refunded later]
").arg(gasNeeded).arg(baseGas).arg(gasNeeded - baseGas).arg((qint64)er.gasRefunded) + htmlInfo; if (er.excepted != TransactionException::None) { From fc5b6de4952d541518029af15a987133af392082 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 25 Mar 2015 19:03:57 +0100 Subject: [PATCH 078/228] Remove asOf redundancy. Minor mix typo fix. --- libethereum/Client.cpp | 21 +++++---------------- libethereum/Client.h | 4 ++-- libethereum/ClientBase.cpp | 23 ++++++++++++++++------- libethereum/ClientBase.h | 19 +++++++++++-------- mix/MixClient.cpp | 23 ++++++----------------- mix/MixClient.h | 6 +++--- 6 files changed, 43 insertions(+), 53 deletions(-) diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index f1ef6df71..5af2bebd3 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -246,7 +246,7 @@ void Client::clearPending() void Client::noteChanged(h256Set const& _filters) { - Guard l(m_filterLock); + Guard l(x_filtersWatches); if (_filters.size()) cnote << "noteChanged(" << _filters << ")"; // accrue all changes left in each filter into the watches. @@ -266,7 +266,7 @@ void Client::noteChanged(h256Set const& _filters) void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Set& io_changed, h256 _transactionHash) { - Guard l(m_filterLock); + Guard l(x_filtersWatches); for (pair& i: m_filters) if (i.second.filter.envelops(RelativeBlock::Pending, m_bc.number() + 1)) { @@ -288,7 +288,7 @@ void Client::appendFromNewBlock(h256 const& _block, h256Set& io_changed) auto d = m_bc.info(_block); auto br = m_bc.receipts(_block); - Guard l(m_filterLock); + Guard l(x_filtersWatches); for (pair& i: m_filters) if (i.second.filter.envelops(RelativeBlock::Latest, d.number) && i.second.filter.matches(d.logBloom)) // acceptable number & looks like block may contain a matching log entry. @@ -535,7 +535,7 @@ void Client::doWork() // watches garbage collection vector toUninstall; { - Guard l(m_filterLock); + Guard l(x_filtersWatches); for (auto key: keysOf(m_watches)) if (m_watches[key].lastPoll != chrono::system_clock::time_point::max() && chrono::system_clock::now() - m_watches[key].lastPoll > chrono::seconds(20)) { @@ -553,18 +553,7 @@ void Client::doWork() } } -State Client::asOf(BlockNumber _h) const -{ - ReadGuard l(x_stateDB); - if (_h == PendingBlock) - return m_postMine; - else if (_h == LatestBlock) - return m_preMine; - - return State(m_stateDB, bc(), bc().numberHash(_h)); -} - -State Client::asOf(h256 _block) const +State Client::asOf(h256 const& _block) const { ReadGuard l(x_stateDB); return State(m_stateDB, bc(), _block); diff --git a/libethereum/Client.h b/libethereum/Client.h index c49181a13..dad0ca664 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -254,8 +254,8 @@ protected: /// Returns the state object for the full block (i.e. the terminal state) for index _h. /// Works properly with LatestBlock and PendingBlock. - virtual State asOf(BlockNumber _h) const override; - virtual State asOf(h256 _block) const override; + using ClientBase::asOf; + virtual State asOf(h256 const& _block) const override; virtual State preMine() const override { ReadGuard l(x_stateDB); return m_preMine; } virtual State postMine() const override { ReadGuard l(x_stateDB); return m_postMine; } virtual void prepareForTransaction() override; diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index 03e74de58..f9caf98ac 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -29,6 +29,15 @@ using namespace std; using namespace dev; using namespace dev::eth; +State ClientBase::asOf(BlockNumber _h) const +{ + if (_h == PendingBlock) + return postMine(); + else if (_h == LatestBlock) + return preMine(); + return asOf(bc().numberHash(_h)); +} + void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) { prepareForTransaction(); @@ -123,7 +132,7 @@ LocalisedLogEntries ClientBase::logs(unsigned _watchId) const LogFilter f; try { - Guard l(m_filterLock); + Guard l(x_filtersWatches); f = m_filters.at(m_watches.at(_watchId).id).filter; } catch (...) @@ -196,7 +205,7 @@ unsigned ClientBase::installWatch(LogFilter const& _f, Reaping _r) { h256 h = _f.sha3(); { - Guard l(m_filterLock); + Guard l(x_filtersWatches); if (!m_filters.count(h)) { cwatch << "FFF" << _f << h.abridged(); @@ -210,7 +219,7 @@ unsigned ClientBase::installWatch(h256 _h, Reaping _r) { unsigned ret; { - Guard l(m_filterLock); + Guard l(x_filtersWatches); ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0; m_watches[ret] = ClientWatch(_h, _r); cwatch << "+++" << ret << _h.abridged(); @@ -219,7 +228,7 @@ unsigned ClientBase::installWatch(h256 _h, Reaping _r) if (ch.empty()) ch.push_back(InitialChange); { - Guard l(m_filterLock); + Guard l(x_filtersWatches); swap(m_watches[ret].changes, ch); } return ret; @@ -229,7 +238,7 @@ bool ClientBase::uninstallWatch(unsigned _i) { cwatch << "XXX" << _i; - Guard l(m_filterLock); + Guard l(x_filtersWatches); auto it = m_watches.find(_i); if (it == m_watches.end()) @@ -249,7 +258,7 @@ bool ClientBase::uninstallWatch(unsigned _i) LocalisedLogEntries ClientBase::peekWatch(unsigned _watchId) const { - Guard l(m_filterLock); + Guard l(x_filtersWatches); cwatch << "peekWatch" << _watchId; auto& w = m_watches.at(_watchId); @@ -260,7 +269,7 @@ LocalisedLogEntries ClientBase::peekWatch(unsigned _watchId) const LocalisedLogEntries ClientBase::checkWatch(unsigned _watchId) { - Guard l(m_filterLock); + Guard l(x_filtersWatches); LocalisedLogEntries ret; cwatch << "checkWatch" << _watchId; diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index 662f170ba..9d9482277 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -126,7 +126,7 @@ public: virtual Addresses addresses(BlockNumber _block) const override; virtual u256 gasLimitRemaining() const override; - // Set the coinbase address + /// Set the coinbase address virtual void setAddress(Address _us) override; /// Get the coinbase address @@ -143,21 +143,24 @@ public: virtual std::pair getWork() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::getWork")); } virtual bool submitWork(eth::ProofOfWork::Proof const&) override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::submitWork")); } -protected: + State asOf(BlockNumber _h) const; +protected: + /// The interface that must be implemented in any class deriving this. + /// { virtual BlockChain const& bc() const = 0; - virtual State asOf(BlockNumber _h) const = 0; - virtual State asOf(h256 _h) const = 0; + virtual State asOf(h256 const& _h) const = 0; virtual State preMine() const = 0; virtual State postMine() const = 0; virtual void prepareForTransaction() = 0; + /// } - TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. + TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. // filters - mutable Mutex m_filterLock; - std::map m_filters; - std::map m_watches; + mutable Mutex x_filtersWatches; ///< Our lock. + std::map m_filters; ///< The dictionary of filters that are active. + std::map m_watches; ///< Each and every watch - these reference a filter. }; diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index b6a9a45b1..0a2464b65 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -58,7 +58,7 @@ bytes MixBlockChain::createGenesisBlock(h256 _stateRoot) } MixClient::MixClient(std::string const& _dbPath): - m_dbPath(_dbPath), m_minigThreads(0) + m_dbPath(_dbPath), m_miningThreads(0) { std::map account; account.insert(std::make_pair(c_defaultUserAccountSecret, 1000000 * ether)); @@ -72,7 +72,7 @@ MixClient::~MixClient() void MixClient::resetState(std::map _accounts) { WriteGuard l(x_state); - Guard fl(m_filterLock); + Guard fl(x_filtersWatches); m_filters.clear(); m_watches.clear(); @@ -188,7 +188,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment")); // collect watches h256Set changed; - Guard l(m_filterLock); + Guard l(x_filtersWatches); for (std::pair& i: m_filters) if ((unsigned)i.second.filter.latest() > bc().number()) { @@ -234,18 +234,7 @@ ExecutionResult MixClient::execution(unsigned _index) const return m_executions.at(_index); } -State MixClient::asOf(BlockNumber _block) const -{ - ReadGuard l(x_state); - if (_block == PendingBlock) - return m_state; - else if (_block == LatestBlock) - return m_startState; - - return State(m_stateDB, bc(), bc().numberHash(_block)); -} - -State MixClient::asOf(h256 _block) const +State MixClient::asOf(h256 const& _block) const { ReadGuard l(x_state); return State(m_stateDB, bc(), _block); @@ -325,12 +314,12 @@ void MixClient::setAddress(Address _us) void MixClient::setMiningThreads(unsigned _threads) { - m_minigThreads = _threads; + m_miningThreads = _threads; } unsigned MixClient::miningThreads() const { - return m_minigThreads; + return m_miningThreads; } void MixClient::startMining() diff --git a/mix/MixClient.h b/mix/MixClient.h index 1a81f10cc..179b445ac 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -78,8 +78,8 @@ protected: virtual dev::eth::BlockChain& bc() { return *m_bc; } /// InterfaceStub methods - virtual dev::eth::State asOf(eth::BlockNumber _block) const override; - virtual dev::eth::State asOf(h256 _block) const override; + virtual dev::eth::State asOf(h256 const& _block) const override; + using ClientBase::asOf; virtual dev::eth::BlockChain const& bc() const override { return *m_bc; } virtual dev::eth::State preMine() const override { ReadGuard l(x_state); return m_startState; } virtual dev::eth::State postMine() const override { ReadGuard l(x_state); return m_state; } @@ -98,7 +98,7 @@ private: mutable boost::shared_mutex x_executions; ExecutionResults m_executions; std::string m_dbPath; - unsigned m_minigThreads; + unsigned m_miningThreads; }; } From e9b968b1b0b2d3a04ab8de80cf2a893dc0ab9123 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 19:30:52 +0100 Subject: [PATCH 079/228] fixed FixedClient --- libtestutils/FixedClient.cpp | 11 +---------- libtestutils/FixedClient.h | 4 ++-- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/libtestutils/FixedClient.cpp b/libtestutils/FixedClient.cpp index a58d4ae0e..052141039 100644 --- a/libtestutils/FixedClient.cpp +++ b/libtestutils/FixedClient.cpp @@ -25,16 +25,7 @@ using namespace dev; using namespace dev::eth; using namespace dev::test; -eth::State FixedClient::asOf(BlockNumber _h) const -{ - ReadGuard l(x_stateDB); - if (_h == PendingBlock || _h == LatestBlock) - return m_state; - - return State(m_state.db(), bc(), bc().numberHash(_h)); -} - -eth::State FixedClient::asOf(h256 _h) const +eth::State FixedClient::asOf(h256 const& _h) const { ReadGuard l(x_stateDB); return State(m_state.db(), bc(), _h); diff --git a/libtestutils/FixedClient.h b/libtestutils/FixedClient.h index f0a7c54f6..daca444fb 100644 --- a/libtestutils/FixedClient.h +++ b/libtestutils/FixedClient.h @@ -43,8 +43,8 @@ public: // stub virtual void flushTransactions() override {} virtual eth::BlockChain const& bc() const override { return m_bc; } - virtual eth::State asOf(eth::BlockNumber _h) const override; - virtual eth::State asOf(h256 _h) const override; + using ClientBase::asOf; + virtual eth::State asOf(h256 const& _h) const override; virtual eth::State preMine() const override { ReadGuard l(x_stateDB); return m_state; } virtual eth::State postMine() const override { ReadGuard l(x_stateDB); return m_state; } virtual void prepareForTransaction() override {} From 701df0bf55629740d3522eecd54e8b33cc6a6c4d Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 19:36:07 +0100 Subject: [PATCH 080/228] const --- libtestutils/BlockChainLoader.cpp | 1 - libtestutils/BlockChainLoader.h | 6 +++--- libtestutils/ShortLivingDirectory.h | 2 +- libtestutils/StateLoader.h | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/libtestutils/BlockChainLoader.cpp b/libtestutils/BlockChainLoader.cpp index 224e40ec5..eafab190e 100644 --- a/libtestutils/BlockChainLoader.cpp +++ b/libtestutils/BlockChainLoader.cpp @@ -34,7 +34,6 @@ namespace test { dev::eth::BlockInfo toBlockInfo(Json::Value const& _json); bytes toGenesisBlock(Json::Value const& _json); - } } diff --git a/libtestutils/BlockChainLoader.h b/libtestutils/BlockChainLoader.h index ed110ff6f..728860727 100644 --- a/libtestutils/BlockChainLoader.h +++ b/libtestutils/BlockChainLoader.h @@ -39,9 +39,9 @@ class BlockChainLoader { public: BlockChainLoader(Json::Value const& _json); - eth::BlockChain& bc() { return *m_bc; } - eth::State state() { return m_state; } - + eth::BlockChain const& bc() const { return *m_bc; } + eth::State const& state() const { return m_state; } + private: ShortLivingDirectory m_dir; std::auto_ptr m_bc; diff --git a/libtestutils/ShortLivingDirectory.h b/libtestutils/ShortLivingDirectory.h index bab6a1c20..10b542f06 100644 --- a/libtestutils/ShortLivingDirectory.h +++ b/libtestutils/ShortLivingDirectory.h @@ -40,7 +40,7 @@ public: ShortLivingDirectory(std::string const& _path = getRandomPath()); ~ShortLivingDirectory(); - std::string const& path() { return m_path; } + std::string const& path() const { return m_path; } private: std::string m_path; diff --git a/libtestutils/StateLoader.h b/libtestutils/StateLoader.h index e8f955440..e5843d0b4 100644 --- a/libtestutils/StateLoader.h +++ b/libtestutils/StateLoader.h @@ -36,7 +36,7 @@ class StateLoader { public: StateLoader(Json::Value const& _json); - eth::State const& state() { return m_state; } + eth::State const& state() const { return m_state; } private: eth::State m_state; From c12aa4decde863898119ac77c42e984c6c012c47 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 19:41:17 +0100 Subject: [PATCH 081/228] TransientDirectory --- libtestutils/BlockChainLoader.h | 4 ++-- .../{ShortLivingDirectory.cpp => TransientDirectory.cpp} | 8 ++++---- .../{ShortLivingDirectory.h => TransientDirectory.h} | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) rename libtestutils/{ShortLivingDirectory.cpp => TransientDirectory.cpp} (85%) rename libtestutils/{ShortLivingDirectory.h => TransientDirectory.h} (88%) diff --git a/libtestutils/BlockChainLoader.h b/libtestutils/BlockChainLoader.h index 728860727..6cb04c53c 100644 --- a/libtestutils/BlockChainLoader.h +++ b/libtestutils/BlockChainLoader.h @@ -24,7 +24,7 @@ #include #include #include -#include "ShortLivingDirectory.h" +#include "TransientDirectory.h" namespace dev { @@ -43,7 +43,7 @@ public: eth::State const& state() const { return m_state; } private: - ShortLivingDirectory m_dir; + TransientDirectory m_dir; std::auto_ptr m_bc; eth::State m_state; }; diff --git a/libtestutils/ShortLivingDirectory.cpp b/libtestutils/TransientDirectory.cpp similarity index 85% rename from libtestutils/ShortLivingDirectory.cpp rename to libtestutils/TransientDirectory.cpp index f60e12a86..48beca7e3 100644 --- a/libtestutils/ShortLivingDirectory.cpp +++ b/libtestutils/TransientDirectory.cpp @@ -14,20 +14,20 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file ShortLivingDirectory.cpp +/** @file TransientDirectory.cpp * @author Marek Kotewicz * @date 2015 */ #include #include -#include "ShortLivingDirectory.h" +#include "TransientDirectory.h" using namespace std; using namespace dev; using namespace dev::test; -ShortLivingDirectory::ShortLivingDirectory(std::string const& _path) : m_path(_path) +TransientDirectory::TransientDirectory(std::string const& _path) : m_path(_path) { // we never ever want to delete a directory (including all its contents) that we did not create ourselves. if (boost::filesystem::exists(m_path)) @@ -36,7 +36,7 @@ ShortLivingDirectory::ShortLivingDirectory(std::string const& _path) : m_path(_p boost::filesystem::create_directories(m_path); } -ShortLivingDirectory::~ShortLivingDirectory() +TransientDirectory::~TransientDirectory() { boost::filesystem::remove_all(m_path); } diff --git a/libtestutils/ShortLivingDirectory.h b/libtestutils/TransientDirectory.h similarity index 88% rename from libtestutils/ShortLivingDirectory.h rename to libtestutils/TransientDirectory.h index 10b542f06..328b4c92b 100644 --- a/libtestutils/ShortLivingDirectory.h +++ b/libtestutils/TransientDirectory.h @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file ShortLivingDirectory.h +/** @file TransientDirectory.h * @author Marek Kotewicz * @date 2015 */ @@ -34,11 +34,11 @@ namespace test * It creates temporary directory in the given path. On dealloc it removes the directory * @throws if the given path already exists, throws an exception */ -class ShortLivingDirectory +class TransientDirectory { public: - ShortLivingDirectory(std::string const& _path = getRandomPath()); - ~ShortLivingDirectory(); + TransientDirectory(std::string const& _path = getRandomPath()); + ~TransientDirectory(); std::string const& path() const { return m_path; } From bd1f719c0f99c43522aaaae972ba6fd942e711bf Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 19:51:08 +0100 Subject: [PATCH 082/228] fixes for test/TestUtils.h --- test/TestUtils.cpp | 25 ++++++------------------- test/TestUtils.h | 14 +++++++------- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/test/TestUtils.cpp b/test/TestUtils.cpp index 71eeadb3a..6222955d5 100644 --- a/test/TestUtils.cpp +++ b/test/TestUtils.cpp @@ -19,8 +19,6 @@ * @date 2015 */ -#include -#include #include #include #include @@ -28,10 +26,6 @@ #include #include "TestUtils.h" -// used methods from TestHelper: -// getTestPath -#include "TestHelper.h" - using namespace std; using namespace dev; using namespace dev::eth; @@ -73,19 +67,12 @@ std::string dev::test::getCommandLineArgument(string const& _name, bool _require return ""; } -bool LoadTestFileFixture::m_loaded = false; -Json::Value LoadTestFileFixture::m_json; - LoadTestFileFixture::LoadTestFileFixture() { - if (!m_loaded) - { - m_json = loadJsonFromFile(toTestFilePath(getCommandLineArgument("--eth_testfile"))); - m_loaded = true; - } + m_json = loadJsonFromFile(toTestFilePath(getCommandLineArgument("--eth_testfile"))); } -void ParallelFixture::enumerateThreads(std::function callback) +void ParallelFixture::enumerateThreads(std::function callback) const { size_t threadsCount = std::stoul(getCommandLineArgument("--eth_threads"), nullptr, 10); @@ -99,7 +86,7 @@ void ParallelFixture::enumerateThreads(std::function callback) }); } -void BlockChainFixture::enumerateBlockchains(std::function callback) +void BlockChainFixture::enumerateBlockchains(std::function callback) const { for (string const& name: m_json.getMemberNames()) { @@ -108,16 +95,16 @@ void BlockChainFixture::enumerateBlockchains(std::function callback) +void ClientBaseFixture::enumerateClients(std::function callback) const { - enumerateBlockchains([&callback](Json::Value const& _json, BlockChain& _bc, State _state) -> void + enumerateBlockchains([&callback](Json::Value const& _json, BlockChain const& _bc, State _state) -> void { FixedClient client(_bc, _state); callback(_json, client); }); } -void ParallelClientBaseFixture::enumerateClients(std::function callback) +void ParallelClientBaseFixture::enumerateClients(std::function callback) const { ClientBaseFixture::enumerateClients([this, &callback](Json::Value const& _json, dev::eth::ClientBase& _client) -> void { diff --git a/test/TestUtils.h b/test/TestUtils.h index fef10e982..f9817c21d 100644 --- a/test/TestUtils.h +++ b/test/TestUtils.h @@ -41,24 +41,24 @@ static SharedMutex x_boostTest; struct LoadTestFileFixture { LoadTestFileFixture(); - - static bool m_loaded; - static Json::Value m_json; + +protected: + Json::Value m_json; }; struct ParallelFixture { - void enumerateThreads(std::function callback); + void enumerateThreads(std::function callback) const; }; struct BlockChainFixture: public LoadTestFileFixture { - void enumerateBlockchains(std::function callback); + void enumerateBlockchains(std::function callback) const; }; struct ClientBaseFixture: public BlockChainFixture { - void enumerateClients(std::function callback); + void enumerateClients(std::function callback) const; }; // important BOOST TEST do have problems with thread safety!!! @@ -70,7 +70,7 @@ struct ClientBaseFixture: public BlockChainFixture // https://codecrafter.wordpress.com/2012/11/01/c-unit-test-framework-adapter-part-3/ struct ParallelClientBaseFixture: public ClientBaseFixture, public ParallelFixture { - void enumerateClients(std::function callback); + void enumerateClients(std::function callback) const; }; struct JsonRpcFixture: public ClientBaseFixture From 8e46d8379052eb40478c330f6f2575e6ff879652 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 20:46:30 +0100 Subject: [PATCH 083/228] fixed minimum jsonrpccpp required version --- cmake/EthDependencies.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index c91f93ef0..f27104c14 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -49,8 +49,9 @@ message(" - Jsoncpp header: ${JSONCPP_INCLUDE_DIRS}") message(" - Jsoncpp lib : ${JSONCPP_LIBRARIES}") # TODO get rid of -DETH_JSONRPC +# TODO add EXACT once we commit ourselves to cmake 3.x if (JSONRPC) - find_package (json_rpc_cpp 0.4 EXACT REQUIRED) + find_package (json_rpc_cpp 0.4 REQUIRED) message (" - json-rpc-cpp header: ${JSON_RPC_CPP_INCLUDE_DIRS}") message (" - json-rpc-cpp lib : ${JSON_RPC_CPP_LIBRARIES}") add_definitions(-DETH_JSONRPC) From 3b1b167811a8f523dd187eb2b975a6ef4498d941 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 20:56:37 +0100 Subject: [PATCH 084/228] minimum qt version explicitly set to 5.4, fixes #1419 --- cmake/EthDependencies.cmake | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index 05893715e..19114f424 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -117,14 +117,17 @@ if (NOT HEADLESS) # find all of the Qt packages # remember to use 'Qt' instead of 'QT', cause unix is case sensitive # TODO make headless client optional - find_package (Qt5Core REQUIRED) - find_package (Qt5Gui REQUIRED) - find_package (Qt5Quick REQUIRED) - find_package (Qt5Qml REQUIRED) - find_package (Qt5Network REQUIRED) - find_package (Qt5Widgets REQUIRED) - find_package (Qt5WebEngine REQUIRED) - find_package (Qt5WebEngineWidgets REQUIRED) + + set (ETH_QT_VERSION 5.4) + + find_package (Qt5Core ${ETH_QT_VERSION} REQUIRED) + find_package (Qt5Gui ${ETH_QT_VERSION} REQUIRED) + find_package (Qt5Quick ${ETH_QT_VERSION} REQUIRED) + find_package (Qt5Qml ${ETH_QT_VERSION} REQUIRED) + find_package (Qt5Network ${ETH_QT_VERSION} REQUIRED) + find_package (Qt5Widgets ${ETH_QT_VERSION} REQUIRED) + find_package (Qt5WebEngine ${ETH_QT_VERSION} REQUIRED) + find_package (Qt5WebEngineWidgets ${ETH_QT_VERSION} REQUIRED) # we need to find path to macdeployqt on mac if (APPLE) From 37c82e6fd74ce548dfde4b78ed2b7ff40e03c0ba Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 25 Mar 2015 22:58:43 +0100 Subject: [PATCH 085/228] First basic version compiling. --- abi/main.cpp | 200 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 166 insertions(+), 34 deletions(-) diff --git a/abi/main.cpp b/abi/main.cpp index 34187acc7..a17a47839 100644 --- a/abi/main.cpp +++ b/abi/main.cpp @@ -34,14 +34,19 @@ namespace js = json_spirit; void help() { cout - << "Usage abi enc (, (, ... ))" << endl - << " abi enc -a (, (, ... ))" << endl + << "Usage abi enc (, (, ... ))" << endl + << " abi enc -a (, (, ... ))" << endl << " abi dec -a [ | ]" << endl << "Options:" << endl << " -a,--abi-file Specify the JSON ABI file." << endl - << "Input options (enc mode):" << endl - << " -p,--prefix Require all arguments to be prefixed 0x (hex), . (decimal), # (binary)." << endl - << "Output options (dec mode):" << endl + << " -h,--help Print this help message and exit." << endl + << " -V,--version Show the version and exit." << endl + << "Input options:" << endl + << " -p,--prefix Require all input formats to be prefixed e.g. 0x for hex, . for decimal, @ for binary." << endl + << " -P,--no-prefix Require no input format to be prefixed." << endl + << " -t,--typing Require all arguments to be typed e.g. b32: (bytes32), u64: (uint64), b[]: (byte[]), i: (int256)." << endl + << " -T,--no-typing Require no arguments to be typed." << endl + << "Output options:" << endl << " -i,--index Output only the nth (counting from 0) return value." << endl << " -d,--decimal All data should be displayed as decimal." << endl << " -x,--hex Display all data as hex." << endl @@ -49,9 +54,6 @@ void help() << " -p,--prefix Prefix by a base identifier." << endl << " -z,--no-zeroes Remove any leading zeroes from the data." << endl << " -n,--no-nulls Remove any trailing nulls from the data." << endl - << "General options:" << endl - << " -h,--help Print this help message and exit." << endl - << " -V,--version Show the version and exit." << endl ; exit(0); } @@ -62,52 +64,173 @@ void version() exit(0); } -enum class Mode { +enum class Mode +{ Encode, Decode }; -enum class Encoding { +enum class Encoding +{ Auto, Decimal, Hex, Binary, }; +enum class Tristate +{ + False = false, + True = true, + Mu +}; + +enum class Format +{ + Binary, + Hex, + Decimal +}; + struct InvalidUserString: public Exception {}; +struct InvalidFormat: public Exception {}; + +enum class Base +{ + Unknown, + Bytes, + Address, + Int, + Uint, + Fixed +}; -pair fromUser(std::string const& _arg, bool _requirePrefix) +struct ABIType { - if (_requirePrefix) + Base base = Base::Unknown; + unsigned size = 0; + unsigned ssize = 0; + vector dims; + ABIType() = default; + ABIType(std::string const& _s) { - if (_arg.substr(0, 2) == "0x") - return make_pair(fromHex(_arg), false); - if (_arg.substr(0, 1) == ".") - return make_pair(toCompactBigEndian(bigint(_arg.substr(1))), false); - if (_arg.substr(0, 1) == "#") - return make_pair(asBytes(_arg.substr(1)), true); - throw InvalidUserString(); + if (_s.size() < 1) + return; + switch (_s[0]) + { + case 'b': base = Base::Bytes; break; + case 'a': base = Base::Address; break; + case 'i': base = Base::Int; break; + case 'u': base = Base::Uint; break; + case 'f': base = Base::Fixed; break; + default: throw InvalidFormat(); + } + if (_s.size() < 2) + return; + if (_s.find_first_of('x') == string::npos) + size = stoi(_s.substr(1)); + else + { + size = stoi(_s.substr(1, _s.find_first_of('x') - 1)); + ssize = stoi(_s.substr(_s.find_first_of('x') + 1)); + } + } + + string canon() const + { + string ret; + switch (base) + { + case Base::Bytes: ret = "bytes" + toString(size); break; + case Base::Address: ret = "address"; break; + case Base::Int: ret = "int" + toString(size); break; + case Base::Uint: ret = "uint" + toString(size); break; + case Base::Fixed: ret = "fixed" + toString(size) + "x" + toString(ssize); break; + default: throw InvalidFormat(); + } + for (int i: dims) + ret += "[" + ((i > -1) ? toString(i) : "") + "]"; + return ret; + } + + void noteHexInput(unsigned _nibbles) { if (base == Base::Unknown) { if (_nibbles == 40) base = Base::Address; else { base = Base::Bytes; size = _nibbles / 2; } } } + void noteBinaryInput() { if (base == Base::Unknown) { base = Base::Bytes; size = 32; } } + void noteDecimalInput() { if (base == Base::Unknown) { base = Base::Uint; size = 32; } } +}; + +tuple fromUser(std::string const& _arg, Tristate _prefix, Tristate _typing) +{ + ABIType type; + string val; + if (_typing == Tristate::True || (_typing == Tristate::Mu && _arg.find(':') != string::npos)) + { + if (_arg.find(':') != string::npos) + throw InvalidUserString(); + type = ABIType(_arg.substr(0, _arg.find(':'))); + val = _arg.substr(_arg.find(':') + 1); } else + val = _arg; + + if (_prefix != Tristate::False) + { + if (val.substr(0, 2) == "0x") + { + type.noteHexInput(val.size() - 2); + return make_tuple(fromHex(val), type, Format::Hex); + } + if (val.substr(0, 1) == ".") + { + type.noteDecimalInput(); + return make_tuple(toCompactBigEndian(bigint(val.substr(1))), type, Format::Decimal); + } + if (val.substr(0, 1) == "@") + { + type.noteBinaryInput(); + return make_tuple(asBytes(val.substr(1)), type, Format::Binary); + } + } + if (_prefix != Tristate::True) + { + if (_arg.find_first_not_of("0123456789") == string::npos) + { + type.noteDecimalInput(); + return make_tuple(toCompactBigEndian(bigint(val)), type, Format::Decimal); + } + if (_arg.find_first_not_of("0123456789abcdefABCDEF") == string::npos) + { + type.noteHexInput(val.size()); + return make_tuple(fromHex(val), type, Format::Hex); + } + type.noteBinaryInput(); + return make_tuple(asBytes(_arg), type, Format::Binary); + } + throw InvalidUserString(); +} + +void userOutput(ostream& _out, bytes const& _data, Encoding _e) +{ + switch (_e) { - if (_arg.substr(0, 2) == "0x") - return make_pair(fromHex(_arg), false); - if (_arg.find_first_not_of("0123456789")) - return make_pair(toCompactBigEndian(bigint(_arg)), false); - return make_pair(asBytes(_arg), true); + case Encoding::Binary: + _out.write((char const*)_data.data(), _data.size()); + break; + default: + _out << toHex(_data) << endl; } } -bytes aligned(bytes const& _b, bool _left, unsigned _length) +bytes aligned(bytes const& _b, ABIType _t, Format _f, unsigned _length) { + (void)_t; bytes ret = _b; while (ret.size() < _length) - if (_left) + if (_f == Format::Binary) ret.push_back(0); else ret.insert(ret.begin(), 0); while (ret.size() > _length) - if (_left) + if (_f == Format::Binary) ret.pop_back(); else ret.erase(ret.begin()); @@ -120,11 +243,12 @@ int main(int argc, char** argv) Mode mode = Mode::Encode; string abiFile; string method; - bool prefix = false; + Tristate prefix = Tristate::Mu; + Tristate typePrefix = Tristate::Mu; bool clearZeroes = false; bool clearNulls = false; int outputIndex = -1; - vector> args; + vector> args; for (int i = 1; i < argc; ++i) { @@ -140,7 +264,13 @@ int main(int argc, char** argv) else if ((arg == "-i" || arg == "--index") && argc > i) outputIndex = atoi(argv[++i]); else if (arg == "-p" || arg == "--prefix") - prefix = true; + prefix = Tristate::True; + else if (arg == "-P" || arg == "--no-prefix") + prefix = Tristate::False; + else if (arg == "-t" || arg == "--typing") + typePrefix = Tristate::True; + else if (arg == "-T" || arg == "--no-typing") + typePrefix = Tristate::False; else if (arg == "-z" || arg == "--no-zeroes") clearZeroes = true; else if (arg == "-n" || arg == "--no-nulls") @@ -156,7 +286,7 @@ int main(int argc, char** argv) else if (method.empty()) method = arg; else - args.push_back(fromUser(arg, prefix)); + args.push_back(fromUser(arg, prefix, typePrefix)); } string abi; @@ -168,22 +298,24 @@ int main(int argc, char** argv) if (mode == Mode::Encode) { + bytes ret; if (abi.empty()) { - bytes ret; if (!method.empty()) ret = FixedHash<32>(sha3(method)).asBytes(); if (method.empty()) - for (pair const& arg: args) - ret += aligned(arg.first, arg.second, 32); + for (tuple const& arg: args) + ret += aligned(get<0>(arg), get<1>(arg), get<2>(arg), 32); } else { // TODO: read abi. } + userOutput(cout, ret, encoding); } else if (mode == Mode::Decode) { + // TODO: read abi to determine output format. (void)encoding; (void)clearZeroes; (void)clearNulls; From d554ecfe7572e4f917ace5d71f3162283929b692 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 25 Mar 2015 23:04:23 +0100 Subject: [PATCH 086/228] Fix for hash size. --- abi/main.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/abi/main.cpp b/abi/main.cpp index a17a47839..63020f07b 100644 --- a/abi/main.cpp +++ b/abi/main.cpp @@ -302,10 +302,9 @@ int main(int argc, char** argv) if (abi.empty()) { if (!method.empty()) - ret = FixedHash<32>(sha3(method)).asBytes(); - if (method.empty()) - for (tuple const& arg: args) - ret += aligned(get<0>(arg), get<1>(arg), get<2>(arg), 32); + ret = FixedHash<4>(sha3(method)).asBytes(); + for (tuple const& arg: args) + ret += aligned(get<0>(arg), get<1>(arg), get<2>(arg), 32); } else { From c43c1b61e36710a1620b0665d30b89cc9c876081 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 25 Mar 2015 23:13:13 +0100 Subject: [PATCH 087/228] Fixes and verbosity. --- abi/main.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/abi/main.cpp b/abi/main.cpp index 63020f07b..24ec1df65 100644 --- a/abi/main.cpp +++ b/abi/main.cpp @@ -164,7 +164,7 @@ tuple fromUser(std::string const& _arg, Tristate _prefix string val; if (_typing == Tristate::True || (_typing == Tristate::Mu && _arg.find(':') != string::npos)) { - if (_arg.find(':') != string::npos) + if (_arg.find(':') == string::npos) throw InvalidUserString(); type = ABIType(_arg.substr(0, _arg.find(':'))); val = _arg.substr(_arg.find(':') + 1); @@ -247,6 +247,7 @@ int main(int argc, char** argv) Tristate typePrefix = Tristate::Mu; bool clearZeroes = false; bool clearNulls = false; + bool verbose = false; int outputIndex = -1; vector> args; @@ -275,12 +276,16 @@ int main(int argc, char** argv) clearZeroes = true; else if (arg == "-n" || arg == "--no-nulls") clearNulls = true; + else if (arg == "-v" || arg == "--verbose") + verbose = true; else if (arg == "-x" || arg == "--hex") encoding = Encoding::Hex; else if (arg == "-d" || arg == "--decimal" || arg == "--dec") encoding = Encoding::Decimal; else if (arg == "-b" || arg == "--binary" || arg == "--bin") encoding = Encoding::Binary; + else if (arg == "-v" || arg == "--verbose") + version(); else if (arg == "-V" || arg == "--version") version(); else if (method.empty()) @@ -302,7 +307,14 @@ int main(int argc, char** argv) if (abi.empty()) { if (!method.empty()) - ret = FixedHash<4>(sha3(method)).asBytes(); + { + string methodArgs; + for (auto const& arg: args) + methodArgs += (methodArgs.empty() ? "" : ",") + get<1>(arg).canon(); + ret = FixedHash<4>(sha3(method + "(" + methodArgs + ")")).asBytes(); + if (verbose) + cerr << "Method signature: " << (method + "(" + methodArgs + ")") << endl; + } for (tuple const& arg: args) ret += aligned(get<0>(arg), get<1>(arg), get<2>(arg), 32); } From b063d5607df54eab6caf1d1bc9f074adc00cad8b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 25 Mar 2015 23:15:19 +0100 Subject: [PATCH 088/228] Size fix for ABI. --- abi/main.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/abi/main.cpp b/abi/main.cpp index 24ec1df65..2225dd4b3 100644 --- a/abi/main.cpp +++ b/abi/main.cpp @@ -108,7 +108,7 @@ enum class Base struct ABIType { Base base = Base::Unknown; - unsigned size = 0; + unsigned size = 32; unsigned ssize = 0; vector dims; ABIType() = default; @@ -126,7 +126,13 @@ struct ABIType default: throw InvalidFormat(); } if (_s.size() < 2) + { + if (base == Base::Fixed) + size = ssize = 16; + else + size = 32; return; + } if (_s.find_first_of('x') == string::npos) size = stoi(_s.substr(1)); else From 58cb54bd49b8bb369ba4b0571f32416af0e2730b Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 26 Mar 2015 06:06:34 +0100 Subject: [PATCH 089/228] correct net/test for ping packet --- test/net.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/net.cpp b/test/net.cpp index 7ba2d8a12..a5f973450 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -145,7 +145,7 @@ public: bool success = false; }; -BOOST_AUTO_TEST_CASE(badPingNodePacket) +BOOST_AUTO_TEST_CASE(v2PingNodePacket) { // test old versino of pingNode packet w/new RLPStream s; @@ -153,7 +153,7 @@ BOOST_AUTO_TEST_CASE(badPingNodePacket) PingNode p((bi::udp::endpoint())); BOOST_REQUIRE_NO_THROW(p = PingNode::fromBytesConstRef(bi::udp::endpoint(), bytesConstRef(&s.out()))); - BOOST_REQUIRE(p.version == 0); + BOOST_REQUIRE(p.version == 2); } BOOST_AUTO_TEST_CASE(test_neighbours_packet) From 45df4ab10f76d54d8ffde09c9a98eb1ba6b6c94e Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 26 Mar 2015 06:07:59 +0100 Subject: [PATCH 090/228] isIPType helpers --- libp2p/Common.cpp | 20 ++++++++++++++++++++ libp2p/Common.h | 6 ++++++ 2 files changed, 26 insertions(+) diff --git a/libp2p/Common.cpp b/libp2p/Common.cpp index 0429c52ab..86e1135c4 100644 --- a/libp2p/Common.cpp +++ b/libp2p/Common.cpp @@ -26,6 +26,16 @@ using namespace dev::p2p; const unsigned dev::p2p::c_protocolVersion = 3; +bool p2p::isPublicAddress(std::string const& _addressToCheck) +{ + return isPublicAddress(bi::address::from_string(_addressToCheck)); +} + +bool p2p::isPublicAddress(bi::address const& _addressToCheck) +{ + return !(isPrivateAddress(_addressToCheck) || isLocalHostAddress(_addressToCheck)); +} + // Helper function to determine if an address falls within one of the reserved ranges // For V4: // Class A "10.*", Class B "172.[16->31].*", Class C "192.168.*" @@ -55,6 +65,11 @@ bool p2p::isPrivateAddress(bi::address const& _addressToCheck) return false; } +bool p2p::isPrivateAddress(std::string const& _addressToCheck) +{ + return isPrivateAddress(bi::address::from_string(_addressToCheck)); +} + // Helper function to determine if an address is localhost bool p2p::isLocalHostAddress(bi::address const& _addressToCheck) { @@ -69,6 +84,11 @@ bool p2p::isLocalHostAddress(bi::address const& _addressToCheck) return find(c_rejectAddresses.begin(), c_rejectAddresses.end(), _addressToCheck) != c_rejectAddresses.end(); } +bool p2p::isLocalHostAddress(std::string const& _addressToCheck) +{ + return isLocalHostAddress(bi::address::from_string(_addressToCheck)); +} + std::string p2p::reasonOf(DisconnectReason _r) { switch (_r) diff --git a/libp2p/Common.h b/libp2p/Common.h index 66b3f08e8..b35cb379c 100644 --- a/libp2p/Common.h +++ b/libp2p/Common.h @@ -54,7 +54,11 @@ extern const unsigned c_protocolVersion; using NodeId = h512; bool isPrivateAddress(bi::address const& _addressToCheck); +bool isPrivateAddress(std::string const& _addressToCheck); bool isLocalHostAddress(bi::address const& _addressToCheck); +bool isLocalHostAddress(std::string const& _addressToCheck); +bool isPublicAddress(bi::address const& _addressToCheck); +bool isPublicAddress(std::string const& _addressToCheck); class UPnP; class Capability; @@ -62,6 +66,8 @@ class Host; class Session; struct NetworkStartRequired: virtual dev::Exception {}; +struct InvalidPublicIPAddress: virtual dev::Exception {}; +struct InvalidHostIPAddress: virtual dev::Exception {}; struct NetWarn: public LogChannel { static const char* name() { return "!N!"; } static const int verbosity = 0; }; struct NetNote: public LogChannel { static const char* name() { return "*N*"; } static const int verbosity = 1; }; From 8fb315be4951ca3877dc890c901fed3d0ed91fc2 Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 26 Mar 2015 06:19:47 +0100 Subject: [PATCH 091/228] refactor network prefs --- libp2p/Host.cpp | 92 ++++++++++++++++++++-------------------------- libp2p/Host.h | 8 ++-- libp2p/Network.cpp | 59 +++++++++++++++++++---------- libp2p/Network.h | 31 ++++++++++------ 4 files changed, 100 insertions(+), 90 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index b946d9b3e..4ce672602 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -287,67 +287,53 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e) } } -void Host::determinePublic(string const& _publicAddress, bool _upnp) +void Host::determinePublic(NetworkPreferences const& _netPrefs) { - m_peerAddresses.clear(); - - // no point continuing if there are no interface addresses or valid listen port - if (!m_ifAddresses.size() || m_listenPort < 1) - return; - - // populate interfaces we'll listen on (eth listens on all interfaces); ignores local - for (auto addr: m_ifAddresses) - if ((m_netPrefs.localNetworking || !isPrivateAddress(addr)) && !isLocalHostAddress(addr)) - m_peerAddresses.insert(addr); - - // if user supplied address is a public address then we use it - // if user supplied address is private, and localnetworking is enabled, we use it - bi::address reqPublicAddr(bi::address(_publicAddress.empty() ? bi::address() : bi::address::from_string(_publicAddress))); - bi::tcp::endpoint reqPublic(reqPublicAddr, m_listenPort); - bool isprivate = isPrivateAddress(reqPublicAddr); - bool ispublic = !isprivate && !isLocalHostAddress(reqPublicAddr); - if (!reqPublicAddr.is_unspecified() && (ispublic || (isprivate && m_netPrefs.localNetworking))) + // set m_tcpPublic := listenIP (if public) > public > upnp > unspecified address. + + auto ifAddresses = Network::getInterfaceAddresses(); + auto laddr = bi::address::from_string(_netPrefs.listenIPAddress); + auto lset = !laddr.is_unspecified(); + auto paddr = bi::address::from_string(_netPrefs.publicIPAddress); + auto pset = !paddr.is_unspecified(); + + bool listenIsPublic = lset && isPublicAddress(laddr); + bool publicIsHost = !lset && pset && ifAddresses.count(paddr); + + bi::tcp::endpoint ep(bi::address(), _netPrefs.listenPort); + if (_netPrefs.traverseNAT && listenIsPublic) { - if (!m_peerAddresses.count(reqPublicAddr)) - m_peerAddresses.insert(reqPublicAddr); - m_tcpPublic = reqPublic; - return; + clog(NetNote) << "Listen address set to Public address:" << laddr << ". UPnP disabled."; + ep.address(laddr); } - - // if address wasn't provided, then use first public ipv4 address found - for (auto addr: m_peerAddresses) - if (addr.is_v4() && !isPrivateAddress(addr)) - { - m_tcpPublic = bi::tcp::endpoint(*m_peerAddresses.begin(), m_listenPort); - return; - } - - // or find address via upnp - if (_upnp) + else if (_netPrefs.traverseNAT && publicIsHost) + { + clog(NetNote) << "Public address set to Host configured address:" << paddr << ". UPnP disabled."; + ep.address(paddr); + } + else if (_netPrefs.traverseNAT) { - bi::address upnpifaddr; - bi::tcp::endpoint upnpep = Network::traverseNAT(m_ifAddresses, m_listenPort, upnpifaddr); - if (!upnpep.address().is_unspecified() && !upnpifaddr.is_unspecified()) + bi::address natIFAddr; + if (lset && ifAddresses.count(laddr)) + ep = Network::traverseNAT(std::set({laddr}), _netPrefs.listenPort, natIFAddr); + else + ep = Network::traverseNAT(ifAddresses, _netPrefs.listenPort, natIFAddr); + + if (lset && natIFAddr != laddr) + // if listen address is set we use it, even if upnp returns different + clog(NetWarn) << "Listen address" << laddr << "differs from local address" << natIFAddr << "returned by UPnP!"; + + if (pset && ep.address() != paddr) { - if (!m_peerAddresses.count(upnpep.address())) - m_peerAddresses.insert(upnpep.address()); - m_tcpPublic = upnpep; - return; + // if public address is set we advertise it, even if upnp returns different + clog(NetWarn) << "Specified public address" << paddr << "differs from external address" << ep.address() << "returned by UPnP!"; + ep.address(paddr); } } + else if (pset) + ep.address(paddr); - // or if no address provided, use private ipv4 address if local networking is enabled - if (reqPublicAddr.is_unspecified()) - if (m_netPrefs.localNetworking) - for (auto addr: m_peerAddresses) - if (addr.is_v4() && isPrivateAddress(addr)) - { - m_tcpPublic = bi::tcp::endpoint(addr, m_listenPort); - return; - } - - // otherwise address is unspecified - m_tcpPublic = bi::tcp::endpoint(bi::address(), m_listenPort); + m_tcpPublic = ep; } void Host::runAcceptor() diff --git a/libp2p/Host.h b/libp2p/Host.h index 0feda364f..e1fe9de68 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -154,8 +154,8 @@ protected: private: bool havePeerSession(NodeId _id) { RecursiveGuard l(x_sessions); return m_sessions.count(_id) ? !!m_sessions[_id].lock() : false; } - /// Populate m_peerAddresses with available public addresses. - void determinePublic(std::string const& _publicAddress, bool _upnp); + /// Determines and sets m_tcpPublic to publicly advertised address. + void determinePublic(NetworkPreferences const& _netPrefs); void connect(std::shared_ptr const& _p); @@ -192,7 +192,7 @@ private: NetworkPreferences m_netPrefs; ///< Network settings. /// Interface addresses (private, public) - std::vector m_ifAddresses; ///< Interface addresses. + std::set m_ifAddresses; ///< Interface addresses. int m_listenPort = -1; ///< What port are we listening on. -1 means binding failed or acceptor hasn't been initialized. @@ -222,8 +222,6 @@ private: unsigned m_idealPeerCount = 5; ///< Ideal number of peers to be connected to. - std::set m_peerAddresses; ///< Public addresses that peers (can) know us by. - std::map> m_capabilities; ///< Each of the capabilities we support. std::chrono::steady_clock::time_point m_lastPing; ///< Time we sent the last ping to all peers. diff --git a/libp2p/Network.cpp b/libp2p/Network.cpp index bdd1f0108..7be54d9fb 100644 --- a/libp2p/Network.cpp +++ b/libp2p/Network.cpp @@ -40,9 +40,9 @@ using namespace std; using namespace dev; using namespace dev::p2p; -std::vector Network::getInterfaceAddresses() +std::set Network::getInterfaceAddresses() { - std::vector addresses; + std::set addresses; #ifdef _WIN32 WSAData wsaData; @@ -72,7 +72,7 @@ std::vector Network::getInterfaceAddresses() char *addrStr = inet_ntoa(addr); bi::address address(bi::address::from_string(addrStr)); if (!isLocalHostAddress(address)) - addresses.push_back(address.to_v4()); + addresses.insert(address.to_v4()); } WSACleanup(); @@ -91,7 +91,7 @@ std::vector Network::getInterfaceAddresses() in_addr addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; boost::asio::ip::address_v4 address(boost::asio::detail::socket_ops::network_to_host_long(addr.s_addr)); if (!isLocalHostAddress(address)) - addresses.push_back(address); + addresses.insert(address); } else if (ifa->ifa_addr->sa_family == AF_INET6) { @@ -101,7 +101,7 @@ std::vector Network::getInterfaceAddresses() memcpy(&bytes[0], addr.s6_addr, 16); boost::asio::ip::address_v6 address(bytes, sockaddr->sin6_scope_id); if (!isLocalHostAddress(address)) - addresses.push_back(address); + addresses.insert(address); } } @@ -113,13 +113,39 @@ std::vector Network::getInterfaceAddresses() return std::move(addresses); } -int Network::tcp4Listen(bi::tcp::acceptor& _acceptor, unsigned short _listenPort) +int Network::tcp4Listen(bi::tcp::acceptor& _acceptor, NetworkPreferences const& _netPrefs) { int retport = -1; - for (unsigned i = 0; i < 2; ++i) + if (_netPrefs.listenIPAddress.empty()) + for (unsigned i = 0; i < 2; ++i) + { + // try to connect w/listenPort, else attempt net-allocated port + bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : _netPrefs.listenPort); + try + { + _acceptor.open(endpoint.protocol()); + _acceptor.set_option(ba::socket_base::reuse_address(true)); + _acceptor.bind(endpoint); + _acceptor.listen(); + retport = _acceptor.local_endpoint().port(); + break; + } + catch (...) + { + if (i) + { + // both attempts failed + cwarn << "Couldn't start accepting connections on host. Something very wrong with network?\n" << boost::current_exception_diagnostic_information(); + } + + // first attempt failed + _acceptor.close(); + continue; + } + } + else { - // try to connect w/listenPort, else attempt net-allocated port - bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : _listenPort); + bi::tcp::endpoint endpoint(bi::address::from_string(_netPrefs.listenIPAddress), _netPrefs.listenPort); try { _acceptor.open(endpoint.protocol()); @@ -127,25 +153,18 @@ int Network::tcp4Listen(bi::tcp::acceptor& _acceptor, unsigned short _listenPort _acceptor.bind(endpoint); _acceptor.listen(); retport = _acceptor.local_endpoint().port(); - break; } catch (...) { - if (i) - { - // both attempts failed - cwarn << "Couldn't start accepting connections on host. Something very wrong with network?\n" << boost::current_exception_diagnostic_information(); - } - - // first attempt failed - _acceptor.close(); - continue; + clog(NetWarn) << "Couldn't start accepting connections on host. Failed to accept socket.\n" << boost::current_exception_diagnostic_information(); } + assert(retport == _netPrefs.listenPort); + return retport; } return retport; } -bi::tcp::endpoint Network::traverseNAT(std::vector const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr) +bi::tcp::endpoint Network::traverseNAT(std::set const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr) { asserts(_listenPort != 0); diff --git a/libp2p/Network.h b/libp2p/Network.h index aeeabf329..127b5ea04 100644 --- a/libp2p/Network.h +++ b/libp2p/Network.h @@ -37,15 +37,7 @@ namespace dev namespace p2p { -struct NetworkPreferences -{ - NetworkPreferences(unsigned short p = 30303, std::string i = std::string(), bool u = true, bool l = false): listenPort(p), publicIP(i), upnp(u), localNetworking(l) {} - - unsigned short listenPort = 30303; - std::string publicIP; - bool upnp = true; - bool localNetworking = false; -}; +struct NetworkPreferences; /** * @brief Network Class @@ -55,14 +47,29 @@ class Network { public: /// @returns public and private interface addresses - static std::vector getInterfaceAddresses(); + static std::set getInterfaceAddresses(); /// Try to bind and listen on _listenPort, else attempt net-allocated port. - static int tcp4Listen(bi::tcp::acceptor& _acceptor, unsigned short _listenPort); + static int tcp4Listen(bi::tcp::acceptor& _acceptor, NetworkPreferences const& _netPrefs); /// Return public endpoint of upnp interface. If successful o_upnpifaddr will be a private interface address and endpoint will contain public address and port. - static bi::tcp::endpoint traverseNAT(std::vector const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr); + static bi::tcp::endpoint traverseNAT(std::set const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr); }; +struct NetworkPreferences +{ + // Network Preferences with unspecified Public IP + NetworkPreferences(unsigned short p = 30303, std::string i = std::string(), bool u = true, bool l = false): listenPort(p), listenIPAddress(i), publicIPAddress(), traverseNAT(u) {} + + // Network Preferences with intended Public IP + NetworkPreferences(std::string publicIP, unsigned short p = 30303, std::string i = std::string(), bool u = true, bool l = false): listenPort(p), listenIPAddress(i), publicIPAddress(publicIP), traverseNAT(u) { if (!publicIPAddress.empty() && !isPublicAddress(publicIPAddress)) BOOST_THROW_EXCEPTION(InvalidPublicIPAddress()); } + + unsigned short listenPort = 30303; + std::string listenIPAddress; + std::string publicIPAddress; + + bool traverseNAT = true; +}; + } } From 07f0cde34ff00ad673d8926c7b3f4bfd85b89afa Mon Sep 17 00:00:00 2001 From: caktux Date: Thu, 26 Mar 2015 03:17:31 -0400 Subject: [PATCH 092/228] add missing options to neth, support for PeerServer mode --- neth/main.cpp | 341 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 229 insertions(+), 112 deletions(-) diff --git a/neth/main.cpp b/neth/main.cpp index fc4c8c9c6..7af96eb08 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -31,12 +31,15 @@ #include #include +#include +#include +#include #include +#include #if ETH_JSONRPC #include #include #endif -#include #include "BuildInfo.h" #undef KEY_EVENT // from windows.h @@ -68,26 +71,37 @@ void help() << "Usage neth [OPTIONS]" << endl << "Options:" << endl << " -a,--address Set the coinbase (mining payout) address to addr (default: auto)." << endl + << " -b,--bootstrap Connect to the default Ethereum peerserver." << endl + << " -B,--block-fees Set the block fee profit in the reference unit e.g. ¢ (Default: 15)." << endl << " -c,--client-name Add a name to your client's version string (default: blank)." << endl << " -d,--db-path Load database from path (default: ~/.ethereum " << endl << " /Etherum or Library/Application Support/Ethereum)." << endl + << " -e,--ether-price Set the ether price in the reference unit e.g. ¢ (Default: 30.679)." << endl + << " -f,--force-mining Mine even when there are no transaction to mine (Default: off)" << endl << " -h,--help Show this help message and exit." << endl #if ETH_JSONRPC << " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: 8080)." << endl #endif + << " -K,--kill-blockchain First kill the blockchain." << endl << " -l,--listen Listen on the given port for incoming connected (default: 30303)." << endl + << " -L,--local-networking Use peers whose addresses are local." << endl << " -m,--mining Enable mining (default: off)" << endl << " -n,--upnp Use upnp for NAT (default: on)." << endl << " -o,--mode Start a full node or a peer node (Default: full)." << endl << " -p,--port Connect to remote port (default: 30303)." << endl + << " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl << " -r,--remote Connect to remote host (default: none)." << endl << " -s,--secret Set the secret key for use with send command (default: auto)." << endl << " -t,--miners Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl << " -u,--public-ip Force public ip to given (default; auto)." << endl << " -v,--verbosity <0..9> Set the log verbosity from 0 to 9 (tmp forced to 1)." << endl << " -x,--peers Attempt to connect to given number of peers (default: 5)." << endl - << " -V,--version Show the version and exit." << endl; + << " -V,--version Show the version and exit." << endl +#if ETH_EVMJIT + << " --jit Use EVM JIT (default: off)." << endl +#endif + ; exit(0); } @@ -319,7 +333,14 @@ int main(int argc, char** argv) bool upnp = true; bool useLocal = false; bool forceMining = false; + bool killChain = false; + bool jit = false; + bool structuredLogging = false; + string structuredLoggingFormat = "%Y-%m-%dT%H:%M:%S"; string clientName; + TransactionPriority priority = TransactionPriority::Medium; + double etherPrice = 30.679; + double blockFees = 15.0; // Init defaults Defaults::get(); @@ -367,6 +388,10 @@ int main(int argc, char** argv) return -1; } } + else if (arg == "-L" || arg == "--local-networking") + useLocal = true; + else if (arg == "-K" || arg == "--kill-blockchain") + killChain = true; else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc) clientName = argv[++i]; else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc) @@ -389,8 +414,58 @@ int main(int argc, char** argv) } else if ((arg == "-s" || arg == "--secret") && i + 1 < argc) us = KeyPair(h256(fromHex(argv[++i]))); + else if (arg == "--structured-logging-format" && i + 1 < argc) + structuredLoggingFormat = string(argv[++i]); + else if (arg == "--structured-logging") + structuredLogging = true; else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc) dbPath = argv[++i]; + else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc) + { + try + { + blockFees = stof(argv[++i]); + } + catch (...) + { + cerr << "Bad " << arg << " option: " << argv[++i] << endl; + return -1; + } + } + else if ((arg == "-e" || arg == "--ether-price") && i + 1 < argc) + { + try + { + etherPrice = stof(argv[++i]); + } + catch (...) + { + cerr << "Bad " << arg << " option: " << argv[++i] << endl; + return -1; + } + } + else if ((arg == "-P" || arg == "--priority") && i + 1 < argc) + { + string m = boost::to_lower_copy(string(argv[++i])); + if (m == "lowest") + priority = TransactionPriority::Lowest; + else if (m == "low") + priority = TransactionPriority::Low; + else if (m == "medium" || m == "mid" || m == "default" || m == "normal") + priority = TransactionPriority::Medium; + else if (m == "high") + priority = TransactionPriority::High; + else if (m == "highest") + priority = TransactionPriority::Highest; + else + try { + priority = (TransactionPriority)(max(0, min(100, stoi(m))) * 8 / 100); + } + catch (...) { + cerr << "Unknown " << arg << " option: " << m << endl; + return -1; + } + } else if ((arg == "-m" || arg == "--mining") && i + 1 < argc) { string m = argv[++i]; @@ -423,6 +498,28 @@ int main(int argc, char** argv) peers = atoi(argv[++i]); else if ((arg == "-t" || arg == "--miners") && i + 1 < argc) miners = atoi(argv[++i]); + else if ((arg == "-o" || arg == "--mode") && i + 1 < argc) + { + string m = argv[++i]; + if (m == "full") + mode = NodeMode::Full; + else if (m == "peer") + mode = NodeMode::PeerServer; + else + { + cerr << "Unknown mode: " << m << endl; + return -1; + } + } + else if (arg == "--jit") + { +#if ETH_EVMJIT + jit = true; +#else + cerr << "EVM JIT not enabled" << endl; + return -1; +#endif + } else if (arg == "-h" || arg == "--help") help(); else if (arg == "-V" || arg == "--version") @@ -438,35 +535,41 @@ int main(int argc, char** argv) clientName += "/"; cout << credits(); + + StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat); + VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); NetworkPreferences netPrefs(listenPort, publicIP, upnp, useLocal); auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); + std::string clientImplString = "NEthereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""); dev::WebThreeDirect web3( - "NEthereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), + clientImplString, dbPath, - false, + killChain, mode == NodeMode::Full ? set{"eth", "shh"} : set(), netPrefs, &nodesState, miners ); web3.setIdealPeerCount(peers); + std::shared_ptr gasPricer = make_shared(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000)); eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr; - + StructuredLogger::starting(clientImplString, dev::Version); if (c) { + c->setGasPricer(gasPricer); c->setForceMining(forceMining); c->setAddress(coinbase); } - cout << "Address: " << endl << toHex(us.address().asArray()) << endl; - + cout << "Transaction Signer: " << us.address() << endl; + cout << "Mining Benefactor: " << coinbase << endl; web3.startNetwork(); if (bootstrap) web3.connect(Host::pocHost()); if (remoteHost.size()) web3.connect(remoteHost, remotePort); - if (mining) + if (c && mining) c->startMining(); #if ETH_JSONRPC @@ -474,11 +577,7 @@ int main(int argc, char** argv) unique_ptr jsonrpcConnector; if (jsonrpc > -1) { -#if ETH_DEBUG - jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 1)); -#else - jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", 4)); -#endif + jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({us}))); jsonrpcServer->setIdentities({us}); jsonrpcServer->StartListening(); @@ -655,7 +754,7 @@ int main(int argc, char** argv) ccout << "Current secret:" << endl; ccout << toHex(us.secret().asArray()) << endl; } - else if (cmd == "block") + else if (c && cmd == "block") { unsigned n = c->blockChain().details().number; ccout << "Current block # "; @@ -668,13 +767,13 @@ int main(int argc, char** argv) << std::chrono::duration_cast(it.lastPing).count() << "ms" << endl; } - else if (cmd == "balance") + else if (c && cmd == "balance") { u256 balance = c->balanceAt(us.address()); ccout << "Current balance:" << endl; ccout << toString(balance) << endl; } - else if (cmd == "transact") + else if (c && cmd == "transact") { auto const& bc = c->blockChain(); auto h = bc.currentHash(); @@ -761,7 +860,7 @@ int main(int argc, char** argv) } } } - else if (cmd == "send") + else if (c && cmd == "send") { vector s; s.push_back("Address"); @@ -812,7 +911,7 @@ int main(int argc, char** argv) } } } - else if (cmd == "contract") + else if (c && cmd == "contract") { auto const& bc = c->blockChain(); auto h = bc.currentHash(); @@ -895,7 +994,7 @@ int main(int argc, char** argv) } } } - else if (cmd == "inspect") + else if (c && cmd == "inspect") { string rechex; iss >> rechex; @@ -941,111 +1040,119 @@ int main(int argc, char** argv) interactiveHelp(); else if (cmd == "exit") break; + else if (cmd != "") + cwarn << "Unrecognised command. Type 'help' for a list of available commands."; // Clear cmd at each pass cmd = ""; - // Lock to prevent corrupt block-chain errors - auto const& bc = c->blockChain(); - ccout << "Genesis hash: " << bc.genesisHash() << endl; + // Contracts and addresses count / offset + int cc = 1; + int ca = 0; - // Blocks - y = 1; - for (auto h = bc.currentHash(); h != bc.genesisHash(); h = bc.details(h).parent) - { - auto d = bc.details(h); - string s = "# " + std::to_string(d.number) + ' ' + toString(h); // .abridged(); - mvwaddnstr(blockswin, y++, x, s.c_str(), qwidth); + if (c) { + // Lock to prevent corrupt block-chain errors + auto const& bc = c->blockChain(); + ccout << "Genesis hash: " << bc.genesisHash() << endl; - auto b = bc.block(h); - for (auto const& i: RLP(b)[1]) + // Blocks + y = 1; + for (auto h = bc.currentHash(); h != bc.genesisHash(); h = bc.details(h).parent) + { + auto d = bc.details(h); + string s = "# " + std::to_string(d.number) + ' ' + toString(h); // .abridged(); + mvwaddnstr(blockswin, y++, x, s.c_str(), qwidth); + + auto b = bc.block(h); + for (auto const& i: RLP(b)[1]) + { + Transaction t(i.data(), CheckSignature::Sender); + auto s = t.receiveAddress() ? + boost::format(" %1% %2%> %3%: %4% [%5%]") % + toString(t.safeSender()) % + (c->codeAt(t.receiveAddress(), PendingBlock).size() ? '*' : '-') % + toString(t.receiveAddress()) % + toString(formatBalance(t.value())) % + toString((unsigned)t.nonce()) : + boost::format(" %1% +> %2%: %3% [%4%]") % + toString(t.safeSender()) % + toString(right160(sha3(rlpList(t.safeSender(), t.nonce())))) % + toString(formatBalance(t.value())) % + toString((unsigned)t.nonce()); + mvwaddnstr(blockswin, y++, x, s.str().c_str(), qwidth - 2); + if (y > qheight - 2) + break; + } + if (y > qheight - 2) + break; + } + + + // Pending + y = 1; + for (Transaction const& t: c->pending()) { - Transaction t(i.data(), CheckSignature::Sender); auto s = t.receiveAddress() ? - boost::format(" %1% %2%> %3%: %4% [%5%]") % + boost::format("%1% %2%> %3%: %4% [%5%]") % toString(t.safeSender()) % (c->codeAt(t.receiveAddress(), PendingBlock).size() ? '*' : '-') % toString(t.receiveAddress()) % toString(formatBalance(t.value())) % toString((unsigned)t.nonce()) : - boost::format(" %1% +> %2%: %3% [%4%]") % + boost::format("%1% +> %2%: %3% [%4%]") % toString(t.safeSender()) % toString(right160(sha3(rlpList(t.safeSender(), t.nonce())))) % toString(formatBalance(t.value())) % toString((unsigned)t.nonce()); - mvwaddnstr(blockswin, y++, x, s.str().c_str(), qwidth - 2); - if (y > qheight - 2) + mvwaddnstr(pendingwin, y++, x, s.str().c_str(), qwidth); + if (y > height * 1 / 5 - 2) break; } - if (y > qheight - 2) - break; - } - - // Pending - y = 1; - for (Transaction const& t: c->pending()) - { - auto s = t.receiveAddress() ? - boost::format("%1% %2%> %3%: %4% [%5%]") % - toString(t.safeSender()) % - (c->codeAt(t.receiveAddress(), PendingBlock).size() ? '*' : '-') % - toString(t.receiveAddress()) % - toString(formatBalance(t.value())) % - toString((unsigned)t.nonce()) : - boost::format("%1% +> %2%: %3% [%4%]") % - toString(t.safeSender()) % - toString(right160(sha3(rlpList(t.safeSender(), t.nonce())))) % - toString(formatBalance(t.value())) % - toString((unsigned)t.nonce()); - mvwaddnstr(pendingwin, y++, x, s.str().c_str(), qwidth); - if (y > height * 1 / 5 - 2) - break; - } + // Contracts and addresses + y = 1; + auto acs = c->addresses(); + ca = acs.size(); + for (auto const& i: acs) + if (c->codeAt(i, PendingBlock).size()) + { + auto s = boost::format("%1%%2% : %3% [%4%]") % + toString(i) % + pretty(i, c->postState()) % + toString(formatBalance(c->balanceAt(i))) % + toString((unsigned)c->countAt(i, PendingBlock)); + mvwaddnstr(contractswin, cc++, x, s.str().c_str(), qwidth); + if (cc > qheight - 2) + break; + } + for (auto const& i: acs) + if (c->codeAt(i, PendingBlock).empty()) + { + auto s = boost::format("%1%%2% : %3% [%4%]") % + toString(i) % + pretty(i, c->postState()) % + toString(formatBalance(c->balanceAt(i))) % + toString((unsigned)c->countAt(i, PendingBlock)); + mvwaddnstr(addswin, y++, x, s.str().c_str(), width / 2 - 4); + if (y > height * 3 / 5 - 4) + break; + } - // Contracts and addresses - y = 1; - int cc = 1; - auto acs = c->addresses(); - for (auto const& i: acs) - if (c->codeAt(i, PendingBlock).size()) - { - auto s = boost::format("%1%%2% : %3% [%4%]") % - toString(i) % - pretty(i, c->postState()) % - toString(formatBalance(c->balanceAt(i))) % - toString((unsigned)c->countAt(i, PendingBlock)); - mvwaddnstr(contractswin, cc++, x, s.str().c_str(), qwidth); - if (cc > qheight - 2) - break; - } - for (auto const& i: acs) - if (c->codeAt(i, PendingBlock).empty()) + // Peers + y = 1; + for (PeerSessionInfo const& i: web3.peers()) { - auto s = boost::format("%1%%2% : %3% [%4%]") % - toString(i) % - pretty(i, c->postState()) % - toString(formatBalance(c->balanceAt(i))) % - toString((unsigned)c->countAt(i, PendingBlock)); - mvwaddnstr(addswin, y++, x, s.str().c_str(), width / 2 - 4); - if (y > height * 3 / 5 - 4) + auto s = boost::format("%1% ms - %2%:%3% - %4%") % + toString(chrono::duration_cast(i.lastPing).count()) % + i.host % + toString(i.port) % + i.clientVersion; + mvwaddnstr(peerswin, y++, x, s.str().c_str(), qwidth); + if (y > height * 2 / 5 - 4) break; } - - // Peers - y = 1; - for (PeerSessionInfo const& i: web3.peers()) - { - auto s = boost::format("%1% ms - %2%:%3% - %4%") % - toString(chrono::duration_cast(i.lastPing).count()) % - i.host % - toString(i.port) % - i.clientVersion; - mvwaddnstr(peerswin, y++, x, s.str().c_str(), qwidth); - if (y > height * 2 / 5 - 4) - break; } box(consolewin, 0, 0); @@ -1058,31 +1165,41 @@ int main(int argc, char** argv) // Balance stringstream ssb; - u256 balance = c->balanceAt(us.address()); - ssb << "Balance: " << formatBalance(balance); + u256 balance; + if (c) + balance = c->balanceAt(us.address()); + ssb << "Balance: "; + if (c) + ssb << formatBalance(balance); mvwprintw(consolewin, 0, x, ssb.str().c_str()); // Block mvwprintw(blockswin, 0, x, "Block # "); - unsigned n = c->blockChain().details().number; - mvwprintw(blockswin, 0, 10, toString(n).c_str()); + if (c) { + unsigned n = c->blockChain().details().number; + mvwprintw(blockswin, 0, 10, toString(n).c_str()); + } // Pending - string pc; - pc = "Pending: " + toString(c->pending().size()); - mvwprintw(pendingwin, 0, x, pc.c_str()); + stringstream pc; + pc << "Pending: "; + if (c) + pc << toString(c->pending().size()); + else + pc << 0; + mvwprintw(pendingwin, 0, x, pc.str().c_str()); // Contracts - string sc = "Contracts: "; - sc += toString(cc - 1); - mvwprintw(contractswin, 0, x, sc.c_str()); + stringstream sc; + sc << "Contracts: " << cc - 1; + mvwprintw(contractswin, 0, x, sc.str().c_str()); // Peers mvwprintw(peerswin, 0, x, "Peers: "); mvwprintw(peerswin, 0, 9, toString(web3.peers().size()).c_str()); // Mining flag - if (c->isMining()) + if (c && c->isMining()) { mvwprintw(consolewin, qheight - 1, width / 4 - 11, "Mining ON"); dev::eth::MineProgress p = c->miningProgress(); @@ -1095,9 +1212,9 @@ int main(int argc, char** argv) wmove(consolewin, 1, x); // Addresses - string ac; - ac = "Addresses: " + toString(acs.size()); - mvwprintw(addswin, 0, x, ac.c_str()); + stringstream ac; + ac << "Addresses: " << ca; + mvwprintw(addswin, 0, x, ac.str().c_str()); wrefresh(consolewin); From afa5971d0624cba9bb926f9d02d68b1e69997d0c Mon Sep 17 00:00:00 2001 From: caktux Date: Thu, 26 Mar 2015 04:14:59 -0400 Subject: [PATCH 093/228] add missing commands to neth --- neth/main.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/neth/main.cpp b/neth/main.cpp index 7af96eb08..aa10c8a6f 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -127,6 +127,10 @@ void interactiveHelp() << " send Execute a given transaction with current secret." << endl << " contract Create a new contract with current secret." << endl << " inspect Dumps a contract to /.evm." << endl + << " verbosity () Gets or sets verbosity level." << endl + << " setblockfees Set the block fee profit in the reference unit e.g. ¢ (Default: 15)" << endl + << " setetherprice

Resets the ether price." << endl + << " setpriority

Resets the transaction priority." << endl << " reset Resets ncurses windows" << endl << " exit Exits the application." << endl; } @@ -711,6 +715,42 @@ int main(int argc, char** argv) iss >> enable; c->setForceMining(isTrue(enable)); } + else if (c && cmd == "setblockfees") + { + iss >> blockFees; + gasPricer->setRefBlockFees(u256(blockFees * 1000)); + cout << "Block fees: " << blockFees << endl; + } + else if (c && cmd == "setetherprice") + { + iss >> etherPrice; + gasPricer->setRefPrice(u256(double(ether / 1000) / etherPrice)); + cout << "ether Price: " << etherPrice << endl; + } + else if (c && cmd == "setpriority") + { + string m; + iss >> m; + boost::to_lower(m); + if (m == "lowest") + priority = TransactionPriority::Lowest; + else if (m == "low") + priority = TransactionPriority::Low; + else if (m == "medium" || m == "mid" || m == "default" || m == "normal") + priority = TransactionPriority::Medium; + else if (m == "high") + priority = TransactionPriority::High; + else if (m == "highest") + priority = TransactionPriority::Highest; + else + try { + priority = (TransactionPriority)(max(0, min(100, stoi(m))) * 8 / 100); + } + catch (...) { + cerr << "Unknown priority: " << m << endl; + } + cout << "Priority: " << (int)priority << "/8" << endl; + } else if (cmd == "verbosity") { if (iss.peek() != -1) @@ -815,6 +855,8 @@ int main(int argc, char** argv) stringstream ssp; ssp << fields[2]; ssp >> gasPrice; + if (!gasPrice) + gasPrice = gasPricer->bid(priority); string sechex = fields[4]; string sdata = fields[5]; cnote << "Data:"; From 2966882c97a9f556bcbee7790b79b69156bbcf4d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 26 Mar 2015 10:19:14 +0100 Subject: [PATCH 094/228] Fixes #1415 --- eth/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/eth/main.cpp b/eth/main.cpp index 03c54a5b1..bc219c1b3 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -88,8 +88,10 @@ void interactiveHelp() << " send Execute a given transaction with current secret." << endl << " contract Create a new contract with current secret." << endl << " peers List the peers that are connected" << endl +#if ETH_FATDB << " listaccounts List the accounts on the network." << endl << " listcontracts List the contracts on the network." << endl +#endif << " setsecret Set the secret to the hex secret key." < Set the coinbase (mining payout) address." < Export the config (.RLP) to the path provided." <addresses(); @@ -707,6 +710,7 @@ int main(int argc, char** argv) cout << ss << endl; } } +#endif else if (c && cmd == "send") { if (iss.peek() != -1) From 99e6074c2fedc3ecd7732f93eb00d1df1e7e34a0 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 26 Mar 2015 10:20:27 +0100 Subject: [PATCH 095/228] Whitespace fixing. --- eth/main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/eth/main.cpp b/eth/main.cpp index bc219c1b3..e4234cee2 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -92,10 +92,10 @@ void interactiveHelp() << " listaccounts List the accounts on the network." << endl << " listcontracts List the contracts on the network." << endl #endif - << " setsecret Set the secret to the hex secret key." < Set the coinbase (mining payout) address." < Export the config (.RLP) to the path provided." < Import the config (.RLP) from the path provided." < Set the secret to the hex secret key." << endl + << " setaddress Set the coinbase (mining payout) address." << endl + << " exportconfig Export the config (.RLP) to the path provided." << endl + << " importconfig Import the config (.RLP) from the path provided." << endl << " inspect Dumps a contract to /.evm." << endl << " dumptrace Dumps a transaction trace" << endl << "to . should be one of pretty, standard, standard+." << endl << " dumpreceipt Dumps a transation receipt." << endl From 98af1b5cd00b4b77118956e9165d9108b998c90e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 26 Mar 2015 10:23:06 +0100 Subject: [PATCH 096/228] Typo fix. --- eth/main.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/eth/main.cpp b/eth/main.cpp index e4234cee2..e0cf76193 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -77,7 +77,6 @@ void interactiveHelp() << " setetherprice

Resets the ether price." << endl << " setpriority

Resets the transaction priority." << endl << " minestart Starts mining." << endl - << " minestart Starts mining." << endl << " minestop Stops mining." << endl << " mineforce Forces mining, even when there are no transactions." << endl << " address Gives the current address." << endl From 584168a8d734862a072cb6800777177028b809f1 Mon Sep 17 00:00:00 2001 From: caktux Date: Thu, 26 Mar 2015 06:10:12 -0400 Subject: [PATCH 097/228] fix #1415 for neth --- neth/main.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/neth/main.cpp b/neth/main.cpp index aa10c8a6f..a844da92d 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -1152,7 +1152,7 @@ int main(int argc, char** argv) break; } - +#if ETH_FATDB // Contracts and addresses y = 1; auto acs = c->addresses(); @@ -1181,6 +1181,10 @@ int main(int argc, char** argv) if (y > height * 3 / 5 - 4) break; } +#else + mvwaddnstr(contractswin, 1, x, "build with ETH_FATDB to list contracts", qwidth); + mvwaddnstr(addswin, 1, x, "build with ETH_FATDB to list addresses", width / 2 - 4); +#endif // Peers y = 1; From 8b14b4f4d19ac5137efadc747c17f6d941517cc1 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Thu, 26 Mar 2015 14:11:24 +0100 Subject: [PATCH 098/228] two more tests style fixes --- libsolidity/AST.cpp | 2 +- libsolidity/AST.h | 2 +- test/SolidityNameAndTypeResolution.cpp | 38 +++++++++++++++++++++++--- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 9e81c2cb2..461c3d0ca 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -431,7 +431,7 @@ void EventDefinition::checkTypeRequirements() if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); if (!var->getType()->externalType()) - BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for Events")); + BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed as event parameter type.")); } if (numIndexed > 3) BOOST_THROW_EXCEPTION(createTypeError("More than 3 indexed arguments for event.")); diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 0d3ef857a..abcae83c5 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -422,7 +422,7 @@ public: void checkTypeRequirements(); /// @returns the external signature of the function - /// That consists of the name of the function followed by the types(external types if isExternalCall) of the + /// That consists of the name of the function followed by the types (external types if isExternalCall) of the /// arguments separated by commas all enclosed in parentheses without any spaces. std::string externalSignature(bool isExternalCall = false) const; diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 75a912244..6b9916549 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -418,9 +418,23 @@ BOOST_AUTO_TEST_CASE(function_external_types) } } -BOOST_AUTO_TEST_CASE(function_external_call_conversion) +BOOST_AUTO_TEST_CASE(function_external_call_allowed_conversion) { - char const* sourceCode = R"( + char const* text = R"( + contract C {} + contract Test { + function externalCall() { + C arg; + this.g(arg); + } + function g (C c) external {} + })"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + +BOOST_AUTO_TEST_CASE(function_external_call_not_allowed_conversion) +{ + char const* text = R"( contract C {} contract Test { function externalCall() { @@ -429,10 +443,26 @@ BOOST_AUTO_TEST_CASE(function_external_call_conversion) } function g (C c) external {} })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + +BOOST_AUTO_TEST_CASE(function_internal_allowed_conversion) +{ + char const* text = R"( + contract C { + uint a; + } + contract Test { + C a; + function g (C c) {} + function internalCall() { + g(a); + } + })"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); } -BOOST_AUTO_TEST_CASE(function_internal_call_conversion) +BOOST_AUTO_TEST_CASE(function_internal_not_allowed_conversion) { char const* text = R"( contract C { From 568146724726cde36f769c7dfefdb36ed835754b Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 26 Mar 2015 14:42:28 +0100 Subject: [PATCH 099/228] - Hide mapping type from debugger. - Better highlighting if debugger in external source. - Always use pendingBlock when making call and transact MixClient. - Compilation errors not kept in logs history. --- mix/ClientModel.cpp | 11 ++++++----- mix/DebuggingStateWrapper.h | 6 ++++-- mix/MixClient.cpp | 16 ++++++---------- mix/MixClient.h | 3 --- mix/qml/CodeEditorView.qml | 21 ++++++++++++++++++++- mix/qml/LogsPane.qml | 25 ------------------------- mix/qml/StatusPane.qml | 1 - 7 files changed, 36 insertions(+), 47 deletions(-) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index b490661ae..082ff59e6 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -350,10 +350,6 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) CompiledContract const* contract = contracts[s.codeIndex]; AssemblyItem const& instruction = codeItems[s.codeIndex][instructionIndex]; - std::stringstream str; - str << instruction.getLocation().sourceName; - qDebug() << QString::fromStdString(str.str()); - if (instruction.type() == dev::eth::Push && !instruction.data()) { //register new local variable initialization @@ -380,8 +376,11 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) for(auto l: solLocals) if (l.first < (int)s.stack.size()) { + if (l.second->type()->name().startsWith("mapping")) + break; //mapping type not yet managed localDeclarations.push_back(QVariant::fromValue(l.second)); localValues[l.second->name()] = formatValue(l.second->type()->type(), s.stack[l.first]); + } locals["variables"] = localDeclarations; locals["values"] = localValues; @@ -404,6 +403,8 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) storageDec = new QVariableDeclaration(debugData, storageIter.value().name.toStdString(), storageIter.value().type); storageDeclarations[storageDec->name()] = storageDec; } + if (storageDec->type()->name().startsWith("mapping")) + break; //mapping type not yet managed storageDeclarationList.push_back(QVariant::fromValue(storageDec)); storageValues[storageDec->name()] = formatValue(storageDec->type()->type(), st.second); } @@ -412,7 +413,7 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) storage["values"] = storageValues; prevInstructionIndex = instructionIndex; - solState = new QSolState(debugData, std::move(storage), std::move(solCallStack), std::move(locals), instruction.getLocation().start, instruction.getLocation().end); + solState = new QSolState(debugData, std::move(storage), std::move(solCallStack), std::move(locals), instruction.getLocation().start, instruction.getLocation().end, QString::fromUtf8(instruction.getLocation().sourceName->c_str())); } states.append(QVariant::fromValue(new QMachineState(debugData, instructionIndex, s, codes[s.codeIndex], data[s.dataIndex], solState))); diff --git a/mix/DebuggingStateWrapper.h b/mix/DebuggingStateWrapper.h index b9eea9365..37bc194fb 100644 --- a/mix/DebuggingStateWrapper.h +++ b/mix/DebuggingStateWrapper.h @@ -65,10 +65,11 @@ class QSolState: public QObject Q_PROPERTY(QVariantMap locals MEMBER m_locals CONSTANT) Q_PROPERTY(int start MEMBER m_start CONSTANT) Q_PROPERTY(int end MEMBER m_end CONSTANT) + Q_PROPERTY(QString sourceName MEMBER m_sourceName CONSTANT) public: - QSolState(QObject* _parent, QVariantMap&& _storage, QVariantList&& _callStack, QVariantMap&& _locals, int _start, int _end): - QObject(_parent), m_storage(_storage), m_callStack(_callStack), m_locals(_locals), m_start(_start), m_end(_end) + QSolState(QObject* _parent, QVariantMap&& _storage, QVariantList&& _callStack, QVariantMap&& _locals, int _start, int _end, QString _sourceName): + QObject(_parent), m_storage(_storage), m_callStack(_callStack), m_locals(_locals), m_start(_start), m_end(_end), m_sourceName(_sourceName) { } private: @@ -77,6 +78,7 @@ private: QVariantMap m_locals; int m_start; int m_end; + QString m_sourceName; }; /** diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index 47983822a..19d946d01 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -235,15 +235,10 @@ ExecutionResult MixClient::execution(unsigned _index) const return m_executions.at(_index); } -State MixClient::asOf(BlockNumber _h) const +State MixClient::asOf(h256 const& _block) const { ReadGuard l(x_state); - if (_h == PendingBlock) - return m_startState; - else if (_h == LatestBlock) - return m_state; - else - return State(m_stateDB, bc(), bc().numberHash(_h)); + return State(m_stateDB, bc(), _block); } void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) @@ -266,8 +261,8 @@ Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes cons dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) { - - State temp = asOf(_blockNumber); + (void)_blockNumber; + State temp = asOf(eth::PendingBlock); u256 n = temp.transactionsFrom(toAddress(_secret)); Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); bytes rlp = t.rlp(); @@ -278,11 +273,12 @@ dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _ dev::eth::ExecutionResult MixClient::create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) { + (void)_blockNumber; u256 n; State temp; { ReadGuard lr(x_state); - temp = asOf(_blockNumber); + temp = asOf(eth::PendingBlock); n = temp.transactionsFrom(toAddress(_secret)); } Transaction t(_value, _gasPrice, _gas, _data, n, _secret); diff --git a/mix/MixClient.h b/mix/MixClient.h index a25657905..179b445ac 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -88,9 +88,6 @@ protected: private: void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call); void noteChanged(h256Set const& _filters); - dev::eth::State asOf(eth::BlockNumber _block) const; - MixBlockChain& bc() { return *m_bc; } - MixBlockChain const& bc() const { return *m_bc; } std::vector m_userAccounts; eth::State m_state; diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml index 4d8559200..01aa0955f 100644 --- a/mix/qml/CodeEditorView.qml +++ b/mix/qml/CodeEditorView.qml @@ -68,9 +68,28 @@ Item { } function highlightExecution(documentId, location) { + var editor = getEditor(documentId); if (editor) - editor.highlightExecution(location); + { + if (documentId !== location.sourceName) + findAndHightlight(location.start, location.end, location.sourceName) + else + editor.highlightExecution(location); + } + } + + // Execution is not in the current document. Try: + // Open targeted document and hightlight (TODO) or + // Relevant hightlighting on the current document + function findAndHightlight(start, end, sourceName) + { + var editor = getEditor(currentDocumentId); + if (editor) + { + var sourceIndex = editor.getText().indexOf(sourceName); + highlightExecution(currentDocumentId, { start: sourceIndex, end: sourceIndex + sourceName.length, sourceName: currentDocumentId }); + } } function editingContract() { diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml index b40dfc4c7..3601483a0 100644 --- a/mix/qml/LogsPane.qml +++ b/mix/qml/LogsPane.qml @@ -153,31 +153,6 @@ Rectangle } } - ToolButton { - id: compilationButton - checkable: true - height: LogsPaneStyle.generic.layout.headerButtonHeight - anchors.verticalCenter: parent.verticalCenter - checked: false - onCheckedChanged: { - proxyModel.toogleFilter("compilation") - } - tooltip: qsTr("Compilation") - style: - ButtonStyle { - label: - Item { - DefaultLabel { - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-3) - color: "#5391d8" - anchors.centerIn: parent - text: qsTr("Compilation") - } - } - } - } - DefaultTextField { id: searchBox diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index 6d4b5e7e1..1e440b3a8 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -24,7 +24,6 @@ Rectangle { var errorInfo = ErrorLocationFormater.extractErrorInfo(message, true); status.text = errorInfo.errorLocation + " " + errorInfo.errorDetail; debugImg.state = ""; - errorMessage(status.text, "Compilation"); } debugRunActionIcon.enabled = codeModel.hasContract; } From 55e34ddadecca5f8d24f6f3b14a080a3f4129d04 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 26 Mar 2015 15:22:16 +0100 Subject: [PATCH 100/228] - warn user if source not available --- mix/qml/CodeEditorView.qml | 11 ++++------- mix/qml/WebCodeEditor.qml | 5 +++++ mix/qml/html/codeeditor.js | 16 ++++++++++++++++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml index 01aa0955f..f948f882c 100644 --- a/mix/qml/CodeEditorView.qml +++ b/mix/qml/CodeEditorView.qml @@ -67,8 +67,8 @@ Item { return null; } - function highlightExecution(documentId, location) { - + function highlightExecution(documentId, location) + { var editor = getEditor(documentId); if (editor) { @@ -81,15 +81,12 @@ Item { // Execution is not in the current document. Try: // Open targeted document and hightlight (TODO) or - // Relevant hightlighting on the current document + // Warn user that file is not available function findAndHightlight(start, end, sourceName) { var editor = getEditor(currentDocumentId); if (editor) - { - var sourceIndex = editor.getText().indexOf(sourceName); - highlightExecution(currentDocumentId, { start: sourceIndex, end: sourceIndex + sourceName.length, sourceName: currentDocumentId }); - } + editor.showWarning(qsTr("Currently debugging in " + sourceName + ". Source not available.")); } function editingContract() { diff --git a/mix/qml/WebCodeEditor.qml b/mix/qml/WebCodeEditor.qml index 7e5945fc7..3313cd2dd 100644 --- a/mix/qml/WebCodeEditor.qml +++ b/mix/qml/WebCodeEditor.qml @@ -45,6 +45,11 @@ Item { editorBrowser.runJavaScript("highlightExecution(" + location.start + "," + location.end + ")"); } + function showWarning(content) { + if (initialized) + editorBrowser.runJavaScript("showWarning('" + content + "')"); + } + function getBreakpoints() { return currentBreakpoints; } diff --git a/mix/qml/html/codeeditor.js b/mix/qml/html/codeeditor.js index daf1286d8..da87e63e0 100644 --- a/mix/qml/html/codeeditor.js +++ b/mix/qml/html/codeeditor.js @@ -134,6 +134,8 @@ highlightExecution = function(start, end) { executionMark.clear(); if (start === 0 && end + 1 === editor.getValue().length) return; // Do not hightlight the whole document. + if (debugWarning) + debugWarning.clear(); executionMark = editor.markText(editor.posFromIndex(start), editor.posFromIndex(end), { className: "CodeMirror-exechighlight" }); } @@ -148,6 +150,20 @@ isClean = function() return editor.isClean(changeId); } +var debugWarning = null; +showWarning = function(content) +{ + if (executionMark) + executionMark.clear(); + if (debugWarning) + debugWarning.clear(); + var node = document.createElement("div"); + node.id = "annotation" + node.innerHTML = content; + node.className = "CodeMirror-errorannotation-context"; + debugWarning = editor.addLineWidget(0, node, { coverGutter: false, above: true }); +} + var annotation = null; var compilationCompleteBool = true; compilationError = function(line, column, content) From 6bdb3ed08008e1457acdab6f929d91c451bbde96 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 26 Mar 2015 15:25:46 +0100 Subject: [PATCH 101/228] small changes --- mix/ClientModel.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 082ff59e6..a49ee3661 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -380,7 +380,6 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) break; //mapping type not yet managed localDeclarations.push_back(QVariant::fromValue(l.second)); localValues[l.second->name()] = formatValue(l.second->type()->type(), s.stack[l.first]); - } locals["variables"] = localDeclarations; locals["values"] = localValues; From bddc4f35a4c049d0761e3b1149b9aa7ce3958dc4 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 26 Mar 2015 15:46:52 +0100 Subject: [PATCH 102/228] - move autocompletion color to solarized lib --- mix/qml/html/cm/show-hint.css | 24 ------------------------ mix/qml/html/cm/solarized.css | 12 ++++++------ 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/mix/qml/html/cm/show-hint.css b/mix/qml/html/cm/show-hint.css index 82b99d39a..479acf024 100644 --- a/mix/qml/html/cm/show-hint.css +++ b/mix/qml/html/cm/show-hint.css @@ -47,30 +47,6 @@ color: white !important; } -.solcurrency { - color: #b58900; -} - -.solkeywords { - color: #cb4b16; -} - -.solstdcontract { - color: #d33682; -} - -.soltime { - color: #268bd2; -} - -.soltypes { - color: #2aa198; -} - -.solMisc { - color: #859900; -} - .solToken { float: left; } diff --git a/mix/qml/html/cm/solarized.css b/mix/qml/html/cm/solarized.css index f07b8b43b..b44269e69 100644 --- a/mix/qml/html/cm/solarized.css +++ b/mix/qml/html/cm/solarized.css @@ -16,14 +16,14 @@ http://ethanschoonover.com/solarized/img/solarized-palette.png .solarized.base1 { color: #93a1a1; } .solarized.base2 { color: #eee8d5; } .solarized.base3 { color: #fdf6e3; } -.solarized.solar-yellow { color: #b58900; } -.solarized.solar-orange { color: #cb4b16; } +.solarized.solar-yellow, .solcurrency { color: #b58900; } +.solarized.solar-orange, .solkeywords { color: #cb4b16; } .solarized.solar-red { color: #dc322f; } -.solarized.solar-magenta { color: #d33682; } +.solarized.solar-magenta, .solstdcontract { color: #d33682; } .solarized.solar-violet { color: #6c71c4; } -.solarized.solar-blue { color: #268bd2; } -.solarized.solar-cyan { color: #2aa198; } -.solarized.solar-green { color: #859900; } +.solarized.solar-blue, .soltime { color: #268bd2; } +.solarized.solar-cyan, .soltypes { color: #2aa198; } +.solarized.solar-green, .solMisc { color: #859900; } /* Color scheme for code-mirror */ From 015c1681fb91591bf977ae2a0507616018194448 Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 26 Mar 2015 16:21:25 +0100 Subject: [PATCH 103/228] adopt node IP address from udp header when ping.ipAddress isn't public --- libp2p/Host.cpp | 8 ++++---- libp2p/NodeTable.cpp | 42 ++++++++++++++++++++++++++++-------------- test/net.cpp | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 18 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index b946d9b3e..41c2fbb77 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -592,12 +592,12 @@ void Host::startedWorking() m_run = true; } - // try to open acceptor (todo: ipv6) - m_listenPort = Network::tcp4Listen(m_tcp4Acceptor, m_netPrefs.listenPort); - - // start capability threads + // start capability threads (ready for incoming connections) for (auto const& h: m_capabilities) h.second->onStarting(); + + // try to open acceptor (todo: ipv6) + m_listenPort = Network::tcp4Listen(m_tcp4Acceptor, m_netPrefs.listenPort); // determine public IP, but only if we're able to listen for connections // todo: GUI when listen is unavailable in UI diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 7fe531762..7bdaf3088 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -84,11 +84,14 @@ shared_ptr NodeTable::addNode(Node const& _node) return move(shared_ptr()); } - // ping address if nodeid is empty + // ping address to recover nodeid if nodeid is empty if (!_node.id) { clog(NodeTableConnect) << "Sending public key discovery Ping to" << _node.endpoint.udp << "(Advertising:" << m_node.endpoint.udp << ")"; - m_pubkDiscoverPings[_node.endpoint.udp.address()] = std::chrono::steady_clock::now(); + { + Guard l(x_pubkDiscoverPings); + m_pubkDiscoverPings[_node.endpoint.udp.address()] = std::chrono::steady_clock::now(); + } PingNode p(_node.endpoint.udp, m_node.endpoint.udp.address().to_string(), m_node.endpoint.udp.port()); p.sign(m_secret); m_socketPointer->send(p); @@ -101,7 +104,11 @@ shared_ptr NodeTable::addNode(Node const& _node) return m_nodes[_node.id]; } - shared_ptr ret(new NodeEntry(m_node, _node.id, NodeIPEndpoint(_node.endpoint.udp, _node.endpoint.tcp))); + // TODO: SECURITY - Temporary until packets are updated. + NodeIPEndpoint ep(_node.endpoint.udp, _node.endpoint.tcp); + if (!isPublicAddress(ep.tcp.address())) + ep.tcp.address(_node.endpoint.udp.address()); + shared_ptr ret(new NodeEntry(m_node, _node.id, ep)); m_nodes[_node.id] = ret; PingNode p(_node.endpoint.udp, m_node.endpoint.udp.address().to_string(), m_node.endpoint.udp.port()); p.sign(m_secret); @@ -315,9 +322,13 @@ void NodeTable::noteActiveNode(Public const& _pubk, bi::udp::endpoint const& _en { clog(NodeTableConnect) << "Noting active node:" << _pubk.abridged() << _endpoint.address().to_string() << ":" << _endpoint.port(); - // update udp endpoint + // TODO: SECURITY - Temporary until packets are updated. + // update udp endpoint and override tcp endpoint if tcp endpoint isn't public + // (for the rare case where NAT port is mapped but node->endpoint.udp.address(_endpoint.address()); node->endpoint.udp.port(_endpoint.port()); + if (!isPublicAddress(node->endpoint.tcp.address())) + node->endpoint.tcp.address(_endpoint.address()); shared_ptr contested; { @@ -399,7 +410,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes bytesConstRef signedBytes(hashedBytes.cropped(Signature::size, hashedBytes.size() - Signature::size)); - // todo: verify sig via known-nodeid and MDC, or, do ping/pong auth if node/endpoint is unknown/untrusted + // todo: verify sig via known-nodeid and MDC bytesConstRef sigBytes(_packet.cropped(h256::size, Signature::size)); Public nodeid(dev::recover(*(Signature const*)sigBytes.data(), sha3(signedBytes))); @@ -430,8 +441,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes dropNode(n); if (auto n = nodeEntry(it->first.first)) - if (m_nodeEventHandler && n->pending) - n->pending = false; + n->pending = false; it = m_evictions.erase(it); } @@ -441,14 +451,18 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes { if (auto n = nodeEntry(nodeid)) n->pending = false; + else if (m_pubkDiscoverPings.count(_from.address())) + { + { + Guard l(x_pubkDiscoverPings); + m_pubkDiscoverPings.erase(_from.address()); + } + if (!haveNode(nodeid)) + addNode(nodeid, _from, bi::tcp::endpoint(_from.address(), _from.port())); + } + else + return; // unsolicited pong; don't note node as active } - else if (m_pubkDiscoverPings.count(_from.address())) - { - m_pubkDiscoverPings.erase(_from.address()); - addNode(nodeid, _from, bi::tcp::endpoint(_from.address(), _from.port())); - } - else - return; // unsolicited pong; don't note node as active break; } diff --git a/test/net.cpp b/test/net.cpp index a5f973450..ec1efb360 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -145,6 +145,40 @@ public: bool success = false; }; +BOOST_AUTO_TEST_CASE(isIPAddressType) +{ + string wildcard = "0.0.0.0"; + BOOST_REQUIRE(bi::address::from_string(wildcard).is_unspecified()); + + string empty = ""; + BOOST_REQUIRE_THROW(bi::address::from_string(empty).is_unspecified(), std::exception); + + string publicAddress192 = "192.169.0.0"; + BOOST_REQUIRE(isPublicAddress(publicAddress192)); + BOOST_REQUIRE(!isPrivateAddress(publicAddress192)); + BOOST_REQUIRE(!isLocalHostAddress(publicAddress192)); + + string publicAddress172 = "172.32.0.0"; + BOOST_REQUIRE(isPublicAddress(publicAddress172)); + BOOST_REQUIRE(!isPrivateAddress(publicAddress172)); + BOOST_REQUIRE(!isLocalHostAddress(publicAddress172)); + + string privateAddress192 = "192.168.1.0"; + BOOST_REQUIRE(isPrivateAddress(privateAddress192)); + BOOST_REQUIRE(!isPublicAddress(privateAddress192)); + BOOST_REQUIRE(!isLocalHostAddress(privateAddress192)); + + string privateAddress172 = "172.16.0.0"; + BOOST_REQUIRE(isPrivateAddress(privateAddress172)); + BOOST_REQUIRE(!isPublicAddress(privateAddress172)); + BOOST_REQUIRE(!isLocalHostAddress(privateAddress172)); + + string privateAddress10 = "10.0.0.0"; + BOOST_REQUIRE(isPrivateAddress(privateAddress10)); + BOOST_REQUIRE(!isPublicAddress(privateAddress10)); + BOOST_REQUIRE(!isLocalHostAddress(privateAddress10)); +} + BOOST_AUTO_TEST_CASE(v2PingNodePacket) { // test old versino of pingNode packet w/new From 89b484e3efb062fbbbdc43cef9e7ece13013d6d3 Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 26 Mar 2015 16:25:54 +0100 Subject: [PATCH 104/228] better handshake error logging, esp when remote disconnects --- libp2p/RLPxHandshake.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index 16bfe0fb1..e311c615a 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -131,7 +131,14 @@ void RLPXHandshake::readAck() void RLPXHandshake::error() { - clog(NetConnect) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Failed)"; + m_idleTimer.cancel(); + + auto connected = m_socket->isConnected(); + if (connected && !m_socket->remoteEndpoint().address().is_unspecified()) + clog(NetConnect) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Failed)"; + else + clog(NetConnect) << "Handshake Failed (Connection reset by peer)"; + m_socket->close(); if (m_io != nullptr) delete m_io; @@ -140,7 +147,10 @@ void RLPXHandshake::error() void RLPXHandshake::transition(boost::system::error_code _ech) { if (_ech || m_nextState == Error || m_cancel) + { + clog(NetConnect) << "Handshake Failed (I/O Error:" << _ech.message() << ")"; return error(); + } auto self(shared_from_this()); if (m_nextState == New) @@ -265,7 +275,8 @@ void RLPXHandshake::transition(boost::system::error_code _ech) { if (!_ec) { - clog(NetWarn) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Timeout)"; + if (!m_socket->remoteEndpoint().address().is_unspecified()) + clog(NetWarn) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Timeout)"; cancel(); } }); From c17edbff0a7c5e01b1b4a2a9d51112a9a6127a66 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 26 Mar 2015 16:41:23 +0100 Subject: [PATCH 105/228] - bug fix #1386 --- mix/ClientModel.cpp | 15 ++++++++++++--- mix/qml/StatusPane.qml | 4 ++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index a49ee3661..6ba7e54f9 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -242,7 +242,12 @@ void ClientModel::executeSequence(std::vector const& _seque break; } if (!f) - BOOST_THROW_EXCEPTION(FunctionNotFoundException() << FunctionName(transaction.functionId.toStdString())); + { + emit runFailed("Function '" + transaction.functionId + tr("' not found. Please check transactions or the contract code.")); + m_running = false; + emit runStateChanged(); + return; + } if (!transaction.functionId.isEmpty()) encoder.encode(f); for (QVariableDeclaration const* p: f->parametersList()) @@ -269,7 +274,12 @@ void ClientModel::executeSequence(std::vector const& _seque { auto contractAddressIter = m_contractAddresses.find(transaction.contractId); if (contractAddressIter == m_contractAddresses.end()) - BOOST_THROW_EXCEPTION(dev::Exception() << dev::errinfo_comment("Contract not deployed: " + transaction.contractId.toStdString())); + { + emit runFailed("Contract '" + transaction.contractId + tr(" not deployed.") + "' " + tr(" Cannot call ") + transaction.functionId); + m_running = false; + emit runStateChanged(); + return; + } callContract(contractAddressIter->second, encoder.encodedData(), transaction); } } @@ -283,7 +293,6 @@ void ClientModel::executeSequence(std::vector const& _seque std::cerr << boost::current_exception_diagnostic_information(); emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); } - catch(std::exception const& e) { std::cerr << boost::current_exception_diagnostic_information(); diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index 1e440b3a8..70a4b1d30 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -76,9 +76,9 @@ Rectangle { function format(_message) { var formatted = _message.match(/(?:)/); - if (formatted === null) + if (formatted) formatted = _message.match(/(?:)/); - if (formatted.length > 1) + if (formatted && formatted.length > 1) formatted = formatted[1]; else return _message; From a5d2567e811231cb583106abdeef5592fe33b9e7 Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 26 Mar 2015 16:46:26 +0100 Subject: [PATCH 106/228] only override tcp address if udp address is public. allow remotes which advertise 0.0.0.0. --- libp2p/NodeTable.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 7bdaf3088..3cba9ec0f 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -70,17 +70,11 @@ shared_ptr NodeTable::addNode(Public const& _pubk, bi::udp::endpoint shared_ptr NodeTable::addNode(Node const& _node) { - if (_node.endpoint.udp.address().to_string() == "0.0.0.0" || _node.endpoint.tcp.address().to_string() == "0.0.0.0") + // re-enable tcp checks when NAT hosts are handled by discover + // we handle when tcp endpoint is 0 below + if (_node.endpoint.udp.address().to_string() == "0.0.0.0") { - string ptype; - if (_node.endpoint.udp.address().to_string() != "0.0.0.0") - ptype = "TCP"; - else if (_node.endpoint.tcp.address().to_string() != "0.0.0.0") - ptype = "UDP"; - else - ptype = "TCP,UDP"; - - clog(NodeTableWarn) << "addNode Failed. Invalid" << ptype << "address 0.0.0.0 for" << _node.id.abridged(); + clog(NodeTableWarn) << "addNode Failed. Invalid UDP address 0.0.0.0 for" << _node.id.abridged(); return move(shared_ptr()); } @@ -106,7 +100,7 @@ shared_ptr NodeTable::addNode(Node const& _node) // TODO: SECURITY - Temporary until packets are updated. NodeIPEndpoint ep(_node.endpoint.udp, _node.endpoint.tcp); - if (!isPublicAddress(ep.tcp.address())) + if (!isPublicAddress(ep.tcp.address()) && isPublicAddress(ep.udp.address())) ep.tcp.address(_node.endpoint.udp.address()); shared_ptr ret(new NodeEntry(m_node, _node.id, ep)); m_nodes[_node.id] = ret; @@ -327,7 +321,7 @@ void NodeTable::noteActiveNode(Public const& _pubk, bi::udp::endpoint const& _en // (for the rare case where NAT port is mapped but node->endpoint.udp.address(_endpoint.address()); node->endpoint.udp.port(_endpoint.port()); - if (!isPublicAddress(node->endpoint.tcp.address())) + if (!isPublicAddress(node->endpoint.tcp.address()) && isPublicAddress(node->endpoint.udp.address())) node->endpoint.tcp.address(_endpoint.address()); shared_ptr contested; From e1ad2cb579488720499211b6e9c0881b279b93fb Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 23 Mar 2015 16:53:28 +0100 Subject: [PATCH 107/228] Comments and renames. --- libevmcore/Instruction.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevmcore/Instruction.h b/libevmcore/Instruction.h index 07c7b52fd..c869e761d 100644 --- a/libevmcore/Instruction.h +++ b/libevmcore/Instruction.h @@ -237,7 +237,7 @@ enum Tier struct InstructionInfo { std::string name; ///< The name of the instruction. - int additional; ///< Additional items required in memory for this instructions (only for PUSH). + int additional; //a/< Additional items required in memory for this instructions (only for PUSH). int args; ///< Number of items required on the stack for this instruction (and, for the purposes of ret, the number taken from the stack). int ret; ///< Number of items placed (back) on the stack by this instruction, assuming args items were removed. bool sideEffects; ///< false if the only effect on the execution environment (apart from gas usage) is a change to a topmost segment of the stack From ea6d5c2c98dcdb6babd04f0715a58992fcf6e9f6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Mar 2015 18:18:25 +0100 Subject: [PATCH 108/228] Refactoring: Extract equivalence class container. --- libevmcore/CommonSubexpressionEliminator.cpp | 154 +++++-------------- libevmcore/CommonSubexpressionEliminator.h | 52 +++---- libevmcore/ExpressionClasses.cpp | 110 +++++++++++++ libevmcore/ExpressionClasses.h | 70 +++++++++ 4 files changed, 240 insertions(+), 146 deletions(-) create mode 100644 libevmcore/ExpressionClasses.cpp create mode 100644 libevmcore/ExpressionClasses.h diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index aa532b2ff..8102721cc 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -32,17 +32,17 @@ using namespace dev::eth; vector CommonSubexpressionEliminator::getOptimizedItems() { - map initialStackContents; - map targetStackContents; + map initialStackContents; + map targetStackContents; int minHeight = m_stackHeight + 1; if (!m_stackElements.empty()) minHeight = min(minHeight, m_stackElements.begin()->first.first); for (int height = minHeight; height <= max(0, m_stackHeight); ++height) { // make sure it is created - EquivalenceClassId c = getStackElement(height); + ExpressionClasses::Id c = getStackElement(height); if (height <= 0) - initialStackContents[height] = getClass(AssemblyItem(dupInstruction(1 - height))); + initialStackContents[height] = m_expressionClasses.find(AssemblyItem(dupInstruction(1 - height))); if (height <= m_stackHeight) targetStackContents[height] = c; } @@ -50,21 +50,21 @@ vector CommonSubexpressionEliminator::getOptimizedItems() // Debug info: //stream(cout, currentStackContents, targetStackContents); - return CSECodeGenerator().generateCode(initialStackContents, targetStackContents, m_equivalenceClasses); + return CSECodeGenerator(m_expressionClasses).generateCode(initialStackContents, targetStackContents); } ostream& CommonSubexpressionEliminator::stream( ostream& _out, - map _currentStack, - map _targetStack + map _currentStack, + map _targetStack ) const { - auto streamEquivalenceClass = [this](ostream& _out, EquivalenceClassId _id) + auto streamExpressionClass = [this](ostream& _out, ExpressionClasses::Id _id) { - auto const& eqClass = m_equivalenceClasses.at(_id); - _out << " " << _id << ": " << *eqClass.first; + auto const& expr = m_expressionClasses.representative(_id); + _out << " " << _id << ": " << *expr.item; _out << "("; - for (EquivalenceClassId arg: eqClass.second) + for (ExpressionClasses::Id arg: expr.arguments) _out << dec << arg << ","; _out << ")" << endl; }; @@ -75,23 +75,23 @@ ostream& CommonSubexpressionEliminator::stream( for (auto const& it: m_stackElements) { _out << " " << dec << it.first.first << "(" << it.first.second << ") = "; - streamEquivalenceClass(_out, it.second); + streamExpressionClass(_out, it.second); } _out << "Equivalence classes: " << endl; - for (EquivalenceClassId eqClass = 0; eqClass < m_equivalenceClasses.size(); ++eqClass) - streamEquivalenceClass(_out, eqClass); + for (ExpressionClasses::Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass) + streamExpressionClass(_out, eqClass); _out << "Current stack: " << endl; for (auto const& it: _currentStack) { _out << " " << dec << it.first << ": "; - streamEquivalenceClass(_out, it.second); + streamExpressionClass(_out, it.second); } _out << "Target stack: " << endl; for (auto const& it: _targetStack) { _out << " " << dec << it.first << ": "; - streamEquivalenceClass(_out, it.second); + streamExpressionClass(_out, it.second); } return _out; @@ -103,7 +103,7 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) { if (_item.deposit() != 1) BOOST_THROW_EXCEPTION(InvalidDeposit()); - setStackElement(++m_stackHeight, getClass(_item, {})); + setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {})); } else { @@ -121,16 +121,16 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) ); else if (instruction != Instruction::POP) { - vector arguments(info.args); + vector arguments(info.args); for (int i = 0; i < info.args; ++i) arguments[i] = getStackElement(m_stackHeight - i); - setStackElement(m_stackHeight + _item.deposit(), getClass(_item, arguments)); + setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments)); } m_stackHeight += _item.deposit(); } } -void CommonSubexpressionEliminator::setStackElement(int _stackHeight, EquivalenceClassId _class) +void CommonSubexpressionEliminator::setStackElement(int _stackHeight, ExpressionClasses::Id _class) { unsigned nextSequence = getNextStackElementSequence(_stackHeight); m_stackElements[make_pair(_stackHeight, nextSequence)] = _class; @@ -140,8 +140,8 @@ void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _st { if (_stackHeightA == _stackHeightB) BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Swap on same stack elements.")); - EquivalenceClassId classA = getStackElement(_stackHeightA); - EquivalenceClassId classB = getStackElement(_stackHeightB); + ExpressionClasses::Id classA = getStackElement(_stackHeightA); + ExpressionClasses::Id classB = getStackElement(_stackHeightB); unsigned nextSequenceA = getNextStackElementSequence(_stackHeightA); unsigned nextSequenceB = getNextStackElementSequence(_stackHeightB); @@ -149,7 +149,7 @@ void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _st m_stackElements[make_pair(_stackHeightB, nextSequenceB)] = classA; } -EquivalenceClassId CommonSubexpressionEliminator::getStackElement(int _stackHeight) +ExpressionClasses::Id CommonSubexpressionEliminator::getStackElement(int _stackHeight) { // retrieve class by last sequence number unsigned nextSequence = getNextStackElementSequence(_stackHeight); @@ -162,86 +162,8 @@ EquivalenceClassId CommonSubexpressionEliminator::getStackElement(int _stackHeig if (_stackHeight <= -16) BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Stack too deep.")); // This is a special assembly item that refers to elements pre-existing on the initial stack. - m_spareAssemblyItem.push_back(make_shared(dupInstruction(1 - _stackHeight))); - m_equivalenceClasses.push_back(make_pair(m_spareAssemblyItem.back().get(), EquivalenceClassIds())); - return m_stackElements[make_pair(_stackHeight, nextSequence)] = EquivalenceClassId(m_equivalenceClasses.size() - 1); -} - -EquivalenceClassId CommonSubexpressionEliminator::getClass( - const AssemblyItem& _item, - EquivalenceClassIds const& _arguments -) -{ - // TODO: do a clever search, i.e. - // - check for the presence of constants in the argument classes and do arithmetic - // - check whether the two items are equal for a SUB instruction - // - check whether 0 or 1 is in one of the classes for a MUL - - EquivalenceClassIds args = _arguments; - if (SemanticInformation::isCommutativeOperation(_item)) - sort(args.begin(), args.end()); - - //@todo use a better data structure for search here - for (EquivalenceClassId c = 0; c < m_equivalenceClasses.size(); ++c) - { - AssemblyItem const& classItem = *m_equivalenceClasses.at(c).first; - if (classItem != _item) - continue; - - assertThrow( - args.size() == m_equivalenceClasses.at(c).second.size(), - OptimizerException, - "Equal assembly items with different number of arguments." - ); - if (equal(args.begin(), args.end(), m_equivalenceClasses.at(c).second.begin())) - return c; - } - // constant folding - if (_item.type() == Operation && args.size() == 2 && all_of( - args.begin(), - args.end(), - [this](EquivalenceClassId eqc) { return m_equivalenceClasses.at(eqc).first->match(Push); })) - { - auto signextend = [](u256 const& _a, u256 const& _b) -> u256 - { - if (_a >= 31) - return _b; - unsigned testBit = unsigned(_a) * 8 + 7; - u256 mask = (u256(1) << testBit) - 1; - return boost::multiprecision::bit_test(_b, testBit) ? _b | ~mask : _b & mask; - }; - map> const arithmetics = - { - { Instruction::SUB, [](u256 const& _a, u256 const& _b) -> u256 {return _a - _b; } }, - { Instruction::DIV, [](u256 const& _a, u256 const& _b) -> u256 {return _b == 0 ? 0 : _a / _b; } }, - { Instruction::SDIV, [](u256 const& _a, u256 const& _b) -> u256 { return _b == 0 ? 0 : s2u(u2s(_a) / u2s(_b)); } }, - { Instruction::MOD, [](u256 const& _a, u256 const& _b) -> u256 { return _b == 0 ? 0 : _a % _b; } }, - { Instruction::SMOD, [](u256 const& _a, u256 const& _b) -> u256 { return _b == 0 ? 0 : s2u(u2s(_a) % u2s(_b)); } }, - { Instruction::EXP, [](u256 const& _a, u256 const& _b) -> u256 { return (u256)boost::multiprecision::powm(bigint(_a), bigint(_b), bigint(1) << 256); } }, - { Instruction::SIGNEXTEND, signextend }, - { Instruction::LT, [](u256 const& _a, u256 const& _b) -> u256 { return _a < _b ? 1 : 0; } }, - { Instruction::GT, [](u256 const& _a, u256 const& _b) -> u256 { return _a > _b ? 1 : 0; } }, - { Instruction::SLT, [](u256 const& _a, u256 const& _b) -> u256 { return u2s(_a) < u2s(_b) ? 1 : 0; } }, - { Instruction::SGT, [](u256 const& _a, u256 const& _b) -> u256 { return u2s(_a) > u2s(_b) ? 1 : 0; } }, - { Instruction::EQ, [](u256 const& _a, u256 const& _b) -> u256 { return _a == _b ? 1 : 0; } }, - { Instruction::ADD, [](u256 const& _a, u256 const& _b) -> u256 { return _a + _b; } }, - { Instruction::MUL, [](u256 const& _a, u256 const& _b) -> u256 { return _a * _b; } }, - { Instruction::AND, [](u256 const& _a, u256 const& _b) -> u256 { return _a & _b; } }, - { Instruction::OR, [](u256 const& _a, u256 const& _b) -> u256 { return _a | _b; } }, - { Instruction::XOR, [](u256 const& _a, u256 const& _b) -> u256 { return _a ^ _b; } }, - }; - if (arithmetics.count(_item.instruction())) - { - u256 result = arithmetics.at(_item.instruction())( - m_equivalenceClasses.at(args[0]).first->data(), - m_equivalenceClasses.at(args[1]).first->data() - ); - m_spareAssemblyItem.push_back(make_shared(result)); - return getClass(*m_spareAssemblyItem.back()); - } - } - m_equivalenceClasses.push_back(make_pair(&_item, args)); - return m_equivalenceClasses.size() - 1; + return m_stackElements[make_pair(_stackHeight, nextSequence)] = + m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight))); } unsigned CommonSubexpressionEliminator::getNextStackElementSequence(int _stackHeight) @@ -318,15 +240,11 @@ bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item) } AssemblyItems CSECodeGenerator::generateCode( - map const& _initialStack, - map const& _targetStackContents, - vector> const& _equivalenceClasses + map const& _initialStack, + map const& _targetStackContents ) { - // reset - *this = move(CSECodeGenerator()); m_stack = _initialStack; - m_equivalenceClasses = _equivalenceClasses; for (auto const& item: m_stack) if (!m_classPositions.count(item.second)) m_classPositions[item.second] = item.first; @@ -377,18 +295,18 @@ AssemblyItems CSECodeGenerator::generateCode( return m_generatedItems; } -void CSECodeGenerator::addDependencies(EquivalenceClassId _c) +void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c) { if (m_neededBy.count(_c)) return; - for (EquivalenceClassId argument: m_equivalenceClasses.at(_c).second) + for (ExpressionClasses::Id argument: m_expressionClasses.representative(_c).arguments) { addDependencies(argument); m_neededBy.insert(make_pair(argument, _c)); } } -int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) +int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c) { if (m_classPositions.count(_c)) { @@ -399,8 +317,8 @@ int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) ); return m_classPositions[_c]; } - EquivalenceClassIds const& arguments = m_equivalenceClasses.at(_c).second; - for (EquivalenceClassId arg: boost::adaptors::reverse(arguments)) + ExpressionClasses::Ids const& arguments = m_expressionClasses.representative(_c).arguments; + for (ExpressionClasses::Id arg: boost::adaptors::reverse(arguments)) generateClassElement(arg); // The arguments are somewhere on the stack now, so it remains to move them at the correct place. @@ -458,7 +376,7 @@ int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) for (size_t i = 0; i < arguments.size(); ++i) assertThrow(m_stack[m_stackHeight - i] == arguments[i], OptimizerException, "Expected arguments not present." ); - AssemblyItem const& item = *m_equivalenceClasses.at(_c).first; + AssemblyItem const& item = *m_expressionClasses.representative(_c).item; while (SemanticInformation::isCommutativeOperation(item) && !m_generatedItems.empty() && m_generatedItems.back() == AssemblyItem(Instruction::SWAP1)) @@ -469,12 +387,12 @@ int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) m_classPositions[arg] = c_invalidPosition; for (size_t i = 0; i < arguments.size(); ++i) m_stack.erase(m_stackHeight - i); - appendItem(*m_equivalenceClasses.at(_c).first); + appendItem(*m_expressionClasses.representative(_c).item); m_stack[m_stackHeight] = _c; return m_classPositions[_c] = m_stackHeight; } -bool CSECodeGenerator::canBeRemoved(EquivalenceClassId _element, EquivalenceClassId _result) +bool CSECodeGenerator::canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result) { // Returns false if _element is finally needed or is needed by a class that has not been // computed yet. Note that m_classPositions also includes classes that were deleted in the meantime. @@ -493,7 +411,7 @@ bool CSECodeGenerator::removeStackTopIfPossible() if (m_stack.empty()) return false; assertThrow(m_stack.count(m_stackHeight), OptimizerException, ""); - EquivalenceClassId top = m_stack[m_stackHeight]; + ExpressionClasses::Id top = m_stack[m_stackHeight]; if (!canBeRemoved(top)) return false; m_generatedItems.push_back(AssemblyItem(Instruction::POP)); diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h index edf1e3f38..2a49d888d 100644 --- a/libevmcore/CommonSubexpressionEliminator.h +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -28,6 +28,7 @@ #include #include #include +#include namespace dev { @@ -37,9 +38,6 @@ namespace eth class AssemblyItem; using AssemblyItems = std::vector; -using EquivalenceClassId = unsigned; -using EquivalenceClassIds = std::vector; - /** * Optimizer step that performs common subexpression elimination and stack reorganisation, * i.e. it tries to infer equality among expressions and compute the values of two expressions @@ -67,24 +65,22 @@ public: /// Streams debugging information to @a _out. std::ostream& stream( std::ostream& _out, - std::map _currentStack = std::map(), - std::map _targetStack = std::map() + std::map _currentStack = std::map(), + std::map _targetStack = std::map() ) const; private: /// Feeds the item into the system for analysis. void feedItem(AssemblyItem const& _item); + /// Simplifies the given item using /// Assigns a new equivalence class to the next sequence number of the given stack element. - void setStackElement(int _stackHeight, EquivalenceClassId _class); + void setStackElement(int _stackHeight, ExpressionClasses::Id _class); /// Swaps the given stack elements in their next sequence number. void swapStackElements(int _stackHeightA, int _stackHeightB); /// Retrieves the current equivalence class fo the given stack element (or generates a new /// one if it does not exist yet). - EquivalenceClassId getStackElement(int _stackHeight); - /// Retrieves the equivalence class resulting from the given item applied to the given classes, - /// might also create a new one. - EquivalenceClassId getClass(AssemblyItem const& _item, EquivalenceClassIds const& _arguments = {}); + ExpressionClasses::Id getStackElement(int _stackHeight); /// @returns the next sequence number of the given stack element. unsigned getNextStackElementSequence(int _stackHeight); @@ -92,12 +88,9 @@ private: /// Current stack height, can be negative. int m_stackHeight = 0; /// Mapping (stack height, sequence number) -> equivalence class - std::map, EquivalenceClassId> m_stackElements; - /// Vector of equivalence class representatives - we only store one item of an equivalence - /// class and the index is used as identifier. - std::vector> m_equivalenceClasses; - /// List of items generated during analysis. - std::vector> m_spareAssemblyItem; + std::map, ExpressionClasses::Id> m_stackElements; + /// Structure containing the classes of equivalent expressions. + ExpressionClasses m_expressionClasses; }; /** @@ -121,27 +114,30 @@ struct SemanticInformation class CSECodeGenerator { public: + CSECodeGenerator(ExpressionClasses const& _expressionClasses): + m_expressionClasses(_expressionClasses) + {} + /// @returns the assembly items generated from the given requirements /// @param _initialStack current contents of the stack (up to stack height of zero) /// @param _targetStackContents final contents of the stack, by stack height relative to initial /// @param _equivalenceClasses equivalence classes as expressions of how to compute them - /// @note resuts the state of the object for each call. + /// @note should only be called once on each object. AssemblyItems generateCode( - std::map const& _initialStack, - std::map const& _targetStackContents, - std::vector> const& _equivalenceClasses + std::map const& _initialStack, + std::map const& _targetStackContents ); private: /// Recursively discovers all dependencies to @a m_requests. - void addDependencies(EquivalenceClassId _c); + void addDependencies(ExpressionClasses::Id _c); /// Produce code that generates the given element if it is not yet present. /// @returns the stack position of the element. - int generateClassElement(EquivalenceClassId _c); + int generateClassElement(ExpressionClasses::Id _c); /// @returns true if @a _element can be removed - in general or, if given, while computing @a _result. - bool canBeRemoved(EquivalenceClassId _element, EquivalenceClassId _result = EquivalenceClassId(-1)); + bool canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result = ExpressionClasses::Id(-1)); /// Appends code to remove the topmost stack element if it can be removed. bool removeStackTopIfPossible(); @@ -160,16 +156,16 @@ private: /// Current height of the stack relative to the start. int m_stackHeight = 0; /// If (b, a) is in m_requests then b is needed to compute a. - std::multimap m_neededBy; + std::multimap m_neededBy; /// Current content of the stack. - std::map m_stack; + std::map m_stack; /// Current positions of equivalence classes, equal to c_invalidPosition if already deleted. - std::map m_classPositions; + std::map m_classPositions; /// The actual eqivalence class items and how to compute them. - std::vector> m_equivalenceClasses; + ExpressionClasses const& m_expressionClasses; /// The set of equivalence classes that should be present on the stack at the end. - std::set m_finalClasses; + std::set m_finalClasses; }; template diff --git a/libevmcore/ExpressionClasses.cpp b/libevmcore/ExpressionClasses.cpp new file mode 100644 index 000000000..5d7b3e11b --- /dev/null +++ b/libevmcore/ExpressionClasses.cpp @@ -0,0 +1,110 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @file ExpressionClasses.cpp + * @author Christian + * @date 2015 + * Container for equivalence classes of expressions for use in common subexpression elimination. + */ + +#include +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace dev::eth; + + +ExpressionClasses::Id ExpressionClasses::find(AssemblyItem const& _item, Ids const& _arguments) +{ + // TODO: do a clever search, i.e. + // - check for the presence of constants in the argument classes and do arithmetic + // - check whether the two items are equal for a SUB instruction + // - check whether 0 or 1 is in one of the classes for a MUL + + Expression exp; + exp.item = &_item; + exp.arguments = _arguments; + if (SemanticInformation::isCommutativeOperation(_item)) + sort(exp.arguments.begin(), exp.arguments.end()); + + //@todo use a data structure that allows better searches + for (Expression const& e: m_representatives) + if (std::tie(*e.item, e.arguments) == std::tie(*exp.item, exp.arguments)) + return e.id; + + if (SemanticInformation::isDupInstruction(_item)) + { + // Special item that refers to values pre-existing on the stack + m_spareAssemblyItem.push_back(make_shared(_item)); + exp.item = m_spareAssemblyItem.back().get(); + } + else if (_item.type() == Operation) + { + //@todo try to avoid having to do this multiple times by storing not only one representative of + // an equivalence class + + // constant folding + auto isConstant = [this](Id eqc) { return representative(eqc).item->match(Push); }; + if (exp.arguments.size() == 2 && all_of(exp.arguments.begin(), exp.arguments.end(), isConstant)) + { + auto signextend = [](u256 a, u256 b) -> u256 + { + if (a >= 31) + return b; + unsigned testBit = unsigned(a) * 8 + 7; + u256 mask = (u256(1) << testBit) - 1; + return boost::multiprecision::bit_test(b, testBit) ? b | ~mask : b & mask; + }; + map> const arithmetics = + { + { Instruction::SUB, [](u256 a, u256 b) -> u256 {return a - b; } }, + { Instruction::DIV, [](u256 a, u256 b) -> u256 {return b == 0 ? 0 : a / b; } }, + { Instruction::SDIV, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : s2u(u2s(a) / u2s(b)); } }, + { Instruction::MOD, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : a % b; } }, + { Instruction::SMOD, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : s2u(u2s(a) % u2s(b)); } }, + { Instruction::EXP, [](u256 a, u256 b) -> u256 { return (u256)boost::multiprecision::powm(bigint(a), bigint(b), bigint(1) << 256); } }, + { Instruction::SIGNEXTEND, signextend }, + { Instruction::LT, [](u256 a, u256 b) -> u256 { return a < b ? 1 : 0; } }, + { Instruction::GT, [](u256 a, u256 b) -> u256 { return a > b ? 1 : 0; } }, + { Instruction::SLT, [](u256 a, u256 b) -> u256 { return u2s(a) < u2s(b) ? 1 : 0; } }, + { Instruction::SGT, [](u256 a, u256 b) -> u256 { return u2s(a) > u2s(b) ? 1 : 0; } }, + { Instruction::EQ, [](u256 a, u256 b) -> u256 { return a == b ? 1 : 0; } }, + { Instruction::ADD, [](u256 a, u256 b) -> u256 { return a + b; } }, + { Instruction::MUL, [](u256 a, u256 b) -> u256 { return a * b; } }, + { Instruction::AND, [](u256 a, u256 b) -> u256 { return a & b; } }, + { Instruction::OR, [](u256 a, u256 b) -> u256 { return a | b; } }, + { Instruction::XOR, [](u256 a, u256 b) -> u256 { return a ^ b; } }, + }; + if (arithmetics.count(_item.instruction())) + { + u256 result = arithmetics.at(_item.instruction())( + representative(exp.arguments[0]).item->data(), + representative(exp.arguments[1]).item->data() + ); + m_spareAssemblyItem.push_back(make_shared(result)); + return find(*m_spareAssemblyItem.back()); + } + } + } + exp.id = m_representatives.size(); + m_representatives.push_back(exp); + return exp.id; +} + diff --git a/libevmcore/ExpressionClasses.h b/libevmcore/ExpressionClasses.h new file mode 100644 index 000000000..89485214c --- /dev/null +++ b/libevmcore/ExpressionClasses.h @@ -0,0 +1,70 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @file ExpressionClasses.h + * @author Christian + * @date 2015 + * Container for equivalence classes of expressions for use in common subexpression elimination. + */ + +#pragma once + +#include +#include +#include + +namespace dev +{ +namespace eth +{ + +class AssemblyItem; + +/** + * Collection of classes of equivalent expressions that can also determine the class of an expression. + * Identifiers are contiguously assigned to new classes starting from zero. + */ +class ExpressionClasses +{ +public: + using Id = unsigned; + using Ids = std::vector; + + struct Expression + { + Id id; + AssemblyItem const* item; + Ids arguments; + }; + + /// Retrieves the id of the expression equivalence class resulting from the given item applied to the + /// given classes, might also create a new one. + Id find(AssemblyItem const& _item, Ids const& _arguments = {}); + /// @returns the canonical representative of an expression class. + Expression const& representative(Id _id) const { return m_representatives.at(_id); } + /// @returns the number of classes. + Id size() const { return m_representatives.size(); } + +private: + + /// Expression equivalence class representatives - we only store one item of an equivalence. + std::vector m_representatives; + std::vector> m_spareAssemblyItem; +}; + +} +} From fd961605b4ded133633f76885dacea8cb4ba9123 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Mar 2015 22:39:42 +0100 Subject: [PATCH 109/228] Optimizing various single operations. --- libevmcore/Assembly.cpp | 39 ++-- libevmcore/CommonSubexpressionEliminator.cpp | 12 +- libevmcore/Exceptions.h | 1 + libevmcore/ExpressionClasses.cpp | 226 +++++++++++++++---- libevmcore/ExpressionClasses.h | 7 +- test/SolidityOptimizer.cpp | 78 ++++--- 6 files changed, 247 insertions(+), 116 deletions(-) diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index ab9d76911..bd504dc22 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -288,18 +288,6 @@ inline bool matches(AssemblyItemsConstRef _a, AssemblyItemsConstRef _b) return true; } -//@todo this has to move to a special optimizer class soon -template -unsigned bytesRequiredBySlice(Iterator _begin, Iterator _end) -{ - // this is only used in the optimizer, so we can provide a guess for the address length - unsigned addressLength = 4; - unsigned size = 0; - for (; _begin != _end; ++_begin) - size += _begin->bytesRequired(addressLength); - return size; -} - struct OptimiserChannel: public LogChannel { static const char* name() { return "OPT"; } static const int verbosity = 12; }; #define copt dev::LogOutputStream() @@ -315,8 +303,6 @@ Assembly& Assembly::optimise(bool _enable) { Instruction::OR, [](u256 a, u256 b)->u256{return a | b;} }, { Instruction::XOR, [](u256 a, u256 b)->u256{return a ^ b;} }, }; - std::vector> const c_identities = - { { Instruction::ADD, 0}, { Instruction::MUL, 1}, { Instruction::MOD, 0}, { Instruction::OR, 0}, { Instruction::XOR, 0} }; std::vector>> rules = { { { Push, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, @@ -334,17 +320,13 @@ Assembly& Assembly::optimise(bool _enable) rules.push_back({ { Push, Push, i.first }, [&](AssemblyItemsConstRef m) -> AssemblyItems { return { i.second(m[1].data(), m[0].data()) }; } }); rules.push_back({ { Push, i.first, Push, i.first }, [&](AssemblyItemsConstRef m) -> AssemblyItems { return { i.second(m[2].data(), m[0].data()), i.first }; } }); } - for (auto const& i: c_identities) - rules.push_back({{Push, i.first}, [&](AssemblyItemsConstRef m) -> AssemblyItems - { return m[0].data() == i.second ? AssemblyItems() : m.toVector(); }}); // jump to next instruction rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }}); - copt << *this; - unsigned total = 0; for (unsigned count = 1; count > 0; total += count) { + copt << *this; count = 0; copt << "Performing common subexpression elimination..."; @@ -353,11 +335,22 @@ Assembly& Assembly::optimise(bool _enable) CommonSubexpressionEliminator eliminator; auto orig = iter; iter = eliminator.feedItems(iter, m_items.end()); - AssemblyItems optItems = eliminator.getOptimizedItems(); - copt << "Old size: " << (iter - orig) << ", new size: " << optItems.size(); - if (optItems.size() < size_t(iter - orig)) + AssemblyItems optItems; + bool shouldReplace = false; + try + { + optItems = eliminator.getOptimizedItems(); + shouldReplace = (optItems.size() < size_t(iter - orig)); + } + catch (StackTooDeepException const&) + { + // This might happen if the opcode reconstruction is not as efficient + // as the hand-crafted code. + } + + if (shouldReplace) { - // replace items + copt << "Old size: " << (iter - orig) << ", new size: " << optItems.size(); count++; for (auto moveIter = optItems.begin(); moveIter != optItems.end(); ++orig, ++moveIter) *orig = move(*moveIter); diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index 8102721cc..65aac74f1 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -157,10 +157,8 @@ ExpressionClasses::Id CommonSubexpressionEliminator::getStackElement(int _stackH return m_stackElements[make_pair(_stackHeight, nextSequence - 1)]; // Stack element not found (not assigned yet), create new equivalence class. - if (_stackHeight > 0) - BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Stack element accessed before assignment.")); - if (_stackHeight <= -16) - BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Stack too deep.")); + assertThrow(_stackHeight <= 0, OptimizerException, "Stack element accessed before assignment."); + assertThrow(_stackHeight > -16, StackTooDeepException, ""); // This is a special assembly item that refers to elements pre-existing on the initial stack. return m_stackElements[make_pair(_stackHeight, nextSequence)] = m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight))); @@ -423,7 +421,8 @@ bool CSECodeGenerator::removeStackTopIfPossible() void CSECodeGenerator::appendDup(int _fromPosition) { int nr = 1 + m_stackHeight - _fromPosition; - assertThrow(1 <= nr && nr <= 16, OptimizerException, "Stack too deep."); + assertThrow(nr <= 16, StackTooDeepException, "Stack too deep."); + assertThrow(1 <= nr, OptimizerException, "Invalid stack access."); m_generatedItems.push_back(AssemblyItem(dupInstruction(nr))); m_stackHeight++; m_stack[m_stackHeight] = m_stack[_fromPosition]; @@ -434,7 +433,8 @@ void CSECodeGenerator::appendSwapOrRemove(int _fromPosition) if (_fromPosition == m_stackHeight) return; int nr = m_stackHeight - _fromPosition; - assertThrow(1 <= nr && nr <= 16, OptimizerException, "Stack too deep."); + assertThrow(nr <= 16, StackTooDeepException, "Stack too deep."); + assertThrow(1 <= nr, OptimizerException, "Invalid stack access."); m_generatedItems.push_back(AssemblyItem(swapInstruction(nr))); // The value of a class can be present in multiple locations on the stack. We only update the // "canonical" one that is tracked by m_classPositions diff --git a/libevmcore/Exceptions.h b/libevmcore/Exceptions.h index fad972179..fa3c19f13 100644 --- a/libevmcore/Exceptions.h +++ b/libevmcore/Exceptions.h @@ -32,6 +32,7 @@ struct AssemblyException: virtual Exception {}; struct InvalidDeposit: virtual AssemblyException {}; struct InvalidOpcode: virtual AssemblyException {}; struct OptimizerException: virtual AssemblyException {}; +struct StackTooDeepException: virtual OptimizerException {}; } } diff --git a/libevmcore/ExpressionClasses.cpp b/libevmcore/ExpressionClasses.cpp index 5d7b3e11b..eebc98f66 100644 --- a/libevmcore/ExpressionClasses.cpp +++ b/libevmcore/ExpressionClasses.cpp @@ -22,6 +22,9 @@ */ #include +#include +#include +#include #include #include #include @@ -31,22 +34,26 @@ using namespace dev; using namespace dev::eth; -ExpressionClasses::Id ExpressionClasses::find(AssemblyItem const& _item, Ids const& _arguments) +bool ExpressionClasses::Expression::operator<(const ExpressionClasses::Expression& _other) const { - // TODO: do a clever search, i.e. - // - check for the presence of constants in the argument classes and do arithmetic - // - check whether the two items are equal for a SUB instruction - // - check whether 0 or 1 is in one of the classes for a MUL + auto type = item->type(); + auto otherType = _other.item->type(); + return std::tie(type, item->data(), arguments) < + std::tie(otherType, _other.item->data(), _other.arguments); +} +ExpressionClasses::Id ExpressionClasses::find(AssemblyItem const& _item, Ids const& _arguments) +{ Expression exp; exp.item = &_item; exp.arguments = _arguments; + if (SemanticInformation::isCommutativeOperation(_item)) sort(exp.arguments.begin(), exp.arguments.end()); - //@todo use a data structure that allows better searches + //@todo store all class members (not only the representatives) in an efficient data structure to search here for (Expression const& e: m_representatives) - if (std::tie(*e.item, e.arguments) == std::tie(*exp.item, exp.arguments)) + if (!(e < exp || exp < e)) return e.id; if (SemanticInformation::isDupInstruction(_item)) @@ -55,56 +62,175 @@ ExpressionClasses::Id ExpressionClasses::find(AssemblyItem const& _item, Ids con m_spareAssemblyItem.push_back(make_shared(_item)); exp.item = m_spareAssemblyItem.back().get(); } - else if (_item.type() == Operation) + + ExpressionClasses::Id id = tryToSimplify(exp); + if (id < m_representatives.size()) + return id; + + exp.id = m_representatives.size(); + m_representatives.push_back(exp); + return exp.id; +} + +ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr, bool _secondRun) +{ + if (_expr.item->type() != Operation) + return -1; + + // @todo: + // ISZERO ISZERO + // associative operations (as done in Assembly.cpp) + // 2 * x == x + x + + Id arg1; + Id arg2; + Id arg3; + u256 data1; + u256 data2; + u256 data3; + switch (_expr.arguments.size()) { - //@todo try to avoid having to do this multiple times by storing not only one representative of - // an equivalence class + default: + arg3 = _expr.arguments.at(2); + data3 = representative(arg3).item->data(); + case 2: + arg2 = _expr.arguments.at(1); + data2 = representative(arg2).item->data(); + case 1: + arg1 = _expr.arguments.at(0); + data1 = representative(arg1).item->data(); + case 0: + break; + } - // constant folding - auto isConstant = [this](Id eqc) { return representative(eqc).item->match(Push); }; - if (exp.arguments.size() == 2 && all_of(exp.arguments.begin(), exp.arguments.end(), isConstant)) + /** + * Simplification rule. If _strict is false, Push or a constant matches any constant, + * otherwise Push matches "0" and a constant matches itself. + * "UndefinedItem" matches any expression, but all of them must be equal inside one rule. + */ + struct Rule + { + Rule(AssemblyItems const& _pattern, bool _strict, function const& _action): + pattern(_pattern), + assemblyItemAction(_action), + strict(_strict) + {} + Rule(AssemblyItems const& _pattern, function _action): + Rule(_pattern, false, _action) + {} + Rule(AssemblyItems const& _pattern, bool _strict, function const& _action): + pattern(_pattern), + idAction(_action), + strict(_strict) + {} + Rule(AssemblyItems const& _pattern, function _action): + Rule(_pattern, false, _action) + {} + bool matches(ExpressionClasses const& _classes, Expression const& _expr) const { - auto signextend = [](u256 a, u256 b) -> u256 - { - if (a >= 31) - return b; - unsigned testBit = unsigned(a) * 8 + 7; - u256 mask = (u256(1) << testBit) - 1; - return boost::multiprecision::bit_test(b, testBit) ? b | ~mask : b & mask; - }; - map> const arithmetics = + if (!_expr.item->match(pattern.front())) + return false; + assertThrow(_expr.arguments.size() == pattern.size() - 1, OptimizerException, ""); + Id argRequiredToBeEqual(-1); + for (size_t i = 1; i < pattern.size(); ++i) { - { Instruction::SUB, [](u256 a, u256 b) -> u256 {return a - b; } }, - { Instruction::DIV, [](u256 a, u256 b) -> u256 {return b == 0 ? 0 : a / b; } }, - { Instruction::SDIV, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : s2u(u2s(a) / u2s(b)); } }, - { Instruction::MOD, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : a % b; } }, - { Instruction::SMOD, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : s2u(u2s(a) % u2s(b)); } }, - { Instruction::EXP, [](u256 a, u256 b) -> u256 { return (u256)boost::multiprecision::powm(bigint(a), bigint(b), bigint(1) << 256); } }, - { Instruction::SIGNEXTEND, signextend }, - { Instruction::LT, [](u256 a, u256 b) -> u256 { return a < b ? 1 : 0; } }, - { Instruction::GT, [](u256 a, u256 b) -> u256 { return a > b ? 1 : 0; } }, - { Instruction::SLT, [](u256 a, u256 b) -> u256 { return u2s(a) < u2s(b) ? 1 : 0; } }, - { Instruction::SGT, [](u256 a, u256 b) -> u256 { return u2s(a) > u2s(b) ? 1 : 0; } }, - { Instruction::EQ, [](u256 a, u256 b) -> u256 { return a == b ? 1 : 0; } }, - { Instruction::ADD, [](u256 a, u256 b) -> u256 { return a + b; } }, - { Instruction::MUL, [](u256 a, u256 b) -> u256 { return a * b; } }, - { Instruction::AND, [](u256 a, u256 b) -> u256 { return a & b; } }, - { Instruction::OR, [](u256 a, u256 b) -> u256 { return a | b; } }, - { Instruction::XOR, [](u256 a, u256 b) -> u256 { return a ^ b; } }, - }; - if (arithmetics.count(_item.instruction())) + Id arg = _expr.arguments[i - 1]; + if (pattern[i].type() == UndefinedItem) + { + if (argRequiredToBeEqual == Id(-1)) + argRequiredToBeEqual = arg; + else if (argRequiredToBeEqual != arg) + return false; + } + else + { + AssemblyItem const& argItem = *_classes.representative(arg).item; + if (strict && argItem != pattern[i]) + return false; + else if (!strict && !argItem.match(pattern[i])) + return false; + } + } + return true; + } + + AssemblyItems pattern; + function assemblyItemAction; + function idAction; + bool strict; + }; + + vector c_singleLevel{ + // arithmetics on constants involving only stack variables + {{Instruction::ADD, Push, Push}, [&]{ return data1 + data2; }}, + {{Instruction::MUL, Push, Push}, [&]{ return data1 * data2; }}, + {{Instruction::SUB, Push, Push}, [&]{ return data1 - data2; }}, + {{Instruction::DIV, Push, Push}, [&]{ return data2 == 0 ? 0 : data1 / data2; }}, + {{Instruction::SDIV, Push, Push}, [&]{ return data2 == 0 ? 0 : s2u(u2s(data1) / u2s(data2)); }}, + {{Instruction::MOD, Push, Push}, [&]{ return data2 == 0 ? 0 : data1 % data2; }}, + {{Instruction::SMOD, Push, Push}, [&]{ return data2 == 0 ? 0 : s2u(u2s(data1) % u2s(data2)); }}, + {{Instruction::EXP, Push, Push}, [&]{ return u256(boost::multiprecision::powm(bigint(data1), bigint(data2), bigint(1) << 256)); }}, + {{Instruction::NOT, Push}, [&]{ return ~data1; }}, + {{Instruction::LT, Push, Push}, [&]() -> u256 { return data1 < data2 ? 1 : 0; }}, + {{Instruction::GT, Push, Push}, [&]() -> u256 { return data1 > data2 ? 1 : 0; }}, + {{Instruction::SLT, Push, Push}, [&]() -> u256 { return u2s(data1) < u2s( data2) ? 1 : 0; }}, + {{Instruction::SGT, Push, Push}, [&]() -> u256 { return u2s(data1) > u2s( data2) ? 1 : 0; }}, + {{Instruction::EQ, Push, Push}, [&]() -> u256 { return data1 == data2 ? 1 : 0; }}, + {{Instruction::ISZERO, Push}, [&]() -> u256 { return data1 == 0 ? 1 : 0; }}, + {{Instruction::AND, Push, Push}, [&]{ return data1 & data2; }}, + {{Instruction::OR, Push, Push}, [&]{ return data1 | data2; }}, + {{Instruction::XOR, Push, Push}, [&]{ return data1 ^ data2; }}, + {{Instruction::BYTE, Push, Push}, [&]{ return data1 >= 32 ? 0 : (data2 >> unsigned(8 * (31 - data1))) & 0xff; }}, + {{Instruction::ADDMOD, Push, Push, Push}, [&]{ return data3 == 0 ? 0 : u256((bigint(data1) + bigint(data2)) % data3); }}, + {{Instruction::MULMOD, Push, Push, Push}, [&]{ return data3 == 0 ? 0 : u256((bigint(data1) * bigint(data2)) % data3); }}, + {{Instruction::MULMOD, Push, Push, Push}, [&]{ return data1 * data2; }}, + {{Instruction::SIGNEXTEND, Push, Push}, [&]{ + if (data1 >= 31) + return data2; + unsigned testBit = unsigned(data1) * 8 + 7; + u256 mask = (u256(1) << testBit) - 1; + return u256(boost::multiprecision::bit_test(data2, testBit) ? data2 | ~mask : data2 & mask); + }}, + {{Instruction::ADD, UndefinedItem, u256(0)}, true, [&]{ return arg1; }}, + {{Instruction::MUL, UndefinedItem, u256(1)}, true, [&]{ return arg1; }}, + {{Instruction::OR, UndefinedItem, u256(0)}, true, [&]{ return arg1; }}, + {{Instruction::XOR, UndefinedItem, u256(0)}, true, [&]{ return arg1; }}, + {{Instruction::AND, UndefinedItem, ~u256(0)}, true, [&]{ return arg1; }}, + {{Instruction::MUL, UndefinedItem, u256(0)}, true, [&]{ return u256(0); }}, + {{Instruction::DIV, UndefinedItem, u256(0)}, true, [&]{ return u256(0); }}, + {{Instruction::MOD, UndefinedItem, u256(0)}, true, [&]{ return u256(0); }}, + {{Instruction::MOD, u256(0), UndefinedItem}, true, [&]{ return u256(0); }}, + {{Instruction::AND, UndefinedItem, u256(0)}, true, [&]{ return u256(0); }}, + {{Instruction::OR, UndefinedItem, ~u256(0)}, true, [&]{ return ~u256(0); }}, + {{Instruction::AND, UndefinedItem, UndefinedItem}, true, [&]{ return arg1; }}, + {{Instruction::OR, UndefinedItem, UndefinedItem}, true, [&]{ return arg1; }}, + {{Instruction::SUB, UndefinedItem, UndefinedItem}, true, [&]{ return u256(0); }}, + {{Instruction::EQ, UndefinedItem, UndefinedItem}, true, [&]{ return u256(1); }}, + {{Instruction::LT, UndefinedItem, UndefinedItem}, true, [&]{ return u256(0); }}, + {{Instruction::SLT, UndefinedItem, UndefinedItem}, true, [&]{ return u256(0); }}, + {{Instruction::GT, UndefinedItem, UndefinedItem}, true, [&]{ return u256(0); }}, + {{Instruction::SGT, UndefinedItem, UndefinedItem}, true, [&]{ return u256(0); }}, + {{Instruction::MOD, UndefinedItem, UndefinedItem}, true, [&]{ return u256(0); }}, + }; + + for (auto const& rule: c_singleLevel) + if (rule.matches(*this, _expr)) + { + if (rule.idAction) + return rule.idAction(); + else { - u256 result = arithmetics.at(_item.instruction())( - representative(exp.arguments[0]).item->data(), - representative(exp.arguments[1]).item->data() - ); - m_spareAssemblyItem.push_back(make_shared(result)); + m_spareAssemblyItem.push_back(make_shared(rule.assemblyItemAction())); return find(*m_spareAssemblyItem.back()); } } + + if (!_secondRun && _expr.arguments.size() == 2 && SemanticInformation::isCommutativeOperation(*_expr.item)) + { + Expression expr = _expr; + swap(expr.arguments[0], expr.arguments[1]); + return tryToSimplify(expr, true); } - exp.id = m_representatives.size(); - m_representatives.push_back(exp); - return exp.id; -} + return -1; +} diff --git a/libevmcore/ExpressionClasses.h b/libevmcore/ExpressionClasses.h index 89485214c..71fc96109 100644 --- a/libevmcore/ExpressionClasses.h +++ b/libevmcore/ExpressionClasses.h @@ -24,7 +24,7 @@ #pragma once #include -#include +#include #include namespace dev @@ -49,6 +49,7 @@ public: Id id; AssemblyItem const* item; Ids arguments; + bool operator<(Expression const& _other) const; }; /// Retrieves the id of the expression equivalence class resulting from the given item applied to the @@ -60,6 +61,10 @@ public: Id size() const { return m_representatives.size(); } private: + /// Tries to simplify the given expression. + /// @returns its class if it possible or Id(-1) otherwise. + /// @param _secondRun is set to true for the second run where arguments of commutative expressions are reversed + Id tryToSimplify(Expression const& _expr, bool _secondRun = false); /// Expression equivalence class representatives - we only store one item of an equivalence. std::vector m_representatives; diff --git a/test/SolidityOptimizer.cpp b/test/SolidityOptimizer.cpp index 9c6a4e361..2ced97430 100644 --- a/test/SolidityOptimizer.cpp +++ b/test/SolidityOptimizer.cpp @@ -74,6 +74,14 @@ public: "\nOptimized: " + toHex(optimizedOutput)); } + void checkCSE(AssemblyItems const& _input, AssemblyItems const& _expectation) + { + eth::CommonSubexpressionEliminator cse; + BOOST_REQUIRE(cse.feedItems(_input.begin(), _input.end()) == _input.end()); + AssemblyItems output = cse.getOptimizedItems(); + BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end()); + } + protected: Address m_optimizedContract; Address m_nonOptimizedContract; @@ -199,61 +207,59 @@ BOOST_AUTO_TEST_CASE(cse_intermediate_swap) BOOST_AUTO_TEST_CASE(cse_negative_stack_access) { - eth::CommonSubexpressionEliminator cse; - AssemblyItems input{AssemblyItem(Instruction::DUP2), AssemblyItem(u256(0))}; - BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end()); - AssemblyItems output = cse.getOptimizedItems(); - BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); + AssemblyItems input{Instruction::DUP2, u256(0)}; + checkCSE(input, input); } BOOST_AUTO_TEST_CASE(cse_negative_stack_end) { - eth::CommonSubexpressionEliminator cse; - AssemblyItems input{ - AssemblyItem(Instruction::ADD) - }; - BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end()); - AssemblyItems output = cse.getOptimizedItems(); - BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); + AssemblyItems input{Instruction::ADD}; + checkCSE(input, input); } BOOST_AUTO_TEST_CASE(cse_intermediate_negative_stack) { - eth::CommonSubexpressionEliminator cse; - AssemblyItems input{ - AssemblyItem(Instruction::ADD), - AssemblyItem(u256(1)), - AssemblyItem(Instruction::DUP2) - }; - BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end()); - AssemblyItems output = cse.getOptimizedItems(); - BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); + AssemblyItems input{Instruction::ADD, u256(1), Instruction::DUP1}; + checkCSE(input, input); } BOOST_AUTO_TEST_CASE(cse_pop) { - eth::CommonSubexpressionEliminator cse; + checkCSE({Instruction::POP}, {Instruction::POP}); +} + +BOOST_AUTO_TEST_CASE(cse_unneeded_items) +{ AssemblyItems input{ - AssemblyItem(Instruction::POP) + Instruction::ADD, + Instruction::SWAP1, + Instruction::POP, + u256(7), + u256(8), }; - BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end()); - AssemblyItems output = cse.getOptimizedItems(); - BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); + checkCSE(input, input); } -BOOST_AUTO_TEST_CASE(cse_unneeded_items) +BOOST_AUTO_TEST_CASE(cse_invariants) { - eth::CommonSubexpressionEliminator cse; AssemblyItems input{ - AssemblyItem(Instruction::ADD), - AssemblyItem(Instruction::SWAP1), - AssemblyItem(Instruction::POP), - AssemblyItem(u256(7)), - AssemblyItem(u256(8)), + Instruction::DUP1, + Instruction::DUP1, + u256(0), + Instruction::OR, + Instruction::OR }; - BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end()); - AssemblyItems output = cse.getOptimizedItems(); - BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); + checkCSE(input, {Instruction::DUP1}); +} + +BOOST_AUTO_TEST_CASE(cse_subself) +{ + checkCSE({Instruction::DUP1, Instruction::SUB}, {Instruction::POP, u256(0)}); +} + +BOOST_AUTO_TEST_CASE(cse_subother) +{ + checkCSE({Instruction::SUB}, {Instruction::SUB}); } BOOST_AUTO_TEST_SUITE_END() From 442a34c9b0e079bf76aa7bb7bdce2515b0688ca4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Sat, 21 Mar 2015 12:34:59 +0100 Subject: [PATCH 110/228] Remove stack sequence id. --- libevmcore/CommonSubexpressionEliminator.cpp | 63 +++++++------------- libevmcore/CommonSubexpressionEliminator.h | 12 ++-- 2 files changed, 28 insertions(+), 47 deletions(-) diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index 65aac74f1..47251e5c8 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -36,16 +36,11 @@ vector CommonSubexpressionEliminator::getOptimizedItems() map targetStackContents; int minHeight = m_stackHeight + 1; if (!m_stackElements.empty()) - minHeight = min(minHeight, m_stackElements.begin()->first.first); - for (int height = minHeight; height <= max(0, m_stackHeight); ++height) - { - // make sure it is created - ExpressionClasses::Id c = getStackElement(height); - if (height <= 0) - initialStackContents[height] = m_expressionClasses.find(AssemblyItem(dupInstruction(1 - height))); - if (height <= m_stackHeight) - targetStackContents[height] = c; - } + minHeight = min(minHeight, m_stackElements.begin()->first); + for (int height = minHeight; height <= 0; ++height) + initialStackContents[height] = initialStackElement(height); + for (int height = minHeight; height <= m_stackHeight; ++height) + targetStackContents[height] = stackElement(height); // Debug info: //stream(cout, currentStackContents, targetStackContents); @@ -74,7 +69,7 @@ ostream& CommonSubexpressionEliminator::stream( _out << "Stack elements: " << endl; for (auto const& it: m_stackElements) { - _out << " " << dec << it.first.first << "(" << it.first.second << ") = "; + _out << " " << dec << it.first << " = "; streamExpressionClass(_out, it.second); } _out << "Equivalence classes: " << endl; @@ -112,7 +107,7 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) if (SemanticInformation::isDupInstruction(_item)) setStackElement( m_stackHeight + 1, - getStackElement(m_stackHeight - int(instruction) + int(Instruction::DUP1)) + stackElement(m_stackHeight - int(instruction) + int(Instruction::DUP1)) ); else if (SemanticInformation::isSwapInstruction(_item)) swapStackElements( @@ -123,7 +118,7 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) { vector arguments(info.args); for (int i = 0; i < info.args; ++i) - arguments[i] = getStackElement(m_stackHeight - i); + arguments[i] = stackElement(m_stackHeight - i); setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments)); } m_stackHeight += _item.deposit(); @@ -132,48 +127,34 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) void CommonSubexpressionEliminator::setStackElement(int _stackHeight, ExpressionClasses::Id _class) { - unsigned nextSequence = getNextStackElementSequence(_stackHeight); - m_stackElements[make_pair(_stackHeight, nextSequence)] = _class; + m_stackElements[_stackHeight] = _class; } void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _stackHeightB) { if (_stackHeightA == _stackHeightB) BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Swap on same stack elements.")); - ExpressionClasses::Id classA = getStackElement(_stackHeightA); - ExpressionClasses::Id classB = getStackElement(_stackHeightB); + // ensure they are created + stackElement(_stackHeightA); + stackElement(_stackHeightB); - unsigned nextSequenceA = getNextStackElementSequence(_stackHeightA); - unsigned nextSequenceB = getNextStackElementSequence(_stackHeightB); - m_stackElements[make_pair(_stackHeightA, nextSequenceA)] = classB; - m_stackElements[make_pair(_stackHeightB, nextSequenceB)] = classA; + swap(m_stackElements[_stackHeightA], m_stackElements[_stackHeightB]); } -ExpressionClasses::Id CommonSubexpressionEliminator::getStackElement(int _stackHeight) +ExpressionClasses::Id CommonSubexpressionEliminator::stackElement(int _stackHeight) { - // retrieve class by last sequence number - unsigned nextSequence = getNextStackElementSequence(_stackHeight); - if (nextSequence > 0) - return m_stackElements[make_pair(_stackHeight, nextSequence - 1)]; - + if (m_stackElements.count(_stackHeight)) + return m_stackElements.at(_stackHeight); // Stack element not found (not assigned yet), create new equivalence class. - assertThrow(_stackHeight <= 0, OptimizerException, "Stack element accessed before assignment."); - assertThrow(_stackHeight > -16, StackTooDeepException, ""); - // This is a special assembly item that refers to elements pre-existing on the initial stack. - return m_stackElements[make_pair(_stackHeight, nextSequence)] = - m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight))); + return m_stackElements[_stackHeight] = initialStackElement(_stackHeight); } -unsigned CommonSubexpressionEliminator::getNextStackElementSequence(int _stackHeight) +ExpressionClasses::Id CommonSubexpressionEliminator::initialStackElement(int _stackHeight) { - auto it = m_stackElements.upper_bound(make_pair(_stackHeight, unsigned(-1))); - if (it == m_stackElements.begin()) - return 0; - --it; - if (it->first.first == _stackHeight) - return it->first.second + 1; - else - return 0; + assertThrow(_stackHeight <= 0, OptimizerException, "Initial stack element of positive height requested."); + assertThrow(_stackHeight > -16, StackTooDeepException, ""); + // This is a special assembly item that refers to elements pre-existing on the initial stack. + return m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight))); } bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item) diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h index 2a49d888d..331a7642d 100644 --- a/libevmcore/CommonSubexpressionEliminator.h +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -80,15 +80,15 @@ private: void swapStackElements(int _stackHeightA, int _stackHeightB); /// Retrieves the current equivalence class fo the given stack element (or generates a new /// one if it does not exist yet). - ExpressionClasses::Id getStackElement(int _stackHeight); - - /// @returns the next sequence number of the given stack element. - unsigned getNextStackElementSequence(int _stackHeight); + ExpressionClasses::Id stackElement(int _stackHeight); + /// @returns the equivalence class id of the special initial stack element at the given height + /// (must not be positive). + ExpressionClasses::Id initialStackElement(int _stackHeight); /// Current stack height, can be negative. int m_stackHeight = 0; - /// Mapping (stack height, sequence number) -> equivalence class - std::map, ExpressionClasses::Id> m_stackElements; + /// Current stack layout, mapping stack height -> equivalence class + std::map m_stackElements; /// Structure containing the classes of equivalent expressions. ExpressionClasses m_expressionClasses; }; From 49712025fd85045c57a69cd2800a86f01520487e Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 23 Mar 2015 13:09:25 +0100 Subject: [PATCH 111/228] Pattern matching for expression simplification. --- libevmcore/Assembly.cpp | 145 +------ libevmcore/Assembly.h | 55 +-- libevmcore/AssemblyItem.cpp | 135 ++++++ libevmcore/AssemblyItem.h | 92 ++++ libevmcore/CommonSubexpressionEliminator.cpp | 2 +- libevmcore/ExpressionClasses.cpp | 418 ++++++++++++------- libevmcore/ExpressionClasses.h | 77 +++- test/SolidityOptimizer.cpp | 41 ++ 8 files changed, 635 insertions(+), 330 deletions(-) create mode 100644 libevmcore/AssemblyItem.cpp create mode 100644 libevmcore/AssemblyItem.h diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index bd504dc22..abe932282 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -28,122 +28,6 @@ using namespace std; using namespace dev; using namespace dev::eth; -unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const -{ - switch (m_type) - { - case Operation: - case Tag: // 1 byte for the JUMPDEST - return 1; - case PushString: - return 33; - case Push: - return 1 + max(1, dev::bytesRequired(m_data)); - case PushSubSize: - case PushProgramSize: - return 4; // worst case: a 16MB program - case PushTag: - case PushData: - case PushSub: - return 1 + _addressLength; - default: - break; - } - BOOST_THROW_EXCEPTION(InvalidOpcode()); -} - -int AssemblyItem::deposit() const -{ - switch (m_type) - { - case Operation: - return instructionInfo(instruction()).ret - instructionInfo(instruction()).args; - case Push: - case PushString: - case PushTag: - case PushData: - case PushSub: - case PushSubSize: - case PushProgramSize: - return 1; - case Tag: - return 0; - default:; - } - return 0; -} - -string AssemblyItem::getJumpTypeAsString() const -{ - switch (m_jumpType) - { - case JumpType::IntoFunction: - return "[in]"; - case JumpType::OutOfFunction: - return "[out]"; - case JumpType::Ordinary: - default: - return ""; - } -} - -ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item) -{ - switch (_item.type()) - { - case Operation: - _out << " " << instructionInfo(_item.instruction()).name; - if (_item.instruction() == eth::Instruction::JUMP || _item.instruction() == eth::Instruction::JUMPI) - _out << "\t" << _item.getJumpTypeAsString(); - break; - case Push: - _out << " PUSH " << hex << _item.data(); - break; - case PushString: - _out << " PushString" << hex << (unsigned)_item.data(); - break; - case PushTag: - _out << " PushTag " << _item.data(); - break; - case Tag: - _out << " Tag " << _item.data(); - break; - case PushData: - _out << " PushData " << hex << (unsigned)_item.data(); - break; - case PushSub: - _out << " PushSub " << hex << h256(_item.data()).abridged(); - break; - case PushSubSize: - _out << " PushSubSize " << hex << h256(_item.data()).abridged(); - break; - case PushProgramSize: - _out << " PushProgramSize"; - break; - case UndefinedItem: - _out << " ???"; - break; - default: - BOOST_THROW_EXCEPTION(InvalidOpcode()); - } - return _out; -} - -unsigned Assembly::bytesRequired() const -{ - for (unsigned br = 1;; ++br) - { - unsigned ret = 1; - for (auto const& i: m_data) - ret += i.second.size(); - - for (AssemblyItem const& i: m_items) - ret += i.bytesRequired(br); - if (dev::bytesRequired(ret) <= br) - return ret; - } -} - void Assembly::append(Assembly const& _a) { auto newDeposit = m_deposit + _a.deposit(); @@ -180,11 +64,19 @@ void Assembly::append(Assembly const& _a, int _deposit) } } -ostream& dev::eth::operator<<(ostream& _out, AssemblyItemsConstRef _i) +unsigned Assembly::bytesRequired() const { - for (AssemblyItem const& i: _i) - _out << i; - return _out; + for (unsigned br = 1;; ++br) + { + unsigned ret = 1; + for (auto const& i: m_data) + ret += i.second.size(); + + for (AssemblyItem const& i: m_items) + ret += i.bytesRequired(br); + if (dev::bytesRequired(ret) <= br) + return ret; + } } string Assembly::getLocationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) const @@ -295,14 +187,6 @@ Assembly& Assembly::optimise(bool _enable) { if (!_enable) return *this; - map> const c_associative = - { - { Instruction::ADD, [](u256 a, u256 b)->u256{return a + b;} }, - { Instruction::MUL, [](u256 a, u256 b)->u256{return a * b;} }, - { Instruction::AND, [](u256 a, u256 b)->u256{return a & b;} }, - { Instruction::OR, [](u256 a, u256 b)->u256{return a | b;} }, - { Instruction::XOR, [](u256 a, u256 b)->u256{return a ^ b;} }, - }; std::vector>> rules = { { { Push, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, @@ -315,11 +199,6 @@ Assembly& Assembly::optimise(bool _enable) { { Instruction::ISZERO, Instruction::ISZERO }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, }; - for (auto const& i: c_associative) - { - rules.push_back({ { Push, Push, i.first }, [&](AssemblyItemsConstRef m) -> AssemblyItems { return { i.second(m[1].data(), m[0].data()) }; } }); - rules.push_back({ { Push, i.first, Push, i.first }, [&](AssemblyItemsConstRef m) -> AssemblyItems { return { i.second(m[2].data(), m[0].data()), i.first }; } }); - } // jump to next instruction rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }}); diff --git a/libevmcore/Assembly.h b/libevmcore/Assembly.h index 280a2e81c..315e6aaed 100644 --- a/libevmcore/Assembly.h +++ b/libevmcore/Assembly.h @@ -27,6 +27,7 @@ #include #include #include +#include #include "Exceptions.h" namespace dev @@ -34,60 +35,6 @@ namespace dev namespace eth { -enum AssemblyItemType { UndefinedItem, Operation, Push, PushString, PushTag, PushSub, PushSubSize, PushProgramSize, Tag, PushData }; - -class Assembly; - -class AssemblyItem -{ - friend class Assembly; - -public: - enum class JumpType { Ordinary, IntoFunction, OutOfFunction }; - - AssemblyItem(u256 _push): m_type(Push), m_data(_push) {} - AssemblyItem(Instruction _i): m_type(Operation), m_data((byte)_i) {} - AssemblyItem(AssemblyItemType _type, u256 _data = 0): m_type(_type), m_data(_data) {} - - AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(Tag, m_data); } - AssemblyItem pushTag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(PushTag, m_data); } - - AssemblyItemType type() const { return m_type; } - u256 const& data() const { return m_data; } - /// @returns the instruction of this item (only valid if type() == Operation) - Instruction instruction() const { return Instruction(byte(m_data)); } - - /// @returns true iff the type and data of the items are equal. - bool operator==(AssemblyItem const& _other) const { return m_type == _other.m_type && m_data == _other.m_data; } - bool operator!=(AssemblyItem const& _other) const { return !operator==(_other); } - - /// @returns an upper bound for the number of bytes required by this item, assuming that - /// the value of a jump tag takes @a _addressLength bytes. - unsigned bytesRequired(unsigned _addressLength) const; - int deposit() const; - - bool match(AssemblyItem const& _i) const { return _i.m_type == UndefinedItem || (m_type == _i.m_type && (m_type != Operation || m_data == _i.m_data)); } - void setLocation(SourceLocation const& _location) { m_location = _location; } - SourceLocation const& getLocation() const { return m_location; } - - void setJumpType(JumpType _jumpType) { m_jumpType = _jumpType; } - JumpType getJumpType() const { return m_jumpType; } - std::string getJumpTypeAsString() const; - -private: - AssemblyItemType m_type; - u256 m_data; - SourceLocation m_location; - JumpType m_jumpType = JumpType::Ordinary; -}; - -using AssemblyItems = std::vector; -using AssemblyItemsConstRef = vector_ref; - -std::ostream& operator<<(std::ostream& _out, AssemblyItem const& _item); -std::ostream& operator<<(std::ostream& _out, AssemblyItemsConstRef _i); -inline std::ostream& operator<<(std::ostream& _out, AssemblyItems const& _i) { return operator<<(_out, AssemblyItemsConstRef(&_i)); } - class Assembly { public: diff --git a/libevmcore/AssemblyItem.cpp b/libevmcore/AssemblyItem.cpp new file mode 100644 index 000000000..a4485a144 --- /dev/null +++ b/libevmcore/AssemblyItem.cpp @@ -0,0 +1,135 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file Assembly.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "AssemblyItem.h" +#include + +using namespace std; +using namespace dev; +using namespace dev::eth; + +unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const +{ + switch (m_type) + { + case Operation: + case Tag: // 1 byte for the JUMPDEST + return 1; + case PushString: + return 33; + case Push: + return 1 + max(1, dev::bytesRequired(m_data)); + case PushSubSize: + case PushProgramSize: + return 4; // worst case: a 16MB program + case PushTag: + case PushData: + case PushSub: + return 1 + _addressLength; + default: + break; + } + BOOST_THROW_EXCEPTION(InvalidOpcode()); +} + +int AssemblyItem::deposit() const +{ + switch (m_type) + { + case Operation: + return instructionInfo(instruction()).ret - instructionInfo(instruction()).args; + case Push: + case PushString: + case PushTag: + case PushData: + case PushSub: + case PushSubSize: + case PushProgramSize: + return 1; + case Tag: + return 0; + default:; + } + return 0; +} + +string AssemblyItem::getJumpTypeAsString() const +{ + switch (m_jumpType) + { + case JumpType::IntoFunction: + return "[in]"; + case JumpType::OutOfFunction: + return "[out]"; + case JumpType::Ordinary: + default: + return ""; + } +} + +ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item) +{ + switch (_item.type()) + { + case Operation: + _out << " " << instructionInfo(_item.instruction()).name; + if (_item.instruction() == eth::Instruction::JUMP || _item.instruction() == eth::Instruction::JUMPI) + _out << "\t" << _item.getJumpTypeAsString(); + break; + case Push: + _out << " PUSH " << hex << _item.data(); + break; + case PushString: + _out << " PushString" << hex << (unsigned)_item.data(); + break; + case PushTag: + _out << " PushTag " << _item.data(); + break; + case Tag: + _out << " Tag " << _item.data(); + break; + case PushData: + _out << " PushData " << hex << (unsigned)_item.data(); + break; + case PushSub: + _out << " PushSub " << hex << h256(_item.data()).abridged(); + break; + case PushSubSize: + _out << " PushSubSize " << hex << h256(_item.data()).abridged(); + break; + case PushProgramSize: + _out << " PushProgramSize"; + break; + case UndefinedItem: + _out << " ???"; + break; + default: + BOOST_THROW_EXCEPTION(InvalidOpcode()); + } + return _out; +} + +ostream& dev::eth::operator<<(ostream& _out, AssemblyItemsConstRef _i) +{ + for (AssemblyItem const& i: _i) + _out << i; + return _out; +} diff --git a/libevmcore/AssemblyItem.h b/libevmcore/AssemblyItem.h new file mode 100644 index 000000000..e7beced39 --- /dev/null +++ b/libevmcore/AssemblyItem.h @@ -0,0 +1,92 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file Assembly.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "Exceptions.h" + +namespace dev +{ +namespace eth +{ + +enum AssemblyItemType { UndefinedItem, Operation, Push, PushString, PushTag, PushSub, PushSubSize, PushProgramSize, Tag, PushData }; + +class Assembly; + +class AssemblyItem +{ + friend class Assembly; + +public: + enum class JumpType { Ordinary, IntoFunction, OutOfFunction }; + + AssemblyItem(u256 _push): m_type(Push), m_data(_push) {} + AssemblyItem(Instruction _i): m_type(Operation), m_data((byte)_i) {} + AssemblyItem(AssemblyItemType _type, u256 _data = 0): m_type(_type), m_data(_data) {} + + AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(Tag, m_data); } + AssemblyItem pushTag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(PushTag, m_data); } + + AssemblyItemType type() const { return m_type; } + u256 const& data() const { return m_data; } + /// @returns the instruction of this item (only valid if type() == Operation) + Instruction instruction() const { return Instruction(byte(m_data)); } + + /// @returns true iff the type and data of the items are equal. + bool operator==(AssemblyItem const& _other) const { return m_type == _other.m_type && m_data == _other.m_data; } + bool operator!=(AssemblyItem const& _other) const { return !operator==(_other); } + + /// @returns an upper bound for the number of bytes required by this item, assuming that + /// the value of a jump tag takes @a _addressLength bytes. + unsigned bytesRequired(unsigned _addressLength) const; + int deposit() const; + + bool match(AssemblyItem const& _i) const { return _i.m_type == UndefinedItem || (m_type == _i.m_type && (m_type != Operation || m_data == _i.m_data)); } + void setLocation(SourceLocation const& _location) { m_location = _location; } + SourceLocation const& getLocation() const { return m_location; } + + void setJumpType(JumpType _jumpType) { m_jumpType = _jumpType; } + JumpType getJumpType() const { return m_jumpType; } + std::string getJumpTypeAsString() const; + +private: + AssemblyItemType m_type; + u256 m_data; + SourceLocation m_location; + JumpType m_jumpType = JumpType::Ordinary; +}; + +using AssemblyItems = std::vector; +using AssemblyItemsConstRef = vector_ref; + +std::ostream& operator<<(std::ostream& _out, AssemblyItem const& _item); +std::ostream& operator<<(std::ostream& _out, AssemblyItemsConstRef _i); +inline std::ostream& operator<<(std::ostream& _out, AssemblyItems const& _i) { return operator<<(_out, AssemblyItemsConstRef(&_i)); } + +} +} diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index 47251e5c8..7fed03b4e 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -43,7 +43,7 @@ vector CommonSubexpressionEliminator::getOptimizedItems() targetStackContents[height] = stackElement(height); // Debug info: - //stream(cout, currentStackContents, targetStackContents); + //stream(cout, initialStackContents, targetStackContents); return CSECodeGenerator(m_expressionClasses).generateCode(initialStackContents, targetStackContents); } diff --git a/libevmcore/ExpressionClasses.cpp b/libevmcore/ExpressionClasses.cpp index eebc98f66..49fd72c1b 100644 --- a/libevmcore/ExpressionClasses.cpp +++ b/libevmcore/ExpressionClasses.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -45,6 +46,7 @@ bool ExpressionClasses::Expression::operator<(const ExpressionClasses::Expressio ExpressionClasses::Id ExpressionClasses::find(AssemblyItem const& _item, Ids const& _arguments) { Expression exp; + exp.id = Id(-1); exp.item = &_item; exp.arguments = _arguments; @@ -72,158 +74,168 @@ ExpressionClasses::Id ExpressionClasses::find(AssemblyItem const& _item, Ids con return exp.id; } -ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr, bool _secondRun) +string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) { - if (_expr.item->type() != Operation) - return -1; + Expression const& expr = representative(_id); + stringstream str; + str << dec << expr.id << ":" << *expr.item << "("; + for (Id arg: expr.arguments) + str << fullDAGToString(arg) << ","; + str << ")"; + return str.str(); +} - // @todo: - // ISZERO ISZERO - // associative operations (as done in Assembly.cpp) - // 2 * x == x + x - - Id arg1; - Id arg2; - Id arg3; - u256 data1; - u256 data2; - u256 data3; - switch (_expr.arguments.size()) - { - default: - arg3 = _expr.arguments.at(2); - data3 = representative(arg3).item->data(); - case 2: - arg2 = _expr.arguments.at(1); - data2 = representative(arg2).item->data(); - case 1: - arg1 = _expr.arguments.at(0); - data1 = representative(arg1).item->data(); - case 0: - break; - } +class Rules: public boost::noncopyable +{ +public: + Rules(); + void resetMatchGroups() { m_matchGroups.clear(); } + vector>> rules() const { return m_rules; } - /** - * Simplification rule. If _strict is false, Push or a constant matches any constant, - * otherwise Push matches "0" and a constant matches itself. - * "UndefinedItem" matches any expression, but all of them must be equal inside one rule. - */ - struct Rule - { - Rule(AssemblyItems const& _pattern, bool _strict, function const& _action): - pattern(_pattern), - assemblyItemAction(_action), - strict(_strict) - {} - Rule(AssemblyItems const& _pattern, function _action): - Rule(_pattern, false, _action) - {} - Rule(AssemblyItems const& _pattern, bool _strict, function const& _action): - pattern(_pattern), - idAction(_action), - strict(_strict) - {} - Rule(AssemblyItems const& _pattern, function _action): - Rule(_pattern, false, _action) - {} - bool matches(ExpressionClasses const& _classes, Expression const& _expr) const - { - if (!_expr.item->match(pattern.front())) - return false; - assertThrow(_expr.arguments.size() == pattern.size() - 1, OptimizerException, ""); - Id argRequiredToBeEqual(-1); - for (size_t i = 1; i < pattern.size(); ++i) - { - Id arg = _expr.arguments[i - 1]; - if (pattern[i].type() == UndefinedItem) - { - if (argRequiredToBeEqual == Id(-1)) - argRequiredToBeEqual = arg; - else if (argRequiredToBeEqual != arg) - return false; - } - else - { - AssemblyItem const& argItem = *_classes.representative(arg).item; - if (strict && argItem != pattern[i]) - return false; - else if (!strict && !argItem.match(pattern[i])) - return false; - } - } - return true; - } +private: + using Expression = ExpressionClasses::Expression; + map m_matchGroups; + vector>> m_rules; +}; - AssemblyItems pattern; - function assemblyItemAction; - function idAction; - bool strict; - }; +Rules::Rules() +{ + // Multiple occurences of one of these inside one rule must match the same equivalence class. + // Constants. + Pattern A(Push); + Pattern B(Push); + Pattern C(Push); + // Anything. + Pattern X; + Pattern Y; + Pattern Z; + A.setMatchGroup(1, m_matchGroups); + B.setMatchGroup(2, m_matchGroups); + C.setMatchGroup(3, m_matchGroups); + X.setMatchGroup(4, m_matchGroups); + Y.setMatchGroup(5, m_matchGroups); + Z.setMatchGroup(6, m_matchGroups); - vector c_singleLevel{ - // arithmetics on constants involving only stack variables - {{Instruction::ADD, Push, Push}, [&]{ return data1 + data2; }}, - {{Instruction::MUL, Push, Push}, [&]{ return data1 * data2; }}, - {{Instruction::SUB, Push, Push}, [&]{ return data1 - data2; }}, - {{Instruction::DIV, Push, Push}, [&]{ return data2 == 0 ? 0 : data1 / data2; }}, - {{Instruction::SDIV, Push, Push}, [&]{ return data2 == 0 ? 0 : s2u(u2s(data1) / u2s(data2)); }}, - {{Instruction::MOD, Push, Push}, [&]{ return data2 == 0 ? 0 : data1 % data2; }}, - {{Instruction::SMOD, Push, Push}, [&]{ return data2 == 0 ? 0 : s2u(u2s(data1) % u2s(data2)); }}, - {{Instruction::EXP, Push, Push}, [&]{ return u256(boost::multiprecision::powm(bigint(data1), bigint(data2), bigint(1) << 256)); }}, - {{Instruction::NOT, Push}, [&]{ return ~data1; }}, - {{Instruction::LT, Push, Push}, [&]() -> u256 { return data1 < data2 ? 1 : 0; }}, - {{Instruction::GT, Push, Push}, [&]() -> u256 { return data1 > data2 ? 1 : 0; }}, - {{Instruction::SLT, Push, Push}, [&]() -> u256 { return u2s(data1) < u2s( data2) ? 1 : 0; }}, - {{Instruction::SGT, Push, Push}, [&]() -> u256 { return u2s(data1) > u2s( data2) ? 1 : 0; }}, - {{Instruction::EQ, Push, Push}, [&]() -> u256 { return data1 == data2 ? 1 : 0; }}, - {{Instruction::ISZERO, Push}, [&]() -> u256 { return data1 == 0 ? 1 : 0; }}, - {{Instruction::AND, Push, Push}, [&]{ return data1 & data2; }}, - {{Instruction::OR, Push, Push}, [&]{ return data1 | data2; }}, - {{Instruction::XOR, Push, Push}, [&]{ return data1 ^ data2; }}, - {{Instruction::BYTE, Push, Push}, [&]{ return data1 >= 32 ? 0 : (data2 >> unsigned(8 * (31 - data1))) & 0xff; }}, - {{Instruction::ADDMOD, Push, Push, Push}, [&]{ return data3 == 0 ? 0 : u256((bigint(data1) + bigint(data2)) % data3); }}, - {{Instruction::MULMOD, Push, Push, Push}, [&]{ return data3 == 0 ? 0 : u256((bigint(data1) * bigint(data2)) % data3); }}, - {{Instruction::MULMOD, Push, Push, Push}, [&]{ return data1 * data2; }}, - {{Instruction::SIGNEXTEND, Push, Push}, [&]{ - if (data1 >= 31) - return data2; - unsigned testBit = unsigned(data1) * 8 + 7; + m_rules = vector>>{ + // arithmetics on constants + {{Instruction::ADD, {A, B}}, [=]{ return A.d() + B.d(); }}, + {{Instruction::MUL, {A, B}}, [=]{ return A.d() * B.d(); }}, + {{Instruction::SUB, {A, B}}, [=]{ return A.d() - B.d(); }}, + {{Instruction::DIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : A.d() / B.d(); }}, + {{Instruction::SDIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(u2s(A.d()) / u2s(B.d())); }}, + {{Instruction::MOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : A.d() % B.d(); }}, + {{Instruction::SMOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(u2s(A.d()) % u2s(B.d())); }}, + {{Instruction::EXP, {A, B}}, [=]{ return u256(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << 256)); }}, + {{Instruction::NOT, {A}}, [=]{ return ~A.d(); }}, + {{Instruction::LT, {A, B}}, [=]() { return A.d() < B.d() ? u256(1) : 0; }}, + {{Instruction::GT, {A, B}}, [=]() -> u256 { return A.d() > B.d() ? 1 : 0; }}, + {{Instruction::SLT, {A, B}}, [=]() -> u256 { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }}, + {{Instruction::SGT, {A, B}}, [=]() -> u256 { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }}, + {{Instruction::EQ, {A, B}}, [=]() -> u256 { return A.d() == B.d() ? 1 : 0; }}, + {{Instruction::ISZERO, {A}}, [=]() -> u256 { return A.d() == 0 ? 1 : 0; }}, + {{Instruction::AND, {A, B}}, [=]{ return A.d() & B.d(); }}, + {{Instruction::OR, {A, B}}, [=]{ return A.d() | B.d(); }}, + {{Instruction::XOR, {A, B}}, [=]{ return A.d() ^ B.d(); }}, + {{Instruction::BYTE, {A, B}}, [=]{ return A.d() >= 32 ? 0 : (B.d() >> unsigned(8 * (31 - A.d()))) & 0xff; }}, + {{Instruction::ADDMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : u256((bigint(A.d()) + bigint(B.d())) % C.d()); }}, + {{Instruction::MULMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : u256((bigint(A.d()) * bigint(B.d())) % C.d()); }}, + {{Instruction::MULMOD, {A, B, C}}, [=]{ return A.d() * B.d(); }}, + {{Instruction::SIGNEXTEND, {A, B}}, [=]() -> u256 { + if (A.d() >= 31) + return B.d(); + unsigned testBit = unsigned(A.d()) * 8 + 7; u256 mask = (u256(1) << testBit) - 1; - return u256(boost::multiprecision::bit_test(data2, testBit) ? data2 | ~mask : data2 & mask); + return u256(boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask); }}, - {{Instruction::ADD, UndefinedItem, u256(0)}, true, [&]{ return arg1; }}, - {{Instruction::MUL, UndefinedItem, u256(1)}, true, [&]{ return arg1; }}, - {{Instruction::OR, UndefinedItem, u256(0)}, true, [&]{ return arg1; }}, - {{Instruction::XOR, UndefinedItem, u256(0)}, true, [&]{ return arg1; }}, - {{Instruction::AND, UndefinedItem, ~u256(0)}, true, [&]{ return arg1; }}, - {{Instruction::MUL, UndefinedItem, u256(0)}, true, [&]{ return u256(0); }}, - {{Instruction::DIV, UndefinedItem, u256(0)}, true, [&]{ return u256(0); }}, - {{Instruction::MOD, UndefinedItem, u256(0)}, true, [&]{ return u256(0); }}, - {{Instruction::MOD, u256(0), UndefinedItem}, true, [&]{ return u256(0); }}, - {{Instruction::AND, UndefinedItem, u256(0)}, true, [&]{ return u256(0); }}, - {{Instruction::OR, UndefinedItem, ~u256(0)}, true, [&]{ return ~u256(0); }}, - {{Instruction::AND, UndefinedItem, UndefinedItem}, true, [&]{ return arg1; }}, - {{Instruction::OR, UndefinedItem, UndefinedItem}, true, [&]{ return arg1; }}, - {{Instruction::SUB, UndefinedItem, UndefinedItem}, true, [&]{ return u256(0); }}, - {{Instruction::EQ, UndefinedItem, UndefinedItem}, true, [&]{ return u256(1); }}, - {{Instruction::LT, UndefinedItem, UndefinedItem}, true, [&]{ return u256(0); }}, - {{Instruction::SLT, UndefinedItem, UndefinedItem}, true, [&]{ return u256(0); }}, - {{Instruction::GT, UndefinedItem, UndefinedItem}, true, [&]{ return u256(0); }}, - {{Instruction::SGT, UndefinedItem, UndefinedItem}, true, [&]{ return u256(0); }}, - {{Instruction::MOD, UndefinedItem, UndefinedItem}, true, [&]{ return u256(0); }}, + + // invariants involving known constants + {{Instruction::ADD, {X, 0}}, [=]{ return X; }}, + {{Instruction::MUL, {X, 1}}, [=]{ return X; }}, + {{Instruction::DIV, {X, 1}}, [=]{ return X; }}, + {{Instruction::SDIV, {X, 1}}, [=]{ return X; }}, + {{Instruction::OR, {X, 0}}, [=]{ return X; }}, + {{Instruction::XOR, {X, 0}}, [=]{ return X; }}, + {{Instruction::AND, {X, ~u256(0)}}, [=]{ return X; }}, + {{Instruction::MUL, {X, 0}}, [=]{ return u256(0); }}, + {{Instruction::DIV, {X, 0}}, [=]{ return u256(0); }}, + {{Instruction::MOD, {X, 0}}, [=]{ return u256(0); }}, + {{Instruction::MOD, {0, X}}, [=]{ return u256(0); }}, + {{Instruction::AND, {X, 0}}, [=]{ return u256(0); }}, + {{Instruction::OR, {X, ~u256(0)}}, [=]{ return ~u256(0); }}, + // operations involving an expression and itself + {{Instruction::AND, {X, X}}, [=]{ return X; }}, + {{Instruction::OR, {X, X}}, [=]{ return X; }}, + {{Instruction::SUB, {X, X}}, [=]{ return u256(0); }}, + {{Instruction::EQ, {X, X}}, [=]{ return u256(1); }}, + {{Instruction::EQ, {X, X}}, [=]{ return u256(1); }}, + {{Instruction::LT, {X, X}}, [=]{ return u256(0); }}, + {{Instruction::SLT, {X, X}}, [=]{ return u256(0); }}, + {{Instruction::GT, {X, X}}, [=]{ return u256(0); }}, + {{Instruction::SGT, {X, X}}, [=]{ return u256(0); }}, + {{Instruction::MOD, {X, X}}, [=]{ return u256(0); }}, + + {{Instruction::NOT, {{Instruction::NOT, {X}}}}, [=]{ return X; }}, + }; + // Associative operations + for (auto const& opFun: vector>>{ + {Instruction::ADD, plus()}, + {Instruction::MUL, multiplies()}, + {Instruction::AND, bit_and()}, + {Instruction::OR, bit_or()}, + {Instruction::XOR, bit_xor()} + }) + { + auto op = opFun.first; + auto fun = opFun.second; + // Moving constants to the outside, order matters here! + // we need actions that return expressions (or patterns?) here, and we need also reversed rules + // (X+A)+B -> X+(A+B) + m_rules.push_back({ + {op, {{op, {X, A}}, B}}, + [=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; } + }); + // X+(Y+A) -> (X+Y)+A + m_rules.push_back({ + {op, {{op, {X, A}}, Y}}, + [=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; } + }); + // For now, we still need explicit commutativity for the inner pattern + m_rules.push_back({ + {op, {{op, {A, X}}, B}}, + [=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; } + }); + m_rules.push_back({ + {op, {{op, {A, X}}, Y}}, + [=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; } + }); }; - for (auto const& rule: c_singleLevel) - if (rule.matches(*this, _expr)) + //@todo: (x+8)-3 and other things +} + +ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr, bool _secondRun) +{ + static Rules rules; + + if (_expr.item->type() != Operation) + return -1; + + for (auto const& rule: rules.rules()) + { + rules.resetMatchGroups(); + if (rule.first.matches(_expr, *this)) { - if (rule.idAction) - return rule.idAction(); - else - { - m_spareAssemblyItem.push_back(make_shared(rule.assemblyItemAction())); - return find(*m_spareAssemblyItem.back()); - } + // Debug info + //cout << "Simplifying " << *_expr.item << "("; + //for (Id arg: _expr.arguments) + // cout << fullDAGToString(arg) << ", "; + //cout << ")" << endl; + //cout << "with rule " << rule.first.toString() << endl; + //ExpressionTemplate t(rule.second()); + //cout << "to" << rule.second().toString() << endl; + return rebuildExpression(ExpressionTemplate(rule.second())); } + } if (!_secondRun && _expr.arguments.size() == 2 && SemanticInformation::isCommutativeOperation(*_expr.item)) { @@ -234,3 +246,127 @@ ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr, return -1; } + +ExpressionClasses::Id ExpressionClasses::rebuildExpression(ExpressionTemplate const& _template) +{ + if (_template.hasId) + return _template.id; + + Ids arguments; + for (ExpressionTemplate const& t: _template.arguments) + arguments.push_back(rebuildExpression(t)); + m_spareAssemblyItem.push_back(make_shared(_template.item)); + return find(*m_spareAssemblyItem.back(), arguments); +} + + +Pattern::Pattern(Instruction _instruction, std::vector const& _arguments): + m_type(Operation), + m_requireDataMatch(true), + m_data(_instruction), + m_arguments(_arguments) +{ +} + +void Pattern::setMatchGroup(unsigned _group, map& _matchGroups) +{ + m_matchGroup = _group; + m_matchGroups = &_matchGroups; +} + +bool Pattern::matches(Expression const& _expr, ExpressionClasses const& _classes) const +{ + if (!matchesBaseItem(*_expr.item)) + return false; + if (m_matchGroup) + { + if (!m_matchGroups->count(m_matchGroup)) + (*m_matchGroups)[m_matchGroup] = &_expr; + else if ((*m_matchGroups)[m_matchGroup]->id != _expr.id) + return false; + } + assertThrow(m_arguments.size() == 0 || _expr.arguments.size() == m_arguments.size(), OptimizerException, ""); + for (size_t i = 0; i < m_arguments.size(); ++i) + if (!m_arguments[i].matches(_classes.representative(_expr.arguments[i]), _classes)) + return false; + return true; +} + +string Pattern::toString() const +{ + stringstream s; + switch (m_type) + { + case Operation: + s << instructionInfo(Instruction(unsigned(m_data))).name; + break; + case Push: + s << "PUSH " << hex << m_data; + break; + case UndefinedItem: + s << "ANY"; + break; + default: + s << "t=" << dec << m_type << " d=" << hex << m_data; + break; + } + if (!m_requireDataMatch) + s << " ~"; + if (m_matchGroup) + s << "[" << dec << m_matchGroup << "]"; + s << "("; + for (Pattern const& p: m_arguments) + s << p.toString() << ", "; + s << ")"; + return s.str(); +} + +bool Pattern::matchesBaseItem(AssemblyItem const& _item) const +{ + if (m_type == UndefinedItem) + return true; + if (m_type != _item.type()) + return false; + if (m_requireDataMatch && m_data != _item.data()) + return false; + return true; +} + +Pattern::Expression const& Pattern::matchGroupValue() const +{ + assertThrow(m_matchGroup > 0, OptimizerException, ""); + assertThrow(!!m_matchGroups, OptimizerException, ""); + assertThrow((*m_matchGroups)[m_matchGroup], OptimizerException, ""); + return *(*m_matchGroups)[m_matchGroup]; +} + + +ExpressionTemplate::ExpressionTemplate(Pattern const& _pattern) +{ + if (_pattern.matchGroup()) + { + hasId = true; + id = _pattern.id(); + } + else + { + hasId = false; + item = _pattern.toAssemblyItem(); + } + for (auto const& arg: _pattern.arguments()) + arguments.push_back(ExpressionTemplate(arg)); +} + +string ExpressionTemplate::toString() const +{ + stringstream s; + if (hasId) + s << id; + else + s << item; + s << "("; + for (auto const& arg: arguments) + s << arg.toString(); + s << ")"; + return s.str(); +} diff --git a/libevmcore/ExpressionClasses.h b/libevmcore/ExpressionClasses.h index 71fc96109..ecd9c9250 100644 --- a/libevmcore/ExpressionClasses.h +++ b/libevmcore/ExpressionClasses.h @@ -26,13 +26,16 @@ #include #include #include +#include +#include namespace dev { namespace eth { -class AssemblyItem; +class Pattern; +struct ExpressionTemplate; /** * Collection of classes of equivalent expressions that can also determine the class of an expression. @@ -60,16 +63,88 @@ public: /// @returns the number of classes. Id size() const { return m_representatives.size(); } + std::string fullDAGToString(Id _id); + private: /// Tries to simplify the given expression. /// @returns its class if it possible or Id(-1) otherwise. /// @param _secondRun is set to true for the second run where arguments of commutative expressions are reversed Id tryToSimplify(Expression const& _expr, bool _secondRun = false); + /// Rebuilds an expression from a (matched) pattern. + Id rebuildExpression(ExpressionTemplate const& _template); + + std::vector>> createRules() const; + /// Expression equivalence class representatives - we only store one item of an equivalence. std::vector m_representatives; std::vector> m_spareAssemblyItem; }; +/** + * Pattern to match against an expression. + * Also stores matched expressions to retrieve them later, for constructing new expressions using + * ExpressionTemplate. + */ +class Pattern +{ +public: + using Expression = ExpressionClasses::Expression; + using Id = ExpressionClasses::Id; + + // Matches a specific constant value. + Pattern(unsigned _value): Pattern(u256(_value)) {} + // Matches a specific constant value. + Pattern(u256 const& _value): m_type(Push), m_requireDataMatch(true), m_data(_value) {} + // Matches a specific assembly item type or anything if not given. + Pattern(AssemblyItemType _type = UndefinedItem): m_type(_type) {} + // Matches a given instruction with given arguments + Pattern(Instruction _instruction, std::vector const& _arguments = {}); + /// Sets this pattern to be part of the match group with the identifier @a _group. + /// Inside one rule, all patterns in the same match group have to match expressions from the + /// same expression equivalence class. + void setMatchGroup(unsigned _group, std::map& _matchGroups); + unsigned matchGroup() const { return m_matchGroup; } + bool matches(Expression const& _expr, ExpressionClasses const& _classes) const; + + AssemblyItem toAssemblyItem() const { return AssemblyItem(m_type, m_data); } + std::vector arguments() const { return m_arguments; } + + /// @returns the id of the matched expression if this pattern is part of a match group. + Id id() const { return matchGroupValue().id; } + /// @returns the data of the matched expression if this pattern is part of a match group. + u256 d() const { return matchGroupValue().item->data(); } + + std::string toString() const; + +private: + bool matchesBaseItem(AssemblyItem const& _item) const; + Expression const& matchGroupValue() const; + + AssemblyItemType m_type; + bool m_requireDataMatch = false; + u256 m_data = 0; + std::vector m_arguments; + unsigned m_matchGroup = 0; + std::map* m_matchGroups = nullptr; +}; + +/** + * Template for a new expression that can be built from matched patterns. + */ +struct ExpressionTemplate +{ + using Expression = ExpressionClasses::Expression; + using Id = ExpressionClasses::Id; + explicit ExpressionTemplate(Pattern const& _pattern); + std::string toString() const; + bool hasId = false; + /// Id of the matched expression, if available. + Id id = Id(-1); + // Otherwise, assembly item. + AssemblyItem item = UndefinedItem; + std::vector arguments; +}; + } } diff --git a/test/SolidityOptimizer.cpp b/test/SolidityOptimizer.cpp index 2ced97430..2d5cff7ac 100644 --- a/test/SolidityOptimizer.cpp +++ b/test/SolidityOptimizer.cpp @@ -240,6 +240,12 @@ BOOST_AUTO_TEST_CASE(cse_unneeded_items) checkCSE(input, input); } +BOOST_AUTO_TEST_CASE(cse_constant_addition) +{ + AssemblyItems input{u256(7), u256(8), Instruction::ADD}; + checkCSE(input, {u256(7 + 8)}); +} + BOOST_AUTO_TEST_CASE(cse_invariants) { AssemblyItems input{ @@ -262,6 +268,41 @@ BOOST_AUTO_TEST_CASE(cse_subother) checkCSE({Instruction::SUB}, {Instruction::SUB}); } +BOOST_AUTO_TEST_CASE(cse_double_negation) +{ + checkCSE({Instruction::DUP5, Instruction::NOT, Instruction::NOT}, {Instruction::DUP5}); +} + +BOOST_AUTO_TEST_CASE(cse_associativity) +{ + AssemblyItems input{ + Instruction::DUP1, + Instruction::DUP1, + u256(0), + Instruction::OR, + Instruction::OR + }; + checkCSE(input, {Instruction::DUP1}); +} + +BOOST_AUTO_TEST_CASE(cse_associativity2) +{ + AssemblyItems input{ + u256(0), + Instruction::DUP2, + u256(2), + u256(1), + Instruction::DUP6, + Instruction::ADD, + u256(2), + Instruction::ADD, + Instruction::ADD, + Instruction::ADD, + Instruction::ADD + }; + checkCSE(input, {Instruction::DUP2, Instruction::DUP2, Instruction::ADD, u256(5), Instruction::ADD}); +} + BOOST_AUTO_TEST_SUITE_END() } From ff6503e51ff377cde8889c88b4cf5471cfe5dfc4 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 26 Mar 2015 18:52:06 +0100 Subject: [PATCH 112/228] Ability to decode ABIs. --- abi/main.cpp | 135 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 125 insertions(+), 10 deletions(-) diff --git a/abi/main.cpp b/abi/main.cpp index 2225dd4b3..4e6caa5d5 100644 --- a/abi/main.cpp +++ b/abi/main.cpp @@ -21,6 +21,7 @@ */ #include #include +#include #include #include "../test/JsonSpiritHeaders.h" #include @@ -105,13 +106,45 @@ enum class Base Fixed }; +static const map s_bases = +{ + { Base::Bytes, "bytes" }, + { Base::Address, "address" }, + { Base::Int, "int" }, + { Base::Uint, "uint" }, + { Base::Fixed, "fixed" } +}; + struct ABIType { Base base = Base::Unknown; unsigned size = 32; unsigned ssize = 0; vector dims; + string name; ABIType() = default; + ABIType(std::string const& _type, std::string const& _name): + name(_name) + { + string rest; + for (auto const& i: s_bases) + if (boost::algorithm::starts_with(_type, i.second)) + { + base = i.first; + rest = _type.substr(i.second.size()); + } + if (base == Base::Unknown) + throw InvalidFormat(); + boost::regex r("(\\d*)(x(\\d+))?((\\[\\d*\\])*)"); + boost::smatch res; + boost::regex_match(rest, res, r); + size = res[1].length() > 0 ? stoi(res[1]) : 0; + ssize = res[3].length() > 0 ? stoi(res[3]) : 0; + boost::regex r2("\\[(\\d*)\\](.*)"); + for (rest = res[4]; boost::regex_match(rest, res, r2); rest = res[2]) + dims.push_back(!res[1].length() ? -1 : stoi(res[1])); + } + ABIType(std::string const& _s) { if (_s.size() < 1) @@ -133,13 +166,21 @@ struct ABIType size = 32; return; } - if (_s.find_first_of('x') == string::npos) - size = stoi(_s.substr(1)); + strings d; + boost::algorithm::split(d, _s, boost::is_any_of("*")); + string s = d[0]; + if (s.find_first_of('x') == string::npos) + size = stoi(s.substr(1)); else { - size = stoi(_s.substr(1, _s.find_first_of('x') - 1)); - ssize = stoi(_s.substr(_s.find_first_of('x') + 1)); + size = stoi(s.substr(1, s.find_first_of('x') - 1)); + ssize = stoi(s.substr(s.find_first_of('x') + 1)); } + for (unsigned i = 1; i < d.size(); ++i) + if (d[i].empty()) + dims.push_back(-1); + else + dims.push_back(stoi(d[i])); } string canon() const @@ -185,7 +226,7 @@ tuple fromUser(std::string const& _arg, Tristate _prefix type.noteHexInput(val.size() - 2); return make_tuple(fromHex(val), type, Format::Hex); } - if (val.substr(0, 1) == ".") + if (val.substr(0, 1) == "+") { type.noteDecimalInput(); return make_tuple(toCompactBigEndian(bigint(val.substr(1))), type, Format::Decimal); @@ -214,6 +255,63 @@ tuple fromUser(std::string const& _arg, Tristate _prefix throw InvalidUserString(); } +struct ABIMethod +{ + string name; + vector ins; + vector outs; + bool isConstant; + + // isolation *IS* documentation. + + ABIMethod() = default; + + ABIMethod(js::mObject _o) + { + name = _o["name"].get_str(); + isConstant = _o["constant"].get_bool(); + if (_o.count("inputs")) + for (auto const& i: _o["inputs"].get_array()) + { + js::mObject a = i.get_obj(); + ins.push_back(ABIType(a["type"].get_str(), a["name"].get_str())); + } + if (_o.count("outputs")) + for (auto const& i: _o["outputs"].get_array()) + { + js::mObject a = i.get_obj(); + outs.push_back(ABIType(a["type"].get_str(), a["name"].get_str())); + } + } + + string sig() const + { + string methodArgs; + for (auto const& arg: ins) + methodArgs += (methodArgs.empty() ? "" : ",") + arg.canon(); + return name + "(" + methodArgs + ")"; + } + FixedHash<4> id() const { return FixedHash<4>(sha3(sig())); } +}; + +using ABI = map, ABIMethod>; + +ABI readABI(std::string const& _json) +{ + ABI ret; + js::mValue v; + js::read_string(_json, v); + for (auto const& i: v.get_array()) + { + js::mObject o = i.get_obj(); + if (o["type"].get_str() != "function") + continue; + ABIMethod m(o); + ret[m.id()] = m; + } + return ret; +} + void userOutput(ostream& _out, bytes const& _data, Encoding _e) { switch (_e) @@ -300,17 +398,17 @@ int main(int argc, char** argv) args.push_back(fromUser(arg, prefix, typePrefix)); } - string abi; + string abiData; if (abiFile == "--") for (int i = cin.get(); i != -1; i = cin.get()) - abi.push_back((char)i); + abiData.push_back((char)i); else if (!abiFile.empty()) - abi = contentsString(abiFile); + abiData = contentsString(abiFile); if (mode == Mode::Encode) { bytes ret; - if (abi.empty()) + if (abiData.empty()) { if (!method.empty()) { @@ -326,7 +424,24 @@ int main(int argc, char** argv) } else { - // TODO: read abi. + ABI abi = readABI(abiData); + if (verbose) + { + cerr << "ABI:" << endl; + for (auto const& i: abi) + { + ABIMethod const& m = i.second; + cerr << " " << i.first.abridged() << ": function " << m.name; + int f = 0; + for (ABIType const& i: m.ins) + cerr << (f++ ? ", " : "(") << i.canon() << " " << i.name; + cerr << ") returns ("; + f = 0; + for (ABIType const& i: m.outs) + cerr << (f ? ", " : "(") << i.canon() << " " << i.name; + cerr << ")" << endl; + } + } } userOutput(cout, ret, encoding); } From 5959a580712a3c28ebf4520cdd99341e92b4a681 Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 26 Mar 2015 19:51:45 +0100 Subject: [PATCH 113/228] abstract overriding tcp endpoint --- libp2p/Common.cpp | 6 ++++++ libp2p/Common.h | 3 +++ libp2p/NodeTable.cpp | 14 +++----------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/libp2p/Common.cpp b/libp2p/Common.cpp index 86e1135c4..b995723c3 100644 --- a/libp2p/Common.cpp +++ b/libp2p/Common.cpp @@ -109,3 +109,9 @@ std::string p2p::reasonOf(DisconnectReason _r) default: return "Unknown reason."; } } + +void Node::cullEndpoint() +{ + if (!isPublicAddress(endpoint.tcp.address()) && isPublicAddress(endpoint.udp.address())) + endpoint.tcp.address(endpoint.udp.address()); +} diff --git a/libp2p/Common.h b/libp2p/Common.h index b35cb379c..c3ca325d9 100644 --- a/libp2p/Common.h +++ b/libp2p/Common.h @@ -175,6 +175,9 @@ struct Node virtual NodeId const& address() const { return id; } virtual Public const& publicKey() const { return id; } + /// Replace TCP address with UDP address if TCP addr is private and UDP is public. Temp. until protocol update. + void cullEndpoint(); + NodeId id; /// Endpoints by which we expect to reach node. diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 3cba9ec0f..c230c5441 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -98,12 +98,9 @@ shared_ptr NodeTable::addNode(Node const& _node) return m_nodes[_node.id]; } - // TODO: SECURITY - Temporary until packets are updated. - NodeIPEndpoint ep(_node.endpoint.udp, _node.endpoint.tcp); - if (!isPublicAddress(ep.tcp.address()) && isPublicAddress(ep.udp.address())) - ep.tcp.address(_node.endpoint.udp.address()); - shared_ptr ret(new NodeEntry(m_node, _node.id, ep)); + shared_ptr ret(new NodeEntry(m_node, _node.id, NodeIPEndpoint(_node.endpoint.udp, _node.endpoint.tcp))); m_nodes[_node.id] = ret; + ret->cullEndpoint(); PingNode p(_node.endpoint.udp, m_node.endpoint.udp.address().to_string(), m_node.endpoint.udp.port()); p.sign(m_secret); m_socketPointer->send(p); @@ -315,14 +312,9 @@ void NodeTable::noteActiveNode(Public const& _pubk, bi::udp::endpoint const& _en if (!!node && !node->pending) { clog(NodeTableConnect) << "Noting active node:" << _pubk.abridged() << _endpoint.address().to_string() << ":" << _endpoint.port(); - - // TODO: SECURITY - Temporary until packets are updated. - // update udp endpoint and override tcp endpoint if tcp endpoint isn't public - // (for the rare case where NAT port is mapped but node->endpoint.udp.address(_endpoint.address()); node->endpoint.udp.port(_endpoint.port()); - if (!isPublicAddress(node->endpoint.tcp.address()) && isPublicAddress(node->endpoint.udp.address())) - node->endpoint.tcp.address(_endpoint.address()); + node->cullEndpoint(); shared_ptr contested; { From 14e83ffb284f28af9f8576bedd87588fe5ba219e Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 26 Mar 2015 19:57:32 +0100 Subject: [PATCH 114/228] clarify method doc --- libp2p/Common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libp2p/Common.h b/libp2p/Common.h index c3ca325d9..3303c9c07 100644 --- a/libp2p/Common.h +++ b/libp2p/Common.h @@ -175,7 +175,7 @@ struct Node virtual NodeId const& address() const { return id; } virtual Public const& publicKey() const { return id; } - /// Replace TCP address with UDP address if TCP addr is private and UDP is public. Temp. until protocol update. + /// Adopt UDP address for TCP if TCP isn't public and UDP is. (to be removed when protocol is updated for nat) void cullEndpoint(); NodeId id; From 88084c835ef19d545be8114d96e344db2d4b29df Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 26 Mar 2015 20:04:28 +0100 Subject: [PATCH 115/228] Encode according to JSON. --- abi/main.cpp | 215 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 157 insertions(+), 58 deletions(-) diff --git a/abi/main.cpp b/abi/main.cpp index 4e6caa5d5..7a5fbc584 100644 --- a/abi/main.cpp +++ b/abi/main.cpp @@ -205,6 +205,23 @@ struct ABIType void noteDecimalInput() { if (base == Base::Unknown) { base = Base::Uint; size = 32; } } }; +bytes aligned(bytes const& _b, ABIType _t, Format _f, unsigned _length) +{ + (void)_t; + bytes ret = _b; + while (ret.size() < _length) + if (_f == Format::Binary) + ret.push_back(0); + else + ret.insert(ret.begin(), 0); + while (ret.size() > _length) + if (_f == Format::Binary) + ret.pop_back(); + else + ret.erase(ret.begin()); + return ret; +} + tuple fromUser(std::string const& _arg, Tristate _prefix, Tristate _typing) { ABIType type; @@ -260,7 +277,7 @@ struct ABIMethod string name; vector ins; vector outs; - bool isConstant; + bool isConstant = false; // isolation *IS* documentation. @@ -284,6 +301,12 @@ struct ABIMethod } } + ABIMethod(string const& _name, vector const& _args) + { + name = _name; + ins = _args; + } + string sig() const { string methodArgs; @@ -292,24 +315,123 @@ struct ABIMethod return name + "(" + methodArgs + ")"; } FixedHash<4> id() const { return FixedHash<4>(sha3(sig())); } + + std::string solidityDeclaration() const + { + ostringstream ss; + ss << "function " << name << "("; + int f = 0; + for (ABIType const& i: ins) + ss << (f++ ? ", " : "") << i.canon() << " " << i.name; + ss << ") "; + if (isConstant) + ss << "constant "; + if (!outs.empty()) + { + ss << "returns ("; + f = 0; + for (ABIType const& i: outs) + ss << (f ? ", " : "") << i.canon() << " " << i.name; + ss << ")"; + } + return ss.str(); + } + + bytes encode(vector> const& _params) const + { + bytes ret = name.empty() ? bytes() : id().asBytes(); + unsigned pi = 0; + vector inArity; + for (ABIType const& i: ins) + { + unsigned arity = 1; + for (auto j: i.dims) + if (j == -1) + { + ret += aligned(_params[pi].first, ABIType(), Format::Decimal, 32); + arity *= fromBigEndian(_params[pi].first); + pi++; + } + else + arity *= j; + inArity.push_back(arity); + } + unsigned ii = 0; + for (ABIType const& i: ins) + { + for (unsigned j = 0; j < inArity[ii]; ++j) + { + ret += aligned(_params[pi].first, i, _params[pi].second, (i.base == Base::Bytes && i.size == 1) ? 1 : 32); + ++pi; + } + ++ii; + while (ret.size() % 32 != 0) + ret.push_back(0); + } + return ret; + } }; -using ABI = map, ABIMethod>; +string canonSig(string const& _name, vector const& _args) +{ + string methodArgs; + for (auto const& arg: _args) + methodArgs += (methodArgs.empty() ? "" : ",") + arg.canon(); + return _name + "(" + methodArgs + ")"; +} + +struct UnknownMethod: public Exception {}; +struct OverloadedMethod: public Exception {}; -ABI readABI(std::string const& _json) +class ABI { - ABI ret; - js::mValue v; - js::read_string(_json, v); - for (auto const& i: v.get_array()) +public: + ABI() = default; + ABI(std::string const& _json) { - js::mObject o = i.get_obj(); - if (o["type"].get_str() != "function") - continue; - ABIMethod m(o); - ret[m.id()] = m; + js::mValue v; + js::read_string(_json, v); + for (auto const& i: v.get_array()) + { + js::mObject o = i.get_obj(); + if (o["type"].get_str() != "function") + continue; + ABIMethod m(o); + m_methods[m.id()] = m; + } } - return ret; + + ABIMethod method(string _nameOrSig, vector const& _args) const + { + auto id = FixedHash<4>(sha3(_nameOrSig)); + if (!m_methods.count(id)) + id = FixedHash<4>(sha3(canonSig(_nameOrSig, _args))); + if (!m_methods.count(id)) + for (auto const& m: m_methods) + if (m.second.name == _nameOrSig) + { + if (m_methods.count(id)) + throw OverloadedMethod(); + id = m.first; + } + if (m_methods.count(id)) + return m_methods.at(id); + throw UnknownMethod(); + } + + friend ostream& operator<<(ostream& _out, ABI const& _abi); + +private: + map, ABIMethod> m_methods; +}; + +ostream& operator<<(ostream& _out, ABI const& _abi) +{ + _out << "contract {" << endl; + for (auto const& i: _abi.m_methods) + _out << " " << i.second.solidityDeclaration() << "; // " << i.first.abridged() << endl; + _out << "}" << endl; + return _out; } void userOutput(ostream& _out, bytes const& _data, Encoding _e) @@ -324,20 +446,11 @@ void userOutput(ostream& _out, bytes const& _data, Encoding _e) } } -bytes aligned(bytes const& _b, ABIType _t, Format _f, unsigned _length) +template vector(T()))>::type> retrieve(vector const& _t) { - (void)_t; - bytes ret = _b; - while (ret.size() < _length) - if (_f == Format::Binary) - ret.push_back(0); - else - ret.insert(ret.begin(), 0); - while (ret.size() > _length) - if (_f == Format::Binary) - ret.pop_back(); - else - ret.erase(ret.begin()); + vector(T()))>::type> ret; + for (T const& i: _t) + ret.push_back(get(i)); return ret; } @@ -353,7 +466,8 @@ int main(int argc, char** argv) bool clearNulls = false; bool verbose = false; int outputIndex = -1; - vector> args; + vector> params; + vector args; for (int i = 1; i < argc; ++i) { @@ -395,7 +509,11 @@ int main(int argc, char** argv) else if (method.empty()) method = arg; else - args.push_back(fromUser(arg, prefix, typePrefix)); + { + auto u = fromUser(arg, prefix, typePrefix); + args.push_back(get<1>(u)); + params.push_back(make_pair(get<0>(u), get<2>(u))); + } } string abiData; @@ -407,43 +525,24 @@ int main(int argc, char** argv) if (mode == Mode::Encode) { - bytes ret; + ABIMethod m; if (abiData.empty()) - { - if (!method.empty()) - { - string methodArgs; - for (auto const& arg: args) - methodArgs += (methodArgs.empty() ? "" : ",") + get<1>(arg).canon(); - ret = FixedHash<4>(sha3(method + "(" + methodArgs + ")")).asBytes(); - if (verbose) - cerr << "Method signature: " << (method + "(" + methodArgs + ")") << endl; - } - for (tuple const& arg: args) - ret += aligned(get<0>(arg), get<1>(arg), get<2>(arg), 32); - } + m = ABIMethod(method, args); else { - ABI abi = readABI(abiData); + ABI abi(abiData); if (verbose) + cerr << "ABI:" << endl << abi; + try { + m = abi.method(method, args); + } + catch(...) { - cerr << "ABI:" << endl; - for (auto const& i: abi) - { - ABIMethod const& m = i.second; - cerr << " " << i.first.abridged() << ": function " << m.name; - int f = 0; - for (ABIType const& i: m.ins) - cerr << (f++ ? ", " : "(") << i.canon() << " " << i.name; - cerr << ") returns ("; - f = 0; - for (ABIType const& i: m.outs) - cerr << (f ? ", " : "(") << i.canon() << " " << i.name; - cerr << ")" << endl; - } + cerr << "Unknown method in ABI." << endl; + exit(-1); } } - userOutput(cout, ret, encoding); + userOutput(cout, m.encode(params), encoding); } else if (mode == Mode::Decode) { From 4da7a0f4c63579169efe3b6eb67ff3d36db74556 Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 26 Mar 2015 20:14:35 +0100 Subject: [PATCH 116/228] initial requirepeer --- libp2p/Host.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ libp2p/Host.h | 10 ++++++++-- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 41c2fbb77..f833efbc5 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -437,6 +437,53 @@ void Host::addNode(NodeId const& _node, std::string const& _addr, unsigned short if (m_nodeTable) m_nodeTable->addNode(Node(_node, NodeIPEndpoint(bi::udp::endpoint(addr, _udpNodePort), bi::tcp::endpoint(addr, _tcpPeerPort)))); } +void Host::requirePeer(NodeId const& _n, std::string const& _addr, unsigned short _port) +{ + auto addr = bi::address::from_string(_addr); + Node node(_n, NodeIPEndpoint(bi::udp::endpoint(addr, _port), bi::tcp::endpoint(addr, _port))); + if (_n) + { + // add or replace peer + shared_ptr p; + RecursiveGuard l(x_sessions); + if (m_peers.count(_n)) + p = m_peers[_n]; + else + { + // TODO p2p: construct peer from node + p.reset(new Peer()); + p->id = _n; + p->endpoint = NodeIPEndpoint(node.endpoint.udp, node.endpoint.tcp); + p->required = true; + m_peers[_n] = p; + } + p->endpoint.udp = node.endpoint.udp; + p->endpoint.tcp = node.endpoint.tcp; + } + else if (m_nodeTable) + { + shared_ptr t(new boost::asio::deadline_timer(m_ioService)); + m_timers.push_back(t); + + m_nodeTable->addNode(node); + t->expires_from_now(boost::posix_time::milliseconds(600)); + t->async_wait([this, _n](boost::system::error_code const& _ec) + { + if (!_ec && m_nodeTable) + if (auto n = m_nodeTable->node(_n)) + if (!m_peers.count(_n)) + { + shared_ptr p(new Peer()); + p->id = _n; + p->endpoint = n.endpoint; + p->required = true; + RecursiveGuard l(x_sessions); + m_peers[_n] = p; + } + }); + } +} + void Host::connect(std::shared_ptr const& _p) { if (!m_run) diff --git a/libp2p/Host.h b/libp2p/Host.h index 0feda364f..e27bc8855 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -70,9 +70,7 @@ private: * @brief The Host class * Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe. * - * @todo cleanup startPeerSession * @todo determinePublic: ipv6, udp - * @todo handle conflict if addNode/requireNode called and Node already exists w/conflicting tcp or udp port * @todo per-session keepalive/ping instead of broadcast; set ping-timeout via median-latency */ class Host: public Worker @@ -106,7 +104,13 @@ public: /// Add node as a peer candidate. Node is added if discovery ping is successful and table has capacity. void addNode(NodeId const& _node, std::string const& _addr, unsigned short _tcpPort, unsigned short _udpPort); + + /// Create Peer and attempt keeping peer connected. + void requirePeer(NodeId const& _node, std::string const& _addr, unsigned short _port); +// /// Note peer as no longer being required. +// void relinquishPeer(NodeId const& _node); + /// Set ideal number of peers. void setIdealPeerCount(unsigned _n) { m_idealPeerCount = _n; } @@ -211,6 +215,8 @@ private: /// Shared storage of Peer objects. Peers are created or destroyed on demand by the Host. Active sessions maintain a shared_ptr to a Peer; std::map> m_peers; + + std::list> m_timers; /// The nodes to which we are currently connected. Used by host to service peer requests and keepAlivePeers and for shutdown. (see run()) /// Mutable because we flush zombie entries (null-weakptrs) as regular maintenance from a const method. From c887d5c0d23171f39bdcd0d5695b11a57e5b83de Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 26 Mar 2015 21:28:53 +0100 Subject: [PATCH 117/228] Don't create address from empty string. Use member property instead of parameter. --- libp2p/Host.cpp | 22 +++++++++++----------- libp2p/Host.h | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 3f2cebff2..5251fd2e6 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -287,37 +287,37 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e) } } -void Host::determinePublic(NetworkPreferences const& _netPrefs) +void Host::determinePublic() { // set m_tcpPublic := listenIP (if public) > public > upnp > unspecified address. auto ifAddresses = Network::getInterfaceAddresses(); - auto laddr = bi::address::from_string(_netPrefs.listenIPAddress); + auto laddr = m_netPrefs.listenIPAddress.empty() ? bi::address() : bi::address::from_string(m_netPrefs.listenIPAddress); auto lset = !laddr.is_unspecified(); - auto paddr = bi::address::from_string(_netPrefs.publicIPAddress); + auto paddr = m_netPrefs.publicIPAddress.empty() ? bi::address() : bi::address::from_string(m_netPrefs.publicIPAddress); auto pset = !paddr.is_unspecified(); bool listenIsPublic = lset && isPublicAddress(laddr); bool publicIsHost = !lset && pset && ifAddresses.count(paddr); - bi::tcp::endpoint ep(bi::address(), _netPrefs.listenPort); - if (_netPrefs.traverseNAT && listenIsPublic) + bi::tcp::endpoint ep(bi::address(), m_netPrefs.listenPort); + if (m_netPrefs.traverseNAT && listenIsPublic) { clog(NetNote) << "Listen address set to Public address:" << laddr << ". UPnP disabled."; ep.address(laddr); } - else if (_netPrefs.traverseNAT && publicIsHost) + else if (m_netPrefs.traverseNAT && publicIsHost) { clog(NetNote) << "Public address set to Host configured address:" << paddr << ". UPnP disabled."; ep.address(paddr); } - else if (_netPrefs.traverseNAT) + else if (m_netPrefs.traverseNAT) { bi::address natIFAddr; if (lset && ifAddresses.count(laddr)) - ep = Network::traverseNAT(std::set({laddr}), _netPrefs.listenPort, natIFAddr); + ep = Network::traverseNAT(std::set({laddr}), m_netPrefs.listenPort, natIFAddr); else - ep = Network::traverseNAT(ifAddresses, _netPrefs.listenPort, natIFAddr); + ep = Network::traverseNAT(ifAddresses, m_netPrefs.listenPort, natIFAddr); if (lset && natIFAddr != laddr) // if listen address is set we use it, even if upnp returns different @@ -583,13 +583,13 @@ void Host::startedWorking() h.second->onStarting(); // try to open acceptor (todo: ipv6) - m_listenPort = Network::tcp4Listen(m_tcp4Acceptor, m_netPrefs.listenPort); + m_listenPort = Network::tcp4Listen(m_tcp4Acceptor, m_netPrefs); // determine public IP, but only if we're able to listen for connections // todo: GUI when listen is unavailable in UI if (m_listenPort) { - determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp); + determinePublic(); if (m_listenPort > 0) runAcceptor(); diff --git a/libp2p/Host.h b/libp2p/Host.h index e1fe9de68..fe0b9a148 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -155,7 +155,7 @@ private: bool havePeerSession(NodeId _id) { RecursiveGuard l(x_sessions); return m_sessions.count(_id) ? !!m_sessions[_id].lock() : false; } /// Determines and sets m_tcpPublic to publicly advertised address. - void determinePublic(NetworkPreferences const& _netPrefs); + void determinePublic(); void connect(std::shared_ptr const& _p); From 11d2446247529da3182586ce9f520cd9c54ea5d3 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 27 Mar 2015 07:50:43 +0100 Subject: [PATCH 118/228] api cleanup. connect peer when required. --- libp2p/Host.cpp | 53 ++++++++++++++++++++++++++----------------------- libp2p/Host.h | 10 +++++++--- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 7c8bed71a..87922f016 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -423,28 +423,39 @@ void Host::addNode(NodeId const& _node, std::string const& _addr, unsigned short if (m_nodeTable) m_nodeTable->addNode(Node(_node, NodeIPEndpoint(bi::udp::endpoint(addr, _udpNodePort), bi::tcp::endpoint(addr, _tcpPeerPort)))); } -void Host::requirePeer(NodeId const& _n, std::string const& _addr, unsigned short _port) +void Host::relinquishPeer(NodeId const& _node) { - auto addr = bi::address::from_string(_addr); - Node node(_n, NodeIPEndpoint(bi::udp::endpoint(addr, _port), bi::tcp::endpoint(addr, _port))); + Guard l(x_requiredPeers); + if (m_requiredPeers.count(_node)) + m_requiredPeers.erase(_node); +} + +void Host::requirePeer(NodeId const& _n, std::string const& _udpAddr, unsigned short _udpPort, std::string const& _tcpAddr, unsigned short _tcpPort) +{ + auto naddr = bi::address::from_string(_udpAddr); + auto paddr = _tcpAddr.empty() ? naddr : bi::address::from_string(_tcpAddr); + auto udp = bi::udp::endpoint(naddr, _udpPort); + auto tcp = bi::tcp::endpoint(paddr, _tcpPort ? _tcpPort : _udpPort); + Node node(_n, NodeIPEndpoint(udp, tcp)); if (_n) { // add or replace peer shared_ptr p; - RecursiveGuard l(x_sessions); - if (m_peers.count(_n)) - p = m_peers[_n]; - else { - // TODO p2p: construct peer from node - p.reset(new Peer()); - p->id = _n; - p->endpoint = NodeIPEndpoint(node.endpoint.udp, node.endpoint.tcp); - p->required = true; - m_peers[_n] = p; + RecursiveGuard l(x_sessions); + if (m_peers.count(_n)) + p = m_peers[_n]; + else + { + p.reset(new Peer()); + p->id = _n; + p->required = true; + m_peers[_n] = p; + } + p->endpoint.udp = node.endpoint.udp; + p->endpoint.tcp = node.endpoint.tcp; } - p->endpoint.udp = node.endpoint.udp; - p->endpoint.tcp = node.endpoint.tcp; + connect(p); } else if (m_nodeTable) { @@ -456,16 +467,8 @@ void Host::requirePeer(NodeId const& _n, std::string const& _addr, unsigned shor t->async_wait([this, _n](boost::system::error_code const& _ec) { if (!_ec && m_nodeTable) - if (auto n = m_nodeTable->node(_n)) - if (!m_peers.count(_n)) - { - shared_ptr p(new Peer()); - p->id = _n; - p->endpoint = n.endpoint; - p->required = true; - RecursiveGuard l(x_sessions); - m_peers[_n] = p; - } + if (auto n = m_nodeTable->node(_n)) + requirePeer(n.id, n.endpoint.udp.address().to_string(), n.endpoint.udp.port(), n.endpoint.tcp.address().to_string(), n.endpoint.tcp.port()); }); } } diff --git a/libp2p/Host.h b/libp2p/Host.h index bb16d079e..7a18380c3 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -106,10 +106,10 @@ public: void addNode(NodeId const& _node, std::string const& _addr, unsigned short _tcpPort, unsigned short _udpPort); /// Create Peer and attempt keeping peer connected. - void requirePeer(NodeId const& _node, std::string const& _addr, unsigned short _port); + void requirePeer(NodeId const& _node, std::string const& _udpAddr, unsigned short _udpPort, std::string const& _tcpAddr = "", unsigned short _tcpPort = 0); -// /// Note peer as no longer being required. -// void relinquishPeer(NodeId const& _node); + /// Note peer as no longer being required. + void relinquishPeer(NodeId const& _node); /// Set ideal number of peers. void setIdealPeerCount(unsigned _n) { m_idealPeerCount = _n; } @@ -216,6 +216,10 @@ private: /// Shared storage of Peer objects. Peers are created or destroyed on demand by the Host. Active sessions maintain a shared_ptr to a Peer; std::map> m_peers; + /// Peers we try to connect regardless of p2p network. + std::set m_requiredPeers; + Mutex x_requiredPeers; + std::list> m_timers; /// The nodes to which we are currently connected. Used by host to service peer requests and keepAlivePeers and for shutdown. (see run()) From c19013da816440f0ad45f8e116eb778604795c08 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 27 Mar 2015 09:12:48 +0100 Subject: [PATCH 119/228] Remove initial event from JSONRPC. Reduce verbosity. --- alethzero/MainWin.cpp | 2 ++ libethereum/ABI.cpp | 27 ++++++++++++++++ libethereum/ABI.h | 63 ++++++++++++++++++++++++++++++++++++++ libethereum/Client.cpp | 14 ++++----- libethereum/Client.h | 32 +------------------ libethereum/ClientBase.cpp | 10 +++--- libethereum/ClientBase.h | 5 ++- 7 files changed, 110 insertions(+), 43 deletions(-) create mode 100644 libethereum/ABI.cpp create mode 100644 libethereum/ABI.h diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 0ee2961a1..36b6176f4 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -263,6 +263,7 @@ unsigned Main::installWatch(LogFilter const& _tf, WatchHandler const& _f) { auto ret = ethereum()->installWatch(_tf); m_handlers[ret] = _f; + _f(LocalisedLogEntries()); return ret; } @@ -270,6 +271,7 @@ unsigned Main::installWatch(dev::h256 _tf, WatchHandler const& _f) { auto ret = ethereum()->installWatch(_tf, Reaping::Manual); m_handlers[ret] = _f; + _f(LocalisedLogEntries()); return ret; } diff --git a/libethereum/ABI.cpp b/libethereum/ABI.cpp new file mode 100644 index 000000000..eada1c419 --- /dev/null +++ b/libethereum/ABI.cpp @@ -0,0 +1,27 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file ABI.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "ABI.h" + +using namespace std; +using namespace dev; +using namespace dev::eth; + diff --git a/libethereum/ABI.h b/libethereum/ABI.h new file mode 100644 index 000000000..fabda4dc2 --- /dev/null +++ b/libethereum/ABI.h @@ -0,0 +1,63 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file ABI.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +namespace dev +{ +namespace eth +{ + +#include +#include +#include + +template struct ABISerialiser {}; +template struct ABISerialiser> { static bytes serialise(FixedHash const& _t) { static_assert(N <= 32, "Cannot serialise hash > 32 bytes."); static_assert(N > 0, "Cannot serialise zero-length hash."); return bytes(32 - N, 0) + _t.asBytes(); } }; +template <> struct ABISerialiser { static bytes serialise(u256 const& _t) { return h256(_t).asBytes(); } }; +template <> struct ABISerialiser { static bytes serialise(u160 const& _t) { return bytes(12, 0) + h160(_t).asBytes(); } }; +template <> struct ABISerialiser { static bytes serialise(string32 const& _t) { return bytesConstRef((byte const*)_t.data(), 32).toBytes(); } }; + +inline bytes abiInAux() { return {}; } +template bytes abiInAux(T const& _t, U const& ... _u) +{ + return ABISerialiser::serialise(_t) + abiInAux(_u ...); +} + +template bytes abiIn(std::string _id, T const& ... _t) +{ + return sha3(_id).ref().cropped(0, 4).toBytes() + abiInAux(_t ...); +} + +template struct ABIDeserialiser {}; +template struct ABIDeserialiser> { static FixedHash deserialise(bytesConstRef& io_t) { static_assert(N <= 32, "Parameter sizes must be at most 32 bytes."); FixedHash ret; io_t.cropped(32 - N, N).populate(ret.ref()); io_t = io_t.cropped(32); return ret; } }; +template <> struct ABIDeserialiser { static u256 deserialise(bytesConstRef& io_t) { u256 ret = fromBigEndian(io_t.cropped(0, 32)); io_t = io_t.cropped(32); return ret; } }; +template <> struct ABIDeserialiser { static u160 deserialise(bytesConstRef& io_t) { u160 ret = fromBigEndian(io_t.cropped(12, 20)); io_t = io_t.cropped(32); return ret; } }; +template <> struct ABIDeserialiser { static string32 deserialise(bytesConstRef& io_t) { string32 ret; io_t.cropped(0, 32).populate(bytesRef((byte*)ret.data(), 32)); io_t = io_t.cropped(32); return ret; } }; + +template T abiOut(bytes const& _data) +{ + bytesConstRef o(&_data); + return ABIDeserialiser::deserialise(o); +} + +} +} diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 5af2bebd3..54f23dc84 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -250,14 +250,14 @@ void Client::noteChanged(h256Set const& _filters) if (_filters.size()) cnote << "noteChanged(" << _filters << ")"; // accrue all changes left in each filter into the watches. - for (auto& i: m_watches) - if (_filters.count(i.second.id)) + for (auto& w: m_watches) + if (_filters.count(w.second.id)) { - cwatch << "!!!" << i.first << i.second.id; - if (m_filters.count(i.second.id)) - i.second.changes += m_filters.at(i.second.id).changes; - else - i.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0)); + cwatch << "!!!" << w.first << w.second.id; + if (m_filters.count(w.second.id)) // Normal filtering watch + w.second.changes += m_filters.at(w.second.id).changes; + else // Special ('pending'/'latest') watch + w.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0)); } // clear the filters now. for (auto& i: m_filters) diff --git a/libethereum/Client.h b/libethereum/Client.h index dad0ca664..013f797b4 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -41,6 +41,7 @@ #include "State.h" #include "CommonNet.h" #include "Miner.h" +#include "ABI.h" #include "ClientBase.h" namespace dev @@ -71,37 +72,6 @@ private: std::string m_path; }; -static const int GenesisBlock = INT_MIN; - -template struct ABISerialiser {}; -template struct ABISerialiser> { static bytes serialise(FixedHash const& _t) { static_assert(N <= 32, "Cannot serialise hash > 32 bytes."); static_assert(N > 0, "Cannot serialise zero-length hash."); return bytes(32 - N, 0) + _t.asBytes(); } }; -template <> struct ABISerialiser { static bytes serialise(u256 const& _t) { return h256(_t).asBytes(); } }; -template <> struct ABISerialiser { static bytes serialise(u160 const& _t) { return bytes(12, 0) + h160(_t).asBytes(); } }; -template <> struct ABISerialiser { static bytes serialise(string32 const& _t) { return bytesConstRef((byte const*)_t.data(), 32).toBytes(); } }; - -inline bytes abiInAux() { return {}; } -template bytes abiInAux(T const& _t, U const& ... _u) -{ - return ABISerialiser::serialise(_t) + abiInAux(_u ...); -} - -template bytes abiIn(std::string _id, T const& ... _t) -{ - return sha3(_id).ref().cropped(0, 4).toBytes() + abiInAux(_t ...); -} - -template struct ABIDeserialiser {}; -template struct ABIDeserialiser> { static FixedHash deserialise(bytesConstRef& io_t) { static_assert(N <= 32, "Parameter sizes must be at most 32 bytes."); FixedHash ret; io_t.cropped(32 - N, N).populate(ret.ref()); io_t = io_t.cropped(32); return ret; } }; -template <> struct ABIDeserialiser { static u256 deserialise(bytesConstRef& io_t) { u256 ret = fromBigEndian(io_t.cropped(0, 32)); io_t = io_t.cropped(32); return ret; } }; -template <> struct ABIDeserialiser { static u160 deserialise(bytesConstRef& io_t) { u160 ret = fromBigEndian(io_t.cropped(12, 20)); io_t = io_t.cropped(32); return ret; } }; -template <> struct ABIDeserialiser { static string32 deserialise(bytesConstRef& io_t) { string32 ret; io_t.cropped(0, 32).populate(bytesRef((byte*)ret.data(), 32)); io_t = io_t.cropped(32); return ret; } }; - -template T abiOut(bytes const& _data) -{ - bytesConstRef o(&_data); - return ABIDeserialiser::deserialise(o); -} - class RemoteMiner: public Miner { public: diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index f9caf98ac..de3249991 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -224,6 +224,7 @@ unsigned ClientBase::installWatch(h256 _h, Reaping _r) m_watches[ret] = ClientWatch(_h, _r); cwatch << "+++" << ret << _h.abridged(); } +#if INITIAL_STATE_AS_CHANGES auto ch = logs(ret); if (ch.empty()) ch.push_back(InitialChange); @@ -231,6 +232,7 @@ unsigned ClientBase::installWatch(h256 _h, Reaping _r) Guard l(x_filtersWatches); swap(m_watches[ret].changes, ch); } +#endif return ret; } @@ -260,9 +262,9 @@ LocalisedLogEntries ClientBase::peekWatch(unsigned _watchId) const { Guard l(x_filtersWatches); - cwatch << "peekWatch" << _watchId; +// cwatch << "peekWatch" << _watchId; auto& w = m_watches.at(_watchId); - cwatch << "lastPoll updated to " << chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); +// cwatch << "lastPoll updated to " << chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); w.lastPoll = chrono::system_clock::now(); return w.changes; } @@ -272,9 +274,9 @@ LocalisedLogEntries ClientBase::checkWatch(unsigned _watchId) Guard l(x_filtersWatches); LocalisedLogEntries ret; - cwatch << "checkWatch" << _watchId; +// cwatch << "checkWatch" << _watchId; auto& w = m_watches.at(_watchId); - cwatch << "lastPoll updated to " << chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); +// cwatch << "lastPoll updated to " << chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); std::swap(ret, w.changes); w.lastPoll = chrono::system_clock::now(); diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index 9d9482277..ba4f9e0c4 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -51,7 +51,11 @@ struct ClientWatch explicit ClientWatch(h256 _id, Reaping _r): id(_id), lastPoll(_r == Reaping::Automatic ? std::chrono::system_clock::now() : std::chrono::system_clock::time_point::max()) {} h256 id; +#if INITIAL_STATE_AS_CHANGES LocalisedLogEntries changes = LocalisedLogEntries{ InitialChange }; +#else + LocalisedLogEntries changes; +#endif mutable std::chrono::system_clock::time_point lastPoll = std::chrono::system_clock::now(); }; @@ -161,7 +165,6 @@ protected: mutable Mutex x_filtersWatches; ///< Our lock. std::map m_filters; ///< The dictionary of filters that are active. std::map m_watches; ///< Each and every watch - these reference a filter. - }; }} From 653eece9e140f1cfea554041dd290fdb46fb6339 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 27 Mar 2015 09:19:02 +0100 Subject: [PATCH 120/228] Windows build fix. --- abi/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/abi/main.cpp b/abi/main.cpp index 7a5fbc584..223ff8283 100644 --- a/abi/main.cpp +++ b/abi/main.cpp @@ -349,7 +349,7 @@ struct ABIMethod if (j == -1) { ret += aligned(_params[pi].first, ABIType(), Format::Decimal, 32); - arity *= fromBigEndian(_params[pi].first); + arity *= fromBigEndian(_params[pi].first); pi++; } else From c2392fd81af9fbf45e7b44034a955673db62a75e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 27 Mar 2015 09:57:00 +0100 Subject: [PATCH 121/228] Gaa. Get them out of that namespace! --- abi/main.cpp | 55 ++++++++++++++++++++++++++++++++++++++++------- libethereum/ABI.h | 8 +++---- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/abi/main.cpp b/abi/main.cpp index 223ff8283..22233f1f0 100644 --- a/abi/main.cpp +++ b/abi/main.cpp @@ -162,6 +162,8 @@ struct ABIType { if (base == Base::Fixed) size = ssize = 16; + else if (base == Base::Address || base == Base::Bytes) + size = 0; else size = 32; return; @@ -188,7 +190,7 @@ struct ABIType string ret; switch (base) { - case Base::Bytes: ret = "bytes" + toString(size); break; + case Base::Bytes: ret = "bytes" + (size > 0 ? toString(size) : ""); break; case Base::Address: ret = "address"; break; case Base::Int: ret = "int" + toString(size); break; case Base::Uint: ret = "uint" + toString(size); break; @@ -361,15 +363,30 @@ struct ABIMethod { for (unsigned j = 0; j < inArity[ii]; ++j) { - ret += aligned(_params[pi].first, i, _params[pi].second, (i.base == Base::Bytes && i.size == 1) ? 1 : 32); + if (i.base == Base::Bytes && !i.size) + { + ret += _params[pi].first; + while (ret.size() % 32 != 0) + ret.push_back(0); + } + else + ret += aligned(_params[pi].first, i, _params[pi].second, 32); ++pi; } ++ii; - while (ret.size() % 32 != 0) - ret.push_back(0); } return ret; } + string decode(bytes const& _data, int _index = -1) + { + stringstream out; + if (_index == -1) + out << "["; + (void)_data; + if (_index == -1) + out << "]"; + return out.str(); + } }; string canonSig(string const& _name, vector const& _args) @@ -517,10 +534,7 @@ int main(int argc, char** argv) } string abiData; - if (abiFile == "--") - for (int i = cin.get(); i != -1; i = cin.get()) - abiData.push_back((char)i); - else if (!abiFile.empty()) + if (abiFile.empty()) abiData = contentsString(abiFile); if (mode == Mode::Encode) @@ -546,6 +560,31 @@ int main(int argc, char** argv) } else if (mode == Mode::Decode) { + if (abiData.empty()) + { + cerr << "Please specify an ABI file." << endl; + exit(-1); + } + else + { + ABI abi(abiData); + ABIMethod m; + if (verbose) + cerr << "ABI:" << endl << abi; + try { + m = abi.method(method, args); + } + catch(...) + { + cerr << "Unknown method in ABI." << endl; + exit(-1); + } + string encoded; + for (int i = cin.get(); i != -1; i = cin.get()) + encoded.push_back((char)i); + cout << m.decode(fromHex(encoded)); + } + // TODO: read abi to determine output format. (void)encoding; (void)clearZeroes; diff --git a/libethereum/ABI.h b/libethereum/ABI.h index fabda4dc2..f5cab10b2 100644 --- a/libethereum/ABI.h +++ b/libethereum/ABI.h @@ -21,15 +21,15 @@ #pragma once +#include +#include +#include + namespace dev { namespace eth { -#include -#include -#include - template struct ABISerialiser {}; template struct ABISerialiser> { static bytes serialise(FixedHash const& _t) { static_assert(N <= 32, "Cannot serialise hash > 32 bytes."); static_assert(N > 0, "Cannot serialise zero-length hash."); return bytes(32 - N, 0) + _t.asBytes(); } }; template <> struct ABISerialiser { static bytes serialise(u256 const& _t) { return h256(_t).asBytes(); } }; From d3211d033dab8762e18d74342db6b32fbb4500fa Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 27 Mar 2015 09:57:41 +0100 Subject: [PATCH 122/228] And the rest. --- libethereum/ABI.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libethereum/ABI.h b/libethereum/ABI.h index f5cab10b2..04c623ac5 100644 --- a/libethereum/ABI.h +++ b/libethereum/ABI.h @@ -24,6 +24,7 @@ #include #include #include +#include namespace dev { From 4025e07cc10ab11757d7fff15a2a003f5f4987c0 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 27 Mar 2015 10:11:01 +0100 Subject: [PATCH 123/228] Better highlighting bevahior for codeeditor.js --- mix/qml/html/cm/codemirror.css | 4 +- mix/qml/html/cm/mark-selection.js | 118 ++++++++++++++++++++++++++++++ mix/qml/html/cm/solarized.css | 4 +- mix/qml/html/codeeditor.html | 1 + mix/qml/html/codeeditor.js | 3 +- mix/web.qrc | 1 + 6 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 mix/qml/html/cm/mark-selection.js diff --git a/mix/qml/html/cm/codemirror.css b/mix/qml/html/cm/codemirror.css index bdb3d5347..b182ab305 100644 --- a/mix/qml/html/cm/codemirror.css +++ b/mix/qml/html/cm/codemirror.css @@ -304,7 +304,7 @@ div.CodeMirror-cursors { @media print { /* Hide the cursor when printing */ .CodeMirror div.CodeMirror-cursors { - visibility: hidden; + visibility: hidden; } } @@ -312,4 +312,4 @@ div.CodeMirror-cursors { .cm-tab-wrap-hack:after { content: ''; } /* Help users use markselection to safely style text background */ -span.CodeMirror-selectedtext { background: none; } +span.CodeMirror-selectedtext { color: #586e75; } diff --git a/mix/qml/html/cm/mark-selection.js b/mix/qml/html/cm/mark-selection.js new file mode 100644 index 000000000..5c42d21eb --- /dev/null +++ b/mix/qml/html/cm/mark-selection.js @@ -0,0 +1,118 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +// Because sometimes you need to mark the selected *text*. +// +// Adds an option 'styleSelectedText' which, when enabled, gives +// selected text the CSS class given as option value, or +// "CodeMirror-selectedtext" when the value is not a string. + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) { + var prev = old && old != CodeMirror.Init; + if (val && !prev) { + cm.state.markedSelection = []; + cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext"; + reset(cm); + cm.on("cursorActivity", onCursorActivity); + cm.on("change", onChange); + } else if (!val && prev) { + cm.off("cursorActivity", onCursorActivity); + cm.off("change", onChange); + clear(cm); + cm.state.markedSelection = cm.state.markedSelectionStyle = null; + } + }); + + function onCursorActivity(cm) { + cm.operation(function() { update(cm); }); + } + + function onChange(cm) { + if (cm.state.markedSelection.length) + cm.operation(function() { clear(cm); }); + } + + var CHUNK_SIZE = 8; + var Pos = CodeMirror.Pos; + var cmp = CodeMirror.cmpPos; + + function coverRange(cm, from, to, addAt) { + if (cmp(from, to) == 0) return; + var array = cm.state.markedSelection; + var cls = cm.state.markedSelectionStyle; + for (var line = from.line;;) { + var start = line == from.line ? from : Pos(line, 0); + var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line; + var end = atEnd ? to : Pos(endLine, 0); + var mark = cm.markText(start, end, {className: cls}); + if (addAt == null) array.push(mark); + else array.splice(addAt++, 0, mark); + if (atEnd) break; + line = endLine; + } + } + + function clear(cm) { + var array = cm.state.markedSelection; + for (var i = 0; i < array.length; ++i) array[i].clear(); + array.length = 0; + } + + function reset(cm) { + clear(cm); + var ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) + coverRange(cm, ranges[i].from(), ranges[i].to()); + } + + function update(cm) { + if (!cm.somethingSelected()) return clear(cm); + if (cm.listSelections().length > 1) return reset(cm); + + var from = cm.getCursor("start"), to = cm.getCursor("end"); + + var array = cm.state.markedSelection; + if (!array.length) return coverRange(cm, from, to); + + var coverStart = array[0].find(), coverEnd = array[array.length - 1].find(); + if (!coverStart || !coverEnd || to.line - from.line < CHUNK_SIZE || + cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0) + return reset(cm); + + while (cmp(from, coverStart.from) > 0) { + array.shift().clear(); + coverStart = array[0].find(); + } + if (cmp(from, coverStart.from) < 0) { + if (coverStart.to.line - from.line < CHUNK_SIZE) { + array.shift().clear(); + coverRange(cm, from, coverStart.to, 0); + } else { + coverRange(cm, from, coverStart.from, 0); + } + } + + while (cmp(to, coverEnd.to) < 0) { + array.pop().clear(); + coverEnd = array[array.length - 1].find(); + } + if (cmp(to, coverEnd.to) > 0) { + if (to.line - coverEnd.from.line < CHUNK_SIZE) { + array.pop().clear(); + coverRange(cm, coverEnd.from, to); + } else { + coverRange(cm, coverEnd.to, to); + } + } + } +}); diff --git a/mix/qml/html/cm/solarized.css b/mix/qml/html/cm/solarized.css index ece98a95c..c5d930627 100644 --- a/mix/qml/html/cm/solarized.css +++ b/mix/qml/html/cm/solarized.css @@ -166,7 +166,7 @@ view-port /* Code execution */ .CodeMirror-exechighlight { - background: rgba(255, 255, 255, 0.10); + background: #eee8d5; } /* Error annotation */ @@ -182,3 +182,5 @@ view-port padding: 2px; } +span.CodeMirror-selectedtext { color: #586e75 !important; } + diff --git a/mix/qml/html/codeeditor.html b/mix/qml/html/codeeditor.html index eec13f109..c9d4ff96a 100644 --- a/mix/qml/html/codeeditor.html +++ b/mix/qml/html/codeeditor.html @@ -22,6 +22,7 @@ + diff --git a/mix/qml/html/codeeditor.js b/mix/qml/html/codeeditor.js index daf1286d8..41e9749a3 100644 --- a/mix/qml/html/codeeditor.js +++ b/mix/qml/html/codeeditor.js @@ -4,7 +4,8 @@ var editor = CodeMirror(document.body, { matchBrackets: true, autofocus: true, gutters: ["CodeMirror-linenumbers", "breakpoints"], - autoCloseBrackets: true + autoCloseBrackets: true, + styleSelectedText: true }); var ternServer; diff --git a/mix/web.qrc b/mix/web.qrc index aa5b38a67..63a3a668f 100644 --- a/mix/web.qrc +++ b/mix/web.qrc @@ -39,5 +39,6 @@ qml/html/cm/acorn.js qml/html/cm/acorn_loose.js qml/html/cm/walk.js + qml/html/cm/mark-selection.js From 22195f9ca1a134b0e13d5a657697ed7433a0d9cf Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 27 Mar 2015 09:46:01 +0100 Subject: [PATCH 124/228] Fixed includes. --- libethereum/ABI.h | 9 +++++---- libnatspec/NatspecExpressionEvaluator.h | 1 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libethereum/ABI.h b/libethereum/ABI.h index fabda4dc2..04c623ac5 100644 --- a/libethereum/ABI.h +++ b/libethereum/ABI.h @@ -21,15 +21,16 @@ #pragma once +#include +#include +#include +#include + namespace dev { namespace eth { -#include -#include -#include - template struct ABISerialiser {}; template struct ABISerialiser> { static bytes serialise(FixedHash const& _t) { static_assert(N <= 32, "Cannot serialise hash > 32 bytes."); static_assert(N > 0, "Cannot serialise zero-length hash."); return bytes(32 - N, 0) + _t.asBytes(); } }; template <> struct ABISerialiser { static bytes serialise(u256 const& _t) { return h256(_t).asBytes(); } }; diff --git a/libnatspec/NatspecExpressionEvaluator.h b/libnatspec/NatspecExpressionEvaluator.h index 4aca090d6..f7984f6e8 100644 --- a/libnatspec/NatspecExpressionEvaluator.h +++ b/libnatspec/NatspecExpressionEvaluator.h @@ -21,7 +21,6 @@ #pragma once -#include #include #include From c105714c63301b100f456c1403f5077f2a95c1a1 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 27 Mar 2015 10:54:51 +0100 Subject: [PATCH 125/228] small changes --- mix/qml/html/cm/codemirror.css | 4 +- mix/qml/html/cm/mark-selection.js | 144 +++++++++++++++--------------- mix/qml/html/cm/solarized.css | 1 - 3 files changed, 74 insertions(+), 75 deletions(-) diff --git a/mix/qml/html/cm/codemirror.css b/mix/qml/html/cm/codemirror.css index b182ab305..625baa942 100644 --- a/mix/qml/html/cm/codemirror.css +++ b/mix/qml/html/cm/codemirror.css @@ -311,5 +311,5 @@ div.CodeMirror-cursors { /* See issue #2901 */ .cm-tab-wrap-hack:after { content: ''; } -/* Help users use markselection to safely style text background */ -span.CodeMirror-selectedtext { color: #586e75; } +/* Help users use markselection to safely style text background +span.CodeMirror-selectedtext { color: #586e75; } */ diff --git a/mix/qml/html/cm/mark-selection.js b/mix/qml/html/cm/mark-selection.js index 5c42d21eb..22e46ef32 100644 --- a/mix/qml/html/cm/mark-selection.js +++ b/mix/qml/html/cm/mark-selection.js @@ -9,37 +9,37 @@ (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror")); + mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror"], mod); + define(["../../lib/codemirror"], mod); else // Plain browser env - mod(CodeMirror); + mod(CodeMirror); })(function(CodeMirror) { "use strict"; CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) { - var prev = old && old != CodeMirror.Init; - if (val && !prev) { - cm.state.markedSelection = []; - cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext"; - reset(cm); - cm.on("cursorActivity", onCursorActivity); - cm.on("change", onChange); - } else if (!val && prev) { - cm.off("cursorActivity", onCursorActivity); - cm.off("change", onChange); - clear(cm); - cm.state.markedSelection = cm.state.markedSelectionStyle = null; - } + var prev = old && old != CodeMirror.Init; + if (val && !prev) { + cm.state.markedSelection = []; + cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext"; + reset(cm); + cm.on("cursorActivity", onCursorActivity); + cm.on("change", onChange); + } else if (!val && prev) { + cm.off("cursorActivity", onCursorActivity); + cm.off("change", onChange); + clear(cm); + cm.state.markedSelection = cm.state.markedSelectionStyle = null; + } }); function onCursorActivity(cm) { - cm.operation(function() { update(cm); }); + cm.operation(function() { update(cm); }); } function onChange(cm) { - if (cm.state.markedSelection.length) - cm.operation(function() { clear(cm); }); + if (cm.state.markedSelection.length) + cm.operation(function() { clear(cm); }); } var CHUNK_SIZE = 8; @@ -47,72 +47,72 @@ var cmp = CodeMirror.cmpPos; function coverRange(cm, from, to, addAt) { - if (cmp(from, to) == 0) return; - var array = cm.state.markedSelection; - var cls = cm.state.markedSelectionStyle; - for (var line = from.line;;) { - var start = line == from.line ? from : Pos(line, 0); - var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line; - var end = atEnd ? to : Pos(endLine, 0); - var mark = cm.markText(start, end, {className: cls}); - if (addAt == null) array.push(mark); - else array.splice(addAt++, 0, mark); - if (atEnd) break; - line = endLine; - } + if (cmp(from, to) == 0) return; + var array = cm.state.markedSelection; + var cls = cm.state.markedSelectionStyle; + for (var line = from.line;;) { + var start = line == from.line ? from : Pos(line, 0); + var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line; + var end = atEnd ? to : Pos(endLine, 0); + var mark = cm.markText(start, end, {className: cls}); + if (addAt == null) array.push(mark); + else array.splice(addAt++, 0, mark); + if (atEnd) break; + line = endLine; + } } function clear(cm) { - var array = cm.state.markedSelection; - for (var i = 0; i < array.length; ++i) array[i].clear(); - array.length = 0; + var array = cm.state.markedSelection; + for (var i = 0; i < array.length; ++i) array[i].clear(); + array.length = 0; } function reset(cm) { - clear(cm); - var ranges = cm.listSelections(); - for (var i = 0; i < ranges.length; i++) - coverRange(cm, ranges[i].from(), ranges[i].to()); + clear(cm); + var ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) + coverRange(cm, ranges[i].from(), ranges[i].to()); } function update(cm) { - if (!cm.somethingSelected()) return clear(cm); - if (cm.listSelections().length > 1) return reset(cm); + if (!cm.somethingSelected()) return clear(cm); + if (cm.listSelections().length > 1) return reset(cm); - var from = cm.getCursor("start"), to = cm.getCursor("end"); + var from = cm.getCursor("start"), to = cm.getCursor("end"); - var array = cm.state.markedSelection; - if (!array.length) return coverRange(cm, from, to); + var array = cm.state.markedSelection; + if (!array.length) return coverRange(cm, from, to); - var coverStart = array[0].find(), coverEnd = array[array.length - 1].find(); - if (!coverStart || !coverEnd || to.line - from.line < CHUNK_SIZE || - cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0) - return reset(cm); + var coverStart = array[0].find(), coverEnd = array[array.length - 1].find(); + if (!coverStart || !coverEnd || to.line - from.line < CHUNK_SIZE || + cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0) + return reset(cm); - while (cmp(from, coverStart.from) > 0) { - array.shift().clear(); - coverStart = array[0].find(); - } - if (cmp(from, coverStart.from) < 0) { - if (coverStart.to.line - from.line < CHUNK_SIZE) { - array.shift().clear(); - coverRange(cm, from, coverStart.to, 0); - } else { - coverRange(cm, from, coverStart.from, 0); - } - } + while (cmp(from, coverStart.from) > 0) { + array.shift().clear(); + coverStart = array[0].find(); + } + if (cmp(from, coverStart.from) < 0) { + if (coverStart.to.line - from.line < CHUNK_SIZE) { + array.shift().clear(); + coverRange(cm, from, coverStart.to, 0); + } else { + coverRange(cm, from, coverStart.from, 0); + } + } - while (cmp(to, coverEnd.to) < 0) { - array.pop().clear(); - coverEnd = array[array.length - 1].find(); - } - if (cmp(to, coverEnd.to) > 0) { - if (to.line - coverEnd.from.line < CHUNK_SIZE) { - array.pop().clear(); - coverRange(cm, coverEnd.from, to); - } else { - coverRange(cm, coverEnd.to, to); - } - } + while (cmp(to, coverEnd.to) < 0) { + array.pop().clear(); + coverEnd = array[array.length - 1].find(); + } + if (cmp(to, coverEnd.to) > 0) { + if (to.line - coverEnd.from.line < CHUNK_SIZE) { + array.pop().clear(); + coverRange(cm, coverEnd.from, to); + } else { + coverRange(cm, coverEnd.to, to); + } + } } }); diff --git a/mix/qml/html/cm/solarized.css b/mix/qml/html/cm/solarized.css index 204654d28..d8c31bfb5 100644 --- a/mix/qml/html/cm/solarized.css +++ b/mix/qml/html/cm/solarized.css @@ -183,4 +183,3 @@ view-port } span.CodeMirror-selectedtext { color: #586e75 !important; } - From 4e234b30ed06bff60afda479eaf9190fe11bc27c Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 27 Mar 2015 11:53:17 +0100 Subject: [PATCH 126/228] Moving contract fullyImplemented check to TypeRequirements --- libsolidity/AST.cpp | 4 ++++ libsolidity/AST.h | 10 +++++----- libsolidity/Parser.cpp | 6 +----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 71af3286f..52c58170d 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -60,6 +60,7 @@ void ContractDefinition::checkTypeRequirements() FunctionDefinition const* fallbackFunction = nullptr; for (ASTPointer const& function: getDefinedFunctions()) + { if (function->getName().empty()) { if (fallbackFunction) @@ -71,6 +72,9 @@ void ContractDefinition::checkTypeRequirements() BOOST_THROW_EXCEPTION(fallbackFunction->getParameterList().createTypeError("Fallback function cannot take parameters.")); } } + if (!function->isFullyImplemented()) + setFullyImplemented(false); + } for (ASTPointer const& modifier: getFunctionModifiers()) modifier->checkTypeRequirements(); diff --git a/libsolidity/AST.h b/libsolidity/AST.h index fa0172b10..c1da90cdb 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -206,6 +206,7 @@ public: /// @return whether this node is fully implemented or not bool isFullyImplemented() const { return m_implemented; } + void setFullyImplemented(bool _implemented) { m_implemented = _implemented; } protected: bool m_implemented; @@ -231,12 +232,11 @@ public: std::vector> const& _stateVariables, std::vector> const& _definedFunctions, std::vector> const& _functionModifiers, - std::vector> const& _events, - bool _isFullyImplemented + std::vector> const& _events ): Declaration(_location, _name), Documented(_documentation), - ImplementationOptional(_isFullyImplemented), + ImplementationOptional(true), m_baseContracts(_baseContracts), m_definedStructs(_definedStructs), m_definedEnums(_definedEnums), @@ -244,7 +244,7 @@ public: m_definedFunctions(_definedFunctions), m_functionModifiers(_functionModifiers), m_events(_events) - {} + {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; @@ -421,7 +421,7 @@ public: m_functionModifiers(_modifiers), m_returnParameters(_returnParameters), m_body(_body) - {} + {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 3dfedf6d2..0f1b34069 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -116,7 +116,6 @@ ASTPointer Parser::parseContractDefinition() { ASTNodeFactory nodeFactory(*this); ASTPointer docString; - bool contractFullyImplemented = true; if (m_scanner->getCurrentCommentLiteral() != "") docString = make_shared(m_scanner->getCurrentCommentLiteral()); expectToken(Token::Contract); @@ -145,8 +144,6 @@ ASTPointer Parser::parseContractDefinition() { ASTPointer func = parseFunctionDefinition(name.get()); functions.push_back(func); - if (!func->isFullyImplemented()) - contractFullyImplemented = false; } else if (currentToken == Token::Struct) structs.push_back(parseStructDefinition()); @@ -179,8 +176,7 @@ ASTPointer Parser::parseContractDefinition() stateVariables, functions, modifiers, - events, - contractFullyImplemented + events ); } From 161f4588bb6d015849bd44f6f0efb4c14708b74c Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 27 Mar 2015 13:11:24 +0100 Subject: [PATCH 127/228] Parse test files and create ctest tests. --- CMakeLists.txt | 3 --- test/CMakeLists.txt | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index eb424e745..ae0274f6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -229,9 +229,6 @@ if (NOT JUSTTESTS) endif() -enable_testing() -add_test(NAME alltests WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test COMMAND testeth) - #unset(TARGET_PLATFORM CACHE) if (WIN32) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ef292c2bb..47dc611a5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -16,6 +16,21 @@ include_directories(${Boost_INCLUDE_DIRS}) include_directories(${CRYPTOPP_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) +# search for test names and create ctest tests +enable_testing() +foreach(file ${SRC_LIST}) + file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/${file} test_list_raw REGEX "BOOST_.*TEST_(SUITE|CASE)") + set(TestSuite "DEFAULT") + foreach(test_raw ${test_list_raw}) + string(REGEX REPLACE ".*TEST_(SUITE|CASE)\\(([^ ,\\)]*).*" "\\1 \\2" test ${test_raw}) + if(test MATCHES "^SUITE .*") + string(SUBSTRING ${test} 6 -1 TestSuite) + elseif(test MATCHES "^CASE .*") + string(SUBSTRING ${test} 5 -1 TestCase) + add_test(NAME ${TestSuite}/${TestCase} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test COMMAND testeth -t ${TestSuite}/${TestCase}) + endif(test MATCHES "^SUITE .*") + endforeach(test_raw) +endforeach(file) file(GLOB HEADERS "*.h") add_executable(testeth ${SRC_LIST} ${HEADERS}) From c941c3d9c4f7108087f2ac22545093e113a9482e Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 27 Mar 2015 13:17:52 +0100 Subject: [PATCH 128/228] Small review fixes. --- libevmcore/ExpressionClasses.cpp | 4 ++-- libevmcore/ExpressionClasses.h | 2 +- libevmcore/Instruction.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libevmcore/ExpressionClasses.cpp b/libevmcore/ExpressionClasses.cpp index 49fd72c1b..f9ed7d6fc 100644 --- a/libevmcore/ExpressionClasses.cpp +++ b/libevmcore/ExpressionClasses.cpp @@ -35,7 +35,7 @@ using namespace dev; using namespace dev::eth; -bool ExpressionClasses::Expression::operator<(const ExpressionClasses::Expression& _other) const +bool ExpressionClasses::Expression::operator<(ExpressionClasses::Expression const& _other) const { auto type = item->type(); auto otherType = _other.item->type(); @@ -74,7 +74,7 @@ ExpressionClasses::Id ExpressionClasses::find(AssemblyItem const& _item, Ids con return exp.id; } -string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) +string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const { Expression const& expr = representative(_id); stringstream str; diff --git a/libevmcore/ExpressionClasses.h b/libevmcore/ExpressionClasses.h index ecd9c9250..eda568e23 100644 --- a/libevmcore/ExpressionClasses.h +++ b/libevmcore/ExpressionClasses.h @@ -63,7 +63,7 @@ public: /// @returns the number of classes. Id size() const { return m_representatives.size(); } - std::string fullDAGToString(Id _id); + std::string fullDAGToString(Id _id) const; private: /// Tries to simplify the given expression. diff --git a/libevmcore/Instruction.h b/libevmcore/Instruction.h index c869e761d..07c7b52fd 100644 --- a/libevmcore/Instruction.h +++ b/libevmcore/Instruction.h @@ -237,7 +237,7 @@ enum Tier struct InstructionInfo { std::string name; ///< The name of the instruction. - int additional; //a/< Additional items required in memory for this instructions (only for PUSH). + int additional; ///< Additional items required in memory for this instructions (only for PUSH). int args; ///< Number of items required on the stack for this instruction (and, for the purposes of ret, the number taken from the stack). int ret; ///< Number of items placed (back) on the stack by this instruction, assuming args items were removed. bool sideEffects; ///< false if the only effect on the execution environment (apart from gas usage) is a change to a topmost segment of the stack From dd15c53ae41b36bafdba0bb0500de58f8cee296f Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Fri, 27 Mar 2015 13:28:32 +0100 Subject: [PATCH 129/228] added externalTypes function to functionType removed flag for externalSigniture --- libsolidity/AST.cpp | 8 ++++---- libsolidity/AST.h | 4 ++-- libsolidity/ExpressionCompiler.cpp | 2 +- libsolidity/Types.cpp | 24 +++++++++++++++++++----- libsolidity/Types.h | 6 ++++-- test/SolidityNameAndTypeResolution.cpp | 2 +- 6 files changed, 31 insertions(+), 15 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 461c3d0ca..1736469eb 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -192,7 +192,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn if (functionsSeen.count(f->getName()) == 0 && f->isPartOfExternalInterface()) { functionsSeen.insert(f->getName()); - FixedHash<4> hash(dev::sha3(f->externalSignature(true))); + FixedHash<4> hash(dev::sha3(f->externalSignature())); m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*f, false))); } @@ -202,7 +202,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn FunctionType ftype(*v); solAssert(v->getType().get(), ""); functionsSeen.insert(v->getName()); - FixedHash<4> hash(dev::sha3(ftype.externalSignature(true, v->getName()))); + FixedHash<4> hash(dev::sha3(ftype.externalSignature(v->getName()))); m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*v))); } } @@ -320,9 +320,9 @@ void FunctionDefinition::checkTypeRequirements() m_body->checkTypeRequirements(); } -string FunctionDefinition::externalSignature(bool isExternalCall) const +string FunctionDefinition::externalSignature() const { - return FunctionType(*this).externalSignature(isExternalCall, getName()); + return FunctionType(*this).externalSignature(getName()); } bool VariableDeclaration::isLValue() const diff --git a/libsolidity/AST.h b/libsolidity/AST.h index abcae83c5..00ad33073 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -422,9 +422,9 @@ public: void checkTypeRequirements(); /// @returns the external signature of the function - /// That consists of the name of the function followed by the types (external types if isExternalCall) of the + /// That consists of the name of the function followed by the types of the /// arguments separated by commas all enclosed in parentheses without any spaces. - std::string externalSignature(bool isExternalCall = false) const; + std::string externalSignature() const; private: bool m_isConstructor; diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index ddc347e68..90568767b 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -544,7 +544,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } if (!event.isAnonymous()) { - m_context << u256(h256::Arith(dev::sha3(function.externalSignature(false, event.getName())))); + m_context << u256(h256::Arith(dev::sha3(function.externalSignature(event.getName())))); ++numIndexed; } solAssert(numIndexed <= 4, "Too many indexed arguments."); diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 1776413a0..5fd7d24a6 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -1098,6 +1098,19 @@ unsigned FunctionType::getSizeOnStack() const return size; } +TypePointer FunctionType::externalType() const +{ + TypePointers paramTypes; + TypePointers retParamTypes; + + for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it) + paramTypes.push_back((*it)->externalType()); + for (auto it = m_returnParameterTypes.cbegin(); it != m_returnParameterTypes.cend(); ++it) + retParamTypes.push_back((*it)->externalType()); + + return make_shared(paramTypes, retParamTypes, m_location, m_arbitraryParameters); +} + MemberList const& FunctionType::getMembers() const { switch (m_location) @@ -1127,7 +1140,7 @@ MemberList const& FunctionType::getMembers() const } } -string FunctionType::externalSignature(bool isExternalCall, std::string const& _name) const +string FunctionType::externalSignature(std::string const& _name) const { std::string funcName = _name; if (_name == "") @@ -1137,12 +1150,13 @@ string FunctionType::externalSignature(bool isExternalCall, std::string const& _ } string ret = funcName + "("; - for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it) + TypePointers externalParameterTypes = dynamic_cast(*externalType()).getParameterTypes(); + for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it) { - if (isExternalCall) - solAssert(!!(*it)->externalType(), "Parameter should have external type"); - ret += (isExternalCall ? (*it)->externalType()->toString() : (*it)->toString()) + (it + 1 == m_parameterTypes.cend() ? "" : ","); + solAssert(!!(*it), "Parameter should have external type"); + ret += (*it)->toString() + (it + 1 == externalParameterTypes.cend() ? "" : ","); } + return ret + ")"; } diff --git a/libsolidity/Types.h b/libsolidity/Types.h index c075ff07f..99fd878f0 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -511,6 +511,9 @@ public: Bare }; virtual Category getCategory() const override { return Category::Function; } + + virtual TypePointer externalType() const override; + explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); explicit FunctionType(VariableDeclaration const& _varDecl); explicit FunctionType(EventDefinition const& _event); @@ -553,8 +556,7 @@ public: /// @returns the external signature of this function type given the function name /// If @a _name is not provided (empty string) then the @c m_declaration member of the /// function type is used - /// @a isExternalCall shows if it is external function call - std::string externalSignature(bool isExternalCall = false, std::string const& _name = "") const; + std::string externalSignature(std::string const& _name = "") const; Declaration const& getDeclaration() const { solAssert(m_declaration, "Requested declaration from a FunctionType that has none"); diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 6b9916549..3bec70a2b 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -414,7 +414,7 @@ BOOST_AUTO_TEST_CASE(function_external_types) auto functions = contract->getDefinedFunctions(); if (functions.empty()) continue; - BOOST_CHECK_EQUAL("boo(uint256,bool,bytes8,bool[2],uint256[],address)", functions[0]->externalSignature(true)); + BOOST_CHECK_EQUAL("boo(uint256,bool,bytes8,bool[2],uint256[],address)", functions[0]->externalSignature()); } } From ff5dfa2e8f16287a29724d7d3fce90da97a75a65 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 27 Mar 2015 14:56:31 +0100 Subject: [PATCH 130/228] Removed duplicate line. --- libevmcore/ExpressionClasses.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libevmcore/ExpressionClasses.cpp b/libevmcore/ExpressionClasses.cpp index f9ed7d6fc..b43b54113 100644 --- a/libevmcore/ExpressionClasses.cpp +++ b/libevmcore/ExpressionClasses.cpp @@ -167,7 +167,6 @@ Rules::Rules() {{Instruction::OR, {X, X}}, [=]{ return X; }}, {{Instruction::SUB, {X, X}}, [=]{ return u256(0); }}, {{Instruction::EQ, {X, X}}, [=]{ return u256(1); }}, - {{Instruction::EQ, {X, X}}, [=]{ return u256(1); }}, {{Instruction::LT, {X, X}}, [=]{ return u256(0); }}, {{Instruction::SLT, {X, X}}, [=]{ return u256(0); }}, {{Instruction::GT, {X, X}}, [=]{ return u256(0); }}, From dfcaf8a414ed78f4d4a92aea67e181f2030a6f24 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 27 Mar 2015 15:15:34 +0100 Subject: [PATCH 131/228] Abstract contract and inheritance - Checking the linearized base contracts for abstract functions and handle their existence appropriately - If a contract is abstract it can't be created with new - An abstract contract is not compiled (no backend code is generated) - Of course tests --- libsolidity/AST.cpp | 40 +++++++++++++++++++++ libsolidity/AST.h | 1 + libsolidity/CompilerStack.cpp | 2 ++ libsolidity/Parser.cpp | 5 +-- test/SolidityNameAndTypeResolution.cpp | 48 ++++++++++++++++++++++++-- 5 files changed, 89 insertions(+), 7 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 52c58170d..2ada73627 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -52,6 +53,7 @@ void ContractDefinition::checkTypeRequirements() baseSpecifier->checkTypeRequirements(); checkIllegalOverrides(); + checkAbstractFunctions(); FunctionDefinition const* constructor = getConstructor(); if (constructor && !constructor->getReturnParameters().empty()) @@ -128,6 +130,42 @@ FunctionDefinition const* ContractDefinition::getFallbackFunction() const return nullptr; } +void ContractDefinition::checkAbstractFunctions() +{ + map functions; + + // Search from base to derived + for (ContractDefinition const* contract: boost::adaptors::reverse(getLinearizedBaseContracts())) + { + for (ASTPointer const& function: contract->getDefinedFunctions()) + { + string const& name = function->getName(); + if (!function->isFullyImplemented() && functions.count(name) && functions[name]) + BOOST_THROW_EXCEPTION(function->createTypeError("Redeclaring an already implemented function as abstract")); + // if (functions.count(name) && !functions[name] && function->isFullyImplemented()) + // functions.insert(make_pair(name, true); + + // if (functions.count(name) && !functions[name] && function->isFullyImplemented) + // functions.insert(make_pair(name, true)); + + // functions.insert(make_pair(name, function->isFullyImplemented())); + functions[name] = function->isFullyImplemented(); + + // if (function->isFullyImplemented()) + // full_functions.insert(make_pair(name, function.get())); + // else + // abs_functions.insert(make_pair(name, function.get())); + } + } + for (auto const& it: functions) + if (!it.second) + { + setFullyImplemented(false); + break; + } + +} + void ContractDefinition::checkIllegalOverrides() const { // TODO unify this at a later point. for this we need to put the constness and the access specifier @@ -643,6 +681,8 @@ void NewExpression::checkTypeRequirements() m_contract = dynamic_cast(m_contractName->getReferencedDeclaration()); if (!m_contract) BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract.")); + if (!m_contract->isFullyImplemented()) + BOOST_THROW_EXCEPTION(m_contract->createTypeError("Trying to create an object of an abstract contract.")); shared_ptr contractType = make_shared(*m_contract); TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes(); m_type = make_shared(parameterTypes, TypePointers{contractType}, diff --git a/libsolidity/AST.h b/libsolidity/AST.h index c1da90cdb..8570e7bd9 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -283,6 +283,7 @@ public: private: void checkIllegalOverrides() const; + void checkAbstractFunctions(); std::vector, FunctionTypePointer>> const& getInterfaceFunctionList() const; diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 55ec0cb59..1301bfa5d 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -138,6 +138,8 @@ void CompilerStack::compile(bool _optimize) for (ASTPointer const& node: source->ast->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { + if (!contract->isFullyImplemented()) + continue; shared_ptr compiler = make_shared(_optimize); compiler->compileContract(*contract, contractBytecode); Contract& compiledContract = m_contracts[contract->getName()]; diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 0f1b34069..5c7676df5 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -141,10 +141,7 @@ ASTPointer Parser::parseContractDefinition() if (currentToken == Token::RBrace) break; else if (currentToken == Token::Function) - { - ASTPointer func = parseFunctionDefinition(name.get()); - functions.push_back(func); - } + functions.push_back(parseFunctionDefinition(name.get())); else if (currentToken == Token::Struct) structs.push_back(parseStructDefinition()); else if (currentToken == Token::Enum) diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 3c088358c..8eb83b117 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -353,14 +353,56 @@ BOOST_AUTO_TEST_CASE(function_no_implementation) " function functionName(bytes32 input) returns (bytes32 out);\n" "}\n"; ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed"); - ContractDefinition* contract; - for (ASTPointer const& node: sourceUnit->getNodes()) - contract = dynamic_cast(node.get()); + std::vector> nodes = sourceUnit->getNodes(); + ContractDefinition* contract = dynamic_cast(nodes[0].get()); BOOST_CHECK(contract); BOOST_CHECK(!contract->isFullyImplemented()); BOOST_CHECK(!contract->getDefinedFunctions()[0]->isFullyImplemented()); } +BOOST_AUTO_TEST_CASE(abstract_contract) +{ + ASTPointer sourceUnit; + char const* text = R"( + contract base { function foo(); } + contract derived is base { function foo() {} } + )"; + ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed"); + std::vector> nodes = sourceUnit->getNodes(); + ContractDefinition* base = dynamic_cast(nodes[0].get()); + ContractDefinition* derived = dynamic_cast(nodes[1].get()); + BOOST_CHECK(base); + BOOST_CHECK(!base->isFullyImplemented()); + BOOST_CHECK(!base->getDefinedFunctions()[0]->isFullyImplemented()); + BOOST_CHECK(derived); + BOOST_CHECK(derived->isFullyImplemented()); + BOOST_CHECK(derived->getDefinedFunctions()[0]->isFullyImplemented()); +} + +BOOST_AUTO_TEST_CASE(create_abstract_contract) +{ + ASTPointer sourceUnit; + char const* text = R"( + contract base { function foo(); } + contract derived { + base b; + function foo() { b = new base();} + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + +BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract) +{ + ASTPointer sourceUnit; + char const* text = R"( + contract base { function foo(); } + contract derived is base { function foo() {} } + contract wrong is derived { function foo(); } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + BOOST_AUTO_TEST_CASE(function_canonical_signature) { ASTPointer sourceUnit; From 87673e411f67c842b6645e588e9d2b63bd1ee678 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 27 Mar 2015 15:47:14 +0100 Subject: [PATCH 132/228] bug fix: #1430 --- mix/qml/CodeEditorView.qml | 2 +- mix/qml/ProjectModel.qml | 5 ++ mix/qml/TransactionLog.qml | 2 +- mix/qml/WebCodeEditor.qml | 57 +++++++++++++-------- mix/qml/html/cm/errorannotation.js | 15 +++--- mix/qml/js/ProjectModel.js | 79 +++++++++++++++--------------- 6 files changed, 93 insertions(+), 67 deletions(-) diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml index f948f882c..b100dffd0 100644 --- a/mix/qml/CodeEditorView.qml +++ b/mix/qml/CodeEditorView.qml @@ -139,7 +139,7 @@ Item { } onProjectSaved: { - if (projectModel.appIsClosing) + if (projectModel.appIsClosing || projectModel.projectIsClosing) return; for (var i = 0; i < editorListModel.count; i++) { diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml index 7cd1363a6..f4b73b601 100644 --- a/mix/qml/ProjectModel.qml +++ b/mix/qml/ProjectModel.qml @@ -35,6 +35,7 @@ Item { readonly property string projectFileName: ".mix" property bool appIsClosing: false + property bool projectIsClosing: false property string projectPath: "" property string projectTitle: "" property string currentDocumentId: "" @@ -121,13 +122,17 @@ Item { icon: StandardIcon.Question property var callBack; onYes: { + projectIsClosing = true; projectModel.saveAll(); + unsavedFiles = []; ProjectModelCode.doCloseProject(); if (callBack) callBack(); } onRejected: {} onNo: { + projectIsClosing = true; + unsavedFiles = []; ProjectModelCode.doCloseProject(); if (callBack) callBack(); diff --git a/mix/qml/TransactionLog.qml b/mix/qml/TransactionLog.qml index e1c1f6b0b..43736a89a 100644 --- a/mix/qml/TransactionLog.qml +++ b/mix/qml/TransactionLog.qml @@ -44,7 +44,7 @@ Item { target: projectModel onProjectSaved: { - if (projectModel.appIsClosing) + if (projectModel.appIsClosing || projectModel.projectIsClosing) return; if (compilationStatus.compilationComplete && codeModel.hasContract && !clientModel.running) projectModel.stateListModel.debugDefaultState(); diff --git a/mix/qml/WebCodeEditor.qml b/mix/qml/WebCodeEditor.qml index 3313cd2dd..39c840dca 100644 --- a/mix/qml/WebCodeEditor.qml +++ b/mix/qml/WebCodeEditor.qml @@ -18,7 +18,7 @@ Item { function setText(text, mode) { currentText = text; currentMode = mode; - if (initialized) { + if (initialized && editorBrowser) { editorBrowser.runJavaScript("setTextBase64(\"" + Qt.btoa(text) + "\")"); editorBrowser.runJavaScript("setMode(\"" + mode + "\")"); } @@ -26,7 +26,8 @@ Item { } function setFocus() { - editorBrowser.forceActiveFocus(); + if (editorBrowser) + editorBrowser.forceActiveFocus(); } function getText() { @@ -34,19 +35,19 @@ Item { } function syncClipboard() { - if (Qt.platform.os == "osx") { + if (Qt.platform.os == "osx" && editorBrowser) { var text = clipboard.text; editorBrowser.runJavaScript("setClipboardBase64(\"" + Qt.btoa(text) + "\")"); } } function highlightExecution(location) { - if (initialized) + if (initialized && editorBrowser) editorBrowser.runJavaScript("highlightExecution(" + location.start + "," + location.end + ")"); } function showWarning(content) { - if (initialized) + if (initialized && editorBrowser) editorBrowser.runJavaScript("showWarning('" + content + "')"); } @@ -55,12 +56,12 @@ Item { } function toggleBreakpoint() { - if (initialized) + if (initialized && editorBrowser) editorBrowser.runJavaScript("toggleBreakpoint()"); } function changeGeneration() { - if (initialized) + if (initialized && editorBrowser) editorBrowser.runJavaScript("changeGeneration()", function(result) {}); } @@ -81,9 +82,15 @@ Item { console.log("editor: " + sourceID + ":" + lineNumber + ":" + message); } + Component.onDestruction: + { + codeModel.onCompilationComplete.disconnect(compilationComplete); + codeModel.onCompilationError.disconnect(compilationError); + } + onLoadingChanged: { - if (!loading) { + if (!loading && editorBrowser) { initialized = true; setText(currentText, currentMode); runJavaScript("getTextChanged()", function(result) { }); @@ -91,23 +98,31 @@ Item { syncClipboard(); if (currentMode === "solidity") { - codeModel.onCompilationComplete.connect(function(){ - runJavaScript("compilationComplete()", function(result) { }); - }); - - codeModel.onCompilationError.connect(function(error){ - var errorInfo = ErrorLocationFormater.extractErrorInfo(error, false); - if (errorInfo.line && errorInfo.column) - runJavaScript("compilationError('" + errorInfo.line + "', '" + errorInfo.column + "', '" + errorInfo.errorDetail + "')", function(result) { }); - else - runJavaScript("compilationComplete()", function(result) { }); - }); + codeModel.onCompilationComplete.connect(compilationComplete); + codeModel.onCompilationError.connect(compilationError); } parent.changeGeneration(); - } } + + function compilationComplete() + { + if (editorBrowser) + editorBrowser.runJavaScript("compilationComplete()", function(result) { }); + } + + function compilationError(error) + { + if (!editorBrowser || !error) + return; + var errorInfo = ErrorLocationFormater.extractErrorInfo(error, false); + if (errorInfo.line && errorInfo.column) + editorBrowser.runJavaScript("compilationError('" + errorInfo.line + "', '" + errorInfo.column + "', '" + errorInfo.errorDetail + "')", function(result) { }); + else + editorBrowser.runJavaScript("compilationComplete()", function(result) { }); + } + Timer { id: pollTimer @@ -115,6 +130,8 @@ Item { running: false repeat: true onTriggered: { + if (!editorBrowser) + return; editorBrowser.runJavaScript("getTextChanged()", function(result) { if (result === true) { editorBrowser.runJavaScript("getText()" , function(textValue) { diff --git a/mix/qml/html/cm/errorannotation.js b/mix/qml/html/cm/errorannotation.js index 03e36c927..a9f5c289f 100644 --- a/mix/qml/html/cm/errorannotation.js +++ b/mix/qml/html/cm/errorannotation.js @@ -23,12 +23,15 @@ ErrorAnnotation.prototype.init = function() ErrorAnnotation.prototype.open = function() { - var node = document.createElement("div"); - node.id = "annotation" - node.innerHTML = this.content; - node.className = "CodeMirror-errorannotation-context"; - this.lineWidget = this.editor.addLineWidget(this.errorMark.find().from.line, node, { coverGutter: false }); - this.opened = true; + if (this.errorMark.find()) + { + var node = document.createElement("div"); + node.id = "annotation" + node.innerHTML = this.content; + node.className = "CodeMirror-errorannotation-context"; + this.lineWidget = this.editor.addLineWidget(this.errorMark.find().from.line, node, { coverGutter: false }); + this.opened = true; + } } ErrorAnnotation.prototype.close = function() diff --git a/mix/qml/js/ProjectModel.js b/mix/qml/js/ProjectModel.js index 625722c56..177115f83 100644 --- a/mix/qml/js/ProjectModel.js +++ b/mix/qml/js/ProjectModel.js @@ -51,6 +51,7 @@ function closeProject(callBack) { } else { + projectIsClosing = true; doCloseProject(); if (callBack) callBack(); @@ -96,45 +97,45 @@ function saveProjectFile() function loadProject(path) { closeProject(function() { - console.log("Loading project at " + path); - var projectFile = path + projectFileName; - var json = fileIo.readFile(projectFile); - var projectData = JSON.parse(json); - if (projectData.deploymentDir) - projectModel.deploymentDir = projectData.deploymentDir - if (projectData.packageHash) - deploymentDialog.packageHash = projectData.packageHash - if (projectData.packageBase64) - deploymentDialog.packageBase64 = projectData.packageBase64 - if (projectData.applicationUrlEth) - deploymentDialog.applicationUrlEth = projectData.applicationUrlEth - if (projectData.applicationUrlHttp) - deploymentDialog.applicationUrlHttp = projectData.applicationUrlHttp - if (!projectData.title) { - var parts = path.split("/"); - projectData.title = parts[parts.length - 2]; - } - deploymentAddresses = projectData.deploymentAddresses ? projectData.deploymentAddresses : []; - projectTitle = projectData.title; - projectPath = path; - if (!projectData.files) - projectData.files = []; - - for(var i = 0; i < projectData.files.length; i++) { - addFile(projectData.files[i]); - } - projectSettings.lastProjectPath = path; - projectLoading(projectData); - projectLoaded() - - //TODO: move this to codemodel - var contractSources = {}; - for (var d = 0; d < listModel.count; d++) { - var doc = listModel.get(d); - if (doc.isContract) - contractSources[doc.documentId] = fileIo.readFile(doc.path); - } - codeModel.reset(contractSources); + console.log("Loading project at " + path); + var projectFile = path + projectFileName; + var json = fileIo.readFile(projectFile); + var projectData = JSON.parse(json); + if (projectData.deploymentDir) + projectModel.deploymentDir = projectData.deploymentDir + if (projectData.packageHash) + deploymentDialog.packageHash = projectData.packageHash + if (projectData.packageBase64) + deploymentDialog.packageBase64 = projectData.packageBase64 + if (projectData.applicationUrlEth) + deploymentDialog.applicationUrlEth = projectData.applicationUrlEth + if (projectData.applicationUrlHttp) + deploymentDialog.applicationUrlHttp = projectData.applicationUrlHttp + if (!projectData.title) { + var parts = path.split("/"); + projectData.title = parts[parts.length - 2]; + } + deploymentAddresses = projectData.deploymentAddresses ? projectData.deploymentAddresses : []; + projectTitle = projectData.title; + projectPath = path; + if (!projectData.files) + projectData.files = []; + + for(var i = 0; i < projectData.files.length; i++) { + addFile(projectData.files[i]); + } + projectSettings.lastProjectPath = path; + projectLoading(projectData); + projectLoaded() + + //TODO: move this to codemodel + var contractSources = {}; + for (var d = 0; d < listModel.count; d++) { + var doc = listModel.get(d); + if (doc.isContract) + contractSources[doc.documentId] = fileIo.readFile(doc.path); + } + codeModel.reset(contractSources); }); } From 3c1ae84557dbaf07261dae46bca58f849b3ae7db Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 27 Mar 2015 18:14:55 +0100 Subject: [PATCH 133/228] inc failedattempt for handshake --- libp2p/Host.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 87922f016..3835b9504 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -521,6 +521,9 @@ void Host::connect(std::shared_ptr const& _p) Guard l(x_connecting); m_connecting.push_back(handshake); } + + // preempt setting failedAttempts; this value is cleared upon success + _p->m_failedAttempts++; handshake->start(); } @@ -647,6 +650,7 @@ void Host::startedWorking() else clog(NetNote) << "p2p.start.notice id:" << id().abridged() << "Listen port is invalid or unavailable. Node Table using default port (30303)."; + // this doesn't work unless local-networking is enabled because the port is -1 m_nodeTable.reset(new NodeTable(m_ioService, m_alias, bi::address::from_string(listenAddress()), listenPort() > 0 ? listenPort() : 30303)); m_nodeTable->setEventHandler(new HostNodeTableHandler(*this)); restoreNetwork(&m_restoreNetwork); From 090c892906d83de078827e47620b3cea268eaf04 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 27 Mar 2015 19:32:03 +0100 Subject: [PATCH 134/228] setAdress fix for mining. --- abi/main.cpp | 66 ++++++++++++++++++++++++++++++++++++++ libethereum/Client.h | 2 ++ libethereum/ClientBase.cpp | 5 --- libethereum/ClientBase.h | 2 +- libtestutils/FixedClient.h | 1 + 5 files changed, 70 insertions(+), 6 deletions(-) diff --git a/abi/main.cpp b/abi/main.cpp index 22233f1f0..e895152cc 100644 --- a/abi/main.cpp +++ b/abi/main.cpp @@ -202,6 +202,20 @@ struct ABIType return ret; } + bool isBytes() const { return base == Base::Bytes && !size; } + + string render(bytes const& _data) const + { + if (base == Base::Uint) + return toString(fromBigEndian(_data)); + else if (base == Base::Int) + return toString((s256)fromBigEndian(_data)); + else if (base == Base::Address) + return toString(Address(h256(_data))); + else + return toHex(_data); + } + void noteHexInput(unsigned _nibbles) { if (base == Base::Unknown) { if (_nibbles == 40) base = Base::Address; else { base = Base::Bytes; size = _nibbles / 2; } } } void noteBinaryInput() { if (base == Base::Unknown) { base = Base::Bytes; size = 32; } } void noteDecimalInput() { if (base == Base::Unknown) { base = Base::Uint; size = 32; } } @@ -341,6 +355,9 @@ struct ABIMethod bytes encode(vector> const& _params) const { + // ALL WRONG!!!! + // INARITIES SHOULD BE HEIRARCHICAL! + bytes ret = name.empty() ? bytes() : id().asBytes(); unsigned pi = 0; vector inArity; @@ -356,6 +373,9 @@ struct ABIMethod } else arity *= j; + if (i.isBytes()) + for (unsigned i = 0; i < arity; ++i) + inArity.push_back(arity); } unsigned ii = 0; @@ -382,6 +402,52 @@ struct ABIMethod stringstream out; if (_index == -1) out << "["; + unsigned di = 0; + vector souts; + vector catDims; + for (ABIType a: outs) + { + unsigned q = 1; + for (auto& i: a.dims) + { + for (unsigned j = 0; j < q; ++j) + if (i == -1) + { + catDims.push_back(fromBigEndian(bytesConstRef(&_data).cropped(di, 32))); + di += 32; + } + q *= i; + } + if (a.isBytes()) + souts.push_back(a); + } + for (ABIType const& a: souts) + { + auto put = [&]() { + out << a.render(bytesConstRef(&_data).cropped(di, 32).toBytes()) << ", "; + di += 32; + }; + function)> putDim = [&](vector addr) { + if (addr.size() == a.dims.size()) + put(); + else + { + out << "["; + auto d = addr; + addr.push_back(0); + for (addr.back() = 0; addr.back() < a.dims[addr.size() - 1]; ++addr.back()) + { + if (addr.back()) + out << ", "; + putDim(addr); + } + out << "]"; + } + }; + putDim(vector()); + if (_index == -1) + out << ", "; + } (void)_data; if (_index == -1) out << "]"; diff --git a/libethereum/Client.h b/libethereum/Client.h index 013f797b4..5fb30b8f0 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -171,6 +171,8 @@ public: // Mining stuff: + void setAddress(Address _us) { WriteGuard l(x_stateDB); m_preMine.setAddress(_us); } + /// Check block validity prior to mining. bool miningParanoia() const { return m_paranoia; } /// Change whether we check block validity prior to mining. diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index de3249991..df30595b6 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -393,11 +393,6 @@ u256 ClientBase::gasLimitRemaining() const return postMine().gasLimitRemaining(); } -void ClientBase::setAddress(Address _us) -{ - preMine().setAddress(_us); -} - Address ClientBase::address() const { return preMine().address(); diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index ba4f9e0c4..557b08818 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -131,7 +131,7 @@ public: virtual u256 gasLimitRemaining() const override; /// Set the coinbase address - virtual void setAddress(Address _us) override; + virtual void setAddress(Address _us) = 0; /// Get the coinbase address virtual Address address() const override; diff --git a/libtestutils/FixedClient.h b/libtestutils/FixedClient.h index daca444fb..95acc3edb 100644 --- a/libtestutils/FixedClient.h +++ b/libtestutils/FixedClient.h @@ -47,6 +47,7 @@ public: virtual eth::State asOf(h256 const& _h) const override; virtual eth::State preMine() const override { ReadGuard l(x_stateDB); return m_state; } virtual eth::State postMine() const override { ReadGuard l(x_stateDB); return m_state; } + virtual void setAddress(Address _us) { WriteGuard l(x_stateDB); m_state.setAddress(_us); } virtual void prepareForTransaction() override {} private: From bb261e553a281d4b96f34fa3d55faf3e6ce485c5 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 27 Mar 2015 20:49:05 +0100 Subject: [PATCH 135/228] update CLI and AZ UI for replacing local-networking with explicit --listen-ip --- alethzero/Main.ui | 73 +++++++++++++++++++++++++------------------ alethzero/MainWin.cpp | 13 +++++--- eth/main.cpp | 19 +++++------ libp2p/Common.cpp | 6 ++-- libp2p/Network.h | 14 +++++---- libp2p/Peer.h | 1 - neth/main.cpp | 24 ++++++++------ test/peer.cpp | 6 ++-- 8 files changed, 89 insertions(+), 67 deletions(-) diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 34f240fbf..99fea901c 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -118,7 +118,7 @@ 0 0 1617 - 24 + 22 @@ -135,7 +135,6 @@ - @@ -284,6 +283,26 @@ + + + + &Listen on + + + port + + + + + + + 1 + + + 5 + + + @@ -297,8 +316,18 @@ + + + + &Client Name + + + clientName + + + - + @@ -310,13 +339,10 @@ - - - - &Listen on - - - port + + + + Anonymous @@ -330,30 +356,17 @@ - - - - 1 - - - 5 - - - - - + + - Anonymous + Automatic - - + + - &Client Name - - - clientName + Public IP @@ -1694,7 +1707,7 @@ font-size: 14pt tabWidget urlEdit idealPeers - forceAddress + listenIP port clientName transactionQueue diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 36b6176f4..1ae139cce 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -251,7 +251,8 @@ void Main::addNewId(QString _ids) NetworkPreferences Main::netPrefs() const { - return NetworkPreferences(ui->port->value(), ui->forceAddress->text().toStdString(), ui->upnp->isChecked(), ui->localNetworking->isChecked()); + return NetworkPreferences(ui->forcePublicIP->text().toStdString(), ui->listenIP->text().toStdString(), ui->port->value(), ui->upnp->isChecked()); +// return NetworkPreferences(ui->port->value(), ui->forceAddress->text().toStdString(), ui->upnp->isChecked(), ui->localNetworking->isChecked()); } void Main::onKeysChanged() @@ -675,9 +676,10 @@ void Main::writeSettings() } s.setValue("upnp", ui->upnp->isChecked()); - s.setValue("forceAddress", ui->forceAddress->text()); +#warning fixme +// s.setValue("forceAddress", ui->forceAddress->text()); s.setValue("usePast", ui->usePast->isChecked()); - s.setValue("localNetworking", ui->localNetworking->isChecked()); +// s.setValue("localNetworking", ui->localNetworking->isChecked()); s.setValue("forceMining", ui->forceMining->isChecked()); s.setValue("paranoia", ui->paranoia->isChecked()); s.setValue("natSpec", ui->natSpec->isChecked()); @@ -744,9 +746,10 @@ void Main::readSettings(bool _skipGeometry) } ui->upnp->setChecked(s.value("upnp", true).toBool()); - ui->forceAddress->setText(s.value("forceAddress", "").toString()); +#warning fixme +// ui->forceAddress->setText(s.value("forceAddress", "").toString()); ui->usePast->setChecked(s.value("usePast", true).toBool()); - ui->localNetworking->setChecked(s.value("localNetworking", true).toBool()); +// ui->localNetworking->setChecked(s.value("localNetworking", true).toBool()); ui->forceMining->setChecked(s.value("forceMining", false).toBool()); on_forceMining_triggered(); ui->paranoia->setChecked(s.value("paranoia", false).toBool()); diff --git a/eth/main.cpp b/eth/main.cpp index e0cf76193..abac6462c 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -121,8 +121,9 @@ void help() << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl #endif << " -K,--kill-blockchain First kill the blockchain." << endl - << " -l,--listen Listen on the given port for incoming connected (default: 30303)." << endl - << " -L,--local-networking Use peers whose addresses are local." << endl + << " --listen-ip Listen on the given port for incoming connections (default: 30303)." << endl + << " -l,--listen Listen on the given IP for incoming connections (default: 0.0.0.0)." << endl + << " -u,--public-ip Force public ip to given (default: auto)." << endl << " -m,--mining Enable mining, optionally for a specified number of blocks (Default: off)" << endl << " -n,--upnp Use upnp for NAT (default: on)." << endl << " -o,--mode Start a full node or a peer node (Default: full)." << endl @@ -131,7 +132,6 @@ void help() << " -r,--remote Connect to remote host (default: none)." << endl << " -s,--secret Set the secret key for use with send command (default: auto)." << endl << " -t,--miners Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl - << " -u,--public-ip Force public ip to given (default; auto)." << endl << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (Default: 8)." << endl << " -x,--peers Attempt to connect to given number of peers (Default: 5)." << endl << " -V,--version Show the version and exit." << endl @@ -199,7 +199,9 @@ enum class NodeMode int main(int argc, char** argv) { + string listenIP; unsigned short listenPort = 30303; + string publicIP; string remoteHost; unsigned short remotePort = 30303; string dbPath; @@ -211,10 +213,8 @@ int main(int argc, char** argv) #if ETH_JSONRPC int jsonrpc = -1; #endif - string publicIP; bool bootstrap = false; bool upnp = true; - bool useLocal = false; bool forceMining = false; bool killChain = false; bool jit = false; @@ -250,7 +250,9 @@ int main(int argc, char** argv) for (int i = 1; i < argc; ++i) { string arg = argv[i]; - if ((arg == "-l" || arg == "--listen" || arg == "--listen-port") && i + 1 < argc) + if (arg == "--listen-ip" && i + 1 < argc) + listenIP = argv[++i]; + else if ((arg == "-l" || arg == "--listen" || arg == "--listen-port") && i + 1 < argc) listenPort = (short)atoi(argv[++i]); else if ((arg == "-u" || arg == "--public-ip" || arg == "--public") && i + 1 < argc) publicIP = argv[++i]; @@ -271,8 +273,6 @@ int main(int argc, char** argv) return -1; } } - else if (arg == "-L" || arg == "--local-networking") - useLocal = true; else if (arg == "-K" || arg == "--kill-blockchain") killChain = true; else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc) @@ -421,7 +421,8 @@ int main(int argc, char** argv) StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat); VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); - NetworkPreferences netPrefs(listenPort, publicIP, upnp, useLocal); +#warning fixme + NetworkPreferences netPrefs(listenPort); auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); std::string clientImplString = "Ethereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""); dev::WebThreeDirect web3( diff --git a/libp2p/Common.cpp b/libp2p/Common.cpp index b995723c3..7051322a9 100644 --- a/libp2p/Common.cpp +++ b/libp2p/Common.cpp @@ -28,7 +28,7 @@ const unsigned dev::p2p::c_protocolVersion = 3; bool p2p::isPublicAddress(std::string const& _addressToCheck) { - return isPublicAddress(bi::address::from_string(_addressToCheck)); + return _addressToCheck.empty() ? false : isPublicAddress(bi::address::from_string(_addressToCheck)); } bool p2p::isPublicAddress(bi::address const& _addressToCheck) @@ -67,7 +67,7 @@ bool p2p::isPrivateAddress(bi::address const& _addressToCheck) bool p2p::isPrivateAddress(std::string const& _addressToCheck) { - return isPrivateAddress(bi::address::from_string(_addressToCheck)); + return _addressToCheck.empty() ? false : isPrivateAddress(bi::address::from_string(_addressToCheck)); } // Helper function to determine if an address is localhost @@ -86,7 +86,7 @@ bool p2p::isLocalHostAddress(bi::address const& _addressToCheck) bool p2p::isLocalHostAddress(std::string const& _addressToCheck) { - return isLocalHostAddress(bi::address::from_string(_addressToCheck)); + return _addressToCheck.empty() ? false : isLocalHostAddress(bi::address::from_string(_addressToCheck)); } std::string p2p::reasonOf(DisconnectReason _r) diff --git a/libp2p/Network.h b/libp2p/Network.h index 127b5ea04..0e3a08398 100644 --- a/libp2p/Network.h +++ b/libp2p/Network.h @@ -58,16 +58,18 @@ public: struct NetworkPreferences { - // Network Preferences with unspecified Public IP - NetworkPreferences(unsigned short p = 30303, std::string i = std::string(), bool u = true, bool l = false): listenPort(p), listenIPAddress(i), publicIPAddress(), traverseNAT(u) {} + // Default Network Preferences + NetworkPreferences(unsigned short lp = 30303): listenPort(lp) {} + + // Network Preferences with specific Listen IP + NetworkPreferences(std::string l, unsigned short lp = 30303, bool u = true): publicIPAddress(), listenIPAddress(l), listenPort(lp), traverseNAT(u) {} // Network Preferences with intended Public IP - NetworkPreferences(std::string publicIP, unsigned short p = 30303, std::string i = std::string(), bool u = true, bool l = false): listenPort(p), listenIPAddress(i), publicIPAddress(publicIP), traverseNAT(u) { if (!publicIPAddress.empty() && !isPublicAddress(publicIPAddress)) BOOST_THROW_EXCEPTION(InvalidPublicIPAddress()); } + NetworkPreferences(std::string publicIP, std::string l = std::string(), unsigned short lp = 30303, bool u = true): publicIPAddress(publicIP), listenIPAddress(l), listenPort(lp), traverseNAT(u) { if (!publicIPAddress.empty() && !isPublicAddress(publicIPAddress)) BOOST_THROW_EXCEPTION(InvalidPublicIPAddress()); } - unsigned short listenPort = 30303; - std::string listenIPAddress; std::string publicIPAddress; - + std::string listenIPAddress; + unsigned short listenPort = 30303; bool traverseNAT = true; }; diff --git a/libp2p/Peer.h b/libp2p/Peer.h index 8774b6578..bfa2eaeb6 100644 --- a/libp2p/Peer.h +++ b/libp2p/Peer.h @@ -47,7 +47,6 @@ namespace p2p * those peers. Modifying these properties via a storage backend alleviates * Host of the responsibility. (&& remove save/restoreNetwork) * @todo reimplement recording of historical session information on per-transport basis - * @todo rebuild nodetable when localNetworking is enabled/disabled * @todo move attributes into protected */ class Peer: public Node diff --git a/neth/main.cpp b/neth/main.cpp index a844da92d..cd0944425 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -84,8 +84,9 @@ void help() << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: 8080)." << endl #endif << " -K,--kill-blockchain First kill the blockchain." << endl - << " -l,--listen Listen on the given port for incoming connected (default: 30303)." << endl - << " -L,--local-networking Use peers whose addresses are local." << endl + << " --listen-ip Listen on the given IP for incoming connections (default: 0.0.0.0)." << endl + << " -l,--listen Listen on the given port for incoming connections (default: 30303)." << endl + << " -u,--public-ip Force public ip to given (default; auto)." << endl << " -m,--mining Enable mining (default: off)" << endl << " -n,--upnp Use upnp for NAT (default: on)." << endl << " -o,--mode Start a full node or a peer node (Default: full)." << endl @@ -94,7 +95,6 @@ void help() << " -r,--remote Connect to remote host (default: none)." << endl << " -s,--secret Set the secret key for use with send command (default: auto)." << endl << " -t,--miners Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl - << " -u,--public-ip Force public ip to given (default; auto)." << endl << " -v,--verbosity <0..9> Set the log verbosity from 0 to 9 (tmp forced to 1)." << endl << " -x,--peers Attempt to connect to given number of peers (default: 5)." << endl << " -V,--version Show the version and exit." << endl @@ -321,7 +321,9 @@ enum class NodeMode int main(int argc, char** argv) { + string listenIP; unsigned short listenPort = 30303; + string publicIP; string remoteHost; unsigned short remotePort = 30303; string dbPath; @@ -332,10 +334,8 @@ int main(int argc, char** argv) #if ETH_JSONRPC int jsonrpc = 8080; #endif - string publicIP; bool bootstrap = false; bool upnp = true; - bool useLocal = false; bool forceMining = false; bool killChain = false; bool jit = false; @@ -371,7 +371,9 @@ int main(int argc, char** argv) for (int i = 1; i < argc; ++i) { string arg = argv[i]; - if ((arg == "-l" || arg == "--listen" || arg == "--listen-port") && i + 1 < argc) + if (arg == "--listen-ip" && i + 1 < argc) + listenIP = argv[++i]; + else if ((arg == "-l" || arg == "--listen" || arg == "--listen-port") && i + 1 < argc) listenPort = (short)atoi(argv[++i]); else if ((arg == "-u" || arg == "--public-ip" || arg == "--public") && i + 1 < argc) publicIP = argv[++i]; @@ -392,8 +394,6 @@ int main(int argc, char** argv) return -1; } } - else if (arg == "-L" || arg == "--local-networking") - useLocal = true; else if (arg == "-K" || arg == "--kill-blockchain") killChain = true; else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc) @@ -535,6 +535,8 @@ int main(int argc, char** argv) } } + + if (!clientName.empty()) clientName += "/"; @@ -542,7 +544,7 @@ int main(int argc, char** argv) StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat); VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); - NetworkPreferences netPrefs(listenPort, publicIP, upnp, useLocal); + NetworkPreferences netPrefs(publicIP, listenIP ,listenPort, upnp); auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); std::string clientImplString = "NEthereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""); dev::WebThreeDirect web3( @@ -687,7 +689,9 @@ int main(int argc, char** argv) { unsigned port; iss >> port; - web3.setNetworkPreferences(NetworkPreferences((short)port, publicIP, upnp)); + if (port) + netPrefs.listenPort = port; + web3.setNetworkPreferences(netPrefs); web3.startNetwork(); } else if (cmd == "connect") diff --git a/test/peer.cpp b/test/peer.cpp index bfb4680d3..b90fc7404 100644 --- a/test/peer.cpp +++ b/test/peer.cpp @@ -35,8 +35,8 @@ BOOST_AUTO_TEST_CASE(host) auto oldLogVerbosity = g_logVerbosity; g_logVerbosity = 10; - NetworkPreferences host1prefs(30301, "127.0.0.1", false, true); - NetworkPreferences host2prefs(30302, "127.0.0.1", false, true); + NetworkPreferences host1prefs("127.0.0.1", 30301, false); + NetworkPreferences host2prefs("127.0.0.1", 30302, false); Host host1("Test", host1prefs); host1.start(); @@ -62,7 +62,7 @@ BOOST_AUTO_TEST_CASE(save_nodes) std::list hosts; for (auto i:{0,1,2,3,4,5}) { - Host* h = new Host("Test", NetworkPreferences(30300 + i, "127.0.0.1", false, true)); + Host* h = new Host("Test", NetworkPreferences("127.0.0.1", 30300 + i, false)); h->setIdealPeerCount(10); // starting host is required so listenport is available h->start(); From f72d91b444aa082f506431c17b8f71043577403e Mon Sep 17 00:00:00 2001 From: Ali Mashatan Date: Sat, 28 Mar 2015 00:21:24 +0430 Subject: [PATCH 136/228] Adding "play" button Changes in the appearance and behavior of StatesComboBox --- mix/qml/StatesComboBox.qml | 55 +++++++++++++++++++++++---------- mix/qml/TransactionLog.qml | 41 +++++++++++++++--------- mix/qml/img/pause_button.png | Bin 0 -> 200 bytes mix/qml/img/pause_button2x.png | Bin 0 -> 414 bytes mix/qml/img/play_button.png | Bin 0 -> 531 bytes mix/qml/img/play_button2x.png | Bin 0 -> 904 bytes mix/res.qrc | 6 +++- 7 files changed, 70 insertions(+), 32 deletions(-) create mode 100755 mix/qml/img/pause_button.png create mode 100755 mix/qml/img/pause_button2x.png create mode 100755 mix/qml/img/play_button.png create mode 100755 mix/qml/img/play_button2x.png diff --git a/mix/qml/StatesComboBox.qml b/mix/qml/StatesComboBox.qml index ada5e00d5..2c61863ec 100644 --- a/mix/qml/StatesComboBox.qml +++ b/mix/qml/StatesComboBox.qml @@ -23,6 +23,7 @@ import QtQuick 2.0 import QtQuick.Controls 1.0 import QtQuick.Layouts 1.1 +import QtGraphicalEffects 1.0 Rectangle { id:statesComboBox @@ -40,7 +41,13 @@ Rectangle { break; } var coordinates = dropDownList.mapToItem(top, 0, 0) + //the order is important + dropDownShowdowList.parent = top; dropDownList.parent = top; + + dropDownShowdowList.x = coordinates.x + dropDownShowdowList.y = coordinates.y + dropDownList.x = coordinates.x dropDownList.y = coordinates.y } @@ -48,7 +55,7 @@ Rectangle { signal selectItem(real item); signal editItem(real item); signal selectCreate(); - + property variant rowHeight:25; property variant items; readonly property alias selectedItem: chosenItemText.text; readonly property alias selectedIndex: listView.currentRow; @@ -87,16 +94,30 @@ Rectangle { } } } -//ToDo: We need scrollbar for items + + Rectangle { + id:dropDownShowdowList + width:statesComboBox.width; + opacity: 0.3 + height:0; + clip:true; + radius:4; + anchors.top: chosenItem.top; + anchors.margins: 2; + color: "gray" + } + //ToDo: We need scrollbar for items Rectangle { id:dropDownList width:statesComboBox.width; height:0; clip:true; radius:4; - anchors.top: chosenItem.bottom; + anchors.top: chosenItem.top; anchors.margins: 2; color: statesComboBox.color + + ColumnLayout{ spacing: 2 TableView { @@ -118,10 +139,13 @@ Rectangle { width: statesComboBox.width; delegate: mainItemDelegate } - + rowDelegate: Rectangle { + width:statesComboBox.width; + height: statesComboBox.rowHeight; + } Component { id: mainItemDelegate - Item{ + Rectangle { id: itemDelegate width:statesComboBox.width; height: statesComboBox.height; @@ -134,22 +158,16 @@ Rectangle { anchors.margins: 5; } - Rectangle - { - id: spaceItemid - anchors.top: textItemid.top; - anchors.left: textItemid.right - width: parent.width - textItemid.width - imageItemid.width - textItemid.anchors.margins- textItemid.anchors.margins - } Image { id: imageItemid height:20 width:20; + anchors.right:parent.right + anchors.top: parent.top; + anchors.margins: 5; visible: false; fillMode: Image.PreserveAspectFit source: "img/edit_combox.png" - anchors.top: spaceItemid.top; - anchors.left: spaceItemid.right; } MouseArea { @@ -180,6 +198,7 @@ Rectangle { }//Item }//Component }//Table View + RowLayout{ Rectangle{ width: 1 @@ -188,7 +207,7 @@ Rectangle { id:createStateText width:statesComboBox.width; height: statesComboBox.height; - font.pointSize: 10 + font.bold: true text:"Create State ..." MouseArea { @@ -208,12 +227,14 @@ Rectangle { } } } + } } states: State { name: "dropDown"; - PropertyChanges { target: dropDownList; height:(20*(statesComboBox.items.count+1)) } - PropertyChanges { target:listView; height:20; implicitHeight: (20*(statesComboBox.items.count)) } + PropertyChanges { target: dropDownList; height:(statesComboBox.rowHeight*(statesComboBox.items.count+1)) } + PropertyChanges { target: dropDownShowdowList; width:statesComboBox.width+3; height:(statesComboBox.rowHeight*(statesComboBox.items.count+1))+3 } + PropertyChanges { target:listView; height:20; implicitHeight: (statesComboBox.rowHeight*(statesComboBox.items.count)) } } } diff --git a/mix/qml/TransactionLog.qml b/mix/qml/TransactionLog.qml index e0aec1a32..48b166eca 100644 --- a/mix/qml/TransactionLog.qml +++ b/mix/qml/TransactionLog.qml @@ -6,7 +6,6 @@ import QtQuick.Layouts 1.1 import org.ethereum.qml.RecordLogEntry 1.0 Item { - property ListModel fullModel: ListModel{} property ListModel transactionModel: ListModel{} property ListModel callModel: ListModel{} @@ -25,11 +24,29 @@ Item { enabled: codeModel.hasContract && !clientModel.running && statesCombo.selectedIndex >= 0 && projectModel.stateListModel.count > 0; onTriggered: projectModel.stateListModel.editState(statesCombo.selectedIndex); } + Action { + id: playAndPauseAction + checkable: true; + checked: false; + iconSource: "qrc:/qml/img/play_button.png" + onToggled: { + if (checked) + { + this.iconSource = "qrc:/qml/img/pause_button2x.png" + console.log("play"); + }else{ + this.iconSource = "qrc:/qml/img/play_button2x.png" + console.log("pause"); + } + } + enabled: true + } ColumnLayout { anchors.fill: parent RowLayout { - + anchors.right: parent.right + anchors.left: parent.left Connections { id: compilationStatus @@ -78,18 +95,6 @@ Item { } } } - /*Button - { - anchors.rightMargin: 9 - anchors.verticalCenter: parent.verticalCenter - action: editStateAction - } - Button - { - anchors.rightMargin: 9 - anchors.verticalCenter: parent.verticalCenter - action: addStateAction - }*/ Button { anchors.rightMargin: 9 @@ -116,6 +121,14 @@ Item { logTable.model = itemFilter.getCurrentModel(); } } + Button + { + id: playPauseBtn + anchors.right: parent.right + anchors.rightMargin: 1 + anchors.verticalCenter: parent.verticalCenter + action: playAndPauseAction + } } TableView { id: logTable diff --git a/mix/qml/img/pause_button.png b/mix/qml/img/pause_button.png new file mode 100755 index 0000000000000000000000000000000000000000..e87889551dc9239887d84bd1511f2ce0f57904e1 GIT binary patch literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^=0L2+!3HFIm>NZa6k~CayA#8@b22Z1oF-2f$B+!x zx0f8b8WcoW0$xv%yBIB|$9ypJd+YTex4-*Vn#waWv2X||I5aRYGDSCL&;B9#Vz2#b z-Q=G0v6*?LVY|wf9~3T@D^JQUPCJ{qZb8HSO$nM8J!W;!;A%4o-!LUZ<*cg@tMe@F o2rrXK&y>^@5vH+lOcAbNY*gb=vpC{%3FsOIPgg&ebxsLQ0RFK*6951J literal 0 HcmV?d00001 diff --git a/mix/qml/img/pause_button2x.png b/mix/qml/img/pause_button2x.png new file mode 100755 index 0000000000000000000000000000000000000000..6f45b3236ab4e8debdc79082f4fa04df0f852786 GIT binary patch literal 414 zcmeAS@N?(olHy`uVBq!ia0vp^c|aV)!3HGzUpB-7DaPU;cPEB*=VV?2*%6*Djv*C{ zZ!aF?ZFUe~aeSkp@3Lu&v4d0A+0}oJZ4Ri7tz+R3P;h8qU}Pea5y4$^js5;T>*s&& z{dvCc{qg)&-|y{-{`qFR%;v4a0(t3k^6K@cI~C=!=Q6_XB1e!{xYdR^Ld`nj=7~(o;#)#o2%V9XSG))=G0DU lVOkp}rA&&~Vc}%LRIk&@;p1(W6b}q=22WQ%mvv4FO#n*Mm`DHs literal 0 HcmV?d00001 diff --git a/mix/qml/img/play_button.png b/mix/qml/img/play_button.png new file mode 100755 index 0000000000000000000000000000000000000000..01a5c85cb2123f6b78e4a7914e18aab8b60f8fea GIT binary patch literal 531 zcmV+u0_^>XP)Px$%}GQ-RA>e5mc38HKp4j9M@$G4j;zkIx#&)qSQ=;-abVD06Wv_Ze?a^XEQF;C zq@#&0Ov22{%0!xwe)yF177}7>@2;4cR;I{g( zir3X@^}EyQT<{W{C67?JSO@@5R8{=|;1jm$Q5JD|V}-*d0>P)=W2;UL!>H#Ko%VZK za7<5K*YzoS@c|RUYb&fOuhFusC{3;?ioOunwQYNmCjKum!rj{^3cYxaH$H4`_Qm9# zFAOdbUO_LuVztMuR;!j5X7~G|;h3Igxm=#37cDlyV>}eNEep13s(wuiuBQzY95?Yi zuje?#n6z zCB;O+EwKfA!{KmJ1Qe^uz2~r6Ho#7c24jFUk^|%bIY17O1LOcXKn{=t{z>Ug}i6zkYRuPT8rapDbDIzSXX+Tz9^@P_t*Zzz@++eXqX$@|)LCV>?}> zP`Yly_cdXw|K@DJ-Cx5x>-sL2PVWT{_x1*@{KEU`p5phrC%LEOKNnQjW92Acdi_ML z`q{MN`{pYu*SN2;IGnaWXyqJ*?|Tn2zPz5aPBS?AjL9o|zE@?tU+XN7Uw_@+;GIF& zqHdkXOZx*qYr4Lx?np0s{3&MF6rI=M%6fZm$UfQE-*L%v@x>dbnH*k8+Oy!o>KDIz1}lQ$v^jqR`nuuAq^Y_Gm+I?1+F-g$BXZ}7P0u@o*fX=d!nUzY-zQS| z$}3E7(;Lfs&n;YLo&qXutQAsVqzFxLe4xMh>o;G?TQN_7S&PBb)z4*}Q$iB}AKit7 literal 0 HcmV?d00001 diff --git a/mix/res.qrc b/mix/res.qrc index 51f2b49c6..6397555db 100644 --- a/mix/res.qrc +++ b/mix/res.qrc @@ -12,7 +12,7 @@ qml/StatesComboBox.qml qml/StateListModel.qml qml/img/dappProjectIcon.png - qml/img/edit_combox.png + qml/img/edit_combox.png qml/img/jumpintoback.png qml/img/jumpintoforward.png qml/img/jumpoutback.png @@ -115,5 +115,9 @@ qml/StructView.qml qml/img/console.png qml/VariablesView.qml + qml/img/play_button2x.png + qml/img/play_button.png + qml/img/pause_button2x.png + qml/img/pause_button.png From 93593a8751b0d05f45828a7d7f8a0013e76de78d Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 27 Mar 2015 22:47:19 +0100 Subject: [PATCH 137/228] updae legacy codepath --- libp2p/Session.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index d7bfca34e..65dedb302 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -216,12 +216,8 @@ bool Session::interpret(PacketType _t, RLP const& _r) NodeId id = _r[i][2].toHash(); clogS(NetAllDetail) << "Checking: " << ep << "(" << id.abridged() << ")"; -// clogS(NetAllDetail) << "Checking: " << ep << "(" << id.abridged() << ")" << isPrivateAddress(peerAddress) << this->id().abridged() << isPrivateAddress(endpoint().address()) << m_server->m_peers.count(id) << (m_server->m_peers.count(id) ? isPrivateAddress(m_server->m_peers.at(id)->address.address()) : -1); - // todo: draft spec: ignore if dist(us,item) - dist(us,them) > 1 - - // TODO: isPrivate - if (!m_server->m_netPrefs.localNetworking && isPrivateAddress(peerAddress)) + if (!isPublicAddress(peerAddress)) goto CONTINUE; // Private address. Ignore. if (!id) From 2d42d05f9bd68d982a3ac16fbea03c10cb008307 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 27 Mar 2015 23:06:25 +0100 Subject: [PATCH 138/228] ABI fixes and finalisation. Testing needed. --- abi/main.cpp | 283 +++++++++++++++++++++++++--------------- libdevcore/CommonData.h | 12 +- 2 files changed, 188 insertions(+), 107 deletions(-) diff --git a/abi/main.cpp b/abi/main.cpp index e895152cc..cdae81f1e 100644 --- a/abi/main.cpp +++ b/abi/main.cpp @@ -43,8 +43,8 @@ void help() << " -h,--help Print this help message and exit." << endl << " -V,--version Show the version and exit." << endl << "Input options:" << endl - << " -p,--prefix Require all input formats to be prefixed e.g. 0x for hex, . for decimal, @ for binary." << endl - << " -P,--no-prefix Require no input format to be prefixed." << endl + << " -f,--format-prefix Require all input formats to be prefixed e.g. 0x for hex, . for decimal, @ for binary." << endl + << " -F,--no-format-prefix Require no input format to be prefixed." << endl << " -t,--typing Require all arguments to be typed e.g. b32: (bytes32), u64: (uint64), b[]: (byte[]), i: (int256)." << endl << " -T,--no-typing Require no arguments to be typed." << endl << "Output options:" << endl @@ -53,8 +53,6 @@ void help() << " -x,--hex Display all data as hex." << endl << " -b,--binary Display all data as binary." << endl << " -p,--prefix Prefix by a base identifier." << endl - << " -z,--no-zeroes Remove any leading zeroes from the data." << endl - << " -n,--no-nulls Remove any trailing nulls from the data." << endl ; exit(0); } @@ -90,7 +88,9 @@ enum class Format { Binary, Hex, - Decimal + Decimal, + Open, + Close }; struct InvalidUserString: public Exception {}; @@ -115,6 +115,12 @@ static const map s_bases = { Base::Fixed, "fixed" } }; +struct EncodingPrefs +{ + Encoding e = Encoding::Auto; + bool prefix = true; +}; + struct ABIType { Base base = Base::Unknown; @@ -204,39 +210,72 @@ struct ABIType bool isBytes() const { return base == Base::Bytes && !size; } - string render(bytes const& _data) const + string render(bytes const& _data, EncodingPrefs _e) const { - if (base == Base::Uint) - return toString(fromBigEndian(_data)); - else if (base == Base::Int) - return toString((s256)fromBigEndian(_data)); + if (base == Base::Uint || base == Base::Int) + { + if (_e.e == Encoding::Hex) + return (_e.prefix ? "0x" : "") + toHex(toCompactBigEndian(fromBigEndian(bytesConstRef(&_data).cropped(32 - size / 8)))); + else + { + bigint i = fromBigEndian(bytesConstRef(&_data).cropped(32 - size / 8)); + if (base == Base::Int && i > (bigint(1) << (size - 1))) + i -= (bigint(1) << size); + return toString(i); + } + } else if (base == Base::Address) - return toString(Address(h256(_data))); + { + Address a = Address(h256(_data), Address::AlignRight); + return _e.e == Encoding::Binary ? asString(a.asBytes()) : ((_e.prefix ? "0x" : "") + toString(a)); + } + else if (isBytes()) + { + return _e.e == Encoding::Binary ? asString(_data) : ((_e.prefix ? "0x" : "") + toHex(_data)); + } + else if (base == Base::Bytes) + { + bytesConstRef b(&_data); + b = b.cropped(0, size); + return _e.e == Encoding::Binary ? asString(b) : ((_e.prefix ? "0x" : "") + toHex(b)); + } + else + throw InvalidFormat(); + } + + bytes unrender(bytes const& _data, Format _f) const + { + if (isBytes()) + { + auto ret = _data; + while (ret.size() % 32 != 0) + ret.push_back(0); + return ret; + } else - return toHex(_data); + return aligned(_data, _f, 32); } void noteHexInput(unsigned _nibbles) { if (base == Base::Unknown) { if (_nibbles == 40) base = Base::Address; else { base = Base::Bytes; size = _nibbles / 2; } } } void noteBinaryInput() { if (base == Base::Unknown) { base = Base::Bytes; size = 32; } } void noteDecimalInput() { if (base == Base::Unknown) { base = Base::Uint; size = 32; } } -}; -bytes aligned(bytes const& _b, ABIType _t, Format _f, unsigned _length) -{ - (void)_t; - bytes ret = _b; - while (ret.size() < _length) - if (_f == Format::Binary) - ret.push_back(0); - else - ret.insert(ret.begin(), 0); - while (ret.size() > _length) - if (_f == Format::Binary) - ret.pop_back(); - else - ret.erase(ret.begin()); - return ret; -} + bytes aligned(bytes const& _b, Format _f, unsigned _length) const + { + bytes ret = _b; + while (ret.size() < _length) + if (_f == Format::Binary) + ret.push_back(0); + else + ret.insert(ret.begin(), 0); + while (ret.size() > _length) + if (_f == Format::Binary) + ret.pop_back(); + else + ret.erase(ret.begin()); + return ret; + } +}; tuple fromUser(std::string const& _arg, Tristate _prefix, Tristate _typing) { @@ -269,25 +308,36 @@ tuple fromUser(std::string const& _arg, Tristate _prefix type.noteBinaryInput(); return make_tuple(asBytes(val.substr(1)), type, Format::Binary); } + if (val == "[") + return make_tuple(bytes(), type, Format::Open); + if (val == "]") + return make_tuple(bytes(), type, Format::Close); } if (_prefix != Tristate::True) { - if (_arg.find_first_not_of("0123456789") == string::npos) + if (val.find_first_not_of("0123456789") == string::npos) { type.noteDecimalInput(); return make_tuple(toCompactBigEndian(bigint(val)), type, Format::Decimal); } - if (_arg.find_first_not_of("0123456789abcdefABCDEF") == string::npos) + if (val.find_first_not_of("0123456789abcdefABCDEF") == string::npos) { type.noteHexInput(val.size()); return make_tuple(fromHex(val), type, Format::Hex); } + if (val == "[") + return make_tuple(bytes(), type, Format::Open); + if (val == "]") + return make_tuple(bytes(), type, Format::Close); type.noteBinaryInput(); - return make_tuple(asBytes(_arg), type, Format::Binary); + return make_tuple(asBytes(val), type, Format::Binary); } throw InvalidUserString(); } +struct ExpectedOpen: public Exception {}; +struct ExpectedClose: public Exception {}; + struct ABIMethod { string name; @@ -355,77 +405,107 @@ struct ABIMethod bytes encode(vector> const& _params) const { - // ALL WRONG!!!! - // INARITIES SHOULD BE HEIRARCHICAL! - bytes ret = name.empty() ? bytes() : id().asBytes(); + bytes suffix; + + // int int[] int + // example: 42 [ 1 2 3 ] 69 + // int[2][][3] + // example: [ [ [ 1 2 3 ] [ 4 5 6 ] ] [ ] ] + unsigned pi = 0; - vector inArity; - for (ABIType const& i: ins) - { - unsigned arity = 1; - for (auto j: i.dims) - if (j == -1) - { - ret += aligned(_params[pi].first, ABIType(), Format::Decimal, 32); - arity *= fromBigEndian(_params[pi].first); - pi++; - } - else - arity *= j; - if (i.isBytes()) - for (unsigned i = 0; i < arity; ++i) - inArity.push_back(arity); - } - unsigned ii = 0; - for (ABIType const& i: ins) + for (ABIType const& a: ins) { - for (unsigned j = 0; j < inArity[ii]; ++j) - { - if (i.base == Base::Bytes && !i.size) + auto put = [&]() { + if (a.isBytes()) + ret += h256(u256(_params[pi].first.size())).asBytes(); + suffix += a.unrender(_params[pi].first, _params[pi].second); + pi++; + }; + function, unsigned)> putDim = [&](vector addr, unsigned q) { + if (addr.size() == a.dims.size()) + put(); + else { - ret += _params[pi].first; - while (ret.size() % 32 != 0) - ret.push_back(0); + if (_params[pi].second != Format::Open) + throw ExpectedOpen(); + int l = a.dims[addr.size()]; + if (l == -1) + { + // read ahead in params and discover the arity. + unsigned depth = 0; + l = 0; + for (unsigned pi2 = pi + 1; depth || _params[pi2].second != Format::Close;) + { + if (_params[pi2].second == Format::Open) + ++depth; + if (_params[pi2].second == Format::Close) + --depth; + if (!depth) + ++l; + if (++pi2 == _params.size()) + throw ExpectedClose(); + } + ret += h256(u256(l)).asBytes(); + } + q *= l; + for (addr.push_back(0); addr.back() < l; ++addr.back()) + putDim(addr, q); + if (_params[pi].second != Format::Close) + throw ExpectedClose(); } - else - ret += aligned(_params[pi].first, i, _params[pi].second, 32); - ++pi; - } - ++ii; + }; + putDim(vector(), 1); } - return ret; + return ret + suffix; } - string decode(bytes const& _data, int _index = -1) + string decode(bytes const& _data, int _index, EncodingPrefs _ep) { stringstream out; if (_index == -1) out << "["; unsigned di = 0; - vector souts; vector catDims; - for (ABIType a: outs) + for (ABIType const& a: outs) { - unsigned q = 1; - for (auto& i: a.dims) - { - for (unsigned j = 0; j < q; ++j) - if (i == -1) + auto put = [&]() { + if (a.isBytes()) + { + catDims.push_back(fromBigEndian(bytesConstRef(&_data).cropped(di, 32))); + di += 32; + } + }; + function, unsigned)> putDim = [&](vector addr, unsigned q) { + if (addr.size() == a.dims.size()) + put(); + else + { + out << "["; + int l = a.dims[addr.size()]; + if (l == -1) { - catDims.push_back(fromBigEndian(bytesConstRef(&_data).cropped(di, 32))); + l = fromBigEndian(bytesConstRef(&_data).cropped(di, 32)); + catDims.push_back(l); di += 32; } - q *= i; - } - if (a.isBytes()) - souts.push_back(a); + q *= l; + for (addr.push_back(0); addr.back() < l; ++addr.back()) + putDim(addr, q); + out << "]"; + } + }; + putDim(vector(), 1); } - for (ABIType const& a: souts) + unsigned d = 0; + for (ABIType const& a: outs) { auto put = [&]() { - out << a.render(bytesConstRef(&_data).cropped(di, 32).toBytes()) << ", "; - di += 32; + unsigned l = 32; + if (a.isBytes()) + l = (catDims[d++] + 31 / 32) * 32; + out << a.render(bytesConstRef(&_data).cropped(di, l).toBytes(), _ep) << ", "; + di += l; }; function)> putDim = [&](vector addr) { if (addr.size() == a.dims.size()) @@ -433,9 +513,11 @@ struct ABIMethod else { out << "["; - auto d = addr; addr.push_back(0); - for (addr.back() = 0; addr.back() < a.dims[addr.size() - 1]; ++addr.back()) + int l = a.dims[addr.size() - 1]; + if (l == -1) + l = catDims[d++]; + for (addr.back() = 0; addr.back() < l; ++addr.back()) { if (addr.back()) out << ", "; @@ -543,10 +625,9 @@ int main(int argc, char** argv) Mode mode = Mode::Encode; string abiFile; string method; - Tristate prefix = Tristate::Mu; + Tristate formatPrefix = Tristate::Mu; Tristate typePrefix = Tristate::Mu; - bool clearZeroes = false; - bool clearNulls = false; + EncodingPrefs prefs; bool verbose = false; int outputIndex = -1; vector> params; @@ -566,25 +647,23 @@ int main(int argc, char** argv) else if ((arg == "-i" || arg == "--index") && argc > i) outputIndex = atoi(argv[++i]); else if (arg == "-p" || arg == "--prefix") - prefix = Tristate::True; - else if (arg == "-P" || arg == "--no-prefix") - prefix = Tristate::False; + prefs.prefix = true; + else if (arg == "-f" || arg == "--format-prefix") + formatPrefix = Tristate::True; + else if (arg == "-F" || arg == "--no-format-prefix") + formatPrefix = Tristate::False; else if (arg == "-t" || arg == "--typing") typePrefix = Tristate::True; else if (arg == "-T" || arg == "--no-typing") typePrefix = Tristate::False; - else if (arg == "-z" || arg == "--no-zeroes") - clearZeroes = true; - else if (arg == "-n" || arg == "--no-nulls") - clearNulls = true; else if (arg == "-v" || arg == "--verbose") verbose = true; else if (arg == "-x" || arg == "--hex") - encoding = Encoding::Hex; + prefs.e = Encoding::Hex; else if (arg == "-d" || arg == "--decimal" || arg == "--dec") - encoding = Encoding::Decimal; + prefs.e = Encoding::Decimal; else if (arg == "-b" || arg == "--binary" || arg == "--bin") - encoding = Encoding::Binary; + prefs.e = Encoding::Binary; else if (arg == "-v" || arg == "--verbose") version(); else if (arg == "-V" || arg == "--version") @@ -593,7 +672,7 @@ int main(int argc, char** argv) method = arg; else { - auto u = fromUser(arg, prefix, typePrefix); + auto u = fromUser(arg, formatPrefix, typePrefix); args.push_back(get<1>(u)); params.push_back(make_pair(get<0>(u), get<2>(u))); } @@ -648,14 +727,8 @@ int main(int argc, char** argv) string encoded; for (int i = cin.get(); i != -1; i = cin.get()) encoded.push_back((char)i); - cout << m.decode(fromHex(encoded)); + cout << m.decode(fromHex(encoded), outputIndex, prefs); } - - // TODO: read abi to determine output format. - (void)encoding; - (void)clearZeroes; - (void)clearNulls; - (void)outputIndex; } return 0; diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 1c7a4cd61..6468e250f 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -42,14 +42,15 @@ enum class WhenError }; /// Convert a series of bytes to the corresponding string of hex duplets. -/// @param _w specifies the width of each of the elements. Defaults to two - enough to represent a byte. +/// @param _w specifies the width of the first of the elements. Defaults to two - enough to represent a byte. /// @example toHex("A\x69") == "4169" template std::string toHex(_T const& _data, int _w = 2) { std::ostringstream ret; + unsigned ii = 0; for (auto i: _data) - ret << std::hex << std::setfill('0') << std::setw(_w) << (int)(typename std::make_unsigned::type)i; + ret << std::hex << std::setfill('0') << std::setw(ii++ ? 2 : _w) << (int)(typename std::make_unsigned::type)i; return ret.str(); } @@ -74,6 +75,13 @@ inline std::string asString(bytes const& _b) return std::string((char const*)_b.data(), (char const*)(_b.data() + _b.size())); } +/// Converts byte array ref to a string containing the same (binary) data. Unless +/// the byte array happens to contain ASCII data, this won't be printable. +inline std::string asString(bytesConstRef _b) +{ + return std::string((char const*)_b.data(), (char const*)(_b.data() + _b.size())); +} + /// Converts a string to a byte array containing the string's (byte) data. inline bytes asBytes(std::string const& _b) { From 29f40bff0f0e4b7aa3a229660c535dc30dfe190f Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 28 Mar 2015 09:39:04 +0100 Subject: [PATCH 139/228] makePeer and support setting listen IP. --- alethzero/CMakeLists.txt | 5 +- alethzero/Connect.cpp | 62 ++++++++++++++++++++ alethzero/Connect.h | 50 ++++++++++++++++ alethzero/Connect.ui | 123 +++++++++++++++++++++++++++++++++++++++ alethzero/Main.ui | 51 ++++++++-------- alethzero/MainWin.cpp | 53 ++++++++++------- alethzero/MainWin.h | 3 + eth/main.cpp | 9 ++- libp2p/Common.cpp | 1 + libp2p/Common.h | 3 +- libp2p/Host.cpp | 51 ++++++---------- libp2p/Host.h | 10 +++- libp2p/Network.cpp | 25 ++++++++ libp2p/Network.h | 3 + libp2p/NodeTable.cpp | 27 +++------ libp2p/NodeTable.h | 2 +- libp2p/Session.cpp | 2 +- libwebthree/WebThree.cpp | 16 +++-- libwebthree/WebThree.h | 42 +++++++++---- neth/main.cpp | 6 +- test/peer.cpp | 8 +-- 21 files changed, 414 insertions(+), 138 deletions(-) create mode 100644 alethzero/Connect.cpp create mode 100644 alethzero/Connect.h create mode 100644 alethzero/Connect.ui diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index 49c847a16..c81c86222 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -19,6 +19,7 @@ find_package (Qt5WebEngine QUIET) find_package (Qt5WebEngineWidgets QUIET) qt5_wrap_ui(ui_Main.h Main.ui) +qt5_wrap_ui(ui_Connect.h Connect.ui) qt5_wrap_ui(ui_Debugger.h Debugger.ui) qt5_wrap_ui(ui_Transact.h Transact.ui) @@ -32,8 +33,8 @@ endif () # eth_add_executable is defined in cmake/EthExecutableHelper.cmake eth_add_executable(${EXECUTABLE} - ICON alethzero - UI_RESOURCES alethzero.icns Main.ui Debugger.ui Transact.ui + ICON alethzero + UI_RESOURCES alethzero.icns Main.ui Connect.ui Debugger.ui Transact.ui WIN_RESOURCES alethzero.rc ) diff --git a/alethzero/Connect.cpp b/alethzero/Connect.cpp new file mode 100644 index 000000000..076ebafe0 --- /dev/null +++ b/alethzero/Connect.cpp @@ -0,0 +1,62 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file Connect.cpp + * @author Alex Leverington + * @date 2015 + */ + +#include "Connect.h" + +#include +#include "ui_Connect.h" + +Connect::Connect(QWidget *parent) : + QDialog(parent), + ui(new Ui::Connect) +{ + reset(); +} + +Connect::~Connect() +{ + delete ui; +} + +void Connect::setEnvironment(QStringList const& _nodes) +{ + ui->host->addItems(_nodes); +} + +void Connect::reset() +{ + ui->setupUi(this); +} + +QString Connect::host() +{ + return ui->host->currentText(); +} + +QString Connect::nodeId() +{ + return ui->nodeId->text(); +} + +bool Connect::required() +{ + return ui->required->isChecked(); +} diff --git a/alethzero/Connect.h b/alethzero/Connect.h new file mode 100644 index 000000000..d8059acd4 --- /dev/null +++ b/alethzero/Connect.h @@ -0,0 +1,50 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file Connect.h + * @author Alex Leverington + * @date 2015 + */ + +#pragma once + +#include +#include + +namespace Ui { class Connect; } +namespace dev { namespace p2p { class Host; } } + +class Connect : public QDialog +{ + Q_OBJECT + +public: + explicit Connect(QWidget *parent = 0); + ~Connect(); + + void setEnvironment(QStringList const& _nodes); + + void reset(); + + QString host(); + + QString nodeId(); + + bool required(); + +private: + Ui::Connect *ui; +}; diff --git a/alethzero/Connect.ui b/alethzero/Connect.ui new file mode 100644 index 000000000..9a0522e5f --- /dev/null +++ b/alethzero/Connect.ui @@ -0,0 +1,123 @@ + + + Connect + + + Qt::WindowModal + + + + 0 + 0 + 343 + 178 + + + + + 0 + 0 + + + + + 343 + 178 + + + + Connect to Peer + + + true + + + + + + + + + 311 + 0 + + + + true + + + + + + + Node Id + + + + + + + Required (Always Connect to this Peer) + + + false + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + Enter a peer to which a connection may be made: + + + + + + + + + buttonBox + accepted() + Connect + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Connect + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 99fea901c..b59c9ce2d 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -134,7 +134,7 @@ - + @@ -283,7 +283,7 @@ - + &Listen on @@ -303,7 +303,7 @@ - + 1024 @@ -316,17 +316,7 @@ - - - - &Client Name - - - clientName - - - - + @@ -339,13 +329,6 @@ - - - - Anonymous - - - @@ -356,20 +339,37 @@ - + Automatic - + Public IP + + + + &Client Name + + + clientName + + + + + + + Anonymous + + + @@ -1456,12 +1456,12 @@ font-size: 14pt Show &Anonymous Accounts - + true - Use &Past Peers + &Drop Past Peers @@ -1709,7 +1709,6 @@ font-size: 14pt idealPeers listenIP port - clientName transactionQueue pendingInfo blockChainFilter diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 1ae139cce..4e5ed49a3 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -28,6 +28,7 @@ //pragma GCC diagnostic ignored "-Werror=pedantic" #include #include +#include #include #include #include @@ -251,8 +252,11 @@ void Main::addNewId(QString _ids) NetworkPreferences Main::netPrefs() const { - return NetworkPreferences(ui->forcePublicIP->text().toStdString(), ui->listenIP->text().toStdString(), ui->port->value(), ui->upnp->isChecked()); -// return NetworkPreferences(ui->port->value(), ui->forceAddress->text().toStdString(), ui->upnp->isChecked(), ui->localNetworking->isChecked()); + auto publicip = ui->forcePublicIP->text().toStdString(); + if (isPublicAddress(publicip)) + return NetworkPreferences(publicip, ui->listenIP->text().toStdString(), ui->port->value(), ui->upnp->isChecked()); + else + return NetworkPreferences(ui->listenIP->text().toStdString(), ui->port->value(), ui->upnp->isChecked()); } void Main::onKeysChanged() @@ -676,10 +680,7 @@ void Main::writeSettings() } s.setValue("upnp", ui->upnp->isChecked()); -#warning fixme -// s.setValue("forceAddress", ui->forceAddress->text()); - s.setValue("usePast", ui->usePast->isChecked()); -// s.setValue("localNetworking", ui->localNetworking->isChecked()); + s.setValue("forceAddress", ui->forcePublicIP->text()); s.setValue("forceMining", ui->forceMining->isChecked()); s.setValue("paranoia", ui->paranoia->isChecked()); s.setValue("natSpec", ui->natSpec->isChecked()); @@ -687,6 +688,7 @@ void Main::writeSettings() s.setValue("showAllAccounts", ui->showAllAccounts->isChecked()); s.setValue("clientName", ui->clientName->text()); s.setValue("idealPeers", ui->idealPeers->value()); + s.setValue("listenIP", ui->listenIP->text()); s.setValue("port", ui->port->value()); s.setValue("url", ui->urlEdit->text()); s.setValue("privateChain", m_privateChain); @@ -746,10 +748,8 @@ void Main::readSettings(bool _skipGeometry) } ui->upnp->setChecked(s.value("upnp", true).toBool()); -#warning fixme -// ui->forceAddress->setText(s.value("forceAddress", "").toString()); - ui->usePast->setChecked(s.value("usePast", true).toBool()); -// ui->localNetworking->setChecked(s.value("localNetworking", true).toBool()); + ui->forcePublicIP->setText(s.value("forceAddress", "").toString()); + ui->dropPeers->setChecked(false); ui->forceMining->setChecked(s.value("forceMining", false).toBool()); on_forceMining_triggered(); ui->paranoia->setChecked(s.value("paranoia", false).toBool()); @@ -760,6 +760,7 @@ void Main::readSettings(bool _skipGeometry) if (ui->clientName->text().isEmpty()) ui->clientName->setText(QInputDialog::getText(nullptr, "Enter identity", "Enter a name that will identify you on the peer network")); ui->idealPeers->setValue(s.value("idealPeers", ui->idealPeers->value()).toInt()); + ui->listenIP->setText(s.value("listenIP", "").toString()); ui->port->setValue(s.value("port", ui->port->value()).toInt()); ui->nameReg->setText(s.value("nameReg", "").toString()); m_privateChain = s.value("privateChain", "").toString(); @@ -1746,11 +1747,8 @@ void Main::on_net_triggered() if (ui->net->isChecked()) { web3()->setIdealPeerCount(ui->idealPeers->value()); - web3()->setNetworkPreferences(netPrefs()); + web3()->setNetworkPreferences(netPrefs(), ui->dropPeers->isChecked()); ethereum()->setNetworkId(m_privateChain.size() ? sha3(m_privateChain.toStdString()) : h256()); - // TODO: p2p -// if (m_networkConfig.size()/* && ui->usePast->isChecked()*/) -// web3()->restoreNetwork(bytesConstRef((byte*)m_networkConfig.data(), m_networkConfig.size())); web3()->startNetwork(); ui->downloadView->setDownloadMan(ethereum()->downloadMan()); } @@ -1768,13 +1766,26 @@ void Main::on_connect_triggered() ui->net->setChecked(true); on_net_triggered(); } - bool ok = false; - QString s = QInputDialog::getItem(this, "Connect to a Network Peer", "Enter a peer to which a connection may be made:", m_servers, m_servers.count() ? rand() % m_servers.count() : 0, true, &ok); - if (ok && s.contains(":")) + + m_connect.setEnvironment(m_servers); + if (m_connect.exec() == QDialog::Accepted) { - string host = s.section(":", 0, 0).toStdString(); - unsigned short port = s.section(":", 1).toInt(); - web3()->connect(host, port); + bool required = m_connect.required(); + string host(m_connect.host().toUtf8().constData()); + NodeId nodeid; + try + { + string nstr(m_connect.nodeId().toUtf8().constData()); + nodeid = NodeId(fromHex(nstr)); + } + catch (BadHexCharacter()) {} + + m_connect.reset(); + + if (required) + web3()->requirePeer(nodeid, host); + else + web3()->addNode(nodeid, host); } } @@ -1885,7 +1896,7 @@ void Main::on_go_triggered() ui->net->setChecked(true); on_net_triggered(); } - web3()->connect(Host::pocHost()); + web3()->addNode(p2p::NodeId(), Host::pocHost()); } QString Main::prettyU256(dev::u256 _n) const diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index ba89b455a..fccbc855d 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -40,6 +40,7 @@ #include "Context.h" #include "Transact.h" #include "NatspecHandler.h" +#include "Connect.h" namespace Ui { class Main; @@ -256,4 +257,6 @@ private: std::unique_ptr m_dappHost; DappLoader* m_dappLoader; QWebEnginePage* m_webPage; + + Connect m_connect; }; diff --git a/eth/main.cpp b/eth/main.cpp index abac6462c..206a6df57 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -421,8 +421,7 @@ int main(int argc, char** argv) StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat); VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); -#warning fixme - NetworkPreferences netPrefs(listenPort); + auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); std::string clientImplString = "Ethereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""); dev::WebThreeDirect web3( @@ -450,9 +449,9 @@ int main(int argc, char** argv) web3.startNetwork(); if (bootstrap) - web3.connect(Host::pocHost()); + web3.addNode(p2p::NodeId(), Host::pocHost()); if (remoteHost.size()) - web3.connect(remoteHost, remotePort); + web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort)); #if ETH_JSONRPC shared_ptr jsonrpcServer; @@ -512,7 +511,7 @@ int main(int argc, char** argv) string addr; unsigned port; iss >> addr >> port; - web3.connect(addr, (short)port); + web3.addNode(p2p::NodeId(), addr + ":" + toString(port ? port : p2p::c_defaultIPPort)); } else if (cmd == "netstop") { diff --git a/libp2p/Common.cpp b/libp2p/Common.cpp index 7051322a9..95c96e346 100644 --- a/libp2p/Common.cpp +++ b/libp2p/Common.cpp @@ -25,6 +25,7 @@ using namespace dev; using namespace dev::p2p; const unsigned dev::p2p::c_protocolVersion = 3; +const unsigned dev::p2p::c_defaultIPPort = 30303; bool p2p::isPublicAddress(std::string const& _addressToCheck) { diff --git a/libp2p/Common.h b/libp2p/Common.h index 3303c9c07..c9aee9a0e 100644 --- a/libp2p/Common.h +++ b/libp2p/Common.h @@ -50,7 +50,8 @@ namespace p2p /// Peer network protocol version. extern const unsigned c_protocolVersion; - +extern const unsigned c_defaultIPPort; + using NodeId = h512; bool isPrivateAddress(bi::address const& _addressToCheck); diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 3835b9504..ac9e9671b 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -66,10 +66,6 @@ Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, byte m_alias(networkAlias(_restoreNetwork)), m_lastPing(chrono::steady_clock::time_point::min()) { - for (auto address: m_ifAddresses) - if (address.is_v4()) - clog(NetNote) << "IP Address: " << address << " = " << (isPrivateAddress(address) ? "[LOCAL]" : "[PEER]"); - clog(NetNote) << "Id:" << id(); } @@ -387,7 +383,7 @@ string Host::pocHost() return "poc-" + strs[1] + ".ethdev.com"; } -void Host::addNode(NodeId const& _node, std::string const& _addr, unsigned short _tcpPeerPort, unsigned short _udpNodePort) +void Host::addNode(NodeId const& _node, bi::address const& _addr, unsigned short _udpNodePort, unsigned short _tcpPeerPort) { // TODO: p2p clean this up (bring tested acceptor code over from network branch) while (isWorking() && !m_run) @@ -403,37 +399,14 @@ void Host::addNode(NodeId const& _node, std::string const& _addr, unsigned short cwarn << "Private port being recorded - setting to 0"; _tcpPeerPort = 0; } - - boost::system::error_code ec; - bi::address addr = bi::address::from_string(_addr, ec); - if (ec) - { - bi::tcp::resolver *r = new bi::tcp::resolver(m_ioService); - r->async_resolve({_addr, toString(_tcpPeerPort)}, [=](boost::system::error_code const& _ec, bi::tcp::resolver::iterator _epIt) - { - if (!_ec) - { - bi::tcp::endpoint tcp = *_epIt; - if (m_nodeTable) m_nodeTable->addNode(Node(_node, NodeIPEndpoint(bi::udp::endpoint(tcp.address(), _udpNodePort), tcp))); - } - delete r; - }); - } - else - if (m_nodeTable) m_nodeTable->addNode(Node(_node, NodeIPEndpoint(bi::udp::endpoint(addr, _udpNodePort), bi::tcp::endpoint(addr, _tcpPeerPort)))); -} - -void Host::relinquishPeer(NodeId const& _node) -{ - Guard l(x_requiredPeers); - if (m_requiredPeers.count(_node)) - m_requiredPeers.erase(_node); + + if (m_nodeTable) m_nodeTable->addNode(Node(_node, NodeIPEndpoint(bi::udp::endpoint(_addr, _udpNodePort), bi::tcp::endpoint(_addr, _tcpPeerPort)))); } -void Host::requirePeer(NodeId const& _n, std::string const& _udpAddr, unsigned short _udpPort, std::string const& _tcpAddr, unsigned short _tcpPort) +void Host::requirePeer(NodeId const& _n, bi::address const& _udpAddr, unsigned short _udpPort, bi::address const& _tcpAddr, unsigned short _tcpPort) { - auto naddr = bi::address::from_string(_udpAddr); - auto paddr = _tcpAddr.empty() ? naddr : bi::address::from_string(_tcpAddr); + auto naddr = _udpAddr; + auto paddr = _tcpAddr.is_unspecified() ? naddr : _tcpAddr; auto udp = bi::udp::endpoint(naddr, _udpPort); auto tcp = bi::tcp::endpoint(paddr, _tcpPort ? _tcpPort : _udpPort); Node node(_n, NodeIPEndpoint(udp, tcp)); @@ -468,11 +441,18 @@ void Host::requirePeer(NodeId const& _n, std::string const& _udpAddr, unsigned s { if (!_ec && m_nodeTable) if (auto n = m_nodeTable->node(_n)) - requirePeer(n.id, n.endpoint.udp.address().to_string(), n.endpoint.udp.port(), n.endpoint.tcp.address().to_string(), n.endpoint.tcp.port()); + requirePeer(n.id, n.endpoint.udp.address(), n.endpoint.udp.port(), n.endpoint.tcp.address(), n.endpoint.tcp.port()); }); } } +void Host::relinquishPeer(NodeId const& _node) +{ + Guard l(x_requiredPeers); + if (m_requiredPeers.count(_node)) + m_requiredPeers.erase(_node); +} + void Host::connect(std::shared_ptr const& _p) { if (!m_run) @@ -760,6 +740,9 @@ void Host::restoreNetwork(bytesConstRef _b) if (!isStarted()) BOOST_THROW_EXCEPTION(NetworkStartRequired()); + if (m_dropPeers) + return; + RecursiveGuard l(x_sessions); RLP r(_b); if (r.itemCount() > 0 && r[0].isInt() && r[0].toInt() == dev::p2p::c_protocolVersion) diff --git a/libp2p/Host.h b/libp2p/Host.h index 7a18380c3..0df24ca79 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -94,6 +94,9 @@ public: /// Default host for current version of client. static std::string pocHost(); + + /// Resolve "host:port" or "ip:port" string as TCP endpoint. Returns unspecified endpoint on failure. + static bi::tcp::endpoint resolveHost(Host& _host, std::string const& _addr) { return Network::resolveHost(_host.m_ioService, _addr); } /// Register a peer-capability; all new peer connections will have this capability. template std::shared_ptr registerCapability(T* _t) { _t->m_host = this; auto ret = std::shared_ptr(_t); m_capabilities[std::make_pair(T::staticName(), T::staticVersion())] = ret; return ret; } @@ -103,10 +106,10 @@ public: template std::shared_ptr cap() const { try { return std::static_pointer_cast(m_capabilities.at(std::make_pair(T::staticName(), T::staticVersion()))); } catch (...) { return nullptr; } } /// Add node as a peer candidate. Node is added if discovery ping is successful and table has capacity. - void addNode(NodeId const& _node, std::string const& _addr, unsigned short _tcpPort, unsigned short _udpPort); + void addNode(NodeId const& _node, bi::address const& _addr, unsigned short _udpPort, unsigned short _tcpPort); /// Create Peer and attempt keeping peer connected. - void requirePeer(NodeId const& _node, std::string const& _udpAddr, unsigned short _udpPort, std::string const& _tcpAddr = "", unsigned short _tcpPort = 0); + void requirePeer(NodeId const& _node, bi::address const& _udpAddr, unsigned short _udpPort, bi::address const& _tcpAddr = bi::address(), unsigned short _tcpPort = 0); /// Note peer as no longer being required. void relinquishPeer(NodeId const& _node); @@ -132,7 +135,7 @@ public: // TODO: P2P this should be combined with peers into a HostStat object of some kind; coalesce data, as it's only used for status information. Peers getPeers() const { RecursiveGuard l(x_sessions); Peers ret; for (auto const& i: m_peers) ret.push_back(*i.second); return ret; } - void setNetworkPreferences(NetworkPreferences const& _p) { auto had = isStarted(); if (had) stop(); m_netPrefs = _p; if (had) start(); } + void setNetworkPreferences(NetworkPreferences const& _p, bool _dropPeers = false) { m_dropPeers = _dropPeers; auto had = isStarted(); if (had) stop(); m_netPrefs = _p; if (had) start(); } /// Start network. @threadsafe void start(); @@ -236,6 +239,7 @@ private: std::chrono::steady_clock::time_point m_lastPing; ///< Time we sent the last ping to all peers. bool m_accepting = false; + bool m_dropPeers = false; }; } diff --git a/libp2p/Network.cpp b/libp2p/Network.cpp index 7be54d9fb..42208ad60 100644 --- a/libp2p/Network.cpp +++ b/libp2p/Network.cpp @@ -27,6 +27,7 @@ #endif #include +#include #include #include @@ -206,3 +207,27 @@ bi::tcp::endpoint Network::traverseNAT(std::set const& _ifAddresses return upnpep; } + +bi::tcp::endpoint Network::resolveHost(ba::io_service& _ioService, string const& _addr) +{ + vector split; + boost::split(split, _addr, boost::is_any_of(":")); + unsigned port = split.size() > 1 ? stoi(split[1]) : c_defaultIPPort; + + bi::tcp::endpoint ep(bi::address(), port); + boost::system::error_code ec; + bi::address address = bi::address::from_string(split[0], ec); + if (!ec) + ep.address(address); + else + { + boost::system::error_code ec; + // resolve returns an iterator (host can resolve to multiple addresses) + bi::tcp::resolver r(_ioService); + auto it = r.resolve({split[0], toString(port)}, ec); + if (!ec) + ep = *it; + } + return ep; +} + diff --git a/libp2p/Network.h b/libp2p/Network.h index 0e3a08398..46034aef3 100644 --- a/libp2p/Network.h +++ b/libp2p/Network.h @@ -54,6 +54,9 @@ public: /// Return public endpoint of upnp interface. If successful o_upnpifaddr will be a private interface address and endpoint will contain public address and port. static bi::tcp::endpoint traverseNAT(std::set const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr); + + /// Resolve "host:port" string as TCP endpoint. Returns unspecified endpoint on failure. + static bi::tcp::endpoint resolveHost(ba::io_service& _ioService, std::string const& _host); }; struct NetworkPreferences diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index c230c5441..7b9852672 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -101,6 +101,7 @@ shared_ptr NodeTable::addNode(Node const& _node) shared_ptr ret(new NodeEntry(m_node, _node.id, NodeIPEndpoint(_node.endpoint.udp, _node.endpoint.tcp))); m_nodes[_node.id] = ret; ret->cullEndpoint(); + clog(NodeTableConnect) << "addNode pending for" << m_node.endpoint.udp << m_node.endpoint.tcp; PingNode p(_node.endpoint.udp, m_node.endpoint.udp.address().to_string(), m_node.endpoint.udp.port()); p.sign(m_secret); m_socketPointer->send(p); @@ -450,6 +451,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes return; // unsolicited pong; don't note node as active } + clog(NodeTableConnect) << "PONG from " << nodeid.abridged() << _from; break; } @@ -550,31 +552,16 @@ void NodeTable::doRefreshBuckets(boost::system::error_code const& _ec) clog(NodeTableEvent) << "refreshing buckets"; bool connected = m_socketPointer->isOpen(); - bool refreshed = false; if (connected) { - Guard l(x_state); - for (auto& d: m_state) - if (chrono::steady_clock::now() - d.modified > c_bucketRefresh) - { - d.touch(); - while (!d.nodes.empty()) - { - auto n = d.nodes.front(); - if (auto p = n.lock()) - { - refreshed = true; - ping(p.get()); - break; - } - d.nodes.pop_front(); - } - } + NodeId randNodeId; + crypto::Nonce::get().ref().copyTo(randNodeId.ref().cropped(0,h256::size)); + crypto::Nonce::get().ref().copyTo(randNodeId.ref().cropped(h256::size,h256::size)); + discover(randNodeId); } - unsigned nextRefresh = connected ? (refreshed ? 200 : c_bucketRefresh.count()*1000) : 10000; auto runcb = [this](boost::system::error_code const& error) { doRefreshBuckets(error); }; - m_bucketRefreshTimer.expires_from_now(boost::posix_time::milliseconds(nextRefresh)); + m_bucketRefreshTimer.expires_from_now(boost::posix_time::milliseconds(c_bucketRefresh.count())); m_bucketRefreshTimer.async_wait(runcb); } diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h index 6a167930d..9ff28b7f1 100644 --- a/libp2p/NodeTable.h +++ b/libp2p/NodeTable.h @@ -195,7 +195,7 @@ private: /* todo: replace boost::posix_time; change constants to upper camelcase */ boost::posix_time::milliseconds const c_evictionCheckInterval = boost::posix_time::milliseconds(75); ///< Interval at which eviction timeouts are checked. std::chrono::milliseconds const c_reqTimeout = std::chrono::milliseconds(300); ///< How long to wait for requests (evict, find iterations). - std::chrono::seconds const c_bucketRefresh = std::chrono::seconds(3600); ///< Refresh interval prevents bucket from becoming stale. [Kademlia] + std::chrono::milliseconds const c_bucketRefresh = std::chrono::milliseconds(112500); ///< Refresh interval prevents bucket from becoming stale. [Kademlia] struct NodeBucket { diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 65dedb302..dac588149 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -237,7 +237,7 @@ bool Session::interpret(PacketType _t, RLP const& _r) // OK passed all our checks. Assume it's good. addRating(1000); - m_server->addNode(id, ep.address().to_string(), ep.port(), ep.port()); + m_server->addNode(id, ep.address(), ep.port(), ep.port()); clogS(NetTriviaDetail) << "New peer: " << ep << "(" << id .abridged()<< ")"; CONTINUE:; LAMEPEER:; diff --git a/libwebthree/WebThree.cpp b/libwebthree/WebThree.cpp index 6c6414741..b2c6765b5 100644 --- a/libwebthree/WebThree.cpp +++ b/libwebthree/WebThree.cpp @@ -27,7 +27,6 @@ #include #include -#include #include #include #include @@ -73,12 +72,12 @@ WebThreeDirect::~WebThreeDirect() m_ethereum.reset(); } -void WebThreeDirect::setNetworkPreferences(p2p::NetworkPreferences const& _n) +void WebThreeDirect::setNetworkPreferences(p2p::NetworkPreferences const& _n, bool _dropPeers) { auto had = haveNetwork(); if (had) stopNetwork(); - m_net.setNetworkPreferences(_n); + m_net.setNetworkPreferences(_n, _dropPeers); if (had) startNetwork(); } @@ -103,7 +102,14 @@ bytes WebThreeDirect::saveNetwork() return m_net.saveNetwork(); } -void WebThreeDirect::connect(std::string const& _seedHost, unsigned short _port) +void WebThreeDirect::addNode(NodeId const& _node, bi::tcp::endpoint const& _host) { - m_net.addNode(NodeId(), _seedHost, _port, _port); + m_net.addNode(_node, _host.address(), _host.port(), _host.port()); } + +void WebThreeDirect::requirePeer(NodeId const& _node, bi::tcp::endpoint const& _host) +{ + m_net.requirePeer(_node, _host.address(), _host.port()); +} + + diff --git a/libwebthree/WebThree.h b/libwebthree/WebThree.h index 242639af4..adb691753 100644 --- a/libwebthree/WebThree.h +++ b/libwebthree/WebThree.h @@ -63,9 +63,12 @@ public: /// 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; - + /// Add node to connect to. + virtual void addNode(p2p::NodeId const& _node, bi::tcp::endpoint const& _hostEndpoint) = 0; + + /// Require connection to peer. + virtual void requirePeer(p2p::NodeId const& _node, bi::tcp::endpoint const& _endpoint) = 0; + /// Save peers virtual dev::bytes saveNetwork() = 0; @@ -74,7 +77,7 @@ public: virtual bool haveNetwork() const = 0; - virtual void setNetworkPreferences(p2p::NetworkPreferences const& _n) = 0; + virtual void setNetworkPreferences(p2p::NetworkPreferences const& _n, bool _dropPeers) = 0; virtual p2p::NodeId id() const = 0; @@ -137,22 +140,37 @@ public: /// Same as peers().size(), but more efficient. size_t peerCount() const override; - - /// Connect to a particular peer. - void connect(std::string const& _seedHost, unsigned short _port = 30303) override; - + + /// Add node to connect to. + virtual void addNode(p2p::NodeId const& _node, bi::tcp::endpoint const& _hostEndpoint) override; + + /// Add node to connect to. + void addNode(p2p::NodeId const& _node, std::string const& _hostString) { addNode(_node, resolveHost(_hostString)); } + + /// Add node to connect to. + void addNode(bi::tcp::endpoint const& _endpoint) { addNode(p2p::NodeId(), _endpoint); } + + /// Add node to connect to. + void addNode(std::string const& _hostString) { addNode(p2p::NodeId(), _hostString); } + + /// Require connection to peer. + void requirePeer(p2p::NodeId const& _node, bi::tcp::endpoint const& _endpoint) override; + + /// Require connection to peer. + void requirePeer(p2p::NodeId const& _node, std::string const& _hostString) { requirePeer(_node, resolveHost(_hostString)); } + + /// Resolve "host[:port]" string as TCP endpoint. Returns unspecified endpoint on failure. + bi::tcp::endpoint resolveHost(std::string const& _host) { return haveNetwork() ? p2p::Host::resolveHost(m_net, _host) : bi::tcp::endpoint(); } + /// Save peers dev::bytes saveNetwork() override; -// /// Restore peers -// void restoreNetwork(bytesConstRef _saved) override; - /// Sets the ideal number of peers. void setIdealPeerCount(size_t _n) override; bool haveNetwork() const override { return m_net.isStarted(); } - void setNetworkPreferences(p2p::NetworkPreferences const& _n) override; + void setNetworkPreferences(p2p::NetworkPreferences const& _n, bool _dropPeers = false) override; p2p::NodeId id() const override { return m_net.id(); } diff --git a/neth/main.cpp b/neth/main.cpp index cd0944425..4d9870b44 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -572,9 +572,9 @@ int main(int argc, char** argv) web3.startNetwork(); if (bootstrap) - web3.connect(Host::pocHost()); + web3.addNode(p2p::NodeId(), Host::pocHost()); if (remoteHost.size()) - web3.connect(remoteHost, remotePort); + web3.addNode(p2p::NodeId(), remoteHost + ":" + toString(remotePort)); if (c && mining) c->startMining(); @@ -699,7 +699,7 @@ int main(int argc, char** argv) string addr; unsigned port; iss >> addr >> port; - web3.connect(addr, (short)port); + web3.addNode(p2p::NodeId(), addr + ":" + toString(port ? port : p2p::c_defaultIPPort)); } else if (cmd == "netstop") { diff --git a/test/peer.cpp b/test/peer.cpp index b90fc7404..48431504f 100644 --- a/test/peer.cpp +++ b/test/peer.cpp @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(host) auto node2 = host2.id(); host2.start(); - host1.addNode(node2, "127.0.0.1", host2prefs.listenPort, host2prefs.listenPort); + host1.addNode(node2, bi::address::from_string("127.0.0.1"), host2prefs.listenPort, host2prefs.listenPort); this_thread::sleep_for(chrono::seconds(3)); @@ -73,11 +73,11 @@ BOOST_AUTO_TEST_CASE(save_nodes) Host& host = *hosts.front(); for (auto const& h: hosts) - host.addNode(h->id(), "127.0.0.1", h->listenPort(), h->listenPort()); + host.addNode(h->id(), bi::address::from_string("127.0.0.1"), h->listenPort(), h->listenPort()); Host& host2 = *hosts.back(); for (auto const& h: hosts) - host2.addNode(h->id(), "127.0.0.1", h->listenPort(), h->listenPort()); + host2.addNode(h->id(), bi::address::from_string("127.0.0.1"), h->listenPort(), h->listenPort()); this_thread::sleep_for(chrono::milliseconds(2000)); bytes firstHostNetwork(host.saveNetwork()); @@ -122,7 +122,7 @@ int peerTest(int argc, char** argv) Host ph("Test", NetworkPreferences(listenPort)); if (!remoteHost.empty() && !remoteAlias) - ph.addNode(remoteAlias, remoteHost, remotePort, remotePort); + ph.addNode(remoteAlias, bi::address::from_string(remoteHost), remotePort, remotePort); this_thread::sleep_for(chrono::milliseconds(200)); From 549ab8860918cebecb215aa955c13cff95a5b6ec Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 28 Mar 2015 10:06:04 +0100 Subject: [PATCH 140/228] garbage collect timers --- libp2p/Host.cpp | 7 +++++++ libp2p/Host.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index ac9e9671b..7c34b31f5 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -560,6 +560,13 @@ void Host::run(boost::system::error_code const&) Guard l(x_connecting); m_connecting.remove_if([](std::weak_ptr h){ return h.lock(); }); } + { + Guard l(x_timers); + m_timers.remove_if([](std::shared_ptr t) + { + return t->expires_from_now().total_milliseconds() > 0; + }); + } for (auto p: m_sessions) if (auto pp = p.second.lock()) diff --git a/libp2p/Host.h b/libp2p/Host.h index 0df24ca79..deb84a4ff 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -223,7 +223,9 @@ private: std::set m_requiredPeers; Mutex x_requiredPeers; + /// Deadline timers used for isolated network events. GC'd by run. std::list> m_timers; + Mutex x_timers; /// The nodes to which we are currently connected. Used by host to service peer requests and keepAlivePeers and for shutdown. (see run()) /// Mutable because we flush zombie entries (null-weakptrs) as regular maintenance from a const method. From b05546fef645d5b73b882df49cf2c086415d7ebe Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 28 Mar 2015 10:07:13 +0100 Subject: [PATCH 141/228] dealloc basic timers first --- libp2p/Host.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libp2p/Host.h b/libp2p/Host.h index deb84a4ff..b58939eb1 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -222,10 +222,6 @@ private: /// Peers we try to connect regardless of p2p network. std::set m_requiredPeers; Mutex x_requiredPeers; - - /// Deadline timers used for isolated network events. GC'd by run. - std::list> m_timers; - Mutex x_timers; /// The nodes to which we are currently connected. Used by host to service peer requests and keepAlivePeers and for shutdown. (see run()) /// Mutable because we flush zombie entries (null-weakptrs) as regular maintenance from a const method. @@ -238,6 +234,10 @@ private: unsigned m_idealPeerCount = 5; ///< Ideal number of peers to be connected to. std::map> m_capabilities; ///< Each of the capabilities we support. + + /// Deadline timers used for isolated network events. GC'd by run. + std::list> m_timers; + Mutex x_timers; std::chrono::steady_clock::time_point m_lastPing; ///< Time we sent the last ping to all peers. bool m_accepting = false; From 88fa4830554a8afe3db23b6bb31c25fedc67af5b Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 28 Mar 2015 10:25:13 +0100 Subject: [PATCH 142/228] fix neth. fix issue w/udp listening to 30303. --- libp2p/Host.cpp | 9 ++++----- libp2p/Host.h | 4 ++-- libp2p/Network.h | 34 ++++++++++++++++------------------ neth/main.cpp | 2 +- 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 7c34b31f5..2354ed5d3 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -316,12 +316,12 @@ void Host::determinePublic() ep = Network::traverseNAT(ifAddresses, m_netPrefs.listenPort, natIFAddr); if (lset && natIFAddr != laddr) - // if listen address is set we use it, even if upnp returns different + // if listen address is set, Host will use it, even if upnp returns different clog(NetWarn) << "Listen address" << laddr << "differs from local address" << natIFAddr << "returned by UPnP!"; if (pset && ep.address() != paddr) { - // if public address is set we advertise it, even if upnp returns different + // if public address is set, Host will advertise it, even if upnp returns different clog(NetWarn) << "Specified public address" << paddr << "differs from external address" << ep.address() << "returned by UPnP!"; ep.address(paddr); } @@ -635,10 +635,9 @@ void Host::startedWorking() runAcceptor(); } else - clog(NetNote) << "p2p.start.notice id:" << id().abridged() << "Listen port is invalid or unavailable. Node Table using default port (30303)."; + clog(NetNote) << "p2p.start.notice id:" << id().abridged() << "TCP Listen port is invalid or unavailable."; - // this doesn't work unless local-networking is enabled because the port is -1 - m_nodeTable.reset(new NodeTable(m_ioService, m_alias, bi::address::from_string(listenAddress()), listenPort() > 0 ? listenPort() : 30303)); + m_nodeTable.reset(new NodeTable(m_ioService, m_alias, bi::address::from_string(listenAddress()), listenPort())); m_nodeTable->setEventHandler(new HostNodeTableHandler(*this)); restoreNetwork(&m_restoreNetwork); diff --git a/libp2p/Host.h b/libp2p/Host.h index b58939eb1..66ad373e3 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -124,10 +124,10 @@ public: size_t peerCount() const; /// Get the address we're listening on currently. - std::string listenAddress() const { return m_tcpPublic.address().to_string(); } + std::string listenAddress() const { return m_netPrefs.listenIPAddress.empty() ? "0.0.0.0" : m_netPrefs.listenIPAddress; } /// Get the port we're listening on currently. - unsigned short listenPort() const { return m_tcpPublic.port(); } + unsigned short listenPort() const { return m_netPrefs.listenPort; } /// Serialise the set of known peers. bytes saveNetwork() const; diff --git a/libp2p/Network.h b/libp2p/Network.h index 46034aef3..5f5ebc868 100644 --- a/libp2p/Network.h +++ b/libp2p/Network.h @@ -37,7 +37,22 @@ namespace dev namespace p2p { -struct NetworkPreferences; +struct NetworkPreferences +{ + // Default Network Preferences + NetworkPreferences(unsigned short lp = 30303): listenPort(lp) {} + + // Network Preferences with specific Listen IP + NetworkPreferences(std::string l, unsigned short lp = 30303, bool u = true): publicIPAddress(), listenIPAddress(l), listenPort(lp), traverseNAT(u) {} + + // Network Preferences with intended Public IP + NetworkPreferences(std::string publicIP, std::string l = std::string(), unsigned short lp = 30303, bool u = true): publicIPAddress(publicIP), listenIPAddress(l), listenPort(lp), traverseNAT(u) { if (!publicIPAddress.empty() && !isPublicAddress(publicIPAddress)) BOOST_THROW_EXCEPTION(InvalidPublicIPAddress()); } + + std::string publicIPAddress; + std::string listenIPAddress; + unsigned short listenPort = 30303; + bool traverseNAT = true; +}; /** * @brief Network Class @@ -58,23 +73,6 @@ public: /// Resolve "host:port" string as TCP endpoint. Returns unspecified endpoint on failure. static bi::tcp::endpoint resolveHost(ba::io_service& _ioService, std::string const& _host); }; - -struct NetworkPreferences -{ - // Default Network Preferences - NetworkPreferences(unsigned short lp = 30303): listenPort(lp) {} - - // Network Preferences with specific Listen IP - NetworkPreferences(std::string l, unsigned short lp = 30303, bool u = true): publicIPAddress(), listenIPAddress(l), listenPort(lp), traverseNAT(u) {} - - // Network Preferences with intended Public IP - NetworkPreferences(std::string publicIP, std::string l = std::string(), unsigned short lp = 30303, bool u = true): publicIPAddress(publicIP), listenIPAddress(l), listenPort(lp), traverseNAT(u) { if (!publicIPAddress.empty() && !isPublicAddress(publicIPAddress)) BOOST_THROW_EXCEPTION(InvalidPublicIPAddress()); } - - std::string publicIPAddress; - std::string listenIPAddress; - unsigned short listenPort = 30303; - bool traverseNAT = true; -}; } } diff --git a/neth/main.cpp b/neth/main.cpp index 4d9870b44..e7dde3cc6 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -544,7 +544,7 @@ int main(int argc, char** argv) StructuredLogger::get().initialize(structuredLogging, structuredLoggingFormat); VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); - NetworkPreferences netPrefs(publicIP, listenIP ,listenPort, upnp); + auto netPrefs = publicIP.empty() ? NetworkPreferences(listenIP ,listenPort, upnp) : NetworkPreferences(publicIP, listenIP ,listenPort, upnp); auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp"); std::string clientImplString = "NEthereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""); dev::WebThreeDirect web3( From 4e62b874e8884aa287e4988d625642a031c13d6e Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 28 Mar 2015 10:37:33 +0100 Subject: [PATCH 143/228] fix dialogue. hard-code localhost address so ipv6 address isn't resolved. --- alethzero/Connect.cpp | 5 +++-- alethzero/MainWin.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/alethzero/Connect.cpp b/alethzero/Connect.cpp index 076ebafe0..cf19f83b8 100644 --- a/alethzero/Connect.cpp +++ b/alethzero/Connect.cpp @@ -28,7 +28,7 @@ Connect::Connect(QWidget *parent) : QDialog(parent), ui(new Ui::Connect) { - reset(); + ui->setupUi(this); } Connect::~Connect() @@ -43,7 +43,8 @@ void Connect::setEnvironment(QStringList const& _nodes) void Connect::reset() { - ui->setupUi(this); + ui->nodeId->clear(); + ui->required->setChecked(false); } QString Connect::host() diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 4e5ed49a3..2b0750d46 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -142,7 +142,7 @@ Main::Main(QWidget *parent) : #endif #if ETH_DEBUG - m_servers.append("localhost:30300"); + m_servers.append("127.0.0.1:30300"); #endif m_servers.append(QString::fromStdString(Host::pocHost() + ":30303")); From 872faf3e50ee153f65fb33e4f5ddf7ac06fe9f6a Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Sat, 28 Mar 2015 18:16:48 +0100 Subject: [PATCH 144/228] fixed #1450 --- libtestutils/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libtestutils/CMakeLists.txt b/libtestutils/CMakeLists.txt index 202b9823c..4ae52e0c9 100644 --- a/libtestutils/CMakeLists.txt +++ b/libtestutils/CMakeLists.txt @@ -18,6 +18,11 @@ set(EXECUTABLE testutils) file(GLOB HEADERS "*.h") +if (NOT JSONRPC) + list(REMOVE_ITEM SRC_LIST "./FixedWebThreeServer.cpp") + list(REMOVE_ITEM HEADERS "./FixedWebThreeServer.h") +endif() + if (ETH_STATIC) add_library(${EXECUTABLE} STATIC ${SRC_LIST} ${HEADERS}) else() From fe7788cdee15049f41b01727e8b27d8cc20eb6e8 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 28 Mar 2015 19:29:56 +0100 Subject: [PATCH 145/228] Extra debug information for #1438. --- libethcore/Ethasher.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/libethcore/Ethasher.cpp b/libethcore/Ethasher.cpp index 1e6f0acef..79a2fdea3 100644 --- a/libethcore/Ethasher.cpp +++ b/libethcore/Ethasher.cpp @@ -119,9 +119,21 @@ bool Ethasher::verify(BlockInfo const& _header) boundary.data()); #if ETH_DEBUG - // should be equivalent to: auto result = eval(_header); - assert((result.mixHash == _header.mixHash && result.value <= boundary) == ret); + if ((result.value <= boundary && result.mixHash == _header.mixHash) != ret) + { + cwarn << "Assertion failure coming: evaluated result gives different outcome to ethash_quick_check_difficulty"; + cwarn << "headerHash:" << _header.headerHash(WithoutNonce); + cwarn << "nonce:" << _header.nonce; + cwarn << "mixHash:" << _header.mixHash; + cwarn << "difficulty:" << _header.difficulty; + cwarn << "boundary:" << boundary; + cwarn << "result.value:" << result.value; + cwarn << "result.mixHash:" << result.mixHash; + } + assert((result.value <= boundary) == ret); + if (result.value <= boundary) + assert(result.mixHash == _header.mixHash); #endif return ret; From 83a26ddc4b7415d971f96c0b4919352b13fa306c Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 28 Mar 2015 20:04:56 +0100 Subject: [PATCH 146/228] Remove the 1m gas reference. --- libethereum/Client.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethereum/Client.h b/libethereum/Client.h index 5fb30b8f0..1091bba58 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -110,7 +110,7 @@ public: private: u256 m_weiPerRef; u256 m_refsPerBlock; - u256 m_gasPerBlock = 1000000; + u256 m_gasPerBlock = 3141592; std::array m_octiles; }; From b3393ffcbadbbb985744721a9c8d3bde1ac7af0e Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 28 Mar 2015 20:40:03 +0100 Subject: [PATCH 147/228] style --- libp2p/Host.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 2354ed5d3..cc0d75fdb 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -400,7 +400,8 @@ void Host::addNode(NodeId const& _node, bi::address const& _addr, unsigned short _tcpPeerPort = 0; } - if (m_nodeTable) m_nodeTable->addNode(Node(_node, NodeIPEndpoint(bi::udp::endpoint(_addr, _udpNodePort), bi::tcp::endpoint(_addr, _tcpPeerPort)))); + if (m_nodeTable) + m_nodeTable->addNode(Node(_node, NodeIPEndpoint(bi::udp::endpoint(_addr, _udpNodePort), bi::tcp::endpoint(_addr, _tcpPeerPort)))); } void Host::requirePeer(NodeId const& _n, bi::address const& _udpAddr, unsigned short _udpPort, bi::address const& _tcpAddr, unsigned short _tcpPort) From 5173be3eb67b100ef1a320211b86133baa47d515 Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 28 Mar 2015 22:18:14 +0100 Subject: [PATCH 148/228] defer signature recovery until after gas price check --- libethereum/State.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 486abde12..af3a4a223 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -421,9 +421,9 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga { try { - Transaction t(i.second, CheckSignature::Sender); - if (t.gasPrice() >= _gp.ask(*this)) + if (Transaction(i.second, CheckSignature::Range).gasPrice() >= _gp.ask(*this)) { + Transaction t(i.second, CheckSignature::Sender); // don't have it yet! Execute it now. uncommitToMine(); // boost::timer t; From 45c3bea8f13fdbfa3a42c95d42f8d15983b4c3f6 Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 28 Mar 2015 23:54:05 +0100 Subject: [PATCH 149/228] prevent exception when invalid IP entered into listen-ip or public-ip fields in AZ --- alethzero/MainWin.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 2b0750d46..eb09d4991 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -252,11 +252,30 @@ void Main::addNewId(QString _ids) NetworkPreferences Main::netPrefs() const { + auto listenip = ui->listenIP->text().toStdString(); + try + { + listenip = bi::address::from_string(listenip).to_string(); + } + catch (...) + { + listenip = ""; + } + auto publicip = ui->forcePublicIP->text().toStdString(); + try + { + publicip = bi::address::from_string(publicip).to_string(); + } + catch (...) + { + publicip = ""; + } + if (isPublicAddress(publicip)) return NetworkPreferences(publicip, ui->listenIP->text().toStdString(), ui->port->value(), ui->upnp->isChecked()); else - return NetworkPreferences(ui->listenIP->text().toStdString(), ui->port->value(), ui->upnp->isChecked()); + return NetworkPreferences(listenip, ui->port->value(), ui->upnp->isChecked()); } void Main::onKeysChanged() From 6b05b8d9ffd2333fd7b7f326a63561816f94ce7e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 29 Mar 2015 19:13:39 +0200 Subject: [PATCH 150/228] Refactored much of transaction queue for tidiness and optimisation. --- alethzero/Debugger.cpp | 3 +- alethzero/MainWin.cpp | 2 +- eth/main.cpp | 7 ++- libethereum/BlockChain.cpp | 52 ++++++++++++++------- libethereum/BlockChain.h | 47 ++++++++++++------- libethereum/ClientBase.cpp | 4 +- libethereum/EthereumHost.cpp | 2 +- libethereum/Executive.cpp | 78 +++++++++++++++---------------- libethereum/Executive.h | 41 ++++++++++------ libethereum/State.cpp | 74 ++++++++++++----------------- libethereum/State.h | 8 +--- libethereum/TransactionQueue.cpp | 10 ++-- libethereum/TransactionQueue.h | 11 +++-- mix/MixClient.cpp | 5 +- test/blockchain.cpp | 21 ++++----- test/checkRandomStateTest.cpp | 3 +- test/createRandomStateTest.cpp | 3 +- test/solidityExecutionFramework.h | 3 +- test/state.cpp | 3 +- test/stateOriginal.cpp | 10 ++-- 20 files changed, 201 insertions(+), 186 deletions(-) diff --git a/alethzero/Debugger.cpp b/alethzero/Debugger.cpp index 93d6b1f6a..371630456 100644 --- a/alethzero/Debugger.cpp +++ b/alethzero/Debugger.cpp @@ -67,7 +67,8 @@ void Debugger::populate(dev::eth::Executive& _executive, dev::eth::Transaction c bool DebugSession::populate(dev::eth::Executive& _executive, dev::eth::Transaction const& _transaction) { try { - if (_executive.setup(_transaction)) + _executive.initialize(_transaction); + if (_executive.execute()) return false; } catch (...) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 36b6176f4..492e935f9 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1099,7 +1099,7 @@ void Main::refreshBlockChain() blocks.insert(bc.numberHash(b)); } else if (f.toLongLong() <= bc.number()) - blocks.insert(bc.numberHash(u256(f.toLongLong()))); + blocks.insert(bc.numberHash((unsigned)f.toLongLong())); else if (f.size() == 40) { Address h(f.toStdString()); diff --git a/eth/main.cpp b/eth/main.cpp index e0cf76193..355db7879 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -835,11 +835,8 @@ int main(int argc, char** argv) Executive e(state, c->blockChain(), 0); Transaction t = state.pending()[index]; state = state.fromPending(index); - bytes r = t.rlp(); try { - e.setup(&r); - OnOpFunc oof; if (format == "pretty") oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM) @@ -872,7 +869,9 @@ int main(int argc, char** argv) f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl; f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)vm->gas(), 1)) << endl; }; - e.go(oof); + e.initialize(t); + if (!e.execute()) + e.go(oof); e.finalize(); } catch(Exception const& _e) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index f80680f38..688a0c326 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -63,7 +63,7 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc) return _out; } -ldb::Slice dev::eth::toSlice(h256 _h, unsigned _sub) +ldb::Slice dev::eth::toSlice(h256 const& _h, unsigned _sub) { #if ALL_COMPILERS_ARE_CPP11_COMPLIANT static thread_local h256 h = _h ^ sha3(h256(u256(_sub))); @@ -184,6 +184,19 @@ inline string toString(h256s const& _bs) return out.str(); } +LastHashes BlockChain::lastHashes(unsigned _n) const +{ + Guard l(x_lastLastHashes); + if (m_lastLastHashesNumber != _n || m_lastLastHashes.empty()) + { + m_lastLastHashes.resize(256); + for (unsigned i = 0; i < 256; ++i) + m_lastLastHashes[i] = _n >= i ? numberHash(_n - i) : h256(); + m_lastLastHashesNumber = _n; + } + return m_lastLastHashes; +} + h256s BlockChain::sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max) { _bq.tick(*this); @@ -412,6 +425,9 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) WriteGuard l(x_lastBlockHash); m_lastBlockHash = newHash; } + + noteCanonChanged(); + m_extrasDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&newHash, 32)); clog(BlockChainNote) << " Imported and best" << td << ". Has" << (details(bi.parentHash).children.size() - 1) << "siblings. Route:" << toString(ret); StructuredLogger::chainNewHead( @@ -428,7 +444,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) return ret; } -h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, bool _post) const +h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common, bool _pre, bool _post) const { // cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged(); if (!_from || !_to) @@ -438,38 +454,40 @@ h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, boo unsigned fn = details(_from).number; unsigned tn = details(_to).number; // cdebug << "treeRoute" << fn << "..." << tn; + h256 from = _from; while (fn > tn) { if (_pre) - ret.push_back(_from); - _from = details(_from).parent; + ret.push_back(from); + from = details(from).parent; fn--; // cdebug << "from:" << fn << _from.abridged(); } + h256 to = _to; while (fn < tn) { if (_post) - back.push_back(_to); - _to = details(_to).parent; + back.push_back(to); + to = details(to).parent; tn--; // cdebug << "to:" << tn << _to.abridged(); } - while (_from != _to) + while (from != to) { - assert(_from); - assert(_to); - _from = details(_from).parent; - _to = details(_to).parent; + assert(from); + assert(to); + from = details(from).parent; + to = details(to).parent; if (_pre) - ret.push_back(_from); + ret.push_back(from); if (_post) - back.push_back(_to); + back.push_back(to); fn--; tn--; // cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged(); } if (o_common) - *o_common = _from; + *o_common = from; ret.reserve(ret.size() + back.size()); for (auto it = back.cbegin(); it != back.cend(); ++it) ret.push_back(*it); @@ -677,7 +695,7 @@ vector BlockChain::withBlockBloom(LogBloom const& _b, unsigned _earlie return ret; } -h256Set BlockChain::allUnclesFrom(h256 _parent) const +h256Set BlockChain::allUnclesFrom(h256 const& _parent) const { // Get all uncles cited given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + 5). h256Set ret; @@ -692,7 +710,7 @@ h256Set BlockChain::allUnclesFrom(h256 _parent) const return ret; } -bool BlockChain::isKnown(h256 _hash) const +bool BlockChain::isKnown(h256 const& _hash) const { if (_hash == m_genesisHash) return true; @@ -706,7 +724,7 @@ bool BlockChain::isKnown(h256 _hash) const return !!d.size(); } -bytes BlockChain::block(h256 _hash) const +bytes BlockChain::block(h256 const& _hash) const { if (_hash == m_genesisHash) return m_genesisBlock; diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 235a39267..3b8e9849c 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -30,9 +30,10 @@ #include #include #include +#include #include #include -#include +#include #include "BlockDetails.h" #include "Account.h" #include "Transaction.h" @@ -61,7 +62,7 @@ struct BlockChainNote: public LogChannel { static const char* name() { return "= // TODO: Move all this Genesis stuff into Genesis.h/.cpp std::map const& genesisState(); -ldb::Slice toSlice(h256 _h, unsigned _sub = 0); +ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0); using BlocksHash = std::map; using TransactionHashes = h256s; @@ -105,37 +106,42 @@ public: h256s import(bytes const& _block, OverlayDB const& _stateDB); /// Returns true if the given block is known (though not necessarily a part of the canon chain). - bool isKnown(h256 _hash) const; + bool isKnown(h256 const& _hash) const; /// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe. - BlockInfo info(h256 _hash) const { return BlockInfo(block(_hash)); } + BlockInfo info(h256 const& _hash) const { return BlockInfo(block(_hash)); } BlockInfo info() const { return BlockInfo(block()); } /// Get a block (RLP format) for the given hash (or the most recent mined if none given). Thread-safe. - bytes block(h256 _hash) const; + bytes block(h256 const& _hash) const; bytes block() const { return block(currentHash()); } /// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe. - BlockDetails details(h256 _hash) const { return queryExtras(_hash, m_details, x_details, NullBlockDetails); } + BlockDetails details(h256 const& _hash) const { return queryExtras(_hash, m_details, x_details, NullBlockDetails); } BlockDetails details() const { return details(currentHash()); } /// Get the transactions' log blooms of a block (or the most recent mined if none given). Thread-safe. - BlockLogBlooms logBlooms(h256 _hash) const { return queryExtras(_hash, m_logBlooms, x_logBlooms, NullBlockLogBlooms); } + BlockLogBlooms logBlooms(h256 const& _hash) const { return queryExtras(_hash, m_logBlooms, x_logBlooms, NullBlockLogBlooms); } BlockLogBlooms logBlooms() const { return logBlooms(currentHash()); } /// Get the transactions' receipts of a block (or the most recent mined if none given). Thread-safe. - BlockReceipts receipts(h256 _hash) const { return queryExtras(_hash, m_receipts, x_receipts, NullBlockReceipts); } + BlockReceipts receipts(h256 const& _hash) const { return queryExtras(_hash, m_receipts, x_receipts, NullBlockReceipts); } BlockReceipts receipts() const { return receipts(currentHash()); } /// Get a list of transaction hashes for a given block. Thread-safe. - TransactionHashes transactionHashes(h256 _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[1]) ret.push_back(sha3(t.data())); return ret; } + TransactionHashes transactionHashes(h256 const& _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[1]) ret.push_back(sha3(t.data())); return ret; } TransactionHashes transactionHashes() const { return transactionHashes(currentHash()); } /// Get a list of uncle hashes for a given block. Thread-safe. - UncleHashes uncleHashes(h256 _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[2]) ret.push_back(sha3(t.data())); return ret; } + UncleHashes uncleHashes(h256 const& _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[2]) ret.push_back(sha3(t.data())); return ret; } UncleHashes uncleHashes() const { return uncleHashes(currentHash()); } - h256 numberHash(u256 _index) const { if (!_index) return genesisHash(); return queryExtras(h256(_index), m_blockHashes, x_blockHashes, NullBlockHash).value; } + /// Get the hash for a given block's number. + h256 numberHash(unsigned _i) const { if (!_i) return genesisHash(); return queryExtras(h256(u256(_i)), m_blockHashes, x_blockHashes, NullBlockHash).value; } + + /// Get the last N hashes for a given block. (N is determined by the LastHashes type.) + LastHashes lastHashes() const { return lastHashes(number() - 1); } + LastHashes lastHashes(unsigned _i) const; /** Get the block blooms for a number of blocks. Thread-safe. * @returns the object pertaining to the blocks: @@ -158,15 +164,15 @@ public: std::vector withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest, unsigned _topLevel, unsigned _index) const; /// Get a transaction from its hash. Thread-safe. - bytes transaction(h256 _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytes(); return transaction(ta.blockHash, ta.index); } - std::pair transactionLocation(h256 _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return std::pair(h256(), 0); return std::make_pair(ta.blockHash, ta.index); } + bytes transaction(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytes(); return transaction(ta.blockHash, ta.index); } + std::pair transactionLocation(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return std::pair(h256(), 0); return std::make_pair(ta.blockHash, ta.index); } /// Get a block's transaction (RLP format) for the given block hash (or the most recent mined if none given) & index. Thread-safe. - bytes transaction(h256 _blockHash, unsigned _i) const { bytes b = block(_blockHash); return RLP(b)[1][_i].data().toBytes(); } + bytes transaction(h256 const& _blockHash, unsigned _i) const { bytes b = block(_blockHash); return RLP(b)[1][_i].data().toBytes(); } bytes transaction(unsigned _i) const { return transaction(currentHash(), _i); } /// Get a number for the given hash (or the most recent mined if none given). Thread-safe. - unsigned number(h256 _hash) const { return details(_hash).number; } + unsigned number(h256 const& _hash) const { return details(_hash).number; } unsigned number() const { return number(currentHash()); } /// Get a given block (RLP format). Thread-safe. @@ -178,7 +184,7 @@ public: /// Get all blocks not allowed as uncles given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + 5). /// @returns set including the header-hash of every parent (including @a _parent) up to and including generation +5 /// togther with all their quoted uncles. - h256Set allUnclesFrom(h256 _parent) const; + h256Set allUnclesFrom(h256 const& _parent) const; /** @returns the hash of all blocks between @a _from and @a _to, all blocks are ordered first by a number of * blocks that are parent-to-child, then two sibling blocks, then a number of blocks that are child-to-parent. @@ -194,7 +200,7 @@ public: * treeRoute(1b, 2a) == { 1b, 1a, 2a }; // *o_common == g * @endcode */ - h256s treeRoute(h256 _from, h256 _to, h256* o_common = nullptr, bool _pre = true, bool _post = true) const; + h256s treeRoute(h256 const& _from, h256 const& _to, h256* o_common = nullptr, bool _pre = true, bool _post = true) const; struct Statistics { @@ -219,7 +225,7 @@ private: void open(std::string _path, bool _killExisting = false); void close(); - template T queryExtras(h256 _h, std::map& _m, boost::shared_mutex& _x, T const& _n) const + template T queryExtras(h256 const& _h, std::map& _m, boost::shared_mutex& _x, T const& _n) const { { ReadGuard l(_x); @@ -268,6 +274,11 @@ private: void noteUsed(h256 const& _h, unsigned _extra = (unsigned)-1) const; std::chrono::system_clock::time_point m_lastCollection; + void noteCanonChanged() const { Guard l(x_lastLastHashes); m_lastLastHashes.clear(); } + mutable Mutex x_lastLastHashes; + mutable LastHashes m_lastLastHashes; + mutable unsigned m_lastLastHashesNumber = (unsigned)-1; + void updateStats() const; mutable Statistics m_lastStats; diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index df30595b6..b45b9cf27 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -73,7 +73,7 @@ ExecutionResult ClientBase::call(Secret _secret, u256 _value, Address _dest, byt State temp = asOf(_blockNumber); u256 n = temp.transactionsFrom(toAddress(_secret)); Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); - ret = temp.execute(bc(), t.rlp(), Permanence::Reverted); + ret = temp.execute(bc().lastHashes(), t, Permanence::Reverted); } catch (...) { @@ -92,7 +92,7 @@ ExecutionResult ClientBase::create(Secret _secret, u256 _value, bytes const& _da // cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); Transaction t(_value, _gasPrice, _gas, _data, n, _secret); - ret = temp.execute(bc(), t.rlp(), Permanence::Reverted); + ret = temp.execute(bc().lastHashes(), t, Permanence::Reverted); } catch (...) { diff --git a/libethereum/EthereumHost.cpp b/libethereum/EthereumHost.cpp index 7dfc51b47..5bc42540f 100644 --- a/libethereum/EthereumHost.cpp +++ b/libethereum/EthereumHost.cpp @@ -178,7 +178,7 @@ void EthereumHost::maintainTransactions() for (auto const& i: m_tq.transactions()) if (ep->m_requireTransactions || (!m_transactionsSent.count(i.first) && !ep->m_knownTransactions.count(i.first))) { - b += i.second; + b += i.second.rlp(); ++n; m_transactionsSent.insert(i.first); } diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index c574fa650..dfd526bef 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -35,7 +35,7 @@ using namespace dev::eth; Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level): m_s(_s), - m_lastHashes(_s.getLastHashes(_bc, (unsigned)_s.info().number - 1)), + m_lastHashes(_bc.lastHashes((unsigned)_s.info().number - 1)), m_depth(_level) {} @@ -55,12 +55,33 @@ void Executive::accrueSubState(SubState& _parentContext) _parentContext += m_ext->sub; } -bool Executive::setup(bytesConstRef _rlp) +void Executive::initialize(Transaction const& _transaction) { - // Entry point for a user-executed transaction. + m_t = _transaction; + + // Avoid transactions that would take us beyond the block gas limit. + u256 startGasUsed = m_s.gasUsed(); + if (startGasUsed + (bigint)m_t.gas() > m_s.m_currentBlock.gasLimit) + { + clog(StateDetail) << "Too much gas used in this block: Require <" << (m_s.m_currentBlock.gasLimit - startGasUsed) << " Got" << m_t.gas(); + m_excepted = TransactionException::BlockGasLimitReached; + BOOST_THROW_EXCEPTION(BlockGasLimitReached() << RequirementError((bigint)(m_s.m_currentBlock.gasLimit - startGasUsed), (bigint)m_t.gas())); + } + + // Check gas cost is enough. + m_gasRequired = Interface::txGas(m_t.data()); + if (m_t.gas() < m_gasRequired) + { + clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << m_gasRequired << " Got" << m_t.gas(); + m_excepted = TransactionException::OutOfGas; + BOOST_THROW_EXCEPTION(OutOfGas() << RequirementError((bigint)m_gasRequired, (bigint)m_t.gas())); + } + + // Avoid invalid transactions. + u256 nonceReq; try { - m_t = Transaction(_rlp, CheckSignature::Sender); + nonceReq = m_s.transactionsFrom(m_t.sender()); } catch (...) { @@ -68,15 +89,6 @@ bool Executive::setup(bytesConstRef _rlp) m_excepted = TransactionException::InvalidSignature; throw; } - return setup(); -} - -bool Executive::setup() -{ - // Entry point for a user-executed transaction. - - // Avoid invalid transactions. - auto nonceReq = m_s.transactionsFrom(m_t.sender()); if (m_t.nonce() != nonceReq) { clog(StateDetail) << "Invalid Nonce: Require" << nonceReq << " Got" << m_t.nonce(); @@ -84,46 +96,32 @@ bool Executive::setup() BOOST_THROW_EXCEPTION(InvalidNonce() << RequirementError((bigint)nonceReq, (bigint)m_t.nonce())); } - // Check gas cost is enough. - auto gasRequired = Interface::txGas(m_t.data()); - - if (m_t.gas() < gasRequired) - { - clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << gasRequired << " Got" << m_t.gas(); - m_excepted = TransactionException::OutOfGas; - BOOST_THROW_EXCEPTION(OutOfGas() << RequirementError((bigint)gasRequired, (bigint)m_t.gas())); - } - - bigint gasCost = (bigint)m_t.gas() * m_t.gasPrice(); - bigint totalCost = m_t.value() + gasCost; - // Avoid unaffordable transactions. - if (m_s.balance(m_t.sender()) < totalCost) + m_gasCost = (bigint)m_t.gas() * m_t.gasPrice(); + m_totalCost = m_t.value() + m_gasCost; + if (m_s.balance(m_t.sender()) < m_totalCost) { - clog(StateDetail) << "Not enough cash: Require >" << totalCost << " Got" << m_s.balance(m_t.sender()); + clog(StateDetail) << "Not enough cash: Require >" << m_totalCost << " Got" << m_s.balance(m_t.sender()); m_excepted = TransactionException::NotEnoughCash; - BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(totalCost, (bigint)m_s.balance(m_t.sender()))); + BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(m_totalCost, (bigint)m_s.balance(m_t.sender()))); } +} - u256 startGasUsed = m_s.gasUsed(); - if (startGasUsed + (bigint)m_t.gas() > m_s.m_currentBlock.gasLimit) - { - clog(StateDetail) << "Too much gas used in this block: Require <" << (m_s.m_currentBlock.gasLimit - startGasUsed) << " Got" << m_t.gas(); - m_excepted = TransactionException::BlockGasLimitReached; - BOOST_THROW_EXCEPTION(BlockGasLimitReached() << RequirementError((bigint)(m_s.m_currentBlock.gasLimit - startGasUsed), (bigint)m_t.gas())); - } +bool Executive::execute() +{ + // Entry point for a user-executed transaction. // Increment associated nonce for sender. m_s.noteSending(m_t.sender()); // Pay... - clog(StateDetail) << "Paying" << formatBalance(u256(gasCost)) << "from sender for gas (" << m_t.gas() << "gas at" << formatBalance(m_t.gasPrice()) << ")"; - m_s.subBalance(m_t.sender(), gasCost); + clog(StateDetail) << "Paying" << formatBalance(u256(m_gasCost)) << "from sender for gas (" << m_t.gas() << "gas at" << formatBalance(m_t.gasPrice()) << ")"; + m_s.subBalance(m_t.sender(), m_gasCost); if (m_t.isCreation()) - return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)gasRequired, &m_t.data(), m_t.sender()); + return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)m_gasRequired, &m_t.data(), m_t.sender()); else - return call(m_t.receiveAddress(), m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)gasRequired, m_t.sender()); + return call(m_t.receiveAddress(), m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)m_gasRequired, m_t.sender()); } bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress) diff --git a/libethereum/Executive.h b/libethereum/Executive.h index eb0c27ad2..a70c8164a 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -41,10 +41,21 @@ struct VMTraceChannel: public LogChannel { static const char* name() { return "E * @brief Message-call/contract-creation executor; useful for executing transactions. * * Two ways of using this class - either as a transaction executive or a CALL/CREATE executive. - * In the first use, after construction, begin with setup() and end with finalize(). Call go() - * after setup() only if it returns false. + * + * In the first use, after construction, begin with initialize(), then execute() and end with finalize(). Call go() + * after execute() only if it returns false. + * * In the second use, after construction, begin with call() or create() and end with * accrueSubState(). Call go() after call()/create() only if it returns false. + * + * Example: + * @code + * Executive e(state, blockchain, 0); + * e.initialize(transaction); + * if (!e.execute()) + * e.go(); + * e.finalize(); + * @endcode */ class Executive { @@ -59,17 +70,17 @@ public: Executive(Executive const&) = delete; void operator=(Executive) = delete; - /// Set up the executive for evaluating a transaction. You must call finalize() following this. - /// @returns true iff go() must be called (and thus a VM execution in required). - bool setup(bytesConstRef _transaction); - /// Set up the executive for evaluating a transaction. You must call finalize() following this. - /// @returns true iff go() must be called (and thus a VM execution in required). - bool setup(Transaction const& _transaction) { m_t = _transaction; return setup(); } - /// Finalise a transaction previously set up with setup(). - /// @warning Only valid after setup(), and possibly go(). + /// Initializes the executive for evaluating a transaction. You must call finalize() at some point following this. + void initialize(bytesConstRef _transaction) { initialize(Transaction(_transaction, CheckSignature::None)); } + void initialize(Transaction const& _transaction); + /// Finalise a transaction previously set up with initialize(). + /// @warning Only valid after initialize() and execute(), and possibly go(). void finalize(); - /// @returns the transaction from setup(). - /// @warning Only valid after setup(). + /// Begins execution of a transaction. You must call finalize() following this. + /// @returns true if the transaction is done, false if go() must be called. + bool execute(); + /// @returns the transaction from initialize(). + /// @warning Only valid after initialize(). Transaction const& t() const { return m_t; } /// @returns the log entries created by this operation. /// @warning Only valid after finalise(). @@ -107,7 +118,7 @@ public: ExecutionResult executionResult() const; private: - bool setup(); + bool prime(); State& m_s; ///< The state to which this operation/transaction is applied. LastHashes m_lastHashes; @@ -125,6 +136,10 @@ private: Transaction m_t; ///< The original transaction. Set by setup(). LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize(). + + bigint m_gasRequired; + bigint m_gasCost; + bigint m_totalCost; }; } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index af3a4a223..9817e2bad 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -388,8 +388,7 @@ bool State::cull(TransactionQueue& _tq) const { try { - Transaction t(i.second, CheckSignature::Sender); - if (t.nonce() <= transactionsFrom(t.sender())) + if (i.second.nonce() <= transactionsFrom(i.second.sender())) { _tq.drop(i.first); ret = true; @@ -411,7 +410,7 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga TransactionReceipts ret; auto ts = _tq.transactions(); - auto lh = getLastHashes(_bc, _bc.number()); + LastHashes lh; for (int goodTxs = 1; goodTxs;) { @@ -421,12 +420,11 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga { try { - if (Transaction(i.second, CheckSignature::Range).gasPrice() >= _gp.ask(*this)) + if (i.second.gasPrice() >= _gp.ask(*this)) { - Transaction t(i.second, CheckSignature::Sender); - // don't have it yet! Execute it now. - uncommitToMine(); // boost::timer t; + if (lh.empty()) + lh = _bc.lastHashes(); execute(lh, i.second); ret.push_back(m_receipts.back()); _tq.noteGood(i); @@ -434,6 +432,7 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga // cnote << "TX took:" << t.elapsed() * 1000; } } +#if ETH_DEBUG catch (InvalidNonce const& in) { bigint const* req = boost::get_error_info(in); @@ -449,13 +448,19 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga else _tq.setFuture(i); } + catch (BlockGasLimitReached const& e) + { + _tq.setFuture(i); + } +#endif catch (Exception const& _e) { // Something else went wrong - drop it. _tq.drop(i.first); if (o_transactionQueueChanged) *o_transactionQueueChanged = true; - cwarn << "Sync went wrong\n" << diagnostic_information(_e); + cnote << "Dropping invalid transaction:"; + cnote << diagnostic_information(_e); } catch (std::exception const&) { @@ -463,6 +468,7 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, Ga _tq.drop(i.first); if (o_transactionQueueChanged) *o_transactionQueueChanged = true; + cnote << "Transaction caused low-level exception :("; } } } @@ -498,7 +504,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce) GenericTrieDB receiptsTrie(&rm); receiptsTrie.init(); - LastHashes lh = getLastHashes(_bc, (unsigned)m_previousBlock.number); + LastHashes lh = _bc.lastHashes((unsigned)m_previousBlock.number); RLP rlp(_block); // All ok with the block generally. Play back the transactions now... @@ -509,7 +515,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce) k << i; transactionsTrie.insert(&k.out(), tr.data()); - execute(lh, tr.data()); + execute(lh, Transaction(tr.data(), CheckSignature::Sender)); RLPStream receiptrlp; m_receipts.back().streamRLP(receiptrlp); @@ -1040,56 +1046,34 @@ bool State::isTrieGood(bool _enforceRefs, bool _requireNoLeftOvers) const return true; } -LastHashes State::getLastHashes(BlockChain const& _bc, unsigned _n) const -{ - LastHashes ret; - ret.resize(256); - if (eth::c_protocolVersion > 49) - { - ret[0] = _bc.numberHash(_n); - for (unsigned i = 1; i < 256; ++i) - ret[i] = ret[i - 1] ? _bc.details(ret[i - 1]).parent : h256(); - } - return ret; -} - -ExecutionResult State::execute(BlockChain const& _bc, bytes const& _rlp, Permanence _p) -{ - return execute(getLastHashes(_bc, _bc.number()), &_rlp, _p); -} - -ExecutionResult State::execute(BlockChain const& _bc, bytesConstRef _rlp, Permanence _p) +ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Permanence _p) { - return execute(getLastHashes(_bc, _bc.number()), _rlp, _p); -} - -// TODO: maintain node overlay revisions for stateroots -> each commit gives a stateroot + OverlayDB; allow overlay copying for rewind operations. -ExecutionResult State::execute(LastHashes const& _lh, bytesConstRef _rlp, Permanence _p) -{ -#ifndef ETH_RELEASE - commit(); // get an updated hash -#endif - +#if ETH_PARANOIA paranoia("start of execution.", true); - State old(*this); -#if ETH_PARANOIA auto h = rootHash(); #endif + // Create and initialize the executive. This will throw fairly cheaply and quickly if the + // transaction is bad in any way. Executive e(*this, _lh, 0); - e.setup(_rlp); + e.initialize(_t); - u256 startGasUsed = gasUsed(); + // Uncommitting is a non-trivial operation - only do it once we've verified as much of the + // transaction as possible. + uncommitToMine(); + // OK - transaction looks valid - execute. + u256 startGasUsed = gasUsed(); #if ETH_PARANOIA ctrace << "Executing" << e.t() << "on" << h; ctrace << toHex(e.t().rlp()); #endif + if (!e.execute()) #if ETH_VMTRACE - e.go(e.simpleTrace()); + e.go(e.simpleTrace()); #else - e.go(); + e.go(); #endif e.finalize(); diff --git a/libethereum/State.h b/libethereum/State.h index ee88f443e..5ed76cc27 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -188,15 +188,9 @@ public: /// Like sync but only operate on _tq, killing the invalid/old ones. bool cull(TransactionQueue& _tq) const; - /// Returns the last few block hashes of the current chain. - LastHashes getLastHashes(BlockChain const& _bc, unsigned _n) const; - /// Execute a given transaction. /// This will append @a _t to the transaction list and change the state accordingly. - ExecutionResult execute(BlockChain const& _bc, bytes const& _rlp, Permanence _p = Permanence::Committed); - ExecutionResult execute(BlockChain const& _bc, bytesConstRef _rlp, Permanence _p = Permanence::Committed); - ExecutionResult execute(LastHashes const& _lh, bytes const& _rlp, Permanence _p = Permanence::Committed) { return execute(_lh, &_rlp, _p); } - ExecutionResult execute(LastHashes const& _lh, bytesConstRef _rlp, Permanence _p = Permanence::Committed); + ExecutionResult execute(LastHashes const& _lh, Transaction const& _t, Permanence _p = Permanence::Committed); /// Get the remaining gas limit in this block. u256 gasLimitRemaining() const { return m_currentBlock.gasLimit - gasUsed(); } diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 5701fc4a5..803d320ee 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -46,7 +46,7 @@ bool TransactionQueue::import(bytesConstRef _transactionRLP) UpgradeGuard ul(l); // If valid, append to blocks. - m_current[h] = _transactionRLP.toBytes(); + m_current[h] = t; m_known.insert(h); } catch (Exception const& _e) @@ -63,20 +63,20 @@ bool TransactionQueue::import(bytesConstRef _transactionRLP) return true; } -void TransactionQueue::setFuture(std::pair const& _t) +void TransactionQueue::setFuture(std::pair const& _t) { WriteGuard l(m_lock); if (m_current.count(_t.first)) { m_current.erase(_t.first); - m_unknown.insert(make_pair(Transaction(_t.second, CheckSignature::Sender).sender(), _t)); + m_unknown.insert(make_pair(_t.second.sender(), _t)); } } -void TransactionQueue::noteGood(std::pair const& _t) +void TransactionQueue::noteGood(std::pair const& _t) { WriteGuard l(m_lock); - auto r = m_unknown.equal_range(Transaction(_t.second, CheckSignature::Sender).sender()); + auto r = m_unknown.equal_range(_t.second.sender()); for (auto it = r.first; it != r.second; ++it) m_current.insert(it->second); m_unknown.erase(r.first, r.second); diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index b58944e4f..b104b98ca 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -25,6 +25,7 @@ #include #include "libethcore/Common.h" #include +#include "Transaction.h" namespace dev { @@ -46,19 +47,19 @@ public: void drop(h256 _txHash); - std::map transactions() const { ReadGuard l(m_lock); return m_current; } + std::map transactions() const { ReadGuard l(m_lock); return m_current; } std::pair items() const { ReadGuard l(m_lock); return std::make_pair(m_current.size(), m_unknown.size()); } - void setFuture(std::pair const& _t); - void noteGood(std::pair const& _t); + void setFuture(std::pair const& _t); + void noteGood(std::pair const& _t); void clear() { WriteGuard l(m_lock); m_known.clear(); m_current.clear(); m_unknown.clear(); } private: mutable boost::shared_mutex m_lock; ///< General lock. std::set m_known; ///< Hashes of transactions in both sets. - std::map m_current; ///< Map of SHA3(tx) to tx. - std::multimap> m_unknown; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX. + std::map m_current; ///< Map of SHA3(tx) to tx. + std::multimap> m_unknown; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX. }; } diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index 19d946d01..7729c0ffe 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -112,7 +112,8 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c State execState = _state; Executive execution(execState, lastHashes, 0); - execution.setup(&rlp); + execution.initialize(&rlp); + execution.execute(); std::vector machineStates; std::vector levels; std::vector codes; @@ -184,7 +185,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c // execute on a state if (!_call) { - _state.execute(lastHashes, rlp); + _state.execute(lastHashes, _t); if (_t.isCreation() && _state.code(d.contractAddress).empty()) BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment")); // collect watches diff --git a/test/blockchain.cpp b/test/blockchain.cpp index 50ca22c54..988859fe5 100644 --- a/test/blockchain.cpp +++ b/test/blockchain.cpp @@ -182,18 +182,17 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) Transactions txList; for (auto const& txi: txs.transactions()) { - Transaction tx(txi.second, CheckSignature::Sender); - txList.push_back(tx); + txList.push_back(txi.second); mObject txObject; - txObject["nonce"] = toString(tx.nonce()); - txObject["data"] = "0x" + toHex(tx.data()); - txObject["gasLimit"] = toString(tx.gas()); - txObject["gasPrice"] = toString(tx.gasPrice()); - txObject["r"] = "0x" + toString(tx.signature().r); - txObject["s"] = "0x" + toString(tx.signature().s); - txObject["v"] = to_string(tx.signature().v + 27); - txObject["to"] = tx.isCreation() ? "" : toString(tx.receiveAddress()); - txObject["value"] = toString(tx.value()); + txObject["nonce"] = toString(txi.second.nonce()); + txObject["data"] = "0x" + toHex(txi.second.data()); + txObject["gasLimit"] = toString(txi.second.gas()); + txObject["gasPrice"] = toString(txi.second.gasPrice()); + txObject["r"] = "0x" + toString(txi.second.signature().r); + txObject["s"] = "0x" + toString(txi.second.signature().s); + txObject["v"] = to_string(txi.second.signature().v + 27); + txObject["to"] = txi.second.isCreation() ? "" : toString(txi.second.receiveAddress()); + txObject["value"] = toString(txi.second.value()); txArray.push_back(txObject); } diff --git a/test/checkRandomStateTest.cpp b/test/checkRandomStateTest.cpp index a4d390b16..49aca852f 100644 --- a/test/checkRandomStateTest.cpp +++ b/test/checkRandomStateTest.cpp @@ -83,12 +83,11 @@ bool doStateTest(mValue& _v) ImportTest importer(o, false); eth::State theState = importer.m_statePre; - bytes tx = importer.m_transaction.rlp(); bytes output; try { - output = theState.execute(lastHashes(importer.m_environment.currentBlock.number), tx).output; + output = theState.execute(lastHashes(importer.m_environment.currentBlock.number), importer.m_transaction).output; } catch (Exception const& _e) { diff --git a/test/createRandomStateTest.cpp b/test/createRandomStateTest.cpp index f422d1717..5758598b9 100644 --- a/test/createRandomStateTest.cpp +++ b/test/createRandomStateTest.cpp @@ -183,12 +183,11 @@ void doStateTests(json_spirit::mValue& _v) test::ImportTest importer(o, true); eth::State theState = importer.m_statePre; - bytes tx = importer.m_transaction.rlp(); bytes output; try { - output = theState.execute(test::lastHashes(importer.m_environment.currentBlock.number), tx).output; + output = theState.execute(test::lastHashes(importer.m_environment.currentBlock.number), importer.m_transaction).output; } catch (Exception const& _e) { diff --git a/test/solidityExecutionFramework.h b/test/solidityExecutionFramework.h index 86062a90b..2451aa381 100644 --- a/test/solidityExecutionFramework.h +++ b/test/solidityExecutionFramework.h @@ -142,7 +142,8 @@ protected: try { // this will throw since the transaction is invalid, but it should nevertheless store the transaction - executive.setup(&transactionRLP); + executive.initialize(&transactionRLP); + executive.execute(); } catch (...) {} if (_isCreation) diff --git a/test/state.cpp b/test/state.cpp index 4ab59f7a1..7c586ec7d 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -57,13 +57,12 @@ void doStateTests(json_spirit::mValue& v, bool _fillin) ImportTest importer(o, _fillin); State theState = importer.m_statePre; - bytes tx = importer.m_transaction.rlp(); bytes output; try { Listener::ExecTimeGuard guard{i.first}; - output = theState.execute(lastHashes(importer.m_environment.currentBlock.number), tx).output; + output = theState.execute(lastHashes(importer.m_environment.currentBlock.number), importer.m_transaction).output; } catch (Exception const& _e) { diff --git a/test/stateOriginal.cpp b/test/stateOriginal.cpp index 5b7b0415e..384d85344 100644 --- a/test/stateOriginal.cpp +++ b/test/stateOriginal.cpp @@ -79,13 +79,9 @@ BOOST_AUTO_TEST_CASE(Complex) cout << s; // Inject a transaction to transfer funds from miner to me. - bytes tx; - { - Transaction t(1000, 10000, 10000, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret()); - assert(t.sender() == myMiner.address()); - tx = t.rlp(); - } - s.execute(bc, tx); + Transaction t(1000, 10000, 10000, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret()); + assert(t.sender() == myMiner.address()); + s.execute(bc.lastHashes(), t); cout << s; From 1523b181690dbae4121ef3dde76449406dda3a92 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 29 Mar 2015 19:25:59 +0200 Subject: [PATCH 151/228] Minor off-by-one fix. --- libethereum/BlockChain.h | 2 +- libethereum/Executive.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 3b8e9849c..03c0fdcfd 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -140,7 +140,7 @@ public: h256 numberHash(unsigned _i) const { if (!_i) return genesisHash(); return queryExtras(h256(u256(_i)), m_blockHashes, x_blockHashes, NullBlockHash).value; } /// Get the last N hashes for a given block. (N is determined by the LastHashes type.) - LastHashes lastHashes() const { return lastHashes(number() - 1); } + LastHashes lastHashes() const { return lastHashes(number()); } LastHashes lastHashes(unsigned _i) const; /** Get the block blooms for a number of blocks. Thread-safe. diff --git a/libethereum/Executive.h b/libethereum/Executive.h index a70c8164a..3445ad407 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -118,8 +118,6 @@ public: ExecutionResult executionResult() const; private: - bool prime(); - State& m_s; ///< The state to which this operation/transaction is applied. LastHashes m_lastHashes; std::shared_ptr m_ext; ///< The VM externality object for the VM execution or null if no VM is required. From 27d1436cfddbb7a17997f086df0d31ba5eed3e45 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 29 Mar 2015 20:20:10 +0200 Subject: [PATCH 152/228] Fixes to eth's Interface. --- alethzero/Transact.cpp | 4 ++-- libethereum/Interface.h | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index 534f18a69..6f52548c7 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -316,7 +316,7 @@ void Transact::rejigData() return; } else - gasNeeded = min((qint64)ethereum()->gasLimitRemaining(), (qint64)((b - value()) / gasPrice())); + gasNeeded = (qint64)min(ethereum()->gasLimitRemaining(), ((b - value()) / gasPrice())); // Dry-run execution to determine gas requirement and any execution errors Address to; @@ -326,7 +326,7 @@ void Transact::rejigData() else { to = m_context->fromString(ui->destination->currentText()); - er = ethereum()->call(s, value(), to, m_data, ethereum()->gasLimitRemaining(), gasPrice()); + er = ethereum()->call(s, value(), to, m_data, gasNeeded, gasPrice()); } gasNeeded = (qint64)(er.gasUsed + er.gasRefunded); htmlInfo = QString("

INFO Gas required: %1 total = %2 base, %3 exec [%4 refunded later]
").arg(gasNeeded).arg(baseGas).arg(gasNeeded - baseGas).arg((qint64)er.gasRefunded) + htmlInfo; diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 47fdb7250..529ddc093 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -71,11 +71,13 @@ public: virtual void flushTransactions() = 0; /// Makes the given call. Nothing is recorded into the state. - virtual ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = 0) = 0; + virtual ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) = 0; + ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) { return call(_secret, _value, _dest, _data, _gas, _gasPrice, m_default); } /// Does the given creation. Nothing is recorded into the state. /// @returns the pair of the Address of the created contract together with its code. - virtual ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = 0) = 0; + virtual ExecutionResult create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) = 0; + ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) { return create(_secret, _value, _data, _gas, _gasPrice, m_default); } // [STATE-QUERY API] From 585962347b85ecfda5e653c75974285aeab4abe2 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 29 Mar 2015 20:53:42 +0200 Subject: [PATCH 153/228] Minor presentation fix. --- alethzero/MainWin.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 492e935f9..40af6f731 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1362,7 +1362,7 @@ void Main::on_transactionQueue_currentItemChanged() if (!!receipt.bloom()) s << "
Log Bloom: " << receipt.bloom() << "
"; else - s << "
Log Bloom: Uneventful
"; + s << "
Log Bloom: Uneventful
"; auto r = receipt.rlp(); s << "
Receipt: " << toString(RLP(r)) << "
"; s << "
Receipt-Hex: " Span(Mono) << toHex(receipt.rlp()) << "
"; @@ -1461,7 +1461,7 @@ void Main::on_blocks_currentItemChanged() if (!!info.logBloom) s << "
Log Bloom: " << info.logBloom << "
"; else - s << "
Log Bloom: Uneventful
"; + s << "
Log Bloom: Uneventful
"; s << "
Transactions: " << block[1].itemCount() << " @" << info.transactionsRoot << "" << "
"; s << "
Uncles: " << block[2].itemCount() << " @" << info.sha3Uncles << "" << "
"; for (auto u: block[2]) From 25333cace3dacb8cc070091864c2b60d58033ebe Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 29 Mar 2015 21:27:29 +0200 Subject: [PATCH 154/228] Clean up variable names. --- libp2p/Network.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/libp2p/Network.cpp b/libp2p/Network.cpp index bdd1f0108..d2de7a484 100644 --- a/libp2p/Network.cpp +++ b/libp2p/Network.cpp @@ -145,7 +145,7 @@ int Network::tcp4Listen(bi::tcp::acceptor& _acceptor, unsigned short _listenPort return retport; } -bi::tcp::endpoint Network::traverseNAT(std::vector const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr) +bi::tcp::endpoint Network::traverseNAT(std::vector const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpInterfaceAddr) { asserts(_listenPort != 0); @@ -157,26 +157,26 @@ bi::tcp::endpoint Network::traverseNAT(std::vector const& _ifAddres // let m_upnp continue as null - we handle it properly. catch (...) {} - bi::tcp::endpoint upnpep; + bi::tcp::endpoint upnpEP; if (upnp && upnp->isValid()) { - bi::address paddr; + bi::address pAddr; int extPort = 0; for (auto const& addr: _ifAddresses) if (addr.is_v4() && isPrivateAddress(addr) && (extPort = upnp->addRedirect(addr.to_string().c_str(), _listenPort))) { - paddr = addr; + pAddr = addr; break; } - auto eip = upnp->externalIP(); - bi::address eipaddr(bi::address::from_string(eip)); - if (extPort && eip != string("0.0.0.0") && !isPrivateAddress(eipaddr)) + auto eIP = upnp->externalIP(); + bi::address eIPAddr(bi::address::from_string(eIP)); + if (extPort && eIP != string("0.0.0.0") && !isPrivateAddress(eIPAddr)) { clog(NetNote) << "Punched through NAT and mapped local port" << _listenPort << "onto external port" << extPort << "."; - clog(NetNote) << "External addr:" << eip; - o_upnpifaddr = paddr; - upnpep = bi::tcp::endpoint(eipaddr, (unsigned short)extPort); + clog(NetNote) << "External addr:" << eIP; + o_upnpInterfaceAddr = pAddr; + upnpEP = bi::tcp::endpoint(eIPAddr, (unsigned short)extPort); } else clog(NetWarn) << "Couldn't punch through NAT (or no NAT in place)."; @@ -185,5 +185,5 @@ bi::tcp::endpoint Network::traverseNAT(std::vector const& _ifAddres delete upnp; } - return upnpep; + return upnpEP; } From 521bf5b384f81c74c3166856d8afc833eafe002f Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 29 Mar 2015 22:10:34 +0200 Subject: [PATCH 155/228] style --- alethzero/Connect.cpp | 30 +++++++++++++++--------------- alethzero/Connect.h | 41 +++++++++++++++++++++-------------------- alethzero/MainWin.cpp | 29 ++++++++++++++--------------- libp2p/NodeTable.cpp | 4 ++-- 4 files changed, 52 insertions(+), 52 deletions(-) diff --git a/alethzero/Connect.cpp b/alethzero/Connect.cpp index cf19f83b8..32fa74f38 100644 --- a/alethzero/Connect.cpp +++ b/alethzero/Connect.cpp @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** @file Connect.cpp * @author Alex Leverington @@ -25,15 +25,15 @@ #include "ui_Connect.h" Connect::Connect(QWidget *parent) : - QDialog(parent), - ui(new Ui::Connect) + QDialog(parent), + ui(new Ui::Connect) { - ui->setupUi(this); + ui->setupUi(this); } Connect::~Connect() { - delete ui; + delete ui; } void Connect::setEnvironment(QStringList const& _nodes) diff --git a/alethzero/Connect.h b/alethzero/Connect.h index d8059acd4..89b25617c 100644 --- a/alethzero/Connect.h +++ b/alethzero/Connect.h @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** @file Connect.h * @author Alex Leverington @@ -29,22 +29,23 @@ namespace dev { namespace p2p { class Host; } } class Connect : public QDialog { - Q_OBJECT + Q_OBJECT public: - explicit Connect(QWidget *parent = 0); - ~Connect(); + explicit Connect(QWidget* _parent = 0); + ~Connect(); - void setEnvironment(QStringList const& _nodes); - + void setEnvironment(QStringList const& _nodes); + + /// clear dialogue inputs void reset(); + + // Form field values: QString host(); - QString nodeId(); - bool required(); - + private: - Ui::Connect *ui; + Ui::Connect* ui; }; diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index eb09d4991..712318442 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -252,30 +252,30 @@ void Main::addNewId(QString _ids) NetworkPreferences Main::netPrefs() const { - auto listenip = ui->listenIP->text().toStdString(); + auto listenIP = ui->listenIP->text().toStdString(); try { - listenip = bi::address::from_string(listenip).to_string(); + listenIP = bi::address::from_string(listenIP).to_string(); } catch (...) { - listenip = ""; + listenIP = ""; } - auto publicip = ui->forcePublicIP->text().toStdString(); + auto publicIP = ui->forcePublicIP->text().toStdString(); try { - publicip = bi::address::from_string(publicip).to_string(); + publicIP = bi::address::from_string(publicIP).to_string(); } catch (...) { - publicip = ""; + publicIP = ""; } - if (isPublicAddress(publicip)) - return NetworkPreferences(publicip, ui->listenIP->text().toStdString(), ui->port->value(), ui->upnp->isChecked()); + if (isPublicAddress(publicIP)) + return NetworkPreferences(publicIP, ui->listenIP->text().toStdString(), ui->port->value(), ui->upnp->isChecked()); else - return NetworkPreferences(listenip, ui->port->value(), ui->upnp->isChecked()); + return NetworkPreferences(listenIP, ui->port->value(), ui->upnp->isChecked()); } void Main::onKeysChanged() @@ -1790,21 +1790,20 @@ void Main::on_connect_triggered() if (m_connect.exec() == QDialog::Accepted) { bool required = m_connect.required(); - string host(m_connect.host().toUtf8().constData()); - NodeId nodeid; + string host(m_connect.host().toStdString()); + NodeId nodeID; try { - string nstr(m_connect.nodeId().toUtf8().constData()); - nodeid = NodeId(fromHex(nstr)); + nodeID = NodeId(fromHex(m_connect.nodeId().toStdString())); } catch (BadHexCharacter()) {} m_connect.reset(); if (required) - web3()->requirePeer(nodeid, host); + web3()->requirePeer(nodeID, host); else - web3()->addNode(nodeid, host); + web3()->addNode(nodeID, host); } } diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 7b9852672..407b99942 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -555,8 +555,8 @@ void NodeTable::doRefreshBuckets(boost::system::error_code const& _ec) if (connected) { NodeId randNodeId; - crypto::Nonce::get().ref().copyTo(randNodeId.ref().cropped(0,h256::size)); - crypto::Nonce::get().ref().copyTo(randNodeId.ref().cropped(h256::size,h256::size)); + crypto::Nonce::get().ref().copyTo(randNodeId.ref().cropped(0, h256::size)); + crypto::Nonce::get().ref().copyTo(randNodeId.ref().cropped(h256::size, h256::size)); discover(randNodeId); } From 55986db3774419a410f0c4c9b9a832d50b484040 Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 29 Mar 2015 22:11:10 +0200 Subject: [PATCH 156/228] synchronous resolver --- libp2p/Network.cpp | 10 +++++++--- libp2p/Network.h | 2 +- libwebthree/WebThree.h | 9 +++------ 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/libp2p/Network.cpp b/libp2p/Network.cpp index 42208ad60..61028d458 100644 --- a/libp2p/Network.cpp +++ b/libp2p/Network.cpp @@ -208,8 +208,10 @@ bi::tcp::endpoint Network::traverseNAT(std::set const& _ifAddresses return upnpep; } -bi::tcp::endpoint Network::resolveHost(ba::io_service& _ioService, string const& _addr) +bi::tcp::endpoint Network::resolveHost(string const& _addr) { + static boost::asio::io_service s_resolverIoService; + vector split; boost::split(split, _addr, boost::is_any_of(":")); unsigned port = split.size() > 1 ? stoi(split[1]) : c_defaultIPPort; @@ -223,9 +225,11 @@ bi::tcp::endpoint Network::resolveHost(ba::io_service& _ioService, string const& { boost::system::error_code ec; // resolve returns an iterator (host can resolve to multiple addresses) - bi::tcp::resolver r(_ioService); + bi::tcp::resolver r(s_resolverIoService); auto it = r.resolve({split[0], toString(port)}, ec); - if (!ec) + if (ec) + clog(NetWarn) << "Error resolving host address " << _addr << ":" << ec.message(); + else ep = *it; } return ep; diff --git a/libp2p/Network.h b/libp2p/Network.h index 5f5ebc868..4259511b5 100644 --- a/libp2p/Network.h +++ b/libp2p/Network.h @@ -71,7 +71,7 @@ public: static bi::tcp::endpoint traverseNAT(std::set const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr); /// Resolve "host:port" string as TCP endpoint. Returns unspecified endpoint on failure. - static bi::tcp::endpoint resolveHost(ba::io_service& _ioService, std::string const& _host); + static bi::tcp::endpoint resolveHost(std::string const& _host); }; } diff --git a/libwebthree/WebThree.h b/libwebthree/WebThree.h index adb691753..a0e5cc666 100644 --- a/libwebthree/WebThree.h +++ b/libwebthree/WebThree.h @@ -145,7 +145,7 @@ public: virtual void addNode(p2p::NodeId const& _node, bi::tcp::endpoint const& _hostEndpoint) override; /// Add node to connect to. - void addNode(p2p::NodeId const& _node, std::string const& _hostString) { addNode(_node, resolveHost(_hostString)); } + void addNode(p2p::NodeId const& _node, std::string const& _hostString) { addNode(_node, p2p::Network::resolveHost(_hostString)); } /// Add node to connect to. void addNode(bi::tcp::endpoint const& _endpoint) { addNode(p2p::NodeId(), _endpoint); } @@ -157,11 +157,8 @@ public: void requirePeer(p2p::NodeId const& _node, bi::tcp::endpoint const& _endpoint) override; /// Require connection to peer. - void requirePeer(p2p::NodeId const& _node, std::string const& _hostString) { requirePeer(_node, resolveHost(_hostString)); } - - /// Resolve "host[:port]" string as TCP endpoint. Returns unspecified endpoint on failure. - bi::tcp::endpoint resolveHost(std::string const& _host) { return haveNetwork() ? p2p::Host::resolveHost(m_net, _host) : bi::tcp::endpoint(); } - + void requirePeer(p2p::NodeId const& _node, std::string const& _hostString) { requirePeer(_node, p2p::Network::resolveHost(_hostString)); } + /// Save peers dev::bytes saveNetwork() override; From 91d85a402333de40e9bdb4c40cc372af34a63517 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 29 Mar 2015 22:23:59 +0200 Subject: [PATCH 157/228] Fix a load of horrible style that got through. Fix blockchain tests so they don't clobber the AZ blockchain DB! Make TransientDirectory use a temp path unless otherwise instructed. --- libdevcore/FixedHash.h | 1 + libp2p/Network.h | 2 +- libtestutils/Common.cpp | 2 +- libtestutils/TransientDirectory.cpp | 8 +++++-- libtestutils/TransientDirectory.h | 3 ++- test/blockchain.cpp | 34 ++++++++++++++--------------- 6 files changed, 28 insertions(+), 22 deletions(-) diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index f5469ada8..456365299 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -141,6 +141,7 @@ public: return ret; } + /// @returns a random valued object. static FixedHash random() { return random(s_fixedHashEngine); } /// A generic std::hash compatible function object. diff --git a/libp2p/Network.h b/libp2p/Network.h index aeeabf329..c116b29e0 100644 --- a/libp2p/Network.h +++ b/libp2p/Network.h @@ -61,7 +61,7 @@ public: static int tcp4Listen(bi::tcp::acceptor& _acceptor, unsigned short _listenPort); /// Return public endpoint of upnp interface. If successful o_upnpifaddr will be a private interface address and endpoint will contain public address and port. - static bi::tcp::endpoint traverseNAT(std::vector const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr); + static bi::tcp::endpoint traverseNAT(std::vector const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpInterfaceAddr); }; } diff --git a/libtestutils/Common.cpp b/libtestutils/Common.cpp index f15a2da12..86f96f667 100644 --- a/libtestutils/Common.cpp +++ b/libtestutils/Common.cpp @@ -74,7 +74,7 @@ std::string dev::test::toTestFilePath(std::string const& _filename) std::string dev::test::getRandomPath() { std::stringstream stream; - stream << getDataDir() << "/EthereumTests/" << randomNumber(); + stream << getDataDir("EthereumTests") << "/" << randomNumber(); return stream.str(); } diff --git a/libtestutils/TransientDirectory.cpp b/libtestutils/TransientDirectory.cpp index 48beca7e3..694784e25 100644 --- a/libtestutils/TransientDirectory.cpp +++ b/libtestutils/TransientDirectory.cpp @@ -22,12 +22,16 @@ #include #include #include "TransientDirectory.h" - using namespace std; using namespace dev; using namespace dev::test; -TransientDirectory::TransientDirectory(std::string const& _path) : m_path(_path) +TransientDirectory::TransientDirectory(): + TransientDirectory((boost::filesystem::temp_directory_path() / "eth_transient" / toString(FixedHash<4>::random())).string()) +{} + +TransientDirectory::TransientDirectory(std::string const& _path): + m_path(_path) { // we never ever want to delete a directory (including all its contents) that we did not create ourselves. if (boost::filesystem::exists(m_path)) diff --git a/libtestutils/TransientDirectory.h b/libtestutils/TransientDirectory.h index 328b4c92b..21a338e59 100644 --- a/libtestutils/TransientDirectory.h +++ b/libtestutils/TransientDirectory.h @@ -37,7 +37,8 @@ namespace test class TransientDirectory { public: - TransientDirectory(std::string const& _path = getRandomPath()); + TransientDirectory(); + TransientDirectory(std::string const& _path); ~TransientDirectory(); std::string const& path() const { return m_path; } diff --git a/test/blockchain.cpp b/test/blockchain.cpp index 988859fe5..e5bba607a 100644 --- a/test/blockchain.cpp +++ b/test/blockchain.cpp @@ -20,6 +20,7 @@ * block test functions. */ +#include #include #include #include "TestHelper.h" @@ -35,8 +36,8 @@ bytes createBlockRLPFromFields(mObject& _tObj); void overwriteBlockHeader(BlockInfo& _current_BlockHeader, mObject& _blObj); BlockInfo constructBlock(mObject& _o); void updatePoW(BlockInfo& _bi); -void writeBlockHeaderToJson(mObject& _o, const BlockInfo& _bi); -RLPStream createFullBlockFromHeader(const BlockInfo& _bi, const bytes& _txs = RLPEmptyList, const bytes& _uncles = RLPEmptyList); +void writeBlockHeaderToJson(mObject& _o, BlockInfo const& _bi); +RLPStream createFullBlockFromHeader(BlockInfo const& _bi, bytes const& _txs = RLPEmptyList, bytes const& _uncles = RLPEmptyList); void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) { @@ -75,7 +76,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) o["genesisRLP"] = "0x" + toHex(rlpGenesisBlock.out()); // construct blockchain - BlockChain bc(rlpGenesisBlock.out(), string(), true); + BlockChain bc(rlpGenesisBlock.out(), (boost::filesystem::temp_directory_path() / ("eth_test_" + toString(rand()))).string(), true); if (_fillin) { @@ -241,6 +242,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) if (sha3(RLP(state.blockData())[2].data()) != sha3(RLP(block2.out())[2].data())) cnote << "uncle list mismatch\n" << RLP(state.blockData())[2].data() << "\n" << RLP(block2.out())[2].data(); + try { state.sync(bc); @@ -292,7 +294,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) BOOST_CHECK(blObj.count("uncleHeaders") == 0); continue; } - catch(...) + catch (...) { cnote << "state sync or block import did throw an exception\n"; BOOST_CHECK(blObj.count("blockHeader") == 0); @@ -388,7 +390,6 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) BOOST_CHECK_MESSAGE(txsFromField[i] == txsFromRlp[i], "transactions from rlp and transaction from field do not match"); BOOST_CHECK_MESSAGE(txsFromField[i].rlp() == txsFromRlp[i].rlp(), "transactions rlp do not match"); - } // check uncle list @@ -488,12 +489,12 @@ bytes createBlockRLPFromFields(mObject& _tObj) return rlpStream.out(); } -void overwriteBlockHeader(BlockInfo& _current_BlockHeader, mObject& _blObj) +void overwriteBlockHeader(BlockInfo& _currentBlockHeader, mObject& _blObj) { if (_blObj["blockHeader"].get_obj().size() != 14) { - BlockInfo tmp = _current_BlockHeader; + BlockInfo tmp = _currentBlockHeader; if (_blObj["blockHeader"].get_obj().count("parentHash")) tmp.parentHash = h256(_blObj["blockHeader"].get_obj()["parentHash"].get_str()); @@ -539,16 +540,16 @@ void overwriteBlockHeader(BlockInfo& _current_BlockHeader, mObject& _blObj) // find new valid nonce - if (tmp != _current_BlockHeader) + if (tmp != _currentBlockHeader) { - _current_BlockHeader = tmp; + _currentBlockHeader = tmp; ProofOfWork pow; std::pair ret; - while (!ProofOfWork::verify(_current_BlockHeader)) + while (!ProofOfWork::verify(_currentBlockHeader)) { - ret = pow.mine(_current_BlockHeader, 1000, true, true); - Ethash::assignResult(ret.second, _current_BlockHeader); + ret = pow.mine(_currentBlockHeader, 1000, true, true); + Ethash::assignResult(ret.second, _currentBlockHeader); } } } @@ -557,13 +558,12 @@ void overwriteBlockHeader(BlockInfo& _current_BlockHeader, mObject& _blObj) // take the blockheader as is const bytes c_blockRLP = createBlockRLPFromFields(_blObj["blockHeader"].get_obj()); const RLP c_bRLP(c_blockRLP); - _current_BlockHeader.populateFromHeader(c_bRLP, IgnoreNonce); + _currentBlockHeader.populateFromHeader(c_bRLP, IgnoreNonce); } } BlockInfo constructBlock(mObject& _o) { - BlockInfo ret; try { @@ -600,7 +600,7 @@ void updatePoW(BlockInfo& _bi) _bi.hash = _bi.headerHash(WithNonce); } -void writeBlockHeaderToJson(mObject& _o, const BlockInfo& _bi) +void writeBlockHeaderToJson(mObject& _o, BlockInfo const& _bi) { _o["parentHash"] = toString(_bi.parentHash); _o["uncleHash"] = toString(_bi.sha3Uncles); @@ -620,7 +620,7 @@ void writeBlockHeaderToJson(mObject& _o, const BlockInfo& _bi) _o["hash"] = toString(_bi.hash); } -RLPStream createFullBlockFromHeader(const BlockInfo& _bi,const bytes& _txs, const bytes& _uncles ) +RLPStream createFullBlockFromHeader(BlockInfo const& _bi, bytes const& _txs, bytes const& _uncles) { RLPStream rlpStream; _bi.streamRLP(rlpStream, WithNonce); @@ -632,8 +632,8 @@ RLPStream createFullBlockFromHeader(const BlockInfo& _bi,const bytes& _txs, cons return ret; } -} }// Namespace Close +} }// Namespace Close BOOST_AUTO_TEST_SUITE(BlockChainTests) From 86092b27ed266de9e820ebbc76316e1d964bc21e Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 29 Mar 2015 22:24:05 +0200 Subject: [PATCH 158/228] remove unused method --- libp2p/Host.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/libp2p/Host.h b/libp2p/Host.h index 66ad373e3..c161f8437 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -94,9 +94,6 @@ public: /// Default host for current version of client. static std::string pocHost(); - - /// Resolve "host:port" or "ip:port" string as TCP endpoint. Returns unspecified endpoint on failure. - static bi::tcp::endpoint resolveHost(Host& _host, std::string const& _addr) { return Network::resolveHost(_host.m_ioService, _addr); } /// Register a peer-capability; all new peer connections will have this capability. template std::shared_ptr registerCapability(T* _t) { _t->m_host = this; auto ret = std::shared_ptr(_t); m_capabilities[std::make_pair(T::staticName(), T::staticVersion())] = ret; return ret; } From 9a69198a747d8e1061d3e3a58214e87b1ae3f00c Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 29 Mar 2015 22:37:32 +0200 Subject: [PATCH 159/228] Use TransientDirectory. --- test/blockchain.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/blockchain.cpp b/test/blockchain.cpp index e5bba607a..17e6c3588 100644 --- a/test/blockchain.cpp +++ b/test/blockchain.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include "TestHelper.h" @@ -76,7 +77,8 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) o["genesisRLP"] = "0x" + toHex(rlpGenesisBlock.out()); // construct blockchain - BlockChain bc(rlpGenesisBlock.out(), (boost::filesystem::temp_directory_path() / ("eth_test_" + toString(rand()))).string(), true); + TransientDirectory td; + BlockChain bc(rlpGenesisBlock.out(), td.path(), true); if (_fillin) { From 8568370a4924cf8daadce514303d1b0e21a38ef9 Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 29 Mar 2015 22:38:28 +0200 Subject: [PATCH 160/228] use explicit name of const --- libp2p/Network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libp2p/Network.cpp b/libp2p/Network.cpp index 5db2d8219..74bc8bd45 100644 --- a/libp2p/Network.cpp +++ b/libp2p/Network.cpp @@ -214,7 +214,7 @@ bi::tcp::endpoint Network::resolveHost(string const& _addr) vector split; boost::split(split, _addr, boost::is_any_of(":")); - unsigned port = split.size() > 1 ? stoi(split[1]) : c_defaultIPPort; + unsigned port = split.size() > 1 ? stoi(split[1]) : dev::p2p::c_defaultIPPort; bi::tcp::endpoint ep(bi::address(), port); boost::system::error_code ec; From 6b409f52ad80a35383043c488ea2eea7c14caba9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 29 Mar 2015 22:54:41 +0200 Subject: [PATCH 161/228] More descriptive errors on database fail. Fixes #1343 --- libethcore/Exceptions.h | 1 + libethereum/BlockChain.cpp | 17 +++++++++++++---- libethereum/State.cpp | 13 ++++++++++++- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/libethcore/Exceptions.h b/libethcore/Exceptions.h index df6a08817..db2718fc4 100644 --- a/libethcore/Exceptions.h +++ b/libethcore/Exceptions.h @@ -38,6 +38,7 @@ using errinfo_difficulty = boost::error_info; using BadFieldError = boost::tuple; struct DatabaseAlreadyOpen: virtual dev::Exception {}; +struct NotEnoughAvailableSpace: virtual dev::Exception {}; struct NotEnoughCash: virtual dev::Exception {}; struct GasPriceTooLow: virtual dev::Exception {}; struct BlockGasLimitReached: virtual dev::Exception {}; diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 688a0c326..508531f9d 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -131,10 +131,19 @@ void BlockChain::open(std::string _path, bool _killExisting) o.create_if_missing = true; ldb::DB::Open(o, _path + "/blocks", &m_blocksDB); ldb::DB::Open(o, _path + "/details", &m_extrasDB); - if (!m_blocksDB) - BOOST_THROW_EXCEPTION(DatabaseAlreadyOpen()); - if (!m_extrasDB) - BOOST_THROW_EXCEPTION(DatabaseAlreadyOpen()); + if (!m_blocksDB || !m_extrasDB) + { + if (boost::filesystem::space(_path + "/blocks").available < 1024) + { + cwarn << "Not enough available space found on hard drive. Please free some up and then re-run. Bailing."; + BOOST_THROW_EXCEPTION(NotEnoughAvailableSpace()); + } + else + { + cwarn << "Database already open. You appear to have another instance of ethereum running. Bailing."; + BOOST_THROW_EXCEPTION(DatabaseAlreadyOpen()); + } + } if (!details(m_genesisHash)) { diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 9817e2bad..378c60fa3 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -60,7 +60,18 @@ OverlayDB State::openDB(std::string _path, bool _killExisting) ldb::DB* db = nullptr; ldb::DB::Open(o, _path + "/state", &db); if (!db) - BOOST_THROW_EXCEPTION(DatabaseAlreadyOpen()); + { + if (boost::filesystem::space(_path + "/state").available < 1024) + { + cwarn << "Not enough available space found on hard drive. Please free some up and then re-run. Bailing."; + BOOST_THROW_EXCEPTION(NotEnoughAvailableSpace()); + } + else + { + cwarn << "Database already open. You appear to have another instance of ethereum running. Bailing."; + BOOST_THROW_EXCEPTION(DatabaseAlreadyOpen()); + } + } cnote << "Opened state DB."; return OverlayDB(db); From 1cd510a541cb92f59a960dddee014883a45a1ee2 Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 29 Mar 2015 23:14:43 +0200 Subject: [PATCH 162/228] coding standards --- alethzero/Connect.h | 10 +++++++--- alethzero/MainWin.cpp | 8 ++++---- libp2p/Network.h | 4 ++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/alethzero/Connect.h b/alethzero/Connect.h index 89b25617c..917c98d67 100644 --- a/alethzero/Connect.h +++ b/alethzero/Connect.h @@ -35,17 +35,21 @@ public: explicit Connect(QWidget* _parent = 0); ~Connect(); + /// Populate host chooser with default host entries void setEnvironment(QStringList const& _nodes); /// clear dialogue inputs void reset(); - // Form field values: - + /// @returns host string chosen or entered QString host(); + + /// @returns NodeId entered QString nodeId(); + + /// @returns true if Required is checked bool required(); private: - Ui::Connect* ui; + Ui::Connect* ui; }; diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 1784fbf96..0de7e2cb8 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -259,7 +259,7 @@ NetworkPreferences Main::netPrefs() const } catch (...) { - listenIP = ""; + listenIP.clear(); } auto publicIP = ui->forcePublicIP->text().toStdString(); @@ -269,11 +269,11 @@ NetworkPreferences Main::netPrefs() const } catch (...) { - publicIP = ""; + publicIP.clear(); } if (isPublicAddress(publicIP)) - return NetworkPreferences(publicIP, ui->listenIP->text().toStdString(), ui->port->value(), ui->upnp->isChecked()); + return NetworkPreferences(publicIP, listenIP, ui->port->value(), ui->upnp->isChecked()); else return NetworkPreferences(listenIP, ui->port->value(), ui->upnp->isChecked()); } @@ -1796,7 +1796,7 @@ void Main::on_connect_triggered() { nodeID = NodeId(fromHex(m_connect.nodeId().toStdString())); } - catch (BadHexCharacter()) {} + catch (BadHexCharacter&) {} m_connect.reset(); diff --git a/libp2p/Network.h b/libp2p/Network.h index 4185cfea5..d02ce3cbe 100644 --- a/libp2p/Network.h +++ b/libp2p/Network.h @@ -43,10 +43,10 @@ struct NetworkPreferences NetworkPreferences(unsigned short lp = 30303): listenPort(lp) {} // Network Preferences with specific Listen IP - NetworkPreferences(std::string l, unsigned short lp = 30303, bool u = true): publicIPAddress(), listenIPAddress(l), listenPort(lp), traverseNAT(u) {} + NetworkPreferences(std::string const& l, unsigned short lp = 30303, bool u = true): publicIPAddress(), listenIPAddress(l), listenPort(lp), traverseNAT(u) {} // Network Preferences with intended Public IP - NetworkPreferences(std::string publicIP, std::string l = std::string(), unsigned short lp = 30303, bool u = true): publicIPAddress(publicIP), listenIPAddress(l), listenPort(lp), traverseNAT(u) { if (!publicIPAddress.empty() && !isPublicAddress(publicIPAddress)) BOOST_THROW_EXCEPTION(InvalidPublicIPAddress()); } + NetworkPreferences(std::string const& publicIP, std::string const& l = std::string(), unsigned short lp = 30303, bool u = true): publicIPAddress(publicIP), listenIPAddress(l), listenPort(lp), traverseNAT(u) { if (!publicIPAddress.empty() && !isPublicAddress(publicIPAddress)) BOOST_THROW_EXCEPTION(InvalidPublicIPAddress()); } std::string publicIPAddress; std::string listenIPAddress; From b9918f94facc74c15852a0e68c35657690a951a9 Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 30 Mar 2015 01:01:28 +0200 Subject: [PATCH 163/228] bugfix. handle recv after disconnect. --- libp2p/UDP.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libp2p/UDP.h b/libp2p/UDP.h index 68160d053..374f986b0 100644 --- a/libp2p/UDP.h +++ b/libp2p/UDP.h @@ -203,7 +203,10 @@ void UDPSocket::doRead() auto self(UDPSocket::shared_from_this()); m_socket.async_receive_from(boost::asio::buffer(m_recvData), m_recvEndpoint, [this, self](boost::system::error_code _ec, size_t _len) { - if (_ec) + // ASIO Safety: It is possible that ASIO will call lambda w/o an error + // and after the socket has been disconnected. Checking m_closed + // guarantees that m_host will not be called after disconnect(). + if (_ec || m_closed) return disconnectWithError(_ec); assert(_len); @@ -222,7 +225,7 @@ void UDPSocket::doWrite() auto self(UDPSocket::shared_from_this()); m_socket.async_send_to(boost::asio::buffer(datagram.data), datagram.endpoint(), [this, self](boost::system::error_code _ec, std::size_t) { - if (_ec) + if (_ec || m_closed) return disconnectWithError(_ec); else { From c83062e76a7f9f7aad8fbc03e84c014646b1f87e Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 30 Mar 2015 01:08:19 +0200 Subject: [PATCH 164/228] coding standards --- alethzero/Connect.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/alethzero/Connect.h b/alethzero/Connect.h index 917c98d67..8209a78af 100644 --- a/alethzero/Connect.h +++ b/alethzero/Connect.h @@ -35,19 +35,19 @@ public: explicit Connect(QWidget* _parent = 0); ~Connect(); - /// Populate host chooser with default host entries + /// Populate host chooser with default host entries. void setEnvironment(QStringList const& _nodes); - /// clear dialogue inputs + /// Clear dialogue inputs. void reset(); - /// @returns host string chosen or entered + /// @returns the host string, as chosen or entered by the user. Assumed to be "hostOrIP:port" (:port is optional). QString host(); - /// @returns NodeId entered + /// @returns the identity of the node, as entered by the user. Assumed to be a 64-character hex string. QString nodeId(); - /// @returns true if Required is checked + /// @returns true if Required is checked by the user, indicating that the host is a required Peer. bool required(); private: From 076f787af9369101fa4bfda6d9bf6effe41c9f49 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 30 Mar 2015 10:12:56 +0200 Subject: [PATCH 165/228] Minor refactor. --- libp2p/Host.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index cc0d75fdb..ab39c5c25 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -310,10 +310,7 @@ void Host::determinePublic() else if (m_netPrefs.traverseNAT) { bi::address natIFAddr; - if (lset && ifAddresses.count(laddr)) - ep = Network::traverseNAT(std::set({laddr}), m_netPrefs.listenPort, natIFAddr); - else - ep = Network::traverseNAT(ifAddresses, m_netPrefs.listenPort, natIFAddr); + ep = Network::traverseNAT(lset && ifAddresses.count(laddr) ? std::set({laddr}) : ifAddresses, m_netPrefs.listenPort, natIFAddr); if (lset && natIFAddr != laddr) // if listen address is set, Host will use it, even if upnp returns different From 965f005a989e0b7fb5a46f364bfb96dad2e18aae Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 24 Mar 2015 14:53:15 +0100 Subject: [PATCH 166/228] Storage access optimisation. --- libevmcore/CommonSubexpressionEliminator.cpp | 205 ++++++++++++++----- libevmcore/CommonSubexpressionEliminator.h | 67 +++++- libevmcore/ExpressionClasses.cpp | 70 +++++-- libevmcore/ExpressionClasses.h | 10 +- test/SolidityOptimizer.cpp | 180 ++++++++++++++++ 5 files changed, 453 insertions(+), 79 deletions(-) diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index 7fed03b4e..43d01fc85 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -45,19 +45,24 @@ vector CommonSubexpressionEliminator::getOptimizedItems() // Debug info: //stream(cout, initialStackContents, targetStackContents); - return CSECodeGenerator(m_expressionClasses).generateCode(initialStackContents, targetStackContents); + return CSECodeGenerator(m_expressionClasses, m_storageWrites).generateCode( + initialStackContents, + targetStackContents + ); } ostream& CommonSubexpressionEliminator::stream( ostream& _out, - map _currentStack, + map _initialStack, map _targetStack ) const { auto streamExpressionClass = [this](ostream& _out, ExpressionClasses::Id _id) { auto const& expr = m_expressionClasses.representative(_id); - _out << " " << _id << ": " << *expr.item; + _out << " " << dec << _id << ": " << *expr.item; + if (expr.sequenceNumber) + _out << "@" << dec << expr.sequenceNumber; _out << "("; for (ExpressionClasses::Id arg: expr.arguments) _out << dec << arg << ","; @@ -66,18 +71,12 @@ ostream& CommonSubexpressionEliminator::stream( _out << "Optimizer analysis:" << endl; _out << "Final stack height: " << dec << m_stackHeight << endl; - _out << "Stack elements: " << endl; - for (auto const& it: m_stackElements) - { - _out << " " << dec << it.first << " = "; - streamExpressionClass(_out, it.second); - } _out << "Equivalence classes: " << endl; for (ExpressionClasses::Id eqClass = 0; eqClass < m_expressionClasses.size(); ++eqClass) streamExpressionClass(_out, eqClass); - _out << "Current stack: " << endl; - for (auto const& it: _currentStack) + _out << "Initial stack: " << endl; + for (auto const& it: _initialStack) { _out << " " << dec << it.first << ": "; streamExpressionClass(_out, it.second); @@ -96,9 +95,8 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) { if (_item.type() != Operation) { - if (_item.deposit() != 1) - BOOST_THROW_EXCEPTION(InvalidDeposit()); - setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {})); + assertThrow(_item.deposit() == 1, InvalidDeposit, ""); + setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {}, false)); } else { @@ -119,7 +117,12 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) vector arguments(info.args); for (int i = 0; i < info.args; ++i) arguments[i] = stackElement(m_stackHeight - i); - setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments)); + if (_item.instruction() == Instruction::SSTORE) + storeInStorage(arguments[0], arguments[1]); + else if (_item.instruction() == Instruction::SLOAD) + setStackElement(m_stackHeight + _item.deposit(), loadFromStorage(arguments[0])); + else + setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments, false)); } m_stackHeight += _item.deposit(); } @@ -132,8 +135,7 @@ void CommonSubexpressionEliminator::setStackElement(int _stackHeight, Expression void CommonSubexpressionEliminator::swapStackElements(int _stackHeightA, int _stackHeightB) { - if (_stackHeightA == _stackHeightB) - BOOST_THROW_EXCEPTION(OptimizerException() << errinfo_comment("Swap on same stack elements.")); + assertThrow(_stackHeightA != _stackHeightB, OptimizerException, "Swap on same stack elements."); // ensure they are created stackElement(_stackHeightA); stackElement(_stackHeightB); @@ -157,6 +159,33 @@ ExpressionClasses::Id CommonSubexpressionEliminator::initialStackElement(int _st return m_expressionClasses.find(AssemblyItem(dupInstruction(1 - _stackHeight))); } +void CommonSubexpressionEliminator::storeInStorage(ExpressionClasses::Id _slot, ExpressionClasses::Id _value) +{ + if (m_storageContent.count(_slot) && m_storageContent[_slot] == _value) + // do not execute the storage if we know that the value is already there + return; + m_sequenceNumber ++; + decltype(m_storageContent) storageContents; + // copy over values at points where we know that they are different from _slot + for (auto const& storageItem: m_storageContent) + if (m_expressionClasses.knownToBeDifferent(storageItem.first, _slot)) + storageContents.insert(storageItem); + m_storageContent = move(storageContents); + ExpressionClasses::Id id = m_expressionClasses.find(Instruction::SSTORE, {_slot, _value}, true, m_sequenceNumber); + m_storageWrites.insert(StorageWriteOperation(_slot, m_sequenceNumber, id)); + m_storageContent[_slot] = _value; + // increment a second time so that we get unique sequence numbers for writes + m_sequenceNumber ++; +} + +ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(ExpressionClasses::Id _slot) +{ + if (m_storageContent.count(_slot)) + return m_storageContent.at(_slot); + else + return m_storageContent[_slot] = m_expressionClasses.find(Instruction::SLOAD, {_slot}, true, m_sequenceNumber); +} + bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item) { switch (_item.type()) @@ -180,6 +209,8 @@ bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item) if (_item.instruction() == Instruction::GAS || _item.instruction() == Instruction::PC) return true; // GAS and PC assume a specific order of opcodes InstructionInfo info = instructionInfo(_item.instruction()); + if (_item.instruction() == Instruction::SSTORE) + return false; // the second requirement will be lifted once it is implemented return info.sideEffects || info.args > 2; } @@ -230,26 +261,49 @@ AssemblyItems CSECodeGenerator::generateCode( // @todo: provide information about the positions of copies of class elements - // generate the dependency graph + // generate the dependency graph starting from final storage writes and target stack contents + for (auto it = m_storageWrites.begin(); it != m_storageWrites.end();) + { + auto next = it; + ++next; + if (next == m_storageWrites.end() || next->slot != it->slot) + // last write to that storage slot + addDependencies(it->expression); + it = next; + } for (auto const& targetItem: _targetStackContents) { m_finalClasses.insert(targetItem.second); addDependencies(targetItem.second); } - // generate the actual elements + // Perform all operations on storage in order, if they are needed. + //@todo use better data structures to optimize these loops + unsigned maxSequenceNumber = 1; + for (StorageWriteOperation const& op: m_storageWrites) + maxSequenceNumber = max(maxSequenceNumber, op.sequenceNumber + 1); + for (unsigned sequenceNumber = 1; sequenceNumber <= maxSequenceNumber; ++sequenceNumber) + for (auto const& depPair: m_neededBy) + for (ExpressionClasses::Id const& id: {depPair.first, depPair.second}) + if ( + m_expressionClasses.representative(id).sequenceNumber == sequenceNumber && + !m_classPositions.count(id) + ) + generateClassElement(id, true); + + // generate the target stack elements for (auto const& targetItem: _targetStackContents) { - removeStackTopIfPossible(); int position = generateClassElement(targetItem.second); + assertThrow(position != c_invalidPosition, OptimizerException, ""); if (position == targetItem.first) continue; if (position < targetItem.first) // it is already at its target, we need another copy appendDup(position); else - appendSwapOrRemove(position); - appendSwapOrRemove(targetItem.first); + appendOrRemoveSwap(position); + appendOrRemoveSwap(targetItem.first); } // remove surplus elements @@ -270,23 +324,48 @@ AssemblyItems CSECodeGenerator::generateCode( // neither initial no target stack, no change in height finalHeight = 0; assertThrow(finalHeight == m_stackHeight, OptimizerException, "Incorrect final stack height."); - return m_generatedItems; } void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c) { if (m_neededBy.count(_c)) - return; - for (ExpressionClasses::Id argument: m_expressionClasses.representative(_c).arguments) + return; // we already computed the dependencies for _c + ExpressionClasses::Expression const& expr = m_expressionClasses.representative(_c); + for (ExpressionClasses::Id argument: expr.arguments) { addDependencies(argument); m_neededBy.insert(make_pair(argument, _c)); } + if (expr.item->type() == Operation && expr.item->instruction() == Instruction::SLOAD) + { + // this loads an unknown value from storage and thus, in addition to its arguments, depends + // on all SSTORE operations to addresses where we do not know that they are different that + // occur before this SLOAD + ExpressionClasses::Id slotToLoadFrom = expr.arguments.at(0); + for (auto it = m_storageWrites.begin(); it != m_storageWrites.end();) + { + auto next = it; + ++next; + // note that SSTORE and SLOAD never have the same sequence number + if (it->sequenceNumber < expr.sequenceNumber && + !m_expressionClasses.knownToBeDifferent(it->slot, slotToLoadFrom) && + (next == m_storageWrites.end() || next->sequenceNumber > expr.sequenceNumber) + ) + { + addDependencies(it->expression); + m_neededBy.insert(make_pair(it->expression, _c)); + } + it = next; + } + } } -int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c) +int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c, bool _allowSequenced) { + // do some cleanup + removeStackTopIfPossible(); + if (m_classPositions.count(_c)) { assertThrow( @@ -296,7 +375,13 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c) ); return m_classPositions[_c]; } - ExpressionClasses::Ids const& arguments = m_expressionClasses.representative(_c).arguments; + ExpressionClasses::Expression const& expr = m_expressionClasses.representative(_c); + assertThrow( + _allowSequenced || expr.sequenceNumber == 0, + OptimizerException, + "Sequence constrained operation requested out of sequence." + ); + ExpressionClasses::Ids const& arguments = expr.arguments; for (ExpressionClasses::Id arg: boost::adaptors::reverse(arguments)) generateClassElement(arg); @@ -307,42 +392,42 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c) if (arguments.size() == 1) { if (canBeRemoved(arguments[0], _c)) - appendSwapOrRemove(generateClassElement(arguments[0])); + appendOrRemoveSwap(classElementPosition(arguments[0])); else - appendDup(generateClassElement(arguments[0])); + appendDup(classElementPosition(arguments[0])); } else if (arguments.size() == 2) { if (canBeRemoved(arguments[1], _c)) { - appendSwapOrRemove(generateClassElement(arguments[1])); + appendOrRemoveSwap(classElementPosition(arguments[1])); if (arguments[0] == arguments[1]) appendDup(m_stackHeight); else if (canBeRemoved(arguments[0], _c)) { - appendSwapOrRemove(m_stackHeight - 1); - appendSwapOrRemove(generateClassElement(arguments[0])); + appendOrRemoveSwap(m_stackHeight - 1); + appendOrRemoveSwap(classElementPosition(arguments[0])); } else - appendDup(generateClassElement(arguments[0])); + appendDup(classElementPosition(arguments[0])); } else { if (arguments[0] == arguments[1]) { - appendDup(generateClassElement(arguments[0])); + appendDup(classElementPosition(arguments[0])); appendDup(m_stackHeight); } else if (canBeRemoved(arguments[0], _c)) { - appendSwapOrRemove(generateClassElement(arguments[0])); - appendDup(generateClassElement(arguments[1])); - appendSwapOrRemove(m_stackHeight - 1); + appendOrRemoveSwap(classElementPosition(arguments[0])); + appendDup(classElementPosition(arguments[1])); + appendOrRemoveSwap(m_stackHeight - 1); } else { - appendDup(generateClassElement(arguments[1])); - appendDup(generateClassElement(arguments[0])); + appendDup(classElementPosition(arguments[1])); + appendDup(classElementPosition(arguments[0])); } } } @@ -355,20 +440,41 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c) for (size_t i = 0; i < arguments.size(); ++i) assertThrow(m_stack[m_stackHeight - i] == arguments[i], OptimizerException, "Expected arguments not present." ); - AssemblyItem const& item = *m_expressionClasses.representative(_c).item; - while (SemanticInformation::isCommutativeOperation(item) && + while (SemanticInformation::isCommutativeOperation(*expr.item) && !m_generatedItems.empty() && m_generatedItems.back() == AssemblyItem(Instruction::SWAP1)) // this will not append a swap but remove the one that is already there - appendSwapOrRemove(m_stackHeight - 1); + appendOrRemoveSwap(m_stackHeight - 1); for (auto arg: arguments) if (canBeRemoved(arg, _c)) m_classPositions[arg] = c_invalidPosition; for (size_t i = 0; i < arguments.size(); ++i) m_stack.erase(m_stackHeight - i); - appendItem(*m_expressionClasses.representative(_c).item); - m_stack[m_stackHeight] = _c; - return m_classPositions[_c] = m_stackHeight; + appendItem(*expr.item); + if (expr.item->type() != Operation || instructionInfo(expr.item->instruction()).ret == 1) + { + m_stack[m_stackHeight] = _c; + return m_classPositions[_c] = m_stackHeight; + } + else + { + assertThrow( + instructionInfo(expr.item->instruction()).ret == 0, + OptimizerException, + "Invalid number of return values." + ); + return m_classPositions[_c] = c_invalidPosition; + } +} + +int CSECodeGenerator::classElementPosition(ExpressionClasses::Id _id) const +{ + assertThrow( + m_classPositions.count(_id) && m_classPositions.at(_id) != c_invalidPosition, + OptimizerException, + "Element requested but is not present." + ); + return m_classPositions.at(_id); } bool CSECodeGenerator::canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result) @@ -401,22 +507,23 @@ bool CSECodeGenerator::removeStackTopIfPossible() void CSECodeGenerator::appendDup(int _fromPosition) { + assertThrow(_fromPosition != c_invalidPosition, OptimizerException, ""); int nr = 1 + m_stackHeight - _fromPosition; assertThrow(nr <= 16, StackTooDeepException, "Stack too deep."); assertThrow(1 <= nr, OptimizerException, "Invalid stack access."); - m_generatedItems.push_back(AssemblyItem(dupInstruction(nr))); - m_stackHeight++; + appendItem(AssemblyItem(dupInstruction(nr))); m_stack[m_stackHeight] = m_stack[_fromPosition]; } -void CSECodeGenerator::appendSwapOrRemove(int _fromPosition) +void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition) { + assertThrow(_fromPosition != c_invalidPosition, OptimizerException, ""); if (_fromPosition == m_stackHeight) return; int nr = m_stackHeight - _fromPosition; assertThrow(nr <= 16, StackTooDeepException, "Stack too deep."); assertThrow(1 <= nr, OptimizerException, "Invalid stack access."); - m_generatedItems.push_back(AssemblyItem(swapInstruction(nr))); + appendItem(AssemblyItem(swapInstruction(nr))); // The value of a class can be present in multiple locations on the stack. We only update the // "canonical" one that is tracked by m_classPositions if (m_classPositions[m_stack[m_stackHeight]] == m_stackHeight) diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h index 331a7642d..c3e291afb 100644 --- a/libevmcore/CommonSubexpressionEliminator.h +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -25,6 +25,8 @@ #include #include +#include +#include #include #include #include @@ -44,9 +46,9 @@ using AssemblyItems = std::vector; * known to be equal only once. * * The general workings are that for each assembly item that is fed into the eliminator, an - * equivalence class is derived from the operation and the equivalence class of its arguments and - * it is assigned to the next sequence number of a stack item. DUPi, SWAPi and some arithmetic - * instructions are used to infer equivalences while these classes are determined. + * equivalence class is derived from the operation and the equivalence class of its arguments. + * DUPi, SWAPi and some arithmetic instructions are used to infer equivalences while these + * classes are determined. * * When the list of optimized items is requested, they are generated in a bottom-up fashion, * adding code for equivalence classes that were not yet computed. @@ -54,6 +56,23 @@ using AssemblyItems = std::vector; class CommonSubexpressionEliminator { public: + struct StorageWriteOperation + { + StorageWriteOperation( + ExpressionClasses::Id _slot, + unsigned _sequenceNumber, + ExpressionClasses::Id _expression + ): slot(_slot), sequenceNumber(_sequenceNumber), expression(_expression) {} + bool operator<(StorageWriteOperation const& _other) const + { + return std::tie(slot, sequenceNumber, expression) < + std::tie(_other.slot, _other.sequenceNumber, _other.expression); + } + ExpressionClasses::Id slot; + unsigned sequenceNumber; + ExpressionClasses::Id expression; + }; + /// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first /// item that must be fed into a new instance of the eliminator. template @@ -65,7 +84,7 @@ public: /// Streams debugging information to @a _out. std::ostream& stream( std::ostream& _out, - std::map _currentStack = std::map(), + std::map _initialStack = std::map(), std::map _targetStack = std::map() ) const; @@ -85,10 +104,23 @@ private: /// (must not be positive). ExpressionClasses::Id initialStackElement(int _stackHeight); + /// Increments the sequence number, deletes all storage information that might be overwritten + /// and stores the new value at the given slot. + void storeInStorage(ExpressionClasses::Id _slot, ExpressionClasses::Id _value); + /// Retrieves the current value at the given slot in storage or creates a new special sload class. + ExpressionClasses::Id loadFromStorage(ExpressionClasses::Id _slot); + /// Current stack height, can be negative. int m_stackHeight = 0; /// Current stack layout, mapping stack height -> equivalence class std::map m_stackElements; + /// Current sequence number, this is incremented with each modification to storage. + unsigned m_sequenceNumber = 1; + /// Knowledge about storage content. + std::map m_storageContent; + /// Keeps information about which storage slots were written to at which sequence number with + /// what SSTORE instruction. + std::set m_storageWrites; /// Structure containing the classes of equivalent expressions. ExpressionClasses m_expressionClasses; }; @@ -114,14 +146,19 @@ struct SemanticInformation class CSECodeGenerator { public: - CSECodeGenerator(ExpressionClasses const& _expressionClasses): - m_expressionClasses(_expressionClasses) + using StorageWriteOperation = CommonSubexpressionEliminator::StorageWriteOperation; + + CSECodeGenerator( + ExpressionClasses& _expressionClasses, + std::set const& _storageWrites + ): + m_expressionClasses(_expressionClasses), + m_storageWrites(_storageWrites) {} /// @returns the assembly items generated from the given requirements /// @param _initialStack current contents of the stack (up to stack height of zero) /// @param _targetStackContents final contents of the stack, by stack height relative to initial - /// @param _equivalenceClasses equivalence classes as expressions of how to compute them /// @note should only be called once on each object. AssemblyItems generateCode( std::map const& _initialStack, @@ -133,8 +170,13 @@ private: void addDependencies(ExpressionClasses::Id _c); /// Produce code that generates the given element if it is not yet present. - /// @returns the stack position of the element. - int generateClassElement(ExpressionClasses::Id _c); + /// @returns the stack position of the element or c_invalidPosition if it does not actually + /// generate a value on the stack. + /// @param _allowSequenced indicates that sequence-constrained operations are allowed + int generateClassElement(ExpressionClasses::Id _c, bool _allowSequenced = false); + /// @returns the position of the representative of the given id on the stack. + /// @note throws an exception if it is not on the stack. + int classElementPosition(ExpressionClasses::Id _id) const; /// @returns true if @a _element can be removed - in general or, if given, while computing @a _result. bool canBeRemoved(ExpressionClasses::Id _element, ExpressionClasses::Id _result = ExpressionClasses::Id(-1)); @@ -146,7 +188,7 @@ private: void appendDup(int _fromPosition); /// Appends a swap instruction to m_generatedItems to retrieve the element at the given stack position. /// @note this might also remove the last item if it exactly the same swap instruction. - void appendSwapOrRemove(int _fromPosition); + void appendOrRemoveSwap(int _fromPosition); /// Appends the given assembly item. void appendItem(AssemblyItem const& _item); @@ -163,7 +205,10 @@ private: std::map m_classPositions; /// The actual eqivalence class items and how to compute them. - ExpressionClasses const& m_expressionClasses; + ExpressionClasses& m_expressionClasses; + /// Keeps information about which storage slots were written to at which sequence number with + /// what SSTORE instruction. + std::set const& m_storageWrites; /// The set of equivalence classes that should be present on the stack at the end. std::set m_finalClasses; }; diff --git a/libevmcore/ExpressionClasses.cpp b/libevmcore/ExpressionClasses.cpp index b43b54113..298d11dbd 100644 --- a/libevmcore/ExpressionClasses.cpp +++ b/libevmcore/ExpressionClasses.cpp @@ -39,16 +39,22 @@ bool ExpressionClasses::Expression::operator<(ExpressionClasses::Expression cons { auto type = item->type(); auto otherType = _other.item->type(); - return std::tie(type, item->data(), arguments) < - std::tie(otherType, _other.item->data(), _other.arguments); + return std::tie(type, item->data(), arguments, sequenceNumber) < + std::tie(otherType, _other.item->data(), _other.arguments, _other.sequenceNumber); } -ExpressionClasses::Id ExpressionClasses::find(AssemblyItem const& _item, Ids const& _arguments) +ExpressionClasses::Id ExpressionClasses::find( + AssemblyItem const& _item, + Ids const& _arguments, + bool _copyItem, + unsigned _sequenceNumber +) { Expression exp; exp.id = Id(-1); exp.item = &_item; exp.arguments = _arguments; + exp.sequenceNumber = _sequenceNumber; if (SemanticInformation::isCommutativeOperation(_item)) sort(exp.arguments.begin(), exp.arguments.end()); @@ -58,9 +64,8 @@ ExpressionClasses::Id ExpressionClasses::find(AssemblyItem const& _item, Ids con if (!(e < exp || exp < e)) return e.id; - if (SemanticInformation::isDupInstruction(_item)) + if (_copyItem) { - // Special item that refers to values pre-existing on the stack m_spareAssemblyItem.push_back(make_shared(_item)); exp.item = m_spareAssemblyItem.back().get(); } @@ -74,6 +79,17 @@ ExpressionClasses::Id ExpressionClasses::find(AssemblyItem const& _item, Ids con return exp.id; } +bool ExpressionClasses::knownToBeDifferent(ExpressionClasses::Id _a, ExpressionClasses::Id _b) +{ + // Try to simplify "_a - _b" and return true iff the value is a non-zero constant. + //@todo we could try to cache this information + map matchGroups; + Pattern constant(Push); + constant.setMatchGroup(1, matchGroups); + Id difference = find(Instruction::SUB, {_a, _b}); + return constant.matches(representative(difference), *this) && constant.d() != u256(0); +} + string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const { Expression const& expr = representative(_id); @@ -189,27 +205,46 @@ Rules::Rules() // Moving constants to the outside, order matters here! // we need actions that return expressions (or patterns?) here, and we need also reversed rules // (X+A)+B -> X+(A+B) - m_rules.push_back({ + m_rules += vector>>{{ {op, {{op, {X, A}}, B}}, [=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; } - }); + }, { // X+(Y+A) -> (X+Y)+A - m_rules.push_back({ {op, {{op, {X, A}}, Y}}, [=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; } - }); + }, { // For now, we still need explicit commutativity for the inner pattern - m_rules.push_back({ {op, {{op, {A, X}}, B}}, [=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; } - }); - m_rules.push_back({ + }, { {op, {{op, {A, X}}, Y}}, [=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; } - }); + }}; + } + // move constants across subtractions + m_rules += vector>>{ + { + // X - A -> X + (-A) + {Instruction::SUB, {X, A}}, + [=]() -> Pattern { return {Instruction::ADD, {X, 0 - A.d()}}; } + }, { + // (X + A) - Y -> (X - Y) + A + {Instruction::SUB, {{Instruction::ADD, {X, A}}, Y}}, + [=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; } + }, { + // (A + X) - Y -> (X - Y) + A + {Instruction::SUB, {{Instruction::ADD, {A, X}}, Y}}, + [=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; } + }, { + // X - (Y + A) -> (X - Y) + (-A) + {Instruction::SUB, {X, {Instruction::ADD, {Y, A}}}}, + [=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; } + }, { + // X - (A + Y) -> (X - Y) + (-A) + {Instruction::SUB, {X, {Instruction::ADD, {A, Y}}}}, + [=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; } + } }; - - //@todo: (x+8)-3 and other things } ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr, bool _secondRun) @@ -231,7 +266,7 @@ ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr, //cout << ")" << endl; //cout << "with rule " << rule.first.toString() << endl; //ExpressionTemplate t(rule.second()); - //cout << "to" << rule.second().toString() << endl; + //cout << "to " << rule.second().toString() << endl; return rebuildExpression(ExpressionTemplate(rule.second())); } } @@ -254,8 +289,7 @@ ExpressionClasses::Id ExpressionClasses::rebuildExpression(ExpressionTemplate co Ids arguments; for (ExpressionTemplate const& t: _template.arguments) arguments.push_back(rebuildExpression(t)); - m_spareAssemblyItem.push_back(make_shared(_template.item)); - return find(*m_spareAssemblyItem.back(), arguments); + return find(_template.item, arguments); } diff --git a/libevmcore/ExpressionClasses.h b/libevmcore/ExpressionClasses.h index eda568e23..b7868ebb9 100644 --- a/libevmcore/ExpressionClasses.h +++ b/libevmcore/ExpressionClasses.h @@ -52,17 +52,25 @@ public: Id id; AssemblyItem const* item; Ids arguments; + unsigned sequenceNumber; ///< Storage modification sequence, only used for SLOAD/SSTORE instructions. bool operator<(Expression const& _other) const; }; /// Retrieves the id of the expression equivalence class resulting from the given item applied to the /// given classes, might also create a new one. - Id find(AssemblyItem const& _item, Ids const& _arguments = {}); + /// @param _copyItem if true, copies the assembly item to an internal storage instead of just + /// keeping a pointer. + /// The @a _sequenceNumber indicates the current storage access sequence. + Id find(AssemblyItem const& _item, Ids const& _arguments = {}, bool _copyItem = true, unsigned _sequenceNumber = 0); /// @returns the canonical representative of an expression class. Expression const& representative(Id _id) const { return m_representatives.at(_id); } /// @returns the number of classes. Id size() const { return m_representatives.size(); } + /// @returns true if the values of the given classes are known to be different (on every input). + /// @note that this function might still return false for some different inputs. + bool knownToBeDifferent(Id _a, Id _b); + std::string fullDAGToString(Id _id) const; private: diff --git a/test/SolidityOptimizer.cpp b/test/SolidityOptimizer.cpp index 2d5cff7ac..de4cac8fd 100644 --- a/test/SolidityOptimizer.cpp +++ b/test/SolidityOptimizer.cpp @@ -303,6 +303,186 @@ BOOST_AUTO_TEST_CASE(cse_associativity2) checkCSE(input, {Instruction::DUP2, Instruction::DUP2, Instruction::ADD, u256(5), Instruction::ADD}); } +BOOST_AUTO_TEST_CASE(cse_storage) +{ + AssemblyItems input{ + u256(0), + Instruction::SLOAD, + u256(0), + Instruction::SLOAD, + Instruction::ADD, + u256(0), + Instruction::SSTORE + }; + checkCSE(input, { + u256(0), + Instruction::DUP1, + Instruction::SLOAD, + Instruction::DUP1, + Instruction::ADD, + Instruction::SWAP1, + Instruction::SSTORE + }); +} + +BOOST_AUTO_TEST_CASE(cse_noninterleaved_storage) +{ + // two stores to the same location should be replaced by only one store, even if we + // read in the meantime + AssemblyItems input{ + u256(7), + Instruction::DUP2, + Instruction::SSTORE, + Instruction::DUP1, + Instruction::SLOAD, + u256(8), + Instruction::DUP3, + Instruction::SSTORE + }; + checkCSE(input, { + u256(8), + Instruction::DUP2, + Instruction::SSTORE, + u256(7) + }); +} + +BOOST_AUTO_TEST_CASE(cse_interleaved_storage) +{ + // stores and reads to/from two unknown locations, should not optimize away the first store + AssemblyItems input{ + u256(7), + Instruction::DUP2, + Instruction::SSTORE, // store to "DUP1" + Instruction::DUP2, + Instruction::SLOAD, // read from "DUP2", might be equal to "DUP1" + u256(0), + Instruction::DUP3, + Instruction::SSTORE // store different value to "DUP1" + }; + checkCSE(input, input); +} + +BOOST_AUTO_TEST_CASE(cse_interleaved_storage_same_value) +{ + // stores and reads to/from two unknown locations, should not optimize away the first store + // but it should optimize away the second, since we already know the value will be the same + AssemblyItems input{ + u256(7), + Instruction::DUP2, + Instruction::SSTORE, // store to "DUP1" + Instruction::DUP2, + Instruction::SLOAD, // read from "DUP2", might be equal to "DUP1" + u256(6), + u256(1), + Instruction::ADD, + Instruction::DUP3, + Instruction::SSTORE // store same value to "DUP1" + }; + checkCSE(input, { + u256(7), + Instruction::DUP2, + Instruction::SSTORE, + Instruction::DUP2, + Instruction::SLOAD + }); +} + +BOOST_AUTO_TEST_CASE(cse_interleaved_storage_at_known_location) +{ + // stores and reads to/from two known locations, should optimize away the first store, + // because we know that the location is different + AssemblyItems input{ + u256(0x70), + u256(1), + Instruction::SSTORE, // store to 1 + u256(2), + Instruction::SLOAD, // read from 2, is different from 1 + u256(0x90), + u256(1), + Instruction::SSTORE // store different value at 1 + }; + checkCSE(input, { + u256(2), + Instruction::SLOAD, + u256(0x90), + u256(1), + Instruction::SSTORE + }); +} + +BOOST_AUTO_TEST_CASE(cse_interleaved_storage_at_known_location_offset) +{ + // stores and reads to/from two locations which are known to be different, + // should optimize away the first store, because we know that the location is different + AssemblyItems input{ + u256(0x70), + Instruction::DUP2, + u256(1), + Instruction::ADD, + Instruction::SSTORE, // store to "DUP1"+1 + Instruction::DUP1, + u256(2), + Instruction::ADD, + Instruction::SLOAD, // read from "DUP1"+2, is different from "DUP1"+1 + u256(0x90), + Instruction::DUP3, + u256(1), + Instruction::ADD, + Instruction::SSTORE // store different value at "DUP1"+1 + }; + checkCSE(input, { + u256(2), + Instruction::DUP2, + Instruction::ADD, + Instruction::SLOAD, + u256(0x90), + u256(1), + Instruction::DUP4, + Instruction::ADD, + Instruction::SSTORE + }); +} + +BOOST_AUTO_TEST_CASE(cse_deep_stack) +{ + AssemblyItems input{ + Instruction::ADD, + Instruction::SWAP1, + Instruction::POP, + Instruction::SWAP8, + Instruction::POP, + Instruction::SWAP8, + Instruction::POP, + Instruction::SWAP8, + Instruction::SWAP5, + Instruction::POP, + Instruction::POP, + Instruction::POP, + Instruction::POP, + Instruction::POP, + }; + checkCSE(input, { + Instruction::SWAP4, + Instruction::SWAP12, + Instruction::SWAP3, + Instruction::SWAP11, + Instruction::POP, + Instruction::SWAP1, + Instruction::SWAP3, + Instruction::ADD, + Instruction::SWAP8, + Instruction::POP, + Instruction::SWAP6, + Instruction::POP, + Instruction::POP, + Instruction::POP, + Instruction::POP, + Instruction::POP, + Instruction::POP, + }); +} + BOOST_AUTO_TEST_SUITE_END() } From c19229415d0f9704d5c9e6ac11733824a56dec20 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 25 Mar 2015 18:42:18 +0100 Subject: [PATCH 167/228] Some optimizations to the optimizer. --- libevmcore/ExpressionClasses.cpp | 19 ++++++++++--------- libevmcore/ExpressionClasses.h | 3 +++ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/libevmcore/ExpressionClasses.cpp b/libevmcore/ExpressionClasses.cpp index 298d11dbd..717379be5 100644 --- a/libevmcore/ExpressionClasses.cpp +++ b/libevmcore/ExpressionClasses.cpp @@ -59,10 +59,9 @@ ExpressionClasses::Id ExpressionClasses::find( if (SemanticInformation::isCommutativeOperation(_item)) sort(exp.arguments.begin(), exp.arguments.end()); - //@todo store all class members (not only the representatives) in an efficient data structure to search here - for (Expression const& e: m_representatives) - if (!(e < exp || exp < e)) - return e.id; + auto it = m_expressions.find(exp); + if (it != m_expressions.end()) + return it->id; if (_copyItem) { @@ -72,17 +71,19 @@ ExpressionClasses::Id ExpressionClasses::find( ExpressionClasses::Id id = tryToSimplify(exp); if (id < m_representatives.size()) - return id; - - exp.id = m_representatives.size(); - m_representatives.push_back(exp); + exp.id = id; + else + { + exp.id = m_representatives.size(); + m_representatives.push_back(exp); + } + m_expressions.insert(exp); return exp.id; } bool ExpressionClasses::knownToBeDifferent(ExpressionClasses::Id _a, ExpressionClasses::Id _b) { // Try to simplify "_a - _b" and return true iff the value is a non-zero constant. - //@todo we could try to cache this information map matchGroups; Pattern constant(Push); constant.setMatchGroup(1, matchGroups); diff --git a/libevmcore/ExpressionClasses.h b/libevmcore/ExpressionClasses.h index b7868ebb9..15824f6ad 100644 --- a/libevmcore/ExpressionClasses.h +++ b/libevmcore/ExpressionClasses.h @@ -53,6 +53,7 @@ public: AssemblyItem const* item; Ids arguments; unsigned sequenceNumber; ///< Storage modification sequence, only used for SLOAD/SSTORE instructions. + /// Behaves as if this was a tuple of (item->type(), item->data(), arguments, sequenceNumber). bool operator<(Expression const& _other) const; }; @@ -86,6 +87,8 @@ private: /// Expression equivalence class representatives - we only store one item of an equivalence. std::vector m_representatives; + /// All expression ever encountered. + std::set m_expressions; std::vector> m_spareAssemblyItem; }; From 9da4c207a3cdfc65096060f7069a452451ef293a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Mar 2015 13:03:24 +0100 Subject: [PATCH 168/228] Change to storage operations. --- libevmcore/CommonSubexpressionEliminator.cpp | 81 ++++++++++---------- libevmcore/CommonSubexpressionEliminator.h | 36 ++++----- libevmcore/ExpressionClasses.h | 9 ++- 3 files changed, 63 insertions(+), 63 deletions(-) diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index 43d01fc85..6002de758 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -45,7 +45,7 @@ vector CommonSubexpressionEliminator::getOptimizedItems() // Debug info: //stream(cout, initialStackContents, targetStackContents); - return CSECodeGenerator(m_expressionClasses, m_storageWrites).generateCode( + return CSECodeGenerator(m_expressionClasses, m_storeOperations).generateCode( initialStackContents, targetStackContents ); @@ -172,7 +172,7 @@ void CommonSubexpressionEliminator::storeInStorage(ExpressionClasses::Id _slot, storageContents.insert(storageItem); m_storageContent = move(storageContents); ExpressionClasses::Id id = m_expressionClasses.find(Instruction::SSTORE, {_slot, _value}, true, m_sequenceNumber); - m_storageWrites.insert(StorageWriteOperation(_slot, m_sequenceNumber, id)); + m_storeOperations.push_back(StoreOperation(_slot, m_sequenceNumber, id)); m_storageContent[_slot] = _value; // increment a second time so that we get unique sequence numbers for writes m_sequenceNumber ++; @@ -249,6 +249,16 @@ bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item) return Instruction::SWAP1 <= _item.instruction() && _item.instruction() <= Instruction::SWAP16; } +CSECodeGenerator::CSECodeGenerator( + ExpressionClasses& _expressionClasses, + vector const& _storeOperations +): + m_expressionClasses(_expressionClasses) +{ + for (auto const& store: _storeOperations) + m_storeOperations[store.slot].push_back(store); +} + AssemblyItems CSECodeGenerator::generateCode( map const& _initialStack, map const& _targetStackContents @@ -261,35 +271,26 @@ AssemblyItems CSECodeGenerator::generateCode( // @todo: provide information about the positions of copies of class elements - // generate the dependency graph starting from final storage writes and target stack contents - for (auto it = m_storageWrites.begin(); it != m_storageWrites.end();) - { - auto next = it; - ++next; - if (next == m_storageWrites.end() || next->slot != it->slot) - // last write to that storage slot - addDependencies(it->expression); - it = next; - } + // generate the dependency graph starting from final storage and memory writes and target stack contents + for (auto const& p: m_storeOperations) + addDependencies(p.second.back().expression); for (auto const& targetItem: _targetStackContents) { m_finalClasses.insert(targetItem.second); addDependencies(targetItem.second); } - // Perform all operations on storage in order, if they are needed. - //@todo use better data structures to optimize these loops - unsigned maxSequenceNumber = 1; - for (StorageWriteOperation const& op: m_storageWrites) - maxSequenceNumber = max(maxSequenceNumber, op.sequenceNumber + 1); - for (unsigned sequenceNumber = 1; sequenceNumber <= maxSequenceNumber; ++sequenceNumber) - for (auto const& depPair: m_neededBy) - for (ExpressionClasses::Id const& id: {depPair.first, depPair.second}) - if ( - m_expressionClasses.representative(id).sequenceNumber == sequenceNumber && - !m_classPositions.count(id) - ) - generateClassElement(id, true); + // store all needed sequenced expressions + set> sequencedExpressions; + for (auto const& p: m_neededBy) + for (auto id: {p.first, p.second}) + if (unsigned seqNr = m_expressionClasses.representative(id).sequenceNumber) + sequencedExpressions.insert(make_pair(seqNr, id)); + + // Perform all operations on storage and memory in order, if they are needed. + for (auto const& seqAndId: sequencedExpressions) + if (!m_classPositions.count(seqAndId.second)) + generateClassElement(seqAndId.second, true); // generate the target stack elements for (auto const& targetItem: _targetStackContents) @@ -340,23 +341,25 @@ void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c) if (expr.item->type() == Operation && expr.item->instruction() == Instruction::SLOAD) { // this loads an unknown value from storage and thus, in addition to its arguments, depends - // on all SSTORE operations to addresses where we do not know that they are different that - // occur before this SLOAD + // on all store operations to addresses where we do not know that they are different that + // occur before this load ExpressionClasses::Id slotToLoadFrom = expr.arguments.at(0); - for (auto it = m_storageWrites.begin(); it != m_storageWrites.end();) + for (auto const& p: m_storeOperations) { - auto next = it; - ++next; - // note that SSTORE and SLOAD never have the same sequence number - if (it->sequenceNumber < expr.sequenceNumber && - !m_expressionClasses.knownToBeDifferent(it->slot, slotToLoadFrom) && - (next == m_storageWrites.end() || next->sequenceNumber > expr.sequenceNumber) + ExpressionClasses::Id slot = p.first; + StoreOperations const& storeOps = p.second; + if ( + m_expressionClasses.knownToBeDifferent(slot, slotToLoadFrom) || + storeOps.front().sequenceNumber > expr.sequenceNumber ) - { - addDependencies(it->expression); - m_neededBy.insert(make_pair(it->expression, _c)); - } - it = next; + continue; + // note that store and load never have the same sequence number + ExpressionClasses::Id latestStore = storeOps.front().expression; + for (auto it = ++storeOps.begin(); it != storeOps.end(); ++it) + if (it->sequenceNumber < expr.sequenceNumber) + latestStore = it->expression; + addDependencies(latestStore); + m_neededBy.insert(make_pair(latestStore, _c)); } } } diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h index c3e291afb..c4d960d91 100644 --- a/libevmcore/CommonSubexpressionEliminator.h +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -56,18 +56,13 @@ using AssemblyItems = std::vector; class CommonSubexpressionEliminator { public: - struct StorageWriteOperation + struct StoreOperation { - StorageWriteOperation( + StoreOperation( ExpressionClasses::Id _slot, unsigned _sequenceNumber, ExpressionClasses::Id _expression ): slot(_slot), sequenceNumber(_sequenceNumber), expression(_expression) {} - bool operator<(StorageWriteOperation const& _other) const - { - return std::tie(slot, sequenceNumber, expression) < - std::tie(_other.slot, _other.sequenceNumber, _other.expression); - } ExpressionClasses::Id slot; unsigned sequenceNumber; ExpressionClasses::Id expression; @@ -114,13 +109,13 @@ private: int m_stackHeight = 0; /// Current stack layout, mapping stack height -> equivalence class std::map m_stackElements; - /// Current sequence number, this is incremented with each modification to storage. + /// Current sequence number, this is incremented with each modification to storage or memory. unsigned m_sequenceNumber = 1; /// Knowledge about storage content. std::map m_storageContent; - /// Keeps information about which storage slots were written to at which sequence number with - /// what SSTORE instruction. - std::set m_storageWrites; + /// Keeps information about which storage or memory slots were written to at which sequence + /// number with what instruction. + std::vector m_storeOperations; /// Structure containing the classes of equivalent expressions. ExpressionClasses m_expressionClasses; }; @@ -146,15 +141,12 @@ struct SemanticInformation class CSECodeGenerator { public: - using StorageWriteOperation = CommonSubexpressionEliminator::StorageWriteOperation; + using StoreOperation = CommonSubexpressionEliminator::StoreOperation; + using StoreOperations = std::vector; - CSECodeGenerator( - ExpressionClasses& _expressionClasses, - std::set const& _storageWrites - ): - m_expressionClasses(_expressionClasses), - m_storageWrites(_storageWrites) - {} + /// Initializes the code generator with the given classes and store operations. + /// The store operations have to be sorted ascendingly by sequence number. + CSECodeGenerator(ExpressionClasses& _expressionClasses, StoreOperations const& _storeOperations); /// @returns the assembly items generated from the given requirements /// @param _initialStack current contents of the stack (up to stack height of zero) @@ -206,9 +198,9 @@ private: /// The actual eqivalence class items and how to compute them. ExpressionClasses& m_expressionClasses; - /// Keeps information about which storage slots were written to at which sequence number with - /// what SSTORE instruction. - std::set const& m_storageWrites; + /// Keeps information about which storage or memory slots were written to by which operations. + /// The operations are sorted ascendingly by sequence number. + std::map m_storeOperations; /// The set of equivalence classes that should be present on the stack at the end. std::set m_finalClasses; }; diff --git a/libevmcore/ExpressionClasses.h b/libevmcore/ExpressionClasses.h index 15824f6ad..d76fd693d 100644 --- a/libevmcore/ExpressionClasses.h +++ b/libevmcore/ExpressionClasses.h @@ -61,8 +61,13 @@ public: /// given classes, might also create a new one. /// @param _copyItem if true, copies the assembly item to an internal storage instead of just /// keeping a pointer. - /// The @a _sequenceNumber indicates the current storage access sequence. - Id find(AssemblyItem const& _item, Ids const& _arguments = {}, bool _copyItem = true, unsigned _sequenceNumber = 0); + /// The @a _sequenceNumber indicates the current storage or memory access sequence. + Id find( + AssemblyItem const& _item, + Ids const& _arguments = {}, + bool _copyItem = true, + unsigned _sequenceNumber = 0 + ); /// @returns the canonical representative of an expression class. Expression const& representative(Id _id) const { return m_representatives.at(_id); } /// @returns the number of classes. From d6a611429f39d726672db6331db86e7d5306a05d Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Mar 2015 15:50:50 +0100 Subject: [PATCH 169/228] Optimizer for memory. --- libevmcore/CommonSubexpressionEliminator.cpp | 70 +++++++++++++++++--- libevmcore/CommonSubexpressionEliminator.h | 16 ++++- libevmcore/ExpressionClasses.cpp | 13 ++++ libevmcore/ExpressionClasses.h | 2 + test/SolidityOptimizer.cpp | 53 +++++++++++++++ 5 files changed, 142 insertions(+), 12 deletions(-) diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index 6002de758..1d2a71836 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -121,6 +121,10 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) storeInStorage(arguments[0], arguments[1]); else if (_item.instruction() == Instruction::SLOAD) setStackElement(m_stackHeight + _item.deposit(), loadFromStorage(arguments[0])); + else if (_item.instruction() == Instruction::MSTORE) + storeInMemory(arguments[0], arguments[1]); + else if (_item.instruction() == Instruction::MLOAD) + setStackElement(m_stackHeight + _item.deposit(), loadFromMemory(arguments[0])); else setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments, false)); } @@ -172,7 +176,7 @@ void CommonSubexpressionEliminator::storeInStorage(ExpressionClasses::Id _slot, storageContents.insert(storageItem); m_storageContent = move(storageContents); ExpressionClasses::Id id = m_expressionClasses.find(Instruction::SSTORE, {_slot, _value}, true, m_sequenceNumber); - m_storeOperations.push_back(StoreOperation(_slot, m_sequenceNumber, id)); + m_storeOperations.push_back(StoreOperation(StoreOperation::Storage, _slot, m_sequenceNumber, id)); m_storageContent[_slot] = _value; // increment a second time so that we get unique sequence numbers for writes m_sequenceNumber ++; @@ -186,6 +190,33 @@ ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(ExpressionC return m_storageContent[_slot] = m_expressionClasses.find(Instruction::SLOAD, {_slot}, true, m_sequenceNumber); } +void CommonSubexpressionEliminator::storeInMemory(ExpressionClasses::Id _slot, ExpressionClasses::Id _value) +{ + if (m_memoryContent.count(_slot) && m_memoryContent[_slot] == _value) + // do not execute the store if we know that the value is already there + return; + m_sequenceNumber ++; + decltype(m_memoryContent) memoryContents; + // copy over values at points where we know that they are different from _slot by at least 32 + for (auto const& memoryItem: m_memoryContent) + if (m_expressionClasses.knownToBeDifferentBy32(memoryItem.first, _slot)) + memoryContents.insert(memoryItem); + m_memoryContent = move(memoryContents); + ExpressionClasses::Id id = m_expressionClasses.find(Instruction::MSTORE, {_slot, _value}, true, m_sequenceNumber); + m_storeOperations.push_back(StoreOperation(StoreOperation::Memory, _slot, m_sequenceNumber, id)); + m_memoryContent[_slot] = _value; + // increment a second time so that we get unique sequence numbers for writes + m_sequenceNumber ++; +} + +ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(ExpressionClasses::Id _slot) +{ + if (m_memoryContent.count(_slot)) + return m_memoryContent.at(_slot); + else + return m_memoryContent[_slot] = m_expressionClasses.find(Instruction::MLOAD, {_slot}, true, m_sequenceNumber); +} + bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item) { switch (_item.type()) @@ -208,9 +239,19 @@ bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item) return false; if (_item.instruction() == Instruction::GAS || _item.instruction() == Instruction::PC) return true; // GAS and PC assume a specific order of opcodes + if (_item.instruction() == Instruction::MSIZE) + return true; // msize is modified already by memory access, avoid that for now + if (_item.instruction() == Instruction::SHA3) + return true; //@todo: we have to compare sha3's not based on their memory addresses but on the memory content. InstructionInfo info = instructionInfo(_item.instruction()); if (_item.instruction() == Instruction::SSTORE) return false; + if (_item.instruction() == Instruction::MSTORE) + return false; + //@todo: We do not handle the following memory instructions for now: + // calldatacopy, codecopy, extcodecopy, mstore8, + // msize (not that msize also depends on memory read access) + // the second requirement will be lifted once it is implemented return info.sideEffects || info.args > 2; } @@ -256,7 +297,7 @@ CSECodeGenerator::CSECodeGenerator( m_expressionClasses(_expressionClasses) { for (auto const& store: _storeOperations) - m_storeOperations[store.slot].push_back(store); + m_storeOperations[make_pair(store.target, store.slot)].push_back(store); } AssemblyItems CSECodeGenerator::generateCode( @@ -332,25 +373,34 @@ void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c) { if (m_neededBy.count(_c)) return; // we already computed the dependencies for _c - ExpressionClasses::Expression const& expr = m_expressionClasses.representative(_c); + ExpressionClasses::Expression expr = m_expressionClasses.representative(_c); for (ExpressionClasses::Id argument: expr.arguments) { addDependencies(argument); m_neededBy.insert(make_pair(argument, _c)); } - if (expr.item->type() == Operation && expr.item->instruction() == Instruction::SLOAD) + if (expr.item->type() == Operation && ( + expr.item->instruction() == Instruction::SLOAD || + expr.item->instruction() == Instruction::MLOAD + )) { - // this loads an unknown value from storage and thus, in addition to its arguments, depends - // on all store operations to addresses where we do not know that they are different that - // occur before this load + // this loads an unknown value from storage or memory and thus, in addition to its + // arguments, depends on all store operations to addresses where we do not know that + // they are different that occur before this load + StoreOperation::Target target = expr.item->instruction() == Instruction::SLOAD ? + StoreOperation::Storage : StoreOperation::Memory; ExpressionClasses::Id slotToLoadFrom = expr.arguments.at(0); for (auto const& p: m_storeOperations) { - ExpressionClasses::Id slot = p.first; + if (p.first.first != target) + continue; + ExpressionClasses::Id slot = p.first.second; StoreOperations const& storeOps = p.second; + if (storeOps.front().sequenceNumber > expr.sequenceNumber) + continue; if ( - m_expressionClasses.knownToBeDifferent(slot, slotToLoadFrom) || - storeOps.front().sequenceNumber > expr.sequenceNumber + (target == StoreOperation::Memory && m_expressionClasses.knownToBeDifferentBy32(slot, slotToLoadFrom)) || + (target == StoreOperation::Storage && m_expressionClasses.knownToBeDifferent(slot, slotToLoadFrom)) ) continue; // note that store and load never have the same sequence number diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h index c4d960d91..03f8cf987 100644 --- a/libevmcore/CommonSubexpressionEliminator.h +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -58,11 +58,14 @@ class CommonSubexpressionEliminator public: struct StoreOperation { + enum Target { Memory, Storage }; StoreOperation( + Target _target, ExpressionClasses::Id _slot, unsigned _sequenceNumber, ExpressionClasses::Id _expression - ): slot(_slot), sequenceNumber(_sequenceNumber), expression(_expression) {} + ): target(_target), slot(_slot), sequenceNumber(_sequenceNumber), expression(_expression) {} + Target target; ExpressionClasses::Id slot; unsigned sequenceNumber; ExpressionClasses::Id expression; @@ -104,6 +107,12 @@ private: void storeInStorage(ExpressionClasses::Id _slot, ExpressionClasses::Id _value); /// Retrieves the current value at the given slot in storage or creates a new special sload class. ExpressionClasses::Id loadFromStorage(ExpressionClasses::Id _slot); + /// Increments the sequence number, deletes all memory information that might be overwritten + /// and stores the new value at the given slot. + void storeInMemory(ExpressionClasses::Id _slot, ExpressionClasses::Id _value); + /// Retrieves the current value at the given slot in memory or creates a new special mload class. + ExpressionClasses::Id loadFromMemory(ExpressionClasses::Id _slot); + /// Current stack height, can be negative. int m_stackHeight = 0; @@ -113,6 +122,9 @@ private: unsigned m_sequenceNumber = 1; /// Knowledge about storage content. std::map m_storageContent; + /// Knowledge about memory content. Keys are memory addresses, note that the values overlap + /// and are not contained here if they are not completely known. + std::map m_memoryContent; /// Keeps information about which storage or memory slots were written to at which sequence /// number with what instruction. std::vector m_storeOperations; @@ -200,7 +212,7 @@ private: ExpressionClasses& m_expressionClasses; /// Keeps information about which storage or memory slots were written to by which operations. /// The operations are sorted ascendingly by sequence number. - std::map m_storeOperations; + std::map, StoreOperations> m_storeOperations; /// The set of equivalence classes that should be present on the stack at the end. std::set m_finalClasses; }; diff --git a/libevmcore/ExpressionClasses.cpp b/libevmcore/ExpressionClasses.cpp index 717379be5..1c7a79e6b 100644 --- a/libevmcore/ExpressionClasses.cpp +++ b/libevmcore/ExpressionClasses.cpp @@ -91,6 +91,19 @@ bool ExpressionClasses::knownToBeDifferent(ExpressionClasses::Id _a, ExpressionC return constant.matches(representative(difference), *this) && constant.d() != u256(0); } +bool ExpressionClasses::knownToBeDifferentBy32(ExpressionClasses::Id _a, ExpressionClasses::Id _b) +{ + // Try to simplify "_a - _b" and return true iff the value is at least 32 away from zero. + map matchGroups; + Pattern constant(Push); + constant.setMatchGroup(1, matchGroups); + Id difference = find(Instruction::SUB, {_a, _b}); + if (!constant.matches(representative(difference), *this)) + return false; + // forbidden interval is ["-31", 31] + return constant.d() + 31 > u256(62); +} + string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const { Expression const& expr = representative(_id); diff --git a/libevmcore/ExpressionClasses.h b/libevmcore/ExpressionClasses.h index d76fd693d..5179845f6 100644 --- a/libevmcore/ExpressionClasses.h +++ b/libevmcore/ExpressionClasses.h @@ -76,6 +76,8 @@ public: /// @returns true if the values of the given classes are known to be different (on every input). /// @note that this function might still return false for some different inputs. bool knownToBeDifferent(Id _a, Id _b); + /// Similar to @a knownToBeDifferent but require that abs(_a - b) >= 32. + bool knownToBeDifferentBy32(Id _a, Id _b); std::string fullDAGToString(Id _id) const; diff --git a/test/SolidityOptimizer.cpp b/test/SolidityOptimizer.cpp index de4cac8fd..cf35536e6 100644 --- a/test/SolidityOptimizer.cpp +++ b/test/SolidityOptimizer.cpp @@ -444,6 +444,59 @@ BOOST_AUTO_TEST_CASE(cse_interleaved_storage_at_known_location_offset) }); } +BOOST_AUTO_TEST_CASE(cse_interleaved_memory_at_known_location_offset) +{ + // stores and reads to/from two locations which are known to be different, + // should not optimize away the first store, because the location overlaps with the load, + // but it should optimize away the second, because we know that the location is different by 32 + AssemblyItems input{ + u256(0x50), + Instruction::DUP2, + u256(2), + Instruction::ADD, + Instruction::MSTORE, // ["DUP1"+2] = 0x50 + u256(0x60), + Instruction::DUP2, + u256(32), + Instruction::ADD, + Instruction::MSTORE, // ["DUP1"+32] = 0x60 + Instruction::DUP1, + Instruction::MLOAD, // read from "DUP1" + u256(0x70), + Instruction::DUP3, + u256(32), + Instruction::ADD, + Instruction::MSTORE, // ["DUP1"+32] = 0x70 + u256(0x80), + Instruction::DUP3, + u256(2), + Instruction::ADD, + Instruction::MSTORE, // ["DUP1"+2] = 0x80 + }; + // If the actual code changes too much, we could also simply check that the output contains + // exactly 3 MSTORE and exactly 1 MLOAD instruction. + checkCSE(input, { + u256(0x50), + u256(2), + Instruction::DUP3, + Instruction::ADD, + Instruction::SWAP1, + Instruction::DUP2, + Instruction::MSTORE, // ["DUP1"+2] = 0x50 + Instruction::DUP2, + Instruction::MLOAD, // read from "DUP1" + u256(0x70), + u256(32), + Instruction::DUP5, + Instruction::ADD, + Instruction::MSTORE, // ["DUP1"+32] = 0x70 + u256(0x80), + Instruction::SWAP1, + Instruction::SWAP2, + Instruction::MSTORE // ["DUP1"+2] = 0x80 + }); +} + BOOST_AUTO_TEST_CASE(cse_deep_stack) { AssemblyItems input{ From b1ea943975e0986e1711cce2a8b3d3ad41f3ae10 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 27 Mar 2015 00:24:31 +0100 Subject: [PATCH 170/228] Optimize breaking item. --- libevmcore/Assembly.cpp | 15 +------- libevmcore/CommonSubexpressionEliminator.cpp | 39 ++++++++++++++++++-- libevmcore/CommonSubexpressionEliminator.h | 11 +++++- test/SolidityOptimizer.cpp | 32 ++++++++++++++++ 4 files changed, 78 insertions(+), 19 deletions(-) diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index abe932282..0301c9325 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -187,18 +187,7 @@ Assembly& Assembly::optimise(bool _enable) { if (!_enable) return *this; - std::vector>> rules = - { - { { Push, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, - { { PushTag, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, - { { PushString, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, - { { PushSub, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, - { { PushSubSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, - { { PushProgramSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, - { { Push, PushTag, Instruction::JUMPI }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].data()) return { m[1], Instruction::JUMP }; else return {}; } }, - { { Instruction::ISZERO, Instruction::ISZERO }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, - }; - + std::vector>> rules; // jump to next instruction rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }}); @@ -235,8 +224,6 @@ Assembly& Assembly::optimise(bool _enable) *orig = move(*moveIter); iter = m_items.erase(orig, iter); } - if (iter != m_items.end()) - ++iter; } for (unsigned i = 0; i < m_items.size(); ++i) diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index 1d2a71836..cc2fdc8bd 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -32,6 +32,8 @@ using namespace dev::eth; vector CommonSubexpressionEliminator::getOptimizedItems() { + optimizeBreakingItem(); + map initialStackContents; map targetStackContents; int minHeight = m_stackHeight + 1; @@ -45,10 +47,13 @@ vector CommonSubexpressionEliminator::getOptimizedItems() // Debug info: //stream(cout, initialStackContents, targetStackContents); - return CSECodeGenerator(m_expressionClasses, m_storeOperations).generateCode( + AssemblyItems items = CSECodeGenerator(m_expressionClasses, m_storeOperations).generateCode( initialStackContents, targetStackContents ); + if (m_breakingItem) + items.push_back(*m_breakingItem); + return items; } ostream& CommonSubexpressionEliminator::stream( @@ -91,12 +96,12 @@ ostream& CommonSubexpressionEliminator::stream( return _out; } -void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) +void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _copyItem) { if (_item.type() != Operation) { assertThrow(_item.deposit() == 1, InvalidDeposit, ""); - setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {}, false)); + setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {}, _copyItem)); } else { @@ -126,12 +131,38 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) else if (_item.instruction() == Instruction::MLOAD) setStackElement(m_stackHeight + _item.deposit(), loadFromMemory(arguments[0])); else - setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments, false)); + setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments, _copyItem)); } m_stackHeight += _item.deposit(); } } +void CommonSubexpressionEliminator::optimizeBreakingItem() +{ + if (!m_breakingItem || *m_breakingItem != AssemblyItem(Instruction::JUMPI)) + return; + + using Id = ExpressionClasses::Id; + static AssemblyItem s_jump = Instruction::JUMP; + + Id condition = stackElement(m_stackHeight - 1); + Id zero = m_expressionClasses.find(u256(0)); + if (m_expressionClasses.knownToBeDifferent(condition, zero)) + { + feedItem(Instruction::SWAP1, true); + feedItem(Instruction::POP, true); + m_breakingItem = &s_jump; + return; + } + Id negatedCondition = m_expressionClasses.find(Instruction::ISZERO, {condition}); + if (m_expressionClasses.knownToBeDifferent(negatedCondition, zero)) + { + feedItem(Instruction::POP, true); + feedItem(Instruction::POP, true); + m_breakingItem = nullptr; + } +} + void CommonSubexpressionEliminator::setStackElement(int _stackHeight, ExpressionClasses::Id _class) { m_stackElements[_stackHeight] = _class; diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h index 03f8cf987..09368f7d2 100644 --- a/libevmcore/CommonSubexpressionEliminator.h +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -88,7 +88,10 @@ public: private: /// Feeds the item into the system for analysis. - void feedItem(AssemblyItem const& _item); + void feedItem(AssemblyItem const& _item, bool _copyItem = false); + + /// Tries to optimize the item that breaks the basic block at the end. + void optimizeBreakingItem(); /// Simplifies the given item using /// Assigns a new equivalence class to the next sequence number of the given stack element. @@ -130,6 +133,10 @@ private: std::vector m_storeOperations; /// Structure containing the classes of equivalent expressions. ExpressionClasses m_expressionClasses; + + /// The item that breaks the basic block, can be nullptr. + /// It is usually appended to the block but can be optimized in some cases. + AssemblyItem const* m_breakingItem = nullptr; }; /** @@ -225,6 +232,8 @@ _AssemblyItemIterator CommonSubexpressionEliminator::feedItems( { for (; _iterator != _end && !SemanticInformation::breaksBasicBlock(*_iterator); ++_iterator) feedItem(*_iterator); + if (_iterator != _end) + m_breakingItem = &(*_iterator++); return _iterator; } diff --git a/test/SolidityOptimizer.cpp b/test/SolidityOptimizer.cpp index cf35536e6..e69d5120e 100644 --- a/test/SolidityOptimizer.cpp +++ b/test/SolidityOptimizer.cpp @@ -536,6 +536,38 @@ BOOST_AUTO_TEST_CASE(cse_deep_stack) }); } +BOOST_AUTO_TEST_CASE(cse_jumpi_no_jump) +{ + AssemblyItems input{ + u256(0), + u256(1), + Instruction::DUP2, + AssemblyItem(PushTag, 1), + Instruction::JUMPI + }; + checkCSE(input, { + u256(0), + u256(1) + }); +} + +BOOST_AUTO_TEST_CASE(cse_jumpi_jump) +{ + AssemblyItems input{ + u256(1), + u256(1), + Instruction::DUP2, + AssemblyItem(PushTag, 1), + Instruction::JUMPI + }; + checkCSE(input, { + u256(1), + Instruction::DUP1, + AssemblyItem(PushTag, 1), + Instruction::JUMP + }); +} + BOOST_AUTO_TEST_SUITE_END() } From f1fd628653822901b5cb1d8eda0e3be7a46cb08a Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Mon, 30 Mar 2015 12:05:10 +0200 Subject: [PATCH 171/228] BlockChainLoader uses genesisRLP to load blockchain --- libtestutils/BlockChainLoader.cpp | 53 +------------------------------ 1 file changed, 1 insertion(+), 52 deletions(-) diff --git a/libtestutils/BlockChainLoader.cpp b/libtestutils/BlockChainLoader.cpp index eafab190e..898812b2a 100644 --- a/libtestutils/BlockChainLoader.cpp +++ b/libtestutils/BlockChainLoader.cpp @@ -28,57 +28,6 @@ using namespace dev; using namespace dev::test; using namespace dev::eth; -namespace dev -{ -namespace test -{ -dev::eth::BlockInfo toBlockInfo(Json::Value const& _json); -bytes toGenesisBlock(Json::Value const& _json); -} -} - -dev::eth::BlockInfo dev::test::toBlockInfo(Json::Value const& _json) -{ - RLPStream rlpStream; - auto size = _json.getMemberNames().size(); - rlpStream.appendList(_json["hash"].empty() ? size : (size - 1)); - rlpStream << fromHex(_json["parentHash"].asString()); - rlpStream << fromHex(_json["uncleHash"].asString()); - rlpStream << fromHex(_json["coinbase"].asString()); - rlpStream << fromHex(_json["stateRoot"].asString()); - rlpStream << fromHex(_json["transactionsTrie"].asString()); - rlpStream << fromHex(_json["receiptTrie"].asString()); - rlpStream << fromHex(_json["bloom"].asString()); - rlpStream << bigint(_json["difficulty"].asString()); - rlpStream << bigint(_json["number"].asString()); - rlpStream << bigint(_json["gasLimit"].asString()); - rlpStream << bigint(_json["gasUsed"].asString()); - rlpStream << bigint(_json["timestamp"].asString()); - rlpStream << fromHex(_json["extraData"].asString()); - rlpStream << fromHex(_json["mixHash"].asString()); - rlpStream << fromHex(_json["nonce"].asString()); - - BlockInfo result; - RLP rlp(rlpStream.out()); - result.populateFromHeader(rlp, IgnoreNonce); - return result; -} - -bytes dev::test::toGenesisBlock(Json::Value const& _json) -{ - BlockInfo bi = toBlockInfo(_json); - RLPStream rlpStream; - bi.streamRLP(rlpStream, WithNonce); - - RLPStream fullStream(3); - fullStream.appendRaw(rlpStream.out()); - fullStream.appendRaw(RLPEmptyList); - fullStream.appendRaw(RLPEmptyList); - bi.verifyInternals(&fullStream.out()); - - return fullStream.out(); -} - BlockChainLoader::BlockChainLoader(Json::Value const& _json) { // load pre state @@ -86,7 +35,7 @@ BlockChainLoader::BlockChainLoader(Json::Value const& _json) m_state = sl.state(); // load genesisBlock - m_bc.reset(new BlockChain(toGenesisBlock(_json["genesisBlockHeader"]), m_dir.path(), true)); + m_bc.reset(new BlockChain(fromHex(_json["genesisRLP"].asString()), m_dir.path(), true)); // load blocks for (auto const& block: _json["blocks"]) From 5bfff8fcac49c1b4f21a60d02006c2f72bf36465 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 30 Mar 2015 12:39:01 +0200 Subject: [PATCH 172/228] - Fix for #1448, #1447 --- mix/CodeModel.cpp | 1 + mix/ContractCallDataEncoder.cpp | 3 ++- mix/qml/StructView.qml | 1 + mix/qml/TransactionDialog.qml | 7 ++++++- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index 27c79e0a0..a96ab8791 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -342,6 +342,7 @@ SolidityType CodeModel::nodeType(dev::solidity::Type const* _type) r.type = SolidityType::Type::Bytes; r.size = static_cast(b->getNumBytes()); } + break; case Type::Category::Contract: r.type = SolidityType::Type::Address; break; diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 6553244dc..3ca133aab 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -91,6 +91,7 @@ unsigned ContractCallDataEncoder::encodeSingleItem(QVariant const& _data, Solidi { QByteArray bytesAr = src.toLocal8Bit(); result = bytes(bytesAr.begin(), bytesAr.end()); + result = paddedRight(result, alignSize); } else { @@ -101,7 +102,7 @@ unsigned ContractCallDataEncoder::encodeSingleItem(QVariant const& _data, Solidi unsigned dataSize = _type.dynamicSize ? result.size() : alignSize; _dest.insert(_dest.end(), result.begin(), result.end()); - if (_dest.size() % alignSize != 0) + if ((_dest.size() - 4) % alignSize != 0) _dest.resize((_dest.size() & ~(alignSize - 1)) + alignSize); return dataSize; } diff --git a/mix/qml/StructView.qml b/mix/qml/StructView.qml index 045b2eabc..42ec5b046 100644 --- a/mix/qml/StructView.qml +++ b/mix/qml/StructView.qml @@ -44,6 +44,7 @@ Column anchors.verticalCenter: parent.verticalCenter sourceComponent: { + console.log("loading param"); var t = modelData.type.category; if (t === QSolidityType.SignedInteger || t === QSolidityType.UnsignedInteger) return Qt.createComponent("qrc:/qml/QIntTypeView.qml"); diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index 5d98d3dbd..33ab8c5d0 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -81,6 +81,7 @@ Window { loadParameter(params[p]); } } + initTypeLoader(); modalTransactionDialog.setX((Screen.width - width) / 2); modalTransactionDialog.setY((Screen.height - height) / 2); @@ -126,11 +127,15 @@ Window { } } } + initTypeLoader(); + } + + function initTypeLoader() + { typeLoader.value = {} typeLoader.members = [] typeLoader.value = paramValues; typeLoader.members = paramsModel; - } function close() From 446471d56980c0e026d0ce11cb93f9e141b47b3d Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 30 Mar 2015 13:42:48 +0200 Subject: [PATCH 173/228] Explicit instantiation to prevent reference and force copy of shared-ptr. Potential fix for #1366. --- libp2p/Host.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libp2p/Host.h b/libp2p/Host.h index c161f8437..753be3150 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -96,7 +96,7 @@ public: static std::string pocHost(); /// Register a peer-capability; all new peer connections will have this capability. - template std::shared_ptr registerCapability(T* _t) { _t->m_host = this; auto ret = std::shared_ptr(_t); m_capabilities[std::make_pair(T::staticName(), T::staticVersion())] = ret; return ret; } + template std::shared_ptr registerCapability(T* _t) { _t->m_host = this; std::shared_ptr ret(_t); m_capabilities[std::make_pair(T::staticName(), T::staticVersion())] = ret; return ret; } bool haveCapability(CapDesc const& _name) const { return m_capabilities.count(_name) != 0; } CapDescs caps() const { CapDescs ret; for (auto const& i: m_capabilities) ret.push_back(i.first); return ret; } From df4f1258e849744920addadf3e22e256820d0857 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Mon, 30 Mar 2015 14:02:27 +0200 Subject: [PATCH 174/228] failing tests for getting genesis block --- test/ClientBase.cpp | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/test/ClientBase.cpp b/test/ClientBase.cpp index 2197ac830..304182cfc 100644 --- a/test/ClientBase.cpp +++ b/test/ClientBase.cpp @@ -34,34 +34,45 @@ BOOST_AUTO_TEST_CASE(blocks) { enumerateClients([](Json::Value const& _json, dev::eth::ClientBase& _client) -> void { - for (string const& name: _json["postState"].getMemberNames()) + auto compareState = [&_client](Json::Value const& _o, string const& _name, BlockNumber _blockNumber) -> void { - Json::Value o = _json["postState"][name]; - Address address(name); + Address address(_name); // balanceAt - u256 expectedBalance = u256(o["balance"].asString()); - u256 balance = _client.balanceAt(address); + u256 expectedBalance = u256(_o["balance"].asString()); + u256 balance = _client.balanceAt(address, _blockNumber); ETH_CHECK_EQUAL(expectedBalance, balance); // countAt - u256 expectedCount = u256(o["nonce"].asString()); - u256 count = _client.countAt(address); + u256 expectedCount = u256(_o["nonce"].asString()); + u256 count = _client.countAt(address, _blockNumber); ETH_CHECK_EQUAL(expectedCount, count); // stateAt - for (string const& pos: o["storage"].getMemberNames()) + for (string const& pos: _o["storage"].getMemberNames()) { - u256 expectedState = u256(o["storage"][pos].asString()); - u256 state = _client.stateAt(address, u256(pos)); + u256 expectedState = u256(_o["storage"][pos].asString()); + u256 state = _client.stateAt(address, u256(pos), _blockNumber); ETH_CHECK_EQUAL(expectedState, state); } // codeAt - bytes expectedCode = fromHex(o["code"].asString()); - bytes code = _client.codeAt(address); + bytes expectedCode = fromHex(_o["code"].asString()); + bytes code = _client.codeAt(address, _blockNumber); ETH_CHECK_EQUAL_COLLECTIONS(expectedCode.begin(), expectedCode.end(), - code.begin(), code.end()); + code.begin(), code.end()); + }; + + for (string const& name: _json["postState"].getMemberNames()) + { + Json::Value o = _json["postState"][name]; + compareState(o, name, PendingBlock); + } + + for (string const& name: _json["pre"].getMemberNames()) + { + Json::Value o = _json["pre"][name]; + compareState(o, name, 0); } // number From 04d0415e52de572b34cea5be19f85ce6e3b0e987 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 30 Mar 2015 15:14:59 +0200 Subject: [PATCH 175/228] Detect if non-existant parameter is documented with natspec --- libsolidity/InterfaceHandler.cpp | 9 +++++++++ test/SolidityNatspecJSON.cpp | 13 ++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 2f35a96f6..aacbbfd72 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -175,8 +175,17 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin method["author"] = m_author; Json::Value params(Json::objectValue); + std::vector paramNames = it.second->getParameterNames(); for (auto const& pair: m_params) + { + if (find(paramNames.begin(), paramNames.end(), pair.first) == paramNames.end()) + // LTODO: mismatching parameter name, throw some form of warning and not just an exception + BOOST_THROW_EXCEPTION( + DocstringParsingError() << + errinfo_comment("documented parameter \"" + pair.first + "\" not found found in the function") + ); params[pair.first] = pair.second; + } if (!m_params.empty()) method["params"] = params; diff --git a/test/SolidityNatspecJSON.cpp b/test/SolidityNatspecJSON.cpp index edfe89861..4e6c98556 100644 --- a/test/SolidityNatspecJSON.cpp +++ b/test/SolidityNatspecJSON.cpp @@ -176,7 +176,6 @@ BOOST_AUTO_TEST_CASE(dev_and_user_no_doc) "}\n"; char const* devNatspec = "{\"methods\":{}}"; - char const* userNatspec = "{\"methods\":{}}"; checkNatspec(sourceCode, devNatspec, false); @@ -230,6 +229,18 @@ BOOST_AUTO_TEST_CASE(dev_multiple_params) checkNatspec(sourceCode, natspec, false); } +BOOST_AUTO_TEST_CASE(dev_documenting_nonexistant_param) +{ + char const* sourceCode = "contract test {\n" + " /// @dev Multiplies a number by 7 and adds second parameter\n" + " /// @param a Documentation for the first parameter\n" + " /// @param not_existing Documentation for the second parameter\n" + " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" + "}\n"; + + BOOST_CHECK_THROW(checkNatspec(sourceCode, natspec, false), DocstringParsingError); +} + BOOST_AUTO_TEST_CASE(dev_mutiline_param_description) { char const* sourceCode = "contract test {\n" From b0629cb2c56df1bc52f90d9221fda57fd145e3fa Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 30 Mar 2015 15:32:25 +0200 Subject: [PATCH 176/228] no need to have target json for failing tests --- test/SolidityNatspecJSON.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/test/SolidityNatspecJSON.cpp b/test/SolidityNatspecJSON.cpp index 4e6c98556..aeaad1966 100644 --- a/test/SolidityNatspecJSON.cpp +++ b/test/SolidityNatspecJSON.cpp @@ -238,7 +238,7 @@ BOOST_AUTO_TEST_CASE(dev_documenting_nonexistant_param) " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" "}\n"; - BOOST_CHECK_THROW(checkNatspec(sourceCode, natspec, false), DocstringParsingError); + BOOST_CHECK_THROW(checkNatspec(sourceCode, "", false), DocstringParsingError); } BOOST_AUTO_TEST_CASE(dev_mutiline_param_description) @@ -498,17 +498,7 @@ BOOST_AUTO_TEST_CASE(dev_title_at_function_error) " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" "}\n"; - char const* natspec = "{" - " \"author\": \"Lefteris\"," - " \"title\": \"Just a test contract\"," - " \"methods\":{" - " \"mul(uint256,uint256)\":{ \n" - " \"details\": \"Mul function\"\n" - " }\n" - " }\n" - "}"; - - BOOST_CHECK_THROW(checkNatspec(sourceCode, natspec, false), DocstringParsingError); + BOOST_CHECK_THROW(checkNatspec(sourceCode, "", false), DocstringParsingError); } BOOST_AUTO_TEST_CASE(natspec_notice_without_tag) From 7274ac735270a7a65000fdc97de8745dba9490a6 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 30 Mar 2015 16:17:03 +0200 Subject: [PATCH 177/228] Display ascii char in return value. --- mix/ContractCallDataEncoder.cpp | 21 ++++++++++++++++++++- mix/ContractCallDataEncoder.h | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 3ca133aab..b2bc888a9 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -159,7 +159,11 @@ dev::bytes ContractCallDataEncoder::decodeBytes(dev::bytes const& _rawValue) QString ContractCallDataEncoder::toString(dev::bytes const& _b) { - return QString::fromStdString(dev::toJS(_b)); + QString str; + if (isString(_b, str)) + return "- " + str + " - " + QString::fromStdString(dev::toJS(_b)); + else + return QString::fromStdString(dev::toJS(_b)); } @@ -197,3 +201,18 @@ QStringList ContractCallDataEncoder::decode(QList const& } return r; } + + +bool ContractCallDataEncoder::isString(dev::bytes const& _b, QString& _str) +{ + dev::bytes bunPad = unpadded(_b); + for (unsigned i = 0; i < bunPad.size(); i++) + { + u256 value(bunPad.at(i)); + if (value > 127) + return false; + else + _str += QString::fromStdString(dev::toJS(bunPad.at(i))).replace("0x", ""); + } + return true; +} diff --git a/mix/ContractCallDataEncoder.h b/mix/ContractCallDataEncoder.h index 3fa165d3f..178cdcdf8 100644 --- a/mix/ContractCallDataEncoder.h +++ b/mix/ContractCallDataEncoder.h @@ -66,6 +66,7 @@ private: dev::bytes encodeBytes(QString const& _str); dev::bytes decodeBytes(dev::bytes const& _rawValue); QString toString(dev::bytes const& _b); + bool isString(dev::bytes const& _b, QString& _str); private: bytes m_encodedData; From 1855b63a955f0a9db931c87799a06efec8d5d6eb Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 30 Mar 2015 16:18:27 +0200 Subject: [PATCH 178/228] small changes --- mix/qml/StructView.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/mix/qml/StructView.qml b/mix/qml/StructView.qml index 42ec5b046..045b2eabc 100644 --- a/mix/qml/StructView.qml +++ b/mix/qml/StructView.qml @@ -44,7 +44,6 @@ Column anchors.verticalCenter: parent.verticalCenter sourceComponent: { - console.log("loading param"); var t = modelData.type.category; if (t === QSolidityType.SignedInteger || t === QSolidityType.UnsignedInteger) return Qt.createComponent("qrc:/qml/QIntTypeView.qml"); From 9d5c75aabe50e4fb93f7ac910c8efaee573f86f9 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 30 Mar 2015 16:46:14 +0200 Subject: [PATCH 179/228] Redesign of logs pane --- mix/qml/LogsPane.qml | 221 +++++++++++++++++++------------------- mix/qml/LogsPaneStyle.qml | 8 +- mix/qml/StatusPane.qml | 47 +++----- 3 files changed, 130 insertions(+), 146 deletions(-) diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml index b40dfc4c7..73c56d4e9 100644 --- a/mix/qml/LogsPane.qml +++ b/mix/qml/LogsPane.qml @@ -10,19 +10,124 @@ Rectangle function push(_level, _type, _content) { _content = _content.replace(/\n/g, " ") - logsModel.insert(0, { "type": _type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss dd.MM.yyyy"), "content": _content, "level": _level }); + logsModel.insert(0, { "type": _type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": _content, "level": _level }); } anchors.fill: parent radius: 5 color: LogsPaneStyle.generic.layout.backgroundColor - border.color: LogsPaneStyle.generic.layout.borderColor - border.width: LogsPaneStyle.generic.layout.borderWidth ColumnLayout { z: 2 height: parent.height width: parent.width spacing: 0 + + ListModel { + id: logsModel + } + + TableView { + id: logsTable + clip: true + Layout.fillWidth: true + Layout.preferredHeight: parent.height - rowAction.height + headerVisible : false + onDoubleClicked: + { + var log = logsModel.get(logsTable.currentRow); + if (log) + clipboard.text = (log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content); + } + + model: SortFilterProxyModel { + id: proxyModel + source: logsModel + property var roles: ["-", "javascript", "run", "state"] + + Component.onCompleted: { + filterType = regEx(proxyModel.roles); + } + + function search(_value) + { + filterContent = _value; + } + + function toogleFilter(_value) + { + var count = roles.length; + for (var i in roles) + { + if (roles[i] === _value) + { + roles.splice(i, 1); + break; + } + } + if (count === roles.length) + roles.push(_value); + + filterType = regEx(proxyModel.roles); + } + + function regEx(_value) + { + return "(?:" + roles.join('|') + ")"; + } + + filterType: "(?:javascript|run|state)" + filterContent: "" + filterSyntax: SortFilterProxyModel.RegExp + filterCaseSensitivity: Qt.CaseInsensitive + } + TableViewColumn + { + role: "date" + title: qsTr("date") + width: LogsPaneStyle.generic.layout.dateWidth + delegate: itemDelegate + } + TableViewColumn + { + role: "type" + title: qsTr("type") + width: LogsPaneStyle.generic.layout.typeWidth + delegate: itemDelegate + } + TableViewColumn + { + role: "content" + title: qsTr("content") + width: LogsPaneStyle.generic.layout.contentWidth + delegate: itemDelegate + } + + rowDelegate: Item { + Rectangle { + width: logsTable.width - 4 + height: 17 + color: styleData.alternate ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor + } + } + } + + Component { + id: itemDelegate + DefaultLabel { + text: styleData.value; + font.family: LogsPaneStyle.generic.layout.logLabelFont + font.pointSize: Style.absoluteSize(-1) + color: { + if (proxyModel.get(styleData.row).level === "error") + return "red"; + else if (proxyModel.get(styleData.row).level === "warning") + return "orange"; + else + return "#808080"; + } + } + } + Row { id: rowAction @@ -178,7 +283,7 @@ Rectangle } } - DefaultTextField + /*DefaultTextField { id: searchBox height: LogsPaneStyle.generic.layout.headerButtonHeight @@ -190,113 +295,7 @@ Rectangle onTextChanged: { proxyModel.search(text); } - } - } - - ListModel { - id: logsModel - } - - TableView { - id: logsTable - clip: true - Layout.fillWidth: true - Layout.preferredHeight: parent.height - rowAction.height - headerVisible : false - onDoubleClicked: - { - var log = logsModel.get(logsTable.currentRow); - if (log) - clipboard.text = (log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content); - } - - model: SortFilterProxyModel { - id: proxyModel - source: logsModel - property var roles: ["-", "javascript", "run", "state"] - - Component.onCompleted: { - filterType = regEx(proxyModel.roles); - } - - function search(_value) - { - filterContent = _value; - } - - function toogleFilter(_value) - { - var count = roles.length; - for (var i in roles) - { - if (roles[i] === _value) - { - roles.splice(i, 1); - break; - } - } - if (count === roles.length) - roles.push(_value); - - filterType = regEx(proxyModel.roles); - } - - function regEx(_value) - { - return "(?:" + roles.join('|') + ")"; - } - - filterType: "(?:javascript|run|state)" - filterContent: "" - filterSyntax: SortFilterProxyModel.RegExp - filterCaseSensitivity: Qt.CaseInsensitive - } - TableViewColumn - { - role: "date" - title: qsTr("date") - width: LogsPaneStyle.generic.layout.dateWidth - delegate: itemDelegate - } - TableViewColumn - { - role: "type" - title: qsTr("type") - width: LogsPaneStyle.generic.layout.typeWidth - delegate: itemDelegate - } - TableViewColumn - { - role: "content" - title: qsTr("content") - width: LogsPaneStyle.generic.layout.contentWidth - delegate: itemDelegate - } - - rowDelegate: Item { - Rectangle { - width: logsTable.width - 4 - height: 17 - color: styleData.alternate ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor - } - } - } - - Component { - id: itemDelegate - DefaultLabel { - text: styleData.value; - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-1) - color: { - if (proxyModel.get(styleData.row).level === "error") - return "red"; - else if (proxyModel.get(styleData.row).level === "warning") - return "orange"; - else - return "#808080"; - } - } + }*/ } } } diff --git a/mix/qml/LogsPaneStyle.qml b/mix/qml/LogsPaneStyle.qml index 59b80653a..1c5e2f4f5 100644 --- a/mix/qml/LogsPaneStyle.qml +++ b/mix/qml/LogsPaneStyle.qml @@ -11,8 +11,6 @@ QtObject { property QtObject generic: QtObject { property QtObject layout: QtObject { property string backgroundColor: "#f7f7f7" - property string borderColor: "#5391d8" - property int borderWidth: 1 property int headerHeight: 35 property int headerButtonSpacing: 5 property int leftMargin: 10 @@ -20,9 +18,9 @@ QtObject { property string logLabelColor: "#5391d8" property string logLabelFont: "sans serif" property int headerInputWidth: 200 - property int dateWidth: 150 - property int typeWidth: 80 - property int contentWidth: 700 + property int dateWidth: 70 + property int typeWidth: 70 + property int contentWidth: 250 property string logAlternateColor: "#f0f0f0" } } diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index 6d4b5e7e1..151688cea 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -91,6 +91,7 @@ Rectangle { return formatted; } } + Connections { target:projectModel onDeploymentStarted: infoMessage(qsTr("Running deployment..."), "Deployment"); @@ -112,28 +113,9 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter radius: 3 - width: 500 + width: 650 height: 30 color: "#fcfbfc" - states: [ - State { - name: "logsOpened" - PropertyChanges { - target: statusContainer - border.color: "#5391d8" - border.width: 1 - } - }, - State { - name: "logsClosed" - PropertyChanges { - target: statusContainer - border.color: "#5391d8" - border.width: 0 - } - } - ] - Text { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter @@ -215,40 +197,45 @@ Rectangle { function toggle() { if (logsContainer.state === "opened") - { - statusContainer.state = "logsClosed"; logsContainer.state = "closed" - } else { - statusContainer.state = "logsOpened"; logsContainer.state = "opened"; logsContainer.focus = true; + calCoord(); forceActiveFocus(); } } id: logsContainer - width: 1000 - height: 0 + width: 650 + //height: 0 anchors.topMargin: 10 anchors.top: statusContainer.bottom + //anchors.left: statusContainer.left anchors.horizontalCenter: parent.horizontalCenter visible: false radius: 5 Component.onCompleted: + { + calCoord(); + } + + function calCoord() { var top = logsContainer; while (top.parent) top = top.parent - var coordinates = logsContainer.mapToItem(top, 0, 0) + var coordinates = logsContainer.mapToItem(top, 0, 0); logsContainer.parent = top; - logsContainer.x = coordinates.x - logsContainer.y = coordinates.y + //logsContainer.x = coordinates.x + 150; + logsContainer.y = coordinates.y; + console.log(logsContainer.x); } + LogsPane { - id: logPane + id: logPane; } states: [ State { From f3b37e3d8607092a7f03cf772ae8706be6b892c5 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 30 Mar 2015 16:53:33 +0200 Subject: [PATCH 180/228] Fixes #1463 --- libweb3jsonrpc/WebThreeStubServerBase.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index cea1750ac..bac513268 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -79,7 +79,7 @@ static Json::Value toJson(dev::eth::Transaction const& _t) Json::Value res; res["hash"] = toJS(_t.sha3()); res["input"] = toJS(_t.data()); - res["to"] = toJS(_t.receiveAddress()); + res["to"] = _t.isCreation() ? "0x" : toJS(_t.receiveAddress()); res["from"] = toJS(_t.safeSender()); res["gas"] = toJS(_t.gas()); res["gasPrice"] = toJS(_t.gasPrice()); @@ -115,7 +115,7 @@ static Json::Value toJson(dev::eth::BlockInfo const& _bi, UncleHashes const& _us static Json::Value toJson(dev::eth::TransactionSkeleton const& _t) { Json::Value res; - res["to"] = toJS(_t.to); + res["to"] = _t.creation ? "0x" : toJS(_t.to); res["from"] = toJS(_t.from); res["gas"] = toJS(_t.gas); res["gasPrice"] = toJS(_t.gasPrice); @@ -418,7 +418,7 @@ static TransactionSkeleton toTransaction(Json::Value const& _json) if (!_json["from"].empty()) ret.from = jsToAddress(_json["from"].asString()); - if (!_json["to"].empty()) + if (!_json["to"].empty() && _json["to"].asString() != "0x") ret.to = jsToAddress(_json["to"].asString()); else ret.creation = true; From 5062f7190a8e509762b2f60a1fda6a90e363901a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 30 Mar 2015 16:54:10 +0200 Subject: [PATCH 181/228] Fix for vector_ref (yes, crazy that it's been dormant for so long). Fixes to ABI. --- abi/main.cpp | 96 ++++++++++++++++++++++++++++------------- libdevcore/vector_ref.h | 3 +- 2 files changed, 67 insertions(+), 32 deletions(-) diff --git a/abi/main.cpp b/abi/main.cpp index cdae81f1e..c1cd63bc2 100644 --- a/abi/main.cpp +++ b/abi/main.cpp @@ -303,7 +303,7 @@ tuple fromUser(std::string const& _arg, Tristate _prefix type.noteDecimalInput(); return make_tuple(toCompactBigEndian(bigint(val.substr(1))), type, Format::Decimal); } - if (val.substr(0, 1) == "@") + if (val.substr(0, 1) == "'") { type.noteBinaryInput(); return make_tuple(asBytes(val.substr(1)), type, Format::Binary); @@ -335,6 +335,7 @@ tuple fromUser(std::string const& _arg, Tristate _prefix throw InvalidUserString(); } +struct ExpectedAdditionalParameter: public Exception {}; struct ExpectedOpen: public Exception {}; struct ExpectedClose: public Exception {}; @@ -394,10 +395,10 @@ struct ABIMethod ss << "constant "; if (!outs.empty()) { - ss << "returns ("; + ss << "returns("; f = 0; for (ABIType const& i: outs) - ss << (f ? ", " : "") << i.canon() << " " << i.name; + ss << (f++ ? ", " : "") << i.canon() << " " << i.name; ss << ")"; } return ss.str(); @@ -417,11 +418,15 @@ struct ABIMethod for (ABIType const& a: ins) { + if (pi >= _params.size()) + throw ExpectedAdditionalParameter(); auto put = [&]() { if (a.isBytes()) ret += h256(u256(_params[pi].first.size())).asBytes(); suffix += a.unrender(_params[pi].first, _params[pi].second); pi++; + if (pi >= _params.size()) + throw ExpectedAdditionalParameter(); }; function, unsigned)> putDim = [&](vector addr, unsigned q) { if (addr.size() == a.dims.size()) @@ -430,13 +435,14 @@ struct ABIMethod { if (_params[pi].second != Format::Open) throw ExpectedOpen(); + ++pi; int l = a.dims[addr.size()]; if (l == -1) { // read ahead in params and discover the arity. unsigned depth = 0; l = 0; - for (unsigned pi2 = pi + 1; depth || _params[pi2].second != Format::Close;) + for (unsigned pi2 = pi; depth || _params[pi2].second != Format::Close;) { if (_params[pi2].second == Format::Open) ++depth; @@ -454,6 +460,7 @@ struct ABIMethod putDim(addr, q); if (_params[pi].second != Format::Close) throw ExpectedClose(); + ++pi; } }; putDim(vector(), 1); @@ -463,8 +470,6 @@ struct ABIMethod string decode(bytes const& _data, int _index, EncodingPrefs _ep) { stringstream out; - if (_index == -1) - out << "["; unsigned di = 0; vector catDims; for (ABIType const& a: outs) @@ -481,7 +486,6 @@ struct ABIMethod put(); else { - out << "["; int l = a.dims[addr.size()]; if (l == -1) { @@ -492,7 +496,6 @@ struct ABIMethod q *= l; for (addr.push_back(0); addr.back() < l; ++addr.back()) putDim(addr, q); - out << "]"; } }; putDim(vector(), 1); @@ -500,12 +503,20 @@ struct ABIMethod unsigned d = 0; for (ABIType const& a: outs) { + if (_index == -1 && out.tellp() > 0) + out << ", "; auto put = [&]() { - unsigned l = 32; if (a.isBytes()) - l = (catDims[d++] + 31 / 32) * 32; - out << a.render(bytesConstRef(&_data).cropped(di, l).toBytes(), _ep) << ", "; - di += l; + { + out << a.render(bytesConstRef(&_data).cropped(di, catDims[d]).toBytes(), _ep); + di += ((catDims[d] + 31) / 32) * 32; + d++; + } + else + { + out << a.render(bytesConstRef(&_data).cropped(di, 32).toBytes(), _ep); + di += 32; + } }; function)> putDim = [&](vector addr) { if (addr.size() == a.dims.size()) @@ -527,22 +538,22 @@ struct ABIMethod } }; putDim(vector()); - if (_index == -1) - out << ", "; } - (void)_data; - if (_index == -1) - out << "]"; return out.str(); } }; string canonSig(string const& _name, vector const& _args) { - string methodArgs; - for (auto const& arg: _args) - methodArgs += (methodArgs.empty() ? "" : ",") + arg.canon(); - return _name + "(" + methodArgs + ")"; + try { + string methodArgs; + for (auto const& arg: _args) + methodArgs += (methodArgs.empty() ? "" : ",") + arg.canon(); + return _name + "(" + methodArgs + ")"; + } + catch (...) { + return string(); + } } struct UnknownMethod: public Exception {}; @@ -632,6 +643,7 @@ int main(int argc, char** argv) int outputIndex = -1; vector> params; vector args; + string incoming; for (int i = 1; i < argc; ++i) { @@ -656,8 +668,6 @@ int main(int argc, char** argv) typePrefix = Tristate::True; else if (arg == "-T" || arg == "--no-typing") typePrefix = Tristate::False; - else if (arg == "-v" || arg == "--verbose") - verbose = true; else if (arg == "-x" || arg == "--hex") prefs.e = Encoding::Hex; else if (arg == "-d" || arg == "--decimal" || arg == "--dec") @@ -665,21 +675,23 @@ int main(int argc, char** argv) else if (arg == "-b" || arg == "--binary" || arg == "--bin") prefs.e = Encoding::Binary; else if (arg == "-v" || arg == "--verbose") - version(); + verbose = true; else if (arg == "-V" || arg == "--version") version(); else if (method.empty()) method = arg; - else + else if (mode == Mode::Encode) { auto u = fromUser(arg, formatPrefix, typePrefix); args.push_back(get<1>(u)); params.push_back(make_pair(get<0>(u), get<2>(u))); } + else if (mode == Mode::Decode) + incoming += arg; } string abiData; - if (abiFile.empty()) + if (!abiFile.empty()) abiData = contentsString(abiFile); if (mode == Mode::Encode) @@ -695,13 +707,30 @@ int main(int argc, char** argv) try { m = abi.method(method, args); } - catch(...) + catch (...) { cerr << "Unknown method in ABI." << endl; exit(-1); } } - userOutput(cout, m.encode(params), encoding); + try { + userOutput(cout, m.encode(params), encoding); + } + catch (ExpectedAdditionalParameter const&) + { + cerr << "Expected additional parameter in input." << endl; + exit(-1); + } + catch (ExpectedOpen const&) + { + cerr << "Expected open-bracket '[' in input." << endl; + exit(-1); + } + catch (ExpectedClose const&) + { + cerr << "Expected close-bracket ']' in input." << endl; + exit(-1); + } } else if (mode == Mode::Decode) { @@ -725,9 +754,14 @@ int main(int argc, char** argv) exit(-1); } string encoded; - for (int i = cin.get(); i != -1; i = cin.get()) - encoded.push_back((char)i); - cout << m.decode(fromHex(encoded), outputIndex, prefs); + if (incoming == "--" || incoming.empty()) + for (int i = cin.get(); i != -1; i = cin.get()) + encoded.push_back((char)i); + else + { + encoded = contentsString(incoming); + } + cout << m.decode(fromHex(boost::trim_copy(encoded)), outputIndex, prefs) << endl; } } diff --git a/libdevcore/vector_ref.h b/libdevcore/vector_ref.h index 4ecc1cbd1..5e9bba3e8 100644 --- a/libdevcore/vector_ref.h +++ b/libdevcore/vector_ref.h @@ -39,7 +39,8 @@ public: size_t size() const { return m_count; } bool empty() const { return !m_count; } vector_ref<_T> next() const { return vector_ref<_T>(m_data + m_count, m_count); } - vector_ref<_T> cropped(size_t _begin, size_t _count = ~size_t(0)) const { if (m_data && _begin + std::max(size_t(0), _count) <= m_count) return vector_ref<_T>(m_data + _begin, _count == ~size_t(0) ? m_count - _begin : _count); else return vector_ref<_T>(); } + vector_ref<_T> cropped(size_t _begin, size_t _count) const { if (m_data && _begin + _count <= m_count) return vector_ref<_T>(m_data + _begin, _count == ~size_t(0) ? m_count - _begin : _count); else return vector_ref<_T>(); } + vector_ref<_T> cropped(size_t _begin) const { if (m_data && _begin <= m_count) return vector_ref<_T>(m_data + _begin, m_count - _begin); else return vector_ref<_T>(); } void retarget(_T* _d, size_t _s) { m_data = _d; m_count = _s; } void retarget(std::vector<_T> const& _t) { m_data = _t.data(); m_count = _t.size(); } void copyTo(vector_ref::type> _t) const { memcpy(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); } From 348c18941b7c32135536d3c08f5ffa8eef247320 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 30 Mar 2015 17:10:05 +0200 Subject: [PATCH 182/228] "to" now null for describing contract-creation transactions. --- libweb3jsonrpc/WebThreeStubServerBase.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index bac513268..cab986364 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -79,7 +79,7 @@ static Json::Value toJson(dev::eth::Transaction const& _t) Json::Value res; res["hash"] = toJS(_t.sha3()); res["input"] = toJS(_t.data()); - res["to"] = _t.isCreation() ? "0x" : toJS(_t.receiveAddress()); + res["to"] = _t.isCreation() ? Json::Value() : toJS(_t.receiveAddress()); res["from"] = toJS(_t.safeSender()); res["gas"] = toJS(_t.gas()); res["gasPrice"] = toJS(_t.gasPrice()); @@ -115,7 +115,7 @@ static Json::Value toJson(dev::eth::BlockInfo const& _bi, UncleHashes const& _us static Json::Value toJson(dev::eth::TransactionSkeleton const& _t) { Json::Value res; - res["to"] = _t.creation ? "0x" : toJS(_t.to); + res["to"] = _t.creation ? Json::Value() : toJS(_t.to); res["from"] = toJS(_t.from); res["gas"] = toJS(_t.gas); res["gasPrice"] = toJS(_t.gasPrice); From 29669c16a2dfcac6dd01f8ea5cde95dadd82b3e9 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 30 Mar 2015 17:18:38 +0200 Subject: [PATCH 183/228] Fix some styling issues and remove comments --- libsolidity/AST.cpp | 18 ++---------------- test/SolidityNameAndTypeResolution.cpp | 6 +++--- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 2ada73627..db72ea65a 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -136,34 +136,20 @@ void ContractDefinition::checkAbstractFunctions() // Search from base to derived for (ContractDefinition const* contract: boost::adaptors::reverse(getLinearizedBaseContracts())) - { for (ASTPointer const& function: contract->getDefinedFunctions()) { string const& name = function->getName(); if (!function->isFullyImplemented() && functions.count(name) && functions[name]) BOOST_THROW_EXCEPTION(function->createTypeError("Redeclaring an already implemented function as abstract")); - // if (functions.count(name) && !functions[name] && function->isFullyImplemented()) - // functions.insert(make_pair(name, true); - - // if (functions.count(name) && !functions[name] && function->isFullyImplemented) - // functions.insert(make_pair(name, true)); - - // functions.insert(make_pair(name, function->isFullyImplemented())); functions[name] = function->isFullyImplemented(); - - // if (function->isFullyImplemented()) - // full_functions.insert(make_pair(name, function.get())); - // else - // abs_functions.insert(make_pair(name, function.get())); } - } + for (auto const& it: functions) if (!it.second) { setFullyImplemented(false); break; } - } void ContractDefinition::checkIllegalOverrides() const @@ -682,7 +668,7 @@ void NewExpression::checkTypeRequirements() if (!m_contract) BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract.")); if (!m_contract->isFullyImplemented()) - BOOST_THROW_EXCEPTION(m_contract->createTypeError("Trying to create an object of an abstract contract.")); + BOOST_THROW_EXCEPTION(m_contract->createTypeError("Trying to create an instance of an abstract contract.")); shared_ptr contractType = make_shared(*m_contract); TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes(); m_type = make_shared(parameterTypes, TypePointers{contractType}, diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 8eb83b117..a1f780134 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -384,10 +384,10 @@ BOOST_AUTO_TEST_CASE(create_abstract_contract) ASTPointer sourceUnit; char const* text = R"( contract base { function foo(); } - contract derived { + contract derived { base b; - function foo() { b = new base();} - } + function foo() { b = new base();} + } )"; BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); } From 2a57a78f6480192a31d1a34da50e5fb94a2e9c03 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 30 Mar 2015 17:29:08 +0200 Subject: [PATCH 184/228] changes on encode input parameters. --- mix/ContractCallDataEncoder.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index b2bc888a9..7a48263a0 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -80,24 +80,27 @@ unsigned ContractCallDataEncoder::encodeSingleItem(QVariant const& _data, Solidi if ((src.startsWith("\"") && src.endsWith("\"")) || (src.startsWith("\'") && src.endsWith("\'"))) src = src.remove(src.length() - 1, 1).remove(0, 1); - QRegExp rx("[a-z]+"); if (src.startsWith("0x")) { result = fromHex(src.toStdString().substr(2)); if (_type.type != SolidityType::Type::Bytes) result = padded(result, alignSize); } - else if (rx.indexIn(src.toLower(), 0) != -1) - { - QByteArray bytesAr = src.toLocal8Bit(); - result = bytes(bytesAr.begin(), bytesAr.end()); - result = paddedRight(result, alignSize); - } else { - bigint i(src.toStdString()); - result = bytes(alignSize); - toBigEndian((u256)i, result); + try + { + bigint i(src.toStdString()); + result = bytes(alignSize); + toBigEndian((u256)i, result); + } + catch (std::exception const& ex) + { + // manage input as a string. + QByteArray bytesAr = src.toLocal8Bit(); + result = bytes(bytesAr.begin(), bytesAr.end()); + result = paddedRight(result, alignSize); + } } unsigned dataSize = _type.dynamicSize ? result.size() : alignSize; @@ -161,7 +164,7 @@ QString ContractCallDataEncoder::toString(dev::bytes const& _b) { QString str; if (isString(_b, str)) - return "- " + str + " - " + QString::fromStdString(dev::toJS(_b)); + return "\"" + str + "\" " + QString::fromStdString(dev::toJS(_b)); else return QString::fromStdString(dev::toJS(_b)); } @@ -208,8 +211,7 @@ bool ContractCallDataEncoder::isString(dev::bytes const& _b, QString& _str) dev::bytes bunPad = unpadded(_b); for (unsigned i = 0; i < bunPad.size(); i++) { - u256 value(bunPad.at(i)); - if (value > 127) + if (bunPad.at(i) < 9 || bunPad.at(i) > 127) return false; else _str += QString::fromStdString(dev::toJS(bunPad.at(i))).replace("0x", ""); From ad1e8454735ccf0a01c1f3efe40441b1704de743 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 30 Mar 2015 17:34:05 +0200 Subject: [PATCH 185/228] small changes --- mix/ContractCallDataEncoder.cpp | 4 ++-- mix/ContractCallDataEncoder.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 7a48263a0..f1283f433 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -163,7 +163,7 @@ dev::bytes ContractCallDataEncoder::decodeBytes(dev::bytes const& _rawValue) QString ContractCallDataEncoder::toString(dev::bytes const& _b) { QString str; - if (isString(_b, str)) + if (asString(_b, str)) return "\"" + str + "\" " + QString::fromStdString(dev::toJS(_b)); else return QString::fromStdString(dev::toJS(_b)); @@ -206,7 +206,7 @@ QStringList ContractCallDataEncoder::decode(QList const& } -bool ContractCallDataEncoder::isString(dev::bytes const& _b, QString& _str) +bool ContractCallDataEncoder::asString(dev::bytes const& _b, QString& _str) { dev::bytes bunPad = unpadded(_b); for (unsigned i = 0; i < bunPad.size(); i++) diff --git a/mix/ContractCallDataEncoder.h b/mix/ContractCallDataEncoder.h index 178cdcdf8..e225158c7 100644 --- a/mix/ContractCallDataEncoder.h +++ b/mix/ContractCallDataEncoder.h @@ -66,7 +66,7 @@ private: dev::bytes encodeBytes(QString const& _str); dev::bytes decodeBytes(dev::bytes const& _rawValue); QString toString(dev::bytes const& _b); - bool isString(dev::bytes const& _b, QString& _str); + bool asString(dev::bytes const& _b, QString& _str); private: bytes m_encodedData; From 26ca96af0e9f9fddfd6553696bd43ca3b24a8731 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 30 Mar 2015 17:34:51 +0200 Subject: [PATCH 186/228] Last little fix for ABI. Should work ok now. --- abi/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/abi/main.cpp b/abi/main.cpp index c1cd63bc2..80742fcd2 100644 --- a/abi/main.cpp +++ b/abi/main.cpp @@ -264,12 +264,12 @@ struct ABIType { bytes ret = _b; while (ret.size() < _length) - if (_f == Format::Binary) + if (base == Base::Bytes || (base == Base::Unknown && _f == Format::Binary)) ret.push_back(0); else ret.insert(ret.begin(), 0); while (ret.size() > _length) - if (_f == Format::Binary) + if (base == Base::Bytes || (base == Base::Unknown && _f == Format::Binary)) ret.pop_back(); else ret.erase(ret.begin()); From ae6bae4a6cd589197548abef3ae6c3c924bffecb Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 30 Mar 2015 19:31:57 +0200 Subject: [PATCH 187/228] Refactor: Pull out array index access. --- libsolidity/ArrayUtils.cpp | 104 ++++++++++++++++++++++++++++ libsolidity/ArrayUtils.h | 6 ++ libsolidity/ExpressionCompiler.cpp | 107 ++--------------------------- 3 files changed, 117 insertions(+), 100 deletions(-) diff --git a/libsolidity/ArrayUtils.cpp b/libsolidity/ArrayUtils.cpp index bbf7f985a..58031390f 100644 --- a/libsolidity/ArrayUtils.cpp +++ b/libsolidity/ArrayUtils.cpp @@ -440,6 +440,110 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const } } +void ArrayUtils::accessIndex(ArrayType const& _arrayType) const +{ + ArrayType::Location location = _arrayType.getLocation(); + eth::Instruction load = + location == ArrayType::Location::Storage ? eth::Instruction::SLOAD : + location == ArrayType::Location::Memory ? eth::Instruction::MLOAD : + eth::Instruction::CALLDATALOAD; + + // retrieve length + if (!_arrayType.isDynamicallySized()) + m_context << _arrayType.getLength(); + else if (location == ArrayType::Location::CallData) + // length is stored on the stack + m_context << eth::Instruction::SWAP1; + else + m_context << eth::Instruction::DUP2 << load; + // stack: + // check out-of-bounds access + m_context << eth::Instruction::DUP2 << eth::Instruction::LT; + eth::AssemblyItem legalAccess = m_context.appendConditionalJump(); + // out-of-bounds access throws exception (just STOP for now) + m_context << eth::Instruction::STOP; + + m_context << legalAccess; + // stack: + if (_arrayType.isByteArray()) + switch (location) + { + case ArrayType::Location::Storage: + // byte array index storage lvalue on stack (goal): + // = + m_context << u256(32) << eth::Instruction::SWAP2; + CompilerUtils(m_context).computeHashStatic(); + // stack: 32 index data_ref + m_context + << eth::Instruction::DUP3 << eth::Instruction::DUP3 + << eth::Instruction::DIV << eth::Instruction::ADD + // stack: 32 index (data_ref + index / 32) + << eth::Instruction::SWAP2 << eth::Instruction::SWAP1 + << eth::Instruction::MOD; + break; + case ArrayType::Location::CallData: + // no lvalue, just retrieve the value + m_context + << eth::Instruction::ADD << eth::Instruction::CALLDATALOAD + << ((u256(0xff) << (256 - 8))) << eth::Instruction::AND; + break; + case ArrayType::Location::Memory: + solAssert(false, "Memory lvalues not yet implemented."); + } + else + { + // stack: + m_context << eth::Instruction::SWAP1; + if (_arrayType.isDynamicallySized()) + { + if (location == ArrayType::Location::Storage) + CompilerUtils(m_context).computeHashStatic(); + else if (location == ArrayType::Location::Memory) + m_context << u256(32) << eth::Instruction::ADD; + } + // stack: + switch (location) + { + case ArrayType::Location::CallData: + m_context + << eth::Instruction::SWAP1 << _arrayType.getBaseType()->getCalldataEncodedSize() + << eth::Instruction::MUL << eth::Instruction::ADD; + if (_arrayType.getBaseType()->isValueType()) + CompilerUtils(m_context).loadFromMemoryDynamic(*_arrayType.getBaseType(), true, true, false); + break; + case ArrayType::Location::Storage: + m_context << eth::Instruction::SWAP1; + if (_arrayType.getBaseType()->getStorageBytes() <= 16) + { + // stack: + // goal: + // = <(index % itemsPerSlot) * byteSize> + unsigned byteSize = _arrayType.getBaseType()->getStorageBytes(); + solAssert(byteSize != 0, ""); + unsigned itemsPerSlot = 32 / byteSize; + m_context << u256(itemsPerSlot) << eth::Instruction::SWAP2; + // stack: itemsPerSlot index data_ref + m_context + << eth::Instruction::DUP3 << eth::Instruction::DUP3 + << eth::Instruction::DIV << eth::Instruction::ADD + // stack: itemsPerSlot index (data_ref + index / itemsPerSlot) + << eth::Instruction::SWAP2 << eth::Instruction::SWAP1 + << eth::Instruction::MOD + << u256(byteSize) << eth::Instruction::MUL; + } + else + { + if (_arrayType.getBaseType()->getStorageSize() != 1) + m_context << _arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL; + m_context << eth::Instruction::ADD << u256(0); + } + break; + case ArrayType::Location::Memory: + solAssert(false, "Memory lvalues not yet implemented."); + } + } +} + void ArrayUtils::incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPosition, unsigned _storageOffsetPosition) const { solAssert(_byteSize < 32, ""); diff --git a/libsolidity/ArrayUtils.h b/libsolidity/ArrayUtils.h index 22c0646a5..dab40e2d6 100644 --- a/libsolidity/ArrayUtils.h +++ b/libsolidity/ArrayUtils.h @@ -70,6 +70,12 @@ public: /// Stack pre: reference (excludes byte offset for dynamic storage arrays) /// Stack post: reference length void retrieveLength(ArrayType const& _arrayType) const; + /// Retrieves the value at a specific index. If the location is storage, only retrieves the + /// position. + /// Stack pre: reference [length] index + /// Stack post for storage: slot byte_offset + /// Stack post for calldata: value + void accessIndex(ArrayType const& _arrayType) const; private: /// Adds the given number of bytes to a storage byte offset counter and also increments diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 90568767b..daea21623 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -755,113 +755,20 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) { ArrayType const& arrayType = dynamic_cast(baseType); solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); - ArrayType::Location location = arrayType.getLocation(); - eth::Instruction load = - location == ArrayType::Location::Storage ? eth::Instruction::SLOAD : - location == ArrayType::Location::Memory ? eth::Instruction::MLOAD : - eth::Instruction::CALLDATALOAD; // remove storage byte offset - if (location == ArrayType::Location::Storage) + if (arrayType.getLocation() == ArrayType::Location::Storage) m_context << eth::Instruction::POP; - // stack layout: [] _indexAccess.getIndexExpression()->accept(*this); - // retrieve length - if (!arrayType.isDynamicallySized()) - m_context << arrayType.getLength(); - else if (location == ArrayType::Location::CallData) - // length is stored on the stack - m_context << eth::Instruction::SWAP1; - else - m_context << eth::Instruction::DUP2 << load; - // stack: - // check out-of-bounds access - m_context << eth::Instruction::DUP2 << eth::Instruction::LT; - eth::AssemblyItem legalAccess = m_context.appendConditionalJump(); - // out-of-bounds access throws exception (just STOP for now) - m_context << eth::Instruction::STOP; - - m_context << legalAccess; - // stack: - if (arrayType.isByteArray()) - switch (location) - { - case ArrayType::Location::Storage: - // byte array index storage lvalue on stack (goal): - // = - m_context << u256(32) << eth::Instruction::SWAP2; - CompilerUtils(m_context).computeHashStatic(); - // stack: 32 index data_ref - m_context - << eth::Instruction::DUP3 << eth::Instruction::DUP3 - << eth::Instruction::DIV << eth::Instruction::ADD - // stack: 32 index (data_ref + index / 32) - << eth::Instruction::SWAP2 << eth::Instruction::SWAP1 - << eth::Instruction::MOD; - setLValue(_indexAccess); - break; - case ArrayType::Location::CallData: - // no lvalue, just retrieve the value - m_context - << eth::Instruction::ADD << eth::Instruction::CALLDATALOAD - << ((u256(0xff) << (256 - 8))) << eth::Instruction::AND; - break; - case ArrayType::Location::Memory: - solAssert(false, "Memory lvalues not yet implemented."); - } - else + // stack layout: [] + ArrayUtils(m_context).accessIndex(arrayType); + if (arrayType.getLocation() == ArrayType::Location::Storage) { - // stack: - m_context << eth::Instruction::SWAP1; - if (arrayType.isDynamicallySized()) - { - if (location == ArrayType::Location::Storage) - CompilerUtils(m_context).computeHashStatic(); - else if (location == ArrayType::Location::Memory) - m_context << u256(32) << eth::Instruction::ADD; - } - // stack: - switch (location) - { - case ArrayType::Location::CallData: - m_context - << eth::Instruction::SWAP1 << arrayType.getBaseType()->getCalldataEncodedSize() - << eth::Instruction::MUL << eth::Instruction::ADD; - if (arrayType.getBaseType()->isValueType()) - CompilerUtils(m_context).loadFromMemoryDynamic(*arrayType.getBaseType(), true, true, false); - break; - case ArrayType::Location::Storage: - m_context << eth::Instruction::SWAP1; - if (arrayType.getBaseType()->getStorageBytes() <= 16) - { - // stack: - // goal: - // = <(index % itemsPerSlot) * byteSize> - unsigned byteSize = arrayType.getBaseType()->getStorageBytes(); - solAssert(byteSize != 0, ""); - unsigned itemsPerSlot = 32 / byteSize; - m_context << u256(itemsPerSlot) << eth::Instruction::SWAP2; - // stack: itemsPerSlot index data_ref - m_context - << eth::Instruction::DUP3 << eth::Instruction::DUP3 - << eth::Instruction::DIV << eth::Instruction::ADD - // stack: itemsPerSlot index (data_ref + index / itemsPerSlot) - << eth::Instruction::SWAP2 << eth::Instruction::SWAP1 - << eth::Instruction::MOD - << u256(byteSize) << eth::Instruction::MUL; - } - else - { - if (arrayType.getBaseType()->getStorageSize() != 1) - m_context << arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL; - m_context << eth::Instruction::ADD << u256(0); - } + if (arrayType.isByteArray()) + setLValue(_indexAccess); + else setLValueToStorageItem(_indexAccess); - break; - case ArrayType::Location::Memory: - solAssert(false, "Memory lvalues not yet implemented."); - } } } else From 13d77a34e50db29913df9af8a4ffc125155c16ac Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 31 Mar 2015 09:20:36 +0200 Subject: [PATCH 188/228] Avoid asserting until I know exactly what ethash_quick_check_difficulty is meant to do. --- libethcore/Ethasher.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/libethcore/Ethasher.cpp b/libethcore/Ethasher.cpp index 79a2fdea3..880bc139a 100644 --- a/libethcore/Ethasher.cpp +++ b/libethcore/Ethasher.cpp @@ -112,17 +112,24 @@ bool Ethasher::verify(BlockInfo const& _header) h256 boundary = u256((bigint(1) << 256) / _header.difficulty); - bool ret = ethash_quick_check_difficulty( + bool quick = ethash_quick_check_difficulty( _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_header.nonce, _header.mixHash.data(), boundary.data()); -#if ETH_DEBUG +#if !ETH_DEBUG + if (!quick) + return false; +#endif + auto result = eval(_header); - if ((result.value <= boundary && result.mixHash == _header.mixHash) != ret) + bool slow = result.value <= boundary && result.mixHash == _header.mixHash; + +#if ETH_DEBUG + if (!quick && slow) { - cwarn << "Assertion failure coming: evaluated result gives different outcome to ethash_quick_check_difficulty"; + cwarn << "WARNING: evaluated result gives true whereas ethash_quick_check_difficulty gives false."; cwarn << "headerHash:" << _header.headerHash(WithoutNonce); cwarn << "nonce:" << _header.nonce; cwarn << "mixHash:" << _header.mixHash; @@ -131,12 +138,9 @@ bool Ethasher::verify(BlockInfo const& _header) cwarn << "result.value:" << result.value; cwarn << "result.mixHash:" << result.mixHash; } - assert((result.value <= boundary) == ret); - if (result.value <= boundary) - assert(result.mixHash == _header.mixHash); #endif - return ret; + return slow; } Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce) From b28a600b33a0d9342ab03ee7c59cc7c5c6d8ee78 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 31 Mar 2015 09:50:04 +0200 Subject: [PATCH 189/228] Fixes #1470 --- libwhisper/Message.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libwhisper/Message.cpp b/libwhisper/Message.cpp index 07bcea0c1..4375e0727 100644 --- a/libwhisper/Message.cpp +++ b/libwhisper/Message.cpp @@ -58,10 +58,10 @@ Message::Message(Envelope const& _e, FullTopic const& _fk, Secret const& _s) // get key from decrypted topic key: just xor h256 tk = h256(bytesConstRef(&(_e.data())).cropped(32 * topicIndex, 32)); bytesConstRef cipherText = bytesConstRef(&(_e.data())).cropped(32 * _e.topic().size()); - cnote << "Decrypting(" << topicIndex << "): " << topicSecret << tk << (topicSecret ^ tk) << toHex(cipherText); +// cdebug << "Decrypting(" << topicIndex << "): " << topicSecret << tk << (topicSecret ^ tk) << toHex(cipherText); if (!decryptSym(topicSecret ^ tk, cipherText, b)) return; - cnote << "Got: " << toHex(b); +// cdebug << "Got: " << toHex(b); } if (populate(b)) From 62b4958952f084fbb45061a5dc6d11b0c6a3e753 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 31 Mar 2015 10:07:13 +0200 Subject: [PATCH 190/228] Provide a little more information on why things might fail. --- alethzero/Transact.cpp | 2 +- libethcore/Params.h | 6 +++--- libethereum/Executive.cpp | 5 ++++- libethereum/Executive.h | 2 ++ libethereum/Transaction.cpp | 8 ++++---- libethereum/Transaction.h | 27 ++++++++++++++++++++------- 6 files changed, 34 insertions(+), 16 deletions(-) diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index 6f52548c7..1ff3427e3 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -338,7 +338,7 @@ void Transact::rejigData() } if (er.codeDeposit == CodeDeposit::Failed) { - bail("
ERROR Code deposit failed due to insufficient gas
"); + bail("
ERROR Code deposit failed due to insufficient gas; " + QString::fromStdString(toString(er.gasForDeposit)) + " GAS < " + QString::fromStdString(toString(er.depositSize)) + " bytes * " + QString::fromStdString(toString(c_createDataGas)) + "GAS/byte
"); return; } diff --git a/libethcore/Params.h b/libethcore/Params.h index 7520e49f1..62cf6b2d8 100644 --- a/libethcore/Params.h +++ b/libethcore/Params.h @@ -29,15 +29,15 @@ namespace eth { //--- BEGIN: AUTOGENERATED FROM /feeStructure.json -extern u256 const c_genesisDifficulty; -extern u256 const c_maximumExtraDataSize; -extern u256 const c_epochDuration; extern u256 const c_genesisGasLimit; extern u256 const c_minGasLimit; extern u256 const c_gasLimitBoundDivisor; +extern u256 const c_genesisDifficulty; extern u256 const c_minimumDifficulty; extern u256 const c_difficultyBoundDivisor; extern u256 const c_durationLimit; +extern u256 const c_maximumExtraDataSize; +extern u256 const c_epochDuration; extern u256 const c_stackLimit; extern u256 const c_tierStepGas[8]; ///< Once per operation, for a selection of them. diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index dfd526bef..0b75a07af 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -46,7 +46,7 @@ u256 Executive::gasUsed() const ExecutionResult Executive::executionResult() const { - return ExecutionResult(gasUsed(), m_excepted, m_newAddress, m_out, m_codeDeposit, m_ext ? m_ext->sub.refunds : 0); + return ExecutionResult(gasUsed(), m_excepted, m_newAddress, m_out, m_codeDeposit, m_ext ? m_ext->sub.refunds : 0, m_depositSize, m_gasForDeposit); } void Executive::accrueSubState(SubState& _parentContext) @@ -221,6 +221,8 @@ bool Executive::go(OnOpFunc const& _onOp) if (m_isCreation) { + m_gasForDeposit = m_endGas; + m_depositSize = m_out.size(); if (m_out.size() * c_createDataGas <= m_endGas) { m_codeDeposit = CodeDeposit::Success; @@ -228,6 +230,7 @@ bool Executive::go(OnOpFunc const& _onOp) } else { + m_codeDeposit = CodeDeposit::Failed; m_out.reset(); } diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 3445ad407..158e86330 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -128,6 +128,8 @@ private: unsigned m_depth = 0; ///< The context's call-depth. bool m_isCreation = false; ///< True if the transaction creates a contract, or if create() is called. + unsigned m_depositSize = 0; ///< Amount of code of the creation's attempted deposit. + u256 m_gasForDeposit; ///< Amount of gas remaining for the code deposit phase. CodeDeposit m_codeDeposit = CodeDeposit::None; ///< True if an attempted deposit failed due to lack of gas. TransactionException m_excepted = TransactionException::None; ///< Details if the VM's execution resulted in an exception. u256 m_endGas; ///< The final amount of gas for the transaction. diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index 502f089fb..3228eca70 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -88,12 +88,12 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig) } catch (Exception& _e) { - _e << errinfo_name("invalid transaction format") << BadFieldError(field,toHex(rlp[field].data().toBytes())); + _e << errinfo_name("invalid transaction format") << BadFieldError(field, toHex(rlp[field].data().toBytes())); throw; } } -Address Transaction::safeSender() const noexcept +Address const& Transaction::safeSender() const noexcept { try { @@ -102,11 +102,11 @@ Address Transaction::safeSender() const noexcept catch (...) { cwarn << "safeSender() did throw an exception: " << boost::current_exception_diagnostic_information(); - return Address(); + return NullAddress; } } -Address Transaction::sender() const +Address const& Transaction::sender() const { if (!m_sender) { diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index bed291868..ab2c12b50 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -75,17 +75,30 @@ TransactionException toTransactionException(VMException const& _e); struct ExecutionResult { ExecutionResult() = default; - ExecutionResult(u256 _gasUsed, TransactionException _excepted, Address _newAddress, bytesConstRef _output, CodeDeposit _codeDeposit, u256 _gasRefund): gasUsed(_gasUsed), excepted(_excepted), newAddress(_newAddress), output(_output.toBytes()), codeDeposit(_codeDeposit), gasRefunded(_gasRefund) {} + ExecutionResult(u256 const& _gasUsed, TransactionException _excepted, Address const& _newAddress, bytesConstRef _output, CodeDeposit _codeDeposit, u256 const& _gasRefund, unsigned _depositSize, u256 const& _gasForDeposit): + gasUsed(_gasUsed), + excepted(_excepted), + newAddress(_newAddress), + output(_output.toBytes()), + codeDeposit(_codeDeposit), + gasRefunded(_gasRefund), + depositSize(_depositSize), + gasForDeposit(_gasForDeposit) + {} u256 gasUsed = 0; TransactionException excepted = TransactionException::Unknown; Address newAddress; bytes output; CodeDeposit codeDeposit = CodeDeposit::None; u256 gasRefunded = 0; + unsigned depositSize = 0; + u256 gasForDeposit; }; std::ostream& operator<<(std::ostream& _out, ExecutionResult const& _er); +static const Address NullAddress; + /// Encodes a transaction, ready to be exported to or freshly imported from RLP. class Transaction { @@ -94,16 +107,16 @@ public: Transaction() {} /// Constructs a signed message-call transaction. - Transaction(u256 _value, u256 _gasPrice, u256 _gas, Address const& _dest, bytes const& _data, u256 _nonce, Secret const& _secret): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); } + Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(MessageCall), m_nonce(_nonce), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); } /// Constructs a signed contract-creation transaction. - Transaction(u256 _value, u256 _gasPrice, u256 _gas, bytes const& _data, u256 _nonce, Secret const& _secret): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); } + Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data, u256 const& _nonce, Secret const& _secret): m_type(ContractCreation), m_nonce(_nonce), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) { sign(_secret); } /// Constructs an unsigned message-call transaction. - Transaction(u256 _value, u256 _gasPrice, u256 _gas, Address const& _dest, bytes const& _data): m_type(MessageCall), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {} + Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, Address const& _dest, bytes const& _data): m_type(MessageCall), m_value(_value), m_receiveAddress(_dest), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {} /// Constructs an unsigned contract-creation transaction. - Transaction(u256 _value, u256 _gasPrice, u256 _gas, bytes const& _data): m_type(ContractCreation), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {} + Transaction(u256 const& _value, u256 const& _gasPrice, u256 const& _gas, bytes const& _data): m_type(ContractCreation), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {} /// Constructs a transaction from the given RLP. explicit Transaction(bytesConstRef _rlp, CheckSignature _checkSig); @@ -118,9 +131,9 @@ public: bool operator!=(Transaction const& _c) const { return !operator==(_c); } /// @returns sender of the transaction from the signature (and hash). - Address sender() const; + Address const& sender() const; /// Like sender() but will never throw. @returns a null Address if the signature is invalid. - Address safeSender() const noexcept; + Address const& safeSender() const noexcept; /// @returns true if transaction is non-null. explicit operator bool() const { return m_type != NullTransaction; } From 9710ce3b7eaa5db9d549950e1514a8eb1bda99fa Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 31 Mar 2015 10:28:46 +0200 Subject: [PATCH 191/228] Fix #1475 --- alethzero/MainWin.cpp | 1 + libp2p/Host.cpp | 66 ++++++++++++++++++------------------------- test/peer.cpp | 9 ++++++ 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 0de7e2cb8..610fd032d 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1774,6 +1774,7 @@ void Main::on_net_triggered() else { ui->downloadView->setDownloadMan(nullptr); + writeSettings(); web3()->stopNetwork(); } } diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index ab39c5c25..5aeeb3f82 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -678,9 +678,6 @@ void Host::disconnectLatePeers() bytes Host::saveNetwork() const { - if (!m_nodeTable) - return bytes(); - std::list peers; { RecursiveGuard l(x_sessions); @@ -692,27 +689,23 @@ bytes Host::saveNetwork() const RLPStream network; int count = 0; + for (auto const& p: peers) { - RecursiveGuard l(x_sessions); - for (auto const& p: peers) + // Only save peers which have connected within 2 days, with properly-advertised port and public IP address + bi::tcp::endpoint endpoint(p.peerEndpoint()); + if (chrono::system_clock::now() - p.m_lastConnected < chrono::seconds(3600 * 48) && endpoint.port() > 0 && endpoint.port() < /*49152*/32768 && p.id != id() && !isPrivateAddress(p.endpoint.udp.address()) && !isPrivateAddress(endpoint.address())) { - // TODO: alpha: Figure out why it ever shares these ports.//p.address.port() >= 30300 && p.address.port() <= 30305 && - // TODO: alpha: if/how to save private addresses - // Only save peers which have connected within 2 days, with properly-advertised port and public IP address - if (chrono::system_clock::now() - p.m_lastConnected < chrono::seconds(3600 * 48) && p.peerEndpoint().port() > 0 && p.peerEndpoint().port() < /*49152*/32768 && p.id != id() && !isPrivateAddress(p.endpoint.udp.address()) && !isPrivateAddress(p.endpoint.tcp.address())) - { - network.appendList(10); - if (p.peerEndpoint().address().is_v4()) - network << p.peerEndpoint().address().to_v4().to_bytes(); - else - network << p.peerEndpoint().address().to_v6().to_bytes(); - // TODO: alpha: replace 0 with trust-state of node - network << p.peerEndpoint().port() << p.id << 0 - << chrono::duration_cast(p.m_lastConnected.time_since_epoch()).count() - << chrono::duration_cast(p.m_lastAttempted.time_since_epoch()).count() - << p.m_failedAttempts << (unsigned)p.m_lastDisconnect << p.m_score << p.m_rating; - count++; - } + // todo: e2e ipv6 support + if (endpoint.address().is_v4()) + network << endpoint.address().to_v4().to_bytes(); + continue; + + network.appendList(10); + network << endpoint.port() << p.id << p.required + << chrono::duration_cast(p.m_lastConnected.time_since_epoch()).count() + << chrono::duration_cast(p.m_lastAttempted.time_since_epoch()).count() + << p.m_failedAttempts << (unsigned)p.m_lastDisconnect << p.m_score << p.m_rating; + count++; } } @@ -731,6 +724,7 @@ bytes Host::saveNetwork() const count++; } } + // else: TODO: use previous configuration if available RLPStream ret(3); ret << dev::p2p::c_protocolVersion << m_alias.secret(); @@ -757,22 +751,14 @@ void Host::restoreNetwork(bytesConstRef _b) for (auto i: r[2]) { + // todo: e2e ipv6 support + // bi::tcp::endpoint(bi::address_v6(i[0].toArray()), i[1].toInt()); + if (i[0].itemCount() != 4) + continue; bi::tcp::endpoint tcp; bi::udp::endpoint udp; - if (i[0].itemCount() == 4) - { - tcp = bi::tcp::endpoint(bi::address_v4(i[0].toArray()), i[1].toInt()); - udp = bi::udp::endpoint(bi::address_v4(i[0].toArray()), i[1].toInt()); - } - else - { - tcp = bi::tcp::endpoint(bi::address_v6(i[0].toArray()), i[1].toInt()); - udp = bi::udp::endpoint(bi::address_v6(i[0].toArray()), i[1].toInt()); - } - - // skip private addresses - // todo: to support private addresseses entries must be stored - // and managed externally by host rather than nodetable. + tcp = bi::tcp::endpoint(bi::address_v4(i[0].toArray()), i[1].toInt()); + udp = bi::udp::endpoint(bi::address_v4(i[0].toArray()), i[1].toInt()); if (isPrivateAddress(tcp.address()) || isPrivateAddress(udp.address())) continue; @@ -783,6 +769,7 @@ void Host::restoreNetwork(bytesConstRef _b) { shared_ptr p = make_shared(); p->id = id; + p->required = i[2].toInt(); p->m_lastConnected = chrono::system_clock::time_point(chrono::seconds(i[4].toInt())); p->m_lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[5].toInt())); p->m_failedAttempts = i[6].toInt(); @@ -792,7 +779,10 @@ void Host::restoreNetwork(bytesConstRef _b) p->endpoint.tcp = tcp; p->endpoint.udp = udp; m_peers[p->id] = p; - m_nodeTable->addNode(*p.get()); + if (p->required) + requirePeer(p->id, p->endpoint.udp.address(), p->endpoint.udp.port()); + else + m_nodeTable->addNode(*p.get()); } } } @@ -801,7 +791,7 @@ void Host::restoreNetwork(bytesConstRef _b) KeyPair Host::networkAlias(bytesConstRef _b) { RLP r(_b); - if (r.itemCount() == 3 && r[0].isInt() && r[0].toInt() == 1) + if (r.itemCount() == 3 && r[0].isInt() && r[0].toInt() == dev::p2p::c_protocolVersion) return move(KeyPair(move(Secret(r[1].toBytes())))); else return move(KeyPair::create()); diff --git a/test/peer.cpp b/test/peer.cpp index 48431504f..904de0e9d 100644 --- a/test/peer.cpp +++ b/test/peer.cpp @@ -57,6 +57,15 @@ BOOST_AUTO_TEST_CASE(host) g_logVerbosity = oldLogVerbosity; } +BOOST_AUTO_TEST_CASE(networkConfig) +{ + Host save("Test", NetworkPreferences(false)); + bytes store(save.saveNetwork()); + + Host restore("Test", NetworkPreferences(false), bytesConstRef(&store)); + BOOST_REQUIRE(save.id() == restore.id()); +} + BOOST_AUTO_TEST_CASE(save_nodes) { std::list hosts; From deaabe142a51fb14a64a8e912c45af49a531a810 Mon Sep 17 00:00:00 2001 From: Ali Mashatan Date: Tue, 31 Mar 2015 13:41:37 +0430 Subject: [PATCH 192/228] Bug fix. --- mix/qml/TransactionLog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix/qml/TransactionLog.qml b/mix/qml/TransactionLog.qml index 83e64ebf1..e5787f19b 100644 --- a/mix/qml/TransactionLog.qml +++ b/mix/qml/TransactionLog.qml @@ -61,7 +61,7 @@ Item { target: projectModel onProjectSaved: { - if (projectModel.appIsClosing) + if (projectModel.appIsClosing || projectModel.projectIsClosing) return; if (compilationStatus.compilationComplete && codeModel.hasContract && !clientModel.running) projectModel.stateListModel.debugDefaultState(); From 1a93beb7e1c8c2f5f46069c30930468d2d58ce9f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 31 Mar 2015 11:18:25 +0200 Subject: [PATCH 193/228] Cleanup and document some of the hairier State code. Fixes #1458. Fixes #1459. Fixes #1460. --- libethereum/State.cpp | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 378c60fa3..c52c90921 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -111,22 +111,36 @@ State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h): m_state(&m_db), m_blockReward(c_blockReward) { - // TODO THINK: is this necessary? - m_state.init(); - auto b = _bc.block(_h); - BlockInfo bi; - BlockInfo bip; - if (_h) - bi.populate(b); - if (bi && bi.number) - bip.populate(_bc.block(bi.parentHash)); - if (!_h || !bip) + BlockInfo bi(b); + + if (!bi) + { + // Might be worth throwing here. + cwarn << "Invalid block given for state population: " << _h; return; - m_ourAddress = bi.coinbaseAddress; + } - sync(_bc, bi.parentHash, bip); - enact(&b, _bc); + if (bi.number) + { + // Non-genesis: + + // 1. Start at parent's end state (state root). + BlockInfo bip; + bip.populate(_bc.block(bi.parentHash)); + sync(_bc, bi.parentHash, bip); + + // 2. Enact the block's transactions onto this state. + m_ourAddress = bi.coinbaseAddress; + enact(&b, _bc); + } + else + { + // Genesis required: + // We know there are no transactions, so just populate directly. + m_state.init(); + sync(_bc, _h, bi); + } } State::State(State const& _s): From 8f68a846da4086cdffd78ad2ba887591d87f7752 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 31 Mar 2015 11:21:43 +0200 Subject: [PATCH 194/228] nodeid doesn't cast to a bool (bug) --- libp2p/Host.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 5aeeb3f82..e530c7e66 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -728,7 +728,9 @@ bytes Host::saveNetwork() const RLPStream ret(3); ret << dev::p2p::c_protocolVersion << m_alias.secret(); - ret.appendList(count).appendRaw(network.out(), count); + ret.appendList(count); + if (count) + ret.appendRaw(network.out(), count); return ret.out(); } @@ -769,7 +771,7 @@ void Host::restoreNetwork(bytesConstRef _b) { shared_ptr p = make_shared(); p->id = id; - p->required = i[2].toInt(); + p->required = i[3].toInt(); p->m_lastConnected = chrono::system_clock::time_point(chrono::seconds(i[4].toInt())); p->m_lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[5].toInt())); p->m_failedAttempts = i[6].toInt(); From a224fd2727f33b7b096e3aa60b185d2839455123 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 31 Mar 2015 11:26:25 +0200 Subject: [PATCH 195/228] fix #1474 --- mix/qml/CodeEditorView.qml | 10 ++++++++++ mix/qml/WebPreview.qml | 7 ++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml index b100dffd0..7ea1e30d9 100644 --- a/mix/qml/CodeEditorView.qml +++ b/mix/qml/CodeEditorView.qml @@ -11,6 +11,16 @@ Item { signal breakpointsChanged(string documentId) signal isCleanChanged(var isClean, string documentId) + function getDocumentIdByName(fileName) + { + for (var i = 0; i < editorListModel.count; i++) { + if (editorListModel.get(i).fileName === fileName) { + return editorListModel.get(i).documentId; + } + } + return null; + } + function getDocumentText(documentId) { for (var i = 0; i < editorListModel.count; i++) { if (editorListModel.get(i).documentId === documentId) { diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml index 8507c9b46..0bf31b326 100644 --- a/mix/qml/WebPreview.qml +++ b/mix/qml/WebPreview.qml @@ -155,18 +155,19 @@ Item { //document request if (urlPath === "/") urlPath = "/index.html"; - var documentId = urlPath.substr(urlPath.lastIndexOf("/") + 1); + var documentName = urlPath.substr(urlPath.lastIndexOf("/") + 1); + var documentId = projectModel.codeEditor.getDocumentIdByName(documentName); var content = ""; if (projectModel.codeEditor.isDocumentOpen(documentId)) content = projectModel.codeEditor.getDocumentText(documentId); else { var doc = projectModel.getDocument(documentId); - if (doc !== undefined) + if (doc) content = fileIo.readFile(doc.path); } - if (documentId === urlInput.text.replace(httpServer.url + "/", "")) { + if (documentName === urlInput.text.replace(httpServer.url + "/", "")) { //root page, inject deployment script content = "\n" + content; _request.setResponseContentType("text/html"); From 0e48d266c8cd2d94b2f1568665eeeaaf9fc88272 Mon Sep 17 00:00:00 2001 From: arkpar Date: Tue, 31 Mar 2015 11:37:10 +0200 Subject: [PATCH 196/228] Mix: fixed editor clipboard for os x --- mix/qml/html/codeeditor.js | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/mix/qml/html/codeeditor.js b/mix/qml/html/codeeditor.js index 299691ec0..4beae1955 100644 --- a/mix/qml/html/codeeditor.js +++ b/mix/qml/html/codeeditor.js @@ -22,17 +22,11 @@ editor.on("change", function(eMirror, object) { }); var mac = /Mac/.test(navigator.platform); +var extraKeys = {}; if (mac === true) { - editor.setOption("extraKeys", { - "Cmd-V": function(cm) { - cm.replaceSelection(clipboard); - }, - "Cmd-X": function(cm) { - window.document.execCommand("cut"); - }, - "Cmd-C": function(cm) { - window.document.execCommand("copy"); - }}); + extraKeys["Cmd-V"] = function(cm) { cm.replaceSelection(clipboard); }; + extraKeys["Cmd-X"] = function(cm) { window.document.execCommand("cut"); }; + extraKeys["Cmd-C"] = function(cm) { window.document.execCommand("copy"); }; } makeMarker = function() { @@ -102,16 +96,14 @@ setMode = function(mode) { if (mode === "javascript") { ternServer = new CodeMirror.TernServer({defs: [ ecma5Spec() ]}); - editor.setOption("extraKeys", { - "Ctrl-Space": function(cm) { ternServer.complete(cm); }, - "Ctrl-I": function(cm) { ternServer.showType(cm); }, - "Ctrl-O": function(cm) { ternServer.showDocs(cm); }, - "Alt-.": function(cm) { ternServer.jumpToDef(cm); }, - "Alt-,": function(cm) { ternServer.jumpBack(cm); }, - "Ctrl-Q": function(cm) { ternServer.rename(cm); }, - "Ctrl-.": function(cm) { ternServer.selectName(cm); }, - "'.'": function(cm) { setTimeout(function() { ternServer.complete(cm); }, 100); throw CodeMirror.Pass; } - }) + extraKeys["Ctrl-Space"] = function(cm) { ternServer.complete(cm); }; + extraKeys["Ctrl-I"] = function(cm) { ternServer.showType(cm); }; + extraKeys["Ctrl-O"] = function(cm) { ternServer.showDocs(cm); }; + extraKeys["Alt-."] = function(cm) { ternServer.jumpToDef(cm); }; + extraKeys["Alt-,"] = function(cm) { ternServer.jumpBack(cm); }; + extraKeys["Ctrl-Q"] = function(cm) { ternServer.rename(cm); }; + extraKeys["Ctrl-."] = function(cm) { ternServer.selectName(cm); }; + extraKeys["'.'"] = function(cm) { setTimeout(function() { ternServer.complete(cm); }, 100); throw CodeMirror.Pass; }; editor.on("cursorActivity", function(cm) { ternServer.updateArgHints(cm); }); } else if (mode === "solidity") @@ -119,9 +111,7 @@ setMode = function(mode) { CodeMirror.commands.autocomplete = function(cm) { CodeMirror.showHint(cm, CodeMirror.hint.anyword); } - editor.setOption("extraKeys", { - "Ctrl-Space": "autocomplete" - }) + extraKeys["Ctrl-Space"] = "autocomplete"; } }; @@ -199,3 +189,5 @@ compilationComplete = function() } compilationCompleteBool = true; } + +editor.setOption("extraKeys", extraKeys); From a0608412acc148889258e51eecde2b882207ba45 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Thu, 26 Mar 2015 21:06:59 +0100 Subject: [PATCH 197/228] removed trailing zeros from rpc results, fixed #1433 --- libweb3jsonrpc/WebThreeStubServerBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index cab986364..7524e7519 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -402,7 +402,7 @@ string WebThreeStubServerBase::eth_getCode(string const& _address, string const& { try { - return toJS(client()->codeAt(jsToAddress(_address), toBlockNumber(_blockNumber)), 1); + return toJS(client()->codeAt(jsToAddress(_address), toBlockNumber(_blockNumber))); } catch (...) { From ab0501a70c4c96bc71becc9b20ac4e7a3d45791b Mon Sep 17 00:00:00 2001 From: arkpar Date: Tue, 31 Mar 2015 12:03:26 +0200 Subject: [PATCH 198/228] apply extrakeys on mode change --- mix/qml/html/codeeditor.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mix/qml/html/codeeditor.js b/mix/qml/html/codeeditor.js index 4beae1955..5d63a7a83 100644 --- a/mix/qml/html/codeeditor.js +++ b/mix/qml/html/codeeditor.js @@ -113,6 +113,7 @@ setMode = function(mode) { } extraKeys["Ctrl-Space"] = "autocomplete"; } + editor.setOption("extraKeys", extraKeys); }; setClipboardBase64 = function(text) { From b1ec65596f7c6532a6d0ea4ac9d70a569a0e3256 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 31 Mar 2015 12:26:42 +0200 Subject: [PATCH 199/228] fix bug found in code review. --- libp2p/Host.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index e530c7e66..45ebd6db1 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -692,14 +692,13 @@ bytes Host::saveNetwork() const for (auto const& p: peers) { // Only save peers which have connected within 2 days, with properly-advertised port and public IP address + // todo: e2e ipv6 support bi::tcp::endpoint endpoint(p.peerEndpoint()); + if (!endpoint.address().is_v4()) + continue; + if (chrono::system_clock::now() - p.m_lastConnected < chrono::seconds(3600 * 48) && endpoint.port() > 0 && endpoint.port() < /*49152*/32768 && p.id != id() && !isPrivateAddress(p.endpoint.udp.address()) && !isPrivateAddress(endpoint.address())) { - // todo: e2e ipv6 support - if (endpoint.address().is_v4()) - network << endpoint.address().to_v4().to_bytes(); - continue; - network.appendList(10); network << endpoint.port() << p.id << p.required << chrono::duration_cast(p.m_lastConnected.time_since_epoch()).count() @@ -729,7 +728,7 @@ bytes Host::saveNetwork() const RLPStream ret(3); ret << dev::p2p::c_protocolVersion << m_alias.secret(); ret.appendList(count); - if (count) + if (!!count) ret.appendRaw(network.out(), count); return ret.out(); } From 1d4e61167ffb783781a2106d66d34737a2c5a161 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Tue, 31 Mar 2015 12:43:08 +0200 Subject: [PATCH 200/228] most compact number representation --- libdevcore/CommonJS.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libdevcore/CommonJS.h b/libdevcore/CommonJS.h index cc487d54c..ade089e16 100644 --- a/libdevcore/CommonJS.h +++ b/libdevcore/CommonJS.h @@ -38,7 +38,10 @@ template std::string toJS(FixedHash const& _h) template std::string toJS(boost::multiprecision::number> const& _n) { - return "0x" + toHex(toCompactBigEndian(_n, 1)); + std::string h = toHex(toCompactBigEndian(_n, 1)); + // remove first 0, if it is necessary; + std::string res = h[0] != '0' ? h : h.substr(1); + return "0x" + res; } inline std::string toJS(bytes const& _n, std::size_t _padding = 0) From 2ad558b9a962c04f856f304da6e6c7bd8ae8e134 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 31 Mar 2015 13:28:28 +0200 Subject: [PATCH 201/228] Started minor refactor - still API compatible though. --- libethash/CMakeLists.txt | 7 ++++ libethash/data_sizes.h | 4 +-- libethash/ethash.h | 75 ++++++++++++++++++++++------------------ libethash/internal.c | 14 ++++---- libethash/internal.h | 2 +- 5 files changed, 58 insertions(+), 44 deletions(-) diff --git a/libethash/CMakeLists.txt b/libethash/CMakeLists.txt index 38fc821c0..c92240086 100644 --- a/libethash/CMakeLists.txt +++ b/libethash/CMakeLists.txt @@ -12,6 +12,7 @@ endif() set(FILES util.c util.h + io.c internal.c ethash.h endian.h @@ -19,6 +20,12 @@ set(FILES util.c fnv.h data_sizes.h) +if (MSVC) + list(APPEND FILES io_win32.c) +else() + list(APPEND FILES io_posix.c) +endif() + if (NOT CRYPTOPP_FOUND) find_package(CryptoPP 5.6.2) endif() diff --git a/libethash/data_sizes.h b/libethash/data_sizes.h index 3b747b3ea..cf52ae4f8 100644 --- a/libethash/data_sizes.h +++ b/libethash/data_sizes.h @@ -48,7 +48,7 @@ extern "C" { // Sow[i*HashBytes]; j++]]]][[2]][[1]] -static const size_t dag_sizes[2048] = { +static const uint64_t dag_sizes[2048] = { 1073739904U, 1082130304U, 1090514816U, 1098906752U, 1107293056U, 1115684224U, 1124070016U, 1132461952U, 1140849536U, 1149232768U, 1157627776U, 1166013824U, 1174404736U, 1182786944U, 1191180416U, @@ -477,7 +477,7 @@ static const size_t dag_sizes[2048] = { // While[! PrimeQ[i], i--]; // Sow[i*HashBytes]; j++]]]][[2]][[1]] -const size_t cache_sizes[2048] = { +const uint64_t cache_sizes[2048] = { 16776896U, 16907456U, 17039296U, 17170112U, 17301056U, 17432512U, 17563072U, 17693888U, 17824192U, 17955904U, 18087488U, 18218176U, 18349504U, 18481088U, 18611392U, 18742336U, 18874304U, 19004224U, 19135936U, 19267264U, 19398208U, diff --git a/libethash/ethash.h b/libethash/ethash.h index a7159de65..eb3097307 100644 --- a/libethash/ethash.h +++ b/libethash/ethash.h @@ -43,26 +43,26 @@ extern "C" { #endif typedef struct ethash_params { - size_t full_size; // Size of full data set (in bytes, multiple of mix size (128)). - size_t cache_size; // Size of compute cache (in bytes, multiple of node size (64)). + uint64_t full_size; // Size of full data set (in bytes, multiple of mix size (128)). + uint64_t cache_size; // Size of compute cache (in bytes, multiple of node size (64)). } ethash_params; typedef struct ethash_return_value { - uint8_t result[32]; - uint8_t mix_hash[32]; + uint8_t result[32]; + uint8_t mix_hash[32]; } ethash_return_value; -size_t ethash_get_datasize(const uint32_t block_number); -size_t ethash_get_cachesize(const uint32_t block_number); +uint64_t ethash_get_datasize(const uint32_t block_number); +uint64_t ethash_get_cachesize(const uint32_t block_number); // initialize the parameters static inline void ethash_params_init(ethash_params *params, const uint32_t block_number) { - params->full_size = ethash_get_datasize(block_number); - params->cache_size = ethash_get_cachesize(block_number); + params->full_size = ethash_get_datasize(block_number); + params->cache_size = ethash_get_cachesize(block_number); } typedef struct ethash_cache { - void *mem; + void *mem; } ethash_cache; void ethash_mkcache(ethash_cache *cache, ethash_params const *params, const uint8_t seed[32]); @@ -72,44 +72,51 @@ void ethash_light(ethash_return_value *ret, ethash_cache const *cache, ethash_pa void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number); static inline void ethash_prep_light(void *cache, ethash_params const *params, const uint8_t seed[32]) { - ethash_cache c; - c.mem = cache; - ethash_mkcache(&c, params, seed); + ethash_cache c; + c.mem = cache; + ethash_mkcache(&c, params, seed); } static inline void ethash_compute_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) { - ethash_cache c; - c.mem = (void *) cache; - ethash_light(ret, &c, params, header_hash, nonce); + ethash_cache c; + c.mem = (void *) cache; + ethash_light(ret, &c, params, header_hash, nonce); } static inline void ethash_prep_full(void *full, ethash_params const *params, void const *cache) { - ethash_cache c; - c.mem = (void *) cache; - ethash_compute_full_data(full, params, &c); + ethash_cache c; + c.mem = (void *) cache; + ethash_compute_full_data(full, params, &c); } static inline void ethash_compute_full(ethash_return_value *ret, void const *full, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) { - ethash_full(ret, full, params, header_hash, nonce); + ethash_full(ret, full, params, header_hash, nonce); } -// Returns if hash is less than or equal to difficulty -static inline int ethash_check_difficulty( - const uint8_t hash[32], - const uint8_t difficulty[32]) { - // Difficulty is big endian - for (int i = 0; i < 32; i++) { - if (hash[i] == difficulty[i]) continue; - return hash[i] < difficulty[i]; - } - return 1; +/// @brief Compare two s256-bit big-endian values. +/// @returns 1 if @a a is less than or equal to @a b, 0 otherwise. +/// Both parameters are 256-bit big-endian values. +static inline int ethash_leq_be256(const uint8_t a[32], const uint8_t b[32]) { + // Boundary is big endian + for (int i = 0; i < 32; i++) { + if (a[i] == b[i]) + continue; + return a[i] < b[i]; + } + return 1; } -int ethash_quick_check_difficulty( - const uint8_t header_hash[32], - const uint64_t nonce, - const uint8_t mix_hash[32], - const uint8_t difficulty[32]); +/// Perofrms a cursory check on the validity of the nonce. +/// @returns 1 if the nonce may possibly be valid for the given header_hash & boundary. +/// @p boundary equivalent to 2 ^ 256 / block_difficulty, represented as a 256-bit big-endian. +int ethash_preliminary_check_boundary( + const uint8_t header_hash[32], + const uint64_t nonce, + const uint8_t mix_hash[32], + const uint8_t boundary[32]); + +#define ethash_quick_check_difficulty ethash_preliminary_check_boundary +#define ethash_check_difficulty ethash_leq_be256 #ifdef __cplusplus } diff --git a/libethash/internal.c b/libethash/internal.c index 0a7e767e7..ae9b95065 100644 --- a/libethash/internal.c +++ b/libethash/internal.c @@ -37,12 +37,12 @@ #include "sha3.h" #endif // WITH_CRYPTOPP -size_t ethash_get_datasize(const uint32_t block_number) { +uint64_t ethash_get_datasize(const uint32_t block_number) { assert(block_number / EPOCH_LENGTH < 2048); return dag_sizes[block_number / EPOCH_LENGTH]; } -size_t ethash_get_cachesize(const uint32_t block_number) { +uint64_t ethash_get_cachesize(const uint32_t block_number) { assert(block_number / EPOCH_LENGTH < 2048); return cache_sizes[block_number / EPOCH_LENGTH]; } @@ -280,15 +280,15 @@ void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number) { SHA3_256(seedhash, seedhash, 32); } -int ethash_quick_check_difficulty( +int ethash_preliminary_check_boundary( const uint8_t header_hash[32], const uint64_t nonce, const uint8_t mix_hash[32], - const uint8_t difficulty[32]) { + const uint8_t difficulty[32]) { - uint8_t return_hash[32]; + uint8_t return_hash[32]; ethash_quick_hash(return_hash, header_hash, nonce, mix_hash); - return ethash_check_difficulty(return_hash, difficulty); + return ethash_leq_be256(return_hash, difficulty); } void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) { @@ -297,4 +297,4 @@ void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params c void ethash_light(ethash_return_value *ret, ethash_cache const *cache, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) { ethash_hash(ret, NULL, cache, params, previous_hash, nonce); -} \ No newline at end of file +} diff --git a/libethash/internal.h b/libethash/internal.h index bcbacdaa4..ddd06e8f4 100644 --- a/libethash/internal.h +++ b/libethash/internal.h @@ -3,7 +3,7 @@ #include "endian.h" #include "ethash.h" -#define ENABLE_SSE 1 +#define ENABLE_SSE 0 #if defined(_M_X64) && ENABLE_SSE #include From 5039e4a9eeffd522d06fedc80ae339c84b39d78e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 31 Mar 2015 14:02:08 +0200 Subject: [PATCH 202/228] Add missing files. --- libethash/io.c | 89 +++++++++++++++++++++++++++++++++ libethash/io.h | 116 +++++++++++++++++++++++++++++++++++++++++++ libethash/io_posix.c | 76 ++++++++++++++++++++++++++++ libethash/io_win32.c | 73 +++++++++++++++++++++++++++ 4 files changed, 354 insertions(+) create mode 100644 libethash/io.c create mode 100644 libethash/io.h create mode 100644 libethash/io_posix.c create mode 100644 libethash/io_win32.c diff --git a/libethash/io.c b/libethash/io.c new file mode 100644 index 000000000..dd4f1f9e8 --- /dev/null +++ b/libethash/io.c @@ -0,0 +1,89 @@ +/* + This file is part of ethash. + + ethash 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. + + ethash 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 ethash. If not, see . +*/ +/** @file io.c + * @author Lefteris Karapetsas + * @date 2015 + */ +#include "io.h" +#include +#include + +// silly macro to save some typing +#define PASS_ARR(c_) (c_), sizeof(c_) + +static bool ethash_io_write_file(char const *dirname, + char const* filename, + size_t filename_length, + void const* data, + size_t data_size) +{ + bool ret = false; + char *fullname = ethash_io_create_filename(dirname, filename, filename_length); + if (!fullname) { + return false; + } + FILE *f = fopen(fullname, "wb"); + if (!f) { + goto free_name; + } + if (data_size != fwrite(data, 1, data_size, f)) { + goto close; + } + + ret = true; +close: + fclose(f); +free_name: + free(fullname); + return ret; +} + +bool ethash_io_write(char const *dirname, + ethash_params const* params, + ethash_blockhash_t seedhash, + void const* cache, + uint8_t **data, + size_t *data_size) +{ + char info_buffer[DAG_MEMO_BYTESIZE]; + // allocate the bytes + uint8_t *temp_data_ptr = malloc(params->full_size); + if (!temp_data_ptr) { + goto end; + } + ethash_compute_full_data(temp_data_ptr, params, cache); + + if (!ethash_io_write_file(dirname, PASS_ARR(DAG_FILE_NAME), temp_data_ptr, params->full_size)) { + goto fail_free; + } + + ethash_io_serialize_info(REVISION, seedhash, info_buffer); + if (!ethash_io_write_file(dirname, PASS_ARR(DAG_MEMO_NAME), info_buffer, DAG_MEMO_BYTESIZE)) { + goto fail_free; + } + + *data = temp_data_ptr; + *data_size = params->full_size; + return true; + +fail_free: + free(temp_data_ptr); +end: + return false; +} + +#undef PASS_ARR diff --git a/libethash/io.h b/libethash/io.h new file mode 100644 index 000000000..0fa292362 --- /dev/null +++ b/libethash/io.h @@ -0,0 +1,116 @@ +/* + This file is part of ethash. + + ethash 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. + + ethash 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 ethash. If not, see . +*/ +/** @file io.h + * @author Lefteris Karapetsas + * @date 2015 + */ +#pragma once +#include +#include +#include +#include "ethash.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ethash_blockhash { uint8_t b[32]; } ethash_blockhash_t; + +static const char DAG_FILE_NAME[] = "full"; +static const char DAG_MEMO_NAME[] = "full.info"; +// MSVC thinks that "static const unsigned int" is not a compile time variable. Sorry for the #define :( +#define DAG_MEMO_BYTESIZE 36 + +/// Possible return values of @see ethash_io_prepare +enum ethash_io_rc { + ETHASH_IO_FAIL = 0, ///< There has been an IO failure + ETHASH_IO_MEMO_MISMATCH, ///< Memo file either did not exist or there was content mismatch + ETHASH_IO_MEMO_MATCH, ///< Memo file existed and contents matched. No need to do anything +}; + +/** + * Prepares io for ethash + * + * Create the DAG directory if it does not exist, and check if the memo file matches. + * If it does not match then it's deleted to pave the way for @ref ethash_io_write() + * + * @param dirname A null terminated c-string of the path of the ethash + * data directory. If it does not exist it's created. + * @param seedhash The seedhash of the current block number + * @return For possible return values @see enum ethash_io_rc + */ +enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash); +/** + * Fully computes data and writes it to the file on disk. + * + * This function should be called after @see ethash_io_prepare() and only if + * its return value is @c ETHASH_IO_MEMO_MISMATCH. Will write both the full data + * and the memo file. + * + * @param[in] dirname A null terminated c-string of the path of the ethash + * data directory. Has to exist. + * @param[in] params An ethash_params object containing the full size + * and the cache size + * @param[in] seedhash The seedhash of the current block number + * @param[in] cache The cache data. Would have usually been calulated by + * @see ethash_prep_light(). + * @param[out] data Pass a pointer to uint8_t by reference here. If the + * function is succesfull then this point to the allocated + * data calculated by @see ethash_prep_full(). Memory + * ownership is transfered to the callee. Remember that + * you eventually need to free this with a call to free(). + * @param[out] data_size Pass a size_t by value. If the function is succesfull + * then this will contain the number of bytes allocated + * for @a data. + * @return True for success and false in case of failure. + */ +bool ethash_io_write(char const *dirname, + ethash_params const* params, + ethash_blockhash_t seedhash, + void const* cache, + uint8_t **data, + size_t *data_size); + +static inline void ethash_io_serialize_info(uint32_t revision, + ethash_blockhash_t seed_hash, + char *output) +{ + // if .info is only consumed locally we don't really care about endianess + memcpy(output, &revision, 4); + memcpy(output + 4, &seed_hash, 32); +} + +static inline char *ethash_io_create_filename(char const *dirname, + char const* filename, + size_t filename_length) +{ + // in C the cast is not needed, but a C++ compiler will complain for invalid conversion + char *name = (char*)malloc(strlen(dirname) + filename_length); + if (!name) { + return NULL; + } + + name[0] = '\0'; + strcat(name, dirname); + strcat(name, filename); + return name; +} + + +#ifdef __cplusplus +} +#endif diff --git a/libethash/io_posix.c b/libethash/io_posix.c new file mode 100644 index 000000000..693bdf750 --- /dev/null +++ b/libethash/io_posix.c @@ -0,0 +1,76 @@ +/* + This file is part of ethash. + + ethash 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. + + ethash 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 ethash. If not, see . +*/ +/** @file io_posix.c + * @author Lefteris Karapetsas + * @date 2015 + */ + +#include "io.h" +#include +#include +#include +#include +#include +#include + +enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash) +{ + char read_buffer[DAG_MEMO_BYTESIZE]; + char expect_buffer[DAG_MEMO_BYTESIZE]; + enum ethash_io_rc ret = ETHASH_IO_FAIL; + + // assert directory exists, full owner permissions and read/search for others + int rc = mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if (rc == -1 && errno != EEXIST) { + goto end; + } + + char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME)); + if (!memofile) { + goto end; + } + + // try to open memo file + FILE *f = fopen(memofile, "rb"); + if (!f) { + // file does not exist, so no checking happens. All is fine. + ret = ETHASH_IO_MEMO_MISMATCH; + goto free_memo; + } + + if (fread(read_buffer, 1, DAG_MEMO_BYTESIZE, f) != DAG_MEMO_BYTESIZE) { + goto close; + } + + ethash_io_serialize_info(REVISION, seedhash, expect_buffer); + if (memcmp(read_buffer, expect_buffer, DAG_MEMO_BYTESIZE) != 0) { + // we have different memo contents so delete the memo file + if (unlink(memofile) != 0) { + goto close; + } + ret = ETHASH_IO_MEMO_MISMATCH; + } + + ret = ETHASH_IO_MEMO_MATCH; + +close: + fclose(f); +free_memo: + free(memofile); +end: + return ret; +} diff --git a/libethash/io_win32.c b/libethash/io_win32.c new file mode 100644 index 000000000..2cabc939a --- /dev/null +++ b/libethash/io_win32.c @@ -0,0 +1,73 @@ +/* + This file is part of ethash. + + ethash 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. + + ethash 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 ethash. If not, see . +*/ +/** @file io_win32.c + * @author Lefteris Karapetsas + * @date 2015 + */ + +#include "io.h" +#include +#include +#include + +enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash) +{ + char read_buffer[DAG_MEMO_BYTESIZE]; + char expect_buffer[DAG_MEMO_BYTESIZE]; + enum ethash_io_rc ret = ETHASH_IO_FAIL; + + // assert directory exists + int rc = _mkdir(dirname); + if (rc == -1 && errno != EEXIST) { + goto end; + } + + char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME)); + if (!memofile) { + goto end; + } + + // try to open memo file + FILE *f = fopen(memofile, "rb"); + if (!f) { + // file does not exist, so no checking happens. All is fine. + ret = ETHASH_IO_MEMO_MISMATCH; + goto free_memo; + } + + if (fread(read_buffer, 1, DAG_MEMO_BYTESIZE, f) != DAG_MEMO_BYTESIZE) { + goto close; + } + + ethash_io_serialize_info(REVISION, seedhash, expect_buffer); + if (memcmp(read_buffer, expect_buffer, DAG_MEMO_BYTESIZE) != 0) { + // we have different memo contents so delete the memo file + if (_unlink(memofile) != 0) { + goto close; + } + ret = ETHASH_IO_MEMO_MISMATCH; + } + + ret = ETHASH_IO_MEMO_MATCH; + +close: + fclose(f); +free_memo: + free(memofile); +end: + return ret; +} From fc1aac2ef42ff2778c1e9789c9ea48c74adfc4d5 Mon Sep 17 00:00:00 2001 From: arkpar Date: Tue, 31 Mar 2015 14:59:21 +0200 Subject: [PATCH 203/228] style file --- mix/style.xml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 mix/style.xml diff --git a/mix/style.xml b/mix/style.xml new file mode 100644 index 000000000..75626ac24 --- /dev/null +++ b/mix/style.xml @@ -0,0 +1,19 @@ + + + + + + CodeStyleData + + false + 4 + 2 + false + 4 + + + + DisplayName + Eth + + From 73040eab7e08373d48e67ed36861976b65b44a2c Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 31 Mar 2015 15:07:05 +0200 Subject: [PATCH 204/228] Clarification about spacing. --- CodingStandards.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodingStandards.txt b/CodingStandards.txt index e1a1b3ba9..a98a74c78 100644 --- a/CodingStandards.txt +++ b/CodingStandards.txt @@ -13,7 +13,7 @@ c. Don't use braces for condition-body one-liners. d. Never place condition bodies on same line as condition. e. Space between first paren and keyword, but *not* following first paren or preceeding final paren. f. No spaces when fewer than intra-expression three parens together; when three or more, space according to clarity. -g. No spaces for subscripting. +g. No spaces for subscripting or unary operators. h. No space before ':' but one after it, except in the ternary operator: one on both sides. i. Space all other operators. j. Braces, when used, always have their own lines and are at same indentation level as "parent" scope. From 01e6801cf5e6b0f9f4cb6f50893e5354caf4137e Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 31 Mar 2015 15:10:21 +0200 Subject: [PATCH 205/228] Style. --- libevmcore/CommonSubexpressionEliminator.cpp | 26 ++++++++++---------- libevmcore/CommonSubexpressionEliminator.h | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index cc2fdc8bd..47bb5b512 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -199,7 +199,7 @@ void CommonSubexpressionEliminator::storeInStorage(ExpressionClasses::Id _slot, if (m_storageContent.count(_slot) && m_storageContent[_slot] == _value) // do not execute the storage if we know that the value is already there return; - m_sequenceNumber ++; + m_sequenceNumber++; decltype(m_storageContent) storageContents; // copy over values at points where we know that they are different from _slot for (auto const& storageItem: m_storageContent) @@ -210,7 +210,7 @@ void CommonSubexpressionEliminator::storeInStorage(ExpressionClasses::Id _slot, m_storeOperations.push_back(StoreOperation(StoreOperation::Storage, _slot, m_sequenceNumber, id)); m_storageContent[_slot] = _value; // increment a second time so that we get unique sequence numbers for writes - m_sequenceNumber ++; + m_sequenceNumber++; } ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(ExpressionClasses::Id _slot) @@ -226,7 +226,7 @@ void CommonSubexpressionEliminator::storeInMemory(ExpressionClasses::Id _slot, E if (m_memoryContent.count(_slot) && m_memoryContent[_slot] == _value) // do not execute the store if we know that the value is already there return; - m_sequenceNumber ++; + m_sequenceNumber++; decltype(m_memoryContent) memoryContents; // copy over values at points where we know that they are different from _slot by at least 32 for (auto const& memoryItem: m_memoryContent) @@ -237,7 +237,7 @@ void CommonSubexpressionEliminator::storeInMemory(ExpressionClasses::Id _slot, E m_storeOperations.push_back(StoreOperation(StoreOperation::Memory, _slot, m_sequenceNumber, id)); m_memoryContent[_slot] = _value; // increment a second time so that we get unique sequence numbers for writes - m_sequenceNumber ++; + m_sequenceNumber++; } ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(ExpressionClasses::Id _slot) @@ -281,7 +281,7 @@ bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item) return false; //@todo: We do not handle the following memory instructions for now: // calldatacopy, codecopy, extcodecopy, mstore8, - // msize (not that msize also depends on memory read access) + // msize (note that msize also depends on memory read access) // the second requirement will be lifted once it is implemented return info.sideEffects || info.args > 2; @@ -592,10 +592,10 @@ bool CSECodeGenerator::removeStackTopIfPossible() void CSECodeGenerator::appendDup(int _fromPosition) { assertThrow(_fromPosition != c_invalidPosition, OptimizerException, ""); - int nr = 1 + m_stackHeight - _fromPosition; - assertThrow(nr <= 16, StackTooDeepException, "Stack too deep."); - assertThrow(1 <= nr, OptimizerException, "Invalid stack access."); - appendItem(AssemblyItem(dupInstruction(nr))); + int instructionNum = 1 + m_stackHeight - _fromPosition; + assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep."); + assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access."); + appendItem(AssemblyItem(dupInstruction(instructionNum))); m_stack[m_stackHeight] = m_stack[_fromPosition]; } @@ -604,10 +604,10 @@ void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition) assertThrow(_fromPosition != c_invalidPosition, OptimizerException, ""); if (_fromPosition == m_stackHeight) return; - int nr = m_stackHeight - _fromPosition; - assertThrow(nr <= 16, StackTooDeepException, "Stack too deep."); - assertThrow(1 <= nr, OptimizerException, "Invalid stack access."); - appendItem(AssemblyItem(swapInstruction(nr))); + int instructionNum = m_stackHeight - _fromPosition; + assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep."); + assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access."); + appendItem(AssemblyItem(swapInstruction(instructionNum))); // The value of a class can be present in multiple locations on the stack. We only update the // "canonical" one that is tracked by m_classPositions if (m_classPositions[m_stack[m_stackHeight]] == m_stackHeight) diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h index 09368f7d2..a9a0c60a4 100644 --- a/libevmcore/CommonSubexpressionEliminator.h +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -164,7 +164,7 @@ public: using StoreOperations = std::vector; /// Initializes the code generator with the given classes and store operations. - /// The store operations have to be sorted ascendingly by sequence number. + /// The store operations have to be sorted by sequence number in ascending order. CSECodeGenerator(ExpressionClasses& _expressionClasses, StoreOperations const& _storeOperations); /// @returns the assembly items generated from the given requirements From 9d2a23b51e881d316ca34fbc1e6d093868628c3a Mon Sep 17 00:00:00 2001 From: Ali Mashatan Date: Tue, 31 Mar 2015 18:15:38 +0430 Subject: [PATCH 206/228] fix #1472 --- mix/qml/Debugger.qml | 27 +++++++++++++ mix/qml/StatesComboBox.qml | 78 +++++++++++++++++++------------------- mix/qml/TransactionLog.qml | 35 +---------------- 3 files changed, 66 insertions(+), 74 deletions(-) diff --git a/mix/qml/Debugger.qml b/mix/qml/Debugger.qml index 358750b24..202d27a0c 100644 --- a/mix/qml/Debugger.qml +++ b/mix/qml/Debugger.qml @@ -219,6 +219,33 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter id: jumpButtons spacing: 3 + + StepActionImage + { + id: playAction + enabledStateImg: "qrc:/qml/img/play_button.png" + disableStateImg: "qrc:/qml/img/play_button.png" + onClicked: console.log("play"); + width: 30 + height: 30 + buttonShortcut: "Ctrl+Shift+F8" + buttonTooltip: qsTr("Play") + visible: true + } + + StepActionImage + { + id: pauseAction + enabledStateImg: "qrc:/qml/img/pause_button.png" + disableStateImg: "qrc:/qml/img/pause_button.png" + onClicked: console.log("pause"); + width: 30 + height: 30 + buttonShortcut: "Ctrl+Shift+F9" + buttonTooltip: qsTr("Pause") + visible: true + } + StepActionImage { id: runBackAction; diff --git a/mix/qml/StatesComboBox.qml b/mix/qml/StatesComboBox.qml index 2c61863ec..26f41aa8d 100644 --- a/mix/qml/StatesComboBox.qml +++ b/mix/qml/StatesComboBox.qml @@ -26,9 +26,9 @@ import QtQuick.Layouts 1.1 import QtGraphicalEffects 1.0 Rectangle { - id:statesComboBox + id: statesComboBox - width:200; + width: 200; height: 20; Component.onCompleted: @@ -72,19 +72,19 @@ Rectangle { smooth:true; Rectangle { - id:chosenItem - width:parent.width; - height:statesComboBox.height; - color: statesComboBox.color; - smooth:true; + id: chosenItem + width: parent.width; + height: statesComboBox.height; + color: statesComboBox.color; + smooth: true; Text { - id:chosenItemText + id: chosenItemText anchors.top: parent.top; anchors.left: parent.left; anchors.margins: 2; color: statesComboBox.colorItem; - text:"" - smooth:true + text: "" + smooth: true } MouseArea { @@ -96,35 +96,34 @@ Rectangle { } Rectangle { - id:dropDownShowdowList - width:statesComboBox.width; + id: dropDownShowdowList + width: statesComboBox.width; opacity: 0.3 - height:0; - clip:true; - radius:4; + height: 0; + clip: true; + radius: 4; anchors.top: chosenItem.top; anchors.margins: 2; color: "gray" } //ToDo: We need scrollbar for items Rectangle { - id:dropDownList - width:statesComboBox.width; - height:0; - clip:true; - radius:4; + id: dropDownList + width: statesComboBox.width; + height: 0; + clip: true; + radius: 4; anchors.top: chosenItem.top; anchors.margins: 2; color: statesComboBox.color - - ColumnLayout{ + ColumnLayout { spacing: 2 TableView { - id:listView - height:20; + id: listView + height: 20; implicitHeight: 0 - width:statesComboBox.width; + width: statesComboBox.width; model: statesComboBox.items horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff; currentRow: -1 @@ -140,14 +139,14 @@ Rectangle { delegate: mainItemDelegate } rowDelegate: Rectangle { - width:statesComboBox.width; + width: statesComboBox.width; height: statesComboBox.rowHeight; } Component { id: mainItemDelegate Rectangle { id: itemDelegate - width:statesComboBox.width; + width: statesComboBox.width; height: statesComboBox.height; Text { id: textItemid @@ -156,13 +155,12 @@ Rectangle { anchors.top: parent.top; anchors.left: parent.left; anchors.margins: 5; - } Image { id: imageItemid - height:20 - width:20; - anchors.right:parent.right + height: 20 + width: 20; + anchors.right: parent.right anchors.top: parent.top; anchors.margins: 5; visible: false; @@ -199,20 +197,20 @@ Rectangle { }//Component }//Table View - RowLayout{ - Rectangle{ + RowLayout { + Rectangle { width: 1 } Text{ - id:createStateText - width:statesComboBox.width; + id: createStateText + width: statesComboBox.width; height: statesComboBox.height; font.bold: true - text:"Create State ..." + text: "Create State ..." MouseArea { anchors.fill: parent; - hoverEnabled : true + hoverEnabled: true onEntered: { createStateText.color = statesComboBox.colorSelect; @@ -232,9 +230,9 @@ Rectangle { } states: State { name: "dropDown"; - PropertyChanges { target: dropDownList; height:(statesComboBox.rowHeight*(statesComboBox.items.count+1)) } - PropertyChanges { target: dropDownShowdowList; width:statesComboBox.width+3; height:(statesComboBox.rowHeight*(statesComboBox.items.count+1))+3 } - PropertyChanges { target:listView; height:20; implicitHeight: (statesComboBox.rowHeight*(statesComboBox.items.count)) } + PropertyChanges { target: dropDownList; height: (statesComboBox.rowHeight*(statesComboBox.items.count+1)) } + PropertyChanges { target: dropDownShowdowList; width: statesComboBox.width+3; height: (statesComboBox.rowHeight*(statesComboBox.items.count+1))+3 } + PropertyChanges { target: listView; height: 20; implicitHeight: (statesComboBox.rowHeight*(statesComboBox.items.count)) } } } diff --git a/mix/qml/TransactionLog.qml b/mix/qml/TransactionLog.qml index e5787f19b..1772c7f18 100644 --- a/mix/qml/TransactionLog.qml +++ b/mix/qml/TransactionLog.qml @@ -10,38 +10,6 @@ Item { property ListModel transactionModel: ListModel{} property ListModel callModel: ListModel{} - Action { - id: addStateAction - text: "Add State" - shortcut: "Ctrl+Alt+T" - enabled: codeModel.hasContract && !clientModel.running; - onTriggered: projectModel.stateListModel.addState(); - } - Action { - id: editStateAction - text: "Edit State" - shortcut: "Ctrl+Alt+T" - enabled: codeModel.hasContract && !clientModel.running && statesCombo.selectedIndex >= 0 && projectModel.stateListModel.count > 0; - onTriggered: projectModel.stateListModel.editState(statesCombo.selectedIndex); - } - Action { - id: playAndPauseAction - checkable: true; - checked: false; - iconSource: "qrc:/qml/img/play_button.png" - onToggled: { - if (checked) - { - this.iconSource = "qrc:/qml/img/pause_button2x.png" - console.log("play"); - }else{ - this.iconSource = "qrc:/qml/img/play_button2x.png" - console.log("pause"); - } - } - enabled: true - } - ColumnLayout { anchors.fill: parent RowLayout { @@ -80,8 +48,7 @@ Item { StatesComboBox { id: statesCombo - items:projectModel.stateListModel - //onSelectItem: console.log("Combobox Select Item: " + item ) + items: projectModel.stateListModel onSelectCreate: projectModel.stateListModel.addState(); onEditItem: projectModel.stateListModel.editState(item) colorItem: "black" From bcb76ac2a7593d93311fdbb5968a62938b45ca28 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 31 Mar 2015 16:56:02 +0200 Subject: [PATCH 207/228] logs window redesign --- mix/qml/LogsPane.qml | 395 +++++++++++++++++++++++--------- mix/qml/LogsPaneStyle.qml | 10 +- mix/qml/StatusPane.qml | 13 +- mix/qml/img/broom.png | Bin 501 -> 0 bytes mix/qml/img/clearicon.png | Bin 0 -> 2032 bytes mix/qml/img/cleariconactive.png | Bin 0 -> 2187 bytes mix/qml/img/copyicon.png | Bin 0 -> 871 bytes mix/qml/img/copyiconactive.png | Bin 0 -> 875 bytes mix/res.qrc | 5 +- 9 files changed, 295 insertions(+), 128 deletions(-) delete mode 100644 mix/qml/img/broom.png create mode 100644 mix/qml/img/clearicon.png create mode 100644 mix/qml/img/cleariconactive.png create mode 100644 mix/qml/img/copyicon.png create mode 100644 mix/qml/img/copyiconactive.png diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml index 1d832c21a..739ebc674 100644 --- a/mix/qml/LogsPane.qml +++ b/mix/qml/LogsPane.qml @@ -15,10 +15,11 @@ Rectangle anchors.fill: parent radius: 5 - color: LogsPaneStyle.generic.layout.backgroundColor - ColumnLayout { + color: "transparent" + id: logsPane + Column { z: 2 - height: parent.height + height: parent.height - rowAction.height width: parent.width spacing: 0 @@ -26,91 +27,129 @@ Rectangle id: logsModel } - TableView { - id: logsTable - clip: true - Layout.fillWidth: true - Layout.preferredHeight: parent.height - rowAction.height - headerVisible : false - onDoubleClicked: + ScrollView + { + id: scrollView + height: parent.height + width: parent.width + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + ColumnLayout { - var log = logsModel.get(logsTable.currentRow); - if (log) - clipboard.text = (log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content); - } + id: logsRect + spacing: 0 + Repeater { + clip: true + model: SortFilterProxyModel { + id: proxyModel + source: logsModel + property var roles: ["-", "javascript", "run", "state"] - model: SortFilterProxyModel { - id: proxyModel - source: logsModel - property var roles: ["-", "javascript", "run", "state"] + Component.onCompleted: { + filterType = regEx(proxyModel.roles); + } - Component.onCompleted: { - filterType = regEx(proxyModel.roles); - } + function search(_value) + { + filterContent = _value; + } - function search(_value) - { - filterContent = _value; - } + function toogleFilter(_value) + { + var count = roles.length; + for (var i in roles) + { + if (roles[i] === _value) + { + roles.splice(i, 1); + break; + } + } + if (count === roles.length) + roles.push(_value); - function toogleFilter(_value) - { - var count = roles.length; - for (var i in roles) - { - if (roles[i] === _value) + filterType = regEx(proxyModel.roles); + } + + function regEx(_value) { - roles.splice(i, 1); - break; + return "(?:" + roles.join('|') + ")"; } + + filterType: "(?:javascript|run|state)" + filterContent: "" + filterSyntax: SortFilterProxyModel.RegExp + filterCaseSensitivity: Qt.CaseInsensitive } - if (count === roles.length) - roles.push(_value); - filterType = regEx(proxyModel.roles); - } + Rectangle + { + width: 750 + height: 30 + color: + { + if (level === "warning" || level === "error") + return "#fffcd5"; + else + return index % 2 === 0 ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor; + } - function regEx(_value) - { - return "(?:" + roles.join('|') + ")"; - } - filterType: "(?:javascript|run|state)" - filterContent: "" - filterSyntax: SortFilterProxyModel.RegExp - filterCaseSensitivity: Qt.CaseInsensitive - } - TableViewColumn - { - role: "date" - title: qsTr("date") - width: LogsPaneStyle.generic.layout.dateWidth - delegate: itemDelegate - } - TableViewColumn - { - role: "type" - title: qsTr("type") - width: LogsPaneStyle.generic.layout.typeWidth - delegate: itemDelegate - } - TableViewColumn - { - role: "content" - title: qsTr("content") - width: LogsPaneStyle.generic.layout.contentWidth - delegate: itemDelegate - } + DefaultLabel { + text: date; + font.family: LogsPaneStyle.generic.layout.logLabelFont + width: LogsPaneStyle.generic.layout.dateWidth + font.pointSize: Style.absoluteSize(-1) + anchors.left: parent.left + anchors.leftMargin: 15 + anchors.verticalCenter: parent.verticalCenter + color: { + parent.getColor(level); + } + } - rowDelegate: Item { - Rectangle { - width: logsTable.width - 4 - height: 17 - color: styleData.alternate ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor + DefaultLabel { + text: type; + font.family: LogsPaneStyle.generic.layout.logLabelFont + width: LogsPaneStyle.generic.layout.typeWidth + font.pointSize: Style.absoluteSize(-1) + anchors.left: parent.left + anchors.leftMargin: 100 + anchors.verticalCenter: parent.verticalCenter + color: { + parent.getColor(level); + } + } + + DefaultLabel { + text: content; + font.family: LogsPaneStyle.generic.layout.logLabelFont + width: LogsPaneStyle.generic.layout.contentWidth + font.pointSize: Style.absoluteSize(-1) + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 190 + color: { + parent.getColor(level); + } + } + + function getColor() + { + if (level === "error") + return "red"; + else if (level === "warning") + return "orange"; + else + return "#808080"; + } + } } } + + } + Component { id: itemDelegate DefaultLabel { @@ -128,58 +167,43 @@ Rectangle } } + } + + Rectangle + { + gradient: Gradient { + GradientStop { position: 0.0; color: "#f1f1f1" } + GradientStop { position: 1.0; color: "#d9d7da" } + } + Layout.preferredHeight: LogsPaneStyle.generic.layout.headerHeight + height: LogsPaneStyle.generic.layout.headerHeight + width: logsPane.width + anchors.bottom: parent.bottom Row { id: rowAction - Layout.preferredHeight: LogsPaneStyle.generic.layout.headerHeight - height: LogsPaneStyle.generic.layout.headerHeight anchors.leftMargin: LogsPaneStyle.generic.layout.leftMargin anchors.left: parent.left spacing: LogsPaneStyle.generic.layout.headerButtonSpacing - Button - { - height: LogsPaneStyle.generic.layout.headerButtonHeight - anchors.verticalCenter: parent.verticalCenter - action: clearAction - iconSource: "qrc:/qml/img/broom.png" - } - - Action { - id: clearAction - enabled: logsModel.count > 0 - tooltip: qsTr("Clear") - onTriggered: { - logsModel.clear() - } - } - - Button + height: parent.height + Rectangle { - height: LogsPaneStyle.generic.layout.headerButtonHeight + color: "transparent" + height: 20 + width: 50 anchors.verticalCenter: parent.verticalCenter - action: copytoClipBoardAction - iconSource: "qrc:/qml/img/copy.png" - } - - Action { - id: copytoClipBoardAction - enabled: logsModel.count > 0 - tooltip: qsTr("Copy to Clipboard") - onTriggered: { - var content = ""; - for (var k = 0; k < logsModel.count; k++) - { - var log = logsModel.get(k); - content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content + "\n"; - } - clipboard.text = content; + DefaultLabel + { + color: "#808080" + font.family: LogsPaneStyle.generic.layout.logLabelFont + text: qsTr("Show:") } } Rectangle { anchors.verticalCenter: parent.verticalCenter width: 1; - height: parent.height - 10 + height: parent.height color : "#808080" } @@ -187,6 +211,7 @@ Rectangle id: javascriptButton checkable: true height: LogsPaneStyle.generic.layout.headerButtonHeight + width: 20 anchors.verticalCenter: parent.verticalCenter checked: true onCheckedChanged: { @@ -202,16 +227,28 @@ Rectangle font.pointSize: Style.absoluteSize(-3) color: LogsPaneStyle.generic.layout.logLabelColor anchors.centerIn: parent - text: qsTr("JavaScript") + text: qsTr("JS") } } + background: + Rectangle { + color: javascriptButton.checked ? "#cfcfcf" : "transparent" + } } } + Rectangle { + anchors.verticalCenter: parent.verticalCenter + width: 1; + height: parent.height + color : "#808080" + } + ToolButton { id: runButton checkable: true height: LogsPaneStyle.generic.layout.headerButtonHeight + width: 30 anchors.verticalCenter: parent.verticalCenter checked: true onCheckedChanged: { @@ -230,14 +267,26 @@ Rectangle text: qsTr("Run") } } + background: + Rectangle { + color: runButton.checked ? "#cfcfcf" : "transparent" + } } } + Rectangle { + anchors.verticalCenter: parent.verticalCenter + width: 1; + height: parent.height + color : "#808080" + } + ToolButton { id: stateButton checkable: true height: LogsPaneStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter + width: 35 checked: true onCheckedChanged: { proxyModel.toogleFilter("state") @@ -250,26 +299,144 @@ Rectangle DefaultLabel { font.family: LogsPaneStyle.generic.layout.logLabelFont font.pointSize: Style.absoluteSize(-3) - color: "#5391d8" + color: LogsPaneStyle.generic.layout.logLabelColor anchors.centerIn: parent text: qsTr("State") } } + background: + Rectangle { + color: stateButton.checked ? "#cfcfcf" : "transparent" + } + } + } + + Rectangle { + anchors.verticalCenter: parent.verticalCenter + width: 1; + height: parent.height + color : "#808080" + } + } + + Row + { + height: parent.height + anchors.right: parent.right + anchors.rightMargin: 4 + spacing: 4 + Button + { + id: clearButton + height: LogsPaneStyle.generic.layout.headerButtonHeight + anchors.verticalCenter: parent.verticalCenter + action: hideAction + iconSource: "qrc:/qml/img/cleariconactive.png" + style: + ButtonStyle { + background: + Rectangle { + height: LogsPaneStyle.generic.layout.headerButtonHeight + implicitHeight: LogsPaneStyle.generic.layout.headerButtonHeight + color: "transparent" + } + } + } + + Image { + id: clearImage + source: "qrc:/qml/img/cleariconactive.png" + anchors.centerIn: parent + fillMode: Image.PreserveAspectFit + width: 30 + height: 30 + } + + Button + { + id: exitButton + height: LogsPaneStyle.generic.layout.headerButtonHeight + anchors.verticalCenter: parent.verticalCenter + action: exitAction + iconSource: "qrc:/qml/img/exit.png" + style: + ButtonStyle { + background: + Rectangle { + height: LogsPaneStyle.generic.layout.headerButtonHeight + color: "transparent" + } + } + } + + Button + { + id: copyButton + height: LogsPaneStyle.generic.layout.headerButtonHeight + anchors.verticalCenter: parent.verticalCenter + action: copytoClipBoardAction + iconSource: "qrc:/qml/img/copyiconactive.png" + style: + ButtonStyle { + background: + Rectangle { + height: LogsPaneStyle.generic.layout.headerButtonHeight + color: "transparent" + } + } + } + + Action { + id: clearAction + tooltip: qsTr("Hide") + onTriggered: { + logsPane.parent.toggle(); + } + } + + Action { + id: hideAction + enabled: logsModel.count > 0 + tooltip: qsTr("Clear") + onTriggered: { + logsModel.clear() + } + } + + Action { + id: copytoClipBoardAction + enabled: logsModel.count > 0 + tooltip: qsTr("Copy to Clipboard") + onTriggered: { + var content = ""; + for (var k = 0; k < logsModel.count; k++) + { + var log = logsModel.get(k); + content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content + "\n"; + } + clipboard.text = content; } } DefaultTextField { id: searchBox - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: LogsPaneStyle.generic.layout.headerButtonHeight - 5 anchors.verticalCenter: parent.verticalCenter - width: LogsPaneStyle.generic.layout.headerInputWidth + width: LogsPaneStyle.generic.layout.headerInputWidth - 40 font.family: LogsPaneStyle.generic.layout.logLabelFont font.pointSize: Style.absoluteSize(-3) font.italic: true + text: qsTr("Search") onTextChanged: { proxyModel.search(text); } + style: + TextFieldStyle { + background: Rectangle { + radius: 10 + } + } } } } diff --git a/mix/qml/LogsPaneStyle.qml b/mix/qml/LogsPaneStyle.qml index 1c5e2f4f5..17d30ff6c 100644 --- a/mix/qml/LogsPaneStyle.qml +++ b/mix/qml/LogsPaneStyle.qml @@ -11,17 +11,17 @@ QtObject { property QtObject generic: QtObject { property QtObject layout: QtObject { property string backgroundColor: "#f7f7f7" - property int headerHeight: 35 - property int headerButtonSpacing: 5 + property int headerHeight: 30 + property int headerButtonSpacing: 1 property int leftMargin: 10 property int headerButtonHeight: 30 - property string logLabelColor: "#5391d8" + property string logLabelColor: "#808080" property string logLabelFont: "sans serif" property int headerInputWidth: 200 property int dateWidth: 70 - property int typeWidth: 70 + property int typeWidth: 90 property int contentWidth: 250 - property string logAlternateColor: "#f0f0f0" + property string logAlternateColor: "#f6f5f6" } } } diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index e097c866b..b42b13fef 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -45,7 +45,7 @@ Rectangle { function errorMessage(text, type) { status.state = "error"; - status.text = text + status.text = text; logPane.push("error", type, text); } @@ -76,7 +76,7 @@ Rectangle { function format(_message) { var formatted = _message.match(/(?:)/); - if (formatted) + if (!formatted) formatted = _message.match(/(?:)/); if (formatted && formatted.length > 1) formatted = formatted[1]; @@ -207,11 +207,9 @@ Rectangle { } id: logsContainer - width: 650 - //height: 0 - anchors.topMargin: 10 + width: 750 + anchors.topMargin: -30 anchors.top: statusContainer.bottom - //anchors.left: statusContainer.left anchors.horizontalCenter: parent.horizontalCenter visible: false radius: 5 @@ -227,9 +225,8 @@ Rectangle { top = top.parent var coordinates = logsContainer.mapToItem(top, 0, 0); logsContainer.parent = top; - //logsContainer.x = coordinates.x + 150; + logsContainer.x = coordinates.x + 150; logsContainer.y = coordinates.y; - console.log(logsContainer.x); } LogsPane diff --git a/mix/qml/img/broom.png b/mix/qml/img/broom.png deleted file mode 100644 index 76a9a0e0c90f9e671c3bbe3708f610c4a67f6383..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 501 zcmVl0knEFf(l4s2f?(imaF8NZjcWVO_}LI%a>%36+i~6T z^SQ6|3LtJhk^&Jz2utc^beF3b$swE(%#*EL^P}y!?)c0Gl3UrZ22;|P1>z-6qMhu) zSc&&J#k}OZ**r#`z41$?i>u|{BT{dCD|@ZL88aFoS+UGi|GK$H7_unV$Vw4NT7uEW zm-O+BfvnnV%>p9leRp*vN5T*w=e|-;#;Y22rS3Px+s!2paRA>e5ntfWkEm&;17j4?BOR^BeKSH7) z3M(pzlKP`01WEiSQd_0DNwT$=jhHnQMWXVL+&{}mH%EdKH`+^|-{qXA^W5G0Joi4& zy*c7Ja5yjboZs*J{r%3%z31GFjC4p_AZ>xP1=1EsTVT#uATvxPwY9Y+MMXtTSy@@N z=$S8ZW@aX$@}l};W@hFkNhMBCPxp_Fjdl0;_x~ji!w7*BZfvr$`z7s* zunc3A^=Z$FVhA6G>qfFE4`GoPFQ_fA6aDu{JMlD0ouA?DnXp=*PNBe@v!YiNA4KUh zWJ4znj+%)gqPt!p-$3C35qJLl`EQmjTULyRWo~DX-bMKi>SeoUBI5_wR zx-O$rUSUP|%19)#cW7ux8^KX`LJzCg#bW7=MbAKXrzwf@i>9Yh+=i#cAPJ3W%1I{A z3cU~AzpB2)3Ts?ZaS5wcal0YmZSvDb*~&VQ-pHIj$h`9P;b52Rp)Au&UTJmMt}yHJ ztTZlRTDRdby%_uqvCdf%RE3>1)lU`{79I$jG+sUm>3)Hsu(l~kj!7cI*8Gs3EE4aL z#GRKeq(k>K*!CLiqL_`kXx5dmELOCtQ7xy&$H(RSG+}Tc{RAuMXJLApkCZNe>8Vzu zusdRPB`hZu3=wM%GGtEiS$4%yf&qjBF6G?sWCj#^Ak7j=hJQzz2A^naPEr_0L9$_;E$>_(lHbtR1TqjN%zQtcg6 zDceaSYkdHu%QPkvyROrzD`6T$t;D_-C}C0VM4I7!sIs#1CJoL{YQq@CZkOSSFcw>G z?-)ecP8vO}ixw?PiZm$!t86-7l;zlVh9T~>p=S9BuPJ{f!VO@YAbd1u-xKd zHM!W5eYm>5zP>c3XMhD4(uchc?}mnkTfn$dAPHu{?gr15u)4fDp*m*e=H@oL2??U! zf%Lk?ix+?8g*3ZJD;V?%%ZYdwZ?1$DiRdRGi^8@bh{cm(Cr#V6LYh+y>xRRnZzkAY zF`h*+8+FlaBEszqk#W`f0Z#wrb}!W6L>h&zq%p>NhI6HWQdryL0gOu+ z{3k@qX$2(0I))5rt zvkGHOU#EiSuORzn8W#N#fe!*mZty}64CN0Y_=1C?;4jleiQLXXQtopsf+(-1x)&hP zmv#p*fmJC_?=b}m7Qy88=1}^28q!(`N(WG|RhJfA*N2f}Soy^w<`q}4 z2qr)J5-wVHVJsv+EnT{FkovU%$RQRppEfU9vV{As!C&m{11M)g_9f)(N7t<)PkDBW z-`jub>v;}KV>2j=cTi_8UOxclWvs4WNzE?4^K3iykbH}19vT1lKecG&Zoe%-x`Qv4 zkMaC>koUEy$-8q!MMVRBUqjFRT9-<1+;2-72xrW$Kc+$a8tr;Wk70mwan-#?C>SC*gIk|uaH~jbESs>FDX$zz+khZ{Fw!lBxaB+RQQH;I- O0000Px-MM*?KRA>e5T5D_-MHD`>w=baB5~Qt(S|}1zh|!?(Dkd0IdI13+5X2WIgdau? z6_tP{_K#FlC{YumA^s5%!3Pq}tu+Kfc!@7e6k;kMY9bE_G=lWO?)aU(JA3zTclYjn zVH5AP+ZFNz0Gver1PMA}NPYMDF0F0%^4hcc!j*Hv(N!H+Q%UiBB5fu>HwZyR z>42A#ER;P7p(v0-(7NL zuB@Il;VI3>$_7+uPeIXg5~4DRh$I7`ze6XqlOgsSq@R?!cABX36w5{AkQzN=RBM8& z1!?s{lQTeY0*1GitVf>1RA{-_X}rF(c|=#ze`#j2B7E2CU#=k}Tr@Z#_m8FrgC^}&=M#|(QVJQz@GJ0pVFTL*fr1CT}8554KJ~lx_ zw5>#<+HOzE!?5T}vAqX2(Uy6-U7lg0aqVxj1j!Z`77_0$Ens>Y!}rlx)9{!o%SSwI&CzlAyloXiT&46Ix_mfY(#ea1(&Q7z zhcT^7{#)+C5N=N{%YE*Pr|S+-PtQ0hu?Sj$M;T*M`7m*43!4j6r1s}wqW|C z3GWf+n<~p!-2Uv17hvhxu=JntHbgaJV~kZ;X&OVNKt=)*A2ciuV z)?W_Ny!*m7bdI?(VF36nniy>`{)v=?c?SgxE>|kB^?NbaREw=25#lB%X~@mLT%>tU zV=66CxELd2Re8TSCE@sXF}{NWx$k0~@3uIVd6J$qe#7OkblaY<*1^h$Ogz}-nOL3R zJksbG|6m>jN=rKI@1NA~i8YPDW}|P2wjOWALSpwgNxAu-&Z7_RY`&cj19=O)If2za zK<{fFBmb`Qam9;Iob{mN9 zW*HN!c~bMQ5llFK#4~H-Bfkm>-ck7mPtuij8}m&XCzN**pv+@wBI`=|61EnA21{P= z(r`Wv5&DoOF?T>k-TV0@-K}@UU<$*6*ATRL`4Z-v3UzpgS-;-dG~!$czNUzqH0JjG z`6Rvbnd|?;5!5_PFlNT?ht-#`c`c)AN%~SH!^WPZE9*Ap%hJ*ipDS!h*j1qw`x3?w ziQhXxP#E%nt6r#VAJQc58(1;&gM5(2h(2tCa$vX1tb{*dT(~J=UFhZwh03QByGdgy zy_ZSfy!e~qbo8W>#lm&cmT_RW%dCViVa!|mV2?SNF=1}Ui*(iKs+^LJ=$cK~i9Ix7 z`zjSRPIsQkim(@H?0D{}s;}CdD@*I*>xXU0*3!c&hF00w9N6tLE8$BRCbj1T zU!}S$W|Mc5#x`_z)}(b~n8M`5uF6#GOW1UOSWB{nt!Vd2{UOrNRd3!4Z zV$6))51cPy%Xwv9<*=`(xO8CDPf0Gy`;aCvv$mrA<1nNnrA4!VL0us3339yo5{{Ed z90@gz7UzOkkPbI#tll%2bo9v%)5GCYSAfK_#D59QiLyj0m@xdaj+?>TrnUY$BT+R! zDKFAAvc#8kwDs6*EOuiUjfoYQgOCLhHt&YupH&Jp(pspwIk1kB%_eq}#u_r+lQggh zp}khv66Bk~%A7kjC?hsqpeQ<2&ql&GR2TYEjtzo-Kj&R-9Pg){b4; zk?x|#d*^Eg54r>INS=p1CaJc}_`iI%q<{oHOat)?7(?ySFh0jM>S4ZF7nJRkPLStu*?K%oZ;JPx&AxT6*RA>e5o4;=pF%-w|l5$+s3@jA`5?clsDzS6|DMO_EV2EHtELGL18xv)r z142|x4EzH~3_u13M1E943T&`3v`dyQhyhhXD5w$n)vv1y%BA($@!5B_a+a(++wb|k zkDqh#%MZ(1uwtv^&FAw=uIt{hZF>`TRs?N*Xfzr-g1Tqamd^A@>;UY8R4Vm6!?bBKDxs*qoX%mwHSaQ%li3Cc~(<#sxq zPq+_Nlx^v9xqK1M_K0_I{uQ?6I*+G_<`>A_Lj3`~`?!Ae7J;bmhK+QJ%%W?-B7KH$ zw*mgw^Klys@+^$MS_2a>+0fbp%Z4@rSSGYFz_Or?0+s=79B^1@BZ0#}8w)H7tqeGu z%?@ymzMD>`i>ObDgDT2-@6$NtZP08fOCD;q+7L$H(+sovak_Gf#o`)Ej*O2+qhlOD zcTt~s@>ghj+`v(%dT@RPJL4#FE5olguEV#Wz3}CB~RPQH`G3Z={Tn0KM z)f=Gg;z|+#iPdhST;*1W^kpk-g9XW8^(6Io=2^|}7T+p!s#{nHXaMaLR z07nU}4KNKlDAgl6^A_H-^t|)> zjAaxszM%UBkb5*3o_?dYG!GCrB#4$ x;#TxeSJSqG?*FHH?RNY0pD{tg$ymS&oB)dpf?$zAFg5@H002ovPDHLkV1mRUlNbO1 literal 0 HcmV?d00001 diff --git a/mix/qml/img/copyiconactive.png b/mix/qml/img/copyiconactive.png new file mode 100644 index 0000000000000000000000000000000000000000..387d7f1fa62de3d8c41537c20f31479c09aa6ec5 GIT binary patch literal 875 zcmV-x1C;!UP)Px&B}qge5o6l<#K@`W|+k`Yl54{u-p=s2M9tvK(6+8%S>Ou4%h!+u0g3{!o zUbG-$Q7}RL2fSH(@E{u7i$899^I$JRXawoOixkB|viqD(8k-HfJCoU+-5s)fNOor4 zoA*BRW|RGfEWn5zsFL^8(z|Sa;_x{nxC#i9Qn4&}Rj(B8O8L4tWnKD0>&j0Q%Ld>> z1}lFUaAKj>%)MDG+?JnY&of9Wd$YKoVX4||AmTIuZ3DrUyP2~W0jJmkjT1o|klL)B z?^@7u7|_aqOV_`R;gPW&J6R%nRxcMWcN6!2U8puc0^%%NpdUo$CBV#UMA+B$3Jz`f zhmUu(UMYS6GS9GT|LJCex7hZ65G1Qa1KVI>Lnj_MY-lZj!-UobI4o$bfWv^+4mc>Z zmcT)vwFQ=iRtCI(_GsHcJfAVLa~Z?9ZzrhYaQmJvRX4AA#Zo37RxhB*g1+<$%<5FPF9nw10VYLn@)kiz9wt@l^~0U6ZXU~_v0?WSS$FE?>E89+ zZwAUfr5#aN!|l^FsMH&bA<~Wv*iI%1(2)Ws0XlNvxS?YKjuSdI;JBb;1&#wccHpR? z69F70bYg(Lp#7wt?KA8ldM`cfybgO+QqK`Yun`HcDyb*P=rBvvmUd*OsG+pNs->RH znkWTcs}x?yB-N36z}L1ibC2AXPJv~%iB3Lgg>|ZyOs*qWVWnS7G8ORiIs0Vmc*|;i z^|P_Z2GiYU%{SgI7T0_2!FR1HJdW94zp3?n5MsA&kwxn}+tvze zlZOpx1-yFW;7raPfq5&W`*>Pm-7Cn;Kdmrtg>)ZJE3A73dHJUm z=B<$K<3fLGu^~&f?_ZelOw_;@nDb2itc4Xqml/fonts/SourceSerifPro-Semibold.ttf qml/img/available_updates.png qml/img/b64.png - qml/img/broom.png qml/img/bugiconactive.png qml/img/bugiconinactive.png qml/img/closedtriangleindicator.png @@ -53,5 +52,9 @@ stdc/config.sol stdc/namereg.sol stdc/std.sol + qml/img/clearicon.png + qml/img/cleariconactive.png + qml/img/copyicon.png + qml/img/copyiconactive.png From 323a1aea0e6c81b58b757c95eaf67612c9c9eae9 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 31 Mar 2015 18:15:45 +0200 Subject: [PATCH 208/228] ui changes --- mix/qml/LogsPane.qml | 244 +++++++++++++++++++++++++------------- mix/qml/LogsPaneStyle.qml | 2 +- 2 files changed, 161 insertions(+), 85 deletions(-) diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml index 739ebc674..765f905b1 100644 --- a/mix/qml/LogsPane.qml +++ b/mix/qml/LogsPane.qml @@ -189,12 +189,13 @@ Rectangle Rectangle { color: "transparent" - height: 20 - width: 50 - anchors.verticalCenter: parent.verticalCenter + height: parent.height + width: 40 DefaultLabel { + anchors.verticalCenter: parent.verticalCenter color: "#808080" + font.pointSize: Style.absoluteSize(-3) font.family: LogsPaneStyle.generic.layout.logLabelFont text: qsTr("Show:") } @@ -204,7 +205,7 @@ Rectangle anchors.verticalCenter: parent.verticalCenter width: 1; height: parent.height - color : "#808080" + color : "transparent" } ToolButton { @@ -232,7 +233,7 @@ Rectangle } background: Rectangle { - color: javascriptButton.checked ? "#cfcfcf" : "transparent" + color: javascriptButton.checked ? "#dcdcdc" : "transparent" } } } @@ -241,7 +242,14 @@ Rectangle anchors.verticalCenter: parent.verticalCenter width: 1; height: parent.height - color : "#808080" + color : "#d3d0d0" + } + + Rectangle { + anchors.verticalCenter: parent.verticalCenter + width: 1; + height: parent.height + color : "#f2f1f2" } ToolButton { @@ -269,7 +277,7 @@ Rectangle } background: Rectangle { - color: runButton.checked ? "#cfcfcf" : "transparent" + color: runButton.checked ? "#dcdcdc" : "transparent" } } } @@ -278,7 +286,14 @@ Rectangle anchors.verticalCenter: parent.verticalCenter width: 1; height: parent.height - color : "#808080" + color : "#d3d0d0" + } + + Rectangle { + anchors.verticalCenter: parent.verticalCenter + width: 1; + height: parent.height + color : "#f2f1f2" } ToolButton { @@ -306,7 +321,7 @@ Rectangle } background: Rectangle { - color: stateButton.checked ? "#cfcfcf" : "transparent" + color: stateButton.checked ? "#dcdcdc" : "transparent" } } } @@ -315,7 +330,14 @@ Rectangle anchors.verticalCenter: parent.verticalCenter width: 1; height: parent.height - color : "#808080" + color : "#d3d0d0" + } + + Rectangle { + anchors.verticalCenter: parent.verticalCenter + width: 1; + height: parent.height + color : "#f2f1f2" } } @@ -323,98 +345,97 @@ Rectangle { height: parent.height anchors.right: parent.right - anchors.rightMargin: 4 - spacing: 4 - Button + anchors.rightMargin: 10 + spacing: 10 + Rectangle { - id: clearButton height: LogsPaneStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter - action: hideAction - iconSource: "qrc:/qml/img/cleariconactive.png" - style: - ButtonStyle { - background: - Rectangle { - height: LogsPaneStyle.generic.layout.headerButtonHeight - implicitHeight: LogsPaneStyle.generic.layout.headerButtonHeight - color: "transparent" + color: "transparent" + width: 20 + Button + { + id: clearButton + action: clearAction + anchors.fill: parent + anchors.verticalCenter: parent.verticalCenter + height: 25 + style: + ButtonStyle { + background: + Rectangle { + height: LogsPaneStyle.generic.layout.headerButtonHeight + implicitHeight: LogsPaneStyle.generic.layout.headerButtonHeight + color: "transparent" + } } } - } - Image { - id: clearImage - source: "qrc:/qml/img/cleariconactive.png" - anchors.centerIn: parent - fillMode: Image.PreserveAspectFit - width: 30 - height: 30 - } + Image { + id: clearImage + source: "qrc:/qml/img/cleariconactive.png" + anchors.centerIn: parent + fillMode: Image.PreserveAspectFit + width: 20 + height: 20 + } - Button - { - id: exitButton - height: LogsPaneStyle.generic.layout.headerButtonHeight - anchors.verticalCenter: parent.verticalCenter - action: exitAction - iconSource: "qrc:/qml/img/exit.png" - style: - ButtonStyle { - background: - Rectangle { - height: LogsPaneStyle.generic.layout.headerButtonHeight - color: "transparent" + Action { + id: clearAction + enabled: logsModel.count > 0 + tooltip: qsTr("Clear") + onTriggered: { + logsModel.clear(); } } } - Button + Rectangle { - id: copyButton height: LogsPaneStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter - action: copytoClipBoardAction - iconSource: "qrc:/qml/img/copyiconactive.png" - style: - ButtonStyle { - background: - Rectangle { - height: LogsPaneStyle.generic.layout.headerButtonHeight - color: "transparent" + color: "transparent" + width: 20 + Button + { + id: copyButton + action: copyAction + anchors.fill: parent + anchors.verticalCenter: parent.verticalCenter + height: 25 + style: + ButtonStyle { + background: + Rectangle { + height: LogsPaneStyle.generic.layout.headerButtonHeight + implicitHeight: LogsPaneStyle.generic.layout.headerButtonHeight + color: "transparent" + } } } - } - - Action { - id: clearAction - tooltip: qsTr("Hide") - onTriggered: { - logsPane.parent.toggle(); - } - } - Action { - id: hideAction - enabled: logsModel.count > 0 - tooltip: qsTr("Clear") - onTriggered: { - logsModel.clear() + Image { + id: copyImage + source: "qrc:/qml/img/copyiconactive.png" + anchors.centerIn: parent + fillMode: Image.PreserveAspectFit + width: 20 + height: 20 } - } - Action { - id: copytoClipBoardAction - enabled: logsModel.count > 0 - tooltip: qsTr("Copy to Clipboard") - onTriggered: { - var content = ""; - for (var k = 0; k < logsModel.count; k++) - { - var log = logsModel.get(k); - content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content + "\n"; + Action { + id: copyAction + enabled: logsModel.count > 0 + tooltip: qsTr("Copy to Clipboard") + onTriggered: { + var content = ""; + for (var k = 0; k < logsModel.count; k++) + { + var log = logsModel.get(k); + content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content + "\n"; + } + clipboard.text = content; } - clipboard.text = content; } } @@ -427,10 +448,22 @@ Rectangle font.family: LogsPaneStyle.generic.layout.logLabelFont font.pointSize: Style.absoluteSize(-3) font.italic: true - text: qsTr("Search") + text: qsTr(" - Search - ") + onFocusChanged: + { + if (!focus && text === "") + text = qsTr(" - Search - "); + else if (focus && text === qsTr(" - Search - ")) + text = ""; + } + onTextChanged: { - proxyModel.search(text); + if (text === qsTr(" - Search - ")) + proxyModel.search(""); + else + proxyModel.search(text); } + style: TextFieldStyle { background: Rectangle { @@ -438,6 +471,49 @@ Rectangle } } } + + + Rectangle + { + height: LogsPaneStyle.generic.layout.headerButtonHeight + anchors.verticalCenter: parent.verticalCenter + color: "transparent" + width: 20 + Button + { + id: hideButton + action: hideAction + anchors.fill: parent + anchors.verticalCenter: parent.verticalCenter + height: 25 + style: + ButtonStyle { + background: + Rectangle { + height: LogsPaneStyle.generic.layout.headerButtonHeight + implicitHeight: LogsPaneStyle.generic.layout.headerButtonHeight + color: "transparent" + } + } + } + + Image { + id: hideImage + source: "qrc:/qml/img/exit.png" + anchors.centerIn: parent + fillMode: Image.PreserveAspectFit + width: 20 + height: 20 + } + + Action { + id: hideAction + tooltip: qsTr("Exit") + onTriggered: { + logsPane.parent.toggle(); + } + } + } } } } diff --git a/mix/qml/LogsPaneStyle.qml b/mix/qml/LogsPaneStyle.qml index 17d30ff6c..215f17bd7 100644 --- a/mix/qml/LogsPaneStyle.qml +++ b/mix/qml/LogsPaneStyle.qml @@ -12,7 +12,7 @@ QtObject { property QtObject layout: QtObject { property string backgroundColor: "#f7f7f7" property int headerHeight: 30 - property int headerButtonSpacing: 1 + property int headerButtonSpacing: 0 property int leftMargin: 10 property int headerButtonHeight: 30 property string logLabelColor: "#808080" From 3d7b28afb7a5ae740f5025056a4041d64420afcc Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 31 Mar 2015 18:20:31 +0200 Subject: [PATCH 209/228] add disabled clear button and copy button --- mix/qml/LogsPane.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml index 765f905b1..21bd9fd96 100644 --- a/mix/qml/LogsPane.qml +++ b/mix/qml/LogsPane.qml @@ -373,7 +373,7 @@ Rectangle Image { id: clearImage - source: "qrc:/qml/img/cleariconactive.png" + source: clearAction.enabled ? "qrc:/qml/img/cleariconactive.png" : "qrc:/qml/img/clearicon.png" anchors.centerIn: parent fillMode: Image.PreserveAspectFit width: 20 @@ -416,7 +416,7 @@ Rectangle Image { id: copyImage - source: "qrc:/qml/img/copyiconactive.png" + source: copyAction.enabled ? "qrc:/qml/img/copyiconactive.png" : "qrc:/qml/img/copyicon.png" anchors.centerIn: parent fillMode: Image.PreserveAspectFit width: 20 From 7bc2251eed74de29862c3cdd37ca24e3840aaaa8 Mon Sep 17 00:00:00 2001 From: arkpar Date: Tue, 31 Mar 2015 18:46:01 +0200 Subject: [PATCH 210/228] debug highlighting filter --- mix/CodeModel.cpp | 22 +++++++++++++++++++--- mix/qml/CodeEditorView.qml | 9 --------- mix/qml/ProjectModel.qml | 1 + mix/qml/WebPreview.qml | 2 +- mix/qml/html/codeeditor.js | 2 -- mix/qml/js/ProjectModel.js | 10 +++++++++- 6 files changed, 30 insertions(+), 16 deletions(-) diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index a96ab8791..3d0f0c749 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -93,8 +93,24 @@ private: bool m_functionScope; uint m_storageSlot; }; + +dev::eth::AssemblyItems filterLocations(dev::eth::AssemblyItems const& _locations, dev::solidity::ContractDefinition const& _contract, QHash _functions) +{ + dev::eth::AssemblyItems result; + result.reserve(_locations.size()); + std::string sourceName = *_contract.getLocation().sourceName; + for (dev::eth::AssemblyItem item : _locations) + { + dev::SourceLocation const& l = item.getLocation(); + if (sourceName != *l.sourceName || _contract.getLocation() == l || _functions.contains(LocationPair(l.start, l.end))) + item.setLocation(dev::SourceLocation(-1, -1, l.sourceName)); + result.push_back(item); + } + return result; } +} //namespace + void BackgroundWorker::queueCodeChange(int _jobId) { m_model->runCompilationJob(_jobId); @@ -105,13 +121,11 @@ CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler m_sourceHash(qHash(_source)) { std::string name = _contractName.toStdString(); - auto const& contractDefinition = _compiler.getContractDefinition(name); + ContractDefinition const& contractDefinition = _compiler.getContractDefinition(name); m_contract.reset(new QContractDefinition(nullptr, &contractDefinition)); QQmlEngine::setObjectOwnership(m_contract.get(), QQmlEngine::CppOwnership); m_contract->moveToThread(QApplication::instance()->thread()); m_bytes = _compiler.getBytecode(_contractName.toStdString()); - m_assemblyItems = _compiler.getRuntimeAssemblyItems(name); - m_constructorAssemblyItems = _compiler.getAssemblyItems(name); dev::solidity::InterfaceHandler interfaceHandler; m_contractInterface = QString::fromStdString(*interfaceHandler.getABIInterface(contractDefinition)); @@ -122,6 +136,8 @@ CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler CollectDeclarationsVisitor visitor(&m_functions, &m_locals, &m_storage); contractDefinition.accept(visitor); + m_assemblyItems = filterLocations(_compiler.getRuntimeAssemblyItems(name), contractDefinition, m_functions); + m_constructorAssemblyItems = filterLocations(_compiler.getAssemblyItems(name), contractDefinition, m_functions); } QString CompiledContract::codeHex() const diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml index 7ea1e30d9..25ecbc98c 100644 --- a/mix/qml/CodeEditorView.qml +++ b/mix/qml/CodeEditorView.qml @@ -11,15 +11,6 @@ Item { signal breakpointsChanged(string documentId) signal isCleanChanged(var isClean, string documentId) - function getDocumentIdByName(fileName) - { - for (var i = 0; i < editorListModel.count; i++) { - if (editorListModel.get(i).fileName === fileName) { - return editorListModel.get(i).documentId; - } - } - return null; - } function getDocumentText(documentId) { for (var i = 0; i < editorListModel.count; i++) { diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml index f4b73b601..ec7681f16 100644 --- a/mix/qml/ProjectModel.qml +++ b/mix/qml/ProjectModel.qml @@ -64,6 +64,7 @@ Item { function renameDocument(documentId, newName) { ProjectModelCode.renameDocument(documentId, newName); } function removeDocument(documentId) { ProjectModelCode.removeDocument(documentId); } function getDocument(documentId) { return ProjectModelCode.getDocument(documentId); } + function getDocumentIdByName(documentName) { return ProjectModelCode.getDocumentIdByName(documentName); } function getDocumentIndex(documentId) { return ProjectModelCode.getDocumentIndex(documentId); } function addExistingFiles(paths) { ProjectModelCode.doAddExistingFiles(paths); } function deployProject() { ProjectModelCode.deployProject(false); } diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml index 0bf31b326..768d188cb 100644 --- a/mix/qml/WebPreview.qml +++ b/mix/qml/WebPreview.qml @@ -156,7 +156,7 @@ Item { if (urlPath === "/") urlPath = "/index.html"; var documentName = urlPath.substr(urlPath.lastIndexOf("/") + 1); - var documentId = projectModel.codeEditor.getDocumentIdByName(documentName); + var documentId = projectModel.getDocumentIdByName(documentName); var content = ""; if (projectModel.codeEditor.isDocumentOpen(documentId)) content = projectModel.codeEditor.getDocumentText(documentId); diff --git a/mix/qml/html/codeeditor.js b/mix/qml/html/codeeditor.js index 5d63a7a83..d61d6e51f 100644 --- a/mix/qml/html/codeeditor.js +++ b/mix/qml/html/codeeditor.js @@ -124,8 +124,6 @@ var executionMark; highlightExecution = function(start, end) { if (executionMark) executionMark.clear(); - if (start === 0 && end + 1 === editor.getValue().length) - return; // Do not hightlight the whole document. if (debugWarning) debugWarning.clear(); executionMark = editor.markText(editor.posFromIndex(start), editor.posFromIndex(end), { className: "CodeMirror-exechighlight" }); diff --git a/mix/qml/js/ProjectModel.js b/mix/qml/js/ProjectModel.js index 177115f83..25e8dcb77 100644 --- a/mix/qml/js/ProjectModel.js +++ b/mix/qml/js/ProjectModel.js @@ -172,7 +172,7 @@ function getDocumentIndex(documentId) for (var i = 0; i < projectListModel.count; i++) if (projectListModel.get(i).documentId === documentId) return i; - console.error("Cant find document " + documentId); + console.error("Can't find document " + documentId); return -1; } @@ -291,6 +291,14 @@ function getDocument(documentId) { return projectListModel.get(i); } +function getDocumentIdByName(fileName) +{ + for (var i = 0; i < projectListModel.count; i++) + if (projectListModel.get(i).fileName === fileName) + return projectListModel.get(i).documentId; + return null; +} + function removeDocument(documentId) { var i = getDocumentIndex(documentId); var document = projectListModel.get(i); From db30ab24a045735f9b05a40bccdbb159891ec038 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 31 Mar 2015 18:54:13 +0200 Subject: [PATCH 211/228] Make paranoia safe again after a break caused by BlockChain cache flushing. Fixes #1334 --- libdevcrypto/MemoryDB.h | 2 ++ libethereum/BlockChain.cpp | 21 +++++++++++++++------ libethereum/State.cpp | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/libdevcrypto/MemoryDB.h b/libdevcrypto/MemoryDB.h index 7d39ba73b..7858126f8 100644 --- a/libdevcrypto/MemoryDB.h +++ b/libdevcrypto/MemoryDB.h @@ -32,8 +32,10 @@ namespace dev { struct DBChannel: public LogChannel { static const char* name() { return "TDB"; } static const int verbosity = 18; }; +struct DBWarn: public LogChannel { static const char* name() { return "TDB"; } static const int verbosity = 1; }; #define dbdebug clog(DBChannel) +#define dbwarn clog(DBWarn) class MemoryDB { diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 508531f9d..8c0bd2b8b 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -339,6 +339,13 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) #endif // All ok - insert into DB { + // ensure parent is cached for later addition. + // TODO: this is a bit horrible would be better refactored into an enveloping UpgradableGuard + // together with an "ensureCachedWithUpdatableLock(l)" method. + // This is safe in practice since the caches don't get flushed nearly often enough to be + // done here. + details(bi.parentHash); + WriteGuard l(x_details); m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {}); m_details[bi.parentHash].children.push_back(newHash); @@ -455,14 +462,14 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common, bool _pre, bool _post) const { - // cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged(); + cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged(); if (!_from || !_to) return h256s(); h256s ret; h256s back; unsigned fn = details(_from).number; unsigned tn = details(_to).number; - // cdebug << "treeRoute" << fn << "..." << tn; + cdebug << "treeRoute" << fn << "..." << tn; h256 from = _from; while (fn > tn) { @@ -470,7 +477,7 @@ h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common, ret.push_back(from); from = details(from).parent; fn--; - // cdebug << "from:" << fn << _from.abridged(); + cdebug << "from:" << fn << _from.abridged(); } h256 to = _to; while (fn < tn) @@ -479,12 +486,14 @@ h256s BlockChain::treeRoute(h256 const& _from, h256 const& _to, h256* o_common, back.push_back(to); to = details(to).parent; tn--; - // cdebug << "to:" << tn << _to.abridged(); + cdebug << "to:" << tn << _to.abridged(); } while (from != to) { - assert(from); - assert(to); + if (!from) + assert(from); + if (!to) + assert(to); from = details(from).parent; to = details(to).parent; if (_pre) diff --git a/libethereum/State.cpp b/libethereum/State.cpp index c52c90921..fa457dc41 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -725,7 +725,7 @@ void State::commitToMine(BlockChain const& _bc) uncommitToMine(); // cnote << "Committing to mine on block" << m_previousBlock.hash.abridged(); -#ifdef ETH_PARANOIA +#if ETH_PARANOIA && 0 commit(); cnote << "Pre-reward stateRoot:" << m_state.root(); #endif From 3efcc49bdfdc751dc17a1fc63f6337d1e7b00bad Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 31 Mar 2015 18:54:50 +0200 Subject: [PATCH 212/228] Upgrade to latest ethhash API. --- libethash/ethash.h | 78 +++++++++++++++++++++++++---------------- libethash/internal.c | 30 ++++++++-------- libethash/internal.h | 6 ++-- libethash/io.c | 2 +- libethash/io_posix.c | 2 +- libethash/io_win32.c | 2 +- libethcore/Ethasher.cpp | 25 ++++++++++--- libethcore/Ethasher.h | 27 ++++++-------- test/dagger.cpp | 4 +-- 9 files changed, 100 insertions(+), 76 deletions(-) diff --git a/libethash/ethash.h b/libethash/ethash.h index eb3097307..7594fc835 100644 --- a/libethash/ethash.h +++ b/libethash/ethash.h @@ -24,19 +24,20 @@ #include #include #include +#include #include "compiler.h" -#define REVISION 23 -#define DATASET_BYTES_INIT 1073741824U // 2**30 -#define DATASET_BYTES_GROWTH 8388608U // 2**23 -#define CACHE_BYTES_INIT 1073741824U // 2**24 -#define CACHE_BYTES_GROWTH 131072U // 2**17 -#define EPOCH_LENGTH 30000U -#define MIX_BYTES 128 -#define HASH_BYTES 64 -#define DATASET_PARENTS 256 -#define CACHE_ROUNDS 3 -#define ACCESSES 64 +#define ETHASH_REVISION 23 +#define ETHASH_DATASET_BYTES_INIT 1073741824U // 2**30 +#define ETHASH_DATASET_BYTES_GROWTH 8388608U // 2**23 +#define ETHASH_CACHE_BYTES_INIT 1073741824U // 2**24 +#define ETHASH_CACHE_BYTES_GROWTH 131072U // 2**17 +#define ETHASH_EPOCH_LENGTH 30000U +#define ETHASH_MIX_BYTES 128 +#define ETHASH_HASH_BYTES 64 +#define ETHASH_DATASET_PARENTS 256 +#define ETHASH_CACHE_ROUNDS 3 +#define ETHASH_ACCESSES 64 #ifdef __cplusplus extern "C" { @@ -61,34 +62,49 @@ static inline void ethash_params_init(ethash_params *params, const uint32_t bloc params->cache_size = ethash_get_cachesize(block_number); } -typedef struct ethash_cache { - void *mem; -} ethash_cache; +/*********************************** + * OLD API ************************* + *********************************** + ******************** (deprecated) * + ***********************************/ -void ethash_mkcache(ethash_cache *cache, ethash_params const *params, const uint8_t seed[32]); -void ethash_compute_full_data(void *mem, ethash_params const *params, ethash_cache const *cache); -void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce); -void ethash_light(ethash_return_value *ret, ethash_cache const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce); void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number); +void ethash_mkcache(void *cache, ethash_params const *params, const uint8_t seed[32]); +void ethash_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce); +void ethash_compute_full_data(void *mem, ethash_params const *params, void const *cache); +void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce); -static inline void ethash_prep_light(void *cache, ethash_params const *params, const uint8_t seed[32]) { - ethash_cache c; - c.mem = cache; - ethash_mkcache(&c, params, seed); -} +/*********************************** + * NEW API ************************* + ***********************************/ + +// TODO: compute params and seed in ethash_new_light; it should take only block_number +// TODO: store params in ethash_light_t/ethash_full_t to avoid having to repass into compute/new_full + +typedef uint8_t const ethash_seedhash_t[32]; -static inline void ethash_compute_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) { - ethash_cache c; - c.mem = (void *) cache; - ethash_light(ret, &c, params, header_hash, nonce); +typedef void const* ethash_light_t; +static inline ethash_light_t ethash_new_light(ethash_params const* params, ethash_seedhash_t seed) { + void* ret = malloc(params->cache_size); + ethash_mkcache(ret, params, seed); + return ret; +} +static inline void ethash_compute_light(ethash_return_value *ret, ethash_light_t light, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) { + ethash_light(ret, light, params, header_hash, nonce); +} +static inline void ethash_delete_light(ethash_light_t light) { + free((void*)light); } +typedef void const* ethash_full_t; +static inline ethash_full_t ethash_new_full(ethash_params const* params, ethash_light_t light) { + void* ret = malloc(params->full_size); + ethash_compute_full_data(ret, params, light); + return ret; +} static inline void ethash_prep_full(void *full, ethash_params const *params, void const *cache) { - ethash_cache c; - c.mem = (void *) cache; - ethash_compute_full_data(full, params, &c); + ethash_compute_full_data(full, params, cache); } - static inline void ethash_compute_full(ethash_return_value *ret, void const *full, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) { ethash_full(ret, full, params, header_hash, nonce); } diff --git a/libethash/internal.c b/libethash/internal.c index ae9b95065..130ca13c3 100644 --- a/libethash/internal.c +++ b/libethash/internal.c @@ -38,13 +38,13 @@ #endif // WITH_CRYPTOPP uint64_t ethash_get_datasize(const uint32_t block_number) { - assert(block_number / EPOCH_LENGTH < 2048); - return dag_sizes[block_number / EPOCH_LENGTH]; + assert(block_number / ETHASH_EPOCH_LENGTH < 2048); + return dag_sizes[block_number / ETHASH_EPOCH_LENGTH]; } uint64_t ethash_get_cachesize(const uint32_t block_number) { - assert(block_number / EPOCH_LENGTH < 2048); - return cache_sizes[block_number / EPOCH_LENGTH]; + assert(block_number / ETHASH_EPOCH_LENGTH < 2048); + return cache_sizes[block_number / ETHASH_EPOCH_LENGTH]; } // Follows Sergio's "STRICT MEMORY HARD HASHING FUNCTIONS" (2014) @@ -63,7 +63,7 @@ void static ethash_compute_cache_nodes( SHA3_512(nodes[i].bytes, nodes[i - 1].bytes, 64); } - for (unsigned j = 0; j != CACHE_ROUNDS; j++) { + for (unsigned j = 0; j != ETHASH_CACHE_ROUNDS; j++) { for (unsigned i = 0; i != num_nodes; i++) { uint32_t const idx = nodes[i].words[0] % num_nodes; node data; @@ -85,10 +85,10 @@ void static ethash_compute_cache_nodes( } void ethash_mkcache( - ethash_cache *cache, + void *cache, ethash_params const *params, const uint8_t seed[32]) { - node *nodes = (node *) cache->mem; + node *nodes = (node *) cache; ethash_compute_cache_nodes(nodes, params, seed); } @@ -96,10 +96,10 @@ void ethash_calculate_dag_item( node *const ret, const unsigned node_index, const struct ethash_params *params, - const struct ethash_cache *cache) { + const void *cache) { uint32_t num_parent_nodes = (uint32_t) (params->cache_size / sizeof(node)); - node const *cache_nodes = (node const *) cache->mem; + node const *cache_nodes = (node const *) cache; node const *init = &cache_nodes[node_index % num_parent_nodes]; memcpy(ret, init, sizeof(node)); @@ -114,7 +114,7 @@ void ethash_calculate_dag_item( __m128i xmm3 = ret->xmm[3]; #endif - for (unsigned i = 0; i != DATASET_PARENTS; ++i) { + for (unsigned i = 0; i != ETHASH_DATASET_PARENTS; ++i) { uint32_t parent_index = ((node_index ^ i) * FNV_PRIME ^ ret->words[i % NODE_WORDS]) % num_parent_nodes; node const *parent = &cache_nodes[parent_index]; @@ -150,7 +150,7 @@ void ethash_calculate_dag_item( void ethash_compute_full_data( void *mem, ethash_params const *params, - ethash_cache const *cache) { + void const *cache) { assert((params->full_size % (sizeof(uint32_t) * MIX_WORDS)) == 0); assert((params->full_size % sizeof(node)) == 0); node *full_nodes = mem; @@ -164,7 +164,7 @@ void ethash_compute_full_data( static void ethash_hash( ethash_return_value *ret, node const *full_nodes, - ethash_cache const *cache, + void const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) { @@ -201,7 +201,7 @@ static void ethash_hash( num_full_pages = (unsigned) (params->full_size / page_size); - for (unsigned i = 0; i != ACCESSES; ++i) { + for (unsigned i = 0; i != ETHASH_ACCESSES; ++i) { uint32_t const index = ((s_mix->words[0] ^ i) * FNV_PRIME ^ mix->words[i % MIX_WORDS]) % num_full_pages; for (unsigned n = 0; n != MIX_NODES; ++n) { @@ -275,7 +275,7 @@ void ethash_quick_hash( void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number) { memset(seedhash, 0, 32); - const uint32_t epochs = block_number / EPOCH_LENGTH; + const uint32_t epochs = block_number / ETHASH_EPOCH_LENGTH; for (uint32_t i = 0; i < epochs; ++i) SHA3_256(seedhash, seedhash, 32); } @@ -295,6 +295,6 @@ void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params c ethash_hash(ret, (node const *) full_mem, NULL, params, previous_hash, nonce); } -void ethash_light(ethash_return_value *ret, ethash_cache const *cache, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) { +void ethash_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) { ethash_hash(ret, NULL, cache, params, previous_hash, nonce); } diff --git a/libethash/internal.h b/libethash/internal.h index ddd06e8f4..dec7e6b13 100644 --- a/libethash/internal.h +++ b/libethash/internal.h @@ -15,7 +15,7 @@ extern "C" { // compile time settings #define NODE_WORDS (64/4) -#define MIX_WORDS (MIX_BYTES/4) +#define MIX_WORDS (ETHASH_MIX_BYTES/4) #define MIX_NODES (MIX_WORDS / NODE_WORDS) #include @@ -34,7 +34,7 @@ void ethash_calculate_dag_item( node *const ret, const unsigned node_index, ethash_params const *params, - ethash_cache const *cache + void const *cache ); void ethash_quick_hash( @@ -45,4 +45,4 @@ void ethash_quick_hash( #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/libethash/io.c b/libethash/io.c index dd4f1f9e8..0e935fa59 100644 --- a/libethash/io.c +++ b/libethash/io.c @@ -71,7 +71,7 @@ bool ethash_io_write(char const *dirname, goto fail_free; } - ethash_io_serialize_info(REVISION, seedhash, info_buffer); + ethash_io_serialize_info(ETHASH_REVISION, seedhash, info_buffer); if (!ethash_io_write_file(dirname, PASS_ARR(DAG_MEMO_NAME), info_buffer, DAG_MEMO_BYTESIZE)) { goto fail_free; } diff --git a/libethash/io_posix.c b/libethash/io_posix.c index 693bdf750..9d9ccac69 100644 --- a/libethash/io_posix.c +++ b/libethash/io_posix.c @@ -56,7 +56,7 @@ enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seed goto close; } - ethash_io_serialize_info(REVISION, seedhash, expect_buffer); + ethash_io_serialize_info(ETHASH_REVISION, seedhash, expect_buffer); if (memcmp(read_buffer, expect_buffer, DAG_MEMO_BYTESIZE) != 0) { // we have different memo contents so delete the memo file if (unlink(memofile) != 0) { diff --git a/libethash/io_win32.c b/libethash/io_win32.c index 2cabc939a..77367fdd9 100644 --- a/libethash/io_win32.c +++ b/libethash/io_win32.c @@ -53,7 +53,7 @@ enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seed goto close; } - ethash_io_serialize_info(REVISION, seedhash, expect_buffer); + ethash_io_serialize_info(ETHASH_REVISION, seedhash, expect_buffer); if (memcmp(read_buffer, expect_buffer, DAG_MEMO_BYTESIZE) != 0) { // we have different memo contents so delete the memo file if (_unlink(memofile) != 0) { diff --git a/libethcore/Ethasher.cpp b/libethcore/Ethasher.cpp index 880bc139a..64db69187 100644 --- a/libethcore/Ethasher.cpp +++ b/libethcore/Ethasher.cpp @@ -41,7 +41,23 @@ using namespace eth; Ethasher* dev::eth::Ethasher::s_this = nullptr; -bytes const& Ethasher::cache(BlockInfo const& _header) +Ethasher::~Ethasher() +{ + while (!m_caches.empty()) + killCache(m_caches.begin()->first); +} + +void Ethasher::killCache(h256 const& _s) +{ + RecursiveGuard l(x_this); + if (m_caches.count(_s)) + { + ethash_delete_light(m_caches.at(_s)); + m_caches.erase(_s); + } +} + +void const* Ethasher::cache(BlockInfo const& _header) { RecursiveGuard l(x_this); if (_header.number > c_ethashEpochLength * 2048) @@ -54,8 +70,7 @@ bytes const& Ethasher::cache(BlockInfo const& _header) if (!m_caches.count(_header.seedHash())) { ethash_params p = params((unsigned)_header.number); - m_caches[_header.seedHash()].resize(p.cache_size); - ethash_prep_light(m_caches[_header.seedHash()].data(), &p, _header.seedHash().data()); + m_caches[_header.seedHash()] = ethash_new_light(&p, _header.seedHash().data()); } return m_caches[_header.seedHash()]; } @@ -84,7 +99,7 @@ bytesConstRef Ethasher::full(BlockInfo const& _header) ethash_params p = params((unsigned)_header.number); m_fulls[_header.seedHash()] = bytesRef(new byte[p.full_size], p.full_size); auto c = cache(_header); - ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c.data()); + ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c); writeFile(memoFile, m_fulls[_header.seedHash()]); writeFile(memoFile + ".info", info); } @@ -147,7 +162,7 @@ Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce) { auto p = Ethasher::params(_header); ethash_return_value r; - ethash_compute_light(&r, Ethasher::get()->cache(_header).data(), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); + ethash_compute_light(&r, Ethasher::get()->cache(_header), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); // cdebug << "Ethasher::eval sha3(cache):" << sha3(Ethasher::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer); return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; } diff --git a/libethcore/Ethasher.h b/libethcore/Ethasher.h index c160d38da..a10c206d1 100644 --- a/libethcore/Ethasher.h +++ b/libethcore/Ethasher.h @@ -30,21 +30,8 @@ #include #include #include // TODO: REMOVE once everything merged into this class and an opaque API can be provided. -static const unsigned c_ethashRevision = REVISION; -static const unsigned c_ethashEpochLength = EPOCH_LENGTH; -#undef REVISION -#undef DATASET_BYTES_INIT -#undef DATASET_BYTES_GROWTH -#undef CACHE_BYTES_INIT -#undef CACHE_BYTES_GROWTH -#undef DAGSIZE_BYTES_INIT -#undef DAG_GROWTH -#undef EPOCH_LENGTH -#undef MIX_BYTES -#undef HASH_BYTES -#undef DATASET_PARENTS -#undef CACHE_ROUNDS -#undef ACCESSES +static const unsigned c_ethashRevision = ETHASH_REVISION; +static const unsigned c_ethashEpochLength = ETHASH_EPOCH_LENGTH; #include "Common.h" #include "BlockInfo.h" @@ -57,10 +44,14 @@ class Ethasher { public: Ethasher() {} + ~Ethasher(); static Ethasher* get() { if (!s_this) s_this = new Ethasher(); return s_this; } - bytes const& cache(BlockInfo const& _header); + using LightType = void const*; + using FullType = void const*; + + LightType cache(BlockInfo const& _header); bytesConstRef full(BlockInfo const& _header); static ethash_params params(BlockInfo const& _header); static ethash_params params(unsigned _n); @@ -104,9 +95,11 @@ public: }; private: + void killCache(h256 const& _s); + static Ethasher* s_this; RecursiveMutex x_this; - std::map m_caches; + std::map m_caches; std::map m_fulls; }; diff --git a/test/dagger.cpp b/test/dagger.cpp index f7230f705..4dda9c4fc 100644 --- a/test/dagger.cpp +++ b/test/dagger.cpp @@ -63,8 +63,8 @@ BOOST_AUTO_TEST_CASE(basic_test) unsigned cacheSize(o["cache_size"].get_int()); h256 cacheHash(o["cache_hash"].get_str()); - BOOST_REQUIRE_EQUAL(Ethasher::get()->cache(header).size(), cacheSize); - BOOST_REQUIRE_EQUAL(sha3(Ethasher::get()->cache(header)), cacheHash); + BOOST_REQUIRE_EQUAL(Ethasher::get()->params(header).cache_size, cacheSize); + BOOST_REQUIRE_EQUAL(sha3(bytesConstRef((byte const*)Ethasher::get()->cache(header), cacheSize)), cacheHash); #if TEST_FULL unsigned fullSize(o["full_size"].get_int()); From ed62ee21f3592464fe5bab06425945bf7357309e Mon Sep 17 00:00:00 2001 From: Ali Mashatan Date: Wed, 1 Apr 2015 04:20:17 +0430 Subject: [PATCH 213/228] -Bug fix: get first item of list in setSelectedIndex function. -Putting qsTr for all user-visible strings. --- mix/qml/StatesComboBox.qml | 201 +++++++++++++++++++------------------ 1 file changed, 104 insertions(+), 97 deletions(-) diff --git a/mix/qml/StatesComboBox.qml b/mix/qml/StatesComboBox.qml index 26f41aa8d..246b3e93b 100644 --- a/mix/qml/StatesComboBox.qml +++ b/mix/qml/StatesComboBox.qml @@ -28,22 +28,20 @@ import QtGraphicalEffects 1.0 Rectangle { id: statesComboBox - width: 200; - height: 20; - - Component.onCompleted: - { - var top = dropDownList; - while (top.parent) - { + width: 200 + height: 20 + + Component.onCompleted: { + var top = dropDownList + while (top.parent) { top = top.parent if (top.objectName == "debugPanel") - break; + break } var coordinates = dropDownList.mapToItem(top, 0, 0) //the order is important - dropDownShowdowList.parent = top; - dropDownList.parent = top; + dropDownShowdowList.parent = top + dropDownList.parent = top dropDownShowdowList.x = coordinates.x dropDownShowdowList.y = coordinates.y @@ -52,187 +50,196 @@ Rectangle { dropDownList.y = coordinates.y } - signal selectItem(real item); - signal editItem(real item); - signal selectCreate(); - property variant rowHeight:25; - property variant items; - readonly property alias selectedItem: chosenItemText.text; - readonly property alias selectedIndex: listView.currentRow; - function setSelectedIndex(index) - { - listView.currentRow = index; - chosenItemText.text = statesComboBox.items.get(0).title; + signal selectItem(real item) + signal editItem(real item) + signal selectCreate + property variant rowHeight: 25 + property variant items + property alias selectedItem: chosenItemText.text + property alias selectedIndex: listView.currentRow + function setSelectedIndex(index) { + listView.currentRow = index + chosenItemText.text = statesComboBox.items.get(index).title } - signal comboClicked; + signal comboClicked - property variant colorItem; - property variant colorSelect; + property variant colorItem + property variant colorSelect - smooth:true; + smooth: true Rectangle { id: chosenItem - width: parent.width; - height: statesComboBox.height; - color: statesComboBox.color; - smooth: true; + width: parent.width + height: statesComboBox.height + color: statesComboBox.color + smooth: true Text { id: chosenItemText - anchors.top: parent.top; - anchors.left: parent.left; - anchors.margins: 2; - color: statesComboBox.colorItem; + anchors.top: parent.top + anchors.left: parent.left + anchors.margins: 2 + color: statesComboBox.colorItem text: "" smooth: true } MouseArea { - anchors.fill: parent; + anchors.fill: parent onClicked: { - statesComboBox.state = statesComboBox.state==="dropDown"?"":"dropDown" + statesComboBox.state = statesComboBox.state === "dropDown" ? "" : "dropDown" } } } Rectangle { id: dropDownShowdowList - width: statesComboBox.width; + width: statesComboBox.width opacity: 0.3 - height: 0; - clip: true; - radius: 4; - anchors.top: chosenItem.top; - anchors.margins: 2; + height: 0 + clip: true + radius: 4 + anchors.top: chosenItem.top + anchors.margins: 2 color: "gray" } //ToDo: We need scrollbar for items Rectangle { id: dropDownList - width: statesComboBox.width; - height: 0; - clip: true; - radius: 4; - anchors.top: chosenItem.top; - anchors.margins: 2; + width: statesComboBox.width + height: 0 + clip: true + radius: 4 + anchors.top: chosenItem.top + anchors.margins: 2 color: statesComboBox.color ColumnLayout { spacing: 2 TableView { id: listView - height: 20; + height: 20 implicitHeight: 0 - width: statesComboBox.width; + width: statesComboBox.width model: statesComboBox.items - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff; + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff currentRow: -1 - headerVisible: false; + headerVisible: false backgroundVisible: false - alternatingRowColors : false; + alternatingRowColors: false frameVisible: false TableViewColumn { role: "title" title: "" - width: statesComboBox.width; + width: statesComboBox.width delegate: mainItemDelegate } - rowDelegate: Rectangle { - width: statesComboBox.width; - height: statesComboBox.rowHeight; + rowDelegate: Rectangle { + width: statesComboBox.width + height: statesComboBox.rowHeight } Component { id: mainItemDelegate Rectangle { id: itemDelegate - width: statesComboBox.width; - height: statesComboBox.height; + width: statesComboBox.width + height: statesComboBox.height Text { id: textItemid text: styleData.value - color: statesComboBox.colorItem; - anchors.top: parent.top; - anchors.left: parent.left; - anchors.margins: 5; + color: statesComboBox.colorItem + anchors.top: parent.top + anchors.left: parent.left + anchors.margins: 5 } Image { id: imageItemid height: 20 - width: 20; + width: 20 anchors.right: parent.right - anchors.top: parent.top; - anchors.margins: 5; - visible: false; + anchors.top: parent.top + anchors.margins: 5 + visible: false fillMode: Image.PreserveAspectFit source: "img/edit_combox.png" } MouseArea { - anchors.fill: parent; - hoverEnabled : true + anchors.fill: parent + hoverEnabled: true onEntered: { - imageItemid.visible = true; - textItemid.color = statesComboBox.colorSelect; + imageItemid.visible = true + textItemid.color = statesComboBox.colorSelect } onExited: { - imageItemid.visible = false; - textItemid.color = statesComboBox.colorItem; + imageItemid.visible = false + textItemid.color = statesComboBox.colorItem } onClicked: { - if (mouseX > imageItemid.x && mouseX < imageItemid.x+ imageItemid.width - && mouseY > imageItemid.y && mouseY < imageItemid.y+ imageItemid.height) - statesComboBox.editItem(styleData.row); + if (mouseX > imageItemid.x + && mouseX < imageItemid.x + imageItemid.width + && mouseY > imageItemid.y + && mouseY < imageItemid.y + imageItemid.height) + statesComboBox.editItem(styleData.row) else { statesComboBox.state = "" var prevSelection = chosenItemText.text chosenItemText.text = styleData.value - listView.currentRow = styleData.row; - statesComboBox.selectItem(styleData.row); + listView.currentRow = styleData.row + statesComboBox.selectItem(styleData.row) } } } - }//Item - }//Component - }//Table View + } //Item + } //Component + } //Table View RowLayout { Rectangle { width: 1 } - Text{ + Text { id: createStateText - width: statesComboBox.width; - height: statesComboBox.height; + width: statesComboBox.width + height: statesComboBox.height font.bold: true - text: "Create State ..." - MouseArea - { - anchors.fill: parent; + text: qsTr("Create State ...") + MouseArea { + anchors.fill: parent hoverEnabled: true onEntered: { - createStateText.color = statesComboBox.colorSelect; + createStateText.color = statesComboBox.colorSelect } onExited: { - createStateText.color = statesComboBox.colorItem; + createStateText.color = statesComboBox.colorItem } onClicked: { statesComboBox.state = "" - statesComboBox.selectCreate(); + statesComboBox.selectCreate() } } } } - } } states: State { - name: "dropDown"; - PropertyChanges { target: dropDownList; height: (statesComboBox.rowHeight*(statesComboBox.items.count+1)) } - PropertyChanges { target: dropDownShowdowList; width: statesComboBox.width+3; height: (statesComboBox.rowHeight*(statesComboBox.items.count+1))+3 } - PropertyChanges { target: listView; height: 20; implicitHeight: (statesComboBox.rowHeight*(statesComboBox.items.count)) } + name: "dropDown" + PropertyChanges { + target: dropDownList + height: (statesComboBox.rowHeight * (statesComboBox.items.count + 1)) + } + PropertyChanges { + target: dropDownShowdowList + width: statesComboBox.width + 3 + height: (statesComboBox.rowHeight * (statesComboBox.items.count + 1)) + 3 + } + PropertyChanges { + target: listView + height: 20 + implicitHeight: (statesComboBox.rowHeight * (statesComboBox.items.count)) + } } - } From d0825c2cd8b97397dfb293b97b61948ec2b2e887 Mon Sep 17 00:00:00 2001 From: arkpar Date: Wed, 1 Apr 2015 11:03:20 +0200 Subject: [PATCH 214/228] re-enabled debugging into base contracts --- mix/CodeModel.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index 3d0f0c749..53ec34e2b 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -98,11 +98,10 @@ dev::eth::AssemblyItems filterLocations(dev::eth::AssemblyItems const& _location { dev::eth::AssemblyItems result; result.reserve(_locations.size()); - std::string sourceName = *_contract.getLocation().sourceName; for (dev::eth::AssemblyItem item : _locations) { dev::SourceLocation const& l = item.getLocation(); - if (sourceName != *l.sourceName || _contract.getLocation() == l || _functions.contains(LocationPair(l.start, l.end))) + if (_contract.getLocation() == l || _functions.contains(LocationPair(l.start, l.end))) item.setLocation(dev::SourceLocation(-1, -1, l.sourceName)); result.push_back(item); } From d6cf8492312341bbbd8df0cc80cb1aa74e26b5f5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 1 Apr 2015 11:48:34 +0200 Subject: [PATCH 215/228] Moved semantic information to its own file. --- libevmcore/CommonSubexpressionEliminator.cpp | 75 +------------ libevmcore/CommonSubexpressionEliminator.h | 17 +-- libevmcore/SemanticInformation.cpp | 107 +++++++++++++++++++ libevmcore/SemanticInformation.h | 50 +++++++++ 4 files changed, 160 insertions(+), 89 deletions(-) create mode 100644 libevmcore/SemanticInformation.cpp create mode 100644 libevmcore/SemanticInformation.h diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index 47bb5b512..5c6ba95af 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include using namespace std; using namespace dev; @@ -248,79 +248,6 @@ ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(ExpressionCl return m_memoryContent[_slot] = m_expressionClasses.find(Instruction::MLOAD, {_slot}, true, m_sequenceNumber); } -bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item) -{ - switch (_item.type()) - { - default: - case UndefinedItem: - case Tag: - return true; - case Push: - case PushString: - case PushTag: - case PushSub: - case PushSubSize: - case PushProgramSize: - case PushData: - return false; - case Operation: - { - if (isSwapInstruction(_item) || isDupInstruction(_item)) - return false; - if (_item.instruction() == Instruction::GAS || _item.instruction() == Instruction::PC) - return true; // GAS and PC assume a specific order of opcodes - if (_item.instruction() == Instruction::MSIZE) - return true; // msize is modified already by memory access, avoid that for now - if (_item.instruction() == Instruction::SHA3) - return true; //@todo: we have to compare sha3's not based on their memory addresses but on the memory content. - InstructionInfo info = instructionInfo(_item.instruction()); - if (_item.instruction() == Instruction::SSTORE) - return false; - if (_item.instruction() == Instruction::MSTORE) - return false; - //@todo: We do not handle the following memory instructions for now: - // calldatacopy, codecopy, extcodecopy, mstore8, - // msize (note that msize also depends on memory read access) - - // the second requirement will be lifted once it is implemented - return info.sideEffects || info.args > 2; - } - } -} - -bool SemanticInformation::isCommutativeOperation(AssemblyItem const& _item) -{ - if (_item.type() != Operation) - return false; - switch (_item.instruction()) - { - case Instruction::ADD: - case Instruction::MUL: - case Instruction::EQ: - case Instruction::AND: - case Instruction::OR: - case Instruction::XOR: - return true; - default: - return false; - } -} - -bool SemanticInformation::isDupInstruction(AssemblyItem const& _item) -{ - if (_item.type() != Operation) - return false; - return Instruction::DUP1 <= _item.instruction() && _item.instruction() <= Instruction::DUP16; -} - -bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item) -{ - if (_item.type() != Operation) - return false; - return Instruction::SWAP1 <= _item.instruction() && _item.instruction() <= Instruction::SWAP16; -} - CSECodeGenerator::CSECodeGenerator( ExpressionClasses& _expressionClasses, vector const& _storeOperations diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h index a9a0c60a4..0dbb47b29 100644 --- a/libevmcore/CommonSubexpressionEliminator.h +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -31,6 +31,7 @@ #include #include #include +#include namespace dev { @@ -139,20 +140,6 @@ private: AssemblyItem const* m_breakingItem = nullptr; }; -/** - * Helper functions to provide context-independent information about assembly items. - */ -struct SemanticInformation -{ - /// @returns true if the given items starts a new basic block - static bool breaksBasicBlock(AssemblyItem const& _item); - /// @returns true if the item is a two-argument operation whose value does not depend on the - /// order of its arguments. - static bool isCommutativeOperation(AssemblyItem const& _item); - static bool isDupInstruction(AssemblyItem const& _item); - static bool isSwapInstruction(AssemblyItem const& _item); -}; - /** * Unit that generates code from current stack layout, target stack layout and information about * the equivalence classes. @@ -230,7 +217,7 @@ _AssemblyItemIterator CommonSubexpressionEliminator::feedItems( _AssemblyItemIterator _end ) { - for (; _iterator != _end && !SemanticInformation::breaksBasicBlock(*_iterator); ++_iterator) + for (; _iterator != _end && !SemanticInformation::breaksCSEAnalysisBlock(*_iterator); ++_iterator) feedItem(*_iterator); if (_iterator != _end) m_breakingItem = &(*_iterator++); diff --git a/libevmcore/SemanticInformation.cpp b/libevmcore/SemanticInformation.cpp new file mode 100644 index 000000000..e561e7554 --- /dev/null +++ b/libevmcore/SemanticInformation.cpp @@ -0,0 +1,107 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @file SemanticInformation.cpp + * @author Christian + * @date 2015 + * Helper to provide semantic information about assembly items. + */ + +#include +#include + +using namespace std; +using namespace dev; +using namespace dev::eth; + +bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item) +{ + switch (_item.type()) + { + default: + case UndefinedItem: + case Tag: + return true; + case Push: + case PushString: + case PushTag: + case PushSub: + case PushSubSize: + case PushProgramSize: + case PushData: + return false; + case Operation: + { + if (isSwapInstruction(_item) || isDupInstruction(_item)) + return false; + if (_item.instruction() == Instruction::GAS || _item.instruction() == Instruction::PC) + return true; // GAS and PC assume a specific order of opcodes + if (_item.instruction() == Instruction::MSIZE) + return true; // msize is modified already by memory access, avoid that for now + if (_item.instruction() == Instruction::SHA3) + return true; //@todo: we have to compare sha3's not based on their memory addresses but on the memory content. + InstructionInfo info = instructionInfo(_item.instruction()); + if (_item.instruction() == Instruction::SSTORE) + return false; + if (_item.instruction() == Instruction::MSTORE) + return false; + //@todo: We do not handle the following memory instructions for now: + // calldatacopy, codecopy, extcodecopy, mstore8, + // msize (note that msize also depends on memory read access) + + // the second requirement will be lifted once it is implemented + return info.sideEffects || info.args > 2; + } + } +} + +bool SemanticInformation::isCommutativeOperation(AssemblyItem const& _item) +{ + if (_item.type() != Operation) + return false; + switch (_item.instruction()) + { + case Instruction::ADD: + case Instruction::MUL: + case Instruction::EQ: + case Instruction::AND: + case Instruction::OR: + case Instruction::XOR: + return true; + default: + return false; + } +} + +bool SemanticInformation::isDupInstruction(AssemblyItem const& _item) +{ + if (_item.type() != Operation) + return false; + return Instruction::DUP1 <= _item.instruction() && _item.instruction() <= Instruction::DUP16; +} + +bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item) +{ + if (_item.type() != Operation) + return false; + return Instruction::SWAP1 <= _item.instruction() && _item.instruction() <= Instruction::SWAP16; +} + +bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item) +{ + return _item == AssemblyItem(Instruction::JUMP) || _item == AssemblyItem(Instruction::JUMPI); +} diff --git a/libevmcore/SemanticInformation.h b/libevmcore/SemanticInformation.h new file mode 100644 index 000000000..7497dc651 --- /dev/null +++ b/libevmcore/SemanticInformation.h @@ -0,0 +1,50 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @file SemanticInformation.h + * @author Christian + * @date 2015 + * Helper to provide semantic information about assembly items. + */ + +#pragma once + + +namespace dev +{ +namespace eth +{ + +class AssemblyItem; + +/** + * Helper functions to provide context-independent information about assembly items. + */ +struct SemanticInformation +{ + /// @returns true if the given items starts a new block for common subexpression analysis. + static bool breaksCSEAnalysisBlock(AssemblyItem const& _item); + /// @returns true if the item is a two-argument operation whose value does not depend on the + /// order of its arguments. + static bool isCommutativeOperation(AssemblyItem const& _item); + static bool isDupInstruction(AssemblyItem const& _item); + static bool isSwapInstruction(AssemblyItem const& _item); + static bool isJumpInstruction(AssemblyItem const& _item); +}; + +} +} From 600b12ab783cf36ec34e04df9b915088c7b6b4a9 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 1 Apr 2015 11:53:57 +0200 Subject: [PATCH 216/228] LogsPanel --- mix/qml/LogsPane.qml | 43 +++++++++++++++++++++++++++++++-------- mix/qml/LogsPaneStyle.qml | 2 +- mix/qml/StatusPane.qml | 10 +++------ 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml index 21bd9fd96..f600f9f9d 100644 --- a/mix/qml/LogsPane.qml +++ b/mix/qml/LogsPane.qml @@ -13,6 +13,11 @@ Rectangle logsModel.insert(0, { "type": _type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": _content, "level": _level }); } + Keys.onEscapePressed: + { + parent.toggle(); + } + anchors.fill: parent radius: 5 color: "transparent" @@ -33,7 +38,7 @@ Rectangle height: parent.height width: parent.width horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - ColumnLayout + Column { id: logsRect spacing: 0 @@ -93,6 +98,26 @@ Rectangle return index % 2 === 0 ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor; } + MouseArea + { + anchors.fill: parent + onClicked: + { + if (logContent.elide === Text.ElideNone) + { + logContent.elide = Text.ElideRight; + logContent.wrapMode = Text.NoWrap + parent.height = 30; + } + else + { + logContent.elide = Text.ElideNone; + logContent.wrapMode = Text.WordWrap; + parent.height = logContent.lineCount * 30; + } + } + } + DefaultLabel { text: date; @@ -120,12 +145,16 @@ Rectangle } } - DefaultLabel { + Text { + id: logContent text: content; font.family: LogsPaneStyle.generic.layout.logLabelFont width: LogsPaneStyle.generic.layout.contentWidth font.pointSize: Style.absoluteSize(-1) anchors.verticalCenter: parent.verticalCenter + elide: Text.ElideRight + + maximumLineCount: 10 anchors.left: parent.left anchors.leftMargin: 190 color: { @@ -145,11 +174,8 @@ Rectangle } } } - - } - Component { id: itemDelegate DefaultLabel { @@ -166,7 +192,6 @@ Rectangle } } } - } Rectangle @@ -377,7 +402,7 @@ Rectangle anchors.centerIn: parent fillMode: Image.PreserveAspectFit width: 20 - height: 20 + height: 25 } Action { @@ -420,7 +445,7 @@ Rectangle anchors.centerIn: parent fillMode: Image.PreserveAspectFit width: 20 - height: 20 + height: 25 } Action { @@ -503,7 +528,7 @@ Rectangle anchors.centerIn: parent fillMode: Image.PreserveAspectFit width: 20 - height: 20 + height: 25 } Action { diff --git a/mix/qml/LogsPaneStyle.qml b/mix/qml/LogsPaneStyle.qml index 215f17bd7..8e0dfd5f4 100644 --- a/mix/qml/LogsPaneStyle.qml +++ b/mix/qml/LogsPaneStyle.qml @@ -20,7 +20,7 @@ QtObject { property int headerInputWidth: 200 property int dateWidth: 70 property int typeWidth: 90 - property int contentWidth: 250 + property int contentWidth: 560 property string logAlternateColor: "#f6f5f6" } } diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index b42b13fef..3d366021f 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -112,7 +112,7 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter radius: 3 - width: 650 + width: 600 height: 30 color: "#fcfbfc" Text { @@ -201,8 +201,8 @@ Rectangle { { logsContainer.state = "opened"; logsContainer.focus = true; - calCoord(); forceActiveFocus(); + calCoord(); } } @@ -213,10 +213,6 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter visible: false radius: 5 - Component.onCompleted: - { - calCoord(); - } function calCoord() { @@ -225,7 +221,7 @@ Rectangle { top = top.parent var coordinates = logsContainer.mapToItem(top, 0, 0); logsContainer.parent = top; - logsContainer.x = coordinates.x + 150; + logsContainer.x = coordinates.x; logsContainer.y = coordinates.y; } From 0e247e0282edaaf1f2cff9b424407be60c481a67 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Fri, 27 Mar 2015 17:07:32 +0100 Subject: [PATCH 217/228] style fixes --- libsolidity/AST.cpp | 2 +- libsolidity/Types.cpp | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index e3c9bc964..7fe3a9825 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -337,7 +337,7 @@ void FunctionDefinition::checkTypeRequirements() { if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); - if (!(var->getType()->externalType()) && getVisibility() >= Visibility::Public) + if (getVisibility() >= Visibility::Public && !(var->getType()->externalType())) BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for function with external visibility")); } for (ASTPointer const& modifier: m_functionModifiers) diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 5fd7d24a6..e784c6b8c 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -1103,11 +1103,16 @@ TypePointer FunctionType::externalType() const TypePointers paramTypes; TypePointers retParamTypes; - for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it) - paramTypes.push_back((*it)->externalType()); - for (auto it = m_returnParameterTypes.cbegin(); it != m_returnParameterTypes.cend(); ++it) - retParamTypes.push_back((*it)->externalType()); - + for(auto type: m_parameterTypes) + { + solAssert(!!type->externalType(), "To be included in external type of the function, the argument should have external type."); + paramTypes.push_back(type->externalType()); + } + for(auto param: m_returnParameterTypes) + { + solAssert(!!param->externalType(), "To be included in external type of the function, the argument should have external type."); + retParamTypes.push_back(param->externalType()); + } return make_shared(paramTypes, retParamTypes, m_location, m_arbitraryParameters); } From 4173a71846c7e35002c56a7c990aace40e9579cd Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Mon, 30 Mar 2015 14:34:38 +0200 Subject: [PATCH 218/228] changed checking for external type in VariableDeclaration::checkTypeRequirements() changed error msg --- libsolidity/AST.cpp | 17 ++++++++++++++++- test/SolidityNameAndTypeResolution.cpp | 14 ++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 7fe3a9825..a111a6f88 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -338,7 +338,14 @@ void FunctionDefinition::checkTypeRequirements() if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); if (getVisibility() >= Visibility::Public && !(var->getType()->externalType())) - BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for function with external visibility")); + { + // todo delete when arrays as parameter type in internal functions will be implemented + ArrayType const* type = dynamic_cast(var->getType().get()); + if (getVisibility() == Visibility::Public && type) + BOOST_THROW_EXCEPTION(var->createTypeError("Array type is not allowed as parameter for internal functions.")); + else + BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for function with external visibility.")); + } } for (ASTPointer const& modifier: m_functionModifiers) modifier->checkTypeRequirements(isConstructor() ? @@ -379,6 +386,14 @@ void VariableDeclaration::checkTypeRequirements() m_value->expectType(*m_type); if (m_isStateVariable && !m_type->externalType() && getVisibility() >= Visibility::Public) BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for state variables.")); + + auto sharedToExternalTypes = FunctionType(*this).externalType(); // do not distroy the shared pointer. + auto externalFunctionTypes = dynamic_cast(sharedToExternalTypes.get()); + TypePointers retParamTypes = externalFunctionTypes->getReturnParameterTypes(); + TypePointers parameterTypes = externalFunctionTypes->getParameterTypes(); + for (auto parameter: parameterTypes + retParamTypes) + if (!parameter && !(parameter->externalType())) + BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for state variables.")); } else { // no type declared and no previous assignment, infer the type diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index ddcf36140..531f3bc13 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -460,7 +460,7 @@ BOOST_AUTO_TEST_CASE(function_external_types) uint a; } contract Test { - function boo(uint arg2, bool arg3, bytes8 arg4, bool[2] pairs, uint[] dynamic, C carg) external returns (uint ret) { + function boo(uint arg2, bool arg3, bytes8 arg4, bool[2] pairs, uint[] dynamic, C carg, address[] addresses) external returns (uint ret) { ret = 5; } })"; @@ -471,7 +471,7 @@ BOOST_AUTO_TEST_CASE(function_external_types) auto functions = contract->getDefinedFunctions(); if (functions.empty()) continue; - BOOST_CHECK_EQUAL("boo(uint256,bool,bytes8,bool[2],uint256[],address)", functions[0]->externalSignature()); + BOOST_CHECK_EQUAL("boo(uint256,bool,bytes8,bool[2],uint256[],address,address[])", functions[0]->externalSignature()); } } @@ -503,6 +503,16 @@ BOOST_AUTO_TEST_CASE(function_external_call_not_allowed_conversion) BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); } +// todo delete when implemented +BOOST_AUTO_TEST_CASE(arrays_in_internal_functions) +{ + char const* text = R"( + contract Test { + function foo(address[] addresses) {} + })"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + BOOST_AUTO_TEST_CASE(function_internal_allowed_conversion) { char const* text = R"( From 4352423fea12a3896af3580f2b3097674cdd1ae9 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Wed, 1 Apr 2015 15:19:33 +0200 Subject: [PATCH 219/228] miner changes in the implementation of the externalTypes function of FunctionType. better error messages for exeptions style fixes after review --- libsolidity/AST.cpp | 18 ++++++++---------- libsolidity/Types.cpp | 12 +++++++----- libsolidity/Types.h | 2 ++ 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index a111a6f88..d489a4489 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -339,12 +339,11 @@ void FunctionDefinition::checkTypeRequirements() BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); if (getVisibility() >= Visibility::Public && !(var->getType()->externalType())) { - // todo delete when arrays as parameter type in internal functions will be implemented - ArrayType const* type = dynamic_cast(var->getType().get()); - if (getVisibility() == Visibility::Public && type) + // todo delete when will be implemented arrays as parameter type in internal functions + if (getVisibility() == Visibility::Public && var->getType()->getCategory() == Type::Category::Array) BOOST_THROW_EXCEPTION(var->createTypeError("Array type is not allowed as parameter for internal functions.")); else - BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for function with external visibility.")); + BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for public and external functions.")); } } for (ASTPointer const& modifier: m_functionModifiers) @@ -389,12 +388,11 @@ void VariableDeclaration::checkTypeRequirements() auto sharedToExternalTypes = FunctionType(*this).externalType(); // do not distroy the shared pointer. auto externalFunctionTypes = dynamic_cast(sharedToExternalTypes.get()); - TypePointers retParamTypes = externalFunctionTypes->getReturnParameterTypes(); - TypePointers parameterTypes = externalFunctionTypes->getParameterTypes(); - for (auto parameter: parameterTypes + retParamTypes) - if (!parameter && !(parameter->externalType())) - BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for state variables.")); - } else + for (auto parameter: externalFunctionTypes->getParameterTypes() + externalFunctionTypes->getReturnParameterTypes()) + if (!parameter || !(parameter->externalType())) + BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables.")); + } + else { // no type declared and no previous assignment, infer the type m_value->checkTypeRequirements(); diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index e784c6b8c..86b740262 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -1103,15 +1103,17 @@ TypePointer FunctionType::externalType() const TypePointers paramTypes; TypePointers retParamTypes; - for(auto type: m_parameterTypes) + for (auto type: m_parameterTypes) { - solAssert(!!type->externalType(), "To be included in external type of the function, the argument should have external type."); + if(!type->externalType()) + return TypePointer(); paramTypes.push_back(type->externalType()); } - for(auto param: m_returnParameterTypes) + for (auto type: m_returnParameterTypes) { - solAssert(!!param->externalType(), "To be included in external type of the function, the argument should have external type."); - retParamTypes.push_back(param->externalType()); + if(!type->externalType()) + return TypePointer(); + retParamTypes.push_back(type->externalType()); } return make_shared(paramTypes, retParamTypes, m_location, m_arbitraryParameters); } diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 99fd878f0..cea711991 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -512,6 +512,8 @@ public: virtual Category getCategory() const override { return Category::Function; } + /// @returns TypePointer of a new FunctionType object. All input/return parameters are an appropriate external types of input/return parameters of current function. + /// Returns an empty shared pointer if one of input/return parameters does not have an externaltype. virtual TypePointer externalType() const override; explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); From b4e869acfaac4a4313c19bb634b842ce6cd65ff6 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 1 Apr 2015 15:25:32 +0200 Subject: [PATCH 220/228] ui changes --- mix/qml/LogsPane.qml | 55 +++++++++++++++++++++++++++------------ mix/qml/LogsPaneStyle.qml | 2 +- mix/qml/StatusPane.qml | 27 ++++++++++++------- 3 files changed, 58 insertions(+), 26 deletions(-) diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml index f600f9f9d..23ab8e682 100644 --- a/mix/qml/LogsPane.qml +++ b/mix/qml/LogsPane.qml @@ -7,19 +7,29 @@ import "." Rectangle { + property variant currentStatus; function push(_level, _type, _content) { _content = _content.replace(/\n/g, " ") logsModel.insert(0, { "type": _type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": _content, "level": _level }); } - Keys.onEscapePressed: + onVisibleChanged: { - parent.toggle(); + if (visible && (logsModel.count === 0 || (logsModel.get(0).date !== currentStatus.date && logsModel.get(0).content !== currentStatus.content))) + logsModel.insert(0, { "type": currentStatus.type, "date": currentStatus.date, "content": currentStatus.content, "level": currentStatus.level }); + else if (!visible) + { + for (var k = 0; k < logsModel.count; k++) + { + if (logsModel.get(k).type === "Comp") //do not keep compilation logs. + logsModel.remove(k); + } + } } anchors.fill: parent - radius: 5 + radius: 10 color: "transparent" id: logsPane Column { @@ -43,11 +53,13 @@ Rectangle id: logsRect spacing: 0 Repeater { + id: logsRepeater clip: true + property string frontColor: "transparent" model: SortFilterProxyModel { id: proxyModel source: logsModel - property var roles: ["-", "javascript", "run", "state"] + property var roles: ["-", "javascript", "run", "state", "comp"] Component.onCompleted: { filterType = regEx(proxyModel.roles); @@ -80,7 +92,7 @@ Rectangle return "(?:" + roles.join('|') + ")"; } - filterType: "(?:javascript|run|state)" + filterType: "(?:javascript|run|state|comp)" filterContent: "" filterSyntax: SortFilterProxyModel.RegExp filterCaseSensitivity: Qt.CaseInsensitive @@ -92,12 +104,17 @@ Rectangle height: 30 color: { + var cl; if (level === "warning" || level === "error") - return "#fffcd5"; + cl = "#fffcd5"; else - return index % 2 === 0 ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor; + cl = index % 2 === 0 ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor; + if (index === 0) + logsRepeater.frontColor = cl; + return cl; } + MouseArea { anchors.fill: parent @@ -148,13 +165,11 @@ Rectangle Text { id: logContent text: content; - font.family: LogsPaneStyle.generic.layout.logLabelFont + font.family: "sans serif" width: LogsPaneStyle.generic.layout.contentWidth font.pointSize: Style.absoluteSize(-1) anchors.verticalCenter: parent.verticalCenter elide: Text.ElideRight - - maximumLineCount: 10 anchors.left: parent.left anchors.leftMargin: 190 color: { @@ -230,7 +245,14 @@ Rectangle anchors.verticalCenter: parent.verticalCenter width: 1; height: parent.height - color : "transparent" + color : "#d3d0d0" + } + + Rectangle { + anchors.verticalCenter: parent.verticalCenter + width: 2; + height: parent.height + color : "#f2f1f2" } ToolButton { @@ -272,7 +294,7 @@ Rectangle Rectangle { anchors.verticalCenter: parent.verticalCenter - width: 1; + width: 2; height: parent.height color : "#f2f1f2" } @@ -311,14 +333,14 @@ Rectangle anchors.verticalCenter: parent.verticalCenter width: 1; height: parent.height - color : "#d3d0d0" + color: "#d3d0d0" } Rectangle { anchors.verticalCenter: parent.verticalCenter - width: 1; + width: 2; height: parent.height - color : "#f2f1f2" + color: "#f2f1f2" } ToolButton { @@ -360,7 +382,7 @@ Rectangle Rectangle { anchors.verticalCenter: parent.verticalCenter - width: 1; + width: 2; height: parent.height color : "#f2f1f2" } @@ -541,4 +563,5 @@ Rectangle } } } + } diff --git a/mix/qml/LogsPaneStyle.qml b/mix/qml/LogsPaneStyle.qml index 8e0dfd5f4..e4985b70e 100644 --- a/mix/qml/LogsPaneStyle.qml +++ b/mix/qml/LogsPaneStyle.qml @@ -15,7 +15,7 @@ QtObject { property int headerButtonSpacing: 0 property int leftMargin: 10 property int headerButtonHeight: 30 - property string logLabelColor: "#808080" + property string logLabelColor: "#4a4a4a" property string logLabelFont: "sans serif" property int headerInputWidth: 200 property int dateWidth: 70 diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index 3d366021f..4a9287bec 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -9,7 +9,7 @@ Rectangle { id: statusHeader objectName: "statusPane" property variant webPreview - + property alias currentStatus: logPane.currentStatus function updateStatus(message) { if (!message) @@ -17,6 +17,7 @@ Rectangle { status.state = ""; status.text = qsTr("Compile successfully."); debugImg.state = "active"; + currentStatus = { "type": "Comp", "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": status.text, "level": "info" } } else { @@ -24,6 +25,7 @@ Rectangle { var errorInfo = ErrorLocationFormater.extractErrorInfo(message, true); status.text = errorInfo.errorLocation + " " + errorInfo.errorDetail; debugImg.state = ""; + currentStatus = { "type": "Comp", "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": status.text, "level": "error" } } debugRunActionIcon.enabled = codeModel.hasContract; } @@ -33,6 +35,7 @@ Rectangle { status.state = ""; status.text = text logPane.push("info", type, text); + currentStatus = { "type": type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": text, "level": "info" } } function warningMessage(text, type) @@ -40,6 +43,7 @@ Rectangle { status.state = "warning"; status.text = text logPane.push("warning", type, text); + currentStatus = { "type": type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": text, "level": "warning" } } function errorMessage(text, type) @@ -47,6 +51,7 @@ Rectangle { status.state = "error"; status.text = text; logPane.push("error", type, text); + currentStatus = { "type": type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": text, "level": "error" } } Connections { @@ -118,7 +123,7 @@ Rectangle { Text { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter - font.pointSize: StatusPaneStyle.general.statusFontSize + font.pointSize: Style.absoluteSize(-1) height: 15 font.family: "sans serif" objectName: "status" @@ -196,7 +201,9 @@ Rectangle { function toggle() { if (logsContainer.state === "opened") + { logsContainer.state = "closed" + } else { logsContainer.state = "opened"; @@ -208,11 +215,10 @@ Rectangle { id: logsContainer width: 750 - anchors.topMargin: -30 anchors.top: statusContainer.bottom - anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 4 visible: false - radius: 5 + radius: 10 function calCoord() { @@ -221,28 +227,31 @@ Rectangle { top = top.parent var coordinates = logsContainer.mapToItem(top, 0, 0); logsContainer.parent = top; - logsContainer.x = coordinates.x; - logsContainer.y = coordinates.y; + logsContainer.x = status.x + statusContainer.x - LogsPaneStyle.generic.layout.dateWidth - LogsPaneStyle.generic.layout.typeWidth - 30 } LogsPane { id: logPane; } + states: [ State { name: "opened"; PropertyChanges { target: logsContainer; height: 500; visible: true } + PropertyChanges { target: statusContainer; width: 100; height: 25 } }, State { name: "closed"; PropertyChanges { target: logsContainer; height: 0; visible: false } + PropertyChanges { target: statusContainer; width: 600; height: 30 } } ] transitions: Transition { NumberAnimation { properties: "height"; easing.type: Easing.InOutQuad; duration: 200 } - NumberAnimation { properties: "visible"; easing.type: Easing.InOutQuad; duration: 200 } - } + NumberAnimation { target: logsContainer; properties: "visible"; easing.type: Easing.InOutQuad; duration: 200 } + NumberAnimation { target: statusContainer; properties: "width"; easing.type: Easing.InOutQuad; duration: 500 } + } } } From f4ea6b8e8d2f8c72223372113ddcf84cecbc1c23 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Wed, 1 Apr 2015 15:26:37 +0200 Subject: [PATCH 221/228] Update Types.h --- libsolidity/Types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/Types.h b/libsolidity/Types.h index cea711991..e1852bc7f 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -513,7 +513,7 @@ public: virtual Category getCategory() const override { return Category::Function; } /// @returns TypePointer of a new FunctionType object. All input/return parameters are an appropriate external types of input/return parameters of current function. - /// Returns an empty shared pointer if one of input/return parameters does not have an externaltype. + /// Returns an empty shared pointer if one of the input/return parameters does not have an externaltype. virtual TypePointer externalType() const override; explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); From c5df9e205b8c9c76d74a7337db02e62956765873 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Wed, 1 Apr 2015 15:41:24 +0200 Subject: [PATCH 222/228] Update AST.cpp --- libsolidity/AST.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index d489a4489..fec123f75 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -341,7 +341,7 @@ void FunctionDefinition::checkTypeRequirements() { // todo delete when will be implemented arrays as parameter type in internal functions if (getVisibility() == Visibility::Public && var->getType()->getCategory() == Type::Category::Array) - BOOST_THROW_EXCEPTION(var->createTypeError("Array type is not allowed as parameter for internal functions.")); + BOOST_THROW_EXCEPTION(var->createTypeError("Arrays only implemented for external functions.")); else BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for public and external functions.")); } @@ -386,7 +386,7 @@ void VariableDeclaration::checkTypeRequirements() if (m_isStateVariable && !m_type->externalType() && getVisibility() >= Visibility::Public) BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for state variables.")); - auto sharedToExternalTypes = FunctionType(*this).externalType(); // do not distroy the shared pointer. + auto sharedToExternalTypes = FunctionType(*this).externalType(); // do not destroy the shared pointer. auto externalFunctionTypes = dynamic_cast(sharedToExternalTypes.get()); for (auto parameter: externalFunctionTypes->getParameterTypes() + externalFunctionTypes->getReturnParameterTypes()) if (!parameter || !(parameter->externalType())) From ab0b92242f58503e3a7f5732a0adf0b0b790fa8d Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Wed, 1 Apr 2015 15:42:30 +0200 Subject: [PATCH 223/228] Update Types.cpp --- libsolidity/Types.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 86b740262..78649cc95 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -1105,13 +1105,13 @@ TypePointer FunctionType::externalType() const for (auto type: m_parameterTypes) { - if(!type->externalType()) + if (!type->externalType()) return TypePointer(); paramTypes.push_back(type->externalType()); } for (auto type: m_returnParameterTypes) { - if(!type->externalType()) + if (!type->externalType()) return TypePointer(); retParamTypes.push_back(type->externalType()); } From 119101812103b7123b86d7f293d3c690a0456530 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 1 Apr 2015 15:43:01 +0200 Subject: [PATCH 224/228] style --- mix/qml/LogsPane.qml | 28 ++++++++++++++-------------- mix/qml/LogsPaneStyle.qml | 4 ++++ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml index 23ab8e682..86378ff17 100644 --- a/mix/qml/LogsPane.qml +++ b/mix/qml/LogsPane.qml @@ -106,7 +106,7 @@ Rectangle { var cl; if (level === "warning" || level === "error") - cl = "#fffcd5"; + cl = LogsPaneStyle.generic.layout.errorColor; else cl = index % 2 === 0 ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor; if (index === 0) @@ -165,7 +165,7 @@ Rectangle Text { id: logContent text: content; - font.family: "sans serif" + font.family: LogsPaneStyle.generic.layout.logLabelFont width: LogsPaneStyle.generic.layout.contentWidth font.pointSize: Style.absoluteSize(-1) anchors.verticalCenter: parent.verticalCenter @@ -234,7 +234,7 @@ Rectangle DefaultLabel { anchors.verticalCenter: parent.verticalCenter - color: "#808080" + color: LogsPaneStyle.generic.layout.logLabelColor font.pointSize: Style.absoluteSize(-3) font.family: LogsPaneStyle.generic.layout.logLabelFont text: qsTr("Show:") @@ -245,14 +245,14 @@ Rectangle anchors.verticalCenter: parent.verticalCenter width: 1; height: parent.height - color : "#d3d0d0" + color: LogsPaneStyle.generic.layout.buttonSeparatorColor1 } Rectangle { anchors.verticalCenter: parent.verticalCenter width: 2; height: parent.height - color : "#f2f1f2" + color: LogsPaneStyle.generic.layout.buttonSeparatorColor2 } ToolButton { @@ -280,7 +280,7 @@ Rectangle } background: Rectangle { - color: javascriptButton.checked ? "#dcdcdc" : "transparent" + color: javascriptButton.checked ? LogsPaneStyle.generic.layout.buttonSelected : "transparent" } } } @@ -289,14 +289,14 @@ Rectangle anchors.verticalCenter: parent.verticalCenter width: 1; height: parent.height - color : "#d3d0d0" + color: LogsPaneStyle.generic.layout.buttonSeparatorColor1 } Rectangle { anchors.verticalCenter: parent.verticalCenter width: 2; height: parent.height - color : "#f2f1f2" + color: LogsPaneStyle.generic.layout.buttonSeparatorColor2 } ToolButton { @@ -324,7 +324,7 @@ Rectangle } background: Rectangle { - color: runButton.checked ? "#dcdcdc" : "transparent" + color: runButton.checked ? LogsPaneStyle.generic.layout.buttonSelected : "transparent" } } } @@ -333,14 +333,14 @@ Rectangle anchors.verticalCenter: parent.verticalCenter width: 1; height: parent.height - color: "#d3d0d0" + color: LogsPaneStyle.generic.layout.buttonSeparatorColor1 } Rectangle { anchors.verticalCenter: parent.verticalCenter width: 2; height: parent.height - color: "#f2f1f2" + color: LogsPaneStyle.generic.layout.buttonSeparatorColor2 } ToolButton { @@ -368,7 +368,7 @@ Rectangle } background: Rectangle { - color: stateButton.checked ? "#dcdcdc" : "transparent" + color: stateButton.checked ? LogsPaneStyle.generic.layout.buttonSelected : "transparent" } } } @@ -377,14 +377,14 @@ Rectangle anchors.verticalCenter: parent.verticalCenter width: 1; height: parent.height - color : "#d3d0d0" + color: LogsPaneStyle.generic.layout.buttonSeparatorColor1 } Rectangle { anchors.verticalCenter: parent.verticalCenter width: 2; height: parent.height - color : "#f2f1f2" + color: LogsPaneStyle.generic.layout.buttonSeparatorColor2 } } diff --git a/mix/qml/LogsPaneStyle.qml b/mix/qml/LogsPaneStyle.qml index e4985b70e..19f6a653e 100644 --- a/mix/qml/LogsPaneStyle.qml +++ b/mix/qml/LogsPaneStyle.qml @@ -22,6 +22,10 @@ QtObject { property int typeWidth: 90 property int contentWidth: 560 property string logAlternateColor: "#f6f5f6" + property string errorColor: "#fffcd5" + property string buttonSeparatorColor1: "#d3d0d0" + property string buttonSeparatorColor2: "#f2f1f2" + property string buttonSelected: "#dcdcdc" } } } From 7d9d8fe200afc53061456f360e91a5d1d2406dbf Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 1 Apr 2015 15:50:01 +0200 Subject: [PATCH 225/228] small change --- mix/qml/LogsPane.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml index 86378ff17..c619080bf 100644 --- a/mix/qml/LogsPane.qml +++ b/mix/qml/LogsPane.qml @@ -489,9 +489,8 @@ Rectangle DefaultTextField { id: searchBox - height: LogsPaneStyle.generic.layout.headerButtonHeight - 5 anchors.verticalCenter: parent.verticalCenter - width: LogsPaneStyle.generic.layout.headerInputWidth - 40 + width: LogsPaneStyle.generic.layout.headerInputWidth - 50 font.family: LogsPaneStyle.generic.layout.logLabelFont font.pointSize: Style.absoluteSize(-3) font.italic: true From 065d9a9e68b3e4ca8413ff5eca15ccca18633080 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Wed, 1 Apr 2015 15:57:39 +0200 Subject: [PATCH 226/228] VariableDeclaration::checkTypeRequirements() refactoring --- libsolidity/AST.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index fec123f75..095ba7bf1 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -386,11 +386,8 @@ void VariableDeclaration::checkTypeRequirements() if (m_isStateVariable && !m_type->externalType() && getVisibility() >= Visibility::Public) BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for state variables.")); - auto sharedToExternalTypes = FunctionType(*this).externalType(); // do not destroy the shared pointer. - auto externalFunctionTypes = dynamic_cast(sharedToExternalTypes.get()); - for (auto parameter: externalFunctionTypes->getParameterTypes() + externalFunctionTypes->getReturnParameterTypes()) - if (!parameter || !(parameter->externalType())) - BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables.")); + if (!FunctionType(*this).externalType()) + BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables.")); } else { From 37086e60e2b8f9ce059a4408cf5eb04af517ba69 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Wed, 1 Apr 2015 18:51:49 +0200 Subject: [PATCH 227/228] removed friendship for Assembly and AssemblyItem added set functions for type and data members of AssemblyItem --- libevmcore/Assembly.cpp | 40 +++++++++++++++++++-------------------- libevmcore/Assembly.h | 2 +- libevmcore/AssemblyItem.h | 5 +++-- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index 0301c9325..904903761 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -34,9 +34,9 @@ void Assembly::append(Assembly const& _a) for (AssemblyItem i: _a.m_items) { if (i.type() == Tag || i.type() == PushTag) - i.m_data += m_usedTags; + i.setData(i.data() + m_usedTags); else if (i.type() == PushSub || i.type() == PushSubSize) - i.m_data += m_subs.size(); + i.setData(i.data() + m_usedTags); append(i); } m_deposit = newDeposit; @@ -106,34 +106,34 @@ ostream& Assembly::stream(ostream& _out, string const& _prefix, StringMap const& for (AssemblyItem const& i: m_items) { _out << _prefix; - switch (i.m_type) + switch (i.type()) { case Operation: _out << " " << instructionInfo(i.instruction()).name << "\t" << i.getJumpTypeAsString(); break; case Push: - _out << " PUSH " << i.m_data; + _out << " PUSH " << i.data(); break; case PushString: - _out << " PUSH \"" << m_strings.at((h256)i.m_data) << "\""; + _out << " PUSH \"" << m_strings.at((h256)i.data()) << "\""; break; case PushTag: - _out << " PUSH [tag" << i.m_data << "]"; + _out << " PUSH [tag" << i.data() << "]"; break; case PushSub: - _out << " PUSH [$" << h256(i.m_data).abridged() << "]"; + _out << " PUSH [$" << h256(i.data()).abridged() << "]"; break; case PushSubSize: - _out << " PUSH #[$" << h256(i.m_data).abridged() << "]"; + _out << " PUSH #[$" << h256(i.data()).abridged() << "]"; break; case PushProgramSize: _out << " PUSHSIZE"; break; case Tag: - _out << "tag" << i.m_data << ": " << endl << _prefix << " JUMPDEST"; + _out << "tag" << i.data() << ": " << endl << _prefix << " JUMPDEST"; break; case PushData: - _out << " PUSH [" << hex << (unsigned)i.m_data << "]"; + _out << " PUSH [" << hex << (unsigned)i.data() << "]"; break; default: BOOST_THROW_EXCEPTION(InvalidOpcode()); @@ -189,7 +189,7 @@ Assembly& Assembly::optimise(bool _enable) return *this; std::vector>> rules; // jump to next instruction - rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }}); + rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].data() == m[2].data()) return {m[2]}; else return m.toVector(); }}); unsigned total = 0; for (unsigned count = 1; count > 0; total += count) @@ -331,16 +331,16 @@ bytes Assembly::assemble() const // m_data must not change from here on for (AssemblyItem const& i: m_items) - switch (i.m_type) + switch (i.type()) { case Operation: - ret.push_back((byte)i.m_data); + ret.push_back((byte)i.data()); break; case PushString: { ret.push_back((byte)Instruction::PUSH32); unsigned ii = 0; - for (auto j: m_strings.at((h256)i.m_data)) + for (auto j: m_strings.at((h256)i.data())) if (++ii > 32) break; else @@ -351,30 +351,30 @@ bytes Assembly::assemble() const } case Push: { - byte b = max(1, dev::bytesRequired(i.m_data)); + byte b = max(1, dev::bytesRequired(i.data())); ret.push_back((byte)Instruction::PUSH1 - 1 + b); ret.resize(ret.size() + b); bytesRef byr(&ret.back() + 1 - b, b); - toBigEndian(i.m_data, byr); + toBigEndian(i.data(), byr); break; } case PushTag: { ret.push_back(tagPush); - tagRef[ret.size()] = (unsigned)i.m_data; + tagRef[ret.size()] = (unsigned)i.data(); ret.resize(ret.size() + bytesPerTag); break; } case PushData: case PushSub: { ret.push_back(dataRefPush); - dataRef.insert(make_pair((h256)i.m_data, ret.size())); + dataRef.insert(make_pair((h256)i.data(), ret.size())); ret.resize(ret.size() + bytesPerDataRef); break; } case PushSubSize: { - auto s = m_data[i.m_data].size(); + auto s = m_data[i.data()].size(); byte b = max(1, dev::bytesRequired(s)); ret.push_back((byte)Instruction::PUSH1 - 1 + b); ret.resize(ret.size() + b); @@ -390,7 +390,7 @@ bytes Assembly::assemble() const break; } case Tag: - tagPos[(unsigned)i.m_data] = ret.size(); + tagPos[(unsigned)i.data()] = ret.size(); ret.push_back((byte)Instruction::JUMPDEST); break; default: diff --git a/libevmcore/Assembly.h b/libevmcore/Assembly.h index 315e6aaed..2744af900 100644 --- a/libevmcore/Assembly.h +++ b/libevmcore/Assembly.h @@ -65,7 +65,7 @@ public: template Assembly& operator<<(T const& _d) { append(_d); return *this; } AssemblyItems const& getItems() const { return m_items; } AssemblyItem const& back() const { return m_items.back(); } - std::string backString() const { return m_items.size() && m_items.back().m_type == PushString ? m_strings.at((h256)m_items.back().m_data) : std::string(); } + std::string backString() const { return m_items.size() && m_items.back().type() == PushString ? m_strings.at((h256)m_items.back().data()) : std::string(); } void onePath() { if (asserts(!m_totalDeposit && !m_baseDeposit)) BOOST_THROW_EXCEPTION(InvalidDeposit()); m_baseDeposit = m_deposit; m_totalDeposit = INT_MAX; } void otherPath() { donePath(); m_totalDeposit = m_deposit; m_deposit = m_baseDeposit; } diff --git a/libevmcore/AssemblyItem.h b/libevmcore/AssemblyItem.h index e7beced39..3e222a6eb 100644 --- a/libevmcore/AssemblyItem.h +++ b/libevmcore/AssemblyItem.h @@ -40,8 +40,6 @@ class Assembly; class AssemblyItem { - friend class Assembly; - public: enum class JumpType { Ordinary, IntoFunction, OutOfFunction }; @@ -54,6 +52,9 @@ public: AssemblyItemType type() const { return m_type; } u256 const& data() const { return m_data; } + void setType(AssemblyItemType const _type) { m_type = _type; } + void setData(u256 const& _data) { m_data = _data; } + /// @returns the instruction of this item (only valid if type() == Operation) Instruction instruction() const { return Instruction(byte(m_data)); } From 11670e68d3e51151057cfcfb10ec4ed44b199615 Mon Sep 17 00:00:00 2001 From: Ali Mashatan Date: Thu, 2 Apr 2015 00:20:27 +0430 Subject: [PATCH 228/228] fix #1472 --- mix/qml/Debugger.qml | 50 ++-- mix/qml/StatesComboBox.qml | 454 ++++++++++++++++++------------------- mix/qml/TransactionLog.qml | 292 ++++++++++++------------ 3 files changed, 398 insertions(+), 398 deletions(-) diff --git a/mix/qml/Debugger.qml b/mix/qml/Debugger.qml index 202d27a0c..9ab2f03a4 100644 --- a/mix/qml/Debugger.qml +++ b/mix/qml/Debugger.qml @@ -220,31 +220,31 @@ Rectangle { id: jumpButtons spacing: 3 - StepActionImage - { - id: playAction - enabledStateImg: "qrc:/qml/img/play_button.png" - disableStateImg: "qrc:/qml/img/play_button.png" - onClicked: console.log("play"); - width: 30 - height: 30 - buttonShortcut: "Ctrl+Shift+F8" - buttonTooltip: qsTr("Play") - visible: true - } - - StepActionImage - { - id: pauseAction - enabledStateImg: "qrc:/qml/img/pause_button.png" - disableStateImg: "qrc:/qml/img/pause_button.png" - onClicked: console.log("pause"); - width: 30 - height: 30 - buttonShortcut: "Ctrl+Shift+F9" - buttonTooltip: qsTr("Pause") - visible: true - } + StepActionImage + { + id: playAction + enabledStateImg: "qrc:/qml/img/play_button.png" + disableStateImg: "qrc:/qml/img/play_button.png" + onClicked: console.log("play"); + width: 30 + height: 30 + buttonShortcut: "Ctrl+Shift+F8" + buttonTooltip: qsTr("Play") + visible: true + } + + StepActionImage + { + id: pauseAction + enabledStateImg: "qrc:/qml/img/pause_button.png" + disableStateImg: "qrc:/qml/img/pause_button.png" + onClicked: console.log("pause"); + width: 30 + height: 30 + buttonShortcut: "Ctrl+Shift+F9" + buttonTooltip: qsTr("Pause") + visible: true + } StepActionImage { diff --git a/mix/qml/StatesComboBox.qml b/mix/qml/StatesComboBox.qml index 246b3e93b..34567d083 100644 --- a/mix/qml/StatesComboBox.qml +++ b/mix/qml/StatesComboBox.qml @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** @file StatesComboBox.qml * @author Ali Mashatan ali@ethdev.com @@ -26,220 +26,220 @@ import QtQuick.Layouts 1.1 import QtGraphicalEffects 1.0 Rectangle { - id: statesComboBox - - width: 200 - height: 20 - - Component.onCompleted: { - var top = dropDownList - while (top.parent) { - top = top.parent - if (top.objectName == "debugPanel") - break - } - var coordinates = dropDownList.mapToItem(top, 0, 0) - //the order is important - dropDownShowdowList.parent = top - dropDownList.parent = top - - dropDownShowdowList.x = coordinates.x - dropDownShowdowList.y = coordinates.y - - dropDownList.x = coordinates.x - dropDownList.y = coordinates.y - } - - signal selectItem(real item) - signal editItem(real item) - signal selectCreate - property variant rowHeight: 25 - property variant items - property alias selectedItem: chosenItemText.text - property alias selectedIndex: listView.currentRow - function setSelectedIndex(index) { - listView.currentRow = index - chosenItemText.text = statesComboBox.items.get(index).title - } - - signal comboClicked - - property variant colorItem - property variant colorSelect - - smooth: true - Rectangle { - id: chosenItem - width: parent.width - height: statesComboBox.height - color: statesComboBox.color - smooth: true - Text { - id: chosenItemText - anchors.top: parent.top - anchors.left: parent.left - anchors.margins: 2 - color: statesComboBox.colorItem - text: "" - smooth: true - } - - MouseArea { - anchors.fill: parent - onClicked: { - statesComboBox.state = statesComboBox.state === "dropDown" ? "" : "dropDown" - } - } - } - - Rectangle { - id: dropDownShowdowList - width: statesComboBox.width - opacity: 0.3 - height: 0 - clip: true - radius: 4 - anchors.top: chosenItem.top - anchors.margins: 2 - color: "gray" - } - //ToDo: We need scrollbar for items - Rectangle { - id: dropDownList - width: statesComboBox.width - height: 0 - clip: true - radius: 4 - anchors.top: chosenItem.top - anchors.margins: 2 - color: statesComboBox.color - - ColumnLayout { - spacing: 2 - TableView { - id: listView - height: 20 - implicitHeight: 0 - width: statesComboBox.width - model: statesComboBox.items - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - currentRow: -1 - headerVisible: false - backgroundVisible: false - alternatingRowColors: false - frameVisible: false - - TableViewColumn { - role: "title" - title: "" - width: statesComboBox.width - delegate: mainItemDelegate - } - rowDelegate: Rectangle { - width: statesComboBox.width - height: statesComboBox.rowHeight - } - Component { - id: mainItemDelegate - Rectangle { - id: itemDelegate - width: statesComboBox.width - height: statesComboBox.height - Text { - id: textItemid - text: styleData.value - color: statesComboBox.colorItem - anchors.top: parent.top - anchors.left: parent.left - anchors.margins: 5 - } - Image { - id: imageItemid - height: 20 - width: 20 - anchors.right: parent.right - anchors.top: parent.top - anchors.margins: 5 - visible: false - fillMode: Image.PreserveAspectFit - source: "img/edit_combox.png" - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - - onEntered: { - imageItemid.visible = true - textItemid.color = statesComboBox.colorSelect - } - onExited: { - imageItemid.visible = false - textItemid.color = statesComboBox.colorItem - } - onClicked: { - if (mouseX > imageItemid.x - && mouseX < imageItemid.x + imageItemid.width - && mouseY > imageItemid.y - && mouseY < imageItemid.y + imageItemid.height) - statesComboBox.editItem(styleData.row) - else { - statesComboBox.state = "" - var prevSelection = chosenItemText.text - chosenItemText.text = styleData.value - listView.currentRow = styleData.row - statesComboBox.selectItem(styleData.row) - } - } - } - } //Item - } //Component - } //Table View - - RowLayout { - Rectangle { - width: 1 - } - Text { - id: createStateText - width: statesComboBox.width - height: statesComboBox.height - font.bold: true - text: qsTr("Create State ...") - MouseArea { - anchors.fill: parent - hoverEnabled: true - - onEntered: { - createStateText.color = statesComboBox.colorSelect - } - onExited: { - createStateText.color = statesComboBox.colorItem - } - onClicked: { - statesComboBox.state = "" - statesComboBox.selectCreate() - } - } - } - } - } - } - states: State { - name: "dropDown" - PropertyChanges { - target: dropDownList - height: (statesComboBox.rowHeight * (statesComboBox.items.count + 1)) - } - PropertyChanges { - target: dropDownShowdowList - width: statesComboBox.width + 3 - height: (statesComboBox.rowHeight * (statesComboBox.items.count + 1)) + 3 - } - PropertyChanges { - target: listView - height: 20 - implicitHeight: (statesComboBox.rowHeight * (statesComboBox.items.count)) - } - } + id: statesComboBox + + width: 200 + height: 20 + + Component.onCompleted: { + var top = dropDownList + while (top.parent) { + top = top.parent + if (top.objectName == "debugPanel") + break + } + var coordinates = dropDownList.mapToItem(top, 0, 0) + //the order is important + dropDownShowdowList.parent = top + dropDownList.parent = top + + dropDownShowdowList.x = coordinates.x + dropDownShowdowList.y = coordinates.y + + dropDownList.x = coordinates.x + dropDownList.y = coordinates.y + } + + signal selectItem(real item) + signal editItem(real item) + signal selectCreate + property variant rowHeight: 25 + property variant items + property alias selectedItem: chosenItemText.text + property alias selectedIndex: listView.currentRow + function setSelectedIndex(index) { + listView.currentRow = index + chosenItemText.text = statesComboBox.items.get(index).title + } + + signal comboClicked + + property variant colorItem + property variant colorSelect + + smooth: true + Rectangle { + id: chosenItem + width: parent.width + height: statesComboBox.height + color: statesComboBox.color + smooth: true + Text { + id: chosenItemText + anchors.top: parent.top + anchors.left: parent.left + anchors.margins: 2 + color: statesComboBox.colorItem + text: "" + smooth: true + } + + MouseArea { + anchors.fill: parent + onClicked: { + statesComboBox.state = statesComboBox.state === "dropDown" ? "" : "dropDown" + } + } + } + + Rectangle { + id: dropDownShowdowList + width: statesComboBox.width + opacity: 0.3 + height: 0 + clip: true + radius: 4 + anchors.top: chosenItem.top + anchors.margins: 2 + color: "gray" + } + //ToDo: We need scrollbar for items + Rectangle { + id: dropDownList + width: statesComboBox.width + height: 0 + clip: true + radius: 4 + anchors.top: chosenItem.top + anchors.margins: 2 + color: statesComboBox.color + + ColumnLayout { + spacing: 2 + TableView { + id: listView + height: 20 + implicitHeight: 0 + width: statesComboBox.width + model: statesComboBox.items + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + currentRow: -1 + headerVisible: false + backgroundVisible: false + alternatingRowColors: false + frameVisible: false + + TableViewColumn { + role: "title" + title: "" + width: statesComboBox.width + delegate: mainItemDelegate + } + rowDelegate: Rectangle { + width: statesComboBox.width + height: statesComboBox.rowHeight + } + Component { + id: mainItemDelegate + Rectangle { + id: itemDelegate + width: statesComboBox.width + height: statesComboBox.height + Text { + id: textItemid + text: styleData.value + color: statesComboBox.colorItem + anchors.top: parent.top + anchors.left: parent.left + anchors.margins: 5 + } + Image { + id: imageItemid + height: 20 + width: 20 + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: 5 + visible: false + fillMode: Image.PreserveAspectFit + source: "img/edit_combox.png" + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + + onEntered: { + imageItemid.visible = true + textItemid.color = statesComboBox.colorSelect + } + onExited: { + imageItemid.visible = false + textItemid.color = statesComboBox.colorItem + } + onClicked: { + if (mouseX > imageItemid.x + && mouseX < imageItemid.x + imageItemid.width + && mouseY > imageItemid.y + && mouseY < imageItemid.y + imageItemid.height) + statesComboBox.editItem(styleData.row) + else { + statesComboBox.state = "" + var prevSelection = chosenItemText.text + chosenItemText.text = styleData.value + listView.currentRow = styleData.row + statesComboBox.selectItem(styleData.row) + } + } + } + } //Item + } //Component + } //Table View + + RowLayout { + Rectangle { + width: 1 + } + Text { + id: createStateText + width: statesComboBox.width + height: statesComboBox.height + font.bold: true + text: qsTr("Create State ...") + MouseArea { + anchors.fill: parent + hoverEnabled: true + + onEntered: { + createStateText.color = statesComboBox.colorSelect + } + onExited: { + createStateText.color = statesComboBox.colorItem + } + onClicked: { + statesComboBox.state = "" + statesComboBox.selectCreate() + } + } + } + } + } + } + states: State { + name: "dropDown" + PropertyChanges { + target: dropDownList + height: (statesComboBox.rowHeight * (statesComboBox.items.count + 1)) + } + PropertyChanges { + target: dropDownShowdowList + width: statesComboBox.width + 3 + height: (statesComboBox.rowHeight * (statesComboBox.items.count + 1)) + 3 + } + PropertyChanges { + target: listView + height: 20 + implicitHeight: (statesComboBox.rowHeight * (statesComboBox.items.count)) + } + } } diff --git a/mix/qml/TransactionLog.qml b/mix/qml/TransactionLog.qml index 1772c7f18..3bcc6c270 100644 --- a/mix/qml/TransactionLog.qml +++ b/mix/qml/TransactionLog.qml @@ -6,157 +6,157 @@ import QtQuick.Layouts 1.1 import org.ethereum.qml.RecordLogEntry 1.0 Item { - property ListModel fullModel: ListModel{} - property ListModel transactionModel: ListModel{} - property ListModel callModel: ListModel{} + property ListModel fullModel: ListModel{} + property ListModel transactionModel: ListModel{} + property ListModel callModel: ListModel{} - ColumnLayout { - anchors.fill: parent - RowLayout { - anchors.right: parent.right - anchors.left: parent.left - Connections - { - id: compilationStatus - target: codeModel - property bool compilationComplete: false - onCompilationComplete: compilationComplete = true - onCompilationError: compilationComplete = false - } + ColumnLayout { + anchors.fill: parent + RowLayout { + anchors.right: parent.right + anchors.left: parent.left + Connections + { + id: compilationStatus + target: codeModel + property bool compilationComplete: false + onCompilationComplete: compilationComplete = true + onCompilationError: compilationComplete = false + } - Connections - { - target: projectModel - onProjectSaved: - { - if (projectModel.appIsClosing || projectModel.projectIsClosing) - return; - if (compilationStatus.compilationComplete && codeModel.hasContract && !clientModel.running) - projectModel.stateListModel.debugDefaultState(); - } - onProjectClosed: - { - fullModel.clear(); - transactionModel.clear(); - callModel.clear(); - } - onContractSaved: { - if (compilationStatus.compilationComplete && codeModel.hasContract && !clientModel.running) - projectModel.stateListModel.debugDefaultState(); - } - } - StatesComboBox - { - id: statesCombo - items: projectModel.stateListModel - onSelectCreate: projectModel.stateListModel.addState(); - onEditItem: projectModel.stateListModel.editState(item) - colorItem: "black" - colorSelect: "blue" - color: "white" - Connections { - target: projectModel.stateListModel - onStateRun: { - if (statesCombo.selectedIndex !== index) - statesCombo.setSelectedIndex( index ); - } - } - } - Button - { - anchors.rightMargin: 9 - anchors.verticalCenter: parent.verticalCenter - action: mineAction - } + Connections + { + target: projectModel + onProjectSaved: + { + if (projectModel.appIsClosing || projectModel.projectIsClosing) + return; + if (compilationStatus.compilationComplete && codeModel.hasContract && !clientModel.running) + projectModel.stateListModel.debugDefaultState(); + } + onProjectClosed: + { + fullModel.clear(); + transactionModel.clear(); + callModel.clear(); + } + onContractSaved: { + if (compilationStatus.compilationComplete && codeModel.hasContract && !clientModel.running) + projectModel.stateListModel.debugDefaultState(); + } + } + StatesComboBox + { + id: statesCombo + items: projectModel.stateListModel + onSelectCreate: projectModel.stateListModel.addState(); + onEditItem: projectModel.stateListModel.editState(item) + colorItem: "black" + colorSelect: "blue" + color: "white" + Connections { + target: projectModel.stateListModel + onStateRun: { + if (statesCombo.selectedIndex !== index) + statesCombo.setSelectedIndex( index ); + } + } + } + Button + { + anchors.rightMargin: 9 + anchors.verticalCenter: parent.verticalCenter + action: mineAction + } - ComboBox { - id: itemFilter + ComboBox { + id: itemFilter - function getCurrentModel() - { - return currentIndex === 0 ? fullModel : currentIndex === 1 ? transactionModel : currentIndex === 2 ? callModel : fullModel; - } + function getCurrentModel() + { + return currentIndex === 0 ? fullModel : currentIndex === 1 ? transactionModel : currentIndex === 2 ? callModel : fullModel; + } - model: ListModel { - ListElement { text: qsTr("Calls and Transactions"); value: 0; } - ListElement { text: qsTr("Only Transactions"); value: 1; } - ListElement { text: qsTr("Only Calls"); value: 2; } - } + model: ListModel { + ListElement { text: qsTr("Calls and Transactions"); value: 0; } + ListElement { text: qsTr("Only Transactions"); value: 1; } + ListElement { text: qsTr("Only Calls"); value: 2; } + } - onCurrentIndexChanged: - { - logTable.model = itemFilter.getCurrentModel(); - } - } - } - TableView { - id: logTable - Layout.fillWidth: true - Layout.fillHeight: true - model: fullModel + onCurrentIndexChanged: + { + logTable.model = itemFilter.getCurrentModel(); + } + } + } + TableView { + id: logTable + Layout.fillWidth: true + Layout.fillHeight: true + model: fullModel - TableViewColumn { - role: "transactionIndex" - title: qsTr("#") - width: 40 - } - TableViewColumn { - role: "contract" - title: qsTr("Contract") - width: 100 - } - TableViewColumn { - role: "function" - title: qsTr("Function") - width: 120 - } - TableViewColumn { - role: "value" - title: qsTr("Value") - width: 60 - } - TableViewColumn { - role: "address" - title: qsTr("Destination") - width: 130 - } - TableViewColumn { - role: "returned" - title: qsTr("Returned") - width: 120 - } - onActivated: { - var item = logTable.model.get(row); - if (item.type === RecordLogEntry.Transaction) - clientModel.debugRecord(item.recordIndex); - else - clientModel.emptyRecord(); - } - Keys.onPressed: { - if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logTable.model.count) { - var item = logTable.model.get(currentRow); - appContext.toClipboard(item.returned); - } - } - } - } - Connections { - target: clientModel - onStateCleared: { - fullModel.clear(); - transactionModel.clear(); - callModel.clear(); - } - onNewRecord: { - fullModel.append(_r); - if (!_r.call) - transactionModel.append(_r); - else - callModel.append(_r); - } - onMiningComplete: { - fullModel.append(clientModel.lastBlock); - transactionModel.append(clientModel.lastBlock); - } - } + TableViewColumn { + role: "transactionIndex" + title: qsTr("#") + width: 40 + } + TableViewColumn { + role: "contract" + title: qsTr("Contract") + width: 100 + } + TableViewColumn { + role: "function" + title: qsTr("Function") + width: 120 + } + TableViewColumn { + role: "value" + title: qsTr("Value") + width: 60 + } + TableViewColumn { + role: "address" + title: qsTr("Destination") + width: 130 + } + TableViewColumn { + role: "returned" + title: qsTr("Returned") + width: 120 + } + onActivated: { + var item = logTable.model.get(row); + if (item.type === RecordLogEntry.Transaction) + clientModel.debugRecord(item.recordIndex); + else + clientModel.emptyRecord(); + } + Keys.onPressed: { + if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logTable.model.count) { + var item = logTable.model.get(currentRow); + appContext.toClipboard(item.returned); + } + } + } + } + Connections { + target: clientModel + onStateCleared: { + fullModel.clear(); + transactionModel.clear(); + callModel.clear(); + } + onNewRecord: { + fullModel.append(_r); + if (!_r.call) + transactionModel.append(_r); + else + callModel.append(_r); + } + onMiningComplete: { + fullModel.append(clientModel.lastBlock); + transactionModel.append(clientModel.lastBlock); + } + } }