From 36bedf6a7998b0dbd48800998e9179789d0a3e1c Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Sat, 21 Feb 2015 13:41:25 +0100 Subject: [PATCH 001/168] 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/168] 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 28ccaf0cfa672b06ee5f354234d42c5c53f96748 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 20 Mar 2015 15:46:29 +0100 Subject: [PATCH 003/168] 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 004/168] 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 005/168] 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 006/168] 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 64922cb150b27eb04f32c69420eec3b0741f7bf2 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Sat, 21 Mar 2015 12:02:59 +0100 Subject: [PATCH 007/168] max stack limit test --- test/stMemoryStressTestFiller.json | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/stMemoryStressTestFiller.json b/test/stMemoryStressTestFiller.json index d697db44e..9fff9c0a4 100644 --- a/test/stMemoryStressTestFiller.json +++ b/test/stMemoryStressTestFiller.json @@ -167,5 +167,49 @@ "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "data" : "" } + }, + + "FillStack" : { + "env" : { + "currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5", + "currentDifficulty" : "5623894562375", + "currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "0", + "code" : "0x5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe457f00000000000000000000000100000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000017f000000000000000000000000000000000000000000000000000000000000c3504357155320803a975560005155", + "nonce" : "0", + "storage" : { + } + }, + "945304eb96065b2a98b57a48a06ae28d285a71b5" : { + "balance" : "46", + "code" : "0x6000355415600957005b60203560003555", + "nonce" : "0", + "storage" : { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "code" : "0x", + "nonce" : "0", + "storage" : { + } + } + }, + "transaction" : { + "data" : "0x5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe457f00000000000000000000000100000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000017f000000000000000000000000000000000000000000000000000000000000c3504357155320803a97", + "gasLimit" : "3141592", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "264050067" + } } + } From f64bf8f17a1b4e18441048587a31023d245faf9a Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 22 Mar 2015 22:37:12 +0100 Subject: [PATCH 008/168] 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 009/168] 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 3b0179e4cdc230064e1db0ef676efa4cb0b21075 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Mon, 23 Mar 2015 10:30:48 +0100 Subject: [PATCH 010/168] stack limit tests --- test/stMemoryTestFiller.json | 204 +++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) diff --git a/test/stMemoryTestFiller.json b/test/stMemoryTestFiller.json index 676a88feb..9e034f8f3 100644 --- a/test/stMemoryTestFiller.json +++ b/test/stMemoryTestFiller.json @@ -1459,5 +1459,209 @@ "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "data" : "0xff55883355001144bbccddffeeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" } + }, + + "stackLimitPush32_1024": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "42949672960", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "(asm 1021 0x00 MSTORE JUMPDEST 0x0102030405060708090a0102030405060708090a0102030405060708090a0102 0x01 0x00 MLOAD SUB 0x00 MSTORE 0x00 MLOAD 0x06 JUMPI STOP )", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "429496729600", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "100000", + "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "stackLimitPush32_1025": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "42949672960", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "(asm 1022 0x00 MSTORE JUMPDEST 0x0102030405060708090a0102030405060708090a0102030405060708090a0102 0x01 0x00 MLOAD SUB 0x00 MSTORE 0x00 MLOAD 0x06 JUMPI STOP )", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "429496729600", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "100000", + "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "stackLimitPush31_1024": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "42949672960", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "(asm 1021 0x00 MSTORE JUMPDEST 0x0102030405060708090a0102030405060708090a0102030405060708090a01 0x01 0x00 MLOAD SUB 0x00 MSTORE 0x00 MLOAD 0x06 JUMPI STOP )", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "429496729600", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "100000", + "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "stackLimitPush31_1025": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "42949672960", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "(asm 1022 0x00 MSTORE JUMPDEST 0x0102030405060708090a0102030405060708090a0102030405060708090a01 0x01 0x00 MLOAD SUB 0x00 MSTORE 0x00 MLOAD 0x06 JUMPI STOP )", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "429496729600", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "100000", + "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "stackLimitGas_1024": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "42949672960", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "(asm 1021 0x00 MSTORE JUMPDEST GAS 0x01 0x00 MLOAD SUB 0x00 MSTORE 0x00 MLOAD 0x06 JUMPI STOP )", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "429496729600", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "100000", + "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "stackLimitGas_1025": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "42949672960", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "(asm 1022 0x00 MSTORE JUMPDEST GAS 0x01 0x00 MLOAD SUB 0x00 MSTORE 0x00 MLOAD 0x06 JUMPI STOP )", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "429496729600", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "100000", + "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } } } From 09c48343d0aa5c4e7ec9ca9ec1908ad25808d561 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Mon, 23 Mar 2015 10:55:02 +0100 Subject: [PATCH 011/168] memory stress test for memory opdcodes - dejavu issue 28 --- test/stMemoryTestFiller.json | 374 +++++++++++++++++++++++++++++++++++ 1 file changed, 374 insertions(+) diff --git a/test/stMemoryTestFiller.json b/test/stMemoryTestFiller.json index 9e034f8f3..18892774b 100644 --- a/test/stMemoryTestFiller.json +++ b/test/stMemoryTestFiller.json @@ -1663,5 +1663,379 @@ "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "data" : "" } + }, + + "mstroe8_dejavu": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "42949672960", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "0x60F1630FFFFFFF53", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "429496729600", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "13421833200", + "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "mload_dejavu": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "42949672960", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "0x630FFFFFFF51", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "429496729600", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "13421833200", + "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "mstore_dejavu": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "42949672960", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "0x60F1630FFFFFFF52", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "429496729600", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "13421833200", + "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log1_dejavu": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "42949672960", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "0x60FF60FF630FFFFFFFA1", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "429496729600", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "13421833200", + "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log2_dejavu": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "42949672960", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "0x60FF60FF630FFFFFFFA2", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "429496729600", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "13421833200", + "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log3_dejavu": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "42949672960", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "0x60FF60FF60FF630FFFFFFFA2", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "429496729600", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "13421833200", + "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "log4_dejavu": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "42949672960", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "0x60FF60FF60FF630FFFFFFFA2 ", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "429496729600", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "13421833200", + "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "extcodecopy_dejavu": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "42949672960", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "0x60FF60FF630FFFFFFF630FFFFFFF3C ", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "429496729600", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "13421833200", + "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "codecopy_dejavu": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "42949672960", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "0x60FF60FF630FFFFFFF630FFFFFFF39 ", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "429496729600", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "13421833200", + "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "calldatacopy_dejavu": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "42949672960", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "0x60FF60FF630FFFFFFF630FFFFFFF37 ", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "429496729600", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "13421833200", + "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + + "sha3_dejavu": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "42949672960", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "nonce" : "0", + "code" : "0x60FF630FFFFFFF20 ", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "429496729600", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "13421833200", + "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "value" : "10", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } } } From 2c90ac7261304325714e2f9b22b6f4147665998a Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Mon, 23 Mar 2015 11:03:40 +0100 Subject: [PATCH 012/168] bug fix --- test/stMemoryTestFiller.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/stMemoryTestFiller.json b/test/stMemoryTestFiller.json index 18892774b..c92cd9cbc 100644 --- a/test/stMemoryTestFiller.json +++ b/test/stMemoryTestFiller.json @@ -1882,7 +1882,7 @@ "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "balance" : "1000000000000000000", "nonce" : "0", - "code" : "0x60FF60FF60FF630FFFFFFFA2 ", + "code" : "0x60FF60FF60FF630FFFFFFFA2", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -1916,7 +1916,7 @@ "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "balance" : "1000000000000000000", "nonce" : "0", - "code" : "0x60FF60FF630FFFFFFF630FFFFFFF3C ", + "code" : "0x60FF60FF630FFFFFFF630FFFFFFF3C", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -1950,7 +1950,7 @@ "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "balance" : "1000000000000000000", "nonce" : "0", - "code" : "0x60FF60FF630FFFFFFF630FFFFFFF39 ", + "code" : "0x60FF60FF630FFFFFFF630FFFFFFF39", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -1984,7 +1984,7 @@ "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "balance" : "1000000000000000000", "nonce" : "0", - "code" : "0x60FF60FF630FFFFFFF630FFFFFFF37 ", + "code" : "0x60FF60FF630FFFFFFF630FFFFFFF37", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { @@ -2018,7 +2018,7 @@ "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "balance" : "1000000000000000000", "nonce" : "0", - "code" : "0x60FF630FFFFFFF20 ", + "code" : "0x60FF630FFFFFFF20", "storage": {} }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { From 8bce0525375ced5e5145dfe15383c5faa7175e1b Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 23 Mar 2015 11:49:15 +0100 Subject: [PATCH 013/168] 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 014/168] 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 015/168] 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 016/168] 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 e39ad0d307a3471582557999d731da4742d97773 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Mon, 23 Mar 2015 13:07:21 +0100 Subject: [PATCH 017/168] gas overflow --- test/vmIOandFlowOperationsTestFiller.json | 28 +++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/vmIOandFlowOperationsTestFiller.json b/test/vmIOandFlowOperationsTestFiller.json index 84a5188a6..36e70f684 100644 --- a/test/vmIOandFlowOperationsTestFiller.json +++ b/test/vmIOandFlowOperationsTestFiller.json @@ -3313,6 +3313,34 @@ } }, + "gasOverFlow": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "(asm PUSH1 0x03 JUMPDEST PUSH1 0x01 SAWP1 SUB DUP1 PUSH1 0x02 JUMPI PUSH9 0x010000000000000016 JUMP JUMPDEST PUSH4 0xbadf000d PUSH1 0x11 SSTORE STOP )", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "100000" + } + }, + "byte1": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", From 9c307fdbcf580d1e2bc41d9b6e8f7b22549d88df Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Mon, 23 Mar 2015 13:08:14 +0100 Subject: [PATCH 018/168] add possibility to have in-chain uncles as tests --- test/blockchain.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/blockchain.cpp b/test/blockchain.cpp index 0f5eeaee2..50ca22c54 100644 --- a/test/blockchain.cpp +++ b/test/blockchain.cpp @@ -72,6 +72,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) // create new "genesis" block RLPStream rlpGenesisBlock = createFullBlockFromHeader(biGenesisBlock); biGenesisBlock.verifyInternals(&rlpGenesisBlock.out()); + o["genesisRLP"] = "0x" + toHex(rlpGenesisBlock.out()); // construct blockchain BlockChain bc(rlpGenesisBlock.out(), string(), true); @@ -114,6 +115,15 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) vBiUncles.push_back(vBiUncles[vBiUncles.size()-1]); continue; } + + if (uncleHeaderObj.count("sameAsBlock")) + { + writeBlockHeaderToJson(uncleHeaderObj_pre, vBiBlocks[(size_t)toInt(uncleHeaderObj["sameAsBlock"])]); + aUncleList.push_back(uncleHeaderObj_pre); + vBiUncles.push_back(vBiBlocks[(size_t)toInt(uncleHeaderObj["sameAsBlock"])]); + continue; + } + BlockInfo uncleBlockFromFields = constructBlock(uncleHeaderObj); // make uncle header valid From 6ef089b1581e79167c1a13f32b41984715838c65 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Mon, 23 Mar 2015 13:13:35 +0100 Subject: [PATCH 019/168] check overflow for v value in transacition test --- test/ttTransactionTestFiller.json | 60 +++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/test/ttTransactionTestFiller.json b/test/ttTransactionTestFiller.json index 105b64abc..0058feac1 100644 --- a/test/ttTransactionTestFiller.json +++ b/test/ttTransactionTestFiller.json @@ -14,6 +14,66 @@ } }, + "V_overflow32bit" : { + "transaction" : + { + "data" : "0x5544", + "gasLimit" : "2000", + "gasPrice" : "1", + "nonce" : "3", + "to" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "10", + "v" : "4294967323", + "r" : "0x98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a", + "s" : "0x8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3" + } + }, + + "V_overflow32bitSigned" : { + "transaction" : + { + "data" : "0x5544", + "gasLimit" : "2000", + "gasPrice" : "1", + "nonce" : "3", + "to" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "10", + "v" : "2147483647", + "r" : "0x98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a", + "s" : "0x8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3" + } + }, + + "V_overflow64bit" : { + "transaction" : + { + "data" : "0x5544", + "gasLimit" : "2000", + "gasPrice" : "1", + "nonce" : "3", + "to" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "10", + "v" : "18446744073709551643", + "r" : "0x98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a", + "s" : "0x8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3" + } + }, + + "V_overflow64bitSigned" : { + "transaction" : + { + "data" : "0x5544", + "gasLimit" : "2000", + "gasPrice" : "1", + "nonce" : "3", + "to" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "10", + "v" : "18446744073709551388", + "r" : "0x98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a", + "s" : "0x8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3" + } + }, + "WrongVRSTestVEqual26" : { "transaction" : { From bbd6330f040b8f8d80e45178cb4bbcef8338e6f6 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Mon, 23 Mar 2015 13:14:49 +0100 Subject: [PATCH 020/168] test for in-chain-uncles and uncles with gen 0 --- test/bcUncleTestFiller.json | 189 +++++++++++++++++++++++++++++++++++ test/stMemoryTestFiller.json | 22 ++-- 2 files changed, 200 insertions(+), 11 deletions(-) diff --git a/test/bcUncleTestFiller.json b/test/bcUncleTestFiller.json index 2a2116ea7..5ab4d7957 100644 --- a/test/bcUncleTestFiller.json +++ b/test/bcUncleTestFiller.json @@ -77,6 +77,99 @@ ] }, + "uncleHeaderWithGeneration0" : { + "genesisBlockHeader" : { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "131072", + "extraData" : "0x42", + "gasLimit" : "3141592", + "gasUsed" : "0", + "number" : "0", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a", + "timestamp" : "0x54c98c81", + "mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "nonce" : "0x0102030405060708", + "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + "pre" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10000000000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "blocks" : [ + { + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "1", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "bcde5374fce5edbc8e2a8697c15331677e6ebf0b", + "difficulty" : "131072", + "extraData" : "0x", + "gasLimit" : "3141592", + "gasUsed" : "0", + "hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04", + "mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770", + "nonce" : "18a524c1790fa83b", + "number" : "0", + "parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae", + "receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd", + "timestamp" : "0x54c98c82", + "transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + } + ] + } + ] + }, + "oneUncle" : { "genesisBlockHeader" : { "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -170,6 +263,102 @@ ] }, + "InChainUncle" : { + "genesisBlockHeader" : { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1", + "difficulty" : "131072", + "extraData" : "0x42", + "gasLimit" : "3141592", + "gasUsed" : "0", + "number" : "0", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a", + "timestamp" : "0x54c98c81", + "mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "nonce" : "0x0102030405060708", + "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + "pre" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10000000000000", + "nonce" : "0", + "code" : "", + "storage": {} + } + }, + "blocks" : [ + { + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "1", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + ] + }, + { + "transactions" : [ + { + "data" : "", + "gasLimit" : "314159", + "gasPrice" : "1", + "nonce" : "2", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "10" + } + ], + "uncleHeaders" : [ + { + "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "coinbase" : "bcde5374fce5edbc8e2a8697c15331677e6ebf0b", + "difficulty" : "131072", + "extraData" : "0x", + "gasLimit" : "3141592", + "gasUsed" : "0", + "hash" : "9de9879b6a81d1b6c4993c63c90a3c9d1e775f14572694778e828bc64972ae04", + "mixHash" : "b557f905d29ed0fca99d65d0adcce698dee97cf72a13c7cd8d7a7826b8eee770", + "nonce" : "18a524c1790fa83b", + "number" : "2", + "parentHash" : "6134fc6b5d99ee03c4aab1592640f6f9dcbc850668d75d631aee34989b938fae", + "receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "stateRoot" : "ff640b30d613c35dad43e3693329e1b1ee6350f989cf46a288025a1cbfdab9cd", + "timestamp" : "0x54c98c82", + "transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + { + "sameAsBlock" : "1" + } + ] + } + ] + }, + "twoEqualUncle" : { "genesisBlockHeader" : { "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", diff --git a/test/stMemoryTestFiller.json b/test/stMemoryTestFiller.json index c92cd9cbc..c1754d52f 100644 --- a/test/stMemoryTestFiller.json +++ b/test/stMemoryTestFiller.json @@ -1691,7 +1691,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "13421833200", + "gasLimit" : "42949672960", "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -1725,7 +1725,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "13421833200", + "gasLimit" : "42949672960", "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -1759,7 +1759,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "13421833200", + "gasLimit" : "42949672960", "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -1793,7 +1793,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "13421833200", + "gasLimit" : "42949672960", "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -1827,7 +1827,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "13421833200", + "gasLimit" : "42949672960", "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -1861,7 +1861,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "13421833200", + "gasLimit" : "42949672960", "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -1895,7 +1895,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "13421833200", + "gasLimit" : "42949672960", "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -1929,7 +1929,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "13421833200", + "gasLimit" : "42949672960", "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -1963,7 +1963,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "13421833200", + "gasLimit" : "42949672960", "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -1997,7 +1997,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "13421833200", + "gasLimit" : "42949672960", "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -2031,7 +2031,7 @@ "transaction" : { "nonce" : "0", "gasPrice" : "1", - "gasLimit" : "13421833200", + "gasLimit" : "42949672960", "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", "value" : "10", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", From 74335368ff3da5a1bdfd0be51e0b8399e92548eb Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 23 Mar 2015 14:15:04 +0100 Subject: [PATCH 021/168] 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 826ebd2a34c51999855b5f2dc6457ff343e24382 Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Mon, 23 Mar 2015 14:41:26 +0100 Subject: [PATCH 022/168] refine bcJS_API_TestFiller.json --- test/bcJS_API_TestFiller.json | 45 +++++++---------------------------- 1 file changed, 9 insertions(+), 36 deletions(-) diff --git a/test/bcJS_API_TestFiller.json b/test/bcJS_API_TestFiller.json index 8c9dbe3ca..468b3b2e8 100644 --- a/test/bcJS_API_TestFiller.json +++ b/test/bcJS_API_TestFiller.json @@ -40,20 +40,11 @@ "blocks" : [ { "transactions" : [ - { - "data" : "", - "gasLimit" : "314159", - "gasPrice" : "1", - "nonce" : "0", - "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", - "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", - "value" : "10" - }, { "data" : "0x60406103ca600439600451602451336000819055506000600481905550816001819055508060028190555042600581905550336003819055505050610381806100496000396000f30060003560e060020a9004806343d726d61461004257806391b7f5ed14610050578063d686f9ee14610061578063f5bade661461006f578063fcfff16f1461008057005b61004a6101de565b60006000f35b61005b6004356100bf565b60006000f35b610069610304565b60006000f35b61007a60043561008e565b60006000f35b6100886100f0565b60006000f35b600054600160a060020a031633600160a060020a031614156100af576100b4565b6100bc565b806001819055505b50565b600054600160a060020a031633600160a060020a031614156100e0576100e5565b6100ed565b806002819055505b50565b600054600160a060020a031633600160a060020a031614806101255750600354600160a060020a031633600160a060020a0316145b61012e57610161565b60016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a16101dc565b60045460011480610173575060015434105b6101b85760016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a142600581905550336003819055506101db565b33600160a060020a03166000346000600060006000848787f16101d757005b5050505b5b565b60006004546000146101ef576101f4565b610301565b600054600160a060020a031633600160a060020a031614801561022c5750600054600160a060020a0316600354600160a060020a0316145b61023557610242565b6000600481905550610301565b600354600160a060020a031633600160a060020a03161461026257610300565b600554420360025402905060015481116102c757600354600160a060020a0316600082600154036000600060006000848787f161029b57005b505050600054600160a060020a03166000826000600060006000848787f16102bf57005b5050506102ee565b600054600160a060020a031660006001546000600060006000848787f16102ea57005b5050505b60006004819055506000546003819055505b5b50565b6000600054600160a060020a031633600160a060020a031614156103275761032c565b61037e565b600554420360025402905060015481116103455761037d565b600054600160a060020a031660006001546000600060006000848787f161036857005b50505060006004819055506000546003819055505b5b505600000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000023", "gasLimit" : "600000", "gasPrice" : "1", - "nonce" : "1", + "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "", "value" : "100000" @@ -68,7 +59,7 @@ "data" : "", "gasLimit" : "314159", "gasPrice" : "1", - "nonce" : "2", + "nonce" : "1", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", "value" : "10" @@ -79,20 +70,11 @@ }, { "transactions" : [ - { - "data" : "", - "gasLimit" : "314159", - "gasPrice" : "1", - "nonce" : "3", - "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", - "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", - "value" : "10" - }, { "data" : "0xfcfff16f", "gasLimit" : "600000", "gasPrice" : "1", - "nonce" : "4", + "nonce" : "2", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "6295ee1b4f6dd65047762f924ecd367c17eabf8f", "value" : "0x42" @@ -107,7 +89,7 @@ "data" : "", "gasLimit" : "314159", "gasPrice" : "1", - "nonce" : "5", + "nonce" : "3", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", "value" : "10" @@ -158,19 +140,10 @@ "data" : "", "gasLimit" : "314159", "gasPrice" : "1", - "nonce" : "6", + "nonce" : "4", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", "value" : "10" - }, - { - "data" : "0xfcfff16f", - "gasLimit" : "600000", - "gasPrice" : "1", - "nonce" : "0", - "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", - "to" : "6295ee1b4f6dd65047762f924ecd367c17eabf8f", - "value" : "0x42" } ], "uncleHeaders" : [ @@ -182,7 +155,7 @@ "data" : "", "gasLimit" : "314159", "gasPrice" : "1", - "nonce" : "7", + "nonce" : "5", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", "value" : "10" @@ -215,7 +188,7 @@ "data" : "", "gasLimit" : "314159", "gasPrice" : "1", - "nonce" : "8", + "nonce" : "6", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", "value" : "10" @@ -230,7 +203,7 @@ "data" : "", "gasLimit" : "314159", "gasPrice" : "1", - "nonce" : "9", + "nonce" : "7", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", "value" : "10" @@ -245,7 +218,7 @@ "data" : "", "gasLimit" : "314159", "gasPrice" : "1", - "nonce" : "10", + "nonce" : "8", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", "value" : "10" From 887cdf4bfb584e368ff573ff39e2adc2d531d93b Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 23 Mar 2015 15:26:48 +0100 Subject: [PATCH 023/168] 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 024/168] 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 025/168] 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 026/168] 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 027/168] 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 028/168] 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 e892d9d426a7739f69be2f2e7901204debf6f3ce Mon Sep 17 00:00:00 2001 From: CJentzsch Date: Mon, 23 Mar 2015 20:44:51 +0100 Subject: [PATCH 029/168] style --- test/stMemoryStressTestFiller.json | 1 - test/vmIOandFlowOperationsTestFiller.json | 24 +++++++++++------------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/test/stMemoryStressTestFiller.json b/test/stMemoryStressTestFiller.json index 9fff9c0a4..2589191c5 100644 --- a/test/stMemoryStressTestFiller.json +++ b/test/stMemoryStressTestFiller.json @@ -211,5 +211,4 @@ "value" : "264050067" } } - } diff --git a/test/vmIOandFlowOperationsTestFiller.json b/test/vmIOandFlowOperationsTestFiller.json index 36e70f684..01019892f 100644 --- a/test/vmIOandFlowOperationsTestFiller.json +++ b/test/vmIOandFlowOperationsTestFiller.json @@ -3315,20 +3315,20 @@ "gasOverFlow": { "env" : { - "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", - "currentNumber" : "0", - "currentGasLimit" : "1000000", - "currentDifficulty" : "256", - "currentTimestamp" : "1", - "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" }, "pre" : { - "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { - "balance" : "100000000000000000000000", - "nonce" : "0", - "code" : "(asm PUSH1 0x03 JUMPDEST PUSH1 0x01 SAWP1 SUB DUP1 PUSH1 0x02 JUMPI PUSH9 0x010000000000000016 JUMP JUMPDEST PUSH4 0xbadf000d PUSH1 0x11 SSTORE STOP )", - "storage": {} - } + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "(asm PUSH1 0x03 JUMPDEST PUSH1 0x01 SAWP1 SUB DUP1 PUSH1 0x02 JUMPI PUSH9 0x010000000000000016 JUMP JUMPDEST PUSH4 0xbadf000d PUSH1 0x11 SSTORE STOP )", + "storage": {} + } }, "exec" : { "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", From 029b94a6ca5519458fc09098d70559f551327e00 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 23 Mar 2015 21:08:32 +0100 Subject: [PATCH 030/168] 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 031/168] 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 032/168] 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 033/168] 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 034/168] 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 035/168] 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 036/168] 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 037/168] 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 038/168] 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 039/168] 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 040/168] 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 041/168] 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 042/168] 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 043/168] 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 044/168] 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 045/168] 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 046/168] 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 047/168] 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 048/168] 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 049/168] 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 050/168] 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 051/168] 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 052/168] 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 7567cf72ec9bb11319eef9c218dd28459eb7c45e Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 09:31:43 +0100 Subject: [PATCH 053/168] 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 054/168] 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 055/168] 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 056/168] 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 057/168] 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 058/168] 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 059/168] 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 060/168] 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 061/168] - 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 062/168] 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 063/168] 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 064/168] 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 065/168] 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 066/168] 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 067/168] 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 068/168] 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 069/168] 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 070/168] - 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 071/168] 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 072/168] 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 073/168] 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 074/168] 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 075/168] 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 42e6a291444e2b3e0b1075302ef3f65d0fcba6d3 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 25 Mar 2015 14:45:50 +0100 Subject: [PATCH 076/168] 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 077/168] - 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 078/168] 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 079/168] 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 080/168] 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 081/168] 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 082/168] 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 083/168] 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 084/168] 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 085/168] 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 086/168] 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 087/168] 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 088/168] 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 089/168] 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 090/168] 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 091/168] 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 092/168] 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 093/168] 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 094/168] 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 095/168] 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 096/168] 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 097/168] 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 098/168] 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 099/168] 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 100/168] 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 101/168] 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 102/168] 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 103/168] 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 104/168] 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 105/168] - 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 106/168] - 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 107/168] 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 108/168] - 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 109/168] 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 110/168] 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 111/168] - 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 112/168] 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 113/168] 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 114/168] 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 115/168] 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 116/168] 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 117/168] 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 118/168] 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 119/168] 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 120/168] 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 121/168] 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 122/168] 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 123/168] 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 124/168] 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 125/168] 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 126/168] 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 127/168] 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 128/168] 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 129/168] 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 130/168] 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 131/168] 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 161f4588bb6d015849bd44f6f0efb4c14708b74c Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 27 Mar 2015 13:11:24 +0100 Subject: [PATCH 132/168] 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 133/168] 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 134/168] 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 135/168] 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 87673e411f67c842b6645e588e9d2b63bd1ee678 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 27 Mar 2015 15:47:14 +0100 Subject: [PATCH 136/168] 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 137/168] 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 138/168] 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 139/168] 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 93593a8751b0d05f45828a7d7f8a0013e76de78d Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 27 Mar 2015 22:47:19 +0100 Subject: [PATCH 140/168] 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 141/168] 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 142/168] 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 143/168] 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 144/168] 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 145/168] 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 146/168] 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 147/168] 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 148/168] 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 149/168] 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 150/168] 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 151/168] 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 152/168] 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 153/168] 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 154/168] 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 155/168] 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 156/168] 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 157/168] 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 158/168] 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 159/168] 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 160/168] 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 161/168] 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 162/168] 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 163/168] 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 164/168] 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 165/168] 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 166/168] 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 167/168] 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 168/168] 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