Browse Source

Merge remote-tracking branch 'upstream/develop' into addTests

Conflicts:
	test/CMakeLists.txt
	test/ttTransactionTestFiller.json
cl-refactor
CJentzsch 10 years ago
parent
commit
4052aa93e6
  1. 7
      BuildInfo.h.in
  2. 55
      BuildInfo.sh
  3. 27
      CMakeLists.txt
  4. 52
      CodingStandards.txt
  5. 9
      README.md
  6. 21
      alethzero/CMakeLists.txt
  7. 59
      alethzero/Context.cpp
  8. 68
      alethzero/Context.h
  9. 380
      alethzero/Debugger.cpp
  10. 103
      alethzero/Debugger.h
  11. 300
      alethzero/Debugger.ui
  12. 3
      alethzero/GraphParameters.h
  13. 16
      alethzero/Grapher.h
  14. 540
      alethzero/Main.ui
  15. 783
      alethzero/MainWin.cpp
  16. 187
      alethzero/MainWin.h
  17. 5
      alethzero/MiningView.cpp
  18. 14
      alethzero/NatspecHandler.cpp
  19. 9
      alethzero/NatspecHandler.h
  20. 131
      alethzero/OurWebThreeStubServer.cpp
  21. 32
      alethzero/OurWebThreeStubServer.h
  22. 334
      alethzero/Transact.cpp
  23. 82
      alethzero/Transact.h
  24. 244
      alethzero/Transact.ui
  25. 3
      cmake/EthCompilerSettings.cmake
  26. 21
      cmake/EthDependencies.cmake
  27. 52
      cmake/EthExecutableHelper.cmake
  28. 24
      cmake/EthUtils.cmake
  29. 3
      cmake/FindJsoncpp.cmake
  30. 53
      cmake/FindMHD.cmake
  31. 48
      cmake/scripts/buildinfo.cmake
  32. 14
      cmake/scripts/configure.cmake
  33. 45
      cmake/scripts/jsonrpcstub.cmake
  34. 7
      eth/CMakeLists.txt
  35. 98
      eth/main.cpp
  36. 7
      evmjit/CMakeLists.txt
  37. 21
      evmjit/LICENSE.md
  38. 43
      evmjit/README.md
  39. 5
      evmjit/libevmjit-cpp/CMakeLists.txt
  40. 21
      evmjit/libevmjit-cpp/Env.cpp
  41. 34
      evmjit/libevmjit-cpp/JitVM.cpp
  42. 4
      evmjit/libevmjit-cpp/JitVM.h
  43. 328
      evmjit/libevmjit/Arith256.cpp
  44. 13
      evmjit/libevmjit/Arith256.h
  45. 19
      evmjit/libevmjit/BasicBlock.cpp
  46. 20
      evmjit/libevmjit/BasicBlock.h
  47. 10
      evmjit/libevmjit/BuildInfo.h.in
  48. 53
      evmjit/libevmjit/CMakeLists.txt
  49. 62
      evmjit/libevmjit/Cache.cpp
  50. 9
      evmjit/libevmjit/Cache.h
  51. 17
      evmjit/libevmjit/Common.h
  52. 94
      evmjit/libevmjit/Compiler.cpp
  53. 9
      evmjit/libevmjit/Compiler.h
  54. 4
      evmjit/libevmjit/CompilerHelper.cpp
  55. 5
      evmjit/libevmjit/CompilerHelper.h
  56. 3
      evmjit/libevmjit/Endianness.cpp
  57. 3
      evmjit/libevmjit/Endianness.h
  58. 97
      evmjit/libevmjit/ExecStats.cpp
  59. 43
      evmjit/libevmjit/ExecStats.h
  60. 133
      evmjit/libevmjit/ExecutionEngine.cpp
  61. 32
      evmjit/libevmjit/ExecutionEngine.h
  62. 31
      evmjit/libevmjit/Ext.cpp
  63. 6
      evmjit/libevmjit/Ext.h
  64. 107
      evmjit/libevmjit/GasMeter.cpp
  65. 3
      evmjit/libevmjit/GasMeter.h
  66. 8
      evmjit/libevmjit/Instruction.cpp
  67. 4
      evmjit/libevmjit/Instruction.h
  68. 93
      evmjit/libevmjit/Memory.cpp
  69. 15
      evmjit/libevmjit/Memory.h
  70. 8
      evmjit/libevmjit/Runtime.cpp
  71. 8
      evmjit/libevmjit/Runtime.h
  72. 4
      evmjit/libevmjit/RuntimeData.h
  73. 54
      evmjit/libevmjit/RuntimeManager.cpp
  74. 17
      evmjit/libevmjit/RuntimeManager.h
  75. 111
      evmjit/libevmjit/Stack.cpp
  76. 9
      evmjit/libevmjit/Stack.h
  77. 11
      evmjit/libevmjit/Type.cpp
  78. 8
      evmjit/libevmjit/Type.h
  79. 1
      evmjit/libevmjit/Utils.cpp
  80. 3
      evmjit/libevmjit/Utils.h
  81. 11
      evmjit/libevmjit/interface.cpp
  82. 5
      evmjit/libevmjit/preprocessor/llvm_includes_end.h
  83. 8
      evmjit/libevmjit/preprocessor/llvm_includes_start.h
  84. 2
      exp/CMakeLists.txt
  85. 6
      exp/main.cpp
  86. 10
      extdep/CMakeLists.txt
  87. 9
      libdevcore/CMakeLists.txt
  88. 2
      libdevcore/Common.cpp
  89. 6
      libdevcore/CommonData.cpp
  90. 9
      libdevcore/CommonData.h
  91. 22
      libdevcore/CommonIO.h
  92. 12
      libdevcore/Exceptions.h
  93. 2
      libdevcore/RLP.cpp
  94. 12
      libdevcore/RLP.h
  95. 2
      libdevcore/Worker.h
  96. 16
      libdevcore/vector_ref.h
  97. 2
      libdevcrypto/CMakeLists.txt
  98. 2
      libdevcrypto/Common.cpp
  99. 3
      libdevcrypto/Common.h
  100. 2
      libdevcrypto/ECDHE.h

7
BuildInfo.h.in

@ -0,0 +1,7 @@
#pragma once
#define ETH_COMMIT_HASH @ETH_COMMIT_HASH@
#define ETH_CLEAN_REPO @ETH_CLEAN_REPO@
#define ETH_BUILD_TYPE @ETH_BUILD_TYPE@
#define ETH_BUILD_PLATFORM @ETH_BUILD_PLATFORM@

55
BuildInfo.sh

@ -1,55 +0,0 @@
CURRENT_SOURCE_DIR=$1
CURRENT_BINARY_DIR=$2
BUILD_TYPE=$3
BUILD_PLATFORM=$4
echo "Current source dir: $CURRENT_SOURCE_DIR"
echo "Current binary dir: $CURRENT_BINARY_DIR"
echo "Build type: $BUILD_TYPE"
echo "Build platform: $BUILD_PLATFORM"
if [[ -e "$CURRENT_SOURCE_DIR/BuildInfo.h" ]]
then
echo "Using existing BuildInfo.h"
cp $CURRENT_SOURCE_DIR/BuildInfo.h $CURRENT_BINARY_DIR/BuildInfo.h.tmp
else
if [[ -e "$CURRENT_SOURCE_DIR/.git" ]]
then
ETH_COMMIT_HASH=$(git --git-dir=$CURRENT_SOURCE_DIR/.git --work-tree=$CURRENT_SOURCE_DIR rev-parse HEAD)
ETH_LOCAL_CHANGES=$(git --git-dir=$CURRENT_SOURCE_DIR/.git --work-tree=$CURRENT_SOURCE_DIR diff --shortstat)
if [[ -z "$ETH_LOCAL_CHANGES" ]]
then
ETH_CLEAN_REPO=1
else
ETH_CLEAN_REPO=0
fi
echo "Commit hash: ${ETH_COMMIT_HASH} (Clean: ${ETH_CLEAN_REPO} - ${ETH_LOCAL_CHANGES})"
else
echo "Unknown repo."
ETH_COMMIT_HASH=0
ETH_CLEAN_REPO=1
fi
echo "// This file was automatically generated by cmake" > $CURRENT_BINARY_DIR/BuildInfo.h.tmp
echo "" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp
echo "#pragma once" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp
echo "" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp
echo "#define ETH_COMMIT_HASH $ETH_COMMIT_HASH" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp
echo "#define ETH_CLEAN_REPO $ETH_CLEAN_REPO" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp
echo "#define ETH_BUILD_TYPE $BUILD_TYPE" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp
echo "#define ETH_BUILD_PLATFORM $BUILD_PLATFORM" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp
fi
if [[ -e "$CURRENT_BINARY_DIR/BuildInfo.h" ]]
then
DIFF=$(diff $CURRENT_BINARY_DIR/BuildInfo.h $CURRENT_BINARY_DIR/BuildInfo.h.tmp)
if [[ -z "$DIFF" ]]
then
rm $CURRENT_BINARY_DIR/BuildInfo.h.tmp
else
mv $CURRENT_BINARY_DIR/BuildInfo.h.tmp $CURRENT_BINARY_DIR/BuildInfo.h
fi
else
mv $CURRENT_BINARY_DIR/BuildInfo.h.tmp $CURRENT_BINARY_DIR/BuildInfo.h
fi

27
CMakeLists.txt

@ -43,6 +43,10 @@ function(configureProject)
if (EVMJIT) if (EVMJIT)
add_definitions(-DETH_EVMJIT) add_definitions(-DETH_EVMJIT)
endif() endif()
if (HEADLESS)
add_definitions(-DETH_HEADLESS)
endif()
endfunction() endfunction()
@ -65,13 +69,13 @@ function(createBuildInfo)
endif () endif ()
if (EVMJIT) if (EVMJIT)
set(ETH_BUILD_PLATFORM "${TARGET_PLATFORM}/JIT") set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/JIT")
else () else ()
set(ETH_BUILD_PLATFORM "${TARGET_PLATFORM}/int") set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/int")
endif () endif ()
if (PARANOIA) if (PARANOIA)
set(ETH_BUILD_PLATFORM "${TARGET_PLATFORM}/PARA") set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/PARA")
endif () endif ()
#cmake build type may be not specified when using msvc #cmake build type may be not specified when using msvc
@ -82,7 +86,12 @@ function(createBuildInfo)
endif() endif()
# Generate header file containing useful build information # Generate header file containing useful build information
add_custom_target(BuildInfo.h ALL COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/BuildInfo.sh ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${_cmake_build_type} ${ETH_BUILD_PLATFORM}) add_custom_target(BuildInfo.h ALL
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMAND ${CMAKE_COMMAND} -DETH_SOURCE_DIR="${CMAKE_SOURCE_DIR}" -DETH_DST_DIR="${CMAKE_BINARY_DIR}"
-DETH_BUILD_TYPE="${_cmake_build_type}" -DETH_BUILD_PLATFORM="${ETH_BUILD_PLATFORM}"
-P "${ETH_SCRIPTS_DIR}/buildinfo.cmake"
)
include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
@ -100,6 +109,7 @@ cmake_policy(SET CMP0015 NEW)
createDefaultCacheConfig() createDefaultCacheConfig()
configureProject() configureProject()
message(STATUS "CMAKE_VERSION: ${CMAKE_VERSION}")
message("-- VMTRACE: ${VMTRACE}; PARANOIA: ${PARANOIA}; HEADLESS: ${HEADLESS}; JSONRPC: ${JSONRPC}; EVMJIT: ${EVMJIT}") message("-- VMTRACE: ${VMTRACE}; PARANOIA: ${PARANOIA}; HEADLESS: ${HEADLESS}; JSONRPC: ${JSONRPC}; EVMJIT: ${EVMJIT}")
@ -131,15 +141,15 @@ endif()
add_subdirectory(libdevcore) add_subdirectory(libdevcore)
add_subdirectory(libevmcore) add_subdirectory(libevmcore)
add_subdirectory(liblll) add_subdirectory(liblll)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
add_subdirectory(libserpent) add_subdirectory(libserpent)
add_subdirectory(sc)
endif () endif ()
add_subdirectory(libsolidity) add_subdirectory(libsolidity)
add_subdirectory(lllc) add_subdirectory(lllc)
add_subdirectory(solc) add_subdirectory(solc)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
add_subdirectory(sc)
endif()
if (JSONRPC) if (JSONRPC)
add_subdirectory(libweb3jsonrpc) add_subdirectory(libweb3jsonrpc)
@ -157,6 +167,7 @@ add_subdirectory(libethereum)
add_subdirectory(libwebthree) add_subdirectory(libwebthree)
add_subdirectory(test) add_subdirectory(test)
add_subdirectory(eth) add_subdirectory(eth)
if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug") if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug")
add_subdirectory(exp) add_subdirectory(exp)
endif () endif ()
@ -168,8 +179,8 @@ endif ()
if (NOT HEADLESS) if (NOT HEADLESS)
add_subdirectory(libnatspec)
add_subdirectory(libjsqrc) add_subdirectory(libjsqrc)
add_subdirectory(libqwebthree)
add_subdirectory(alethzero) add_subdirectory(alethzero)
add_subdirectory(third) add_subdirectory(third)
add_subdirectory(mix) add_subdirectory(mix)

52
CodingStandards.txt

@ -1,8 +1,11 @@
0. Formatting 0. Formatting
GOLDEN RULE: Never *ever* use spaces for formatting.
a. Use tabs for indentation! a. Use tabs for indentation!
- tab stops are every 4 characters. - tab stops are every 4 characters.
- One indentation level -> exactly one byte (i.e. a tab character) in the source file. - One indentation level -> exactly one byte (i.e. a tab character) in the source file.
- Never use spaces to line up sequential lines: If you have run-on lines, indent as you would for a block.
b. Line widths: b. Line widths:
- Don't worry about having lines of code > 80-char wide. - Don't worry about having lines of code > 80-char wide.
- Lines of comments should be formatted according to ease of viewing, but simplicity is to be prefered over beauty. - Lines of comments should be formatted according to ease of viewing, but simplicity is to be prefered over beauty.
@ -17,10 +20,22 @@ j. Braces, when used, always have their own lines and are at same indentation le
(WRONG) (WRONG)
if( a==b[ i ] ) { printf ("Hello\n"); } if( a==b[ i ] ) { printf ("Hello\n"); }
foo->bar(someLongVariableName,
anotherLongVariableName,
anotherLongVariableName,
anotherLongVariableName,
anotherLongVariableName);
(RIGHT) (RIGHT)
if (a == b[i]) if (a == b[i])
printf("Hello\n"); // NOTE spaces used instead of tab here for clarify - first byte should be '\t'. printf("Hello\n"); // NOTE spaces used instead of tab here for clarity - first byte should be '\t'.
foo->bar(
someLongVariableName,
anotherLongVariableName,
anotherLongVariableName,
anotherLongVariableName,
anotherLongVariableName
);
@ -57,6 +72,8 @@ e. Split complex macro on multiple lines with '\'.
3. Capitalization; 3. Capitalization;
GOLDEN RULE: Preprocessor: ALL_CAPS; C++: camelCase.
a. Use camelCase for splitting words in names, except where obviously extending STL/boost functionality in which case follow those naming conventions. a. Use camelCase for splitting words in names, except where obviously extending STL/boost functionality in which case follow those naming conventions.
b. The following entities' first alpha is upper case: b. The following entities' first alpha is upper case:
- Type names. - Type names.
@ -65,6 +82,7 @@ b. The following entities' first alpha is upper case:
- static const variables that form an external API. - static const variables that form an external API.
c. All preprocessor symbols (macros, macro argments) in full uppercase with underscore word separation. c. All preprocessor symbols (macros, macro argments) in full uppercase with underscore word separation.
All other entities' first alpha is lower case. All other entities' first alpha is lower case.
@ -93,15 +111,21 @@ b. Only one per line.
c. Associate */& with type, not variable (at ends with parser, but more readable, and safe if in conjunction with (b)). c. Associate */& with type, not variable (at ends with parser, but more readable, and safe if in conjunction with (b)).
d. Favour declarations close to use; don't habitually declare at top of scope ala C. d. Favour declarations close to use; don't habitually declare at top of scope ala C.
e. Always pass non-trivial parameters with a const& suffix. e. Always pass non-trivial parameters with a const& suffix.
f. If a func tion returns multiple values, use std::tuple (std::pair acceptable). Prefer not using */& arguments, except where efficiency requires. f. If a function returns multiple values, use std::tuple (std::pair acceptable). Prefer not using */& arguments, except where efficiency requires.
g. Never use a macro where adequate non-preprocessor C++ can be written. g. Never use a macro where adequate non-preprocessor C++ can be written.
h. Prefer "using NewType = OldType" to "typedef OldType NewType". h. Prefer "using NewType = OldType" to "typedef OldType NewType".
i. Make use of auto whenever type is clear or unimportant:
- Always avoid doubly-stating the type.
- Use to avoid vast and unimportant type declarations.
(WRONG) (WRONG)
const double d = 0; const double d = 0;
int i, j; int i, j;
char *s; char *s;
float meanAndSigma(std::vector<float> _v, float* _sigma); float meanAndSigma(std::vector<float> _v, float* _sigma);
Derived* x(dynamic_cast<Derived*>(base));
for (map<ComplexTypeOne, ComplexTypeTwo>::iterator i = l.begin(); i != l.end(); ++l) {}
(CORRECT) (CORRECT)
double const d = 0; double const d = 0;
@ -109,12 +133,14 @@ int i;
int j; int j;
char* s; char* s;
std::tuple<float, float> meanAndSigma(std::vector<float> const& _v); std::tuple<float, float> meanAndSigma(std::vector<float> const& _v);
auto x = dynamic_cast<Derived*>(base);
for (auto i = x.begin(); i != x.end(); ++i) {}
7. Structs & classes 7. Structs & classes
a. Structs to be used when all members public and no virtual functions. a. Structs to be used when all members public and no virtual functions.
- In this case, members should be named naturally and not prefixed with 'm_'
b. Classes to be used in all other circumstances. b. Classes to be used in all other circumstances.
@ -124,7 +150,7 @@ b. Classes to be used in all other circumstances.
a. One member per line only. a. One member per line only.
b. Private, non-static, non-const fields prefixed with m_. b. Private, non-static, non-const fields prefixed with m_.
c. Avoid public fields, except in structs. c. Avoid public fields, except in structs.
d. Use override, final and const judiciously. d. Use override, final and const as much as possible.
e. No implementations with the class declaration, except: e. No implementations with the class declaration, except:
- template or force-inline method (though prefer implementation at bottom of header file). - template or force-inline method (though prefer implementation at bottom of header file).
- one-line implementation (in which case include it in same line as declaration). - one-line implementation (in which case include it in same line as declaration).
@ -147,14 +173,18 @@ c. Avoid unpronouncable names;
- If you need to shorten a name favour a pronouncable slice of the original to a scatterred set of consonants. - If you need to shorten a name favour a pronouncable slice of the original to a scatterred set of consonants.
- e.g. Manager shortens to Man rather than Mgr. - e.g. Manager shortens to Man rather than Mgr.
d. Avoid prefixes of initials (e.g. DON'T use IMyInterface, CMyImplementation) d. Avoid prefixes of initials (e.g. DON'T use IMyInterface, CMyImplementation)
e. A dictionary and thesaurus are your friends. e. Find short, memorable & (at least semi-) descriptive names for commonly used classes or name-fragments.
- A dictionary and thesaurus are your friends.
- Spell correctly. - Spell correctly.
- Find short, memorable & (at least semi-) descriptive names for commonly used classes or name-fragments. - Think carefully about the class's purpose.
- Imagine it as an isolated component to try to decontextualise it when considering its name.
- Don't be trapped into naming it (purely) in terms of its implementation.
10. Type-definitions 10. Type-definitions
a. Prefer using to typedef. e.g. using ints = std::vector<int>; rather than typedef std::vector<int> ints; a. Prefer 'using' to 'typedef'. e.g. using ints = std::vector<int>; rather than typedef std::vector<int> ints;
b. Generally avoid shortening a standard form that already includes all important information: b. Generally avoid shortening a standard form that already includes all important information:
- e.g. stick to shared_ptr<X> rather than shortening to ptr<X>. - e.g. stick to shared_ptr<X> rather than shortening to ptr<X>.
c. Where there are exceptions to this (due to excessive use and clear meaning), note the change prominently and use it consistently. c. Where there are exceptions to this (due to excessive use and clear meaning), note the change prominently and use it consistently.
@ -162,9 +192,17 @@ c. Where there are exceptions to this (due to excessive use and clear meaning),
d. In general expressions should be roughly as important/semantically meaningful as the space they occupy. d. In general expressions should be roughly as important/semantically meaningful as the space they occupy.
11. Commenting 11. Commenting
a. Comments should be doxygen-compilable, using @notation rather than \notation. a. Comments should be doxygen-compilable, using @notation rather than \notation.
b. Document the interface, not the implementation.
- Documentation should be able to remain completely unchanged, even if the method is reimplemented.
- Comment in terms of the method properties and intended alteration to class state (or what aspects of the state it reports).
- Be careful to scrutinise documentation that extends only to intended purpose and usage.
- Reject documentation that is simply an English transaction of the implementation.
12. Include Headers 12. Include Headers

9
README.md

@ -1,12 +1,12 @@
## Ethereum C++ Client. ## Ethereum C++ Client.
By Gav Wood, 2014. By Gav Wood et al, 2013, 2014, 2015.
| Linux | OSX | Windows | Linux | OSX | Windows
----------|---------|-----|-------- ----------|---------|-----|--------
develop | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20C%2B%2B%20develop%20branch)](http://build.ethdev.com/builders/Linux%20C%2B%2B%20develop%20branch/builds/-1) | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=OSX%20C%2B%2B%20develop%20branch)](http://build.ethdev.com/builders/OSX%20C%2B%2B%20develop%20branch/builds/-1) | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=Windows%20C%2B%2B%20develop%20branch)](http://build.ethdev.com/builders/Windows%20C%2B%2B%20develop%20branch/builds/-1) develop | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20C%2B%2B%20develop%20branch)](https://build.ethdev.com/builders/Linux%20C%2B%2B%20develop%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=OSX%20C%2B%2B%20develop%20branch)](https://build.ethdev.com/builders/OSX%20C%2B%2B%20develop%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Windows%20C%2B%2B%20develop%20branch)](https://build.ethdev.com/builders/Windows%20C%2B%2B%20develop%20branch/builds/-1)
master | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20C%2B%2B%20master%20branch)](http://build.ethdev.com/builders/Linux%20C%2B%2B%20master%20branch/builds/-1) | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=OSX%20C%2B%2B%20master%20branch)](http://build.ethdev.com/builders/OSX%20C%2B%2B%20master%20branch/builds/-1) | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=Windows%20C%2B%2B%20master%20branch)](http://build.ethdev.com/builders/Windows%20C%2B%2B%20master%20branch/builds/-1) master | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20C%2B%2B%20master%20branch)](https://build.ethdev.com/builders/Linux%20C%2B%2B%20master%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=OSX%20C%2B%2B%20master%20branch)](https://build.ethdev.com/builders/OSX%20C%2B%2B%20master%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Windows%20C%2B%2B%20master%20branch)](https://build.ethdev.com/builders/Windows%20C%2B%2B%20master%20branch/builds/-1)
evmjit | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20C%2B%2B%20develop%20evmjit)](http://build.ethdev.com/builders/Linux%20C%2B%2B%20develop%20evmjit/builds/-1) | [![Build+Status](http://build.ethdev.com/buildstatusimage?builder=OSX%20C%2B%2B%20develop%20evmjit)](http://build.ethdev.com/builders/OSX%20C%2B%2B%20develop%20evmjit/builds/-1) | N/A evmjit | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20C%2B%2B%20develop%20evmjit)](https://build.ethdev.com/builders/Linux%20C%2B%2B%20develop%20evmjit/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=OSX%20C%2B%2B%20develop%20evmjit)](https://build.ethdev.com/builders/OSX%20C%2B%2B%20develop%20evmjit/builds/-1) | N/A
[![Stories in Ready](https://badge.waffle.io/ethereum/cpp-ethereum.png?label=ready&title=Ready)](http://waffle.io/ethereum/cpp-ethereum) [![Stories in Ready](https://badge.waffle.io/ethereum/cpp-ethereum.png?label=ready&title=Ready)](http://waffle.io/ethereum/cpp-ethereum)
@ -24,7 +24,6 @@ To run the tests, make sure you clone the tests repository from github.com/ether
See [TODO](https://github.com/ethereum/cpp-ethereum/wiki/TODO) See [TODO](https://github.com/ethereum/cpp-ethereum/wiki/TODO)
### License ### License
See [LICENSE](LICENSE) See [LICENSE](LICENSE)

21
alethzero/CMakeLists.txt

@ -10,10 +10,13 @@ endif()
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
aux_source_directory(. SRC_LIST) aux_source_directory(. SRC_LIST)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..)
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
include_directories(..)
qt5_wrap_ui(ui_Main.h Main.ui) qt5_wrap_ui(ui_Main.h Main.ui)
qt5_wrap_ui(ui_Debugger.h Debugger.ui)
qt5_wrap_ui(ui_Transact.h Transact.ui)
file(GLOB HEADERS "*.h") file(GLOB HEADERS "*.h")
@ -26,30 +29,34 @@ endif ()
# eth_add_executable is defined in cmake/EthExecutableHelper.cmake # eth_add_executable is defined in cmake/EthExecutableHelper.cmake
eth_add_executable(${EXECUTABLE} eth_add_executable(${EXECUTABLE}
ICON alethzero ICON alethzero
UI_RESOURCES alethzero.icns Main.ui UI_RESOURCES alethzero.icns Main.ui Debugger.ui Transact.ui
WIN_RESOURCES alethzero.rc WIN_RESOURCES alethzero.rc
) )
add_dependencies(${EXECUTABLE} BuildInfo.h) add_dependencies(${EXECUTABLE} BuildInfo.h)
target_link_libraries(${EXECUTABLE} Qt5::Core) target_link_libraries(${EXECUTABLE} Qt5::Core)
target_link_libraries(${EXECUTABLE} Qt5::Widgets)
target_link_libraries(${EXECUTABLE} Qt5::WebKit)
target_link_libraries(${EXECUTABLE} Qt5::WebKitWidgets)
target_link_libraries(${EXECUTABLE} webthree) target_link_libraries(${EXECUTABLE} webthree)
target_link_libraries(${EXECUTABLE} qwebthree)
target_link_libraries(${EXECUTABLE} ethereum) target_link_libraries(${EXECUTABLE} ethereum)
target_link_libraries(${EXECUTABLE} evm) target_link_libraries(${EXECUTABLE} evm)
target_link_libraries(${EXECUTABLE} ethcore) target_link_libraries(${EXECUTABLE} ethcore)
target_link_libraries(${EXECUTABLE} devcrypto) target_link_libraries(${EXECUTABLE} devcrypto)
target_link_libraries(${EXECUTABLE} secp256k1) target_link_libraries(${EXECUTABLE} secp256k1)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
target_link_libraries(${EXECUTABLE} serpent)
endif()
target_link_libraries(${EXECUTABLE} lll) target_link_libraries(${EXECUTABLE} lll)
target_link_libraries(${EXECUTABLE} solidity) target_link_libraries(${EXECUTABLE} solidity)
target_link_libraries(${EXECUTABLE} evmcore) target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} devcore) target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} web3jsonrpc) target_link_libraries(${EXECUTABLE} web3jsonrpc)
target_link_libraries(${EXECUTABLE} jsqrc) target_link_libraries(${EXECUTABLE} jsqrc)
target_link_libraries(${EXECUTABLE} natspec)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
target_link_libraries(${EXECUTABLE} serpent)
endif()
# eth_install_executable is defined in cmake/EthExecutableHelper.cmake # eth_install_executable is defined in cmake/EthExecutableHelper.cmake
eth_install_executable(${EXECUTABLE}) eth_install_executable(${EXECUTABLE} DLLS ${MHD_DLL_RELEASE})

59
alethzero/Context.cpp

@ -0,0 +1,59 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Debugger.h
* @author Gav Wood <i@gavwood.com>
* @date 2015
*/
#include "Context.h"
#include <QComboBox>
#include <libethcore/CommonEth.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
NatSpecFace::~NatSpecFace()
{
}
Context::~Context()
{
}
void initUnits(QComboBox* _b)
{
for (auto n = (unsigned)units().size(); n-- != 0; )
_b->addItem(QString::fromStdString(units()[n].second), n);
}
vector<KeyPair> keysAsVector(QList<KeyPair> const& keys)
{
auto list = keys.toStdList();
return {begin(list), end(list)};
}
bool sourceIsSolidity(string const& _source)
{
// TODO: Improve this heuristic
return (_source.substr(0, 8) == "contract" || _source.substr(0, 5) == "//sol");
}
bool sourceIsSerpent(string const& _source)
{
// TODO: Improve this heuristic
return (_source.substr(0, 5) == "//ser");
}

68
alethzero/Context.h

@ -0,0 +1,68 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Debugger.h
* @author Gav Wood <i@gavwood.com>
* @date 2015
*/
#pragma once
#include <string>
#include <vector>
#include <QString>
#include <QList>
#include <libethcore/CommonEth.h>
class QComboBox;
namespace dev { namespace eth { struct StateDiff; } }
#define Small "font-size: small; "
#define Mono "font-family: Ubuntu Mono, Monospace, Lucida Console, Courier New; font-weight: bold; "
#define Div(S) "<div style=\"" S "\">"
#define Span(S) "<span style=\"" S "\">"
void initUnits(QComboBox* _b);
std::vector<dev::KeyPair> keysAsVector(QList<dev::KeyPair> const& _keys);
bool sourceIsSolidity(std::string const& _source);
bool sourceIsSerpent(std::string const& _source);
class NatSpecFace
{
public:
virtual ~NatSpecFace();
virtual void add(dev::h256 const& _contractHash, std::string const& _doc) = 0;
virtual std::string retrieve(dev::h256 const& _contractHash) const = 0;
virtual std::string getUserNotice(std::string const& json, const dev::bytes& _transactionData) = 0;
virtual std::string getUserNotice(dev::h256 const& _contractHash, dev::bytes const& _transactionDacta) = 0;
};
class Context
{
public:
virtual ~Context();
virtual QString pretty(dev::Address _a) const = 0;
virtual QString prettyU256(dev::u256 _n) const = 0;
virtual QString render(dev::Address _a) const = 0;
virtual dev::Address fromString(QString const& _a) const = 0;
virtual std::string renderDiff(dev::eth::StateDiff const& _d) const = 0;
};

380
alethzero/Debugger.cpp

@ -0,0 +1,380 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Debugger.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2015
*/
#include "Debugger.h"
#include <fstream>
#include <QFileDialog>
#include <libevm/VM.h>
#include <libethereum/ExtVM.h>
#include <libethereum/Executive.h>
#include "ui_Debugger.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
Debugger::Debugger(Context* _c, QWidget* _parent):
QDialog(_parent),
ui(new Ui::Debugger),
m_context(_c)
{
ui->setupUi(this);
}
Debugger::~Debugger()
{
delete ui;
}
void Debugger::init()
{
if (m_session.history.size())
{
alterDebugStateGroup(true);
ui->debugCode->setEnabled(false);
ui->debugTimeline->setMinimum(0);
ui->debugTimeline->setMaximum(m_session.history.size());
ui->debugTimeline->setValue(0);
}
}
void Debugger::populate(dev::eth::Executive& _executive, dev::eth::Transaction const& _transaction)
{
finished();
if (m_session.populate(_executive, _transaction))
init();
update();
}
bool DebugSession::populate(dev::eth::Executive& _executive, dev::eth::Transaction const& _transaction)
{
try {
if (_executive.setup(_transaction))
return false;
}
catch (...)
{
// Invalid transaction
return false;
}
vector<WorldState const*> levels;
bytes lastExtCode;
bytesConstRef lastData;
h256 lastHash;
h256 lastDataHash;
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, VM* voidVM, ExtVMFace const* voidExt)
{
VM& vm = *voidVM;
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
if (ext.code != lastExtCode)
{
lastExtCode = ext.code;
lastHash = sha3(lastExtCode);
if (!codes.count(lastHash))
codes[lastHash] = ext.code;
}
if (ext.data != lastData)
{
lastData = ext.data;
lastDataHash = sha3(lastData);
if (!codes.count(lastDataHash))
codes[lastDataHash] = ext.data.toBytes();
}
if (levels.size() < ext.depth)
levels.push_back(&history.back());
else
levels.resize(ext.depth);
history.append(WorldState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), lastHash, lastDataHash, vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels}));
};
_executive.go(onOp);
_executive.finalize();
return true;
}
void Debugger::finished()
{
m_session = DebugSession();
ui->callStack->clear();
ui->debugCode->clear();
ui->debugStack->clear();
ui->debugMemory->setHtml("");
ui->debugStorage->setHtml("");
ui->debugStateInfo->setText("");
alterDebugStateGroup(false);
}
void Debugger::update()
{
if (m_session.history.size())
{
WorldState const& nws = m_session.history[min((int)m_session.history.size() - 1, ui->debugTimeline->value())];
WorldState const& ws = ui->callStack->currentRow() > 0 ? *nws.levels[nws.levels.size() - ui->callStack->currentRow()] : nws;
if (ui->debugTimeline->value() >= m_session.history.size())
{
if (ws.gasCost > ws.gas)
ui->debugMemory->setHtml("<h3>OUT-OF-GAS</h3>");
else if (ws.inst == Instruction::RETURN && ws.stack.size() >= 2)
{
unsigned from = (unsigned)ws.stack.back();
unsigned size = (unsigned)ws.stack[ws.stack.size() - 2];
unsigned o = 0;
bytes out(size, 0);
for (; o < size && from + o < ws.memory.size(); ++o)
out[o] = ws.memory[from + o];
ui->debugMemory->setHtml("<h3>RETURN</h3>" + QString::fromStdString(dev::memDump(out, 16, true)));
}
else if (ws.inst == Instruction::STOP)
ui->debugMemory->setHtml("<h3>STOP</h3>");
else if (ws.inst == Instruction::SUICIDE && ws.stack.size() >= 1)
ui->debugMemory->setHtml("<h3>SUICIDE</h3>0x" + QString::fromStdString(toString(right160(ws.stack.back()))));
else
ui->debugMemory->setHtml("<h3>EXCEPTION</h3>");
ostringstream ss;
ss << dec << "EXIT | GAS: " << dec << max<dev::bigint>(0, (dev::bigint)ws.gas - ws.gasCost);
ui->debugStateInfo->setText(QString::fromStdString(ss.str()));
ui->debugStorage->setHtml("");
ui->debugCallData->setHtml("");
m_session.currentData = h256();
ui->callStack->clear();
m_session.currentLevels.clear();
ui->debugCode->clear();
m_session.currentCode = h256();
ui->debugStack->setHtml("");
}
else
{
if (m_session.currentLevels != nws.levels || !ui->callStack->count())
{
m_session.currentLevels = nws.levels;
ui->callStack->clear();
for (unsigned i = 0; i <= nws.levels.size(); ++i)
{
WorldState const& s = i ? *nws.levels[nws.levels.size() - i] : nws;
ostringstream out;
out << s.cur.abridged();
if (i)
out << " " << instructionInfo(s.inst).name << " @0x" << hex << s.curPC;
ui->callStack->addItem(QString::fromStdString(out.str()));
}
}
if (ws.code != m_session.currentCode)
{
m_session.currentCode = ws.code;
bytes const& code = m_session.codes[ws.code];
QListWidget* dc = ui->debugCode;
dc->clear();
m_session.pcWarp.clear();
for (unsigned i = 0; i <= code.size(); ++i)
{
byte b = i < code.size() ? code[i] : 0;
try
{
QString s = QString::fromStdString(instructionInfo((Instruction)b).name);
ostringstream out;
out << hex << setw(4) << setfill('0') << i;
m_session.pcWarp[i] = dc->count();
if (b >= (byte)Instruction::PUSH1 && b <= (byte)Instruction::PUSH32)
{
unsigned bc = b - (byte)Instruction::PUSH1 + 1;
s = "PUSH 0x" + QString::fromStdString(toHex(bytesConstRef(&code[i + 1], bc)));
i += bc;
}
dc->addItem(QString::fromStdString(out.str()) + " " + s);
}
catch (...)
{
cerr << "Unhandled exception!" << endl << boost::current_exception_diagnostic_information();
break; // probably hit data segment
}
}
}
if (ws.callData != m_session.currentData)
{
m_session.currentData = ws.callData;
if (ws.callData)
{
assert(m_session.codes.count(ws.callData));
ui->debugCallData->setHtml(QString::fromStdString(dev::memDump(m_session.codes[ws.callData], 16, true)));
}
else
ui->debugCallData->setHtml("");
}
QString stack;
for (auto i: ws.stack)
stack.prepend("<div>" + m_context->prettyU256(i) + "</div>");
ui->debugStack->setHtml(stack);
ui->debugMemory->setHtml(QString::fromStdString(dev::memDump(ws.memory, 16, true)));
assert(m_session.codes.count(ws.code));
if (m_session.codes[ws.code].size() >= (unsigned)ws.curPC)
{
int l = m_session.pcWarp[(unsigned)ws.curPC];
ui->debugCode->setCurrentRow(max(0, l - 5));
ui->debugCode->setCurrentRow(min(ui->debugCode->count() - 1, l + 5));
ui->debugCode->setCurrentRow(l);
}
else
cwarn << "PC (" << (unsigned)ws.curPC << ") is after code range (" << m_session.codes[ws.code].size() << ")";
ostringstream ss;
ss << dec << "STEP: " << ws.steps << " | PC: 0x" << hex << ws.curPC << " : " << instructionInfo(ws.inst).name << " | ADDMEM: " << dec << ws.newMemSize << " words | COST: " << dec << ws.gasCost << " | GAS: " << dec << ws.gas;
ui->debugStateInfo->setText(QString::fromStdString(ss.str()));
stringstream s;
for (auto const& i: ws.storage)
s << "@" << m_context->prettyU256(i.first).toStdString() << "&nbsp;&nbsp;&nbsp;&nbsp;" << m_context->prettyU256(i.second).toStdString() << "<br/>";
ui->debugStorage->setHtml(QString::fromStdString(s.str()));
}
}
}
void Debugger::on_callStack_currentItemChanged()
{
update();
}
void Debugger::alterDebugStateGroup(bool _enable) const
{
ui->stepOver->setEnabled(_enable);
ui->stepInto->setEnabled(_enable);
ui->stepOut->setEnabled(_enable);
ui->backOver->setEnabled(_enable);
ui->backInto->setEnabled(_enable);
ui->backOut->setEnabled(_enable);
ui->dump->setEnabled(_enable);
ui->dumpStorage->setEnabled(_enable);
ui->dumpPretty->setEnabled(_enable);
}
void Debugger::on_debugTimeline_valueChanged()
{
update();
}
void Debugger::on_stepOver_clicked()
{
if (ui->debugTimeline->value() < m_session.history.size()) {
auto l = m_session.history[ui->debugTimeline->value()].levels.size();
if ((ui->debugTimeline->value() + 1) < m_session.history.size() && m_session.history[ui->debugTimeline->value() + 1].levels.size() > l)
{
on_stepInto_clicked();
if (m_session.history[ui->debugTimeline->value()].levels.size() > l)
on_stepOut_clicked();
}
else
on_stepInto_clicked();
}
}
void Debugger::on_stepInto_clicked()
{
ui->debugTimeline->setValue(ui->debugTimeline->value() + 1);
ui->callStack->setCurrentRow(0);
}
void Debugger::on_stepOut_clicked()
{
if (ui->debugTimeline->value() < m_session.history.size())
{
auto ls = m_session.history[ui->debugTimeline->value()].levels.size();
auto l = ui->debugTimeline->value();
for (; l < m_session.history.size() && m_session.history[l].levels.size() >= ls; ++l) {}
ui->debugTimeline->setValue(l);
ui->callStack->setCurrentRow(0);
}
}
void Debugger::on_backInto_clicked()
{
ui->debugTimeline->setValue(ui->debugTimeline->value() - 1);
ui->callStack->setCurrentRow(0);
}
void Debugger::on_backOver_clicked()
{
auto l = m_session.history[ui->debugTimeline->value()].levels.size();
if (ui->debugTimeline->value() > 0 && m_session.history[ui->debugTimeline->value() - 1].levels.size() > l)
{
on_backInto_clicked();
if (m_session.history[ui->debugTimeline->value()].levels.size() > l)
on_backOut_clicked();
}
else
on_backInto_clicked();
}
void Debugger::on_backOut_clicked()
{
if (ui->debugTimeline->value() > 0 && m_session.history.size() > 0)
{
auto ls = m_session.history[min(ui->debugTimeline->value(), m_session.history.size() - 1)].levels.size();
int l = ui->debugTimeline->value();
for (; l > 0 && m_session.history[l].levels.size() >= ls; --l) {}
ui->debugTimeline->setValue(l);
ui->callStack->setCurrentRow(0);
}
}
void Debugger::on_dump_clicked()
{
QString fn = QFileDialog::getSaveFileName(this, "Select file to output EVM trace");
ofstream f(fn.toStdString());
if (f.is_open())
for (WorldState const& ws: m_session.history)
f << ws.cur << " " << hex << toHex(dev::toCompactBigEndian(ws.curPC, 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)ws.inst, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)ws.gas, 1)) << endl;
}
void Debugger::on_dumpPretty_clicked()
{
QString fn = QFileDialog::getSaveFileName(this, "Select file to output EVM trace");
ofstream f(fn.toStdString());
if (f.is_open())
for (WorldState const& ws: m_session.history)
{
f << endl << " STACK" << endl;
for (auto i: ws.stack)
f << (h256)i << endl;
f << " MEMORY" << endl << dev::memDump(ws.memory);
f << " STORAGE" << endl;
for (auto const& i: ws.storage)
f << showbase << hex << i.first << ": " << i.second << endl;
f << dec << ws.levels.size() << " | " << ws.cur << " | #" << ws.steps << " | " << hex << setw(4) << setfill('0') << ws.curPC << " : " << instructionInfo(ws.inst).name << " | " << dec << ws.gas << " | -" << dec << ws.gasCost << " | " << ws.newMemSize << "x32";
}
}
void Debugger::on_dumpStorage_clicked()
{
QString fn = QFileDialog::getSaveFileName(this, "Select file to output EVM trace");
ofstream f(fn.toStdString());
if (f.is_open())
for (WorldState const& ws: m_session.history)
{
if (ws.inst == Instruction::STOP || ws.inst == Instruction::RETURN || ws.inst == Instruction::SUICIDE)
for (auto i: ws.storage)
f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl;
f << ws.cur << " " << hex << toHex(dev::toCompactBigEndian(ws.curPC, 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)ws.inst, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)ws.gas, 1)) << endl;
}
}

103
alethzero/Debugger.h

@ -0,0 +1,103 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Debugger.h
* @author Gav Wood <i@gavwood.com>
* @date 2015
*/
#pragma once
#include <libdevcore/RLP.h>
#include <libethcore/CommonEth.h>
#include <libethereum/State.h>
#include <libethereum/Executive.h>
#include <QDialog>
#include <QMap>
#include <QList>
#include "Context.h"
namespace Ui { class Debugger; }
struct WorldState
{
uint64_t steps;
dev::Address cur;
dev::u256 curPC;
dev::eth::Instruction inst;
dev::bigint newMemSize;
dev::u256 gas;
dev::h256 code;
dev::h256 callData;
dev::u256s stack;
dev::bytes memory;
dev::bigint gasCost;
std::map<dev::u256, dev::u256> storage;
std::vector<WorldState const*> levels;
};
struct DebugSession
{
DebugSession() {}
bool populate(dev::eth::Executive& _executive, dev::eth::Transaction const& _transaction);
dev::h256 currentCode;
dev::h256 currentData;
std::vector<WorldState const*> currentLevels;
QMap<unsigned, unsigned> pcWarp;
QList<WorldState> history;
std::map<dev::u256, dev::bytes> codes;
};
class Debugger: public QDialog
{
Q_OBJECT
public:
explicit Debugger(Context* _context, QWidget* _parent = 0);
~Debugger();
void populate(dev::eth::Executive& _executive, dev::eth::Transaction const& _transaction);
protected slots:
void on_callStack_currentItemChanged();
void on_debugTimeline_valueChanged();
void on_stepOver_clicked();
void on_stepInto_clicked();
void on_stepOut_clicked();
void on_backOver_clicked();
void on_backInto_clicked();
void on_backOut_clicked();
void on_dump_clicked();
void on_dumpPretty_clicked();
void on_dumpStorage_clicked();
void on_close_clicked() { close(); }
private:
void init();
void update();
void finished();
void alterDebugStateGroup(bool _enable) const;
Ui::Debugger* ui;
DebugSession m_session;
Context* m_context;
};

300
alethzero/Debugger.ui

@ -0,0 +1,300 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Debugger</class>
<widget class="QDialog" name="Debugger">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>989</width>
<height>690</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="stepOver">
<property name="text">
<string>Step Over</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="stepInto">
<property name="text">
<string>Step Into</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="stepOut">
<property name="text">
<string>Step Out</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="backOver">
<property name="text">
<string>Back Over</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="backInto">
<property name="text">
<string>Back Into</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="backOut">
<property name="text">
<string>Back Out</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QSplitter" name="splitter_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QSplitter" name="splitter_42">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QListWidget" name="debugCode">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
</widget>
<widget class="QListWidget" name="callStack">
<property name="font">
<font>
<family>Ubuntu Mono</family>
</font>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
</widget>
</widget>
<widget class="QSplitter" name="splitter_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QTextEdit" name="debugStack">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
<widget class="QTextEdit" name="debugMemory">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
<widget class="QTextEdit" name="debugStorage">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
<widget class="QTextEdit" name="debugCallData">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</widget>
</widget>
</item>
<item>
<widget class="QLabel" name="debugStateInfo">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="debugTimeline">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="dump">
<property name="text">
<string>Dump</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="dumpStorage">
<property name="text">
<string>Dump Storage</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="dumpPretty">
<property name="text">
<string>Dump Pretty</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>577</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="close">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

3
alethzero/GraphParameters.h

@ -36,9 +36,12 @@ static T graphParameters(T _min, T _max, unsigned _divisions, T* o_from = 0, T*
T uMin = _min / _divisor; T uMin = _min / _divisor;
T uMax = _max / _divisor; T uMax = _max / _divisor;
if (uMax == uMin || !_divisions) if (uMax == uMin || !_divisions)
{
if (o_delta && o_from)
{ {
*o_from = 0; *o_from = 0;
*o_delta = 1; *o_delta = 1;
}
return 1; return 1;
} }
long double l10 = std::log10((uMax - uMin) / T(_divisions) * 5.5f); long double l10 = std::log10((uMax - uMin) / T(_divisions) * 5.5f);

16
alethzero/Grapher.h

@ -76,24 +76,24 @@ public:
void labelYOrderedPoints(std::map<float, float> const& _translatedData, int _maxCount = 20, float _minFactor = .01f) const; void labelYOrderedPoints(std::map<float, float> const& _translatedData, int _maxCount = 20, float _minFactor = .01f) const;
protected: protected:
QPainter* p; QPainter* p = nullptr;
QRect active; QRect active;
std::pair<float, float> xRange; std::pair<float, float> xRange;
std::pair<float, float> yRange; std::pair<float, float> yRange;
float xM; float xM = 0;
float xC; float xC = 0;
float yM; float yM = 0;
float yC; float yC = 0;
float dx; float dx = 0;
float dy; float dy = 0;
std::function<std::string(float _f)> xLabel; std::function<std::string(float _f)> xLabel;
std::function<std::string(float _f)> yLabel; std::function<std::string(float _f)> yLabel;
std::function<std::string(float _x, float _y)> pLabel; std::function<std::string(float _x, float _y)> pLabel;
float fontPixelSize; float fontPixelSize = 0;
// Translate from raw indexed data into x/y graph units. Only relevant for indexed data. // Translate from raw indexed data into x/y graph units. Only relevant for indexed data.
float xT(float _dataIndex) const { return _dataIndex * xM + xC; } float xT(float _dataIndex) const { return _dataIndex * xM + xC; }

540
alethzero/Main.ui

@ -117,7 +117,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1617</width> <width>1617</width>
<height>25</height> <height>24</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menu_File"> <widget class="QMenu" name="menu_File">
@ -140,11 +140,12 @@
</widget> </widget>
<widget class="QMenu" name="menu_Tools"> <widget class="QMenu" name="menu_Tools">
<property name="title"> <property name="title">
<string>T&amp;ools</string> <string>&amp;Tools</string>
</property> </property>
<addaction name="mine"/> <addaction name="mine"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="create"/> <addaction name="newTransaction"/>
<addaction name="newAccount"/>
<addaction name="importKey"/> <addaction name="importKey"/>
<addaction name="importKeyFile"/> <addaction name="importKeyFile"/>
<addaction name="exportKey"/> <addaction name="exportKey"/>
@ -160,18 +161,15 @@
</widget> </widget>
<widget class="QMenu" name="menu_Debug"> <widget class="QMenu" name="menu_Debug">
<property name="title"> <property name="title">
<string>Deb&amp;ug</string> <string>&amp;Special</string>
</property> </property>
<addaction name="debugDumpState"/> <addaction name="natSpec"/>
<addaction name="debugDumpStatePre"/>
<addaction name="separator"/>
<addaction name="paranoia"/> <addaction name="paranoia"/>
<addaction name="clearPending"/> <addaction name="clearPending"/>
<addaction name="killBlockchain"/> <addaction name="killBlockchain"/>
<addaction name="inject"/> <addaction name="inject"/>
<addaction name="forceMining"/> <addaction name="forceMining"/>
<addaction name="turboMining"/> <addaction name="turboMining"/>
<addaction name="enableOptimizer"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="usePrivate"/> <addaction name="usePrivate"/>
<addaction name="jitvm"/> <addaction name="jitvm"/>
@ -185,42 +183,28 @@
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="preview"/> <addaction name="preview"/>
</widget> </widget>
<widget class="QMenu" name="menuDebugger">
<property name="title">
<string>D&amp;ebugger</string>
</property>
<widget class="QMenu" name="menu_Dump_Trace">
<property name="title">
<string>&amp;Dump Trace</string>
</property>
<addaction name="dumpTrace"/>
<addaction name="dumpTraceStorage"/>
<addaction name="dumpTracePretty"/>
</widget>
<addaction name="debugCurrent"/>
<addaction name="menu_Dump_Trace"/>
<addaction name="separator"/>
<addaction name="debugStep"/>
<addaction name="debugStepInto"/>
<addaction name="debugStepOut"/>
<addaction name="debugStepBack"/>
<addaction name="debugStepBackInto"/>
<addaction name="debugStepBackOut"/>
</widget>
<widget class="QMenu" name="menuWhispe"> <widget class="QMenu" name="menuWhispe">
<property name="title"> <property name="title">
<string>&amp;Whisper</string> <string>&amp;Whisper</string>
</property> </property>
<addaction name="newIdentity"/> <addaction name="newIdentity"/>
</widget> </widget>
<widget class="QMenu" name="menuDebug">
<property name="title">
<string>&amp;Debug</string>
</property>
<addaction name="debugCurrent"/>
<addaction name="debugDumpState"/>
<addaction name="debugDumpStatePre"/>
</widget>
<addaction name="menu_File"/> <addaction name="menu_File"/>
<addaction name="menu_View"/> <addaction name="menu_View"/>
<addaction name="menu_Network"/> <addaction name="menu_Network"/>
<addaction name="menu_Tools"/> <addaction name="menu_Tools"/>
<addaction name="menuDebugger"/>
<addaction name="menuWhispe"/> <addaction name="menuWhispe"/>
<addaction name="menu_Debug"/> <addaction name="menuDebug"/>
<addaction name="menu_Help"/> <addaction name="menu_Help"/>
<addaction name="menu_Debug"/>
</widget> </widget>
<widget class="QStatusBar" name="statusbar"/> <widget class="QStatusBar" name="statusbar"/>
<widget class="QDockWidget" name="dockWidget_2"> <widget class="QDockWidget" name="dockWidget_2">
@ -533,237 +517,11 @@
<bool>false</bool> <bool>false</bool>
</attribute> </attribute>
<addaction name="go"/> <addaction name="go"/>
<addaction name="newTransaction"/>
<addaction name="preview"/> <addaction name="preview"/>
<addaction name="mine"/> <addaction name="mine"/>
<addaction name="refresh"/> <addaction name="refresh"/>
</widget> </widget>
<widget class="QDockWidget" name="dockWidget_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>510</width>
<height>386</height>
</size>
</property>
<property name="features">
<set>QDockWidget::DockWidgetFeatureMask</set>
</property>
<property name="windowTitle">
<string>Transact</string>
</property>
<attribute name="dockWidgetArea">
<number>1</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents_5">
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&amp;Data</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>data</cstring>
</property>
</widget>
</item>
<item row="5" column="0" colspan="4">
<widget class="QSplitter" name="splitter_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QPlainTextEdit" name="data">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
</widget>
<widget class="QTextEdit" name="code">
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>&amp;Gas</string>
</property>
<property name="buddy">
<cstring>gas</cstring>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label5">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&amp;To</string>
</property>
<property name="buddy">
<cstring>destination</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="gas">
<property name="suffix">
<string> gas</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>430000000</number>
</property>
<property name="value">
<number>10000</number>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QComboBox" name="valueUnits"/>
</item>
<item row="3" column="3">
<widget class="QComboBox" name="gasPriceUnits"/>
</item>
<item row="3" column="2">
<widget class="QSpinBox" name="gasPrice">
<property name="prefix">
<string>@ </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>430000000</number>
</property>
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="QLineEdit" name="calculatedName">
<property name="enabled">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="placeholderText">
<string/>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QSpinBox" name="value">
<property name="suffix">
<string/>
</property>
<property name="maximum">
<number>430000000</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label5_2">
<property name="text">
<string>&amp;Amount</string>
</property>
<property name="buddy">
<cstring>value</cstring>
</property>
</widget>
</item>
<item row="4" column="1" colspan="3">
<widget class="QLabel" name="fee">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QComboBox" name="destination">
<property name="editable">
<bool>true</bool>
</property>
<item>
<property name="text">
<string>(Create Contract)</string>
</property>
</item>
</widget>
</item>
<item row="6" column="3">
<widget class="QPushButton" name="send">
<property name="text">
<string>&amp;Execute</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QLabel" name="total">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="QPushButton" name="debug">
<property name="text">
<string>De&amp;bug</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QDockWidget" name="dockWidget_7"> <widget class="QDockWidget" name="dockWidget_7">
<property name="features"> <property name="features">
<set>QDockWidget::DockWidgetFeatureMask</set> <set>QDockWidget::DockWidgetFeatureMask</set>
@ -987,171 +745,6 @@
</layout> </layout>
</widget> </widget>
</widget> </widget>
<widget class="QDockWidget" name="dockWidget_9">
<property name="floating">
<bool>false</bool>
</property>
<property name="features">
<set>QDockWidget::DockWidgetFeatureMask</set>
</property>
<property name="windowTitle">
<string>Debugger</string>
</property>
<attribute name="dockWidgetArea">
<number>1</number>
</attribute>
<widget class="QWidget" name="debugPanel">
<property name="enabled">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSplitter" name="splitter_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QSplitter" name="splitter_42">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QListWidget" name="debugCode">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
</widget>
<widget class="QListWidget" name="callStack">
<property name="font">
<font>
<family>Ubuntu Mono</family>
</font>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
</widget>
</widget>
<widget class="QSplitter" name="splitter_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QTextEdit" name="debugStack">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
<widget class="QTextEdit" name="debugMemory">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
<widget class="QTextEdit" name="debugStorage">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
<widget class="QTextEdit" name="debugCallData">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</widget>
</widget>
</item>
<item>
<widget class="QLabel" name="debugStateInfo">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="debugTimeline">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QDockWidget" name="dockWidget_10"> <widget class="QDockWidget" name="dockWidget_10">
<property name="features"> <property name="features">
<set>QDockWidget::DockWidgetFeatureMask</set> <set>QDockWidget::DockWidgetFeatureMask</set>
@ -1566,9 +1159,6 @@ font-size: 14pt</string>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property> </property>
<property name="buddy">
<cstring>data</cstring>
</property>
</widget> </widget>
</item> </item>
<item row="3" column="1" colspan="4"> <item row="3" column="1" colspan="4">
@ -1621,9 +1211,6 @@ font-size: 14pt</string>
<property name="text"> <property name="text">
<string>TTL</string> <string>TTL</string>
</property> </property>
<property name="buddy">
<cstring>destination</cstring>
</property>
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
@ -1637,9 +1224,6 @@ font-size: 14pt</string>
<property name="text"> <property name="text">
<string>From</string> <string>From</string>
</property> </property>
<property name="buddy">
<cstring>destination</cstring>
</property>
</widget> </widget>
</item> </item>
<item row="0" column="0"> <item row="0" column="0">
@ -1653,9 +1237,6 @@ font-size: 14pt</string>
<property name="text"> <property name="text">
<string>To</string> <string>To</string>
</property> </property>
<property name="buddy">
<cstring>destination</cstring>
</property>
</widget> </widget>
</item> </item>
<item row="1" column="1" colspan="4"> <item row="1" column="1" colspan="4">
@ -1686,9 +1267,6 @@ font-size: 14pt</string>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property> </property>
<property name="buddy">
<cstring>data</cstring>
</property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="2" column="1">
@ -1715,9 +1293,6 @@ font-size: 14pt</string>
<property name="text"> <property name="text">
<string>Work to Prove</string> <string>Work to Prove</string>
</property> </property>
<property name="buddy">
<cstring>destination</cstring>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -1776,7 +1351,7 @@ font-size: 14pt</string>
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Use &amp;UPnP</string> <string>&amp;Use UPnP</string>
</property> </property>
</action> </action>
<action name="connect"> <action name="connect">
@ -1800,9 +1375,9 @@ font-size: 14pt</string>
<string>&amp;Mine</string> <string>&amp;Mine</string>
</property> </property>
</action> </action>
<action name="create"> <action name="newAccount">
<property name="text"> <property name="text">
<string>&amp;New Address</string> <string>&amp;New Address...</string>
</property> </property>
</action> </action>
<action name="about"> <action name="about">
@ -1834,7 +1409,7 @@ font-size: 14pt</string>
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Mining &amp;Paranoia</string> <string>&amp;Mining Paranoia</string>
</property> </property>
</action> </action>
<action name="killBlockchain"> <action name="killBlockchain">
@ -1862,7 +1437,7 @@ font-size: 14pt</string>
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Show Ancient &amp;Blocks</string> <string>&amp;Show Ancient Blocks</string>
</property> </property>
</action> </action>
<action name="showAllAccounts"> <action name="showAllAccounts">
@ -1870,7 +1445,7 @@ font-size: 14pt</string>
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Show Anonymous &amp;Accounts</string> <string>Show &amp;Anonymous Accounts</string>
</property> </property>
</action> </action>
<action name="usePast"> <action name="usePast">
@ -1970,7 +1545,7 @@ font-size: 14pt</string>
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Debu&amp;g Current Transaction</string> <string>&amp;Debug Current Transaction</string>
</property> </property>
</action> </action>
<action name="debugDumpState"> <action name="debugDumpState">
@ -1978,7 +1553,7 @@ font-size: 14pt</string>
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="text"> <property name="text">
<string>D&amp;ump Current Transaction State (post)</string> <string>Dump &amp;Current Transaction State (post)</string>
</property> </property>
</action> </action>
<action name="debugDumpStatePre"> <action name="debugDumpStatePre">
@ -1986,7 +1561,7 @@ font-size: 14pt</string>
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="text"> <property name="text">
<string>D&amp;ump Current Transaction State (pre)</string> <string>Dump Current &amp;Transaction State (pre)</string>
</property> </property>
</action> </action>
<action name="dumpTracePretty"> <action name="dumpTracePretty">
@ -2007,7 +1582,7 @@ font-size: 14pt</string>
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="text"> <property name="text">
<string>&amp;Use Private Chain...</string> <string>Use &amp;Private Chain...</string>
</property> </property>
</action> </action>
<action name="enableOptimizer"> <action name="enableOptimizer">
@ -2015,7 +1590,7 @@ font-size: 14pt</string>
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="text"> <property name="text">
<string>&amp;Enable LLL &amp;Optimizer</string> <string>&amp;Enable LLL Optimizer</string>
</property> </property>
</action> </action>
<action name="turboMining"> <action name="turboMining">
@ -2023,7 +1598,7 @@ font-size: 14pt</string>
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Reserved Debug 1</string> <string>&amp;Reserved Debug 1</string>
</property> </property>
</action> </action>
<action name="localNetworking"> <action name="localNetworking">
@ -2031,7 +1606,7 @@ font-size: 14pt</string>
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Enable Local Addresses</string> <string>&amp;Enable Local Addresses</string>
</property> </property>
</action> </action>
<action name="importKeyFile"> <action name="importKeyFile">
@ -2044,7 +1619,7 @@ font-size: 14pt</string>
</action> </action>
<action name="go"> <action name="go">
<property name="text"> <property name="text">
<string>Go!</string> <string>&amp;Go!</string>
</property> </property>
</action> </action>
<action name="newIdentity"> <action name="newIdentity">
@ -2054,7 +1629,7 @@ font-size: 14pt</string>
</action> </action>
<action name="clearPending"> <action name="clearPending">
<property name="text"> <property name="text">
<string>Clear Pe&amp;nd&amp;ing</string> <string>&amp;Clear Pending</string>
</property> </property>
</action> </action>
<action name="jitvm"> <action name="jitvm">
@ -2073,6 +1648,22 @@ font-size: 14pt</string>
<string>&amp;Kill Account</string> <string>&amp;Kill Account</string>
</property> </property>
</action> </action>
<action name="newTransaction">
<property name="text">
<string>New &amp;Transaction...</string>
</property>
</action>
<action name="natSpec">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;NatSpec Enabled</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<customwidgets> <customwidgets>
@ -2095,27 +1686,30 @@ font-size: 14pt</string>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<tabstops> <tabstops>
<tabstop>destination</tabstop> <tabstop>shhTo</tabstop>
<tabstop>calculatedName</tabstop> <tabstop>shhFrom</tabstop>
<tabstop>value</tabstop> <tabstop>shhTtl</tabstop>
<tabstop>valueUnits</tabstop> <tabstop>shhTopic</tabstop>
<tabstop>gas</tabstop> <tabstop>shhWork</tabstop>
<tabstop>gasPrice</tabstop> <tabstop>shhData</tabstop>
<tabstop>gasPriceUnits</tabstop> <tabstop>log</tabstop>
<tabstop>data</tabstop> <tabstop>post</tabstop>
<tabstop>send</tabstop>
<tabstop>idealPeers</tabstop>
<tabstop>port</tabstop>
<tabstop>clientName</tabstop>
<tabstop>verbosity</tabstop> <tabstop>verbosity</tabstop>
<tabstop>jsConsole</tabstop>
<tabstop>tabWidget</tabstop> <tabstop>tabWidget</tabstop>
<tabstop>urlEdit</tabstop> <tabstop>urlEdit</tabstop>
<tabstop>webView</tabstop> <tabstop>webView</tabstop>
<tabstop>idealPeers</tabstop>
<tabstop>forceAddress</tabstop>
<tabstop>port</tabstop>
<tabstop>clientName</tabstop>
<tabstop>transactionQueue</tabstop>
<tabstop>pendingInfo</tabstop>
<tabstop>blockChainFilter</tabstop>
<tabstop>nameReg</tabstop> <tabstop>nameReg</tabstop>
<tabstop>debugCode</tabstop> <tabstop>nodes</tabstop>
<tabstop>debugStack</tabstop> <tabstop>whispers</tabstop>
<tabstop>debugMemory</tabstop> <tabstop>jsInput</tabstop>
<tabstop>debugStorage</tabstop>
</tabstops> </tabstops>
<resources/> <resources/>
<connections/> <connections/>

783
alethzero/MainWin.cpp

File diff suppressed because it is too large

187
alethzero/MainWin.h

@ -34,10 +34,10 @@
#include <libethcore/CommonEth.h> #include <libethcore/CommonEth.h>
#include <libethereum/State.h> #include <libethereum/State.h>
#include <libethereum/Executive.h> #include <libethereum/Executive.h>
#include <libqwebthree/QWebThree.h>
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include "Context.h"
#include "Transact.h"
#include "NatspecHandler.h" #include "NatspecHandler.h"
namespace Ui { namespace Ui {
@ -49,31 +49,18 @@ class Client;
class State; class State;
}} }}
namespace jsonrpc {
class HttpServer;
}
class QQuickView; class QQuickView;
class OurWebThreeStubServer; class OurWebThreeStubServer;
struct WorldState
{
uint64_t steps;
dev::Address cur;
dev::u256 curPC;
dev::eth::Instruction inst;
dev::bigint newMemSize;
dev::u256 gas;
dev::h256 code;
dev::h256 callData;
dev::u256s stack;
dev::bytes memory;
dev::bigint gasCost;
std::map<dev::u256, dev::u256> storage;
std::vector<WorldState const*> levels;
};
using WatchHandler = std::function<void(dev::eth::LocalisedLogEntries const&)>; using WatchHandler = std::function<void(dev::eth::LocalisedLogEntries const&)>;
QString contentsOfQResource(std::string const& res); QString contentsOfQResource(std::string const& res);
class Main : public QMainWindow class Main: public QMainWindow, public Context
{ {
Q_OBJECT Q_OBJECT
@ -85,12 +72,20 @@ public:
dev::eth::Client* ethereum() const { return m_webThree->ethereum(); } dev::eth::Client* ethereum() const { return m_webThree->ethereum(); }
std::shared_ptr<dev::shh::WhisperHost> whisper() const { return m_webThree->whisper(); } std::shared_ptr<dev::shh::WhisperHost> whisper() const { return m_webThree->whisper(); }
std::string lookupNatSpec(dev::h256 const& _contractHash) const; bool confirm() const;
std::string lookupNatSpecUserNotice(dev::h256 const& _contractHash, dev::bytes const& _transactionData); NatSpecFace* natSpec() { return &m_natSpecDB; }
QVariant evalRaw(QString const& _js);
QString pretty(dev::Address _a) const override;
QString prettyU256(dev::u256 _n) const override;
QString render(dev::Address _a) const override;
dev::Address fromString(QString const& _a) const override;
std::string renderDiff(dev::eth::StateDiff const& _d) const override;
QList<dev::KeyPair> owned() const { return m_myIdentities + m_myKeys; } QList<dev::KeyPair> owned() const { return m_myIdentities + m_myKeys; }
QVariant evalRaw(QString const& _js); dev::u256 gasPrice() const { return 10 * dev::eth::szabo; }
public slots: public slots:
void load(QString _file); void load(QString _file);
@ -99,75 +94,80 @@ public slots:
void warn(QString _entry); void warn(QString _entry);
QString contents(QString _file); QString contents(QString _file);
int authenticate(QString _title, QString _text);
void onKeysChanged(); void onKeysChanged();
private slots: private slots:
void eval(QString const& _js); void eval(QString const& _js);
// Application
void on_about_triggered();
void on_quit_triggered() { close(); }
// Network
void on_go_triggered();
void on_net_triggered();
void on_connect_triggered(); void on_connect_triggered();
void on_idealPeers_valueChanged();
// Mining
void on_mine_triggered(); void on_mine_triggered();
void on_send_clicked();
void on_create_triggered(); // View
void on_refresh_triggered();
void on_showAll_triggered() { refreshBlockChain(); }
void on_showAllAccounts_triggered() { refreshAccounts(); }
void on_preview_triggered();
// Account management
void on_newAccount_triggered();
void on_killAccount_triggered(); void on_killAccount_triggered();
void on_net_triggered(); void on_importKey_triggered();
void on_verbosity_valueChanged(); void on_importKeyFile_triggered();
void on_ourAccounts_doubleClicked(); void on_exportKey_triggered();
// Tools
void on_newTransaction_triggered();
void on_loadJS_triggered();
// Stuff concerning the blocks/transactions/accounts panels
void ourAccountsRowsMoved(); void ourAccountsRowsMoved();
void on_ourAccounts_doubleClicked();
void on_accounts_doubleClicked(); void on_accounts_doubleClicked();
void on_destination_currentTextChanged();
void on_data_textChanged();
void on_idealPeers_valueChanged();
void on_value_valueChanged() { updateFee(); }
void on_gas_valueChanged() { updateFee(); }
void on_valueUnits_currentIndexChanged() { updateFee(); }
void on_gasPriceUnits_currentIndexChanged() { updateFee(); }
void on_gasPrice_valueChanged() { updateFee(); }
void on_log_doubleClicked();
void on_blocks_currentItemChanged();
void on_contracts_doubleClicked(); void on_contracts_doubleClicked();
void on_contracts_currentItemChanged(); void on_contracts_currentItemChanged();
void on_transactionQueue_currentItemChanged(); void on_transactionQueue_currentItemChanged();
void on_about_triggered(); void on_blockChainFilter_textChanged();
void on_paranoia_triggered(); void on_blocks_currentItemChanged();
void on_nameReg_textChanged();
void on_preview_triggered(); // Logging
void on_quit_triggered() { close(); } void on_log_doubleClicked();
void on_verbosity_valueChanged();
// Misc
void on_urlEdit_returnPressed(); void on_urlEdit_returnPressed();
void on_debugStep_triggered();
void on_debugStepBack_triggered();
void on_debug_clicked();
void on_debugTimeline_valueChanged();
void on_jsInput_returnPressed(); void on_jsInput_returnPressed();
void on_nameReg_textChanged();
// Special (debug) stuff
void on_paranoia_triggered();
void on_killBlockchain_triggered(); void on_killBlockchain_triggered();
void on_clearPending_triggered(); void on_clearPending_triggered();
void on_importKey_triggered();
void on_exportKey_triggered();
void on_inject_triggered(); void on_inject_triggered();
void on_showAll_triggered() { refreshBlockChain(); }
void on_showAllAccounts_triggered() { refreshAccounts(); }
void on_loadJS_triggered();
void on_blockChainFilter_textChanged();
void on_forceMining_triggered(); void on_forceMining_triggered();
void on_dumpTrace_triggered(); void on_usePrivate_triggered();
void on_dumpTraceStorage_triggered(); void on_turboMining_triggered();
void on_dumpTracePretty_triggered(); void on_jitvm_triggered();
void on_debugStepInto_triggered();
void on_debugStepOut_triggered(); // Debugger
void on_debugStepBackOut_triggered();
void on_debugStepBackInto_triggered();
void on_callStack_currentItemChanged();
void on_debugCurrent_triggered(); void on_debugCurrent_triggered();
void on_debugDumpState_triggered(int _add = 1); void on_debugDumpState_triggered(int _add = 1);
void on_debugDumpStatePre_triggered(); void on_debugDumpStatePre_triggered();
void on_refresh_triggered();
void on_usePrivate_triggered(); // Whisper
void on_enableOptimizer_triggered();
void on_turboMining_triggered();
void on_go_triggered();
void on_importKeyFile_triggered();
void on_post_clicked();
void on_newIdentity_triggered(); void on_newIdentity_triggered();
void on_jitvm_triggered(); void on_post_clicked();
void refreshWhisper(); void refreshWhisper();
void refreshBlockChain(); void refreshBlockChain();
@ -179,33 +179,14 @@ signals:
private: private:
dev::p2p::NetworkPreferences netPrefs() const; dev::p2p::NetworkPreferences netPrefs() const;
QString pretty(dev::Address _a) const;
QString prettyU256(dev::u256 _n) const;
QString lookup(QString const& _n) const; QString lookup(QString const& _n) const;
dev::Address getNameReg() const; dev::Address getNameReg() const;
dev::Address getCurrencies() const; dev::Address getCurrencies() const;
void populateDebugger(dev::bytesConstRef r);
void initDebugger();
void updateDebugger();
void debugFinished();
QString render(dev::Address _a) const;
dev::Address fromString(QString const& _a) const;
std::string renderDiff(dev::eth::StateDiff const& _d) const;
void alterDebugStateGroup(bool _enable) const;
void updateFee(); void updateFee();
void readSettings(bool _skipGeometry = false); void readSettings(bool _skipGeometry = false);
void writeSettings(); void writeSettings();
bool isCreation() const;
dev::u256 fee() const;
dev::u256 total() const;
dev::u256 value() const;
dev::u256 gasPrice() const;
unsigned installWatch(dev::eth::LogFilter const& _tf, WatchHandler const& _f); unsigned installWatch(dev::eth::LogFilter const& _tf, WatchHandler const& _f);
unsigned installWatch(dev::h256 _tf, WatchHandler const& _f); unsigned installWatch(dev::h256 _tf, WatchHandler const& _f);
void uninstallWatch(unsigned _w); void uninstallWatch(unsigned _w);
@ -232,16 +213,9 @@ private:
void refreshAll(); void refreshAll();
void refreshPending(); void refreshPending();
void refreshAccounts(); void refreshAccounts();
void refreshDestination();
void refreshBlockCount(); void refreshBlockCount();
void refreshBalances(); void refreshBalances();
/// Attempts to infer that @c _source contains Solidity code
bool sourceIsSolidity(std::string const& _source);
/// @eturns all method hashes of a Solidity contract in a string
std::string const getFunctionHashes(dev::solidity::CompilerStack const &_compiler,
std::string const& _contractName = "");
std::unique_ptr<Ui::Main> ui; std::unique_ptr<Ui::Main> ui;
std::unique_ptr<dev::WebThreeDirect> m_webThree; std::unique_ptr<dev::WebThreeDirect> m_webThree;
@ -251,27 +225,13 @@ private:
unsigned m_currenciesFilter = (unsigned)-1; unsigned m_currenciesFilter = (unsigned)-1;
unsigned m_balancesFilter = (unsigned)-1; unsigned m_balancesFilter = (unsigned)-1;
QByteArray m_peers; QByteArray m_networkConfig;
QStringList m_servers; QStringList m_servers;
QList<dev::KeyPair> m_myKeys; QList<dev::KeyPair> m_myKeys;
QList<dev::KeyPair> m_myIdentities; QList<dev::KeyPair> m_myIdentities;
QString m_privateChain; QString m_privateChain;
dev::bytes m_data;
dev::Address m_nameReg; dev::Address m_nameReg;
unsigned m_backupGas;
dev::eth::State m_executiveState;
std::unique_ptr<dev::eth::Executive> m_currentExecution;
dev::h256 m_lastCode;
dev::h256 m_lastData;
std::vector<WorldState const*> m_lastLevels;
QMap<unsigned, unsigned> m_pcWarp;
QList<WorldState> m_history;
std::map<dev::u256, dev::bytes> m_codes; // and pcWarps
bool m_enableOptimizer = true;
QNetworkAccessManager m_webCtrl; QNetworkAccessManager m_webCtrl;
QList<QPair<QString, QString>> m_consoleHistory; QList<QPair<QString, QString>> m_consoleHistory;
@ -279,10 +239,11 @@ private:
QString m_logHistory; QString m_logHistory;
bool m_logChanged = true; bool m_logChanged = true;
std::unique_ptr<QWebThreeConnector> m_qwebConnector; std::unique_ptr<jsonrpc::HttpServer> m_httpConnector;
std::unique_ptr<OurWebThreeStubServer> m_server; std::unique_ptr<OurWebThreeStubServer> m_server;
QWebThree* m_qweb = nullptr;
static QString fromRaw(dev::h256 _n, unsigned* _inc = nullptr); static QString fromRaw(dev::h256 _n, unsigned* _inc = nullptr);
NatspecHandler m_natspecDB; NatspecHandler m_natSpecDB;
Transact m_transact;
}; };

5
alethzero/MiningView.cpp

@ -48,7 +48,6 @@ string sL(float _x, float _y) { return toString(round(_x * 1000)) + "s (" + toSt
MiningView::MiningView(QWidget* _p): QWidget(_p) MiningView::MiningView(QWidget* _p): QWidget(_p)
{ {
} }
void MiningView::appendStats(list<MineInfo> const& _i, MineProgress const& _p) void MiningView::appendStats(list<MineInfo> const& _i, MineProgress const& _p)
@ -86,10 +85,10 @@ void MiningView::appendStats(list<MineInfo> const& _i, MineProgress const& _p)
for (auto& i: m_resets) for (auto& i: m_resets)
i -= o; i -= o;
remove_if(m_resets.begin(), m_resets.end(), [](int i){return i < 0;}); m_resets.erase(remove_if(m_resets.begin(), m_resets.end(), [](int i){return i < 0;}), m_resets.end());
for (auto& i: m_completes) for (auto& i: m_completes)
i -= o; i -= o;
remove_if(m_completes.begin(), m_completes.end(), [](int i){return i < 0;}); m_completes.erase(remove_if(m_completes.begin(), m_completes.end(), [](int i){return i < 0;}), m_completes.end());
m_progress = _p; m_progress = _p;
update(); update();

14
alethzero/NatspecHandler.cpp

@ -50,16 +50,15 @@ NatspecHandler::~NatspecHandler()
void NatspecHandler::add(dev::h256 const& _contractHash, string const& _doc) void NatspecHandler::add(dev::h256 const& _contractHash, string const& _doc)
{ {
bytes k = _contractHash.asBytes(); m_db->Put(m_writeOptions, _contractHash.ref(), _doc);
string v = _doc; cdebug << "Registering NatSpec: " << _contractHash.abridged() << _doc;
m_db->Put(m_writeOptions, ldb::Slice((char const*)k.data(), k.size()), ldb::Slice((char const*)v.data(), v.size()));
} }
string NatspecHandler::retrieve(dev::h256 const& _contractHash) const string NatspecHandler::retrieve(dev::h256 const& _contractHash) const
{ {
bytes k = _contractHash.asBytes();
string ret; string ret;
m_db->Get(m_readOptions, ldb::Slice((char const*)k.data(), k.size()), &ret); m_db->Get(m_readOptions, _contractHash.ref(), &ret);
cdebug << "Looking up NatSpec: " << _contractHash.abridged() << ret;
return ret; return ret;
} }
@ -67,10 +66,9 @@ string NatspecHandler::getUserNotice(string const& json, dev::bytes const& _tran
{ {
Json::Value natspec; Json::Value natspec;
Json::Value userNotice; Json::Value userNotice;
string retStr;
m_reader.parse(json, natspec); m_reader.parse(json, natspec);
bytes transactionFunctionPart(_transactionData.begin(), _transactionData.begin() + 4);
FixedHash<4> transactionFunctionHash(transactionFunctionPart); FixedHash<4> transactionFunctionHash((bytesConstRef(&_transactionData).cropped(0, 4).toBytes()));
Json::Value methods = natspec["methods"]; Json::Value methods = natspec["methods"];
for (Json::ValueIterator it = methods.begin(); it != methods.end(); ++it) for (Json::ValueIterator it = methods.begin(); it != methods.end(); ++it)

9
alethzero/NatspecHandler.h

@ -26,12 +26,13 @@
#pragma warning(disable: 4100 4267) #pragma warning(disable: 4100 4267)
#include <leveldb/db.h> #include <leveldb/db.h>
#pragma warning(pop) #pragma warning(pop)
#include <jsoncpp/json/json.h> #include <json/json.h>
#include <libdevcore/FixedHash.h> #include <libdevcore/FixedHash.h>
#include "Context.h"
namespace ldb = leveldb; namespace ldb = leveldb;
class NatspecHandler class NatspecHandler: public NatSpecFace
{ {
public: public:
NatspecHandler(); NatspecHandler();
@ -40,7 +41,7 @@ class NatspecHandler
/// Stores locally in a levelDB a key value pair of contract code hash to natspec documentation /// Stores locally in a levelDB a key value pair of contract code hash to natspec documentation
void add(dev::h256 const& _contractHash, std::string const& _doc); void add(dev::h256 const& _contractHash, std::string const& _doc);
/// Retrieves the natspec documentation as a string given a contract code hash /// Retrieves the natspec documentation as a string given a contract code hash
std::string retrieve(dev::h256 const& _contractHash) const; std::string retrieve(dev::h256 const& _contractHash) const override;
/// Given a json natspec string and the transaction data return the user notice /// Given a json natspec string and the transaction data return the user notice
std::string getUserNotice(std::string const& json, const dev::bytes& _transactionData); std::string getUserNotice(std::string const& json, const dev::bytes& _transactionData);
@ -53,6 +54,6 @@ class NatspecHandler
private: private:
ldb::ReadOptions m_readOptions; ldb::ReadOptions m_readOptions;
ldb::WriteOptions m_writeOptions; ldb::WriteOptions m_writeOptions;
ldb::DB* m_db; ldb::DB* m_db = nullptr;
Json::Reader m_reader; Json::Reader m_reader;
}; };

131
alethzero/OurWebThreeStubServer.cpp

@ -24,6 +24,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <QAbstractButton> #include <QAbstractButton>
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
#include <libnatspec/NatspecExpressionEvaluator.h>
#include "MainWin.h" #include "MainWin.h"
@ -32,9 +33,11 @@ using namespace dev;
using namespace dev::eth; using namespace dev::eth;
OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3, OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3,
vector<KeyPair> const& _accounts, Main* main): vector<KeyPair> const& _accounts, Main* _main):
WebThreeStubServer(_conn, _web3, _accounts), m_web3(&_web3), m_main(main) WebThreeStubServer(_conn, _web3, _accounts), m_web3(&_web3), m_main(_main)
{} {
connect(_main, SIGNAL(poll()), this, SLOT(doValidations()));
}
string OurWebThreeStubServer::shh_newIdentity() string OurWebThreeStubServer::shh_newIdentity()
{ {
@ -43,77 +46,107 @@ string OurWebThreeStubServer::shh_newIdentity()
return toJS(kp.pub()); return toJS(kp.pub());
} }
bool OurWebThreeStubServer::showAuthenticationPopup(string const& _title, string const& _text) const bool OurWebThreeStubServer::showAuthenticationPopup(string const& _title, string const& _text)
{ {
if (!m_main->confirm())
{
cnote << "Skipping confirmation step for: " << _title << "\n" << _text;
return true;
}
QMessageBox userInput; QMessageBox userInput;
userInput.setText(QString::fromStdString(_title)); userInput.setText(QString::fromStdString(_title));
userInput.setInformativeText(QString::fromStdString(_text + "\n Do you wish to allow this?")); userInput.setInformativeText(QString::fromStdString(_text));
userInput.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); userInput.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
userInput.button(QMessageBox::Ok)->setText("Allow"); userInput.button(QMessageBox::Ok)->setText("Allow");
userInput.button(QMessageBox::Cancel)->setText("Reject"); userInput.button(QMessageBox::Cancel)->setText("Reject");
userInput.setDefaultButton(QMessageBox::Cancel); userInput.setDefaultButton(QMessageBox::Cancel);
return userInput.exec() == QMessageBox::Ok; return userInput.exec() == QMessageBox::Ok;
//QMetaObject::invokeMethod(m_main, "authenticate", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, button), Q_ARG(QString, QString::fromStdString(_title)), Q_ARG(QString, QString::fromStdString(_text)));
//return button == QMessageBox::Ok;
} }
void OurWebThreeStubServer::showBasicValueTransferNotice(u256 _value) const bool OurWebThreeStubServer::showCreationNotice(TransactionSkeleton const& _t, bool _toProxy)
{ {
QMessageBox notice; return showAuthenticationPopup("Contract Creation Transaction", string("ÐApp is attemping to create a contract; ") + (_toProxy ? "(this transaction is not executed directly, but forwarded to another ÐApp) " : "") + "to be endowed with " + formatBalance(_t.value) + ", with additional network fees of up to " + formatBalance(_t.gas * _t.gasPrice) + ".\n\nMaximum total cost is " + formatBalance(_t.value + _t.gas * _t.gasPrice) + ".");
notice.setText("Basic Value Transfer Transaction");
notice.setInformativeText(QString::fromStdString("Value is " + toString(_value)));
notice.setStandardButtons(QMessageBox::Ok);
notice.exec();
} }
bool OurWebThreeStubServer::authenticate(TransactionSkeleton const& _t) bool OurWebThreeStubServer::showSendNotice(TransactionSkeleton const& _t, bool _toProxy)
{ {
h256 contractCodeHash = m_web3->ethereum()->postState().codeHash(_t.to); return showAuthenticationPopup("Fund Transfer Transaction", "ÐApp is attempting to send " + formatBalance(_t.value) + " to a recipient " + m_main->pretty(_t.to).toStdString() + (_toProxy ? " (this transaction is not executed directly, but forwarded to another ÐApp)" : "") +
if (contractCodeHash == EmptySHA3) ", with additional network fees of up to " + formatBalance(_t.gas * _t.gasPrice) + ".\n\nMaximum total cost is " + formatBalance(_t.value + _t.gas * _t.gasPrice) + ".");
// contract creation
return true;
if (false) //TODO: When is is just a value transfer?
{
// recipient has no code - nothing special about this transaction, show basic value transfer info
showBasicValueTransferNotice(_t.value);
return true;
}
string userNotice = m_main->lookupNatSpecUserNotice(contractCodeHash, _t.data);
if (userNotice.empty())
return showAuthenticationPopup("Unverified Pending Transaction",
"An undocumented transaction is about to be executed.");
QNatspecExpressionEvaluator evaluator(this, m_main);
userNotice = evaluator.evalExpression(QString::fromStdString(userNotice)).toStdString();
// otherwise it's a transaction to a contract for which we have the natspec
return showAuthenticationPopup("Pending Transaction", userNotice);
} }
QNatspecExpressionEvaluator::QNatspecExpressionEvaluator(OurWebThreeStubServer* _server, Main* _main) bool OurWebThreeStubServer::showUnknownCallNotice(TransactionSkeleton const& _t, bool _toProxy)
: m_server(_server), m_main(_main) {
{} return showAuthenticationPopup("DANGEROUS! Unknown Contract Transaction!",
"ÐApp is attempting to call into an unknown contract at address " +
m_main->pretty(_t.to).toStdString() + ".\n\n" +
(_toProxy ? "This transaction is not executed directly, but forwarded to another ÐApp.\n\n" : "") +
"Call involves sending " +
formatBalance(_t.value) + " to the recipient, with additional network fees of up to " +
formatBalance(_t.gas * _t.gasPrice) +
"However, this also does other stuff which we don't understand, and does so in your name.\n\n" +
"WARNING: This is probably going to cost you at least " +
formatBalance(_t.value + _t.gas * _t.gasPrice) +
", however this doesn't include any side-effects, which could be of far greater importance.\n\n" +
"REJECT UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!");
}
QNatspecExpressionEvaluator::~QNatspecExpressionEvaluator() void OurWebThreeStubServer::authenticate(TransactionSkeleton const& _t, bool _toProxy)
{} {
Guard l(x_queued);
m_queued.push(make_pair(_t, _toProxy));
}
QString QNatspecExpressionEvaluator::evalExpression(QString const& _expression) const void OurWebThreeStubServer::doValidations()
{ {
// load natspec.js only when we need it for natspec evaluation Guard l(x_queued);
m_main->evalRaw(contentsOfQResource(":/js/natspec.js")); while (!m_queued.empty())
QVariant result = m_main->evalRaw("evaluateExpression('" + _expression + "')");
if (result.type() == QVariant::Invalid)
{ {
cerr << "Could not evaluate natspec expression: \"" << _expression.toStdString() << "\"" << endl; auto q = m_queued.front();
// return the expression unevaluated m_queued.pop();
return _expression; if (validateTransaction(q.first, q.second))
WebThreeStubServerBase::authenticate(q.first, q.second);
} }
return result.toString();
} }
bool OurWebThreeStubServer::validateTransaction(TransactionSkeleton const& _t, bool _toProxy)
{
if (_t.creation)
{
// show notice concerning the creation code. TODO: this needs entering into natspec.
return showCreationNotice(_t, _toProxy);
}
h256 contractCodeHash = m_web3->ethereum()->postState().codeHash(_t.to);
if (contractCodeHash == EmptySHA3)
{
// recipient has no code - nothing special about this transaction, show basic value transfer info
return showSendNotice(_t, _toProxy);
}
string userNotice = m_main->natSpec()->getUserNotice(contractCodeHash, _t.data);
if (userNotice.empty())
return showUnknownCallNotice(_t, _toProxy);
NatspecExpressionEvaluator evaluator;
userNotice = evaluator.evalExpression(QString::fromStdString(userNotice)).toStdString();
// otherwise it's a transaction to a contract for which we have the natspec
return showAuthenticationPopup("Contract Transaction",
"ÐApp attempting to conduct contract interaction with " +
m_main->pretty(_t.to).toStdString() +
": <b>" + userNotice + "</b>.\n\n" +
(_toProxy ? "This transaction is not executed directly, but forwarded to another ÐApp.\n\n" : "") +
(_t.value > 0 ?
"In addition, ÐApp is attempting to send " +
formatBalance(_t.value) + " to said recipient, with additional network fees of up to " +
formatBalance(_t.gas * _t.gasPrice) + " = " +
formatBalance(_t.value + _t.gas * _t.gasPrice) + "."
:
"Additional network fees are at most" +
formatBalance(_t.gas * _t.gasPrice) + ".")
);
}

32
alethzero/OurWebThreeStubServer.h

@ -19,7 +19,9 @@
* @date 2014 * @date 2014
*/ */
#include <queue>
#include <QtCore/QObject> #include <QtCore/QObject>
#include <libdevcore/Guards.h>
#include <libethcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <libdevcrypto/Common.h> #include <libdevcrypto/Common.h>
#include <libweb3jsonrpc/WebThreeStubServer.h> #include <libweb3jsonrpc/WebThreeStubServer.h>
@ -35,31 +37,25 @@ public:
std::vector<dev::KeyPair> const& _accounts, Main* main); std::vector<dev::KeyPair> const& _accounts, Main* main);
virtual std::string shh_newIdentity() override; virtual std::string shh_newIdentity() override;
virtual bool authenticate(dev::eth::TransactionSkeleton const& _t); virtual void authenticate(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
signals: signals:
void onNewId(QString _s); void onNewId(QString _s);
private: public slots:
bool showAuthenticationPopup(std::string const& _title, std::string const& _text) const; void doValidations();
void showBasicValueTransferNotice(dev::u256 _value) const;
dev::WebThreeDirect* m_web3;
Main* m_main;
};
class QNatspecExpressionEvaluator: public QObject private:
{ bool showAuthenticationPopup(std::string const& _title, std::string const& _text);
Q_OBJECT bool showCreationNotice(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
bool showSendNotice(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
bool showUnknownCallNotice(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
public: bool validateTransaction(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
QNatspecExpressionEvaluator(OurWebThreeStubServer* _server, Main* _main);
virtual ~QNatspecExpressionEvaluator();
QString evalExpression(QString const& _expression) const; std::queue<std::pair<dev::eth::TransactionSkeleton, bool>> m_queued;
dev::Mutex x_queued;
private: dev::WebThreeDirect* m_web3;
OurWebThreeStubServer* m_server;
Main* m_main; Main* m_main;
}; };

334
alethzero/Transact.cpp

@ -0,0 +1,334 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Transact.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2015
*/
#include "Transact.h"
#include <fstream>
#include <QFileDialog>
#include <QMessageBox>
#include <liblll/Compiler.h>
#include <liblll/CodeFragment.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/AST.h>
#include <libsolidity/SourceReferenceFormatter.h>
#include <libnatspec/NatspecExpressionEvaluator.h>
#include <libethereum/Client.h>
#include <libethereum/Utility.h>
#ifndef _MSC_VER
#include <libserpent/funcs.h>
#include <libserpent/util.h>
#endif
#include "Debugger.h"
#include "ui_Transact.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
Transact::Transact(Context* _c, QWidget* _parent):
QDialog(_parent),
ui(new Ui::Transact),
m_context(_c)
{
ui->setupUi(this);
initUnits(ui->gasPriceUnits);
initUnits(ui->valueUnits);
ui->valueUnits->setCurrentIndex(6);
ui->gasPriceUnits->setCurrentIndex(4);
ui->gasPrice->setValue(10);
on_destination_currentTextChanged();
}
Transact::~Transact()
{
delete ui;
}
void Transact::setEnvironment(QList<dev::KeyPair> _myKeys, dev::eth::Client* _eth, NatSpecFace* _natSpecDB)
{
m_myKeys = _myKeys;
m_ethereum = _eth;
m_natSpecDB = _natSpecDB;
}
bool Transact::isCreation() const
{
return ui->destination->currentText().isEmpty() || ui->destination->currentText() == "(Create Contract)";
}
u256 Transact::fee() const
{
return ui->gas->value() * gasPrice();
}
u256 Transact::value() const
{
if (ui->valueUnits->currentIndex() == -1)
return 0;
return ui->value->value() * units()[units().size() - 1 - ui->valueUnits->currentIndex()].first;
}
u256 Transact::gasPrice() const
{
if (ui->gasPriceUnits->currentIndex() == -1)
return 0;
return ui->gasPrice->value() * units()[units().size() - 1 - ui->gasPriceUnits->currentIndex()].first;
}
u256 Transact::total() const
{
return value() + fee();
}
void Transact::updateDestination()
{
cwatch << "updateDestination()";
QString s;
for (auto i: ethereum()->addresses())
if ((s = m_context->pretty(i)).size())
// A namereg address
if (ui->destination->findText(s, Qt::MatchExactly | Qt::MatchCaseSensitive) == -1)
ui->destination->addItem(s);
for (int i = 0; i < ui->destination->count(); ++i)
if (ui->destination->itemText(i) != "(Create Contract)" && !m_context->fromString(ui->destination->itemText(i)))
ui->destination->removeItem(i--);
}
void Transact::updateFee()
{
ui->fee->setText(QString("(gas sub-total: %1)").arg(formatBalance(fee()).c_str()));
auto totalReq = total();
ui->total->setText(QString("Total: %1").arg(formatBalance(totalReq).c_str()));
bool ok = false;
for (auto i: m_myKeys)
if (ethereum()->balanceAt(i.address()) >= totalReq)
{
ok = true;
break;
}
ui->send->setEnabled(ok);
QPalette p = ui->total->palette();
p.setColor(QPalette::WindowText, QColor(ok ? 0x00 : 0x80, 0x00, 0x00));
ui->total->setPalette(p);
}
string Transact::getFunctionHashes(dev::solidity::CompilerStack const& _compiler, string const& _contractName)
{
string ret = "";
auto const& contract = _compiler.getContractDefinition(_contractName);
auto interfaceFunctions = contract.getInterfaceFunctions();
for (auto const& it: interfaceFunctions)
{
ret += it.first.abridged();
ret += " :";
ret += it.second->getDeclaration().getName() + "\n";
}
return ret;
}
void Transact::on_destination_currentTextChanged()
{
if (ui->destination->currentText().size() && ui->destination->currentText() != "(Create Contract)")
if (Address a = m_context->fromString(ui->destination->currentText()))
ui->calculatedName->setText(m_context->render(a));
else
ui->calculatedName->setText("Unknown Address");
else
ui->calculatedName->setText("Create Contract");
rejigData();
// updateFee();
}
void Transact::rejigData()
{
if (isCreation())
{
string src = ui->data->toPlainText().toStdString();
vector<string> errors;
QString lll;
QString solidity;
if (src.find_first_not_of("1234567890abcdefABCDEF") == string::npos && src.size() % 2 == 0)
m_data = fromHex(src);
else if (sourceIsSolidity(src))
{
dev::solidity::CompilerStack compiler;
try
{
// compiler.addSources(dev::solidity::StandardSources);
m_data = compiler.compile(src, ui->optimize->isChecked());
solidity = "<h4>Solidity</h4>";
solidity += "<pre>var " + QString::fromStdString(compiler.defaultContractName()) + " = web3.eth.contractFromAbi(" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped() + ");</pre>";
solidity += "<pre>" + QString::fromStdString(compiler.getSolidityInterface()).toHtmlEscaped() + "</pre>";
solidity += "<pre>" + QString::fromStdString(getFunctionHashes(compiler)).toHtmlEscaped() + "</pre>";
}
catch (dev::Exception const& exception)
{
ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler);
solidity = "<h4>Solidity</h4><pre>" + QString::fromStdString(error.str()).toHtmlEscaped() + "</pre>";
}
catch (...)
{
solidity = "<h4>Solidity</h4><pre>Uncaught exception.</pre>";
}
}
#ifndef _MSC_VER
else if (sourceIsSerpent(src))
{
try
{
m_data = dev::asBytes(::compile(src));
for (auto& i: errors)
i = "(LLL " + i + ")";
}
catch (string const& err)
{
errors.push_back("Serpent " + err);
}
}
#endif
else
{
m_data = compileLLL(src, ui->optimize->isChecked(), &errors);
if (errors.empty())
{
auto asmcode = compileLLLToAsm(src, false);
lll = "<h4>Pre</h4><pre>" + QString::fromStdString(asmcode).toHtmlEscaped() + "</pre>";
if (ui->optimize->isChecked())
{
asmcode = compileLLLToAsm(src, true);
lll = "<h4>Opt</h4><pre>" + QString::fromStdString(asmcode).toHtmlEscaped() + "</pre>" + lll;
}
}
}
QString errs;
if (errors.size())
{
errs = "<h4>Errors</h4>";
for (auto const& i: errors)
errs.append("<div style=\"border-left: 6px solid #c00; margin-top: 2px\">" + QString::fromStdString(i).toHtmlEscaped() + "</div>");
}
ui->code->setHtml(errs + lll + solidity + "<h4>Code</h4>" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped() + "<h4>Hex</h4>" Div(Mono) + QString::fromStdString(toHex(m_data)) + "</div>");
ui->gas->setMinimum((qint64)Interface::txGas(m_data, 0));
if (!ui->gas->isEnabled())
ui->gas->setValue(m_backupGas);
ui->gas->setEnabled(true);
}
else
{
m_data = parseData(ui->data->toPlainText().toStdString());
auto to = m_context->fromString(ui->destination->currentText());
QString natspec;
if (ethereum()->codeAt(to, 0).size())
{
string userNotice = m_natSpecDB->getUserNotice(ethereum()->postState().codeHash(to), m_data);
if (userNotice.empty())
natspec = "Destination contract unknown.";
else
{
NatspecExpressionEvaluator evaluator;
natspec = evaluator.evalExpression(QString::fromStdString(userNotice));
}
ui->gas->setMinimum((qint64)Interface::txGas(m_data, 1));
if (!ui->gas->isEnabled())
ui->gas->setValue(m_backupGas);
ui->gas->setEnabled(true);
}
else
{
natspec += "Destination not a contract.";
if (ui->gas->isEnabled())
m_backupGas = ui->gas->value();
ui->gas->setValue((qint64)Interface::txGas(m_data));
ui->gas->setEnabled(false);
}
ui->code->setHtml("<h3>NatSpec</h3>" + natspec + "<h3>Dump</h3>" + QString::fromStdString(dev::memDump(m_data, 8, true)) + "<h3>Hex</h3>" + Div(Mono) + QString::fromStdString(toHex(m_data)) + "</div>");
}
updateFee();
}
void Transact::on_send_clicked()
{
u256 totalReq = value() + fee();
for (auto const& i: m_myKeys)
if (ethereum()->balanceAt(i.address(), 0) >= totalReq)
{
Secret s = i.secret();
if (isCreation())
{
// If execution is a contract creation, add Natspec to
// a local Natspec LEVELDB
ethereum()->transact(s, value(), m_data, ui->gas->value(), gasPrice());
string src = ui->data->toPlainText().toStdString();
if (sourceIsSolidity(src))
try
{
dev::solidity::CompilerStack compiler;
m_data = compiler.compile(src, ui->optimize->isChecked());
for (string const& s: compiler.getContractNames())
{
h256 contractHash = compiler.getContractCodeHash(s);
m_natSpecDB->add(contractHash, compiler.getMetadata(s, dev::solidity::DocumentationType::NatspecUser));
}
}
catch (...)
{
}
close();
return;
}
else
ethereum()->transact(s, value(), m_context->fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice());
return;
}
QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount.");
}
void Transact::on_debug_clicked()
{
try
{
u256 totalReq = value() + fee();
for (auto i: m_myKeys)
if (ethereum()->balanceAt(i.address()) >= totalReq)
{
State st(ethereum()->postState());
Secret s = i.secret();
Transaction t = isCreation() ?
Transaction(value(), gasPrice(), ui->gas->value(), m_data, st.transactionsFrom(dev::toAddress(s)), s) :
Transaction(value(), gasPrice(), ui->gas->value(), m_context->fromString(ui->destination->currentText()), m_data, st.transactionsFrom(dev::toAddress(s)), s);
Debugger dw(m_context, this);
Executive e(st, ethereum()->blockChain(), 0);
dw.populate(e, t);
dw.exec();
return;
}
QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount.");
}
catch (dev::Exception const& _e)
{
QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction. Low-level error: " + QString::fromStdString(diagnostic_information(_e)));
// this output is aimed at developers, reconsider using _e.what for more user friendly output.
}
}

82
alethzero/Transact.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 <http://www.gnu.org/licenses/>.
*/
/** @file Transact.h
* @author Gav Wood <i@gavwood.com>
* @date 2015
*/
#pragma once
#include <libdevcore/RLP.h>
#include <libethcore/CommonEth.h>
#include <libethereum/Transaction.h>
#include <QDialog>
#include <QMap>
#include <QList>
#include "Context.h"
namespace Ui { class Transact; }
namespace dev { namespace eth { class Client; } }
namespace dev { namespace solidity { class CompilerStack; } }
class Transact: public QDialog
{
Q_OBJECT
public:
explicit Transact(Context* _context, QWidget* _parent = 0);
~Transact();
void setEnvironment(QList<dev::KeyPair> _myKeys, dev::eth::Client* _eth, NatSpecFace* _natSpecDB);
private slots:
void on_destination_currentTextChanged();
void on_value_valueChanged() { updateFee(); }
void on_gas_valueChanged() { updateFee(); }
void on_valueUnits_currentIndexChanged() { updateFee(); }
void on_gasPriceUnits_currentIndexChanged() { updateFee(); }
void on_gasPrice_valueChanged() { updateFee(); }
void on_data_textChanged() { rejigData(); }
void on_optimize_clicked() { rejigData(); }
void on_send_clicked();
void on_debug_clicked();
void on_cancel_clicked() { close(); }
private:
dev::eth::Client* ethereum() { return m_ethereum; }
void rejigData();
void updateDestination();
void updateFee();
bool isCreation() const;
dev::u256 fee() const;
dev::u256 total() const;
dev::u256 value() const;
dev::u256 gasPrice() const;
std::string getFunctionHashes(dev::solidity::CompilerStack const& _compiler, std::string const& _contractName = std::string());
Ui::Transact* ui;
unsigned m_backupGas;
dev::bytes m_data;
QList<dev::KeyPair> m_myKeys;
dev::eth::Client* m_ethereum;
Context* m_context;
NatSpecFace* m_natSpecDB;
};

244
alethzero/Transact.ui

@ -0,0 +1,244 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Transact</class>
<widget class="QDialog" name="Transact">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>543</width>
<height>695</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="1" colspan="2">
<widget class="QSpinBox" name="value">
<property name="suffix">
<string/>
</property>
<property name="maximum">
<number>430000000</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label5_2">
<property name="text">
<string>&amp;Amount</string>
</property>
<property name="buddy">
<cstring>value</cstring>
</property>
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="QLineEdit" name="calculatedName">
<property name="enabled">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="placeholderText">
<string/>
</property>
</widget>
</item>
<item row="5" column="0" colspan="4">
<widget class="QSplitter" name="splitter_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QPlainTextEdit" name="data">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
</widget>
<widget class="QTextEdit" name="code">
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</widget>
</item>
<item row="3" column="3">
<widget class="QComboBox" name="gasPriceUnits"/>
</item>
<item row="4" column="1" colspan="2">
<widget class="QLabel" name="fee">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label5">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&amp;To</string>
</property>
<property name="buddy">
<cstring>destination</cstring>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QComboBox" name="valueUnits"/>
</item>
<item row="7" column="2">
<widget class="QPushButton" name="debug">
<property name="text">
<string>&amp;Debug</string>
</property>
</widget>
</item>
<item row="7" column="3">
<widget class="QPushButton" name="send">
<property name="text">
<string>&amp;Execute</string>
</property>
<property name="default">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>&amp;Gas</string>
</property>
<property name="buddy">
<cstring>gas</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="gas">
<property name="suffix">
<string> gas</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>430000000</number>
</property>
<property name="value">
<number>10000</number>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QSpinBox" name="gasPrice">
<property name="prefix">
<string>@ </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>430000000</number>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QCheckBox" name="optimize">
<property name="text">
<string>&amp;Optimise</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>D&amp;ata</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>data</cstring>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QComboBox" name="destination">
<property name="editable">
<bool>true</bool>
</property>
<item>
<property name="text">
<string>(Create Contract)</string>
</property>
</item>
</widget>
</item>
<item row="6" column="0" colspan="4">
<widget class="QLabel" name="total">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QPushButton" name="cancel">
<property name="text">
<string>&amp;Cancel</string>
</property>
<property name="shortcut">
<string>Esc</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

3
cmake/EthCompilerSettings.cmake

@ -27,13 +27,14 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# enable parallel compilation
# specify Exception Handling Model in msvc # specify Exception Handling Model in msvc
# disable unknown pragma warning (4068) # disable unknown pragma warning (4068)
# disable unsafe function warning (4996) # disable unsafe function warning (4996)
# disable decorated name length exceeded, name was truncated (4503) # disable decorated name length exceeded, name was truncated (4503)
# disable warning C4535: calling _set_se_translator() requires /EHa (for boost tests) # disable warning C4535: calling _set_se_translator() requires /EHa (for boost tests)
# declare Windows XP requirement # declare Windows XP requirement
add_compile_options(/EHsc /wd4068 /wd4996 /wd4503 -D_WIN32_WINNT=0x0501) add_compile_options(/MP /EHsc /wd4068 /wd4996 /wd4503 -D_WIN32_WINNT=0x0501)
# disable empty object file warning # disable empty object file warning
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221") set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221")
# warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification # warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification

21
cmake/EthDependencies.cmake

@ -7,6 +7,14 @@ string(TOLOWER ${CMAKE_SYSTEM_NAME} _system_name)
set (ETH_DEPENDENCY_INSTALL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/extdep/install/${_system_name}") set (ETH_DEPENDENCY_INSTALL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/extdep/install/${_system_name}")
set (CMAKE_PREFIX_PATH ${ETH_DEPENDENCY_INSTALL_DIR}) set (CMAKE_PREFIX_PATH ${ETH_DEPENDENCY_INSTALL_DIR})
# setup directory for cmake generated files and include it globally
# it's not used yet, but if we have more generated files, consider moving them to ETH_GENERATED_DIR
set(ETH_GENERATED_DIR "${PROJECT_BINARY_DIR}/gen")
include_directories(${ETH_GENERATED_DIR})
# custom cmake scripts
set(ETH_SCRIPTS_DIR ${CMAKE_SOURCE_DIR}/cmake/scripts)
# Qt5 requires opengl # Qt5 requires opengl
# TODO use proper version of windows SDK (32 vs 64) # TODO use proper version of windows SDK (32 vs 64)
# TODO make it possible to use older versions of windows SDK (7.0+ should also work) # TODO make it possible to use older versions of windows SDK (7.0+ should also work)
@ -51,6 +59,10 @@ if (JSONRPC)
message (" - json-rpc-cpp lib : ${JSON_RPC_CPP_LIBRARIES}") message (" - json-rpc-cpp lib : ${JSON_RPC_CPP_LIBRARIES}")
add_definitions(-DETH_JSONRPC) add_definitions(-DETH_JSONRPC)
find_package(MHD)
message(" - microhttpd header: ${MHD_INCLUDE_DIRS}")
message(" - microhttpd lib : ${MHD_LIBRARIES}")
endif() #JSONRPC endif() #JSONRPC
# TODO readline package does not yet check for correct version number # TODO readline package does not yet check for correct version number
@ -87,6 +99,10 @@ find_package (CURL)
message(" - curl header: ${CURL_INCLUDE_DIRS}") message(" - curl header: ${CURL_INCLUDE_DIRS}")
message(" - curl lib : ${CURL_LIBRARIES}") message(" - curl lib : ${CURL_LIBRARIES}")
# find location of jsonrpcstub
find_program(ETH_JSON_RPC_STUB jsonrpcstub)
message(" - jsonrpcstub location : ${ETH_JSON_RPC_STUB}")
# do not compile GUI # do not compile GUI
if (NOT HEADLESS) if (NOT HEADLESS)
@ -112,6 +128,11 @@ if (NOT HEADLESS)
set (MACDEPLOYQT_APP ${Qt5Core_DIR}/../../../bin/macdeployqt) set (MACDEPLOYQT_APP ${Qt5Core_DIR}/../../../bin/macdeployqt)
message(" - macdeployqt path: ${MACDEPLOYQT_APP}") message(" - macdeployqt path: ${MACDEPLOYQT_APP}")
endif() endif()
# we need to find path to windeployqt on windows
if (WIN32)
set (WINDEPLOYQT_APP ${Qt5Core_DIR}/../../../bin/windeployqt)
message(" - windeployqt path: ${WINDEPLOYQT_APP}")
endif()
# TODO check node && npm version # TODO check node && npm version
find_program(ETH_NODE node) find_program(ETH_NODE node)

52
cmake/EthExecutableHelper.cmake

@ -56,14 +56,14 @@ macro(eth_install_executable EXECUTABLE)
set (extra_macro_args ${ARGN}) set (extra_macro_args ${ARGN})
set (options) set (options)
set (one_value_args QMLDIR) set (one_value_args QMLDIR)
set (multi_value_args) set (multi_value_args DLLS)
cmake_parse_arguments (ETH_INSTALL_EXECUTABLE "${options}" "${one_value_args}" "${multi_value_args}" "${extra_macro_args}") cmake_parse_arguments (ETH_INSTALL_EXECUTABLE "${options}" "${one_value_args}" "${multi_value_args}" "${extra_macro_args}")
if (ETH_INSTALL_EXECUTABLE_QMLDIR) if (ETH_INSTALL_EXECUTABLE_QMLDIR)
if (APPLE) if (APPLE)
set(eth_qml_dir "-qmldir=${ETH_INSTALL_EXECUTABLE_QMLDIR}") set(eth_qml_dir "-qmldir=${ETH_INSTALL_EXECUTABLE_QMLDIR}")
elseif (WIN32) elseif (WIN32)
set(eth_qml_dir --qmldir ${ETH_INSTALL_EXECUTABLE_QMLDIR}) set(eth_qml_dir "--qmldir ${ETH_INSTALL_EXECUTABLE_QMLDIR}")
endif() endif()
message(STATUS "${EXECUTABLE} qmldir: ${eth_qml_dir}") message(STATUS "${EXECUTABLE} qmldir: ${eth_qml_dir}")
endif() endif()
@ -88,48 +88,30 @@ macro(eth_install_executable EXECUTABLE)
set(BU_CHMOD_BUNDLE_ITEMS 1) set(BU_CHMOD_BUNDLE_ITEMS 1)
verify_app(\"${APP_BUNDLE_PATH}\") verify_app(\"${APP_BUNDLE_PATH}\")
" COMPONENT RUNTIME ) " COMPONENT RUNTIME )
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# copy all dlls to executable directory elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# TODO improve that by copying only required dlls
file (GLOB DLLS ${ETH_DEPENDENCY_INSTALL_DIR}/bin/*.dll)
foreach(DLL ${DLLS}) get_target_property(TARGET_LIBS ${EXECUTABLE} INTERFACE_LINK_LIBRARIES)
string(REGEX MATCH "Qt5::Core" HAVE_QT ${TARGET_LIBS})
if ("${HAVE_QT}" STREQUAL "Qt5::Core")
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND cmake -E copy "${DLL}" "$<TARGET_FILE_DIR:${EXECUTABLE}>" COMMAND cmd /C "set PATH=${Qt5Core_DIR}/../../../bin;%PATH% && ${WINDEPLOYQT_APP} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.exe ${eth_qml_dir}"
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
) )
endforeach() #workaround for https://bugreports.qt.io/browse/QTBUG-42083
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND cmake -E copy_directory COMMAND cmd /C "(echo [Paths] & echo.Prefix=.)" > "qt.conf"
"${ETH_DEPENDENCY_INSTALL_DIR}/plugins/platforms" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} VERBATIM
$<TARGET_FILE_DIR:${EXECUTABLE}>/platforms
) )
endif()
# ugly way, improve that #copy additional dlls
foreach(dll ${ETH_INSTALL_EXECUTABLE_DLLS})
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND cmake -E copy_directory COMMAND ${CMAKE_COMMAND}
"${ETH_DEPENDENCY_INSTALL_DIR}/qml" ARGS -E copy ${dll} "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}"
$<TARGET_FILE_DIR:${EXECUTABLE}>
)
install( FILES ${DLLS}
DESTINATION bin
COMPONENT ${EXECUTABLE}
)
install( DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/plugins/platforms
DESTINATION bin
COMPONENT ${EXECUTABLE}
)
file (GLOB QMLS ${ETH_DEPENDENCY_INSTALL_DIR}/qml/*)
foreach(QML ${QMLS})
install( DIRECTORY ${QML}
DESTINATION bin
COMPONENT ${EXECUTABLE}
) )
endforeach() endforeach(dll)
install( TARGETS ${EXECUTABLE} RUNTIME install( TARGETS ${EXECUTABLE} RUNTIME
DESTINATION bin DESTINATION bin

24
cmake/EthUtils.cmake

@ -0,0 +1,24 @@
#
# renames the file if it is different from its destination
include(CMakeParseArguments)
#
macro(replace_if_different SOURCE DST)
set(extra_macro_args ${ARGN})
set(options CREATE)
set(one_value_args)
set(multi_value_args)
cmake_parse_arguments(REPLACE_IF_DIFFERENT "${options}" "${one_value_args}" "${multi_value_args}" "${extra_macro_args}")
if (REPLACE_IF_DIFFERENT_CREATE AND (NOT (EXISTS "${DST}")))
file(WRITE "${DST}" "")
endif()
execute_process(COMMAND ${CMAKE_COMMAND} -E compare_files "${SOURCE}" "${DST}" RESULT_VARIABLE DIFFERENT)
if (DIFFERENT)
execute_process(COMMAND ${CMAKE_COMMAND} -E rename "${SOURCE}" "${DST}")
else()
execute_process(COMMAND ${CMAKE_COMMAND} -E remove "${SOURCE}")
endif()
endmacro()

3
cmake/FindJsoncpp.cmake

@ -12,7 +12,8 @@
# only look in default directories # only look in default directories
find_path( find_path(
JSONCPP_INCLUDE_DIR JSONCPP_INCLUDE_DIR
NAMES jsoncpp/json/json.h NAMES json/json.h
PATH_SUFFIXES jsoncpp
DOC "jsoncpp include dir" DOC "jsoncpp include dir"
) )

53
cmake/FindMHD.cmake

@ -0,0 +1,53 @@
# Find microhttpd
#
# Find the microhttpd includes and library
#
# if you need to add a custom library search path, do it via via CMAKE_PREFIX_PATH
#
# This module defines
# MHD_INCLUDE_DIRS, where to find header, etc.
# MHD_LIBRARIES, the libraries needed to use jsoncpp.
# MHD_FOUND, If false, do not try to use jsoncpp.
find_path(
MHD_INCLUDE_DIR
NAMES microhttpd.h
DOC "microhttpd include dir"
)
find_library(
MHD_LIBRARY
NAMES microhttpd microhttpd-10 libmicrohttpd libmicrohttpd-dll
DOC "microhttpd library"
)
set(MHD_INCLUDE_DIRS ${MHD_INCLUDE_DIR})
set(MHD_LIBRARIES ${MHD_LIBRARY})
# debug library on windows
# same naming convention as in QT (appending debug library with d)
# boost is using the same "hack" as us with "optimized" and "debug"
# official MHD project actually uses _d suffix
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
#TODO: place dlls into CMAKE_CFG_INTDIR subfolders
string(REPLACE ".lib" ".dll" MHD_DLL_RELEASE ${MHD_LIBRARY})
string(REPLACE "/lib/" "/bin/" MHD_DLL_RELEASE ${MHD_DLL_RELEASE})
find_library(
MHD_LIBRARY_DEBUG
NAMES microhttpd_d microhttpd-10_d libmicrohttpd_d libmicrohttpd-dll_d
DOC "mhd debug library"
)
# always use release for now
#string(REPLACE ".lib" ".dll" MHD_DLL_DEBUG ${MHD_LIBRARY_DEBUG})
#set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG})
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(mhd DEFAULT_MSG
MHD_INCLUDE_DIR MHD_LIBRARY)
mark_as_advanced(MHD_INCLUDE_DIR MHD_LIBRARY)

48
cmake/scripts/buildinfo.cmake

@ -0,0 +1,48 @@
# generates BuildInfo.h
#
# this module expects
# ETH_SOURCE_DIR - main CMAKE_SOURCE_DIR
# ETH_DST_DIR - main CMAKE_BINARY_DIR
# ETH_BUILD_TYPE
# ETH_BUILD_PLATFORM
#
# example usage:
# cmake -DETH_SOURCE_DIR=. -DETH_DST_DIR=build -DETH_BUILD_TYPE=Debug -DETH_BUILD_PLATFORM=mac -P scripts/buildinfo.cmake
if (NOT ETH_BUILD_TYPE)
set(ETH_BUILD_TYPE "unknown")
endif()
if (NOT ETH_BUILD_PLATFORM)
set(ETH_BUILD_PLATFORM "unknown")
endif()
execute_process(
COMMAND git --git-dir=${ETH_SOURCE_DIR}/.git --work-tree=${ETH_SOURCE_DIR} rev-parse HEAD
OUTPUT_VARIABLE ETH_COMMIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET
)
if (NOT ETH_COMMIT_HASH)
set(ETH_COMMIT_HASH 0)
endif()
execute_process(
COMMAND git --git-dir=${ETH_SOURCE_DIR}/.git --work-tree=${ETH_SOURCE_DIR} diff --shortstat
OUTPUT_VARIABLE ETH_LOCAL_CHANGES OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET
)
if (ETH_LOCAL_CHANGES)
set(ETH_CLEAN_REPO 0)
else()
set(ETH_CLEAN_REPO 1)
endif()
set(INFILE "${ETH_SOURCE_DIR}/BuildInfo.h.in")
set(TMPFILE "${ETH_DST_DIR}/BuildInfo.h.tmp")
set(OUTFILE "${ETH_DST_DIR}/BuildInfo.h")
configure_file("${INFILE}" "${TMPFILE}")
include("${ETH_SOURCE_DIR}/cmake/EthUtils.cmake")
replace_if_different("${TMPFILE}" "${OUTFILE}" CREATE)

14
cmake/scripts/configure.cmake

@ -0,0 +1,14 @@
# adds possibility to run configure_file as buildstep
# reference:
# http://www.cmake.org/pipermail/cmake/2012-May/050227.html
#
# This module expects
# INFILE
# OUTFILE
# other custom vars
#
# example usage:
# cmake -DINFILE=blah.in -DOUTFILE=blah.out -Dvar1=value1 -Dvar2=value2 -P scripts/configure.cmake
configure_file(${INFILE} ${OUTFILE})

45
cmake/scripts/jsonrpcstub.cmake

@ -0,0 +1,45 @@
# generates JSONRPC Stub Server && Client
#
# this script expects
# ETH_SOURCE_DIR - main CMAKE_SOURCE_DIR
# ETH_SPEC_PATH
# ETH_SERVER_DIR
# ETH_CLIENT_DIR
# ETH_SERVER_NAME
# ETH_CLIENT_NAME
# ETH_JSON_RPC_STUB
#
# example usage:
# cmake -DETH_SPEC_PATH=spec.json -DETH_SERVER_DIR=libweb3jsonrpc -DETH_CLIENT_DIR=test
# -DETH_SERVER_NAME=AbstractWebThreeStubServer -DETH_CLIENT_NAME=WebThreeStubClient -DETH_JSON_RPC_STUB=/usr/local/bin/jsonrpcstub
# by default jsonrpcstub produces files in lowercase, we want to stick to this
string(TOLOWER ${ETH_SERVER_NAME} ETH_SERVER_NAME_LOWER)
string(TOLOWER ${ETH_CLIENT_NAME} ETH_CLIENT_NAME_LOWER)
# setup names
set(SERVER_TMPFILE "${ETH_SERVER_DIR}/${ETH_SERVER_NAME_LOWER}.h.tmp")
set(SERVER_OUTFILE "${ETH_SERVER_DIR}/${ETH_SERVER_NAME_LOWER}.h")
set(CLIENT_TMPFILE "${ETH_CLIENT_DIR}/${ETH_CLIENT_NAME_LOWER}.h.tmp")
set(CLIENT_OUTFILE "${ETH_CLIENT_DIR}/${ETH_CLIENT_NAME_LOWER}.h")
# create tmp files
execute_process(
COMMAND ${ETH_JSON_RPC_STUB} ${ETH_SPEC_PATH}
--cpp-server=${ETH_SERVER_NAME} --cpp-server-file=${SERVER_TMPFILE}
--cpp-client=${ETH_CLIENT_NAME} --cpp-client-file=${CLIENT_TMPFILE}
OUTPUT_VARIABLE ERR ERROR_QUIET
)
# don't throw fatal error on jsonrpcstub error, someone might have old version of jsonrpcstub,
# he does not need to upgrade it if he is not working on JSON RPC
# show him warning instead
if (ERR)
message(WARNING "Your version of jsonrcpstub tool is not supported. Please upgrade it.")
message(WARNING "${ERR}")
else()
include("${ETH_SOURCE_DIR}/cmake/EthUtils.cmake")
replace_if_different("${SERVER_TMPFILE}" "${SERVER_OUTFILE}")
replace_if_different("${CLIENT_TMPFILE}" "${CLIENT_OUTFILE}")
endif()

7
eth/CMakeLists.txt

@ -3,9 +3,9 @@ set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST) aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
include_directories(..)
set(EXECUTABLE eth) set(EXECUTABLE eth)
@ -16,7 +16,6 @@ add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
add_dependencies(${EXECUTABLE} BuildInfo.h) add_dependencies(${EXECUTABLE} BuildInfo.h)
target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${Boost_DATE_TIME_LIBRARIES})
if (READLINE_FOUND) if (READLINE_FOUND)
target_link_libraries(${EXECUTABLE} ${READLINE_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${READLINE_LIBRARIES})
@ -29,5 +28,9 @@ endif()
target_link_libraries(${EXECUTABLE} webthree) target_link_libraries(${EXECUTABLE} webthree)
target_link_libraries(${EXECUTABLE} secp256k1) target_link_libraries(${EXECUTABLE} secp256k1)
if (WIN32)
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 ) install( TARGETS ${EXECUTABLE} DESTINATION bin )

98
eth/main.cpp

@ -119,6 +119,7 @@ void help()
<< " -p,--port <port> Connect to remote port (default: 30303)." << endl << " -p,--port <port> Connect to remote port (default: 30303)." << endl
<< " -r,--remote <host> Connect to remote host (default: none)." << endl << " -r,--remote <host> Connect to remote host (default: none)." << endl
<< " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl << " -s,--secret <secretkeyhex> Set the secret key for use with send command (default: auto)." << endl
<< " -t,--miners <number> Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl
<< " -u,--public-ip <ip> Force public ip to given (default; auto)." << endl << " -u,--public-ip <ip> Force public ip to given (default; auto)." << endl
<< " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (Default: 8)." << endl << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (Default: 8)." << endl
<< " -x,--peers <number> Attempt to connect to given number of peers (Default: 5)." << endl << " -x,--peers <number> Attempt to connect to given number of peers (Default: 5)." << endl
@ -195,6 +196,7 @@ int main(int argc, char** argv)
unsigned mining = ~(unsigned)0; unsigned mining = ~(unsigned)0;
NodeMode mode = NodeMode::Full; NodeMode mode = NodeMode::Full;
unsigned peers = 5; unsigned peers = 5;
int miners = -1;
bool interactive = false; bool interactive = false;
#if ETH_JSONRPC #if ETH_JSONRPC
int jsonrpc = -1; int jsonrpc = -1;
@ -258,7 +260,23 @@ int main(int argc, char** argv)
else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc) else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc)
clientName = argv[++i]; clientName = argv[++i];
else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc) else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc)
coinbase = h160(fromHex(argv[++i])); {
try
{
coinbase = h160(fromHex(argv[++i], ThrowType::Throw));
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, coinbase rejected";
cwarn << boost::diagnostic_information(_e);
break;
}
catch (...)
{
cwarn << "coinbase rejected";
break;
}
}
else if ((arg == "-s" || arg == "--secret") && i + 1 < argc) else if ((arg == "-s" || arg == "--secret") && i + 1 < argc)
us = KeyPair(h256(fromHex(argv[++i]))); us = KeyPair(h256(fromHex(argv[++i])));
else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc) else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc)
@ -295,6 +313,8 @@ int main(int argc, char** argv)
g_logVerbosity = atoi(argv[++i]); g_logVerbosity = atoi(argv[++i]);
else if ((arg == "-x" || arg == "--peers") && i + 1 < argc) else if ((arg == "-x" || arg == "--peers") && i + 1 < argc)
peers = atoi(argv[++i]); 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) else if ((arg == "-o" || arg == "--mode") && i + 1 < argc)
{ {
string m = argv[++i]; string m = argv[++i];
@ -332,12 +352,15 @@ int main(int argc, char** argv)
VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter);
NetworkPreferences netPrefs(listenPort, publicIP, upnp, useLocal); NetworkPreferences netPrefs(listenPort, publicIP, upnp, useLocal);
auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp");
dev::WebThreeDirect web3( dev::WebThreeDirect web3(
"Ethereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""), "Ethereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""),
dbPath, dbPath,
false, false,
mode == NodeMode::Full ? set<string>{"eth", "shh"} : set<string>(), mode == NodeMode::Full ? set<string>{"eth", "shh"} : set<string>(),
netPrefs netPrefs,
&nodesState,
miners
); );
web3.setIdealPeerCount(peers); web3.setIdealPeerCount(peers);
eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr; eth::Client* c = mode == NodeMode::Full ? web3.ethereum() : nullptr;
@ -348,9 +371,6 @@ int main(int argc, char** argv)
c->setAddress(coinbase); c->setAddress(coinbase);
} }
auto nodesState = contents((dbPath.size() ? dbPath : getDataDir()) + "/nodeState.rlp");
web3.restoreNodes(&nodesState);
cout << "Address: " << endl << toHex(us.address().asArray()) << endl; cout << "Address: " << endl << toHex(us.address().asArray()) << endl;
web3.startNetwork(); web3.startNetwork();
@ -531,11 +551,23 @@ int main(int argc, char** argv)
cwarn << "Invalid secret length:" << ssize; cwarn << "Invalid secret length:" << ssize;
} }
else else
{
try
{ {
Secret secret = h256(fromHex(sechex)); Secret secret = h256(fromHex(sechex));
Address dest = h160(fromHex(hexAddr)); Address dest = h160(fromHex(hexAddr));
c->transact(secret, amount, dest, data, gas, gasPrice); c->transact(secret, amount, dest, data, gas, gasPrice);
} }
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
}
} }
else else
cwarn << "Require parameters: transact ADDRESS AMOUNT GASPRICE GAS SECRET DATA"; cwarn << "Require parameters: transact ADDRESS AMOUNT GASPRICE GAS SECRET DATA";
@ -583,9 +615,21 @@ int main(int argc, char** argv)
auto blockData = bc.block(h); auto blockData = bc.block(h);
BlockInfo info(blockData); BlockInfo info(blockData);
u256 minGas = (u256)Client::txGas(bytes(), 0); u256 minGas = (u256)Client::txGas(bytes(), 0);
Address dest = h160(fromHex(hexAddr)); try
{
Address dest = h160(fromHex(hexAddr, ThrowType::Throw));
c->transact(us.secret(), amount, dest, bytes(), minGas); c->transact(us.secret(), amount, dest, bytes(), minGas);
} }
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
}
} }
else else
cwarn << "Require parameters: send ADDRESS AMOUNT"; cwarn << "Require parameters: send ADDRESS AMOUNT";
@ -615,14 +659,30 @@ int main(int argc, char** argv)
{ {
cnote << "Assembled:"; cnote << "Assembled:";
stringstream ssc; stringstream ssc;
init = fromHex(sinit); try
{
init = fromHex(sinit, ThrowType::Throw);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, code rejected";
cwarn << boost::diagnostic_information(_e);
init = bytes();
}
catch (...)
{
cwarn << "code rejected";
init = bytes();
}
ssc.str(string()); ssc.str(string());
ssc << disassemble(init); ssc << disassemble(init);
cnote << "Init:"; cnote << "Init:";
cnote << ssc.str(); cnote << ssc.str();
} }
u256 minGas = (u256)Client::txGas(init, 0); u256 minGas = (u256)Client::txGas(init, 0);
if (endowment < 0) if (!init.size())
cwarn << "Contract creation aborted, no init code.";
else if (endowment < 0)
cwarn << "Invalid endowment"; cwarn << "Invalid endowment";
else if (gas < minGas) else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas; cwarn << "Minimum gas amount is" << minGas;
@ -689,7 +749,7 @@ int main(int argc, char** argv)
else if (format == "standard+") else if (format == "standard+")
oof = [&](uint64_t, Instruction instr, bigint, bigint, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM) oof = [&](uint64_t, Instruction instr, bigint, bigint, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{ {
dev::eth::VM* vm = (VM*)vvm; dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM); dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
if (instr == Instruction::STOP || instr == Instruction::RETURN || instr == Instruction::SUICIDE) if (instr == Instruction::STOP || instr == Instruction::RETURN || instr == Instruction::SUICIDE)
for (auto const& i: ext->state().storage(ext->myAddress)) for (auto const& i: ext->state().storage(ext->myAddress))
@ -759,7 +819,21 @@ int main(int argc, char** argv)
if (hexAddr.length() != 40) if (hexAddr.length() != 40)
cwarn << "Invalid address length: " << hexAddr.length(); cwarn << "Invalid address length: " << hexAddr.length();
else else
coinbase = h160(fromHex(hexAddr)); {
try
{
coinbase = h160(fromHex(hexAddr, ThrowType::Throw));
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, coinbase rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "coinbase rejected";
}
}
} }
else else
cwarn << "Require parameter: setAddress HEXADDRESS"; cwarn << "Require parameter: setAddress HEXADDRESS";
@ -824,7 +898,9 @@ int main(int argc, char** argv)
while (!g_exit) while (!g_exit)
this_thread::sleep_for(chrono::milliseconds(1000)); this_thread::sleep_for(chrono::milliseconds(1000));
writeFile((dbPath.size() ? dbPath : getDataDir()) + "/nodeState.rlp", web3.saveNodes()); auto netData = web3.saveNetwork();
if (!netData.empty())
writeFile((dbPath.size() ? dbPath : getDataDir()) + "/network.rlp", netData);
return 0; return 0;
} }

7
evmjit/CMakeLists.txt

@ -3,13 +3,14 @@ cmake_minimum_required(VERSION 2.8.12)
project(evmjit) project(evmjit)
set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_AUTOMOC OFF)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
else() else()
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -fPIC") set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-unknown-pragmas")
endif() endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
# Do not allow unresovled symbols in shared library (default on linux) # Do not allow unresovled symbols in shared library (default on linux)
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined")
endif() endif()

21
evmjit/LICENSE.md

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Paweł Bylica <chfast@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

43
evmjit/README.md

@ -0,0 +1,43 @@
# The Ethereum EVM JIT
EVM JIT is a library for just-in-time compilation of Ethereum EVM code.
It can be used to substitute classic interpreter-like EVM Virtual Machine in Ethereum client.
## Build
### Linux / Ubuntu
1. Install llvm-3.5-dev package
1. For Ubuntu 14.04 using LLVM deb packages source: http://llvm.org/apt
2. For Ubuntu 14.10 using Ubuntu packages
2. Build library with cmake
1. `mkdir build && cd $_`
2. `cmake .. && make`
3. Install library
1. `sudo make install`
2. `sudo ldconfig`
### OSX
1. Install llvm35
1. `brew install llvm35 --disable-shared --HEAD`
2. Build library with cmake
1. `mkdir build && cd $_`
2. `cmake -DLLVM_DIR=/usr/local/lib/llvm-3.5/share/llvm/cmake .. && make`
3. Install library
1. `make install` (with admin rights?)
### Windows
Ask me.
## Options
Options to evmjit library can be passed by environmental variables, e.g. `EVMJIT_CACHE=0 testeth --jit`.
Option | Default value | Description
------------- | ------------- | ----------------------------------------------
EVMJIT_CACHE | 1 | Enables on disk cache for compiled EVM objects
EVMJIT_DUMP | 0 | Dumps generated LLVM module to standard output

5
evmjit/libevmjit-cpp/CMakeLists.txt

@ -9,6 +9,11 @@ set(SOURCES
) )
source_group("" FILES ${SOURCES}) source_group("" FILES ${SOURCES})
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") # add PIC for archive
endif()
add_library(${TARGET_NAME} ${SOURCES}) add_library(${TARGET_NAME} ${SOURCES})
set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs") set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs")

21
evmjit/libevmjit-cpp/Env.cpp

@ -1,4 +1,5 @@
#pragma GCC diagnostic ignored "-Wconversion"
#include <libdevcrypto/SHA3.h> #include <libdevcrypto/SHA3.h>
#include <libevm/FeeStructure.h> #include <libevm/FeeStructure.h>
#include <libevm/ExtVMFace.h> #include <libevm/ExtVMFace.h>
@ -46,23 +47,22 @@ extern "C"
*o_hash = _env->blockhash(llvm2eth(*_number)); *o_hash = _env->blockhash(llvm2eth(*_number));
} }
EXPORT void env_create(ExtVMFace* _env, i256* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address) EXPORT void env_create(ExtVMFace* _env, int64_t* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address)
{ {
auto endowment = llvm2eth(*_endowment); auto endowment = llvm2eth(*_endowment);
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024) if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024)
{ {
_env->subBalance(endowment); _env->subBalance(endowment);
auto gas = llvm2eth(*io_gas); u256 gas = *io_gas;
OnOpFunc onOp {}; // TODO: Handle that thing h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, {}), h256::AlignRight);
h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, onOp), h256::AlignRight); *io_gas = static_cast<int64_t>(gas);
*io_gas = eth2llvm(gas);
*o_address = address; *o_address = address;
} }
else else
*o_address = {}; *o_address = {};
} }
EXPORT bool env_call(ExtVMFace* _env, i256* io_gas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress) EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress)
{ {
auto value = llvm2eth(*_value); auto value = llvm2eth(*_value);
if (_env->balance(_env->myAddress) >= value && _env->depth < 1024) if (_env->balance(_env->myAddress) >= value && _env->depth < 1024)
@ -70,12 +70,11 @@ extern "C"
_env->subBalance(value); _env->subBalance(value);
auto receiveAddress = right160(*_receiveAddress); auto receiveAddress = right160(*_receiveAddress);
auto inRef = bytesConstRef{_inBeg, _inSize}; auto inRef = bytesConstRef{_inBeg, _inSize};
auto outRef = bytesConstRef{_outBeg, _outSize}; auto outRef = bytesRef{_outBeg, _outSize};
OnOpFunc onOp {}; // TODO: Handle that thing
auto codeAddress = right160(*_codeAddress); auto codeAddress = right160(*_codeAddress);
auto gas = llvm2eth(*io_gas); u256 gas = *io_gas;
auto ret = _env->call(receiveAddress, value, inRef, gas, outRef, onOp, {}, codeAddress); auto ret = _env->call(receiveAddress, value, inRef, gas, outRef, {}, {}, codeAddress);
*io_gas = eth2llvm(gas); *io_gas = static_cast<int64_t>(gas);
return ret; return ret;
} }

34
evmjit/libevmjit-cpp/JitVM.cpp

@ -1,6 +1,9 @@
#pragma GCC diagnostic ignored "-Wconversion"
#include "JitVM.h" #include "JitVM.h"
#include <libevm/VM.h> #include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include <libdevcrypto/SHA3.h>
#include <evmjit/libevmjit/ExecutionEngine.h> #include <evmjit/libevmjit/ExecutionEngine.h>
#include "Utils.h" #include "Utils.h"
@ -11,22 +14,26 @@ namespace eth
extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below
bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t) bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
{ {
using namespace jit; using namespace jit;
if (m_gas > std::numeric_limits<decltype(m_data.gas)>::max()) auto rejected = false;
BOOST_THROW_EXCEPTION(OutOfGas()); // Do not accept requests with gas > 2^63 (int64 max) // TODO: Return "not accepted" exception to allow interpreted handle that // TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope
rejected |= m_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
if (_ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max()) rejected |= _ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max();
BOOST_THROW_EXCEPTION(OutOfGas()); rejected |= _ext.currentBlock.number > std::numeric_limits<decltype(m_data.number)>::max();
rejected |= _ext.currentBlock.timestamp > std::numeric_limits<decltype(m_data.timestamp)>::max();
if (_ext.currentBlock.number > std::numeric_limits<decltype(m_data.number)>::max())
BOOST_THROW_EXCEPTION(OutOfGas());
if (_ext.currentBlock.timestamp > std::numeric_limits<decltype(m_data.timestamp)>::max())
BOOST_THROW_EXCEPTION(OutOfGas());
if (rejected)
{
UNTESTED;
std::cerr << "Rejected\n";
VMFactory::setKind(VMKind::Interpreter);
m_fallbackVM = VMFactory::create(m_gas);
VMFactory::setKind(VMKind::JIT);
return m_fallbackVM->go(_ext, _onOp, _step);
}
m_data.gas = static_cast<decltype(m_data.gas)>(m_gas); m_data.gas = static_cast<decltype(m_data.gas)>(m_gas);
m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice); m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice);
@ -43,9 +50,10 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t)
m_data.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.currentBlock.timestamp); m_data.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.currentBlock.timestamp);
m_data.code = _ext.code.data(); m_data.code = _ext.code.data();
m_data.codeSize = _ext.code.size(); m_data.codeSize = _ext.code.size();
m_data.codeHash = eth2llvm(sha3(_ext.code));
auto env = reinterpret_cast<Env*>(&_ext); auto env = reinterpret_cast<Env*>(&_ext);
auto exitCode = m_engine.run(_ext.code, &m_data, env); auto exitCode = m_engine.run(&m_data, env);
switch (exitCode) switch (exitCode)
{ {
case ReturnCode::Suicide: case ReturnCode::Suicide:

4
evmjit/libevmjit-cpp/JitVM.h

@ -12,15 +12,13 @@ class JitVM: public VMFace
{ {
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
enum Kind: bool { Interpreter, JIT };
static std::unique_ptr<VMFace> create(Kind, u256 _gas = 0);
private: private:
friend class VMFactory; friend class VMFactory;
explicit JitVM(u256 _gas = 0) : VMFace(_gas) {} explicit JitVM(u256 _gas = 0) : VMFace(_gas) {}
jit::RuntimeData m_data; jit::RuntimeData m_data;
jit::ExecutionEngine m_engine; jit::ExecutionEngine m_engine;
std::unique_ptr<VMFace> m_fallbackVM; ///< VM used in case of input data rejected by JIT
}; };

328
evmjit/libevmjit/Arith256.cpp

@ -1,11 +1,14 @@
#include "Arith256.h" #include "Arith256.h"
#include "Runtime.h"
#include "Type.h"
#include "Endianness.h"
#include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h>
#include <iostream> #include <iostream>
#include <iomanip>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "Type.h"
#include "Endianness.h"
namespace dev namespace dev
{ {
@ -16,29 +19,102 @@ namespace jit
Arith256::Arith256(llvm::IRBuilder<>& _builder) : Arith256::Arith256(llvm::IRBuilder<>& _builder) :
CompilerHelper(_builder) CompilerHelper(_builder)
{ {}
using namespace llvm;
m_result = m_builder.CreateAlloca(Type::Word, nullptr, "arith.result"); void Arith256::debug(llvm::Value* _value, char _c)
m_arg1 = m_builder.CreateAlloca(Type::Word, nullptr, "arith.arg1"); {
m_arg2 = m_builder.CreateAlloca(Type::Word, nullptr, "arith.arg2"); if (!m_debug)
m_arg3 = m_builder.CreateAlloca(Type::Word, nullptr, "arith.arg3"); {
llvm::Type* argTypes[] = {Type::Word, m_builder.getInt8Ty()};
m_debug = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "debug", getModule());
}
createCall(m_debug, {m_builder.CreateZExtOrTrunc(_value, Type::Word), m_builder.getInt8(_c)});
}
using Linkage = GlobalValue::LinkageTypes; llvm::Function* Arith256::getMulFunc()
{
auto& func = m_mul;
if (!func)
{
llvm::Type* argTypes[] = {Type::Word, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mul", getModule());
llvm::Type* arg2Types[] = {Type::WordPtr, Type::WordPtr, Type::WordPtr}; auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
m_mul = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_mul", getModule()); InsertPointGuard guard{m_builder};
auto bb = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
m_builder.SetInsertPoint(bb);
auto i64 = Type::Size;
auto i128 = m_builder.getIntNTy(128);
auto i256 = Type::Word;
auto x_lo = m_builder.CreateTrunc(x, i64, "x.lo");
auto y_lo = m_builder.CreateTrunc(y, i64, "y.lo");
auto x_mi = m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(64)), i64);
auto y_mi = m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(64)), i64);
auto x_hi = m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(128)), i128);
auto y_hi = m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(128)), i128);
auto t1 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_lo, i128));
auto t2 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_mi, i128));
auto t3 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), y_hi);
auto t4 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), m_builder.CreateZExt(y_lo, i128));
auto t5 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), m_builder.CreateZExt(y_mi, i128));
auto t6 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), y_hi);
auto t7 = m_builder.CreateMul(x_hi, m_builder.CreateZExt(y_lo, i128));
auto t8 = m_builder.CreateMul(x_hi, m_builder.CreateZExt(y_mi, i128));
auto p = m_builder.CreateZExt(t1, i256);
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i256), Constant::get(64)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t3, i256), Constant::get(128)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t4, i256), Constant::get(64)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t5, i256), Constant::get(128)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t6, i256), Constant::get(192)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t7, i256), Constant::get(128)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t8, i256), Constant::get(192)));
m_builder.CreateRet(p);
}
return func;
} }
void Arith256::debug(llvm::Value* _value, char _c) llvm::Function* Arith256::getMul512Func()
{ {
if (!m_debug) auto& func = m_mul512;
if (!func)
{ {
llvm::Type* argTypes[] = {Type::Word, m_builder.getInt8Ty()}; auto i512 = m_builder.getIntNTy(512);
m_debug = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "debug", getModule()); llvm::Type* argTypes[] = {Type::Word, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(i512, argTypes, false), llvm::Function::PrivateLinkage, "mul512", getModule());
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
InsertPointGuard guard{m_builder};
auto bb = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
m_builder.SetInsertPoint(bb);
auto i128 = m_builder.getIntNTy(128);
auto i256 = Type::Word;
auto x_lo = m_builder.CreateZExt(m_builder.CreateTrunc(x, i128, "x.lo"), i256);
auto y_lo = m_builder.CreateZExt(m_builder.CreateTrunc(y, i128, "y.lo"), i256);
auto x_hi = m_builder.CreateZExt(m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(128)), i128, "x.hi"), i256);
auto y_hi = m_builder.CreateZExt(m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(128)), i128, "y.hi"), i256);
auto t1 = createCall(getMulFunc(), {x_lo, y_lo});
auto t2 = createCall(getMulFunc(), {x_lo, y_hi});
auto t3 = createCall(getMulFunc(), {x_hi, y_lo});
auto t4 = createCall(getMulFunc(), {x_hi, y_hi});
auto p = m_builder.CreateZExt(t1, i512);
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i512), m_builder.getIntN(512, 128)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t3, i512), m_builder.getIntN(512, 128)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t4, i512), m_builder.getIntN(512, 256)));
m_builder.CreateRet(p);
} }
createCall(m_debug, {_value, m_builder.getInt8(_c)}); return func;
} }
llvm::Function* Arith256::getDivFunc(llvm::Type* _type) llvm::Function* Arith256::getDivFunc(llvm::Type* _type)
@ -85,6 +161,15 @@ llvm::Function* Arith256::getDivFunc(llvm::Type* _type)
auto i0 = m_builder.CreateNUWSub(yLz, rLz, "i0"); auto i0 = m_builder.CreateNUWSub(yLz, rLz, "i0");
auto shlBy0 = m_builder.CreateICmpEQ(i0, zero); auto shlBy0 = m_builder.CreateICmpEQ(i0, zero);
auto y0 = m_builder.CreateShl(yArg, i0); auto y0 = m_builder.CreateShl(yArg, i0);
if (_type == m_builder.getIntNTy(512)) // Workaround for shl bug for long shifts
{
const auto treshold = m_builder.getIntN(512, 128);
auto highShift = m_builder.CreateICmpUGT(i0, treshold);
auto s = m_builder.CreateNUWSub(i0, treshold);
auto yhs = m_builder.CreateShl(yArg, treshold);
yhs = m_builder.CreateShl(yhs, s);
y0 = m_builder.CreateSelect(highShift, yhs, y0);
}
y0 = m_builder.CreateSelect(shlBy0, yArg, y0, "y0"); // Workaround for LLVM bug: shl by 0 produces wrong result y0 = m_builder.CreateSelect(shlBy0, yArg, y0, "y0"); // Workaround for LLVM bug: shl by 0 produces wrong result
m_builder.CreateBr(loopBB); m_builder.CreateBr(loopBB);
@ -135,7 +220,7 @@ llvm::Function* Arith256::getExpFunc()
if (!m_exp) if (!m_exp)
{ {
llvm::Type* argTypes[] = {Type::Word, Type::Word}; llvm::Type* argTypes[] = {Type::Word, Type::Word};
m_exp = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "arith.exp", getModule()); m_exp = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "exp", getModule());
auto base = &m_exp->getArgumentList().front(); auto base = &m_exp->getArgumentList().front();
base->setName("base"); base->setName("base");
@ -159,9 +244,6 @@ llvm::Function* Arith256::getExpFunc()
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", m_exp); auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", m_exp);
m_builder.SetInsertPoint(entryBB); m_builder.SetInsertPoint(entryBB);
auto a1 = m_builder.CreateAlloca(Type::Word, nullptr, "a1");
auto a2 = m_builder.CreateAlloca(Type::Word, nullptr, "a2");
auto a3 = m_builder.CreateAlloca(Type::Word, nullptr, "a3");
m_builder.CreateBr(headerBB); m_builder.CreateBr(headerBB);
m_builder.SetInsertPoint(headerBB); m_builder.SetInsertPoint(headerBB);
@ -176,20 +258,14 @@ llvm::Function* Arith256::getExpFunc()
m_builder.CreateCondBr(eOdd, updateBB, continueBB); m_builder.CreateCondBr(eOdd, updateBB, continueBB);
m_builder.SetInsertPoint(updateBB); m_builder.SetInsertPoint(updateBB);
m_builder.CreateStore(r, a1); auto r0 = createCall(getMulFunc(), {r, b});
m_builder.CreateStore(b, a2);
createCall(m_mul, {a1, a2, a3});
auto r0 = m_builder.CreateLoad(a3, "r0");
m_builder.CreateBr(continueBB); m_builder.CreateBr(continueBB);
m_builder.SetInsertPoint(continueBB); m_builder.SetInsertPoint(continueBB);
auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1"); auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1");
r1->addIncoming(r, bodyBB); r1->addIncoming(r, bodyBB);
r1->addIncoming(r0, updateBB); r1->addIncoming(r0, updateBB);
m_builder.CreateStore(b, a1); auto b1 = createCall(getMulFunc(), {b, b});
m_builder.CreateStore(b, a2);
createCall(m_mul, {a1, a2, a3});
auto b1 = m_builder.CreateLoad(a3, "b1");
auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1"); auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1");
m_builder.CreateBr(headerBB); m_builder.CreateBr(headerBB);
@ -244,9 +320,6 @@ llvm::Function* Arith256::getMulModFunc()
m_mulmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mulmod", getModule()); m_mulmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mulmod", getModule());
auto i512Ty = m_builder.getIntNTy(512); auto i512Ty = m_builder.getIntNTy(512);
llvm::Type* mul512ArgTypes[] = {Type::WordPtr, Type::WordPtr, i512Ty->getPointerTo()};
auto mul512 = llvm::Function::Create(llvm::FunctionType::get(Type::Void, mul512ArgTypes, false), llvm::Function::ExternalLinkage, "arith_mul512", getModule());
auto x = &m_mulmod->getArgumentList().front(); auto x = &m_mulmod->getArgumentList().front();
x->setName("x"); x->setName("x");
auto y = x->getNextNode(); auto y = x->getNextNode();
@ -258,32 +331,19 @@ llvm::Function* Arith256::getMulModFunc()
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mulmod); auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mulmod);
m_builder.SetInsertPoint(entryBB); m_builder.SetInsertPoint(entryBB);
auto a1 = m_builder.CreateAlloca(Type::Word); auto p = createCall(getMul512Func(), {x, y});
auto a2 = m_builder.CreateAlloca(Type::Word);
auto a3 = m_builder.CreateAlloca(i512Ty);
m_builder.CreateStore(x, a1);
m_builder.CreateStore(y, a2);
createCall(mul512, {a1, a2, a3});
auto p = m_builder.CreateLoad(a3, "p");
auto m = m_builder.CreateZExt(mod, i512Ty, "m"); auto m = m_builder.CreateZExt(mod, i512Ty, "m");
auto d = createCall(getDivFunc(i512Ty), {p, m}); auto d = createCall(getDivFunc(i512Ty), {p, m});
auto r = m_builder.CreateExtractValue(d, 1, "r"); auto r = m_builder.CreateExtractValue(d, 1, "r");
m_builder.CreateRet(m_builder.CreateTrunc(r, Type::Word)); r = m_builder.CreateTrunc(r, Type::Word);
m_builder.CreateRet(r);
} }
return m_mulmod; return m_mulmod;
} }
llvm::Value* Arith256::binaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2)
{
m_builder.CreateStore(_arg1, m_arg1);
m_builder.CreateStore(_arg2, m_arg2);
m_builder.CreateCall3(_op, m_arg1, m_arg2, m_result);
return m_builder.CreateLoad(m_result);
}
llvm::Value* Arith256::mul(llvm::Value* _arg1, llvm::Value* _arg2) llvm::Value* Arith256::mul(llvm::Value* _arg1, llvm::Value* _arg2)
{ {
return binaryOp(m_mul, _arg1, _arg2); return createCall(getMulFunc(), {_arg1, _arg2});
} }
std::pair<llvm::Value*, llvm::Value*> Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2) std::pair<llvm::Value*, llvm::Value*> Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2)
@ -331,157 +391,6 @@ llvm::Value* Arith256::mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Valu
return createCall(getMulModFunc(), {_arg1, _arg2, _arg3}); return createCall(getMulModFunc(), {_arg1, _arg2, _arg3});
} }
namespace
{
#ifdef __SIZEOF_INT128__
using uint128 = __uint128_t;
#else
struct uint128
{
uint64_t lo = 0;
uint64_t hi = 0;
uint128(uint64_t lo) : lo(lo) {}
uint128 operator+(uint128 a)
{
uint128 r = 0;
bool overflow = lo > std::numeric_limits<uint64_t>::max() - a.lo;
r.lo = lo + a.lo;
r.hi = hi + a.hi + overflow;
return r;
}
uint128 operator>>(int s)
{
assert(s == 64);
return hi;
}
uint128 operator<<(int s)
{
assert(s == 64);
uint128 r = 0;
r.hi = lo;
return r;
}
explicit operator uint64_t() { return lo; }
static uint128 mul(uint64_t a, uint64_t b)
{
auto x_lo = 0xFFFFFFFF & a;
auto y_lo = 0xFFFFFFFF & b;
auto x_hi = a >> 32;
auto y_hi = b >> 32;
auto t1 = x_lo * y_lo;
auto t2 = x_lo * y_hi;
auto t3 = x_hi * y_lo;
auto t4 = x_hi * y_hi;
auto lo = (uint32_t)t1;
auto mid = (uint64_t)(t1 >> 32) + (uint32_t)t2 + (uint32_t)t3;
auto hi = (uint64_t)(t2 >> 32) + (t3 >> 32) + t4 + (mid >> 32);
uint128 r = 0;
r.lo = (uint64_t)lo + (mid << 32);
r.hi = hi;
return r;
}
uint128 operator*(uint128 a)
{
auto t1 = mul(lo, a.lo);
auto t2 = mul(lo, a.hi);
auto t3 = mul(hi, a.lo);
return t1 + (t2 << 64) + (t3 << 64);
}
};
#endif
struct uint256
{
uint64_t lo = 0;
uint64_t mid = 0;
uint128 hi = 0;
uint256(uint64_t lo, uint64_t mid, uint128 hi): lo(lo), mid(mid), hi(hi) {}
uint256(uint128 n)
{
lo = (uint64_t) n;
mid = (uint64_t) (n >> 64);
}
explicit operator uint128()
{
uint128 r = lo;
r |= ((uint128) mid) << 64;
return r;
}
uint256 operator+(uint256 a)
{
auto _lo = (uint128) lo + a.lo;
auto _mid = (uint128) mid + a.mid + (_lo >> 64);
auto _hi = hi + a.hi + (_mid >> 64);
return {(uint64_t)_lo, (uint64_t)_mid, _hi};
}
uint256 lo2hi()
{
hi = (uint128)*this;
lo = 0;
mid = 0;
return *this;
}
};
struct uint512
{
uint128 lo;
uint128 mid;
uint256 hi;
};
uint256 mul(uint256 x, uint256 y)
{
auto t1 = (uint128) x.lo * y.lo;
auto t2 = (uint128) x.lo * y.mid;
auto t3 = (uint128) x.lo * y.hi;
auto t4 = (uint128) x.mid * y.lo;
auto t5 = (uint128) x.mid * y.mid;
auto t6 = (uint128) x.mid * y.hi;
auto t7 = x.hi * y.lo;
auto t8 = x.hi * y.mid;
auto lo = (uint64_t) t1;
auto m1 = (t1 >> 64) + (uint64_t) t2;
auto m2 = (uint64_t) m1;
auto mid = (uint128) m2 + (uint64_t) t4;
auto hi = (t2 >> 64) + t3 + (t4 >> 64) + t5 + (t6 << 64) + t7
+ (t8 << 64) + (m1 >> 64) + (mid >> 64);
return {lo, (uint64_t)mid, hi};
}
uint512 mul512(uint256 x, uint256 y)
{
auto x_lo = (uint128) x;
auto y_lo = (uint128) y;
auto t1 = mul(x_lo, y_lo);
auto t2 = mul(x_lo, y.hi);
auto t3 = mul(x.hi, y_lo);
auto t4 = mul(x.hi, y.hi);
auto lo = (uint128) t1;
auto mid = (uint256) t1.hi + (uint128) t2 + (uint128) t3;
auto hi = (uint256)t2.hi + t3.hi + t4 + mid.hi;
return {lo, (uint128)mid, hi};
}
}
} }
} }
@ -489,20 +398,9 @@ namespace
extern "C" extern "C"
{ {
using namespace dev::eth::jit;
EXPORT void debug(uint64_t a, uint64_t b, uint64_t c, uint64_t d, char z) EXPORT void debug(uint64_t a, uint64_t b, uint64_t c, uint64_t d, char z)
{ {
std::cerr << "DEBUG " << z << ": " << d << c << b << a << std::endl; std::cerr << "DEBUG " << std::dec << z << ": " //<< d << c << b << a
} << " [" << std::hex << std::setfill('0') << std::setw(16) << d << std::setw(16) << c << std::setw(16) << b << std::setw(16) << a << "]\n";
EXPORT void arith_mul(uint256* _arg1, uint256* _arg2, uint256* o_result)
{
*o_result = mul(*_arg1, *_arg2);
}
EXPORT void arith_mul512(uint256* _arg1, uint256* _arg2, uint512* o_result)
{
*o_result = mul512(*_arg1, *_arg2);
} }
} }

13
evmjit/libevmjit/Arith256.h

@ -24,26 +24,21 @@ public:
void debug(llvm::Value* _value, char _c); void debug(llvm::Value* _value, char _c);
private: private:
llvm::Function* getMulFunc();
llvm::Function* getMul512Func();
llvm::Function* getDivFunc(llvm::Type* _type); llvm::Function* getDivFunc(llvm::Type* _type);
llvm::Function* getExpFunc(); llvm::Function* getExpFunc();
llvm::Function* getAddModFunc(); llvm::Function* getAddModFunc();
llvm::Function* getMulModFunc(); llvm::Function* getMulModFunc();
llvm::Value* binaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2); llvm::Function* m_mul = nullptr;
llvm::Function* m_mul512 = nullptr;
llvm::Function* m_mul;
llvm::Function* m_div = nullptr; llvm::Function* m_div = nullptr;
llvm::Function* m_div512 = nullptr; llvm::Function* m_div512 = nullptr;
llvm::Function* m_exp = nullptr; llvm::Function* m_exp = nullptr;
llvm::Function* m_addmod = nullptr; llvm::Function* m_addmod = nullptr;
llvm::Function* m_mulmod = nullptr; llvm::Function* m_mulmod = nullptr;
llvm::Function* m_debug = nullptr; llvm::Function* m_debug = nullptr;
llvm::Value* m_arg1;
llvm::Value* m_arg2;
llvm::Value* m_arg3;
llvm::Value* m_result;
}; };

19
evmjit/libevmjit/BasicBlock.cpp

@ -1,13 +1,14 @@
#include "BasicBlock.h" #include "BasicBlock.h"
#include <iostream> #include <iostream>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/CFG.h> #include <llvm/IR/CFG.h>
#include <llvm/IR/Function.h> #include <llvm/IR/Function.h>
#include <llvm/IR/Instructions.h> #include <llvm/IR/Instructions.h>
#include <llvm/IR/IRBuilder.h> #include <llvm/IR/IRBuilder.h>
#include <llvm/Support/raw_os_ostream.h> #include <llvm/Support/raw_os_ostream.h>
#include "preprocessor/llvm_includes_end.h"
#include "Type.h" #include "Type.h"
@ -18,13 +19,14 @@ namespace eth
namespace jit namespace jit
{ {
const char* BasicBlock::NamePrefix = "Instr."; static const char* jumpDestName = "JmpDst.";
static const char* basicBlockName = "Instr.";
BasicBlock::BasicBlock(bytes::const_iterator _begin, bytes::const_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) : BasicBlock::BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) :
m_firstInstrIdx{_firstInstrIdx},
m_begin(_begin), m_begin(_begin),
m_end(_end), m_end(_end),
// TODO: Add begin index to name m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {isJumpDest ? jumpDestName : basicBlockName, std::to_string(_firstInstrIdx)}, _mainFunc)),
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), NamePrefix, _mainFunc)),
m_stack(*this), m_stack(*this),
m_builder(_builder), m_builder(_builder),
m_isJumpDest(isJumpDest) m_isJumpDest(isJumpDest)
@ -43,6 +45,7 @@ BasicBlock::LocalStack::LocalStack(BasicBlock& _owner) :
void BasicBlock::LocalStack::push(llvm::Value* _value) void BasicBlock::LocalStack::push(llvm::Value* _value)
{ {
assert(_value->getType() == Type::Word);
m_bblock.m_currentStack.push_back(_value); m_bblock.m_currentStack.push_back(_value);
m_bblock.m_tosOffset += 1; m_bblock.m_tosOffset += 1;
} }
@ -139,7 +142,7 @@ void BasicBlock::synchronizeLocalStack(Stack& _evmStack)
auto endIter = m_currentStack.end(); auto endIter = m_currentStack.end();
// Update (emit set()) changed values // Update (emit set()) changed values
for (int idx = m_currentStack.size() - 1 - m_tosOffset; for (int idx = (int)m_currentStack.size() - 1 - m_tosOffset;
currIter < endIter && idx >= 0; currIter < endIter && idx >= 0;
++currIter, --idx) ++currIter, --idx)
{ {
@ -306,7 +309,7 @@ void BasicBlock::linkLocalStacks(std::vector<BasicBlock*> basicBlocks, llvm::IRB
auto& initialStack = bblock.m_initialStack; auto& initialStack = bblock.m_initialStack;
initialStack.erase(initialStack.begin(), initialStack.begin() + info.inputItems); initialStack.erase(initialStack.begin(), initialStack.begin() + info.inputItems);
// Initial stack shrinks, so the size difference grows: // Initial stack shrinks, so the size difference grows:
bblock.m_tosOffset += info.inputItems; bblock.m_tosOffset += (int)info.inputItems;
} }
// We must account for the items that were pushed directly to successor // We must account for the items that were pushed directly to successor
@ -319,7 +322,7 @@ void BasicBlock::linkLocalStacks(std::vector<BasicBlock*> basicBlocks, llvm::IRB
auto& exitStack = bblock.m_currentStack; auto& exitStack = bblock.m_currentStack;
exitStack.erase(exitStack.end() - info.outputItems, exitStack.end()); exitStack.erase(exitStack.end() - info.outputItems, exitStack.end());
bblock.m_tosOffset -= info.outputItems; bblock.m_tosOffset -= (int)info.outputItems; // FIXME: Fix types
} }
} }

20
evmjit/libevmjit/BasicBlock.h

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <llvm/IR/BasicBlock.h>
#include "Common.h" #include "Common.h"
#include "Stack.h" #include "Stack.h"
@ -11,7 +12,7 @@ namespace eth
namespace jit namespace jit
{ {
using ProgramCounter = uint64_t; // TODO: Rename using instr_idx = uint64_t;
class BasicBlock class BasicBlock
{ {
@ -50,10 +51,7 @@ public:
BasicBlock& m_bblock; BasicBlock& m_bblock;
}; };
/// Basic block name prefix. The rest is instruction index. explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest);
static const char* NamePrefix;
explicit BasicBlock(bytes::const_iterator _begin, bytes::const_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest);
explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest);
BasicBlock(const BasicBlock&) = delete; BasicBlock(const BasicBlock&) = delete;
@ -61,8 +59,9 @@ public:
llvm::BasicBlock* llvm() { return m_llvmBB; } llvm::BasicBlock* llvm() { return m_llvmBB; }
bytes::const_iterator begin() { return m_begin; } instr_idx firstInstrIdx() const { return m_firstInstrIdx; }
bytes::const_iterator end() { return m_end; } code_iterator begin() const { return m_begin; }
code_iterator end() const { return m_end; }
bool isJumpDest() const { return m_isJumpDest; } bool isJumpDest() const { return m_isJumpDest; }
@ -84,8 +83,9 @@ public:
void dump(std::ostream& os, bool _dotOutput = false); void dump(std::ostream& os, bool _dotOutput = false);
private: private:
bytes::const_iterator const m_begin; instr_idx const m_firstInstrIdx = 0; ///< Code index of first instruction in the block
bytes::const_iterator const m_end; code_iterator const m_begin = {}; ///< Iterator pointing code beginning of the block
code_iterator const m_end = {}; ///< Iterator pointing code end of the block
llvm::BasicBlock* const m_llvmBB; llvm::BasicBlock* const m_llvmBB;

10
evmjit/libevmjit/BuildInfo.h.in

@ -0,0 +1,10 @@
#define EVMJIT_VERSION "${EVMJIT_VERSION}"
#define EVMJIT_VERSION_MAJOR ${EVMJIT_VERSION_MAJOR}
#define EVMJIT_VERSION_MINOR ${EVMJIT_VERSION_MINOR}
#define EVMJIT_VERSION_PATCH ${EVMJIT_VERSION_PATCH}
#define EVMJIT_VERSION_PRERELEASE "${EVMJIT_VERSION_PRERELEASE}"
#define EVMJIT_VERSION_FULL "${EVMJIT_VERSION_FULL}"
#define LLVM_VERSION "${LLVM_PACKAGE_VERSION}"
#define LLVM_ASSERTIONS "${LLVM_ENABLE_ASSERTIONS}"

53
evmjit/libevmjit/CMakeLists.txt

@ -6,26 +6,59 @@ set(INTERFACE_HEADERS interface.h)
source_group("" FILES ${HEADERS}) source_group("" FILES ${HEADERS})
source_group("" FILES ${SOURCES}) source_group("" FILES ${SOURCES})
if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
# Disable rtti for Cache as LLVM has no rtti else()
set_source_files_properties(Cache.cpp PROPERTIES COMPILE_FLAGS -fno-rtti) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
endif() endif()
set(EVMJIT_VERSION "0.0.0")
set(EVMJIT_VERSION_MAJOR 0)
set(EVMJIT_VERSION_MINOR 0)
set(EVMJIT_VERSION_PATCH 0)
set(EVMJIT_VERSION_FULL "v0.0.0-nogit")
find_package(Git) find_package(Git)
if(GIT_FOUND) if(GIT_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} describe --dirty --always execute_process(COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} describe --dirty --always --match v*
OUTPUT_VARIABLE EVMJIT_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) OUTPUT_VARIABLE EVMJIT_VERSION_FULL OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
if(${EVMJIT_VERSION_FULL} MATCHES "^v[0-9]+\\.[0-9]+")
string(SUBSTRING ${EVMJIT_VERSION_FULL} 1 -1 EVMJIT_VERSION_FULL) # skip "v"
string(REPLACE "-" ";" VERSION_COMPONENTS ${EVMJIT_VERSION_FULL})
list(LENGTH VERSION_COMPONENTS NUM_VERSION_COMPONENTS)
list(GET VERSION_COMPONENTS 0 EVMJIT_VERSION)
string(REPLACE "." ";" VERSION_NUMBERS ${EVMJIT_VERSION})
list(LENGTH VERSION_NUMBERS NUM_VERSION_NUMBERS)
list(GET VERSION_NUMBERS 0 EVMJIT_VERSION_MAJOR)
list(GET VERSION_NUMBERS 1 EVMJIT_VERSION_MINOR)
if(${NUM_VERSION_NUMBERS} GREATER 2)
list(GET VERSION_NUMBERS 2 EVMJIT_VERSION_PATCH) # patch number is optional
endif()
if(${NUM_VERSION_COMPONENTS} GREATER 1)
list(GET VERSION_COMPONENTS 1 VERSION_PRERELEASE_CANDIDATE)
string(REGEX MATCH "^[a-zA-Z]+.*" EVMJIT_VERSION_PRERELEASE ${VERSION_PRERELEASE_CANDIDATE}) # prerelease starts with letter
endif()
endif() endif()
if(NOT EVMJIT_VERSION)
set(EVMJIT_VERSION "unknown") if(${EVMJIT_VERSION_MAJOR} EQUAL 0)
set(EVMJIT_SOVERSION "0.${EVMJIT_VERSION_MINOR}")
else()
set(EVMJIT_SOVERSION ${EVMJIT_VERSION_MAJOR})
endif() endif()
message("EVM JIT version: ${EVMJIT_VERSION}") configure_file(BuildInfo.h.in ${CMAKE_CURRENT_BINARY_DIR}/gen/BuildInfo.gen.h)
message(STATUS "EVM JIT version: ${EVMJIT_VERSION_MAJOR}.${EVMJIT_VERSION_MINOR}.${EVMJIT_VERSION_PATCH} ${EVMJIT_VERSION_PRERELEASE} (${EVMJIT_VERSION_FULL})")
add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS}) add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS} gen/BuildInfo.gen.h)
set_target_properties(${TARGET_NAME} PROPERTIES VERSION ${EVMJIT_VERSION} FOLDER "libs") set_target_properties(${TARGET_NAME} PROPERTIES
VERSION ${EVMJIT_VERSION} SOVERSION ${EVMJIT_SOVERSION}
FOLDER "libs")
include_directories(${LLVM_INCLUDE_DIRS}) include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR}/gen)
target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS}) target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS})

62
evmjit/libevmjit/Cache.cpp

@ -1,12 +1,19 @@
#include "Cache.h" #include "Cache.h"
#include <unordered_map>
#include <cassert>
#include <iostream> #include <iostream>
#include <cassert>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h> #include <llvm/IR/Module.h>
#include <llvm/IR/LLVMContext.h> #include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Instructions.h>
#include <llvm/Support/Path.h> #include <llvm/Support/Path.h>
#include <llvm/Support/FileSystem.h> #include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_os_ostream.h> #include <llvm/Support/raw_os_ostream.h>
#include "preprocessor/llvm_includes_end.h"
#include "ExecutionEngine.h"
#include "Utils.h"
namespace dev namespace dev
{ {
@ -15,23 +22,31 @@ namespace eth
namespace jit namespace jit
{ {
//#define LOG(...) std::cerr << "CACHE " //#define CACHE_LOG std::cerr << "CACHE "
#define LOG(...) std::ostream(nullptr) #define CACHE_LOG std::ostream(nullptr)
ObjectCache* Cache::getObjectCache() namespace
{ {
static ObjectCache objectCache; llvm::MemoryBuffer* g_lastObject;
return &objectCache; ExecutionEngineListener* g_listener;
} }
namespace ObjectCache* Cache::getObjectCache(ExecutionEngineListener* _listener)
{ {
llvm::MemoryBuffer* lastObject; static ObjectCache objectCache;
g_listener = _listener;
return &objectCache;
} }
std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id) std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
{ {
assert(!lastObject); if (g_listener)
g_listener->stateChanged(ExecState::CacheLoad);
CACHE_LOG << id << ": search\n";
if (!CHECK(!g_lastObject))
g_lastObject = nullptr;
llvm::SmallString<256> cachePath; llvm::SmallString<256> cachePath;
llvm::sys::path::system_temp_directory(false, cachePath); llvm::sys::path::system_temp_directory(false, cachePath);
llvm::sys::path::append(cachePath, "evm_objs", id); llvm::sys::path::append(cachePath, "evm_objs", id);
@ -51,22 +66,31 @@ std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
#endif #endif
if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false)) if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false))
lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); g_lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer());
else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory))
std::cerr << r.getError().message(); // TODO: Add log std::cerr << r.getError().message(); // TODO: Add log
if (lastObject) // if object found create fake module if (g_lastObject) // if object found create fake module
{ {
auto module = std::unique_ptr<llvm::Module>(new llvm::Module(id, llvm::getGlobalContext())); CACHE_LOG << id << ": found\n";
auto mainFuncType = llvm::FunctionType::get(llvm::IntegerType::get(llvm::getGlobalContext(), 32), {}, false); auto&& context = llvm::getGlobalContext();
llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get()); auto module = std::unique_ptr<llvm::Module>(new llvm::Module(id, context));
auto mainFuncType = llvm::FunctionType::get(llvm::Type::getVoidTy(context), {}, false);
auto mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get());
auto bb = llvm::BasicBlock::Create(context, {}, mainFunc);
bb->getInstList().push_back(new llvm::UnreachableInst{context});
return module;
} }
CACHE_LOG << id << ": not found\n";
return nullptr; return nullptr;
} }
void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object) void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object)
{ {
if (g_listener)
g_listener->stateChanged(ExecState::CacheWrite);
auto&& id = _module->getModuleIdentifier(); auto&& id = _module->getModuleIdentifier();
llvm::SmallString<256> cachePath; llvm::SmallString<256> cachePath;
llvm::sys::path::system_temp_directory(false, cachePath); llvm::sys::path::system_temp_directory(false, cachePath);
@ -77,15 +101,17 @@ void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::Memory
llvm::sys::path::append(cachePath, id); llvm::sys::path::append(cachePath, id);
CACHE_LOG << id << ": write\n";
std::string error; std::string error;
llvm::raw_fd_ostream cacheFile(cachePath.c_str(), error, llvm::sys::fs::F_None); llvm::raw_fd_ostream cacheFile(cachePath.c_str(), error, llvm::sys::fs::F_None);
cacheFile << _object->getBuffer(); cacheFile << _object->getBuffer();
} }
llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const*) llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module)
{ {
auto o = lastObject; CACHE_LOG << _module->getModuleIdentifier() << ": use\n";
lastObject = nullptr; auto o = g_lastObject;
g_lastObject = nullptr;
return o; return o;
} }

9
evmjit/libevmjit/Cache.h

@ -1,9 +1,8 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <unordered_map>
#include <llvm/ExecutionEngine/ObjectCache.h>
#include <llvm/ExecutionEngine/ObjectCache.h>
namespace dev namespace dev
{ {
@ -11,6 +10,7 @@ namespace eth
{ {
namespace jit namespace jit
{ {
class ExecutionEngineListener;
class ObjectCache : public llvm::ObjectCache class ObjectCache : public llvm::ObjectCache
{ {
@ -23,16 +23,13 @@ public:
/// not available. The caller owns both the MemoryBuffer returned by this /// not available. The caller owns both the MemoryBuffer returned by this
/// and the memory it references. /// and the memory it references.
virtual llvm::MemoryBuffer* getObject(llvm::Module const* _module) final override; virtual llvm::MemoryBuffer* getObject(llvm::Module const* _module) final override;
private:
std::unordered_map<std::string, std::unique_ptr<llvm::MemoryBuffer>> m_map;
}; };
class Cache class Cache
{ {
public: public:
static ObjectCache* getObjectCache(); static ObjectCache* getObjectCache(ExecutionEngineListener* _listener);
static std::unique_ptr<llvm::Module> getObject(std::string const& id); static std::unique_ptr<llvm::Module> getObject(std::string const& id);
}; };

17
evmjit/libevmjit/Common.h

@ -20,25 +20,30 @@ namespace jit
using byte = uint8_t; using byte = uint8_t;
using bytes = std::vector<byte>; using bytes = std::vector<byte>;
using bytes_ref = std::tuple<byte const*, size_t>; using bytes_ref = std::tuple<byte const*, size_t>;
using code_iterator = byte const*;
struct NoteChannel {}; // FIXME: Use some log library? struct NoteChannel {}; // FIXME: Use some log library?
enum class ReturnCode enum class ReturnCode
{ {
// Success codes
Stop = 0, Stop = 0,
Return = 1, Return = 1,
Suicide = 2, Suicide = 2,
// Standard error codes
OutOfGas = -1, OutOfGas = -1,
BadJumpDestination = -2, StackTooSmall = -2,
StackTooSmall = -3, BadJumpDestination = -3,
BadInstruction = -4, BadInstruction = -4,
Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected
LLVMConfigError = -5, // Internal error codes
LLVMCompileError = -6, LLVMConfigError = -101,
LLVMLinkError = -7, LLVMCompileError = -102,
LLVMLinkError = -103,
UnexpectedException = -8, UnexpectedException = -111,
LinkerWorkaround = -299, LinkerWorkaround = -299,
}; };

94
evmjit/libevmjit/Compiler.cpp

@ -1,4 +1,3 @@
#include "Compiler.h" #include "Compiler.h"
#include <functional> #include <functional>
@ -6,13 +5,15 @@
#include <chrono> #include <chrono>
#include <sstream> #include <sstream>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/ADT/PostOrderIterator.h> #include <llvm/ADT/PostOrderIterator.h>
#include <llvm/IR/CFG.h> #include <llvm/IR/CFG.h>
#include <llvm/IR/Module.h> #include <llvm/IR/Module.h>
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include <llvm/IR/MDBuilder.h>
#include <llvm/PassManager.h> #include <llvm/PassManager.h>
#include <llvm/Transforms/Scalar.h> #include <llvm/Transforms/Scalar.h>
#include "preprocessor/llvm_includes_end.h"
#include "Instruction.h" #include "Instruction.h"
#include "Type.h" #include "Type.h"
@ -39,10 +40,10 @@ Compiler::Compiler(Options const& _options):
Type::init(m_builder.getContext()); Type::init(m_builder.getContext());
} }
void Compiler::createBasicBlocks(bytes const& _bytecode) void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEnd)
{ {
/// Helper function that skips push data and finds next iterator (can be the end) /// Helper function that skips push data and finds next iterator (can be the end)
auto skipPushDataAndGetNext = [](bytes::const_iterator _curr, bytes::const_iterator _end) auto skipPushDataAndGetNext = [](code_iterator _curr, code_iterator _end)
{ {
static const auto push1 = static_cast<size_t>(Instruction::PUSH1); static const auto push1 = static_cast<size_t>(Instruction::PUSH1);
static const auto push32 = static_cast<size_t>(Instruction::PUSH32); static const auto push32 = static_cast<size_t>(Instruction::PUSH32);
@ -52,11 +53,11 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
return _curr + offset; return _curr + offset;
}; };
auto begin = _bytecode.begin(); auto begin = _codeBegin; // begin of current block
bool nextJumpDest = false; bool nextJumpDest = false;
for (auto curr = begin, next = begin; curr != _bytecode.end(); curr = next) for (auto curr = begin, next = begin; curr != _codeEnd; curr = next)
{ {
next = skipPushDataAndGetNext(curr, _bytecode.end()); next = skipPushDataAndGetNext(curr, _codeEnd);
bool isEnd = false; bool isEnd = false;
switch (Instruction(*curr)) switch (Instruction(*curr))
@ -77,22 +78,19 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
break; break;
} }
assert(next <= _bytecode.end()); assert(next <= _codeEnd);
if (next == _bytecode.end() || Instruction(*next) == Instruction::JUMPDEST) if (next == _codeEnd || Instruction(*next) == Instruction::JUMPDEST)
isEnd = true; isEnd = true;
if (isEnd) if (isEnd)
{ {
auto beginIdx = begin - _bytecode.begin(); auto beginIdx = begin - _codeBegin;
m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx), m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx),
std::forward_as_tuple(begin, next, m_mainFunc, m_builder, nextJumpDest)); std::forward_as_tuple(beginIdx, begin, next, m_mainFunc, m_builder, nextJumpDest));
nextJumpDest = false; nextJumpDest = false;
begin = next; begin = next;
} }
} }
// TODO: Create Stop basic block on demand
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
} }
llvm::BasicBlock* Compiler::getJumpTableBlock() llvm::BasicBlock* Compiler::getJumpTableBlock()
@ -125,7 +123,7 @@ llvm::BasicBlock* Compiler::getBadJumpBlock()
return m_badJumpBlock->llvm(); return m_badJumpBlock->llvm();
} }
std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::string const& _id) std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_iterator _end, std::string const& _id)
{ {
auto compilationStartTime = std::chrono::high_resolution_clock::now(); auto compilationStartTime = std::chrono::high_resolution_clock::now();
auto module = std::unique_ptr<llvm::Module>(new llvm::Module(_id, m_builder.getContext())); auto module = std::unique_ptr<llvm::Module>(new llvm::Module(_id, m_builder.getContext()));
@ -135,21 +133,40 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::str
m_mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, _id, module.get()); m_mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, _id, module.get());
m_mainFunc->getArgumentList().front().setName("rt"); m_mainFunc->getArgumentList().front().setName("rt");
// Create the basic blocks. // Create entry basic block
auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), "entry", m_mainFunc); auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mainFunc);
m_builder.SetInsertPoint(entryBlock); m_builder.SetInsertPoint(entryBlock);
createBasicBlocks(_bytecode); auto jmpBufWords = m_builder.CreateAlloca(Type::BytePtr, m_builder.getInt64(3), "jmpBuf.words");
auto frameaddress = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::frameaddress);
auto fp = m_builder.CreateCall(frameaddress, m_builder.getInt32(0), "fp");
m_builder.CreateStore(fp, jmpBufWords);
auto stacksave = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::stacksave);
auto sp = m_builder.CreateCall(stacksave, "sp");
auto jmpBufSp = m_builder.CreateConstInBoundsGEP1_64(jmpBufWords, 2, "jmpBuf.sp");
m_builder.CreateStore(sp, jmpBufSp);
auto setjmp = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::eh_sjlj_setjmp);
auto jmpBuf = m_builder.CreateBitCast(jmpBufWords, Type::BytePtr, "jmpBuf");
auto r = m_builder.CreateCall(setjmp, jmpBuf);
auto normalFlow = m_builder.CreateICmpEQ(r, m_builder.getInt32(0));
createBasicBlocks(_begin, _end);
// Init runtime structures. // Init runtime structures.
RuntimeManager runtimeManager(m_builder); RuntimeManager runtimeManager(m_builder, jmpBuf, _begin, _end);
GasMeter gasMeter(m_builder, runtimeManager); GasMeter gasMeter(m_builder, runtimeManager);
Memory memory(runtimeManager, gasMeter); Memory memory(runtimeManager, gasMeter);
Ext ext(runtimeManager, memory); Ext ext(runtimeManager, memory);
Stack stack(m_builder, runtimeManager); Stack stack(m_builder, runtimeManager);
Arith256 arith(m_builder); Arith256 arith(m_builder);
m_builder.CreateBr(m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm()); // TODO: Create Stop basic block on demand
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
auto abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc);
auto firstBB = m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm();
auto expectTrue = llvm::MDBuilder{m_builder.getContext()}.createBranchWeights(1, 0);
m_builder.CreateCondBr(normalFlow, firstBB, abortBB, expectTrue);
for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt) for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt)
{ {
@ -157,7 +174,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::str
auto iterCopy = basicBlockPairIt; auto iterCopy = basicBlockPairIt;
++iterCopy; ++iterCopy;
auto nextBasicBlock = (iterCopy != m_basicBlocks.end()) ? iterCopy->second.llvm() : nullptr; auto nextBasicBlock = (iterCopy != m_basicBlocks.end()) ? iterCopy->second.llvm() : nullptr;
compileBasicBlock(basicBlock, _bytecode, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock); compileBasicBlock(basicBlock, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock);
} }
// Code for special blocks: // Code for special blocks:
@ -165,6 +182,9 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::str
m_builder.SetInsertPoint(m_stopBB); m_builder.SetInsertPoint(m_stopBB);
m_builder.CreateRet(Constant::get(ReturnCode::Stop)); m_builder.CreateRet(Constant::get(ReturnCode::Stop));
m_builder.SetInsertPoint(abortBB);
m_builder.CreateRet(Constant::get(ReturnCode::OutOfGas));
removeDeadBlocks(); removeDeadBlocks();
// Link jump table target index // Link jump table target index
@ -224,7 +244,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::str
} }
void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode, RuntimeManager& _runtimeManager, void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runtimeManager,
Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock) Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock)
{ {
if (!_nextBasicBlock) // this is the last block in the code if (!_nextBasicBlock) // this is the last block in the code
@ -623,7 +643,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
case Instruction::PC: case Instruction::PC:
{ {
auto value = Constant::get(it - _bytecode.begin()); auto value = Constant::get(it - _basicBlock.begin() + _basicBlock.firstInstrIdx());
stack.push(value); stack.push(value);
break; break;
} }
@ -631,7 +651,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
case Instruction::GAS: case Instruction::GAS:
{ {
_gasMeter.commitCostBlock(); _gasMeter.commitCostBlock();
stack.push(_runtimeManager.getGas()); stack.push(m_builder.CreateZExt(_runtimeManager.getGas(), Type::Word));
break; break;
} }
@ -741,10 +761,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
_memory.require(initOff, initSize); _memory.require(initOff, initSize);
_gasMeter.commitCostBlock(); _gasMeter.commitCostBlock();
auto address = _ext.create(endowment, initOff, initSize);
auto gas = _runtimeManager.getGas();
auto address = _ext.create(gas, endowment, initOff, initSize);
_runtimeManager.setGas(gas);
stack.push(address); stack.push(address);
break; break;
} }
@ -752,7 +769,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
case Instruction::CALL: case Instruction::CALL:
case Instruction::CALLCODE: case Instruction::CALLCODE:
{ {
auto gas = stack.pop(); auto callGas256 = stack.pop();
auto codeAddress = stack.pop(); auto codeAddress = stack.pop();
auto value = stack.pop(); auto value = stack.pop();
auto inOff = stack.pop(); auto inOff = stack.pop();
@ -770,9 +787,13 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
if (inst == Instruction::CALLCODE) if (inst == Instruction::CALLCODE)
receiveAddress = _runtimeManager.get(RuntimeData::Address); receiveAddress = _runtimeManager.get(RuntimeData::Address);
_gasMeter.count(gas); auto gas = _runtimeManager.getGas();
auto ret = _ext.call(gas, receiveAddress, value, inOff, inSize, outOff, outSize, codeAddress); _gasMeter.count(callGas256);
_gasMeter.giveBack(gas); auto callGas = m_builder.CreateTrunc(callGas256, Type::Gas);
auto gasLeft = m_builder.CreateNSWSub(gas, callGas);
_runtimeManager.setGas(callGas);
auto ret = _ext.call(receiveAddress, value, inOff, inSize, outOff, outSize, codeAddress);
_gasMeter.giveBack(gasLeft);
stack.push(ret); stack.push(ret);
break; break;
} }
@ -825,12 +846,9 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
break; break;
} }
default: // Invalid instruction - runtime exception default: // Invalid instruction - abort
{ m_builder.CreateRet(Constant::get(ReturnCode::BadInstruction));
// TODO: Replace with return statement it = _basicBlock.end() - 1; // finish block compilation
_runtimeManager.raiseException(ReturnCode::BadInstruction);
}
} }
} }

9
evmjit/libevmjit/Compiler.h

@ -1,8 +1,5 @@
#pragma once #pragma once
#include <llvm/IR/IRBuilder.h>
#include "Common.h" #include "Common.h"
#include "BasicBlock.h" #include "BasicBlock.h"
@ -33,13 +30,13 @@ public:
Compiler(Options const& _options); Compiler(Options const& _options);
std::unique_ptr<llvm::Module> compile(bytes const& _bytecode, std::string const& _id); std::unique_ptr<llvm::Module> compile(code_iterator _begin, code_iterator _end, std::string const& _id);
private: private:
void createBasicBlocks(bytes const& _bytecode); void createBasicBlocks(code_iterator _begin, code_iterator _end);
void compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock); void compileBasicBlock(BasicBlock& _basicBlock, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock);
llvm::BasicBlock* getJumpTableBlock(); llvm::BasicBlock* getJumpTableBlock();

4
evmjit/libevmjit/CompilerHelper.cpp

@ -1,8 +1,8 @@
#include "CompilerHelper.h" #include "CompilerHelper.h"
#include <llvm/IR/Function.h> #include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h> #include <llvm/IR/Module.h>
#include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h" #include "RuntimeManager.h"

5
evmjit/libevmjit/CompilerHelper.h

@ -1,7 +1,8 @@
#pragma once #pragma once
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IRBuilder.h> #include <llvm/IR/IRBuilder.h>
#include "preprocessor/llvm_includes_end.h"
namespace dev namespace dev
@ -19,7 +20,7 @@ protected:
CompilerHelper(llvm::IRBuilder<>& _builder); CompilerHelper(llvm::IRBuilder<>& _builder);
CompilerHelper(const CompilerHelper&) = delete; CompilerHelper(const CompilerHelper&) = delete;
void operator=(CompilerHelper) = delete; CompilerHelper& operator=(CompilerHelper) = delete;
/// Reference to the IR module being compiled /// Reference to the IR module being compiled
llvm::Module* getModule(); llvm::Module* getModule();

3
evmjit/libevmjit/Endianness.cpp

@ -1,7 +1,8 @@
#include "Endianness.h" #include "Endianness.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "Type.h" #include "Type.h"

3
evmjit/libevmjit/Endianness.h

@ -1,7 +1,8 @@
#pragma once #pragma once
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IRBuilder.h> #include <llvm/IR/IRBuilder.h>
#include "preprocessor/llvm_includes_end.h"
namespace dev namespace dev
{ {

97
evmjit/libevmjit/ExecStats.cpp

@ -0,0 +1,97 @@
#include "ExecStats.h"
#include <iostream>
#include <iomanip>
#include <cassert>
#include "Utils.h"
namespace dev
{
namespace eth
{
namespace jit
{
void ExecStats::stateChanged(ExecState _state)
{
if (!CHECK(m_state != ExecState::Finished))
return;
auto now = clock::now();
if (_state != ExecState::Started)
{
assert(time[(int)m_state] == ExecStats::duration::zero());
time[(int)m_state] = now - m_tp;
}
m_state = _state;
m_tp = now;
}
namespace
{
struct StatsAgg
{
using unit = std::chrono::microseconds;
ExecStats::duration tot = ExecStats::duration::zero();
ExecStats::duration min = ExecStats::duration::max();
ExecStats::duration max = ExecStats::duration::zero();
size_t count = 0;
void update(ExecStats::duration _d)
{
++count;
tot += _d;
min = _d < min ? _d : min;
max = _d > max ? _d : max;
}
void output(char const* _name, std::ostream& _os)
{
auto avg = tot / count;
_os << std::setfill(' ')
<< std::setw(12) << std::left << _name
<< std::setw(10) << std::right << std::chrono::duration_cast<unit>(tot).count()
<< std::setw(10) << std::right << std::chrono::duration_cast<unit>(avg).count()
<< std::setw(10) << std::right << std::chrono::duration_cast<unit>(min).count()
<< std::setw(10) << std::right << std::chrono::duration_cast<unit>(max).count()
<< std::endl;
}
};
char const* getExecStateName(ExecState _state)
{
switch (_state)
{
case ExecState::Started: return "Start";
case ExecState::CacheLoad: return "CacheLoad";
case ExecState::CacheWrite: return "CacheWrite";
case ExecState::Compilation: return "Compilation";
case ExecState::CodeGen: return "CodeGen";
case ExecState::Execution: return "Execution";
case ExecState::Return: return "Return";
case ExecState::Finished: return "Finish";
}
return nullptr;
}
}
StatsCollector::~StatsCollector()
{
if (stats.empty())
return;
std::cout << " [us] total avg min max\n";
for (int i = 0; i < (int)ExecState::Finished; ++i)
{
StatsAgg agg;
for (auto&& s : stats)
agg.update(s->time[i]);
agg.output(getExecStateName(ExecState(i)), std::cout);
}
}
}
}
}

43
evmjit/libevmjit/ExecStats.h

@ -0,0 +1,43 @@
#pragma once
#include <chrono>
#include "ExecutionEngine.h"
namespace dev
{
namespace eth
{
namespace jit
{
class ExecStats : public ExecutionEngineListener
{
public:
using clock = std::chrono::high_resolution_clock;
using duration = clock::duration;
using time_point = clock::time_point;
std::string id;
duration time[(int)ExecState::Finished] = {};
void stateChanged(ExecState _state) override;
private:
ExecState m_state = {};
time_point m_tp = {};
};
class StatsCollector
{
public:
std::vector<std::unique_ptr<ExecStats>> stats;
~StatsCollector();
};
}
}
}

133
evmjit/libevmjit/ExecutionEngine.cpp

@ -1,24 +1,25 @@
#include "ExecutionEngine.h" #include "ExecutionEngine.h"
#include <chrono> #include <array>
#include <cstdlib> // env options #include <cstdlib> // env options
#include <iostream>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h> #include <llvm/IR/Module.h>
#include <llvm/ADT/Triple.h> #include <llvm/ADT/Triple.h>
#pragma warning(push)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <llvm/ExecutionEngine/ExecutionEngine.h> #include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/SectionMemoryManager.h> #include <llvm/ExecutionEngine/SectionMemoryManager.h>
#pragma warning(pop)
#pragma GCC diagnostic pop
#include <llvm/ExecutionEngine/MCJIT.h> #include <llvm/ExecutionEngine/MCJIT.h>
#include <llvm/Support/TargetSelect.h> #include <llvm/Support/TargetSelect.h>
#include <llvm/Support/Host.h> #include <llvm/Support/Host.h>
#include "preprocessor/llvm_includes_end.h"
#include "Runtime.h" #include "Runtime.h"
#include "Compiler.h" #include "Compiler.h"
#include "Cache.h" #include "Cache.h"
#include "ExecStats.h"
#include "Utils.h"
#include "BuildInfo.gen.h"
namespace dev namespace dev
{ {
@ -29,36 +30,22 @@ namespace jit
namespace namespace
{ {
typedef ReturnCode(*EntryFuncPtr)(Runtime*); using EntryFuncPtr = ReturnCode(*)(Runtime*);
ReturnCode runEntryFunc(EntryFuncPtr _mainFunc, Runtime* _runtime) std::string codeHash(i256 const& _hash)
{ {
// That function uses long jumps to handle "execeptions". static const auto size = sizeof(_hash);
// Do not create any non-POD objects here static const auto hexChars = "0123456789abcdef";
std::string str;
ReturnCode returnCode{}; str.resize(size * 2);
auto sj = setjmp(_runtime->getJmpBuf()); auto outIt = str.rbegin(); // reverse for BE
if (sj == 0) auto& arr = *(std::array<byte, size>*)&_hash;
returnCode = _mainFunc(_runtime); for (auto b : arr)
else
returnCode = static_cast<ReturnCode>(sj);
return returnCode;
}
std::string codeHash(bytes const& _code)
{
uint32_t hash = 0;
for (auto b : _code)
{ {
hash += b; *(outIt++) = hexChars[b & 0xf];
hash += (hash << 10); *(outIt++) = hexChars[b >> 4];
hash ^= (hash >> 6);
} }
hash += (hash << 3); return str;
hash ^= (hash >> 11);
hash += (hash << 15);
return std::to_string(hash);
} }
bool getEnvOption(char const* _name, bool _default) bool getEnvOption(char const* _name, bool _default)
@ -69,41 +56,41 @@ bool getEnvOption(char const* _name, bool _default)
return std::strtol(var, nullptr, 10) != 0; return std::strtol(var, nullptr, 10) != 0;
} }
bool showInfo()
{
auto show = getEnvOption("EVMJIT_INFO", false);
if (show)
{
std::cout << "The Ethereum EVM JIT " EVMJIT_VERSION_FULL " LLVM " LLVM_VERSION << std::endl;
}
return show;
}
} }
ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _env) ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
{ {
static std::unique_ptr<llvm::ExecutionEngine> ee; // TODO: Use Managed Objects from LLVM?
static auto debugDumpModule = getEnvOption("EVMJIT_DUMP", false); static auto debugDumpModule = getEnvOption("EVMJIT_DUMP", false);
static auto objectCacheEnabled = getEnvOption("EVMJIT_CACHE", true); static auto objectCacheEnabled = getEnvOption("EVMJIT_CACHE", true);
static auto statsCollectingEnabled = getEnvOption("EVMJIT_STATS", false);
static auto infoShown = showInfo();
(void) infoShown;
auto mainFuncName = codeHash(_code); std::unique_ptr<ExecStats> listener{new ExecStats};
EntryFuncPtr entryFuncPtr{}; listener->stateChanged(ExecState::Started);
Runtime runtime(_data, _env); // TODO: I don't know why but it must be created before getFunctionAddress() calls
if (ee && (entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName))) auto objectCache = objectCacheEnabled ? Cache::getObjectCache(listener.get()) : nullptr;
{
} static std::unique_ptr<llvm::ExecutionEngine> ee;
else
{
auto objectCache = objectCacheEnabled ? Cache::getObjectCache() : nullptr;
std::unique_ptr<llvm::Module> module;
if (objectCache)
module = Cache::getObject(mainFuncName);
if (!module)
module = Compiler({}).compile(_code, mainFuncName);
if (debugDumpModule)
module->dump();
if (!ee) if (!ee)
{ {
llvm::InitializeNativeTarget(); llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter(); llvm::InitializeNativeTargetAsmPrinter();
auto module = std::unique_ptr<llvm::Module>(new llvm::Module({}, llvm::getGlobalContext()));
llvm::EngineBuilder builder(module.get()); llvm::EngineBuilder builder(module.get());
builder.setEngineKind(llvm::EngineKind::JIT); builder.setEngineKind(llvm::EngineKind::JIT);
builder.setUseMCJIT(true); builder.setUseMCJIT(true);
std::unique_ptr<llvm::SectionMemoryManager> memoryManager(new llvm::SectionMemoryManager);
builder.setMCJITMemoryManager(memoryManager.get());
builder.setOptLevel(llvm::CodeGenOpt::None); builder.setOptLevel(llvm::CodeGenOpt::None);
auto triple = llvm::Triple(llvm::sys::getProcessTriple()); auto triple = llvm::Triple(llvm::sys::getProcessTriple());
@ -112,39 +99,51 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en
module->setTargetTriple(triple.str()); module->setTargetTriple(triple.str());
ee.reset(builder.create()); ee.reset(builder.create());
if (!ee) if (!CHECK(ee))
return ReturnCode::LLVMConfigError; return ReturnCode::LLVMConfigError;
module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module
memoryManager.release(); // and memory manager
if (objectCache)
ee->setObjectCache(objectCache); ee->setObjectCache(objectCache);
entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
} }
else
{ static StatsCollector statsCollector;
auto mainFuncName = codeHash(_data->codeHash);
Runtime runtime(_data, _env); // TODO: I don't know why but it must be created before getFunctionAddress() calls
auto entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
if (!entryFuncPtr) if (!entryFuncPtr)
{ {
auto module = objectCache ? Cache::getObject(mainFuncName) : nullptr;
if (!module)
{
listener->stateChanged(ExecState::Compilation);
assert(_data->code || !_data->codeSize); //TODO: Is it good idea to execute empty code?
module = Compiler({}).compile(_data->code, _data->code + _data->codeSize, mainFuncName);
}
if (debugDumpModule)
module->dump();
ee->addModule(module.get()); ee->addModule(module.get());
module.release(); module.release();
listener->stateChanged(ExecState::CodeGen);
entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName); entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
} }
} if (!CHECK(entryFuncPtr))
} return ReturnCode::LLVMLinkError;
assert(entryFuncPtr);
auto executionStartTime = std::chrono::high_resolution_clock::now(); listener->stateChanged(ExecState::Execution);
auto returnCode = entryFuncPtr(&runtime);
listener->stateChanged(ExecState::Return);
auto returnCode = runEntryFunc(entryFuncPtr, &runtime);
if (returnCode == ReturnCode::Return) if (returnCode == ReturnCode::Return)
{ {
returnData = runtime.getReturnData(); // Save reference to return data returnData = runtime.getReturnData(); // Save reference to return data
std::swap(m_memory, runtime.getMemory()); // Take ownership of memory std::swap(m_memory, runtime.getMemory()); // Take ownership of memory
} }
listener->stateChanged(ExecState::Finished);
auto executionEndTime = std::chrono::high_resolution_clock::now(); if (statsCollectingEnabled)
clog(JIT) << " + " << std::chrono::duration_cast<std::chrono::milliseconds>(executionEndTime - executionStartTime).count() << " ms\n"; statsCollector.stats.push_back(std::move(listener));
return returnCode; return returnCode;
} }

32
evmjit/libevmjit/ExecutionEngine.h

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <memory>
#include "RuntimeData.h" #include "RuntimeData.h"
namespace dev namespace dev
@ -9,14 +11,40 @@ namespace eth
namespace jit namespace jit
{ {
enum class ExecState
{
Started,
CacheLoad,
CacheWrite,
Compilation,
CodeGen,
Execution,
Return,
Finished
};
class ExecutionEngineListener
{
public:
ExecutionEngineListener() = default;
ExecutionEngineListener(ExecutionEngineListener const&) = delete;
ExecutionEngineListener& operator=(ExecutionEngineListener) = delete;
virtual ~ExecutionEngineListener() {}
virtual void executionStarted() {}
virtual void executionEnded() {}
virtual void stateChanged(ExecState) {}
};
class ExecutionEngine class ExecutionEngine
{ {
public: public:
ExecutionEngine() = default; ExecutionEngine() = default;
ExecutionEngine(ExecutionEngine const&) = delete; ExecutionEngine(ExecutionEngine const&) = delete;
void operator=(ExecutionEngine) = delete; ExecutionEngine& operator=(ExecutionEngine) = delete;
EXPORT ReturnCode run(bytes const& _code, RuntimeData* _data, Env* _env); EXPORT ReturnCode run(RuntimeData* _data, Env* _env);
/// Reference to returned data (RETURN opcode used) /// Reference to returned data (RETURN opcode used)
bytes_ref returnData; bytes_ref returnData;

31
evmjit/libevmjit/Ext.cpp

@ -1,9 +1,8 @@
#include "Ext.h" #include "Ext.h"
#include <llvm/IR/Function.h> #include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/TypeBuilder.h>
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h" #include "RuntimeManager.h"
#include "Memory.h" #include "Memory.h"
@ -41,8 +40,8 @@ std::array<FuncDesc, sizeOf<EnvFunc>::value> const& getEnvFuncDescs()
FuncDesc{"env_sstore", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, FuncDesc{"env_sstore", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, Type::WordPtr})}, FuncDesc{"env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, Type::WordPtr})},
FuncDesc{"env_balance", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, FuncDesc{"env_balance", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_create", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr})}, FuncDesc{"env_create", getFunctionType(Type::Void, {Type::EnvPtr, Type::GasPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr})},
FuncDesc{"env_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr})}, FuncDesc{"env_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::GasPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr})},
FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})}, FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})}, FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})},
@ -60,14 +59,16 @@ llvm::Function* createFunc(EnvFunc _id, llvm::Module* _module)
llvm::Value* Ext::getArgAlloca() llvm::Value* Ext::getArgAlloca()
{ {
auto& a = m_argAllocas[m_argCounter++]; auto& a = m_argAllocas[m_argCounter];
if (!a) if (!a)
{ {
// FIXME: Improve order and names
InsertPointGuard g{getBuilder()}; InsertPointGuard g{getBuilder()};
getBuilder().SetInsertPoint(getMainFunction()->front().getFirstNonPHI()); auto allocaIt = getMainFunction()->front().begin();
a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg"); std::advance(allocaIt, m_argCounter); // Skip already created allocas
getBuilder().SetInsertPoint(allocaIt);
a = getBuilder().CreateAlloca(Type::Word, nullptr, {"a.", std::to_string(m_argCounter)});
} }
++m_argCounter;
return a; return a;
} }
@ -124,30 +125,26 @@ llvm::Value* Ext::blockhash(llvm::Value* _number)
return Endianness::toNative(getBuilder(), hash); return Endianness::toNative(getBuilder(), hash);
} }
llvm::Value* Ext::create(llvm::Value*& _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize) llvm::Value* Ext::create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize)
{ {
auto gas = byPtr(_gas);
auto ret = getArgAlloca(); auto ret = getArgAlloca();
auto begin = m_memoryMan.getBytePtr(_initOff); auto begin = m_memoryMan.getBytePtr(_initOff);
auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size"); auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size");
createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), gas, byPtr(_endowment), begin, size, ret}); createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), byPtr(_endowment), begin, size, ret});
_gas = m_builder.CreateLoad(gas); // Return gas
llvm::Value* address = m_builder.CreateLoad(ret); llvm::Value* address = m_builder.CreateLoad(ret);
address = Endianness::toNative(m_builder, address); address = Endianness::toNative(m_builder, address);
return address; return address;
} }
llvm::Value* Ext::call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress) llvm::Value* Ext::call(llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress)
{ {
auto gas = byPtr(_gas);
auto receiveAddress = Endianness::toBE(m_builder, _receiveAddress); auto receiveAddress = Endianness::toBE(m_builder, _receiveAddress);
auto inBeg = m_memoryMan.getBytePtr(_inOff); auto inBeg = m_memoryMan.getBytePtr(_inOff);
auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size"); auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size");
auto outBeg = m_memoryMan.getBytePtr(_outOff); auto outBeg = m_memoryMan.getBytePtr(_outOff);
auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size"); auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size");
auto codeAddress = Endianness::toBE(m_builder, _codeAddress); auto codeAddress = Endianness::toBE(m_builder, _codeAddress);
auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), gas, byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(codeAddress)}); auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(codeAddress)});
_gas = m_builder.CreateLoad(gas); // Return gas
return m_builder.CreateZExt(ret, Type::Word, "ret"); return m_builder.CreateZExt(ret, Type::Word, "ret");
} }

6
evmjit/libevmjit/Ext.h

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <array> #include <array>
#include "CompilerHelper.h" #include "CompilerHelper.h"
namespace dev namespace dev
@ -50,8 +50,8 @@ public:
llvm::Value* balance(llvm::Value* _address); llvm::Value* balance(llvm::Value* _address);
llvm::Value* calldataload(llvm::Value* _index); llvm::Value* calldataload(llvm::Value* _index);
llvm::Value* create(llvm::Value*& _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize); llvm::Value* create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize);
llvm::Value* call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress); llvm::Value* call(llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress);
llvm::Value* blockhash(llvm::Value* _number); llvm::Value* blockhash(llvm::Value* _number);
llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize); llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize);

107
evmjit/libevmjit/GasMeter.cpp

@ -1,11 +1,9 @@
#include "GasMeter.h" #include "GasMeter.h"
#include <llvm/IR/GlobalVariable.h> #include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "Type.h"
#include "Ext.h" #include "Ext.h"
#include "RuntimeManager.h" #include "RuntimeManager.h"
@ -19,29 +17,29 @@ namespace jit
namespace // Helper functions namespace // Helper functions
{ {
uint64_t const c_stepGas = 1; int64_t const c_stepGas = 1;
uint64_t const c_balanceGas = 20; int64_t const c_balanceGas = 20;
uint64_t const c_sha3Gas = 10; int64_t const c_sha3Gas = 10;
uint64_t const c_sha3WordGas = 10; int64_t const c_sha3WordGas = 10;
uint64_t const c_sloadGas = 20; int64_t const c_sloadGas = 20;
uint64_t const c_sstoreSetGas = 300; int64_t const c_sstoreSetGas = 300;
uint64_t const c_sstoreResetGas = 100; int64_t const c_sstoreResetGas = 100;
uint64_t const c_sstoreRefundGas = 100; int64_t const c_sstoreRefundGas = 100;
uint64_t const c_createGas = 100; int64_t const c_createGas = 100;
uint64_t const c_createDataGas = 5; int64_t const c_createDataGas = 5;
uint64_t const c_callGas = 20; int64_t const c_callGas = 20;
uint64_t const c_expGas = 1; int64_t const c_expGas = 1;
uint64_t const c_expByteGas = 1; int64_t const c_expByteGas = 1;
uint64_t const c_memoryGas = 1; int64_t const c_memoryGas = 1;
uint64_t const c_txDataZeroGas = 1; int64_t const c_txDataZeroGas = 1;
uint64_t const c_txDataNonZeroGas = 5; int64_t const c_txDataNonZeroGas = 5;
uint64_t const c_txGas = 500; int64_t const c_txGas = 500;
uint64_t const c_logGas = 32; int64_t const c_logGas = 32;
uint64_t const c_logDataGas = 1; int64_t const c_logDataGas = 1;
uint64_t const c_logTopicGas = 32; int64_t const c_logTopicGas = 32;
uint64_t const c_copyGas = 1; int64_t const c_copyGas = 1;
uint64_t getStepCost(Instruction inst) int64_t getStepCost(Instruction inst)
{ {
switch (inst) switch (inst)
{ {
@ -72,7 +70,7 @@ uint64_t getStepCost(Instruction inst)
case Instruction::LOG3: case Instruction::LOG3:
case Instruction::LOG4: case Instruction::LOG4:
{ {
auto numTopics = static_cast<uint64_t>(inst) - static_cast<uint64_t>(Instruction::LOG0); auto numTopics = static_cast<int64_t>(inst) - static_cast<int64_t>(Instruction::LOG0);
return c_logGas + numTopics * c_logTopicGas; return c_logGas + numTopics * c_logTopicGas;
} }
} }
@ -86,7 +84,7 @@ GasMeter::GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager)
{ {
auto module = getModule(); auto module = getModule();
llvm::Type* gasCheckArgs[] = {Type::RuntimePtr, Type::Word}; llvm::Type* gasCheckArgs[] = {Type::RuntimePtr, Type::Gas};
m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, gasCheckArgs, false), llvm::Function::PrivateLinkage, "gas.check", module); m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, gasCheckArgs, false), llvm::Function::PrivateLinkage, "gas.check", module);
InsertPointGuard guard(m_builder); InsertPointGuard guard(m_builder);
@ -94,22 +92,22 @@ GasMeter::GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager)
auto outOfGasBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfGas", m_gasCheckFunc); auto outOfGasBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfGas", m_gasCheckFunc);
auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_gasCheckFunc); auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_gasCheckFunc);
auto rt = &m_gasCheckFunc->getArgumentList().front();
rt->setName("rt");
auto cost = rt->getNextNode();
cost->setName("cost");
m_builder.SetInsertPoint(checkBB); m_builder.SetInsertPoint(checkBB);
auto arg = m_gasCheckFunc->arg_begin();
arg->setName("rt");
++arg;
arg->setName("cost");
auto cost = arg;
auto gas = m_runtimeManager.getGas(); auto gas = m_runtimeManager.getGas();
auto isOutOfGas = m_builder.CreateICmpUGT(cost, gas, "isOutOfGas"); gas = m_builder.CreateNSWSub(gas, cost, "gasUpdated");
auto isOutOfGas = m_builder.CreateICmpSLT(gas, m_builder.getInt64(0), "isOutOfGas"); // gas < 0, with gas == 0 we can still do 0 cost instructions
m_builder.CreateCondBr(isOutOfGas, outOfGasBB, updateBB); m_builder.CreateCondBr(isOutOfGas, outOfGasBB, updateBB);
m_builder.SetInsertPoint(outOfGasBB); m_builder.SetInsertPoint(outOfGasBB);
m_runtimeManager.raiseException(ReturnCode::OutOfGas); m_runtimeManager.abort();
m_builder.CreateUnreachable(); m_builder.CreateUnreachable();
m_builder.SetInsertPoint(updateBB); m_builder.SetInsertPoint(updateBB);
gas = m_builder.CreateSub(gas, cost);
m_runtimeManager.setGas(gas); m_runtimeManager.setGas(gas);
m_builder.CreateRetVoid(); m_builder.CreateRetVoid();
} }
@ -119,7 +117,7 @@ void GasMeter::count(Instruction _inst)
if (!m_checkCall) if (!m_checkCall)
{ {
// Create gas check call with mocked block cost at begining of current cost-block // Create gas check call with mocked block cost at begining of current cost-block
m_checkCall = createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), llvm::UndefValue::get(Type::Word)}); m_checkCall = createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), llvm::UndefValue::get(Type::Gas)});
} }
m_blockCost += getStepCost(_inst); m_blockCost += getStepCost(_inst);
@ -127,6 +125,15 @@ void GasMeter::count(Instruction _inst)
void GasMeter::count(llvm::Value* _cost) void GasMeter::count(llvm::Value* _cost)
{ {
if (_cost->getType() == Type::Word)
{
auto gasMax256 = m_builder.CreateZExt(Constant::gasMax, Type::Word);
auto tooHigh = m_builder.CreateICmpUGT(_cost, gasMax256, "costTooHigh");
auto cost64 = m_builder.CreateTrunc(_cost, Type::Gas);
_cost = m_builder.CreateSelect(tooHigh, Constant::gasMax, cost64, "cost");
}
assert(_cost->getType() == Type::Gas);
createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), _cost}); createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), _cost});
} }
@ -136,12 +143,13 @@ void GasMeter::countExp(llvm::Value* _exponent)
// lz - leading zeros // lz - leading zeros
// cost = ((256 - lz) + 7) / 8 // cost = ((256 - lz) + 7) / 8
// OPT: All calculations can be done on 32/64 bits // OPT: Can gas update be done in exp algorithm?
auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word); auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word);
auto lz = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false)); auto lz256 = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false));
auto sigBits = m_builder.CreateSub(Constant::get(256), lz); auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz");
auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, Constant::get(7)), Constant::get(8)); auto sigBits = m_builder.CreateSub(m_builder.getInt64(256), lz, "sigBits");
auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, m_builder.getInt64(7)), m_builder.getInt64(8));
count(sigBytes); count(sigBytes);
} }
@ -154,8 +162,8 @@ void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValu
auto newValueIsntZero = m_builder.CreateICmpNE(_newValue, Constant::get(0), "newValueIsntZero"); auto newValueIsntZero = m_builder.CreateICmpNE(_newValue, Constant::get(0), "newValueIsntZero");
auto isInsert = m_builder.CreateAnd(oldValueIsZero, newValueIsntZero, "isInsert"); auto isInsert = m_builder.CreateAnd(oldValueIsZero, newValueIsntZero, "isInsert");
auto isDelete = m_builder.CreateAnd(oldValueIsntZero, newValueIsZero, "isDelete"); auto isDelete = m_builder.CreateAnd(oldValueIsntZero, newValueIsZero, "isDelete");
auto cost = m_builder.CreateSelect(isInsert, Constant::get(c_sstoreSetGas), Constant::get(c_sstoreResetGas), "cost"); auto cost = m_builder.CreateSelect(isInsert, m_builder.getInt64(c_sstoreSetGas), m_builder.getInt64(c_sstoreResetGas), "cost");
cost = m_builder.CreateSelect(isDelete, Constant::get(0), cost, "cost"); cost = m_builder.CreateSelect(isDelete, m_builder.getInt64(0), cost, "cost");
count(cost); count(cost);
} }
@ -173,17 +181,16 @@ void GasMeter::countSha3Data(llvm::Value* _dataLength)
assert(m_blockCost > 0); // SHA3 instruction is already counted assert(m_blockCost > 0); // SHA3 instruction is already counted
// TODO: This round ups to 32 happens in many places // TODO: This round ups to 32 happens in many places
// FIXME: 64-bit arith used, but not verified
static_assert(c_sha3WordGas != 1, "SHA3 data cost has changed. Update GasMeter"); static_assert(c_sha3WordGas != 1, "SHA3 data cost has changed. Update GasMeter");
auto dataLength64 = getBuilder().CreateTrunc(_dataLength, Type::lowPrecision); auto dataLength64 = getBuilder().CreateTrunc(_dataLength, Type::Gas);
auto words64 = m_builder.CreateUDiv(m_builder.CreateAdd(dataLength64, getBuilder().getInt64(31)), getBuilder().getInt64(32)); auto words64 = m_builder.CreateUDiv(m_builder.CreateNUWAdd(dataLength64, getBuilder().getInt64(31)), getBuilder().getInt64(32));
auto cost64 = m_builder.CreateNUWMul(getBuilder().getInt64(c_sha3WordGas), words64); auto cost64 = m_builder.CreateNUWMul(getBuilder().getInt64(c_sha3WordGas), words64);
auto cost = getBuilder().CreateZExt(cost64, Type::Word); count(cost64);
count(cost);
} }
void GasMeter::giveBack(llvm::Value* _gas) void GasMeter::giveBack(llvm::Value* _gas)
{ {
assert(_gas->getType() == Type::Gas);
m_runtimeManager.setGas(m_builder.CreateAdd(m_runtimeManager.getGas(), _gas)); m_runtimeManager.setGas(m_builder.CreateAdd(m_runtimeManager.getGas(), _gas));
} }
@ -199,7 +206,7 @@ void GasMeter::commitCostBlock()
return; return;
} }
m_checkCall->setArgOperand(1, Constant::get(m_blockCost)); // Update block cost in gas check call m_checkCall->setArgOperand(1, m_builder.getInt64(m_blockCost)); // Update block cost in gas check call
m_checkCall = nullptr; // End cost-block m_checkCall = nullptr; // End cost-block
m_blockCost = 0; m_blockCost = 0;
} }

3
evmjit/libevmjit/GasMeter.h

@ -1,4 +1,3 @@
#pragma once #pragma once
#include "CompilerHelper.h" #include "CompilerHelper.h"
@ -50,7 +49,7 @@ public:
private: private:
/// Cumulative gas cost of a block of instructions /// Cumulative gas cost of a block of instructions
/// @TODO Handle overflow /// @TODO Handle overflow
uint64_t m_blockCost = 0; int64_t m_blockCost = 0;
llvm::CallInst* m_checkCall = nullptr; llvm::CallInst* m_checkCall = nullptr;
llvm::Function* m_gasCheckFunc = nullptr; llvm::Function* m_gasCheckFunc = nullptr;

8
evmjit/libevmjit/Instruction.cpp

@ -1,6 +1,8 @@
#include "Instruction.h" #include "Instruction.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/ADT/APInt.h> #include <llvm/ADT/APInt.h>
#include "preprocessor/llvm_includes_end.h"
namespace dev namespace dev
{ {
@ -9,7 +11,7 @@ namespace eth
namespace jit namespace jit
{ {
llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _end) llvm::APInt readPushData(code_iterator& _curr, code_iterator _end)
{ {
auto pushInst = *_curr; auto pushInst = *_curr;
assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32);
@ -26,7 +28,7 @@ llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _en
return value; return value;
} }
void skipPushData(bytes::const_iterator& _curr, bytes::const_iterator _end) void skipPushData(code_iterator& _curr, code_iterator _end)
{ {
auto pushInst = *_curr; auto pushInst = *_curr;
assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32);

4
evmjit/libevmjit/Instruction.h

@ -161,11 +161,11 @@ enum class Instruction: uint8_t
/// Reads PUSH data from pointed fragment of bytecode and constructs number out of it /// Reads PUSH data from pointed fragment of bytecode and constructs number out of it
/// Reading out of bytecode means reading 0 /// Reading out of bytecode means reading 0
/// @param _curr is updated and points the last real byte read /// @param _curr is updated and points the last real byte read
llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _end); llvm::APInt readPushData(code_iterator& _curr, code_iterator _end);
/// Skips PUSH data in pointed fragment of bytecode. /// Skips PUSH data in pointed fragment of bytecode.
/// @param _curr is updated and points the last real byte skipped /// @param _curr is updated and points the last real byte skipped
void skipPushData(bytes::const_iterator& _curr, bytes::const_iterator _end); void skipPushData(code_iterator& _curr, code_iterator _end);
#define ANY_PUSH PUSH1: \ #define ANY_PUSH PUSH1: \
case Instruction::PUSH2: \ case Instruction::PUSH2: \

93
evmjit/libevmjit/Memory.cpp

@ -1,14 +1,8 @@
#include "Memory.h" #include "Memory.h"
#include <vector> #include "preprocessor/llvm_includes_start.h"
#include <iostream>
#include <iomanip>
#include <cstdint>
#include <cassert>
#include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "Type.h" #include "Type.h"
#include "Runtime.h" #include "Runtime.h"
@ -26,23 +20,15 @@ namespace jit
Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter): Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter):
RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed
m_gasMeter(_gasMeter) m_gasMeter(_gasMeter)
{ {}
llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr};
m_resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule());
llvm::AttrBuilder attrBuilder;
attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly);
m_resize->setAttributes(llvm::AttributeSet::get(m_resize->getContext(), 1, attrBuilder));
m_require = createRequireFunc(_gasMeter); llvm::Function* Memory::getRequireFunc()
m_loadWord = createFunc(false, Type::Word, _gasMeter);
m_storeWord = createFunc(true, Type::Word, _gasMeter);
m_storeByte = createFunc(true, Type::Byte, _gasMeter);
}
llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter)
{ {
auto& func = m_require;
if (!func)
{
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word}; llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word};
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule()); func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule());
auto rt = func->arg_begin(); auto rt = func->arg_begin();
rt->setName("rt"); rt->setName("rt");
auto offset = rt->getNextNode(); auto offset = rt->getNextNode();
@ -50,6 +36,12 @@ llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter)
auto size = offset->getNextNode(); auto size = offset->getNextNode();
size->setName("size"); size->setName("size");
llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr};
auto resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule());
llvm::AttrBuilder attrBuilder;
attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly);
resize->setAttributes(llvm::AttributeSet::get(resize->getContext(), 1, attrBuilder));
auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func); auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func);
auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func);
auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func); auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func);
@ -87,10 +79,10 @@ llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter)
sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq"); sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq");
auto words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k auto words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k
auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords"); auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords");
_gasMeter.countMemory(newWords); m_gasMeter.countMemory(newWords);
// Resize // Resize
m_builder.CreateStore(sizeRequired, sizePtr); m_builder.CreateStore(sizeRequired, sizePtr);
auto newData = m_builder.CreateCall2(m_resize, rt, sizePtr, "newData"); auto newData = m_builder.CreateCall2(resize, rt, sizePtr, "newData");
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3);
m_builder.CreateStore(newData, dataPtr); m_builder.CreateStore(newData, dataPtr);
m_builder.CreateBr(returnBB); m_builder.CreateBr(returnBB);
@ -98,6 +90,7 @@ llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter)
// BB "Return" // BB "Return"
m_builder.SetInsertPoint(returnBB); m_builder.SetInsertPoint(returnBB);
m_builder.CreateRetVoid(); m_builder.CreateRetVoid();
}
return func; return func;
} }
@ -143,21 +136,45 @@ llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMet
return func; return func;
} }
llvm::Function* Memory::getLoadWordFunc()
{
auto& func = m_loadWord;
if (!func)
func = createFunc(false, Type::Word, m_gasMeter);
return func;
}
llvm::Function* Memory::getStoreWordFunc()
{
auto& func = m_storeWord;
if (!func)
func = createFunc(true, Type::Word, m_gasMeter);
return func;
}
llvm::Function* Memory::getStoreByteFunc()
{
auto& func = m_storeByte;
if (!func)
func = createFunc(true, Type::Byte, m_gasMeter);
return func;
}
llvm::Value* Memory::loadWord(llvm::Value* _addr) llvm::Value* Memory::loadWord(llvm::Value* _addr)
{ {
return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr}); return createCall(getLoadWordFunc(), {getRuntimeManager().getRuntimePtr(), _addr});
} }
void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word)
{ {
createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word}); createCall(getStoreWordFunc(), {getRuntimeManager().getRuntimePtr(), _addr, _word});
} }
void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word) void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word)
{ {
auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte");
createCall(m_storeByte, {getRuntimeManager().getRuntimePtr(), _addr, byte}); createCall(getStoreByteFunc(), {getRuntimeManager().getRuntimePtr(), _addr, byte});
} }
llvm::Value* Memory::getData() llvm::Value* Memory::getData()
@ -182,7 +199,12 @@ llvm::Value* Memory::getBytePtr(llvm::Value* _index)
void Memory::require(llvm::Value* _offset, llvm::Value* _size) void Memory::require(llvm::Value* _offset, llvm::Value* _size)
{ {
createCall(m_require, {getRuntimeManager().getRuntimePtr(), _offset, _size}); if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(_size))
{
if (!constant->getValue())
return;
}
createCall(getRequireFunc(), {getRuntimeManager().getRuntimePtr(), _offset, _size});
} }
void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx,
@ -192,7 +214,8 @@ void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value*
// Additional copy cost // Additional copy cost
// TODO: This round ups to 32 happens in many places // TODO: This round ups to 32 happens in many places
auto copyWords = m_builder.CreateUDiv(m_builder.CreateAdd(_reqBytes, Constant::get(31)), Constant::get(32)); auto reqBytes = m_builder.CreateTrunc(_reqBytes, Type::Gas);
auto copyWords = m_builder.CreateUDiv(m_builder.CreateNUWAdd(reqBytes, m_builder.getInt64(31)), m_builder.getInt64(32));
m_gasMeter.countCopy(copyWords); m_gasMeter.countCopy(copyWords);
// Algorithm: // Algorithm:
@ -205,14 +228,12 @@ void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value*
// bytesToCopy = select(isOutsideData, 0, bytesToCopy0) // bytesToCopy = select(isOutsideData, 0, bytesToCopy0)
auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize); auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize);
auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::lowPrecision); auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::Size);
auto size64 = m_builder.CreateTrunc(_srcSize, Type::lowPrecision); auto size64 = m_builder.CreateTrunc(_srcSize, Type::Size);
auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64); auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64);
auto reqBytes64 = m_builder.CreateTrunc(_reqBytes, Type::lowPrecision); auto outOfBound = m_builder.CreateICmpUGT(reqBytes, dataLeftSize);
auto outOfBound = m_builder.CreateICmpUGT(reqBytes64, dataLeftSize); auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes);
auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes64); auto bytesToCopy = m_builder.CreateSelect(isOutsideData, m_builder.getInt64(0), bytesToCopyInner);
auto zero64 = llvm::ConstantInt::get(Type::lowPrecision, 0); // TODO: Cache common constants
auto bytesToCopy = m_builder.CreateSelect(isOutsideData, zero64, bytesToCopyInner);
auto src = m_builder.CreateGEP(_srcPtr, idx64, "src"); auto src = m_builder.CreateGEP(_srcPtr, idx64, "src");
auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); // Never allow memory index be a type bigger than i64 auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); // Never allow memory index be a type bigger than i64

15
evmjit/libevmjit/Memory.h

@ -31,13 +31,16 @@ private:
GasMeter& m_gasMeter; GasMeter& m_gasMeter;
llvm::Function* createFunc(bool _isStore, llvm::Type* _type, GasMeter& _gasMeter); llvm::Function* createFunc(bool _isStore, llvm::Type* _type, GasMeter& _gasMeter);
llvm::Function* createRequireFunc(GasMeter& _gasMeter);
llvm::Function* m_resize; llvm::Function* getRequireFunc();
llvm::Function* m_require; llvm::Function* getLoadWordFunc();
llvm::Function* m_loadWord; llvm::Function* getStoreWordFunc();
llvm::Function* m_storeWord; llvm::Function* getStoreByteFunc();
llvm::Function* m_storeByte;
llvm::Function* m_require = nullptr;
llvm::Function* m_loadWord = nullptr;
llvm::Function* m_storeWord = nullptr;
llvm::Function* m_storeByte = nullptr;
}; };
} }

8
evmjit/libevmjit/Runtime.cpp

@ -1,9 +1,6 @@
#include "Runtime.h" #include "Runtime.h"
#include <llvm/IR/GlobalVariable.h> #include <cassert>
#include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h>
namespace dev namespace dev
{ {
@ -14,8 +11,7 @@ namespace jit
Runtime::Runtime(RuntimeData* _data, Env* _env) : Runtime::Runtime(RuntimeData* _data, Env* _env) :
m_data(*_data), m_data(*_data),
m_env(*_env), m_env(*_env)
m_currJmpBuf(m_jmpBuf)
{} {}
bytes_ref Runtime::getReturnData() const bytes_ref Runtime::getReturnData() const

8
evmjit/libevmjit/Runtime.h

@ -1,7 +1,5 @@
#pragma once #pragma once
#include <csetjmp>
#include "RuntimeData.h" #include "RuntimeData.h"
namespace dev namespace dev
@ -13,7 +11,6 @@ namespace jit
using StackImpl = std::vector<i256>; using StackImpl = std::vector<i256>;
using MemoryImpl = bytes; using MemoryImpl = bytes;
using jmp_buf_ref = decltype(&std::jmp_buf{}[0]);
class Runtime class Runtime
{ {
@ -25,18 +22,15 @@ public:
StackImpl& getStack() { return m_stack; } StackImpl& getStack() { return m_stack; }
MemoryImpl& getMemory() { return m_memory; } MemoryImpl& getMemory() { return m_memory; }
Env* getEnvPtr() { return &m_env; }
bytes_ref getReturnData() const; bytes_ref getReturnData() const;
jmp_buf_ref getJmpBuf() { return m_jmpBuf; }
private: private:
RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract. RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract.
Env& m_env; ///< Pointer to environment proxy. Expected by compiled contract. Env& m_env; ///< Pointer to environment proxy. Expected by compiled contract.
jmp_buf_ref m_currJmpBuf; ///< Pointer to jump buffer. Expected by compiled contract. void* m_currJmpBuf = nullptr; ///< Pointer to jump buffer. Expected by compiled contract.
byte* m_memoryData = nullptr; byte* m_memoryData = nullptr;
i256 m_memorySize; i256 m_memorySize;
std::jmp_buf m_jmpBuf;
StackImpl m_stack; StackImpl m_stack;
MemoryImpl m_memory; MemoryImpl m_memory;
}; };

4
evmjit/libevmjit/RuntimeData.h

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "Utils.h" #include "Common.h"
namespace dev namespace dev
{ {
@ -50,6 +49,7 @@ struct RuntimeData
int64_t timestamp = 0; int64_t timestamp = 0;
byte const* code = nullptr; byte const* code = nullptr;
uint64_t codeSize = 0; uint64_t codeSize = 0;
i256 codeHash;
}; };
/// VM Environment (ExtVM) opaque type /// VM Environment (ExtVM) opaque type

54
evmjit/libevmjit/RuntimeManager.cpp

@ -1,12 +1,8 @@
#include "RuntimeManager.h" #include "RuntimeManager.h"
#include <llvm/IR/GlobalVariable.h> #include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "RuntimeData.h"
#include "Instruction.h"
namespace dev namespace dev
{ {
@ -87,9 +83,17 @@ llvm::Twine getName(RuntimeData::Index _index)
} }
} }
RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder): CompilerHelper(_builder) RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, llvm::Value* _jmpBuf, code_iterator _codeBegin, code_iterator _codeEnd):
CompilerHelper(_builder),
m_jmpBuf(_jmpBuf),
m_codeBegin(_codeBegin),
m_codeEnd(_codeEnd)
{ {
m_longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::longjmp); m_longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp);
// save jmpBuf to be used in helper functions
auto ptr = m_builder.CreateStructGEP(getRuntimePtr(), 2);
m_builder.CreateStore(m_jmpBuf, ptr, "jmpBufExt");
// Unpack data // Unpack data
auto rtPtr = getRuntimePtr(); auto rtPtr = getRuntimePtr();
@ -161,9 +165,10 @@ void RuntimeManager::registerSuicide(llvm::Value* _balanceAddress)
set(RuntimeData::SuicideDestAddress, _balanceAddress); set(RuntimeData::SuicideDestAddress, _balanceAddress);
} }
void RuntimeManager::raiseException(ReturnCode _returnCode) void RuntimeManager::abort(llvm::Value* _jmpBuf)
{ {
m_builder.CreateCall2(m_longjmp, getJmpBuf(), Constant::get(_returnCode)); auto longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp);
createCall(longjmp, {_jmpBuf});
} }
llvm::Value* RuntimeManager::get(Instruction _inst) llvm::Value* RuntimeManager::get(Instruction _inst)
@ -191,14 +196,14 @@ llvm::Value* RuntimeManager::getCallData()
llvm::Value* RuntimeManager::getCode() llvm::Value* RuntimeManager::getCode()
{ {
return get(RuntimeData::Code); // OPT Check what is faster
//return get(RuntimeData::Code);
return m_builder.CreateGlobalStringPtr({reinterpret_cast<char const*>(m_codeBegin), static_cast<size_t>(m_codeEnd - m_codeBegin)}, "code");
} }
llvm::Value* RuntimeManager::getCodeSize() llvm::Value* RuntimeManager::getCodeSize()
{ {
auto value = get(RuntimeData::CodeSize); return Constant::get(m_codeEnd - m_codeBegin);
assert(value->getType() == Type::Size);
return getBuilder().CreateZExt(value, Type::Word);
} }
llvm::Value* RuntimeManager::getCallDataSize() llvm::Value* RuntimeManager::getCallDataSize()
@ -208,23 +213,28 @@ llvm::Value* RuntimeManager::getCallDataSize()
return getBuilder().CreateZExt(value, Type::Word); return getBuilder().CreateZExt(value, Type::Word);
} }
llvm::Value* RuntimeManager::getJmpBuf() llvm::Value* RuntimeManager::getJmpBufExt()
{ {
auto ptr = getBuilder().CreateStructGEP(getRuntimePtr(), 2, "jmpbufPtr"); auto ptr = getBuilder().CreateStructGEP(getRuntimePtr(), 2);
return getBuilder().CreateLoad(ptr, "jmpbuf"); return getBuilder().CreateLoad(ptr, "jmpBufExt");
} }
llvm::Value* RuntimeManager::getGas() llvm::Value* RuntimeManager::getGas()
{ {
auto value = get(RuntimeData::Gas); auto gas = get(RuntimeData::Gas);
assert(value->getType() == Type::Size); assert(gas->getType() == Type::Gas);
return getBuilder().CreateZExt(value, Type::Word); return gas;
}
llvm::Value* RuntimeManager::getGasPtr()
{
return getPtr(RuntimeData::Gas);
} }
void RuntimeManager::setGas(llvm::Value* _gas) void RuntimeManager::setGas(llvm::Value* _gas)
{ {
auto newGas = getBuilder().CreateTrunc(_gas, Type::Size); assert(_gas->getType() == Type::Gas);
set(RuntimeData::Gas, newGas); set(RuntimeData::Gas, _gas);
} }
} }

17
evmjit/libevmjit/RuntimeManager.h

@ -15,25 +15,28 @@ namespace jit
class RuntimeManager: public CompilerHelper class RuntimeManager: public CompilerHelper
{ {
public: public:
RuntimeManager(llvm::IRBuilder<>& _builder); RuntimeManager(llvm::IRBuilder<>& _builder, llvm::Value* _jmpBuf, code_iterator _codeBegin, code_iterator _codeEnd);
llvm::Value* getRuntimePtr(); llvm::Value* getRuntimePtr();
llvm::Value* getDataPtr(); llvm::Value* getDataPtr();
llvm::Value* getEnvPtr(); // TODO: Can we make it const? llvm::Value* getEnvPtr();
llvm::Value* get(RuntimeData::Index _index); llvm::Value* get(RuntimeData::Index _index);
llvm::Value* get(Instruction _inst); llvm::Value* get(Instruction _inst);
llvm::Value* getGas(); // TODO: Remove llvm::Value* getGas();
llvm::Value* getGasPtr();
llvm::Value* getCallData(); llvm::Value* getCallData();
llvm::Value* getCode(); llvm::Value* getCode();
llvm::Value* getCodeSize(); llvm::Value* getCodeSize();
llvm::Value* getCallDataSize(); llvm::Value* getCallDataSize();
llvm::Value* getJmpBuf() { return m_jmpBuf; }
void setGas(llvm::Value* _gas); void setGas(llvm::Value* _gas);
void registerReturnData(llvm::Value* _index, llvm::Value* _size); void registerReturnData(llvm::Value* _index, llvm::Value* _size);
void registerSuicide(llvm::Value* _balanceAddress); void registerSuicide(llvm::Value* _balanceAddress);
void raiseException(ReturnCode _returnCode); void abort(llvm::Value* _jmpBuf);
void abort() { abort(getJmpBufExt()); }
static llvm::StructType* getRuntimeType(); static llvm::StructType* getRuntimeType();
static llvm::StructType* getRuntimeDataType(); static llvm::StructType* getRuntimeDataType();
@ -41,11 +44,15 @@ public:
private: private:
llvm::Value* getPtr(RuntimeData::Index _index); llvm::Value* getPtr(RuntimeData::Index _index);
void set(RuntimeData::Index _index, llvm::Value* _value); void set(RuntimeData::Index _index, llvm::Value* _value);
llvm::Value* getJmpBuf(); llvm::Value* getJmpBufExt();
llvm::Function* m_longjmp = nullptr; llvm::Function* m_longjmp = nullptr;
llvm::Value* const m_jmpBuf;
llvm::Value* m_dataPtr = nullptr; llvm::Value* m_dataPtr = nullptr;
llvm::Value* m_envPtr = nullptr; llvm::Value* m_envPtr = nullptr;
code_iterator m_codeBegin = {};
code_iterator m_codeEnd = {};
}; };
} }

111
evmjit/libevmjit/Stack.cpp

@ -1,10 +1,11 @@
#include "Stack.h" #include "Stack.h"
#include "RuntimeManager.h"
#include "Runtime.h"
#include "Type.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Function.h> #include <llvm/IR/Function.h>
#include <llvm/IR/TypeBuilder.h> #include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h"
#include "Runtime.h"
namespace dev namespace dev
{ {
@ -27,18 +28,87 @@ Stack::Stack(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager):
llvm::Type* pushArgTypes[] = {Type::RuntimePtr, Type::WordPtr}; llvm::Type* pushArgTypes[] = {Type::RuntimePtr, Type::WordPtr};
m_push = Function::Create(FunctionType::get(Type::Void, pushArgTypes, false), Linkage::ExternalLinkage, "stack_push", module); m_push = Function::Create(FunctionType::get(Type::Void, pushArgTypes, false), Linkage::ExternalLinkage, "stack_push", module);
llvm::Type* popArgTypes[] = {Type::RuntimePtr, Type::Size};
m_pop = Function::Create(FunctionType::get(Type::Void, popArgTypes, false), Linkage::ExternalLinkage, "stack_pop", module);
llvm::Type* getSetArgTypes[] = {Type::RuntimePtr, Type::Size, Type::WordPtr}; llvm::Type* getSetArgTypes[] = {Type::RuntimePtr, Type::Size, Type::WordPtr};
m_get = Function::Create(FunctionType::get(Type::Void, getSetArgTypes, false), Linkage::ExternalLinkage, "stack_get", module);
m_set = Function::Create(FunctionType::get(Type::Void, getSetArgTypes, false), Linkage::ExternalLinkage, "stack_set", module); m_set = Function::Create(FunctionType::get(Type::Void, getSetArgTypes, false), Linkage::ExternalLinkage, "stack_set", module);
} }
llvm::Function* Stack::getPopFunc()
{
auto& func = m_pop;
if (!func)
{
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::BytePtr};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.pop", getModule());
llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size};
auto extPopFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Bool, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_pop", getModule());
auto rt = &func->getArgumentList().front();
rt->setName("rt");
auto index = rt->getNextNode();
index->setName("index");
auto jmpBuf = index->getNextNode();
jmpBuf->setName("jmpBuf");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
auto underflowBB = llvm::BasicBlock::Create(m_builder.getContext(), "Underflow", func);
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func);
m_builder.SetInsertPoint(entryBB);
auto ok = createCall(extPopFunc, {rt, index});
m_builder.CreateCondBr(ok, returnBB, underflowBB); //TODO: Add branch weight
m_builder.SetInsertPoint(underflowBB);
m_runtimeManager.abort(jmpBuf);
m_builder.CreateUnreachable();
m_builder.SetInsertPoint(returnBB);
m_builder.CreateRetVoid();
}
return func;
}
llvm::Function* Stack::getGetFunc()
{
auto& func = m_get;
if (!func)
{
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::BytePtr};
func = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, argTypes, false), llvm::Function::ExternalLinkage, "stack.get", getModule());
llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size};
auto extGetFunc = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_get", getModule());
auto rt = &func->getArgumentList().front();
rt->setName("rt");
auto index = rt->getNextNode();
index->setName("index");
auto jmpBuf = index->getNextNode();
jmpBuf->setName("jmpBuf");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
auto underflowBB = llvm::BasicBlock::Create(m_builder.getContext(), "Underflow", func);
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func);
m_builder.SetInsertPoint(entryBB);
auto valuePtr = createCall(extGetFunc, {rt, index});
auto ok = m_builder.CreateICmpNE(valuePtr, llvm::ConstantPointerNull::get(Type::WordPtr));
m_builder.CreateCondBr(ok, returnBB, underflowBB); //TODO: Add branch weight
m_builder.SetInsertPoint(underflowBB);
m_runtimeManager.abort(jmpBuf);
m_builder.CreateUnreachable();
m_builder.SetInsertPoint(returnBB);
m_builder.CreateRet(valuePtr);
}
return func;
}
llvm::Value* Stack::get(size_t _index) llvm::Value* Stack::get(size_t _index)
{ {
m_builder.CreateCall3(m_get, m_runtimeManager.getRuntimePtr(), llvm::ConstantInt::get(Type::Size, _index, false), m_arg); auto valuePtr = createCall(getGetFunc(), {m_runtimeManager.getRuntimePtr(), m_builder.getInt64(_index), m_runtimeManager.getJmpBuf()});
return m_builder.CreateLoad(m_arg); return m_builder.CreateLoad(valuePtr);
} }
void Stack::set(size_t _index, llvm::Value* _value) void Stack::set(size_t _index, llvm::Value* _value)
@ -49,7 +119,7 @@ void Stack::set(size_t _index, llvm::Value* _value)
void Stack::pop(size_t _count) void Stack::pop(size_t _count)
{ {
m_builder.CreateCall2(m_pop, m_runtimeManager.getRuntimePtr(), llvm::ConstantInt::get(Type::Size, _count, false)); createCall(getPopFunc(), {m_runtimeManager.getRuntimePtr(), m_builder.getInt64(_count), m_runtimeManager.getJmpBuf()});
} }
void Stack::push(llvm::Value* _value) void Stack::push(llvm::Value* _value)
@ -69,13 +139,14 @@ extern "C"
{ {
using namespace dev::eth::jit; using namespace dev::eth::jit;
EXPORT void stack_pop(Runtime* _rt, uint64_t _count) EXPORT bool stack_pop(Runtime* _rt, uint64_t _count)
{ {
auto& stack = _rt->getStack(); auto& stack = _rt->getStack();
if (stack.size() < _count) if (stack.size() < _count)
longjmp(_rt->getJmpBuf(), static_cast<int>(ReturnCode::StackTooSmall)); return false;
stack.erase(stack.end() - _count, stack.end()); stack.erase(stack.end() - _count, stack.end());
return true;
} }
EXPORT void stack_push(Runtime* _rt, i256 const* _word) EXPORT void stack_push(Runtime* _rt, i256 const* _word)
@ -87,22 +158,18 @@ extern "C"
Stack::maxStackSize = stack.size(); Stack::maxStackSize = stack.size();
} }
EXPORT void stack_get(Runtime* _rt, uint64_t _index, i256* o_ret) EXPORT i256* stack_get(Runtime* _rt, uint64_t _index)
{ {
auto& stack = _rt->getStack(); auto& stack = _rt->getStack();
// TODO: encode _index and stack size in the return code return _index < stack.size() ? &*(stack.rbegin() + _index) : nullptr;
if (stack.size() <= _index)
longjmp(_rt->getJmpBuf(), static_cast<int>(ReturnCode::StackTooSmall));
*o_ret = *(stack.rbegin() + _index);
} }
EXPORT void stack_set(Runtime* _rt, uint64_t _index, i256 const* _word) EXPORT void stack_set(Runtime* _rt, uint64_t _index, i256 const* _word)
{ {
auto& stack = _rt->getStack(); auto& stack = _rt->getStack();
// TODO: encode _index and stack size in the return code assert(_index < stack.size());
if (stack.size() <= _index) if (_index >= stack.size())
longjmp(_rt->getJmpBuf(), static_cast<int>(ReturnCode::StackTooSmall)); return;
*(stack.rbegin() + _index) = *_word; *(stack.rbegin() + _index) = *_word;
} }

9
evmjit/libevmjit/Stack.h

@ -2,8 +2,6 @@
#include "CompilerHelper.h" #include "CompilerHelper.h"
#include <llvm/IR/Module.h>
namespace dev namespace dev
{ {
namespace eth namespace eth
@ -25,11 +23,14 @@ public:
static size_t maxStackSize; static size_t maxStackSize;
private: private:
llvm::Function* getPopFunc();
llvm::Function* getGetFunc();
RuntimeManager& m_runtimeManager; RuntimeManager& m_runtimeManager;
llvm::Function* m_pop = nullptr;
llvm::Function* m_push; llvm::Function* m_push;
llvm::Function* m_pop; llvm::Function* m_get = nullptr;
llvm::Function* m_get;
llvm::Function* m_set; llvm::Function* m_set;
llvm::Value* m_arg; llvm::Value* m_arg;

11
evmjit/libevmjit/Type.cpp

@ -1,8 +1,4 @@
#include "Type.h" #include "Type.h"
#include <llvm/IR/DerivedTypes.h>
#include "RuntimeManager.h" #include "RuntimeManager.h"
namespace dev namespace dev
@ -17,6 +13,8 @@ llvm::PointerType* Type::WordPtr;
llvm::IntegerType* Type::lowPrecision; llvm::IntegerType* Type::lowPrecision;
llvm::IntegerType* Type::Bool; llvm::IntegerType* Type::Bool;
llvm::IntegerType* Type::Size; llvm::IntegerType* Type::Size;
llvm::IntegerType* Type::Gas;
llvm::PointerType* Type::GasPtr;
llvm::IntegerType* Type::Byte; llvm::IntegerType* Type::Byte;
llvm::PointerType* Type::BytePtr; llvm::PointerType* Type::BytePtr;
llvm::Type* Type::Void; llvm::Type* Type::Void;
@ -24,6 +22,7 @@ llvm::IntegerType* Type::MainReturn;
llvm::PointerType* Type::EnvPtr; llvm::PointerType* Type::EnvPtr;
llvm::PointerType* Type::RuntimeDataPtr; llvm::PointerType* Type::RuntimeDataPtr;
llvm::PointerType* Type::RuntimePtr; llvm::PointerType* Type::RuntimePtr;
llvm::ConstantInt* Constant::gasMax;
void Type::init(llvm::LLVMContext& _context) void Type::init(llvm::LLVMContext& _context)
{ {
@ -35,6 +34,8 @@ void Type::init(llvm::LLVMContext& _context)
// TODO: Size should be architecture-dependent // TODO: Size should be architecture-dependent
Bool = llvm::Type::getInt1Ty(_context); Bool = llvm::Type::getInt1Ty(_context);
Size = llvm::Type::getInt64Ty(_context); Size = llvm::Type::getInt64Ty(_context);
Gas = Size;
GasPtr = Gas->getPointerTo();
Byte = llvm::Type::getInt8Ty(_context); Byte = llvm::Type::getInt8Ty(_context);
BytePtr = Byte->getPointerTo(); BytePtr = Byte->getPointerTo();
Void = llvm::Type::getVoidTy(_context); Void = llvm::Type::getVoidTy(_context);
@ -43,6 +44,8 @@ void Type::init(llvm::LLVMContext& _context)
EnvPtr = llvm::StructType::create(_context, "Env")->getPointerTo(); EnvPtr = llvm::StructType::create(_context, "Env")->getPointerTo();
RuntimeDataPtr = RuntimeManager::getRuntimeDataType()->getPointerTo(); RuntimeDataPtr = RuntimeManager::getRuntimeDataType()->getPointerTo();
RuntimePtr = RuntimeManager::getRuntimeType()->getPointerTo(); RuntimePtr = RuntimeManager::getRuntimeType()->getPointerTo();
Constant::gasMax = llvm::ConstantInt::getSigned(Type::Gas, std::numeric_limits<int64_t>::max());
} }
} }

8
evmjit/libevmjit/Type.h

@ -1,8 +1,10 @@
#pragma once #pragma once
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Type.h> #include <llvm/IR/Type.h>
#include <llvm/IR/Constants.h> #include <llvm/IR/Constants.h>
#include "preprocessor/llvm_includes_end.h"
#include "Common.h" #include "Common.h"
namespace dev namespace dev
@ -23,6 +25,8 @@ struct Type
static llvm::IntegerType* Bool; static llvm::IntegerType* Bool;
static llvm::IntegerType* Size; static llvm::IntegerType* Size;
static llvm::IntegerType* Gas;
static llvm::PointerType* GasPtr;
static llvm::IntegerType* Byte; static llvm::IntegerType* Byte;
static llvm::PointerType* BytePtr; static llvm::PointerType* BytePtr;
@ -41,6 +45,8 @@ struct Type
struct Constant struct Constant
{ {
static llvm::ConstantInt* gasMax;
/// Returns word-size constant /// Returns word-size constant
static llvm::ConstantInt* get(int64_t _n); static llvm::ConstantInt* get(int64_t _n);
static llvm::ConstantInt* get(llvm::APInt const& _n); static llvm::ConstantInt* get(llvm::APInt const& _n);

1
evmjit/libevmjit/Utils.cpp

@ -1,4 +1,3 @@
#include "Utils.h" #include "Utils.h"
namespace dev namespace dev

3
evmjit/libevmjit/Utils.h

@ -16,6 +16,9 @@ struct JIT: public NoteChannel { static const char* name() { return "JIT"; } };
//#define clog(CHANNEL) std::cerr //#define clog(CHANNEL) std::cerr
#define clog(CHANNEL) std::ostream(nullptr) #define clog(CHANNEL) std::ostream(nullptr)
// The same as assert, but expression is always evaluated and result returned
#define CHECK(expr) (assert(expr), expr)
} }
} }
} }

11
evmjit/libevmjit/interface.cpp

@ -12,6 +12,7 @@ using namespace dev::eth::jit;
EXPORT void* evmjit_create() noexcept EXPORT void* evmjit_create() noexcept
{ {
// TODO: Make sure ExecutionEngine constructor does not throw
return new(std::nothrow) ExecutionEngine; return new(std::nothrow) ExecutionEngine;
} }
@ -22,14 +23,12 @@ EXPORT void evmjit_destroy(ExecutionEngine* _engine) noexcept
EXPORT int evmjit_run(ExecutionEngine* _engine, RuntimeData* _data, Env* _env) noexcept EXPORT int evmjit_run(ExecutionEngine* _engine, RuntimeData* _data, Env* _env) noexcept
{ {
if (!_engine || !_data)
return static_cast<int>(ReturnCode::UnexpectedException);
try try
{ {
auto codePtr = _data->code; auto returnCode = _engine->run(_data, _env);
auto codeSize = _data->codeSize;
bytes bytecode;
bytecode.insert(bytecode.end(), codePtr, codePtr + codeSize);
auto returnCode = _engine->run(bytecode, _data, _env);
return static_cast<int>(returnCode); return static_cast<int>(returnCode);
} }
catch(...) catch(...)

5
evmjit/libevmjit/preprocessor/llvm_includes_end.h

@ -0,0 +1,5 @@
#if defined(_MSC_VER)
#pragma warning(pop)
#else
#pragma GCC diagnostic pop
#endif

8
evmjit/libevmjit/preprocessor/llvm_includes_start.h

@ -0,0 +1,8 @@
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4267 4244 4800)
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wconversion"
#endif

2
exp/CMakeLists.txt

@ -3,8 +3,8 @@ set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST) aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..)
include_directories(${LEVELDB_INCLUDE_DIRS}) include_directories(${LEVELDB_INCLUDE_DIRS})
include_directories(..)
set(EXECUTABLE exp) set(EXECUTABLE exp)

6
exp/main.cpp

@ -26,6 +26,7 @@
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/CommonData.h> #include <libdevcore/CommonData.h>
#include <libdevcore/RLP.h> #include <libdevcore/RLP.h>
#include <libdevcrypto/TrieDB.h>
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libp2p/All.h> #include <libp2p/All.h>
#include <libethereum/DownloadMan.h> #include <libethereum/DownloadMan.h>
@ -33,11 +34,13 @@
#include <liblll/All.h> #include <liblll/All.h>
#include <libwhisper/WhisperPeer.h> #include <libwhisper/WhisperPeer.h>
#include <libwhisper/WhisperHost.h> #include <libwhisper/WhisperHost.h>
#include <test/JsonSpiritHeaders.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
using namespace dev::p2p; using namespace dev::p2p;
using namespace dev::shh; using namespace dev::shh;
namespace js = json_spirit;
#if 0 #if 0
int main() int main()
@ -98,8 +101,7 @@ int main()
#else #else
int main() int main()
{ {
cnote << KeyPair(Secret("0000000000000000000000000000000000000000000000000000000000000000")).address(); return 0;
cnote << KeyPair(Secret("1111111111111111111111111111111111111111111111111111111111111111")).address();
} }
#endif #endif

10
extdep/CMakeLists.txt

@ -7,7 +7,7 @@ include(eth_download.cmake)
# all dependencies will be installed into this directory, separated by platform # all dependencies will be installed into this directory, separated by platform
string(TOLOWER ${CMAKE_SYSTEM_NAME} _system_name) string(TOLOWER ${CMAKE_SYSTEM_NAME} _system_name)
set(ETH_DEPENDENCY_INSTALL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/install/${_system_name}") set(ETH_DEPENDENCY_INSTALL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/install/${_system_name}")
set(ETH_DEPENDENCY_SERVER "http://build.ethdev.com/builds/${_system_name}-precompiled") set(ETH_DEPENDENCY_SERVER "https://build.ethdev.com/builds/${_system_name}-precompiled")
file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/lib) file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/lib)
file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/include) file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/include)
file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/bin) file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/bin)
@ -35,14 +35,18 @@ if (ETH_COMPILE)
include(compile/boost.cmake) include(compile/boost.cmake)
else() else()
eth_download(jsoncpp) eth_download(jsoncpp)
eth_download(json-rpc-cpp OSX_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/scripts/json-rpc-cpp_osx.sh) eth_download(microhttpd)
eth_download(json-rpc-cpp
VERSION 4.2
OSX_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/scripts/json-rpc-cpp_osx.sh
)
if (APPLE) if (APPLE)
eth_download(snappy OSX_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/scripts/snappy_osx.sh) eth_download(snappy OSX_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/scripts/snappy_osx.sh)
endif() endif()
eth_download(leveldb OSX_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/scripts/leveldb_osx.sh) eth_download(leveldb OSX_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/scripts/leveldb_osx.sh)
eth_download(qt) eth_download(qt VERSION 5.4)
eth_download(cryptopp) eth_download(cryptopp)
eth_download(boost) eth_download(boost)
eth_download(curl) eth_download(curl)

9
libdevcore/CMakeLists.txt

@ -12,8 +12,8 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB")
aux_source_directory(. SRC_LIST) aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
include_directories(..)
set(EXECUTABLE devcore) set(EXECUTABLE devcore)
@ -26,9 +26,14 @@ else()
endif() endif()
target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARIES})
#target_link_libraries(${EXECUTABLE} ${Boost_DATE_TIME_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${Boost_SYSTEM_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${Boost_SYSTEM_LIBRARIES})
# transitive dependencies for windows executables
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
target_link_libraries(${EXECUTABLE} ${Boost_CHRONO_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${Boost_DATE_TIME_LIBRARIES})
endif()
if (APPLE) if (APPLE)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT})

2
libdevcore/Common.cpp

@ -27,7 +27,7 @@ using namespace dev;
namespace dev namespace dev
{ {
char const* Version = "0.8.1"; char const* Version = "0.8.2";
} }

6
libdevcore/CommonData.cpp

@ -78,7 +78,7 @@ int dev::fromHex(char _i)
BOOST_THROW_EXCEPTION(BadHexCharacter() << errinfo_invalidSymbol(_i)); BOOST_THROW_EXCEPTION(BadHexCharacter() << errinfo_invalidSymbol(_i));
} }
bytes dev::fromHex(std::string const& _s) bytes dev::fromHex(std::string const& _s, ThrowType _throw)
{ {
unsigned s = (_s[0] == '0' && _s[1] == 'x') ? 2 : 0; unsigned s = (_s[0] == '0' && _s[1] == 'x') ? 2 : 0;
std::vector<uint8_t> ret; std::vector<uint8_t> ret;
@ -96,6 +96,8 @@ bytes dev::fromHex(std::string const& _s)
#ifndef BOOST_NO_EXCEPTIONS #ifndef BOOST_NO_EXCEPTIONS
cwarn << boost::current_exception_diagnostic_information(); cwarn << boost::current_exception_diagnostic_information();
#endif #endif
if (_throw == ThrowType::Throw)
throw;
} }
for (unsigned i = s; i < _s.size(); i += 2) for (unsigned i = s; i < _s.size(); i += 2)
try try
@ -107,6 +109,8 @@ bytes dev::fromHex(std::string const& _s)
#ifndef BOOST_NO_EXCEPTIONS #ifndef BOOST_NO_EXCEPTIONS
cwarn << boost::current_exception_diagnostic_information(); cwarn << boost::current_exception_diagnostic_information();
#endif #endif
if (_throw == ThrowType::Throw)
throw;
} }
return ret; return ret;
} }

9
libdevcore/CommonData.h

@ -35,6 +35,12 @@ namespace dev
// String conversion functions, mainly to/from hex/nibble/byte representations. // String conversion functions, mainly to/from hex/nibble/byte representations.
enum class ThrowType
{
NoThrow = 0,
Throw = 1,
};
/// Convert a series of bytes to the corresponding string of hex duplets. /// 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 each of the elements. Defaults to two - enough to represent a byte.
/// @example toHex("A\x69") == "4169" /// @example toHex("A\x69") == "4169"
@ -53,7 +59,8 @@ int fromHex(char _i);
/// Converts a (printable) ASCII hex string into the corresponding byte stream. /// Converts a (printable) ASCII hex string into the corresponding byte stream.
/// @example fromHex("41626261") == asBytes("Abba") /// @example fromHex("41626261") == asBytes("Abba")
bytes fromHex(std::string const& _s); /// If _throw = ThrowType::NoThrow, it replaces bad hex characters with 0's, otherwise it will throw an exception.
bytes fromHex(std::string const& _s, ThrowType _throw = ThrowType::NoThrow);
#if 0 #if 0
std::string toBase58(bytesConstRef _data); std::string toBase58(bytesConstRef _data);

22
libdevcore/CommonIO.h

@ -57,7 +57,7 @@ template <class S, class T> struct StreamOut { static S& bypass(S& _out, T const
template <class S> struct StreamOut<S, uint8_t> { static S& bypass(S& _out, uint8_t const& _t) { _out << (int)_t; return _out; } }; template <class S> struct StreamOut<S, uint8_t> { static S& bypass(S& _out, uint8_t const& _t) { _out << (int)_t; return _out; } };
template <class T> inline std::ostream& operator<<(std::ostream& _out, std::vector<T> const& _e); template <class T> inline std::ostream& operator<<(std::ostream& _out, std::vector<T> const& _e);
template <class T, unsigned Z> inline std::ostream& operator<<(std::ostream& _out, std::array<T, Z> const& _e); template <class T, std::size_t Z> inline std::ostream& operator<<(std::ostream& _out, std::array<T, Z> const& _e);
template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::pair<T, U> const& _e); template <class T, class U> inline std::ostream& operator<<(std::ostream& _out, std::pair<T, U> const& _e);
template <class T> inline std::ostream& operator<<(std::ostream& _out, std::list<T> const& _e); template <class T> inline std::ostream& operator<<(std::ostream& _out, std::list<T> const& _e);
template <class T1, class T2, class T3> inline std::ostream& operator<<(std::ostream& _out, std::tuple<T1, T2, T3> const& _e); template <class T1, class T2, class T3> inline std::ostream& operator<<(std::ostream& _out, std::tuple<T1, T2, T3> const& _e);
@ -84,7 +84,7 @@ inline S& streamout(S& _out, std::vector<T> const& _e)
template <class T> inline std::ostream& operator<<(std::ostream& _out, std::vector<T> const& _e) { streamout(_out, _e); return _out; } template <class T> inline std::ostream& operator<<(std::ostream& _out, std::vector<T> const& _e) { streamout(_out, _e); return _out; }
template <class S, class T, unsigned Z> template <class S, class T, std::size_t Z>
inline S& streamout(S& _out, std::array<T, Z> const& _e) inline S& streamout(S& _out, std::array<T, Z> const& _e)
{ {
_out << "["; _out << "[";
@ -98,23 +98,7 @@ inline S& streamout(S& _out, std::array<T, Z> const& _e)
_out << "]"; _out << "]";
return _out; return _out;
} }
template <class T, unsigned Z> inline std::ostream& operator<<(std::ostream& _out, std::array<T, Z> const& _e) { streamout(_out, _e); return _out; } template <class T, std::size_t Z> inline std::ostream& operator<<(std::ostream& _out, std::array<T, Z> const& _e) { streamout(_out, _e); return _out; }
template <class S, class T, unsigned long Z>
inline S& streamout(S& _out, std::array<T, Z> const& _e)
{
_out << "[";
if (!_e.empty())
{
StreamOut<S, T>::bypass(_out, _e.front());
auto i = _e.begin();
for (++i; i != _e.end(); ++i)
StreamOut<S, T>::bypass(_out << ",", *i);
}
_out << "]";
return _out;
}
template <class T, unsigned long Z> inline std::ostream& operator<<(std::ostream& _out, std::array<T, Z> const& _e) { streamout(_out, _e); return _out; }
template <class S, class T> template <class S, class T>
inline S& streamout(S& _out, std::list<T> const& _e) inline S& streamout(S& _out, std::list<T> const& _e)

12
libdevcore/Exceptions.h

@ -44,10 +44,10 @@ struct FileError: virtual Exception {};
struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): m_f("Interface " + _f + " not supported.") {} virtual const char* what() const noexcept { return m_f.c_str(); } private: std::string m_f; }; struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): m_f("Interface " + _f + " not supported.") {} virtual const char* what() const noexcept { return m_f.c_str(); } private: std::string m_f; };
// error information to be added to exceptions // error information to be added to exceptions
typedef boost::error_info<struct tag_invalidSymbol, char> errinfo_invalidSymbol; using errinfo_invalidSymbol = boost::error_info<struct tag_invalidSymbol, char>;
typedef boost::error_info<struct tag_address, std::string> errinfo_wrongAddress; using errinfo_wrongAddress = boost::error_info<struct tag_address, std::string>;
typedef boost::error_info<struct tag_comment, std::string> errinfo_comment; using errinfo_comment = boost::error_info<struct tag_comment, std::string>;
typedef boost::error_info<struct tag_required, bigint> errinfo_required; using errinfo_required = boost::error_info<struct tag_required, bigint>;
typedef boost::error_info<struct tag_got, bigint> errinfo_got; using errinfo_got = boost::error_info<struct tag_got, bigint>;
typedef boost::tuple<errinfo_required, errinfo_got> RequirementError; using RequirementError = boost::tuple<errinfo_required, errinfo_got>;
} }

2
libdevcore/RLP.cpp

@ -177,7 +177,7 @@ void RLPStream::noteAppended(unsigned _itemCount)
while (m_listStack.size()) while (m_listStack.size())
{ {
if (m_listStack.back().first < _itemCount) if (m_listStack.back().first < _itemCount)
BOOST_THROW_EXCEPTION(RLPException() << errinfo_comment("itemCount too large")); BOOST_THROW_EXCEPTION(RLPException() << errinfo_comment("itemCount too large") << RequirementError((bigint)m_listStack.back().first, (bigint)_itemCount));
m_listStack.back().first -= _itemCount; m_listStack.back().first -= _itemCount;
if (m_listStack.back().first) if (m_listStack.back().first)
break; break;

12
libdevcore/RLP.h

@ -37,7 +37,7 @@ namespace dev
{ {
class RLP; class RLP;
typedef std::vector<RLP> RLPs; using RLPs = std::vector<RLP>;
template <class _T> struct intTraits { static const unsigned maxSize = sizeof(_T); }; template <class _T> struct intTraits { static const unsigned maxSize = sizeof(_T); };
template <> struct intTraits<u160> { static const unsigned maxSize = 20; }; template <> struct intTraits<u160> { static const unsigned maxSize = 20; };
@ -125,7 +125,7 @@ public:
/// @note if used to access items in ascending order, this is efficient. /// @note if used to access items in ascending order, this is efficient.
RLP operator[](unsigned _i) const; RLP operator[](unsigned _i) const;
typedef RLP element_type; using element_type = RLP;
/// @brief Iterator class for iterating through items of RLP list. /// @brief Iterator class for iterating through items of RLP list.
class iterator class iterator
@ -133,8 +133,8 @@ public:
friend class RLP; friend class RLP;
public: public:
typedef RLP value_type; using value_type = RLP;
typedef RLP element_type; using element_type = RLP;
iterator& operator++(); iterator& operator++();
iterator operator++(int) { auto ret = *this; operator++(); return ret; } iterator operator++(int) { auto ret = *this; operator++(); return ret; }
@ -242,7 +242,9 @@ public:
AllowNonCanon = 1, AllowNonCanon = 1,
ThrowOnFail = 4, ThrowOnFail = 4,
FailIfTooBig = 8, FailIfTooBig = 8,
FailIfTooSmall = 16,
Strict = ThrowOnFail | FailIfTooBig, Strict = ThrowOnFail | FailIfTooBig,
VeryStrict = ThrowOnFail | FailIfTooBig | FailIfTooSmall,
LaisezFaire = AllowNonCanon LaisezFaire = AllowNonCanon
}; };
@ -269,7 +271,7 @@ public:
template <class _N> _N toHash(int _flags = Strict) const template <class _N> _N toHash(int _flags = Strict) const
{ {
if (!isData() || (length() > _N::size && (_flags & FailIfTooBig))) if (!isData() || (length() > _N::size && (_flags & FailIfTooBig)) || (length() < _N::size && (_flags & FailIfTooSmall)))
if (_flags & ThrowOnFail) if (_flags & ThrowOnFail)
BOOST_THROW_EXCEPTION(BadCast()); BOOST_THROW_EXCEPTION(BadCast());
else else

2
libdevcore/Worker.h

@ -64,7 +64,7 @@ protected:
private: private:
std::string m_name; std::string m_name;
unsigned m_idleWaitMs; unsigned m_idleWaitMs = 0;
mutable Mutex x_work; ///< Lock for the network existance. mutable Mutex x_work; ///< Lock for the network existance.
std::unique_ptr<std::thread> m_work; ///< The network thread. std::unique_ptr<std::thread> m_work; ///< The network thread.

16
libdevcore/vector_ref.h

@ -1,7 +1,8 @@
#pragma once #pragma once
#include <type_traits> #include <cstring>
#include <cassert> #include <cassert>
#include <type_traits>
#include <vector> #include <vector>
#include <string> #include <string>
@ -12,13 +13,13 @@ template <class _T>
class vector_ref class vector_ref
{ {
public: public:
typedef _T value_type; using value_type = _T;
typedef _T element_type; using element_type = _T;
typedef typename std::conditional<std::is_const<_T>::value, typename std::remove_const<_T>::type, _T>::type mutable_value_type; using mutable_value_type = typename std::conditional<std::is_const<_T>::value, typename std::remove_const<_T>::type, _T>::type;
vector_ref(): m_data(nullptr), m_count(0) {} vector_ref(): m_data(nullptr), m_count(0) {}
vector_ref(_T* _data, size_t _count): m_data(_data), m_count(_count) {} vector_ref(_T* _data, size_t _count): m_data(_data), m_count(_count) {}
vector_ref(std::string* _data): m_data((_T*)_data->data()), m_count(_data->size() / sizeof(_T)) {} vector_ref(typename std::conditional<std::is_const<_T>::value, std::string const*, std::string*>::type _data): m_data(reinterpret_cast<_T*>(_data->data())), m_count(_data->size() / sizeof(_T)) {}
vector_ref(typename std::conditional<std::is_const<_T>::value, std::vector<typename std::remove_const<_T>::type> const*, std::vector<_T>*>::type _data): m_data(_data->data()), m_count(_data->size()) {} vector_ref(typename std::conditional<std::is_const<_T>::value, std::vector<typename std::remove_const<_T>::type> const*, std::vector<_T>*>::type _data): m_data(_data->data()), m_count(_data->size()) {}
vector_ref(typename std::conditional<std::is_const<_T>::value, std::string const&, std::string&>::type _data): m_data((_T*)_data.data()), m_count(_data.size() / sizeof(_T)) {} vector_ref(typename std::conditional<std::is_const<_T>::value, std::string const&, std::string&>::type _data): m_data((_T*)_data.data()), m_count(_data.size() / sizeof(_T)) {}
#ifdef STORAGE_LEVELDB_INCLUDE_DB_H_ #ifdef STORAGE_LEVELDB_INCLUDE_DB_H_
@ -28,9 +29,10 @@ public:
bool contentsEqual(std::vector<mutable_value_type> const& _c) const { return _c.size() == m_count && !memcmp(_c.data(), m_data, m_count); } bool contentsEqual(std::vector<mutable_value_type> const& _c) const { return _c.size() == m_count && !memcmp(_c.data(), m_data, m_count); }
std::vector<mutable_value_type> toVector() const { return std::vector<mutable_value_type>(m_data, m_data + m_count); } std::vector<mutable_value_type> toVector() const { return std::vector<mutable_value_type>(m_data, m_data + m_count); }
std::vector<unsigned char> toBytes() const { return std::vector<unsigned char>((unsigned char const*)m_data, (unsigned char const*)m_data + m_count * sizeof(_T)); } std::vector<unsigned char> toBytes() const { return std::vector<unsigned char>(reinterpret_cast<unsigned char const*>(m_data), reinterpret_cast<unsigned char const*>(m_data) + m_count * sizeof(_T)); }
std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count * sizeof(_T)); } std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count * sizeof(_T)); }
template <class _T2> operator vector_ref<_T2>() const { assert(m_count * sizeof(_T) / sizeof(_T2) * sizeof(_T2) / sizeof(_T) == m_count); return vector_ref<_T2>((_T2*)m_data, m_count * sizeof(_T) / sizeof(_T2)); } template <class _T2> explicit operator vector_ref<_T2>() const { assert(m_count * sizeof(_T) / sizeof(_T2) * sizeof(_T2) / sizeof(_T) == m_count); return vector_ref<_T2>(reinterpret_cast<_T2*>(m_data), m_count * sizeof(_T) / sizeof(_T2)); }
operator vector_ref<_T const>() const { return vector_ref<_T const>(m_data, m_count); }
_T* data() const { return m_data; } _T* data() const { return m_data; }
size_t count() const { return m_count; } size_t count() const { return m_count; }

2
libdevcrypto/CMakeLists.txt

@ -9,10 +9,10 @@ set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST) aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
include_directories(${CRYPTOPP_INCLUDE_DIRS}) include_directories(${CRYPTOPP_INCLUDE_DIRS})
include_directories(${LEVELDB_INCLUDE_DIRS}) include_directories(${LEVELDB_INCLUDE_DIRS})
include_directories(..)
set(EXECUTABLE devcrypto) set(EXECUTABLE devcrypto)

2
libdevcrypto/Common.cpp

@ -44,6 +44,8 @@ bool dev::SignatureStruct::isValid() const
return true; return true;
} }
Address dev::ZeroAddress = Address();
Public dev::toPublic(Secret const& _secret) Public dev::toPublic(Secret const& _secret)
{ {
Public p; Public p;

3
libdevcrypto/Common.h

@ -62,6 +62,9 @@ struct SignatureStruct
/// @NOTE This is not endian-specific; it's just a bunch of bytes. /// @NOTE This is not endian-specific; it's just a bunch of bytes.
using Address = h160; using Address = h160;
/// The zero address.
extern Address ZeroAddress;
/// A vector of Ethereum addresses. /// A vector of Ethereum addresses.
using Addresses = h160s; using Addresses = h160s;

2
libdevcrypto/ECDHE.h

@ -31,7 +31,7 @@ namespace crypto
{ {
/// Public key of remote and corresponding shared secret. /// Public key of remote and corresponding shared secret.
typedef std::pair<Public,h256> AliasSession; using AliasSession = std::pair<Public,h256>;
/** /**
* @brief An addressable EC key pair. * @brief An addressable EC key pair.

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save