Browse Source

Merge branch 'remove-libraries'

cl-refactor
Genoil 9 years ago
parent
commit
966fb537b7
  1. 74
      CMakeLists.txt
  2. 19
      abi/CMakeLists.txt
  3. 769
      abi/main.cpp
  4. 1
      alethzero/CMakeLists.txt
  5. 4
      cmake/scripts/buildinfo.cmake
  6. 33
      ethkey/CMakeLists.txt
  7. 769
      ethkey/KeyAux.h
  8. 84
      ethkey/main.cpp
  9. 19
      ethvm/CMakeLists.txt
  10. 224
      ethvm/main.cpp
  11. 1
      evmjit/.gitignore
  12. 65
      evmjit/CMakeLists.txt
  13. 21
      evmjit/LICENSE.md
  14. 36
      evmjit/README.md
  15. 70
      evmjit/include/evmjit/JIT-c.h
  16. 172
      evmjit/include/evmjit/JIT.h
  17. 24
      evmjit/libevmjit-cpp/CMakeLists.txt
  18. 135
      evmjit/libevmjit-cpp/Env.cpp
  19. 77
      evmjit/libevmjit-cpp/JitVM.cpp
  20. 24
      evmjit/libevmjit-cpp/JitVM.h
  21. 45
      evmjit/libevmjit-cpp/Utils.h
  22. 508
      evmjit/libevmjit/Arith256.cpp
  23. 42
      evmjit/libevmjit/Arith256.h
  24. 270
      evmjit/libevmjit/Array.cpp
  25. 72
      evmjit/libevmjit/Array.h
  26. 148
      evmjit/libevmjit/BasicBlock.cpp
  27. 86
      evmjit/libevmjit/BasicBlock.h
  28. 9
      evmjit/libevmjit/BuildInfo.h.in
  29. 58
      evmjit/libevmjit/CMakeLists.txt
  30. 190
      evmjit/libevmjit/Cache.cpp
  31. 59
      evmjit/libevmjit/Cache.h
  32. 16
      evmjit/libevmjit/Common.h
  33. 800
      evmjit/libevmjit/Compiler.cpp
  34. 59
      evmjit/libevmjit/Compiler.h
  35. 51
      evmjit/libevmjit/CompilerHelper.cpp
  36. 64
      evmjit/libevmjit/CompilerHelper.h
  37. 33
      evmjit/libevmjit/Endianness.cpp
  38. 25
      evmjit/libevmjit/Endianness.h
  39. 95
      evmjit/libevmjit/ExecStats.cpp
  40. 68
      evmjit/libevmjit/ExecStats.h
  41. 210
      evmjit/libevmjit/Ext.cpp
  42. 79
      evmjit/libevmjit/Ext.h
  43. 299
      evmjit/libevmjit/GasMeter.cpp
  44. 64
      evmjit/libevmjit/GasMeter.h
  45. 39
      evmjit/libevmjit/Instruction.cpp
  46. 236
      evmjit/libevmjit/Instruction.h
  47. 48
      evmjit/libevmjit/JIT-c.cpp
  48. 252
      evmjit/libevmjit/JIT.cpp
  49. 246
      evmjit/libevmjit/Memory.cpp
  50. 51
      evmjit/libevmjit/Memory.h
  51. 125
      evmjit/libevmjit/Optimizer.cpp
  52. 21
      evmjit/libevmjit/Optimizer.h
  53. 302
      evmjit/libevmjit/RuntimeManager.cpp
  54. 79
      evmjit/libevmjit/RuntimeManager.h
  55. 44
      evmjit/libevmjit/Stack.cpp
  56. 30
      evmjit/libevmjit/Stack.h
  57. 73
      evmjit/libevmjit/Type.cpp
  58. 61
      evmjit/libevmjit/Type.h
  59. 27
      evmjit/libevmjit/Utils.cpp
  60. 40
      evmjit/libevmjit/Utils.h
  61. 7
      evmjit/libevmjit/preprocessor/llvm_includes_end.h
  62. 12
      evmjit/libevmjit/preprocessor/llvm_includes_start.h
  63. 38
      exp/CMakeLists.txt
  64. 402
      exp/main.cpp
  65. 1
      mix/CMakeLists.txt
  66. 21
      rlp/CMakeLists.txt
  67. 394
      rlp/main.cpp

74
CMakeLists.txt

@ -51,7 +51,6 @@ set(D_TESTS ON)
set(D_FATDB ON) set(D_FATDB ON)
set(D_ETHASHCL ON) set(D_ETHASHCL ON)
set(D_ETHASHCUDA OFF) set(D_ETHASHCUDA OFF)
set(D_EVMJIT ON)
set(D_JSCONSOLE ON) set(D_JSCONSOLE ON)
set(D_JSONRPC ON) set(D_JSONRPC ON)
set(D_VMTRACE OFF) set(D_VMTRACE OFF)
@ -60,7 +59,6 @@ set(D_PROFILING OFF)
set(D_ROCKSDB OFF) set(D_ROCKSDB OFF)
set(D_OLYMPIC OFF) set(D_OLYMPIC OFF)
set(D_MINER ON) set(D_MINER ON)
set(D_ETHKEY ON)
set(D_ETHSTRATUM OFF) set(D_ETHSTRATUM OFF)
if (BUNDLE STREQUAL "minimal") if (BUNDLE STREQUAL "minimal")
@ -109,26 +107,22 @@ elseif (BUNDLE STREQUAL "wallet")
set(D_GUI OFF) set(D_GUI OFF)
set(D_TOOLS OFF) set(D_TOOLS OFF)
set(D_TESTS OFF) set(D_TESTS OFF)
set(D_ETHKEY ON)
set(D_MINER OFF) set(D_MINER OFF)
set(D_ETHASHCL OFF) set(D_ETHASHCL OFF)
set(D_FATDB OFF) set(D_FATDB OFF)
set(D_JSONRPC OFF) set(D_JSONRPC OFF)
set(D_JSCONSOLE OFF) set(D_JSCONSOLE OFF)
set(D_EVMJIT OFF)
elseif (BUNDLE STREQUAL "miner") elseif (BUNDLE STREQUAL "miner")
set(D_SERPENT OFF) set(D_SERPENT OFF)
set(D_USENPM OFF) set(D_USENPM OFF)
set(D_GUI OFF) set(D_GUI OFF)
set(D_TOOLS OFF) set(D_TOOLS OFF)
set(D_TESTS OFF) set(D_TESTS OFF)
set(D_ETHKEY OFF)
set(D_MINER ON) set(D_MINER ON)
set(D_ETHASHCL ON) set(D_ETHASHCL ON)
set(D_FATDB OFF) set(D_FATDB OFF)
set(D_JSONRPC ON) set(D_JSONRPC ON)
set(D_JSCONSOLE OFF) set(D_JSCONSOLE OFF)
set(D_EVMJIT OFF)
set(D_ETHSTRATUM ON) set(D_ETHSTRATUM ON)
elseif (BUNDLE STREQUAL "cudaminer") elseif (BUNDLE STREQUAL "cudaminer")
set(D_SERPENT OFF) set(D_SERPENT OFF)
@ -136,14 +130,12 @@ elseif (BUNDLE STREQUAL "cudaminer")
set(D_GUI OFF) set(D_GUI OFF)
set(D_TOOLS OFF) set(D_TOOLS OFF)
set(D_TESTS OFF) set(D_TESTS OFF)
set(D_ETHKEY OFF)
set(D_MINER ON) set(D_MINER ON)
set(D_ETHASHCL ON) set(D_ETHASHCL ON)
set(D_ETHASHCUDA ON) set(D_ETHASHCUDA ON)
set(D_FATDB OFF) set(D_FATDB OFF)
set(D_JSONRPC ON) set(D_JSONRPC ON)
set(D_JSCONSOLE OFF) set(D_JSCONSOLE OFF)
set(D_EVMJIT OFF)
set(D_ETHSTRATUM ON) set(D_ETHSTRATUM ON)
elseif (BUNDLE STREQUAL "release") # release builds elseif (BUNDLE STREQUAL "release") # release builds
set(D_SERPENT ${DECENT_PLATFORM}) set(D_SERPENT ${DECENT_PLATFORM})
@ -153,7 +145,6 @@ elseif (BUNDLE STREQUAL "release") # release builds
set(D_TESTS OFF) set(D_TESTS OFF)
set(D_FATDB OFF) set(D_FATDB OFF)
set(D_ETHASHCL ON) set(D_ETHASHCL ON)
set(D_EVMJIT ON)
set(D_JSCONSOLE ON) set(D_JSCONSOLE ON)
set(D_JSONRPC ON) set(D_JSONRPC ON)
set(D_CMAKE_BUILD_TYPE "Release") set(D_CMAKE_BUILD_TYPE "Release")
@ -185,10 +176,6 @@ function(configureProject)
add_definitions(-DETH_STRATUM) add_definitions(-DETH_STRATUM)
endif() endif()
if (EVMJIT)
add_definitions(-DETH_EVMJIT)
endif()
if (FATDB) if (FATDB)
add_definitions(-DETH_FATDB) add_definitions(-DETH_FATDB)
endif() endif()
@ -233,11 +220,7 @@ function(createBuildInfo)
set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/unknown") set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/unknown")
endif () endif ()
if (EVMJIT) set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/int")
set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/JIT")
else ()
set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/int")
endif ()
if (PARANOID) if (PARANOID)
set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/PARA") set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/PARA")
@ -305,7 +288,6 @@ endmacro()
# Normalise build options # Normalise build options
eth_format_option(PARANOID) eth_format_option(PARANOID)
eth_format_option(VMTRACE) eth_format_option(VMTRACE)
eth_format_option(EVMJIT)
eth_format_option(FATDB) eth_format_option(FATDB)
eth_format_option(JSONRPC) eth_format_option(JSONRPC)
eth_format_option(MINER) eth_format_option(MINER)
@ -316,7 +298,6 @@ eth_format_option(GUI)
eth_format_option(TESTS) eth_format_option(TESTS)
eth_format_option(ROCKSDB) eth_format_option(ROCKSDB)
eth_format_option(TOOLS) eth_format_option(TOOLS)
eth_format_option(ETHKEY)
eth_format_option(ETHASHCL) eth_format_option(ETHASHCL)
eth_format_option(ETHASHCUDA) eth_format_option(ETHASHCUDA)
eth_format_option(JSCONSOLE) eth_format_option(JSCONSOLE)
@ -364,16 +345,14 @@ message("-- ROCKSDB Prefer rocksdb to leveldb ${ROCKSDB}
message("-- OLYMPIC Default to the Olympic network ${OLYMPIC}") message("-- OLYMPIC Default to the Olympic network ${OLYMPIC}")
message("------------------------------------------------------------- components") message("------------------------------------------------------------- components")
message("-- MINER Build miner ${MINER}") message("-- MINER Build miner ${MINER}")
message("-- ETHKEY Build wallet tools ${ETHKEY}")
message("-- TOOLS Build basic tools ${TOOLS}") message("-- TOOLS Build basic tools ${TOOLS}")
message("-- SERPENT Build Serpent language components ${SERPENT}") message("-- SERPENT Build Serpent language components ${SERPENT}")
message("-- GUI Build GUI components ${GUI}") message("-- GUI Build GUI components ${GUI}")
message("-- TESTS Build tests ${TESTS}") message("-- TESTS Build tests ${TESTS}")
message("-- ETHASHCL Build OpenCL components ${ETHASHCL}") message("-- ETHASHCL Build OpenCL components ${ETHASHCL}")
message("-- ETHASHCUDA Build CUDA components ${ETHASHCUDA}") message("-- ETHASHCUDA Build CUDA components ${ETHASHCUDA}")
message("-- ETHSTRATUM Build Stratum components ${ETHSTRATUM}") message("-- ETHSTRATUM Build Stratum components (experimental) ${ETHSTRATUM}")
message("-- JSCONSOLE Build with javascript console ${JSCONSOLE}") message("-- JSCONSOLE Build with javascript console ${JSCONSOLE}")
message("-- EVMJIT Build LLVM-based JIT EVM ${EVMJIT}")
message("------------------------------------------------------------------------") message("------------------------------------------------------------------------")
message("") message("")
@ -397,23 +376,6 @@ else()
set(DB_LIBRARIES ${LEVELDB_LIBRARIES}) set(DB_LIBRARIES ${LEVELDB_LIBRARIES})
endif() endif()
if (EVMJIT)
if (NOT DEFINED LLVM_DIR)
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(LLVM_DIR "${CMAKE_SOURCE_DIR}/extdep/install/windows/x64/share/llvm/cmake")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(LLVM_DIR "/usr/local/opt/llvm/share/llvm/cmake")
endif()
endif()
set(EVMJIT_CPP TRUE) # include CPP-JIT connector
add_subdirectory(evmjit)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(EVMJIT_DLLS_LOCAL $<TARGET_FILE:evmjit>)
set(EVMJIT_DLLS optimized ${EVMJIT_DLLS_LOCAL} debug ${EVMJIT_DLLS_LOCAL})
endif()
endif()
if (TOOLS OR GUI OR TESTS) if (TOOLS OR GUI OR TESTS)
set(GENERAL 1) set(GENERAL 1)
else () else ()
@ -479,37 +441,7 @@ if (MINER OR TOOLS)
add_subdirectory(ethminer) add_subdirectory(ethminer)
endif () endif ()
if (ETHKEY OR TOOLS)
add_subdirectory(ethkey)
endif ()
# TODO: sort out tests so they're not dependent on webthree/web3jsonrpc and reenable
#if (TESTS)
# add_subdirectory(libtestutils)
# add_subdirectory(test)
# if (JSONRPC)
# add_subdirectory(ethrpctest)
# endif ()
#endif ()
if (TOOLS)
add_subdirectory(rlp)
add_subdirectory(abi)
add_subdirectory(ethvm)
# if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug")
# add_subdirectory(exp)
# endif ()
endif()
if (GUI) if (GUI)
add_subdirectory(libnatspec) add_subdirectory(libnatspec)
add_subdirectory(libjsqrc) add_subdirectory(libjsqrc)
add_subdirectory(alethzero)
add_subdirectory(mix)
endif() endif()

19
abi/CMakeLists.txt

@ -1,19 +0,0 @@
cmake_policy(SET CMP0015 NEW)
set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..)
include_directories(${DB_INCLUDE_DIRS})
set(EXECUTABLE abi)
add_executable(${EXECUTABLE} ${SRC_LIST})
target_link_libraries(${EXECUTABLE} ethereum)
if (APPLE)
install(TARGETS ${EXECUTABLE} DESTINATION bin)
else()
eth_install_executable(${EXECUTABLE})
endif()

769
abi/main.cpp

@ -1,769 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file main.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
* RLP tool.
*/
#include <fstream>
#include <iostream>
#include <boost/regex.hpp>
#include <boost/algorithm/string.hpp>
#include "../test/JsonSpiritHeaders.h"
#include <libdevcore/CommonIO.h>
#include <libdevcore/RLP.h>
#include <libdevcore/SHA3.h>
#include <libethereum/Client.h>
using namespace std;
using namespace dev;
namespace js = json_spirit;
void help()
{
cout
<< "Usage abi enc <method_name> (<arg1>, (<arg2>, ... ))" << endl
<< " abi enc -a <abi.json> <method_name> (<arg1>, (<arg2>, ... ))" << endl
<< " abi dec -a <abi.json> [ <signature> | <unique_method_name> ]" << endl
<< "Options:" << endl
<< " -a,--abi-file <filename> Specify the JSON ABI file." << endl
<< " -h,--help Print this help message and exit." << endl
<< " -V,--version Show the version and exit." << endl
<< "Input options:" << endl
<< " -f,--format-prefix Require all input formats to be prefixed e.g. 0x for hex, + for decimal, ' for binary." << endl
<< " -F,--no-format-prefix Require no input format to be prefixed." << endl
<< " -t,--typing Require all arguments to be typed e.g. b32: (bytes32), u64: (uint64), b[]: (byte[]), i: (int256)." << endl
<< " -T,--no-typing Require no arguments to be typed." << endl
<< "Output options:" << endl
<< " -i,--index <n> Output only the nth (counting from 0) return value." << endl
<< " -d,--decimal All data should be displayed as decimal." << endl
<< " -x,--hex Display all data as hex." << endl
<< " -b,--binary Display all data as binary." << endl
<< " -p,--prefix Prefix by a base identifier." << endl
;
exit(0);
}
void version()
{
cout << "abi version " << dev::Version << endl;
exit(0);
}
enum class Mode
{
Encode,
Decode
};
enum class Encoding
{
Auto,
Decimal,
Hex,
Binary,
};
enum class Tristate
{
False = false,
True = true,
Mu
};
enum class Format
{
Binary,
Hex,
Decimal,
Open,
Close
};
struct InvalidUserString: public Exception {};
struct InvalidFormat: public Exception {};
enum class Base
{
Unknown,
Bytes,
Address,
Int,
Uint,
Fixed
};
static const map<Base, string> s_bases =
{
{ Base::Bytes, "bytes" },
{ Base::Address, "address" },
{ Base::Int, "int" },
{ Base::Uint, "uint" },
{ Base::Fixed, "fixed" }
};
struct EncodingPrefs
{
Encoding e = Encoding::Auto;
bool prefix = true;
};
struct ABIType
{
Base base = Base::Unknown;
unsigned size = 32;
unsigned ssize = 0;
vector<int> dims;
string name;
ABIType() = default;
ABIType(std::string const& _type, std::string const& _name):
name(_name)
{
string rest;
for (auto const& i: s_bases)
if (boost::algorithm::starts_with(_type, i.second))
{
base = i.first;
rest = _type.substr(i.second.size());
}
if (base == Base::Unknown)
throw InvalidFormat();
boost::regex r("(\\d*)(x(\\d+))?((\\[\\d*\\])*)");
boost::smatch res;
boost::regex_match(rest, res, r);
size = res[1].length() > 0 ? stoi(res[1]) : 0;
ssize = res[3].length() > 0 ? stoi(res[3]) : 0;
boost::regex r2("\\[(\\d*)\\](.*)");
for (rest = res[4]; boost::regex_match(rest, res, r2); rest = res[2])
dims.push_back(!res[1].length() ? -1 : stoi(res[1]));
}
ABIType(std::string const& _s)
{
if (_s.size() < 1)
return;
switch (_s[0])
{
case 'b': base = Base::Bytes; break;
case 'a': base = Base::Address; break;
case 'i': base = Base::Int; break;
case 'u': base = Base::Uint; break;
case 'f': base = Base::Fixed; break;
default: throw InvalidFormat();
}
if (_s.size() < 2)
{
if (base == Base::Fixed)
size = ssize = 16;
else if (base == Base::Address || base == Base::Bytes)
size = 0;
else
size = 32;
return;
}
strings d;
boost::algorithm::split(d, _s, boost::is_any_of("*"));
string s = d[0];
if (s.find_first_of('x') == string::npos)
size = stoi(s.substr(1));
else
{
size = stoi(s.substr(1, s.find_first_of('x') - 1));
ssize = stoi(s.substr(s.find_first_of('x') + 1));
}
for (unsigned i = 1; i < d.size(); ++i)
if (d[i].empty())
dims.push_back(-1);
else
dims.push_back(stoi(d[i]));
}
string canon() const
{
string ret;
switch (base)
{
case Base::Bytes: ret = "bytes" + (size > 0 ? toString(size) : ""); break;
case Base::Address: ret = "address"; break;
case Base::Int: ret = "int" + toString(size); break;
case Base::Uint: ret = "uint" + toString(size); break;
case Base::Fixed: ret = "fixed" + toString(size) + "x" + toString(ssize); break;
default: throw InvalidFormat();
}
for (int i: dims)
ret += "[" + ((i > -1) ? toString(i) : "") + "]";
return ret;
}
bool isBytes() const { return base == Base::Bytes && !size; }
string render(bytes const& _data, EncodingPrefs _e) const
{
if (base == Base::Uint || base == Base::Int)
{
if (_e.e == Encoding::Hex)
return (_e.prefix ? "0x" : "") + toHex(toCompactBigEndian(fromBigEndian<bigint>(bytesConstRef(&_data).cropped(32 - size / 8))));
else
{
bigint i = fromBigEndian<bigint>(bytesConstRef(&_data).cropped(32 - size / 8));
if (base == Base::Int && i > (bigint(1) << (size - 1)))
i -= (bigint(1) << size);
return toString(i);
}
}
else if (base == Base::Address)
{
Address a = Address(h256(_data), Address::AlignRight);
return _e.e == Encoding::Binary ? asString(a.asBytes()) : ((_e.prefix ? "0x" : "") + toString(a));
}
else if (isBytes())
{
return _e.e == Encoding::Binary ? asString(_data) : ((_e.prefix ? "0x" : "") + toHex(_data));
}
else if (base == Base::Bytes)
{
bytesConstRef b(&_data);
b = b.cropped(0, size);
return _e.e == Encoding::Binary ? asString(b) : ((_e.prefix ? "0x" : "") + toHex(b));
}
else
throw InvalidFormat();
}
bytes unrender(bytes const& _data, Format _f) const
{
if (isBytes())
{
auto ret = _data;
while (ret.size() % 32 != 0)
ret.push_back(0);
return ret;
}
else
return aligned(_data, _f, 32);
}
void noteHexInput(unsigned _nibbles) { if (base == Base::Unknown) { if (_nibbles == 40) base = Base::Address; else { base = Base::Bytes; size = _nibbles / 2; } } }
void noteBinaryInput() { if (base == Base::Unknown) { base = Base::Bytes; size = 32; } }
void noteDecimalInput() { if (base == Base::Unknown) { base = Base::Uint; size = 32; } }
bytes aligned(bytes const& _b, Format _f, unsigned _length) const
{
bytes ret = _b;
while (ret.size() < _length)
if (base == Base::Bytes || (base == Base::Unknown && _f == Format::Binary))
ret.push_back(0);
else
ret.insert(ret.begin(), 0);
while (ret.size() > _length)
if (base == Base::Bytes || (base == Base::Unknown && _f == Format::Binary))
ret.pop_back();
else
ret.erase(ret.begin());
return ret;
}
};
tuple<bytes, ABIType, Format> fromUser(std::string const& _arg, Tristate _prefix, Tristate _typing)
{
ABIType type;
string val;
if (_typing == Tristate::True || (_typing == Tristate::Mu && _arg.find(':') != string::npos))
{
if (_arg.find(':') == string::npos)
throw InvalidUserString();
type = ABIType(_arg.substr(0, _arg.find(':')));
val = _arg.substr(_arg.find(':') + 1);
}
else
val = _arg;
if (_prefix != Tristate::False)
{
if (val.substr(0, 2) == "0x")
{
type.noteHexInput(val.size() - 2);
return make_tuple(fromHex(val), type, Format::Hex);
}
if (val.substr(0, 1) == "+")
{
type.noteDecimalInput();
return make_tuple(toCompactBigEndian(bigint(val.substr(1))), type, Format::Decimal);
}
if (val.substr(0, 1) == "'")
{
type.noteBinaryInput();
return make_tuple(asBytes(val.substr(1)), type, Format::Binary);
}
if (val == "[")
return make_tuple(bytes(), type, Format::Open);
if (val == "]")
return make_tuple(bytes(), type, Format::Close);
}
if (_prefix != Tristate::True)
{
if (val.find_first_not_of("0123456789") == string::npos)
{
type.noteDecimalInput();
return make_tuple(toCompactBigEndian(bigint(val)), type, Format::Decimal);
}
if (val.find_first_not_of("0123456789abcdefABCDEF") == string::npos)
{
type.noteHexInput(val.size());
return make_tuple(fromHex(val), type, Format::Hex);
}
if (val == "[")
return make_tuple(bytes(), type, Format::Open);
if (val == "]")
return make_tuple(bytes(), type, Format::Close);
type.noteBinaryInput();
return make_tuple(asBytes(val), type, Format::Binary);
}
throw InvalidUserString();
}
struct ExpectedAdditionalParameter: public Exception {};
struct ExpectedOpen: public Exception {};
struct ExpectedClose: public Exception {};
struct ABIMethod
{
string name;
vector<ABIType> ins;
vector<ABIType> outs;
bool isConstant = false;
// isolation *IS* documentation.
ABIMethod() = default;
ABIMethod(js::mObject _o)
{
name = _o["name"].get_str();
isConstant = _o["constant"].get_bool();
if (_o.count("inputs"))
for (auto const& i: _o["inputs"].get_array())
{
js::mObject a = i.get_obj();
ins.push_back(ABIType(a["type"].get_str(), a["name"].get_str()));
}
if (_o.count("outputs"))
for (auto const& i: _o["outputs"].get_array())
{
js::mObject a = i.get_obj();
outs.push_back(ABIType(a["type"].get_str(), a["name"].get_str()));
}
}
ABIMethod(string const& _name, vector<ABIType> const& _args)
{
name = _name;
ins = _args;
}
string sig() const
{
string methodArgs;
for (auto const& arg: ins)
methodArgs += (methodArgs.empty() ? "" : ",") + arg.canon();
return name + "(" + methodArgs + ")";
}
FixedHash<4> id() const { return FixedHash<4>(sha3(sig())); }
std::string solidityDeclaration() const
{
ostringstream ss;
ss << "function " << name << "(";
int f = 0;
for (ABIType const& i: ins)
ss << (f++ ? ", " : "") << i.canon() << " " << i.name;
ss << ") ";
if (isConstant)
ss << "constant ";
if (!outs.empty())
{
ss << "returns(";
f = 0;
for (ABIType const& i: outs)
ss << (f++ ? ", " : "") << i.canon() << " " << i.name;
ss << ")";
}
return ss.str();
}
bytes encode(vector<pair<bytes, Format>> const& _params) const
{
bytes ret = name.empty() ? bytes() : id().asBytes();
bytes suffix;
// int int[] int
// example: 42 [ 1 2 3 ] 69
// int[2][][3]
// example: [ [ [ 1 2 3 ] [ 4 5 6 ] ] [ ] ]
unsigned pi = 0;
for (ABIType const& a: ins)
{
if (pi >= _params.size())
throw ExpectedAdditionalParameter();
auto put = [&]() {
if (a.isBytes())
ret += h256(u256(_params[pi].first.size())).asBytes();
suffix += a.unrender(_params[pi].first, _params[pi].second);
pi++;
if (pi >= _params.size())
throw ExpectedAdditionalParameter();
};
function<void(vector<int>, unsigned)> putDim = [&](vector<int> addr, unsigned q) {
if (addr.size() == a.dims.size())
put();
else
{
if (_params[pi].second != Format::Open)
throw ExpectedOpen();
++pi;
int l = a.dims[a.dims.size() - 1 - addr.size()];
if (l == -1)
{
// read ahead in params and discover the arity.
unsigned depth = 0;
l = 0;
for (unsigned pi2 = pi; depth || _params[pi2].second != Format::Close;)
{
if (_params[pi2].second == Format::Open)
++depth;
if (_params[pi2].second == Format::Close)
--depth;
if (!depth)
++l;
if (++pi2 == _params.size())
throw ExpectedClose();
}
ret += h256(u256(l)).asBytes();
}
q *= l;
for (addr.push_back(0); addr.back() < l; ++addr.back())
putDim(addr, q);
if (_params[pi].second != Format::Close)
throw ExpectedClose();
++pi;
}
};
putDim(vector<int>(), 1);
}
return ret + suffix;
}
string decode(bytes const& _data, int _index, EncodingPrefs _ep)
{
stringstream out;
unsigned di = 0;
vector<unsigned> catDims;
for (ABIType const& a: outs)
{
auto put = [&]() {
if (a.isBytes())
{
catDims.push_back(fromBigEndian<unsigned>(bytesConstRef(&_data).cropped(di, 32)));
di += 32;
}
};
function<void(vector<int>, unsigned)> putDim = [&](vector<int> addr, unsigned q) {
if (addr.size() == a.dims.size())
put();
else
{
int l = a.dims[a.dims.size() - 1 - addr.size()];
if (l == -1)
{
l = fromBigEndian<unsigned>(bytesConstRef(&_data).cropped(di, 32));
catDims.push_back(l);
di += 32;
}
q *= l;
for (addr.push_back(0); addr.back() < l; ++addr.back())
putDim(addr, q);
}
};
putDim(vector<int>(), 1);
}
unsigned d = 0;
for (ABIType const& a: outs)
{
if (_index == -1 && out.tellp() > 0)
out << ", ";
auto put = [&]() {
if (a.isBytes())
{
out << a.render(bytesConstRef(&_data).cropped(di, catDims[d]).toBytes(), _ep);
di += ((catDims[d] + 31) / 32) * 32;
d++;
}
else
{
out << a.render(bytesConstRef(&_data).cropped(di, 32).toBytes(), _ep);
di += 32;
}
};
function<void(vector<int>)> putDim = [&](vector<int> addr) {
if (addr.size() == a.dims.size())
put();
else
{
out << "[";
addr.push_back(0);
int l = a.dims[a.dims.size() - 1 - (addr.size() - 1)];
if (l == -1)
l = catDims[d++];
for (addr.back() = 0; addr.back() < l; ++addr.back())
{
if (addr.back())
out << ", ";
putDim(addr);
}
out << "]";
}
};
putDim(vector<int>());
}
return out.str();
}
};
string canonSig(string const& _name, vector<ABIType> const& _args)
{
try {
string methodArgs;
for (auto const& arg: _args)
methodArgs += (methodArgs.empty() ? "" : ",") + arg.canon();
return _name + "(" + methodArgs + ")";
}
catch (...) {
return string();
}
}
struct UnknownMethod: public Exception {};
struct OverloadedMethod: public Exception {};
class ABI
{
public:
ABI() = default;
ABI(std::string const& _json)
{
js::mValue v;
js::read_string(_json, v);
for (auto const& i: v.get_array())
{
js::mObject o = i.get_obj();
if (o["type"].get_str() != "function")
continue;
ABIMethod m(o);
m_methods[m.id()] = m;
}
}
ABIMethod method(string _nameOrSig, vector<ABIType> const& _args) const
{
auto id = FixedHash<4>(sha3(_nameOrSig));
if (!m_methods.count(id))
id = FixedHash<4>(sha3(canonSig(_nameOrSig, _args)));
if (!m_methods.count(id))
for (auto const& m: m_methods)
if (m.second.name == _nameOrSig)
{
if (m_methods.count(id))
throw OverloadedMethod();
id = m.first;
}
if (m_methods.count(id))
return m_methods.at(id);
throw UnknownMethod();
}
friend ostream& operator<<(ostream& _out, ABI const& _abi);
private:
map<FixedHash<4>, ABIMethod> m_methods;
};
ostream& operator<<(ostream& _out, ABI const& _abi)
{
_out << "contract {" << endl;
for (auto const& i: _abi.m_methods)
_out << " " << i.second.solidityDeclaration() << "; // " << i.first.abridged() << endl;
_out << "}" << endl;
return _out;
}
void userOutput(ostream& _out, bytes const& _data, Encoding _e)
{
switch (_e)
{
case Encoding::Binary:
_out.write((char const*)_data.data(), _data.size());
break;
default:
_out << toHex(_data) << endl;
}
}
template <unsigned n, class T> vector<typename std::remove_reference<decltype(get<n>(T()))>::type> retrieve(vector<T> const& _t)
{
vector<typename std::remove_reference<decltype(get<n>(T()))>::type> ret;
for (T const& i: _t)
ret.push_back(get<n>(i));
return ret;
}
int main(int argc, char** argv)
{
Encoding encoding = Encoding::Auto;
Mode mode = Mode::Encode;
string abiFile;
string method;
Tristate formatPrefix = Tristate::Mu;
Tristate typePrefix = Tristate::Mu;
EncodingPrefs prefs;
bool verbose = false;
int outputIndex = -1;
vector<pair<bytes, Format>> params;
vector<ABIType> args;
string incoming;
for (int i = 1; i < argc; ++i)
{
string arg = argv[i];
if (arg == "-h" || arg == "--help")
help();
else if (arg == "enc" && i == 1)
mode = Mode::Encode;
else if (arg == "dec" && i == 1)
mode = Mode::Decode;
else if ((arg == "-a" || arg == "--abi") && argc > i)
abiFile = argv[++i];
else if ((arg == "-i" || arg == "--index") && argc > i)
outputIndex = atoi(argv[++i]);
else if (arg == "-p" || arg == "--prefix")
prefs.prefix = true;
else if (arg == "-f" || arg == "--format-prefix")
formatPrefix = Tristate::True;
else if (arg == "-F" || arg == "--no-format-prefix")
formatPrefix = Tristate::False;
else if (arg == "-t" || arg == "--typing")
typePrefix = Tristate::True;
else if (arg == "-T" || arg == "--no-typing")
typePrefix = Tristate::False;
else if (arg == "-x" || arg == "--hex")
prefs.e = Encoding::Hex;
else if (arg == "-d" || arg == "--decimal" || arg == "--dec")
prefs.e = Encoding::Decimal;
else if (arg == "-b" || arg == "--binary" || arg == "--bin")
prefs.e = Encoding::Binary;
else if (arg == "-v" || arg == "--verbose")
verbose = true;
else if (arg == "-V" || arg == "--version")
version();
else if (method.empty())
method = arg;
else if (mode == Mode::Encode)
{
auto u = fromUser(arg, formatPrefix, typePrefix);
args.push_back(get<1>(u));
params.push_back(make_pair(get<0>(u), get<2>(u)));
}
else if (mode == Mode::Decode)
incoming += arg;
}
string abiData;
if (!abiFile.empty())
abiData = contentsString(abiFile);
if (mode == Mode::Encode)
{
ABIMethod m;
if (abiData.empty())
m = ABIMethod(method, args);
else
{
ABI abi(abiData);
if (verbose)
cerr << "ABI:" << endl << abi;
try {
m = abi.method(method, args);
}
catch (...)
{
cerr << "Unknown method in ABI." << endl;
exit(-1);
}
}
try {
userOutput(cout, m.encode(params), encoding);
}
catch (ExpectedAdditionalParameter const&)
{
cerr << "Expected additional parameter in input." << endl;
exit(-1);
}
catch (ExpectedOpen const&)
{
cerr << "Expected open-bracket '[' in input." << endl;
exit(-1);
}
catch (ExpectedClose const&)
{
cerr << "Expected close-bracket ']' in input." << endl;
exit(-1);
}
}
else if (mode == Mode::Decode)
{
if (abiData.empty())
{
cerr << "Please specify an ABI file." << endl;
exit(-1);
}
else
{
ABI abi(abiData);
ABIMethod m;
if (verbose)
cerr << "ABI:" << endl << abi;
try {
m = abi.method(method, args);
}
catch(...)
{
cerr << "Unknown method in ABI." << endl;
exit(-1);
}
string encoded;
if (incoming == "--" || incoming.empty())
for (int i = cin.get(); i != -1; i = cin.get())
encoded.push_back((char)i);
else
{
encoded = contentsString(incoming);
}
cout << m.decode(fromHex(boost::trim_copy(encoded)), outputIndex, prefs) << endl;
}
}
return 0;
}

1
alethzero/CMakeLists.txt

@ -1 +0,0 @@
add_custom_target(alethzero)

4
cmake/scripts/buildinfo.cmake

@ -47,6 +47,10 @@ set(INFILE "${ETH_SOURCE_DIR}/BuildInfo.h.in")
set(TMPFILE "${ETH_DST_DIR}/BuildInfo.h.tmp") set(TMPFILE "${ETH_DST_DIR}/BuildInfo.h.tmp")
set(OUTFILE "${ETH_DST_DIR}/BuildInfo.h") set(OUTFILE "${ETH_DST_DIR}/BuildInfo.h")
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(OUTFILE "${ETH_DST_DIR}/CMakeFiles/BuildInfo.h")
endif()
message("ETH_FATDB: ${ETH_FATDB}") message("ETH_FATDB: ${ETH_FATDB}")
configure_file("${INFILE}" "${TMPFILE}") configure_file("${INFILE}" "${TMPFILE}")

33
ethkey/CMakeLists.txt

@ -1,33 +0,0 @@
cmake_policy(SET CMP0015 NEW)
set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
if (JSCONSOLE)
include_directories(${V8_INCLUDE_DIRS})
endif()
set(EXECUTABLE ethkey)
file(GLOB HEADERS "*.h")
add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
add_dependencies(${EXECUTABLE} BuildInfo.h)
target_link_libraries(${EXECUTABLE} devcrypto)
target_link_libraries(${EXECUTABLE} ethcore)
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
eth_copy_dlls("${EXECUTABLE}" MHD_DLLS)
endif()
if (APPLE)
install(TARGETS ${EXECUTABLE} DESTINATION bin)
else()
eth_install_executable(${EXECUTABLE})
endif()

769
ethkey/KeyAux.h

@ -1,769 +0,0 @@
#pragma once
/*
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 KeyAux.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
* CLI module for key management.
*/
#include <thread>
#include <chrono>
#include <fstream>
#include <iostream>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/trim_all.hpp>
#include <libdevcore/SHA3.h>
#include <libdevcore/FileSystem.h>
#include <libethcore/KeyManager.h>
#include <libethcore/ICAP.h>
#include <libethcore/Transaction.h>
#include <libdevcrypto/WordList.h>
#include "BuildInfo.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace boost::algorithm;
#undef RETURN
class BadArgument: public Exception {};
string getAccountPassword(KeyManager& keyManager, Address const& a)
{
return getPassword("Enter password for address " + keyManager.accountName(a) + " (" + a.abridged() + "; hint:" + keyManager.passwordHint(a) + "): ");
}
string createPassword(std::string const& _prompt)
{
string ret;
while (true)
{
ret = getPassword(_prompt);
string confirm = getPassword("Please confirm the password by entering it again: ");
if (ret == confirm)
break;
cout << "Passwords were different. Try again." << endl;
}
return ret;
// cout << "Enter a hint to help you remember this password: " << flush;
// cin >> hint;
// return make_pair(ret, hint);
}
pair<string, string> createPassword(KeyManager& _keyManager, std::string const& _prompt, std::string const& _pass = std::string(), std::string const& _hint = std::string())
{
string pass = _pass;
if (pass.empty())
while (true)
{
pass = getPassword(_prompt);
string confirm = getPassword("Please confirm the password by entering it again: ");
if (pass == confirm)
break;
cout << "Passwords were different. Try again." << endl;
}
string hint = _hint;
if (hint.empty() && !pass.empty() && !_keyManager.haveHint(pass))
{
cout << "Enter a hint to help you remember this password: " << flush;
getline(cin, hint);
}
return make_pair(pass, hint);
}
class KeyCLI
{
public:
enum class OperationMode
{
None,
ListBare,
NewBare,
ImportBare,
ExportBare,
RecodeBare,
KillBare,
InspectBare,
CreateWallet,
List,
New,
Import,
ImportWithAddress,
ImportPresale,
Export,
Recode,
Kill,
NewBrain,
ImportBrain,
InspectBrain,
SignTx,
DecodeTx,
};
KeyCLI(OperationMode _mode = OperationMode::None): m_mode(_mode) {}
bool interpretOption(int& i, int argc, char** argv)
{
string arg = argv[i];
if (arg == "--wallet-path" && i + 1 < argc)
m_walletPath = argv[++i];
else if (arg == "--secrets-path" && i + 1 < argc)
m_secretsPath = argv[++i];
else if ((arg == "-m" || arg == "--master") && i + 1 < argc)
m_masterPassword = argv[++i];
else if (arg == "--unlock" && i + 1 < argc)
m_unlocks.push_back(argv[++i]);
else if (arg == "--lock" && i + 1 < argc)
m_lock = argv[++i];
else if (arg == "--kdf" && i + 1 < argc)
m_kdf = argv[++i];
else if (arg == "--kdf-param" && i + 2 < argc)
{
auto n = argv[++i];
auto v = argv[++i];
m_kdfParams[n] = v;
}
else if (arg == "--sign-tx" && i + 1 < argc)
{
m_mode = OperationMode::SignTx;
m_signKey = argv[++i];
}
else if (arg == "--tx-data" && i + 1 < argc)
try
{
m_toSign.data = fromHex(argv[++i]);
}
catch (...)
{
cerr << "Invalid argument to " << arg << endl;
exit(-1);
}
else if (arg == "--tx-nonce" && i + 1 < argc)
try
{
m_toSign.nonce = u256(argv[++i]);
}
catch (...)
{
cerr << "Invalid argument to " << arg << endl;
exit(-1);
}
else if ((arg == "--tx-dest" || arg == "--tx-to" || arg == "--tx-destination") && i + 1 < argc)
try
{
m_toSign.creation = false;
m_toSign.to = toAddress(argv[++i]);
}
catch (...)
{
cerr << "Invalid argument to " << arg << endl;
exit(-1);
}
else if (arg == "--tx-gas" && i + 1 < argc)
try
{
m_toSign.gas = u256(argv[++i]);
}
catch (...)
{
cerr << "Invalid argument to " << arg << endl;
exit(-1);
}
else if (arg == "--tx-gasprice" && i + 1 < argc)
try
{
m_toSign.gasPrice = u256(argv[++i]);
}
catch (...)
{
cerr << "Invalid argument to " << arg << endl;
exit(-1);
}
else if (arg == "--tx-value" && i + 1 < argc)
try
{
m_toSign.value = u256(argv[++i]);
}
catch (...)
{
cerr << "Invalid argument to " << arg << endl;
exit(-1);
}
else if (arg == "--decode-tx")
m_mode = OperationMode::DecodeTx;
else if (arg == "--import-bare")
m_mode = OperationMode::ImportBare;
else if (arg == "--list-bare")
m_mode = OperationMode::ListBare;
else if (arg == "--export-bare")
m_mode = OperationMode::ExportBare;
else if (arg == "--inspect-bare")
m_mode = OperationMode::InspectBare;
else if (arg == "--recode-bare")
m_mode = OperationMode::RecodeBare;
else if (arg == "--kill-bare")
m_mode = OperationMode::KillBare;
else if (arg == "--create-wallet")
m_mode = OperationMode::CreateWallet;
else if (arg == "-l" || arg == "--list")
m_mode = OperationMode::List;
else if ((arg == "-n" || arg == "--new") && i + 1 < argc)
{
m_mode = OperationMode::New;
m_name = argv[++i];
}
else if ((arg == "-i" || arg == "--import") && i + 2 < argc)
{
m_mode = OperationMode::Import;
m_inputs = strings(1, argv[++i]);
m_name = argv[++i];
}
else if (arg == "--import-presale" && i + 2 < argc)
{
m_mode = OperationMode::ImportPresale;
m_inputs = strings(1, argv[++i]);
m_name = argv[++i];
}
else if (arg == "--new-brain" && i + 1 < argc)
{
m_mode = OperationMode::NewBrain;
m_name = argv[++i];
}
else if (arg == "--import-brain" && i + 1 < argc)
{
m_mode = OperationMode::ImportBrain;
m_name = argv[++i];
}
else if (arg == "--inspect-brain")
m_mode = OperationMode::InspectBrain;
else if (arg == "--import-with-address" && i + 3 < argc)
{
m_mode = OperationMode::ImportWithAddress;
m_inputs = strings(1, argv[++i]);
m_address = Address(argv[++i]);
m_name = argv[++i];
}
else if (arg == "--export")
m_mode = OperationMode::Export;
else if (arg == "--recode")
m_mode = OperationMode::Recode;
else if (arg == "--no-icap")
m_icap = false;
else if (m_mode == OperationMode::DecodeTx || m_mode == OperationMode::SignTx || m_mode == OperationMode::ImportBare || m_mode == OperationMode::InspectBare || m_mode == OperationMode::KillBare || m_mode == OperationMode::Recode || m_mode == OperationMode::Export || m_mode == OperationMode::RecodeBare || m_mode == OperationMode::ExportBare)
m_inputs.push_back(arg);
else
return false;
return true;
}
KeyPair makeKey() const
{
KeyPair k(Secret::random());
while (m_icap && k.address()[0])
k = KeyPair(Secret(sha3(k.secret().ref())));
return k;
}
Secret getSecret(std::string const& _signKey)
{
string json = contentsString(_signKey);
if (!json.empty())
return Secret(secretStore().secret(secretStore().readKeyContent(json), [&](){ return getPassword("Enter password for key: "); }));
else
{
if (h128 u = fromUUID(_signKey))
return Secret(secretStore().secret(u, [&](){ return getPassword("Enter password for key: "); }));
if (_signKey.substr(0, 6) == "brain#" && _signKey.find(":") != string::npos)
return KeyManager::subkey(KeyManager::brain(_signKey.substr(_signKey.find(":"))), stoul(_signKey.substr(6, _signKey.find(":") - 7)));
if (_signKey.substr(0, 6) == "brain:")
return KeyManager::brain(_signKey.substr(6));
if (_signKey == "brain")
return KeyManager::brain(getPassword("Enter brain wallet phrase: "));
Address a;
DEV_IGNORE_EXCEPTIONS(a = Address(_signKey));
if (a)
return keyManager().secret(a, [&](){ return getPassword("Enter password for key (hint:" + keyManager().passwordHint(a) + "): "); });
cerr << "Bad file, UUID and address: " << _signKey << endl;
exit(-1);
}
}
void execute()
{
switch (m_mode)
{
case OperationMode::CreateWallet:
{
KeyManager wallet(m_walletPath, m_secretsPath);
if (m_masterPassword.empty())
m_masterPassword = createPassword("Please enter a MASTER password to protect your key store (make it strong!): ");
if (m_masterPassword.empty())
cerr << "Aborted (empty password not allowed)." << endl;
else
{
try
{
wallet.create(m_masterPassword);
}
catch (Exception const& _e)
{
cerr << "unable to create wallet" << endl << boost::diagnostic_information(_e);
}
}
break;
}
case OperationMode::DecodeTx:
{
bytes b = inputData(m_inputs[0]);
if (b.empty())
cerr << "Unknown file or bad hex: '" << m_inputs[0] << "'" << endl;
else
try
{
TransactionBase t(b, CheckTransaction::None);
cout << "Transaction " << t.sha3().hex() << endl;
if (t.isCreation())
{
cout << " type: creation" << endl;
cout << " code: " << toHex(t.data()) << endl;
}
else
{
cout << " type: message" << endl;
cout << " to: " << t.to().hex() << endl;
cout << " data: " << (t.data().empty() ? "none" : toHex(t.data())) << endl;
}
try
{
auto s = t.sender();
if (t.isCreation())
cout << " creates: " << toAddress(s, t.nonce()).hex() << endl;
cout << " from: " << s.hex() << endl;
}
catch (...)
{
cout << " from: <unsigned>" << endl;
}
cout << " value: " << formatBalance(t.value()) << " (" << t.value() << " wei)" << endl;
cout << " nonce: " << t.nonce() << endl;
cout << " gas: " << t.gas() << endl;
cout << " gas price: " << formatBalance(t.gasPrice()) << " (" << t.gasPrice() << " wei)" << endl;
cout << " signing hash: " << t.sha3(WithoutSignature).hex() << endl;
cout << " v: " << (int)t.signature().v << endl;
cout << " r: " << t.signature().r << endl;
cout << " s: " << t.signature().s << endl;
}
catch (Exception& ex)
{
cerr << "Invalid transaction: " << ex.what() << endl;
}
break;
}
case OperationMode::SignTx:
{
Secret s = getSecret(m_signKey);
if (m_inputs.empty())
m_inputs.push_back(string());
for (string const& i: m_inputs)
{
bool isFile = false;
try
{
TransactionBase t = i.empty() ? TransactionBase(m_toSign) : TransactionBase(inputData(i, &isFile), CheckTransaction::None);
t.sign(s);
cout << t.sha3() << ": ";
if (isFile)
{
writeFile(i + ".signed", toHex(t.rlp()));
cout << i + ".signed" << endl;
}
else
cout << toHex(t.rlp()) << endl;
}
catch (Exception& ex)
{
cerr << "Invalid transaction: " << ex.what() << endl;
}
}
break;
}
case OperationMode::NewBrain:
{
if (m_name != "--")
keyManager();
boost::random_device d;
boost::random::uniform_int_distribution<unsigned> pickWord(0, WordList.size() - 1);
string seed;
for (int i = 0; i < 13; ++i)
seed += (seed.size() ? " " : "") + WordList[pickWord(d)];
cout << "Your brain key phrase: <<" << seed << ">>" << endl;
if (m_name != "--")
{
std::string hint;
cout << "Enter a hint for the phrase if you want: " << flush;
getline(cin, hint);
Address a = keyManager().importBrain(seed, m_name, hint);
cout << a.abridged() << endl;
cout << " ICAP: " << ICAP(a).encoded() << endl;
cout << " Address: " << a.hex() << endl;
}
break;
}
case OperationMode::InspectBrain:
{
Address a = toAddress(KeyManager::brain(getPassword("Enter brain wallet key phrase: ")));
cout << a.abridged() << endl;
cout << " ICAP: " << ICAP(a).encoded() << endl;
cout << " Address: " << a.hex() << endl;
break;
}
case OperationMode::ListBare:
for (h128 const& u: std::set<h128>() + secretStore().keys())
cout << toUUID(u) << endl;
break;
case OperationMode::NewBare:
{
if (m_lock.empty())
m_lock = createPassword("Enter a password with which to secure this account: ");
auto k = makeKey();
h128 u = secretStore().importSecret(k.secret().ref(), m_lock);
cout << "Created key " << toUUID(u) << endl;
cout << " Address: " << k.address().hex() << endl;
cout << " ICAP: " << ICAP(k.address()).encoded() << endl;
break;
}
case OperationMode::ImportBare:
for (string const& input: m_inputs)
{
h128 u;
bytesSec b;
b.writable() = fromHex(input);
if (b.size() != 32)
{
std::string s = contentsString(input);
b.writable() = fromHex(s);
if (b.size() != 32)
u = secretStore().importKey(input);
}
if (!u && b.size() == 32)
u = secretStore().importSecret(b, lockPassword(toAddress(Secret(b)).abridged()));
if (!u)
{
cerr << "Cannot import " << input << " not a file or secret." << endl;
continue;
}
cout << "Successfully imported " << input << " as " << toUUID(u);
}
break;
case OperationMode::InspectBare:
for (auto const& i: m_inputs)
if (!contents(i).empty())
{
h128 u = secretStore().readKey(i, false);
bytesSec s = secretStore().secret(u, [&](){ return getPassword("Enter password for key " + i + ": "); });
cout << "Key " << i << ":" << endl;
cout << " UUID: " << toUUID(u) << ":" << endl;
cout << " Address: " << toAddress(Secret(s)).hex() << endl;
cout << " Secret: " << toHex(s.ref().cropped(0, 8)) << "..." << endl;
}
else if (h128 u = fromUUID(i))
{
bytesSec s = secretStore().secret(u, [&](){ return getPassword("Enter password for key " + toUUID(u) + ": "); });
cout << "Key " << i << ":" << endl;
cout << " Address: " << toAddress(Secret(s)).hex() << endl;
cout << " Secret: " << toHex(s.ref().cropped(0, 8)) << "..." << endl;
}
else
cerr << "Couldn't inspect " << i << "; not found." << endl;
break;
case OperationMode::ExportBare: break;
case OperationMode::RecodeBare:
for (auto const& i: m_inputs)
if (h128 u = fromUUID(i))
if (secretStore().recode(u, lockPassword(toUUID(u)), [&](){ return getPassword("Enter password for key " + toUUID(u) + ": "); }, kdf()))
cerr << "Re-encoded " << toUUID(u) << endl;
else
cerr << "Couldn't re-encode " << toUUID(u) << "; key corrupt or incorrect password supplied." << endl;
else
cerr << "Couldn't re-encode " << i << "; not found." << endl;
break;
case OperationMode::KillBare:
for (auto const& i: m_inputs)
if (h128 u = fromUUID(i))
secretStore().kill(u);
else
cerr << "Couldn't kill " << i << "; not found." << endl;
break;
case OperationMode::New:
{
keyManager();
tie(m_lock, m_lockHint) = createPassword(keyManager(), "Enter a password with which to secure this account (or nothing to use the master password): ", m_lock, m_lockHint);
auto k = makeKey();
bool usesMaster = m_lock.empty();
h128 u = usesMaster ? keyManager().import(k.secret(), m_name) : keyManager().import(k.secret(), m_name, m_lock, m_lockHint);
cout << "Created key " << toUUID(u) << endl;
cout << " Name: " << m_name << endl;
if (usesMaster)
cout << " Uses master password." << endl;
else
cout << " Password hint: " << m_lockHint << endl;
cout << " Address: " << k.address().hex() << endl;
cout << " ICAP: " << ICAP(k.address()).encoded() << endl;
break;
}
case OperationMode::ImportWithAddress:
{
keyManager();
string const& i = m_inputs[0];
h128 u;
bytesSec b;
b.writable() = fromHex(i);
if (b.size() != 32)
{
std::string s = contentsString(i);
b.writable() = fromHex(s);
if (b.size() != 32)
u = keyManager().store().importKey(i);
}
if (!u && b.size() == 32)
u = keyManager().store().importSecret(b, lockPassword(toAddress(Secret(b)).abridged()));
if (!u)
{
cerr << "Cannot import " << i << " not a file or secret." << endl;
break;
}
keyManager().importExisting(u, m_name, m_address);
cout << "Successfully imported " << i << ":" << endl;
cout << " Name: " << m_name << endl;
cout << " Address: " << m_address << endl;
cout << " UUID: " << toUUID(u) << endl;
break;
}
case OperationMode::ImportBrain:
{
keyManager();
std::string seed = getPassword("Enter brain wallet key phrase: ");
std::string hint;
cout << "Enter a hint for the phrase if you want: " << flush;
getline(cin, hint);
Address a = keyManager().importBrain(seed, m_name, hint);
cout << a << endl;
cout << " ICAP: " << ICAP(a).encoded() << endl;
cout << " Address: " << a.hex() << endl;
break;
}
case OperationMode::ImportPresale:
{
keyManager();
std::string pw;
KeyPair k = keyManager().presaleSecret(contentsString(m_inputs[0]), [&](bool){ return (pw = getPassword("Enter the password for the presale key: ")); });
keyManager().import(k.secret(), m_name, pw, "Same password as used for presale key");
break;
}
case OperationMode::List:
{
vector<u128> bare;
AddressHash got;
for (auto const& u: keyManager().store().keys())
if (Address a = keyManager().address(u))
{
got.insert(a);
cout << toUUID(u) << " " << a.abridged();
string s = ICAP(a).encoded();
cout << " " << s << string(35 - s.size(), ' ');
cout << " " << keyManager().accountName(a) << endl;
}
else
bare.push_back(u);
for (auto const& a: keyManager().accounts())
if (!got.count(a))
{
cout << " (Brain) " << a.abridged();
cout << " " << ICAP(a).encoded() << " ";
cout << " " << keyManager().accountName(a) << endl;
}
for (auto const& u: bare)
cout << toUUID(u) << " (Bare)" << endl;
}
default: break;
}
}
std::string lockPassword(std::string const& _accountName)
{
return m_lock.empty() ? createPassword("Enter a password with which to secure account " + _accountName + ": ") : m_lock;
}
static void streamHelp(ostream& _out)
{
_out
<< "Secret-store (\"bare\") operation modes:" << endl
<< " --list-bare List all secret available in secret-store." << endl
<< " --new-bare Generate and output a key without interacting with wallet and dump the JSON." << endl
<< " --import-bare [ <file>|<secret-hex> , ... ] Import keys from given sources." << endl
<< " --recode-bare [ <uuid>|<file> , ... ] Decrypt and re-encrypt given keys." << endl
<< " --inspect-bare [ <uuid>|<file> , ... ] Output information on given keys." << endl
// << " --export-bare [ <uuid> , ... ] Export given keys." << endl
<< " --kill-bare [ <uuid> , ... ] Delete given keys." << endl
<< "Secret-store configuration:" << endl
<< " --secrets-path <path> Specify Web3 secret-store path (default: " << SecretStore::defaultPath() << ")" << endl
<< endl
<< "Wallet operating modes:" << endl
<< " -l,--list List all keys available in wallet." << endl
<< " -n,--new <name> Create a new key with given name and add it in the wallet." << endl
<< " -i,--import [<uuid>|<file>|<secret-hex>] <name> Import keys from given source and place in wallet." << endl
<< " --import-presale <file> <name> Import a presale wallet into a key with the given name." << endl
<< " --import-with-address [<uuid>|<file>|<secret-hex>] <address> <name> Import keys from given source with given address and place in wallet." << endl
<< " -e,--export [ <address>|<uuid> , ... ] Export given keys." << endl
<< " -r,--recode [ <address>|<uuid>|<file> , ... ] Decrypt and re-encrypt given keys." << endl
<< "Brain wallet operating modes:" << endl
<< "WARNING: Brain wallets with human-generated passphrasses are highly susceptible to attack. Don't use such a thing for" << endl
<< "anything important." << endl
<< " --new-brain [ <name>|-- ] Create a new 13-word brain wallet; argument is the name or if --, do not add to wallet."<< endl
<< " --import-brain <name> Import your own brain wallet." << endl
<< " --inspect-brain Check the address of a particular brain wallet." << endl
<< "Wallet configuration:" << endl
<< " --create-wallet Create an Ethereum master wallet." << endl
<< " --wallet-path <path> Specify Ethereum wallet path (default: " << KeyManager::defaultPath() << ")" << endl
<< " -m, --master <password> Specify wallet (master) password." << endl
<< endl
<< "Transaction operating modes:" << endl
<< " -d,--decode-tx [<hex>|<file>] Decode given transaction." << endl
<< " -s,--sign-tx [ <address>|<uuid>|<file>|brain((#<HD-index>):<brain-phrase>) ] [ <hex>|<file> , ... ] (Re-)Sign given transaction." << endl
<< endl
<< "Encryption configuration:" << endl
<< " --kdf <kdfname> Specify KDF to use when encrypting (default: sc rypt)" << endl
<< " --kdf-param <name> <value> Specify a parameter for the KDF." << endl
// << " --cipher <ciphername> Specify cipher to use when encrypting (default: aes-128-ctr)" << endl
// << " --cipher-param <name> <value> Specify a parameter for the cipher." << endl
<< " --lock <password> Specify password for when encrypting a (the) key." << endl
<< " --hint <hint> Specify hint for the --lock password." << endl
<< endl
<< "Decryption configuration:" << endl
<< " --unlock <password> Specify password for a (the) key." << endl
<< "Key generation configuration:" << endl
<< " --no-icap Don't bother to make a direct-ICAP capable key." << endl
;
}
static bytes inputData(std::string const& _input, bool* _isFile = nullptr)
{
bytes b = fromHex(_input);
if (_isFile)
*_isFile = false;
if (b.empty())
{
if (_isFile)
*_isFile = true;
std::string s = boost::trim_copy_if(contentsString(_input), is_any_of(" \t\n"));
b = fromHex(s);
if (b.empty())
b = asBytes(s);
}
return b;
}
static bool isTrue(std::string const& _m)
{
return _m == "on" || _m == "yes" || _m == "true" || _m == "1";
}
static bool isFalse(std::string const& _m)
{
return _m == "off" || _m == "no" || _m == "false" || _m == "0";
}
private:
void openWallet(KeyManager& _w)
{
while (true)
{
if (_w.load(m_masterPassword))
break;
if (!m_masterPassword.empty())
{
cout << "Password invalid. Try again." << endl;
m_masterPassword.clear();
}
m_masterPassword = getPassword("Please enter your MASTER password: ");
}
}
KDF kdf() const { return m_kdf == "pbkdf2" ? KDF::PBKDF2_SHA256 : KDF::Scrypt; }
KeyManager& keyManager()
{
if (!m_keyManager)
{
m_keyManager.reset(new KeyManager(m_walletPath, m_secretsPath));
if (m_keyManager->exists())
openWallet(*m_keyManager);
else
{
cerr << "Couldn't open wallet. Does it exist?" << endl;
exit(-1);
}
}
return *m_keyManager;
}
SecretStore& secretStore()
{
if (m_keyManager)
return m_keyManager->store();
if (!m_secretStore)
m_secretStore.reset(new SecretStore(m_secretsPath));
return *m_secretStore;
}
/// Where the keys are.
unique_ptr<SecretStore> m_secretStore;
unique_ptr<KeyManager> m_keyManager;
/// Operating mode.
OperationMode m_mode;
/// Wallet stuff
string m_secretsPath = SecretStore::defaultPath();
string m_walletPath = KeyManager::defaultPath();
/// Wallet password stuff
string m_masterPassword;
strings m_unlocks;
string m_lock;
string m_lockHint;
bool m_icap = true;
/// Creating/importing
string m_name;
Address m_address;
/// Importing
strings m_inputs;
/// Signing
string m_signKey;
TransactionSkeleton m_toSign;
string m_kdf = "scrypt";
map<string, string> m_kdfParams;
// string m_cipher;
// map<string, string> m_cipherParams;
};

84
ethkey/main.cpp

@ -1,84 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file main.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
* Ethereum client.
*/
#include <thread>
#include <chrono>
#include <fstream>
#include <iostream>
#include <libdevcore/FileSystem.h>
#include <libdevcore/Log.h>
#include <libethcore/KeyManager.h>
#include "BuildInfo.h"
#include "KeyAux.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
void help()
{
cout
<< "Usage ethkey [OPTIONS]" << endl
<< "Options:" << endl << endl;
KeyCLI::streamHelp(cout);
cout
<< "General Options:" << endl
<< " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (default: 8)." << endl
<< " -V,--version Show the version and exit." << endl
<< " -h,--help Show this help message and exit." << endl
;
exit(0);
}
void version()
{
cout << "ethkey version " << dev::Version << endl;
cout << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl;
exit(0);
}
int main(int argc, char** argv)
{
KeyCLI m(KeyCLI::OperationMode::ListBare);
g_logVerbosity = 0;
for (int i = 1; i < argc; ++i)
{
string arg = argv[i];
if (m.interpretOption(i, argc, argv)) {}
else if ((arg == "-v" || arg == "--verbosity") && i + 1 < argc)
g_logVerbosity = atoi(argv[++i]);
else if (arg == "-h" || arg == "--help")
help();
else if (arg == "-V" || arg == "--version")
version();
else
{
cerr << "Invalid argument: " << arg << endl;
exit(-1);
}
}
m.execute();
return 0;
}

19
ethvm/CMakeLists.txt

@ -1,19 +0,0 @@
cmake_policy(SET CMP0015 NEW)
set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..)
include_directories(${DB_INCLUDE_DIRS})
set(EXECUTABLE ethvm)
add_executable(${EXECUTABLE} ${SRC_LIST})
target_link_libraries(${EXECUTABLE} ethereum)
if (APPLE)
install(TARGETS ${EXECUTABLE} DESTINATION bin)
else()
eth_install_executable(${EXECUTABLE})
endif()

224
ethvm/main.cpp

@ -1,224 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file main.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
* EVM Execution tool.
*/
#include <fstream>
#include <iostream>
#include <ctime>
#include <boost/algorithm/string.hpp>
#include <libdevcore/CommonIO.h>
#include <libdevcore/RLP.h>
#include <libdevcore/SHA3.h>
#include <libethereum/Block.h>
#include <libethereum/Executive.h>
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
using namespace std;
using namespace dev;
using namespace eth;
void help()
{
cout
<< "Usage ethvm <options> [trace|stats|output] (<file>|--)" << endl
<< "Transaction options:" << endl
<< " --value <n> Transaction should transfer the <n> wei (default: 0)." << endl
<< " --gas <n> Transaction should be given <n> gas (default: block gas limit)." << endl
<< " --gas-price <n> Transaction's gas price' should be <n> (default: 0)." << endl
<< " --sender <a> Transaction sender should be <a> (default: 0000...0069)." << endl
<< " --origin <a> Transaction origin should be <a> (default: 0000...0069)." << endl
#if ETH_EVMJIT || !ETH_TRUE
<< endl
<< "VM options:" << endl
<< " --vm <vm-kind> Select VM. Options are: interpreter, jit, smart. (default: interpreter)" << endl
#endif
<< endl
<< "Options for trace:" << endl
<< " --flat Minimal whitespace in the JSON." << endl
<< " --mnemonics Show instruction mnemonics in the trace (non-standard)." << endl
<< endl
<< "General options:" << endl
<< " -V,--version Show the version and exit." << endl
<< " -h,--help Show this help message and exit." << endl;
exit(0);
}
void version()
{
cout << "ethvm version " << dev::Version << endl;
cout << "By Gav Wood, 2015." << endl;
cout << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl;
exit(0);
}
enum class Mode
{
Trace,
Statistics,
OutputOnly
};
int main(int argc, char** argv)
{
string incoming = "--";
Mode mode = Mode::Statistics;
State state;
Address sender = Address(69);
Address origin = Address(69);
u256 value = 0;
u256 gas = Block().gasLimitRemaining();
u256 gasPrice = 0;
bool styledJson = true;
StandardTrace st;
EnvInfo envInfo;
for (int i = 1; i < argc; ++i)
{
string arg = argv[i];
if (arg == "-h" || arg == "--help")
help();
else if (arg == "-V" || arg == "--version")
version();
#if ETH_EVMJIT
else if (arg == "--vm" && i + 1 < argc)
{
string vmKind = argv[++i];
if (vmKind == "interpreter")
VMFactory::setKind(VMKind::Interpreter);
else if (vmKind == "jit")
VMFactory::setKind(VMKind::JIT);
else if (vmKind == "smart")
VMFactory::setKind(VMKind::Smart);
else
{
cerr << "Unknown VM kind: " << vmKind << endl;
return -1;
}
}
#endif
else if (arg == "--mnemonics")
st.setShowMnemonics();
else if (arg == "--flat")
styledJson = false;
else if (arg == "--value" && i + 1 < argc)
value = u256(argv[++i]);
else if (arg == "--sender" && i + 1 < argc)
sender = Address(argv[++i]);
else if (arg == "--origin" && i + 1 < argc)
origin = Address(argv[++i]);
else if (arg == "--gas" && i + 1 < argc)
gas = u256(argv[++i]);
else if (arg == "--gas-price" && i + 1 < argc)
gasPrice = u256(argv[++i]);
else if (arg == "--value" && i + 1 < argc)
value = u256(argv[++i]);
else if (arg == "--value" && i + 1 < argc)
value = u256(argv[++i]);
else if (arg == "--beneficiary" && i + 1 < argc)
envInfo.setBeneficiary(Address(argv[++i]));
else if (arg == "--number" && i + 1 < argc)
envInfo.setNumber(u256(argv[++i]));
else if (arg == "--difficulty" && i + 1 < argc)
envInfo.setDifficulty(u256(argv[++i]));
else if (arg == "--timestamp" && i + 1 < argc)
envInfo.setTimestamp(u256(argv[++i]));
else if (arg == "--gas-limit" && i + 1 < argc)
envInfo.setGasLimit(u256(argv[++i]));
else if (arg == "--value" && i + 1 < argc)
value = u256(argv[++i]);
else if (arg == "stats")
mode = Mode::Statistics;
else if (arg == "output")
mode = Mode::OutputOnly;
else if (arg == "trace")
mode = Mode::Trace;
else
incoming = arg;
}
bytes code;
if (incoming == "--" || incoming.empty())
for (int i = cin.get(); i != -1; i = cin.get())
code.push_back((char)i);
else
code = contents(incoming);
bytes data = fromHex(boost::trim_copy(asString(code)));
if (data.empty())
data = code;
state.addBalance(sender, value);
Executive executive(state, envInfo);
ExecutionResult res;
executive.setResultRecipient(res);
Transaction t = eth::Transaction(value, gasPrice, gas, data, 0);
t.forceSender(sender);
unordered_map<byte, pair<unsigned, bigint>> counts;
unsigned total = 0;
bigint memTotal;
auto onOp = [&](uint64_t step, Instruction inst, bigint m, bigint gasCost, bigint gas, VM* vm, ExtVMFace const* extVM) {
if (mode == Mode::Statistics)
{
counts[(byte)inst].first++;
counts[(byte)inst].second += gasCost;
total++;
if (m > 0)
memTotal = m;
}
else if (mode == Mode::Trace)
st(step, inst, m, gasCost, gas, vm, extVM);
};
executive.initialize(t);
executive.create(sender, value, gasPrice, gas, &data, origin);
Timer timer;
executive.go(onOp);
double execTime = timer.elapsed();
executive.finalize();
bytes output = std::move(res.output);
if (mode == Mode::Statistics)
{
cout << "Gas used: " << res.gasUsed << " (+" << t.gasRequired() << " for transaction, -" << res.gasRefunded << " refunded)" << endl;
cout << "Output: " << toHex(output) << endl;
LogEntries logs = executive.logs();
cout << logs.size() << " logs" << (logs.empty() ? "." : ":") << endl;
for (LogEntry const& l: logs)
{
cout << " " << l.address.hex() << ": " << toHex(t.data()) << endl;
for (h256 const& t: l.topics)
cout << " " << t.hex() << endl;
}
cout << total << " operations in " << execTime << " seconds." << endl;
cout << "Maximum memory usage: " << memTotal * 32 << " bytes" << endl;
cout << "Expensive operations:" << endl;
for (auto const& c: {Instruction::SSTORE, Instruction::SLOAD, Instruction::CALL, Instruction::CREATE, Instruction::CALLCODE, Instruction::MSTORE8, Instruction::MSTORE, Instruction::MLOAD, Instruction::SHA3})
if (!!counts[(byte)c].first)
cout << " " << instructionInfo(c).name << " x " << counts[(byte)c].first << " (" << counts[(byte)c].second << " gas)" << endl;
}
else if (mode == Mode::Trace)
cout << st.json(styledJson);
else if (mode == Mode::OutputOnly)
cout << toHex(output);
return 0;
}

1
evmjit/.gitignore

@ -1 +0,0 @@
/build/

65
evmjit/CMakeLists.txt

@ -1,65 +0,0 @@
cmake_minimum_required(VERSION 2.8.12)
if (${CMAKE_VERSION} VERSION_GREATER 3.0)
cmake_policy(SET CMP0042 OLD) # fix MACOSX_RPATH
cmake_policy(SET CMP0048 NEW) # allow VERSION argument in project()
project(EVMJIT VERSION 0.9.0.1 LANGUAGES CXX)
else()
project(EVMJIT)
set(EVMJIT_VERSION "0.9.0.1")
set(EVMJIT_VERSION_MAJOR 0)
set(EVMJIT_VERSION_MINOR 9)
set(EVMJIT_VERSION_PATCH 0)
set(EVMJIT_VERSION_TWEAK 1)
endif()
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_AUTOMOC OFF)
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
else()
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas ${CMAKE_CXX_FLAGS}")
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT ${CMAKE_BUILD_TYPE} STREQUAL "DebugSan")
# Do not allow unresovled symbols in shared library (default on linux)
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined")
endif()
# LLVM
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT LLVM_DIR)
# Workaround for Ubuntu broken LLVM package
find_program(LLVM3_7_CONFIG llvm-config-3.7)
find_program(LLVM3_8_CONFIG llvm-config-3.8)
if (LLVM3_7_CONFIG)
message(STATUS "Using llvm-3.7-dev package from Ubuntu. If does not work, build LLVM and set -DLLVM_DIR=llvm-build/share/llvm/cmake")
set(LLVM_CONFIG_EXEC llvm-config-3.7)
set(LLVM_LIB_DIR /usr/lib/llvm-3.7/lib)
elseif(LLVM3_8_CONFIG)
message(STATUS "Using llvm-3.8-dev package from Ubuntu. If does not work, build LLVM and set -DLLVM_DIR=llvm-build/share/llvm/cmake")
set(LLVM_CONFIG_EXEC llvm-config-3.8)
set(LLVM_LIB_DIR /usr/lib/llvm-3.8/lib)
else()
message(FATAL_ERROR "No LLVM package found!")
endif()
execute_process(COMMAND ${LLVM_CONFIG_EXEC} --includedir OUTPUT_VARIABLE LLVM_INCLUDE_DIRS OUTPUT_STRIP_TRAILING_WHITESPACE)
message(STATUS "LLVM include dirs: ${LLVM_INCLUDE_DIRS}")
set(LLVM_LIBS "-lLLVMipo -lLLVMVectorize -lLLVMX86AsmParser -lLLVMX86CodeGen -lLLVMX86Desc -lLLVMX86Info -lLLVMMCDisassembler -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMCodeGen -lLLVMScalarOpts -lLLVMProfileData -lLLVMInstCombine -lLLVMInstrumentation -lLLVMTransformUtils -lLLVMipa -lLLVMMCJIT -lLLVMExecutionEngine -lLLVMTarget -lLLVMAnalysis -lLLVMRuntimeDyld -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMMC -lLLVMCore -lLLVMSupport -lz -lpthread -lffi -ltinfo -ldl -lm")
set(LLVM_DEFINITIONS "-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS")
link_directories(${LLVM_LIB_DIR})
else()
find_package(LLVM REQUIRED CONFIG)
if (${LLVM_VERSION} VERSION_LESS 3.7)
message(FATAL_ERROR "Incompatible LLVM version ${LLVM_VERSION}")
endif()
message(STATUS "Found LLVM ${LLVM_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen ipo)
endif()
add_subdirectory(libevmjit)
if(EVMJIT_CPP)
add_subdirectory(libevmjit-cpp)
endif()

21
evmjit/LICENSE.md

@ -1,21 +0,0 @@
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.

36
evmjit/README.md

@ -1,36 +0,0 @@
# 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 variable, e.g. `EVMJIT="-help" testeth --jit`.

70
evmjit/include/evmjit/JIT-c.h

@ -1,70 +0,0 @@
#include "stdint.h"
#ifdef _MSC_VER
#define EXPORT __declspec(dllexport)
#define _ALLOW_KEYWORD_MACROS
#define noexcept throw()
#else
#define EXPORT
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct evmjit_i256
{
uint64_t words[4];
} evmjit_i256;
typedef struct evmjit_runtime_data
{
int64_t gas;
int64_t gasPrice;
char const* callData;
uint64_t callDataSize;
evmjit_i256 address;
evmjit_i256 caller;
evmjit_i256 origin;
evmjit_i256 callValue;
evmjit_i256 coinBase;
evmjit_i256 difficulty;
evmjit_i256 gasLimit;
uint64_t number;
int64_t timestamp;
char const* code;
uint64_t codeSize;
evmjit_i256 codeHash;
} evmjit_runtime_data;
typedef enum evmjit_return_code
{
// Success codes
Stop = 0,
Return = 1,
Suicide = 2,
// Standard error codes
OutOfGas = -1,
// Internal error codes
LLVMError = -101,
UnexpectedException = -111
} evmjit_return_code;
typedef struct evmjit_context evmjit_context;
EXPORT evmjit_context* evmjit_create(evmjit_runtime_data* _data, void* _env);
EXPORT evmjit_return_code evmjit_exec(evmjit_context* _context);
EXPORT void evmjit_destroy(evmjit_context* _context);
inline char const* evmjit_get_output(evmjit_runtime_data* _data) { return _data->callData; }
inline uint64_t evmjit_get_output_size(evmjit_runtime_data* _data) { return _data->callDataSize; }
#ifdef __cplusplus
}
#endif

172
evmjit/include/evmjit/JIT.h

@ -1,172 +0,0 @@
#pragma once
#include <cstdint>
#include <cstring>
#include <functional>
#ifdef _MSC_VER
#define EXPORT __declspec(dllexport)
#define _ALLOW_KEYWORD_MACROS
#define noexcept throw()
#else
#define EXPORT
#endif
namespace dev
{
namespace evmjit
{
using byte = uint8_t;
using bytes_ref = std::tuple<byte const*, size_t>;
/// Representation of 256-bit hash value
struct h256
{
uint64_t words[4];
};
inline bool operator==(h256 const& _h1, h256 const& _h2)
{
return _h1.words[0] == _h2.words[0] &&
_h1.words[1] == _h2.words[1] &&
_h1.words[2] == _h2.words[2] &&
_h1.words[3] == _h2.words[3];
}
/// Representation of 256-bit value binary compatible with LLVM i256
struct i256
{
uint64_t words[4];
i256() = default;
i256(h256 const& _h) { std::memcpy(this, &_h, sizeof(*this)); }
};
// TODO: Merge with ExecutionContext
struct RuntimeData
{
enum Index
{
Gas,
GasPrice,
CallData,
CallDataSize,
Address,
Caller,
Origin,
CallValue,
CoinBase,
Difficulty,
GasLimit,
Number,
Timestamp,
Code,
CodeSize,
SuicideDestAddress = Address, ///< Suicide balance destination address
ReturnData = CallData, ///< Return data pointer (set only in case of RETURN)
ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN)
};
static size_t const numElements = CodeSize + 1;
int64_t gas = 0;
int64_t gasPrice = 0;
byte const* callData = nullptr;
uint64_t callDataSize = 0;
i256 address;
i256 caller;
i256 origin;
i256 callValue;
i256 coinBase;
i256 difficulty;
i256 gasLimit;
uint64_t number = 0;
int64_t timestamp = 0;
byte const* code = nullptr;
uint64_t codeSize = 0;
h256 codeHash;
};
/// VM Environment (ExtVM) opaque type
struct Env;
enum class ReturnCode
{
// Success codes
Stop = 0,
Return = 1,
Suicide = 2,
// Standard error codes
OutOfGas = -1,
// Internal error codes
LLVMError = -101,
UnexpectedException = -111,
LinkerWorkaround = -299,
};
class ExecutionContext
{
public:
ExecutionContext() = default;
ExecutionContext(RuntimeData& _data, Env* _env) { init(_data, _env); }
ExecutionContext(ExecutionContext const&) = delete;
ExecutionContext& operator=(ExecutionContext const&) = delete;
EXPORT ~ExecutionContext() noexcept;
void init(RuntimeData& _data, Env* _env) { m_data = &_data; m_env = _env; }
byte const* code() const { return m_data->code; }
uint64_t codeSize() const { return m_data->codeSize; }
h256 const& codeHash() const { return m_data->codeHash; }
bytes_ref getReturnData() const;
protected:
RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract.
Env* m_env = nullptr; ///< Pointer to environment proxy. Expected by compiled contract.
byte* m_memData = nullptr;
uint64_t m_memSize = 0;
uint64_t m_memCap = 0;
public:
/// Reference to returned data (RETURN opcode used)
bytes_ref returnData;
};
class JIT
{
public:
/// Ask JIT if the EVM code is ready for execution.
/// Returns `true` if the EVM code has been compiled and loaded into memory.
/// In this case the code can be executed without overhead.
/// \param _codeHash The Keccak hash of the EVM code.
EXPORT static bool isCodeReady(h256 const& _codeHash);
/// Compile the given EVM code to machine code and make available for execution.
EXPORT static void compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash);
EXPORT static ReturnCode exec(ExecutionContext& _context);
};
}
}
namespace std
{
template<> struct hash<dev::evmjit::h256>
{
size_t operator()(dev::evmjit::h256 const& _h) const
{
/// This implementation expects the argument to be a full 256-bit Keccak hash.
/// It does nothing more than returning a slice of the input hash.
return static_cast<size_t>(_h.words[0]);
};
};
}

24
evmjit/libevmjit-cpp/CMakeLists.txt

@ -1,24 +0,0 @@
set(TARGET_NAME evmjit-cpp)
# Boost
find_package(Boost REQUIRED)
set(SOURCES
Env.cpp
JitVM.cpp JitVM.h
Utils.h
)
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} STATIC ${SOURCES})
set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs")
include_directories(../..)
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} evmjit)

135
evmjit/libevmjit-cpp/Env.cpp

@ -1,135 +0,0 @@
#pragma GCC diagnostic ignored "-Wconversion"
#include <libdevcore/SHA3.h>
#include <libevmcore/Params.h>
#include <libevm/ExtVMFace.h>
#include "Utils.h"
extern "C"
{
#ifdef _MSC_VER
#define EXPORT __declspec(dllexport)
#else
#define EXPORT
#endif
using namespace dev;
using namespace dev::eth;
using evmjit::i256;
EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value)
{
auto index = jit2eth(*_index);
auto value = _env->store(index); // Interface uses native endianness
*o_value = eth2jit(value);
}
EXPORT void env_sstore(ExtVMFace* _env, i256* _index, i256* _value)
{
auto index = jit2eth(*_index);
auto value = jit2eth(*_value);
if (value == 0 && _env->store(index) != 0) // If delete
_env->sub.refunds += c_sstoreRefundGas; // Increase refund counter
_env->setStore(index, value); // Interface uses native endianness
}
EXPORT void env_balance(ExtVMFace* _env, h256* _address, i256* o_value)
{
auto u = _env->balance(right160(*_address));
*o_value = eth2jit(u);
}
EXPORT void env_blockhash(ExtVMFace* _env, i256* _number, h256* o_hash)
{
*o_hash = _env->blockHash(jit2eth(*_number));
}
EXPORT void env_create(ExtVMFace* _env, int64_t* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address)
{
auto endowment = jit2eth(*_endowment);
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024)
{
u256 gas = *io_gas;
h256 address(_env->create(endowment, gas, {_initBeg, (size_t)_initSize}, {}), h256::AlignRight);
*io_gas = static_cast<int64_t>(gas);
*o_address = address;
}
else
*o_address = {};
}
EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, int64_t _callGas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress)
{
CallParameters params;
params.value = jit2eth(*_value);
params.senderAddress = _env->myAddress;
params.receiveAddress = right160(*_receiveAddress);
params.codeAddress = right160(*_codeAddress);
params.data = {_inBeg, (size_t)_inSize};
params.out = {_outBeg, (size_t)_outSize};
params.onOp = {};
const auto isCall = params.receiveAddress == params.codeAddress; // OPT: The same address pointer can be used if not CODECALL
*io_gas -= _callGas;
if (*io_gas < 0)
return false;
if (isCall && !_env->exists(params.receiveAddress))
*io_gas -= static_cast<int64_t>(c_callNewAccountGas); // no underflow, *io_gas non-negative before
if (params.value > 0) // value transfer
{
/*static*/ assert(c_callValueTransferGas > c_callStipend && "Overflow possible");
*io_gas -= static_cast<int64_t>(c_callValueTransferGas); // no underflow
_callGas += static_cast<int64_t>(c_callStipend); // overflow possibility, but in the same time *io_gas < 0
}
if (*io_gas < 0)
return false;
auto ret = false;
params.gas = u256{_callGas};
if (_env->balance(_env->myAddress) >= params.value && _env->depth < 1024)
ret = _env->call(params);
*io_gas += static_cast<int64_t>(params.gas); // it is never more than initial _callGas
return ret;
}
EXPORT void env_sha3(byte* _begin, uint64_t _size, h256* o_hash)
{
auto hash = sha3({_begin, (size_t)_size});
*o_hash = hash;
}
EXPORT byte const* env_extcode(ExtVMFace* _env, h256* _addr256, uint64_t* o_size)
{
auto addr = right160(*_addr256);
auto& code = _env->codeAt(addr);
*o_size = code.size();
return code.data();
}
EXPORT void env_log(ExtVMFace* _env, byte* _beg, uint64_t _size, h256* _topic1, h256* _topic2, h256* _topic3, h256* _topic4)
{
dev::h256s topics;
if (_topic1)
topics.push_back(*_topic1);
if (_topic2)
topics.push_back(*_topic2);
if (_topic3)
topics.push_back(*_topic3);
if (_topic4)
topics.push_back(*_topic4);
_env->log(std::move(topics), {_beg, (size_t)_size});
}
}

77
evmjit/libevmjit-cpp/JitVM.cpp

@ -1,77 +0,0 @@
#pragma GCC diagnostic ignored "-Wconversion"
#include "JitVM.h"
#include <libdevcore/Log.h>
#include <libdevcore/SHA3.h>
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include "Utils.h"
namespace dev
{
namespace eth
{
extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below
bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
{
auto rejected = false;
// TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope
rejected |= io_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
rejected |= _ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max();
rejected |= _ext.envInfo().number() > std::numeric_limits<decltype(m_data.number)>::max();
rejected |= _ext.envInfo().timestamp() > std::numeric_limits<decltype(m_data.timestamp)>::max();
if (rejected)
{
cwarn << "Execution rejected by EVM JIT (gas limit: " << io_gas << "), executing with interpreter";
m_fallbackVM = VMFactory::create(VMKind::Interpreter);
return m_fallbackVM->execImpl(io_gas, _ext, _onOp);
}
m_data.gas = static_cast<decltype(m_data.gas)>(io_gas);
m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice);
m_data.callData = _ext.data.data();
m_data.callDataSize = _ext.data.size();
m_data.address = eth2jit(fromAddress(_ext.myAddress));
m_data.caller = eth2jit(fromAddress(_ext.caller));
m_data.origin = eth2jit(fromAddress(_ext.origin));
m_data.callValue = eth2jit(_ext.value);
m_data.coinBase = eth2jit(fromAddress(_ext.envInfo().beneficiary()));
m_data.difficulty = eth2jit(_ext.envInfo().difficulty());
m_data.gasLimit = eth2jit(_ext.envInfo().gasLimit());
m_data.number = static_cast<decltype(m_data.number)>(_ext.envInfo().number());
m_data.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.envInfo().timestamp());
m_data.code = _ext.code.data();
m_data.codeSize = _ext.code.size();
m_data.codeHash = eth2jit(_ext.codeHash);
// Pass pointer to ExtVMFace casted to evmjit::Env* opaque type.
// JIT will do nothing with the pointer, just pass it to Env callback functions implemented in Env.cpp.
m_context.init(m_data, reinterpret_cast<evmjit::Env*>(&_ext));
auto exitCode = evmjit::JIT::exec(m_context);
switch (exitCode)
{
case evmjit::ReturnCode::Suicide:
_ext.suicide(right160(jit2eth(m_data.address)));
break;
case evmjit::ReturnCode::OutOfGas:
BOOST_THROW_EXCEPTION(OutOfGas());
case evmjit::ReturnCode::LinkerWorkaround: // never happens
env_sload(); // but forces linker to include env_* JIT callback functions
break;
default:
break;
}
io_gas = m_data.gas;
return {std::get<0>(m_context.returnData), std::get<1>(m_context.returnData)};
}
}
}

24
evmjit/libevmjit-cpp/JitVM.h

@ -1,24 +0,0 @@
#pragma once
#include <libevm/VMFace.h>
#include <evmjit/JIT.h>
namespace dev
{
namespace eth
{
class JitVM: public VMFace
{
public:
virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final;
private:
evmjit::RuntimeData m_data;
evmjit::ExecutionContext m_context;
std::unique_ptr<VMFace> m_fallbackVM; ///< VM used in case of input data rejected by JIT
};
}
}

45
evmjit/libevmjit-cpp/Utils.h

@ -1,45 +0,0 @@
#pragma once
#include <evmjit/JIT.h>
namespace dev
{
namespace eth
{
/// Converts EVM JIT representation of 256-bit integer to eth type dev::u256.
inline u256 jit2eth(evmjit::i256 _i)
{
u256 u = _i.words[3];
u <<= 64;
u |= _i.words[2];
u <<= 64;
u |= _i.words[1];
u <<= 64;
u |= _i.words[0];
return u;
}
/// Converts eth type dev::u256 to EVM JIT representation of 256-bit integer.
inline evmjit::i256 eth2jit(u256 _u)
{
evmjit::i256 i;
i.words[0] = static_cast<uint64_t>(_u);
_u >>= 64;
i.words[1] = static_cast<uint64_t>(_u);
_u >>= 64;
i.words[2] = static_cast<uint64_t>(_u);
_u >>= 64;
i.words[3] = static_cast<uint64_t>(_u);
return i;
}
/// Converts eth type dev::h256 to EVM JIT representation of 256-bit hash value.
inline evmjit::h256 eth2jit(h256 _u)
{
/// Just directly copies memory
return *(evmjit::h256*)&_u;
}
}
}

508
evmjit/libevmjit/Arith256.cpp

@ -1,508 +0,0 @@
#include "Arith256.h"
#include <iostream>
#include <iomanip>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "Type.h"
#include "Endianness.h"
#include "Utils.h"
namespace dev
{
namespace eth
{
namespace jit
{
Arith256::Arith256(llvm::IRBuilder<>& _builder) :
CompilerHelper(_builder)
{}
void Arith256::debug(llvm::Value* _value, char _c)
{
if (!m_debug)
{
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)});
}
llvm::Function* Arith256::getMulFunc(llvm::Module& _module)
{
static const auto funcName = "evm.mul.i256";
if (auto func = _module.getFunction(funcName))
return func;
llvm::Type* argTypes[] = {Type::Word, Type::Word};
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto i64 = Type::Size;
auto i128 = builder.getIntNTy(128);
auto i256 = Type::Word;
auto c64 = Constant::get(64);
auto c128 = Constant::get(128);
auto c192 = Constant::get(192);
auto x_lo = builder.CreateTrunc(x, i64, "x.lo");
auto y_lo = builder.CreateTrunc(y, i64, "y.lo");
auto x_mi = builder.CreateTrunc(builder.CreateLShr(x, c64), i64);
auto y_mi = builder.CreateTrunc(builder.CreateLShr(y, c64), i64);
auto x_hi = builder.CreateTrunc(builder.CreateLShr(x, c128), i128);
auto y_hi = builder.CreateTrunc(builder.CreateLShr(y, c128), i128);
auto t1 = builder.CreateMul(builder.CreateZExt(x_lo, i128), builder.CreateZExt(y_lo, i128));
auto t2 = builder.CreateMul(builder.CreateZExt(x_lo, i128), builder.CreateZExt(y_mi, i128));
auto t3 = builder.CreateMul(builder.CreateZExt(x_lo, i128), y_hi);
auto t4 = builder.CreateMul(builder.CreateZExt(x_mi, i128), builder.CreateZExt(y_lo, i128));
auto t5 = builder.CreateMul(builder.CreateZExt(x_mi, i128), builder.CreateZExt(y_mi, i128));
auto t6 = builder.CreateMul(builder.CreateZExt(x_mi, i128), y_hi);
auto t7 = builder.CreateMul(x_hi, builder.CreateZExt(y_lo, i128));
auto t8 = builder.CreateMul(x_hi, builder.CreateZExt(y_mi, i128));
auto p = builder.CreateZExt(t1, i256);
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t2, i256), c64));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t3, i256), c128));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t4, i256), c64));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t5, i256), c128));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t6, i256), c192));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t7, i256), c128));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t8, i256), c192));
builder.CreateRet(p);
return func;
}
llvm::Function* Arith256::getMul512Func(llvm::Module& _module)
{
static const auto funcName = "evm.mul.i512";
if (auto func = _module.getFunction(funcName))
return func;
auto i512Ty = llvm::IntegerType::get(_module.getContext(), 512);
auto func = llvm::Function::Create(llvm::FunctionType::get(i512Ty, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto i128 = builder.getIntNTy(128);
auto i256 = Type::Word;
auto x_lo = builder.CreateZExt(builder.CreateTrunc(x, i128, "x.lo"), i256);
auto y_lo = builder.CreateZExt(builder.CreateTrunc(y, i128, "y.lo"), i256);
auto x_hi = builder.CreateZExt(builder.CreateTrunc(builder.CreateLShr(x, Constant::get(128)), i128, "x.hi"), i256);
auto y_hi = builder.CreateZExt(builder.CreateTrunc(builder.CreateLShr(y, Constant::get(128)), i128, "y.hi"), i256);
auto mul256Func = getMulFunc(_module);
auto t1 = builder.CreateCall(mul256Func, {x_lo, y_lo});
auto t2 = builder.CreateCall(mul256Func, {x_lo, y_hi});
auto t3 = builder.CreateCall(mul256Func, {x_hi, y_lo});
auto t4 = builder.CreateCall(mul256Func, {x_hi, y_hi});
auto p = builder.CreateZExt(t1, i512Ty);
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t2, i512Ty), builder.getIntN(512, 128)));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t3, i512Ty), builder.getIntN(512, 128)));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t4, i512Ty), builder.getIntN(512, 256)));
builder.CreateRet(p);
return func;
}
namespace
{
llvm::Function* createUDivRemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName)
{
// Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research
// The following algorithm also handles divisor of value 0 returning 0 for both quotient and remainder
auto retType = llvm::VectorType::get(_type, 2);
auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto zero = llvm::ConstantInt::get(_type, 0);
auto one = llvm::ConstantInt::get(_type, 1);
auto x = &func->getArgumentList().front();
x->setName("x");
auto yArg = x->getNextNode();
yArg->setName("y");
auto entryBB = llvm::BasicBlock::Create(_module.getContext(), "Entry", func);
auto mainBB = llvm::BasicBlock::Create(_module.getContext(), "Main", func);
auto loopBB = llvm::BasicBlock::Create(_module.getContext(), "Loop", func);
auto continueBB = llvm::BasicBlock::Create(_module.getContext(), "Continue", func);
auto returnBB = llvm::BasicBlock::Create(_module.getContext(), "Return", func);
auto builder = llvm::IRBuilder<>{entryBB};
auto yLEx = builder.CreateICmpULE(yArg, x);
auto r0 = x;
builder.CreateCondBr(yLEx, mainBB, returnBB);
builder.SetInsertPoint(mainBB);
auto ctlzIntr = llvm::Intrinsic::getDeclaration(&_module, llvm::Intrinsic::ctlz, _type);
// both y and r are non-zero
auto yLz = builder.CreateCall(ctlzIntr, {yArg, builder.getInt1(true)}, "y.lz");
auto rLz = builder.CreateCall(ctlzIntr, {r0, builder.getInt1(true)}, "r.lz");
auto i0 = builder.CreateNUWSub(yLz, rLz, "i0");
auto y0 = builder.CreateShl(yArg, i0);
builder.CreateBr(loopBB);
builder.SetInsertPoint(loopBB);
auto yPhi = builder.CreatePHI(_type, 2, "y.phi");
auto rPhi = builder.CreatePHI(_type, 2, "r.phi");
auto iPhi = builder.CreatePHI(_type, 2, "i.phi");
auto qPhi = builder.CreatePHI(_type, 2, "q.phi");
auto rUpdate = builder.CreateNUWSub(rPhi, yPhi);
auto qUpdate = builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0
auto rGEy = builder.CreateICmpUGE(rPhi, yPhi);
auto r1 = builder.CreateSelect(rGEy, rUpdate, rPhi, "r1");
auto q1 = builder.CreateSelect(rGEy, qUpdate, qPhi, "q");
auto iZero = builder.CreateICmpEQ(iPhi, zero);
builder.CreateCondBr(iZero, returnBB, continueBB);
builder.SetInsertPoint(continueBB);
auto i2 = builder.CreateNUWSub(iPhi, one);
auto q2 = builder.CreateShl(q1, one);
auto y2 = builder.CreateLShr(yPhi, one);
builder.CreateBr(loopBB);
yPhi->addIncoming(y0, mainBB);
yPhi->addIncoming(y2, continueBB);
rPhi->addIncoming(r0, mainBB);
rPhi->addIncoming(r1, continueBB);
iPhi->addIncoming(i0, mainBB);
iPhi->addIncoming(i2, continueBB);
qPhi->addIncoming(zero, mainBB);
qPhi->addIncoming(q2, continueBB);
builder.SetInsertPoint(returnBB);
auto qRet = builder.CreatePHI(_type, 2, "q.ret");
qRet->addIncoming(zero, entryBB);
qRet->addIncoming(q1, loopBB);
auto rRet = builder.CreatePHI(_type, 2, "r.ret");
rRet->addIncoming(r0, entryBB);
rRet->addIncoming(r1, loopBB);
auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), qRet, uint64_t(0), "ret0");
ret = builder.CreateInsertElement(ret, rRet, 1, "ret");
builder.CreateRet(ret);
return func;
}
}
llvm::Function* Arith256::getUDivRem256Func(llvm::Module& _module)
{
static const auto funcName = "evm.udivrem.i256";
if (auto func = _module.getFunction(funcName))
return func;
return createUDivRemFunc(Type::Word, _module, funcName);
}
llvm::Function* Arith256::getUDivRem512Func(llvm::Module& _module)
{
static const auto funcName = "evm.udivrem.i512";
if (auto func = _module.getFunction(funcName))
return func;
return createUDivRemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName);
}
llvm::Function* Arith256::getUDiv256Func(llvm::Module& _module)
{
static const auto funcName = "evm.udiv.i256";
if (auto func = _module.getFunction(funcName))
return func;
auto udivremFunc = getUDivRem256Func(_module);
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto udivrem = builder.CreateCall(udivremFunc, {x, y});
auto udiv = builder.CreateExtractElement(udivrem, uint64_t(0));
builder.CreateRet(udiv);
return func;
}
namespace
{
llvm::Function* createURemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName)
{
auto udivremFunc = _type == Type::Word ? Arith256::getUDivRem256Func(_module) : Arith256::getUDivRem512Func(_module);
auto func = llvm::Function::Create(llvm::FunctionType::get(_type, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto udivrem = builder.CreateCall(udivremFunc, {x, y});
auto r = builder.CreateExtractElement(udivrem, uint64_t(1));
builder.CreateRet(r);
return func;
}
}
llvm::Function* Arith256::getURem256Func(llvm::Module& _module)
{
static const auto funcName = "evm.urem.i256";
if (auto func = _module.getFunction(funcName))
return func;
return createURemFunc(Type::Word, _module, funcName);
}
llvm::Function* Arith256::getURem512Func(llvm::Module& _module)
{
static const auto funcName = "evm.urem.i512";
if (auto func = _module.getFunction(funcName))
return func;
return createURemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName);
}
llvm::Function* Arith256::getSDivRem256Func(llvm::Module& _module)
{
static const auto funcName = "evm.sdivrem.i256";
if (auto func = _module.getFunction(funcName))
return func;
auto udivremFunc = getUDivRem256Func(_module);
auto retType = llvm::VectorType::get(Type::Word, 2);
auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), "", func);
auto builder = llvm::IRBuilder<>{bb};
auto xIsNeg = builder.CreateICmpSLT(x, Constant::get(0));
auto xNeg = builder.CreateSub(Constant::get(0), x);
auto xAbs = builder.CreateSelect(xIsNeg, xNeg, x);
auto yIsNeg = builder.CreateICmpSLT(y, Constant::get(0));
auto yNeg = builder.CreateSub(Constant::get(0), y);
auto yAbs = builder.CreateSelect(yIsNeg, yNeg, y);
auto res = builder.CreateCall(udivremFunc, {xAbs, yAbs});
auto qAbs = builder.CreateExtractElement(res, uint64_t(0));
auto rAbs = builder.CreateExtractElement(res, 1);
// the remainder has the same sign as dividend
auto rNeg = builder.CreateSub(Constant::get(0), rAbs);
auto r = builder.CreateSelect(xIsNeg, rNeg, rAbs);
auto qNeg = builder.CreateSub(Constant::get(0), qAbs);
auto xyOpposite = builder.CreateXor(xIsNeg, yIsNeg);
auto q = builder.CreateSelect(xyOpposite, qNeg, qAbs);
auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), q, uint64_t(0));
ret = builder.CreateInsertElement(ret, r, 1);
builder.CreateRet(ret);
return func;
}
llvm::Function* Arith256::getSDiv256Func(llvm::Module& _module)
{
static const auto funcName = "evm.sdiv.i256";
if (auto func = _module.getFunction(funcName))
return func;
auto sdivremFunc = getSDivRem256Func(_module);
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto sdivrem = builder.CreateCall(sdivremFunc, {x, y});
auto q = builder.CreateExtractElement(sdivrem, uint64_t(0));
builder.CreateRet(q);
return func;
}
llvm::Function* Arith256::getSRem256Func(llvm::Module& _module)
{
static const auto funcName = "evm.srem.i256";
if (auto func = _module.getFunction(funcName))
return func;
auto sdivremFunc = getSDivRem256Func(_module);
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto sdivrem = builder.CreateCall(sdivremFunc, {x, y});
auto r = builder.CreateExtractElement(sdivrem, uint64_t(1));
builder.CreateRet(r);
return func;
}
llvm::Function* Arith256::getExpFunc()
{
if (!m_exp)
{
llvm::Type* argTypes[] = {Type::Word, Type::Word};
m_exp = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "exp", getModule());
m_exp->setDoesNotThrow();
m_exp->setDoesNotAccessMemory();
auto base = &m_exp->getArgumentList().front();
base->setName("base");
auto exponent = base->getNextNode();
exponent->setName("exponent");
InsertPointGuard guard{m_builder};
// while (e != 0) {
// if (e % 2 == 1)
// r *= b;
// b *= b;
// e /= 2;
// }
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", m_exp);
auto headerBB = llvm::BasicBlock::Create(m_builder.getContext(), "LoopHeader", m_exp);
auto bodyBB = llvm::BasicBlock::Create(m_builder.getContext(), "LoopBody", m_exp);
auto updateBB = llvm::BasicBlock::Create(m_builder.getContext(), "ResultUpdate", m_exp);
auto continueBB = llvm::BasicBlock::Create(m_builder.getContext(), "Continue", m_exp);
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", m_exp);
m_builder.SetInsertPoint(entryBB);
m_builder.CreateBr(headerBB);
m_builder.SetInsertPoint(headerBB);
auto r = m_builder.CreatePHI(Type::Word, 2, "r");
auto b = m_builder.CreatePHI(Type::Word, 2, "b");
auto e = m_builder.CreatePHI(Type::Word, 2, "e");
auto eNonZero = m_builder.CreateICmpNE(e, Constant::get(0), "e.nonzero");
m_builder.CreateCondBr(eNonZero, bodyBB, returnBB);
m_builder.SetInsertPoint(bodyBB);
auto eOdd = m_builder.CreateICmpNE(m_builder.CreateAnd(e, Constant::get(1)), Constant::get(0), "e.isodd");
m_builder.CreateCondBr(eOdd, updateBB, continueBB);
m_builder.SetInsertPoint(updateBB);
auto mul256Func = getMulFunc(*getModule());
auto r0 = createCall(mul256Func, {r, b});
m_builder.CreateBr(continueBB);
m_builder.SetInsertPoint(continueBB);
auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1");
r1->addIncoming(r, bodyBB);
r1->addIncoming(r0, updateBB);
auto b1 = createCall(mul256Func, {b, b});
auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1");
m_builder.CreateBr(headerBB);
r->addIncoming(Constant::get(1), entryBB);
r->addIncoming(r1, continueBB);
b->addIncoming(base, entryBB);
b->addIncoming(b1, continueBB);
e->addIncoming(exponent, entryBB);
e->addIncoming(e1, continueBB);
m_builder.SetInsertPoint(returnBB);
m_builder.CreateRet(r);
}
return m_exp;
}
llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2)
{
// while (e != 0) {
// if (e % 2 == 1)
// r *= b;
// b *= b;
// e /= 2;
// }
if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1))
{
if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2))
{
auto b = c1->getValue();
auto e = c2->getValue();
auto r = llvm::APInt{256, 1};
while (e != 0)
{
if (e[0])
r *= b;
b *= b;
e = e.lshr(1);
}
return Constant::get(r);
}
}
return createCall(getExpFunc(), {_arg1, _arg2});
}
}
}
}
extern "C"
{
EXPORT void debug(uint64_t a, uint64_t b, uint64_t c, uint64_t d, char z)
{
DLOG(JIT) << "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";
}
}

42
evmjit/libevmjit/Arith256.h

@ -1,42 +0,0 @@
#pragma once
#include "CompilerHelper.h"
namespace dev
{
namespace eth
{
namespace jit
{
class Arith256 : public CompilerHelper
{
public:
Arith256(llvm::IRBuilder<>& _builder);
llvm::Value* exp(llvm::Value* _arg1, llvm::Value* _arg2);
void debug(llvm::Value* _value, char _c);
static llvm::Function* getMulFunc(llvm::Module& _module);
static llvm::Function* getMul512Func(llvm::Module& _module);
static llvm::Function* getUDiv256Func(llvm::Module& _module);
static llvm::Function* getURem256Func(llvm::Module& _module);
static llvm::Function* getURem512Func(llvm::Module& _module);
static llvm::Function* getUDivRem256Func(llvm::Module& _module);
static llvm::Function* getSDiv256Func(llvm::Module& _module);
static llvm::Function* getSRem256Func(llvm::Module& _module);
static llvm::Function* getSDivRem256Func(llvm::Module& _module);
static llvm::Function* getUDivRem512Func(llvm::Module& _module);
private:
llvm::Function* getExpFunc();
llvm::Function* m_exp = nullptr;
llvm::Function* m_debug = nullptr;
};
}
}
}

270
evmjit/libevmjit/Array.cpp

@ -1,270 +0,0 @@
#include "Array.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include <llvm/IR/Function.h>
#include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h"
#include "Utils.h"
namespace dev
{
namespace eth
{
namespace jit
{
static const auto c_reallocStep = 1;
llvm::Value* LazyFunction::call(llvm::IRBuilder<>& _builder, std::initializer_list<llvm::Value*> const& _args, llvm::Twine const& _name)
{
if (!m_func)
m_func = m_creator();
return _builder.CreateCall(m_func, {_args.begin(), _args.size()}, _name);
}
llvm::Function* Array::createArrayPushFunc()
{
llvm::Type* argTypes[] = {m_array->getType(), Type::Word};
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.push", getModule());
func->setDoesNotThrow();
func->setDoesNotCapture(1);
auto arrayPtr = &func->getArgumentList().front();
arrayPtr->setName("arrayPtr");
auto value = arrayPtr->getNextNode();
value->setName("value");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", func);
auto reallocBB = llvm::BasicBlock::Create(m_builder.getContext(), "Realloc", func);
auto pushBB = llvm::BasicBlock::Create(m_builder.getContext(), "Push", func);
m_builder.SetInsertPoint(entryBB);
auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr");
auto sizePtr = m_builder.CreateStructGEP(getType(), arrayPtr, 1, "sizePtr");
auto capPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 2, "capPtr");
auto data = m_builder.CreateLoad(dataPtr, "data");
auto size = m_builder.CreateLoad(sizePtr, "size");
auto cap = m_builder.CreateLoad(capPtr, "cap");
auto reallocReq = m_builder.CreateICmpEQ(cap, size, "reallocReq");
m_builder.CreateCondBr(reallocReq, reallocBB, pushBB);
m_builder.SetInsertPoint(reallocBB);
auto newCap = m_builder.CreateNUWAdd(cap, m_builder.getInt64(c_reallocStep), "newCap");
auto reallocSize = m_builder.CreateShl(newCap, 5, "reallocSize"); // size in bytes: newCap * 32
auto bytes = m_builder.CreateBitCast(data, Type::BytePtr, "bytes");
auto newBytes = m_reallocFunc.call(m_builder, {bytes, reallocSize}, "newBytes");
auto newData = m_builder.CreateBitCast(newBytes, Type::WordPtr, "newData");
m_builder.CreateStore(newData, dataPtr);
m_builder.CreateStore(newCap, capPtr);
m_builder.CreateBr(pushBB);
m_builder.SetInsertPoint(pushBB);
auto dataPhi = m_builder.CreatePHI(Type::WordPtr, 2, "dataPhi");
dataPhi->addIncoming(data, entryBB);
dataPhi->addIncoming(newData, reallocBB);
auto newElemPtr = m_builder.CreateGEP(dataPhi, size, "newElemPtr");
m_builder.CreateStore(value, newElemPtr);
auto newSize = m_builder.CreateNUWAdd(size, m_builder.getInt64(1), "newSize");
m_builder.CreateStore(newSize, sizePtr);
m_builder.CreateRetVoid();
return func;
}
llvm::Function* Array::createArraySetFunc()
{
llvm::Type* argTypes[] = {m_array->getType(), Type::Size, Type::Word};
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.set", getModule());
func->setDoesNotThrow();
func->setDoesNotCapture(1);
auto arrayPtr = &func->getArgumentList().front();
arrayPtr->setName("arrayPtr");
auto index = arrayPtr->getNextNode();
index->setName("index");
auto value = index->getNextNode();
value->setName("value");
InsertPointGuard guard{m_builder};
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func));
auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr");
auto data = m_builder.CreateLoad(dataPtr, "data");
auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr");
m_builder.CreateStore(value, valuePtr);
m_builder.CreateRetVoid();
return func;
}
llvm::Function* Array::createArrayGetFunc()
{
llvm::Type* argTypes[] = {m_array->getType(), Type::Size};
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "array.get", getModule());
func->setDoesNotThrow();
func->setDoesNotCapture(1);
auto arrayPtr = &func->getArgumentList().front();
arrayPtr->setName("arrayPtr");
auto index = arrayPtr->getNextNode();
index->setName("index");
InsertPointGuard guard{m_builder};
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func));
auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr");
auto data = m_builder.CreateLoad(dataPtr, "data");
auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr");
auto value = m_builder.CreateLoad(valuePtr, "value");
m_builder.CreateRet(value);
return func;
}
llvm::Function* Array::createGetPtrFunc()
{
llvm::Type* argTypes[] = {m_array->getType(), Type::Size};
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, argTypes, false), llvm::Function::PrivateLinkage, "array.getPtr", getModule());
func->setDoesNotThrow();
func->setDoesNotCapture(1);
auto arrayPtr = &func->getArgumentList().front();
arrayPtr->setName("arrayPtr");
auto index = arrayPtr->getNextNode();
index->setName("index");
InsertPointGuard guard{m_builder};
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func));
auto dataPtr = m_builder.CreateBitCast(arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr");
auto data = m_builder.CreateLoad(dataPtr, "data");
auto bytePtr = m_builder.CreateGEP(data, index, "bytePtr");
auto wordPtr = m_builder.CreateBitCast(bytePtr, Type::WordPtr, "wordPtr");
m_builder.CreateRet(wordPtr);
return func;
}
llvm::Function* Array::createFreeFunc()
{
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, m_array->getType(), false), llvm::Function::PrivateLinkage, "array.free", getModule());
func->setDoesNotThrow();
func->setDoesNotCapture(1);
auto freeFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, Type::BytePtr, false), llvm::Function::ExternalLinkage, "ext_free", getModule());
freeFunc->setDoesNotThrow();
freeFunc->setDoesNotCapture(1);
auto arrayPtr = &func->getArgumentList().front();
arrayPtr->setName("arrayPtr");
InsertPointGuard guard{m_builder};
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func));
auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr");
auto data = m_builder.CreateLoad(dataPtr, "data");
auto mem = m_builder.CreateBitCast(data, Type::BytePtr, "mem");
m_builder.CreateCall(freeFunc, mem);
m_builder.CreateRetVoid();
return func;
}
llvm::Function* Array::getReallocFunc()
{
if (auto func = getModule()->getFunction("ext_realloc"))
return func;
llvm::Type* reallocArgTypes[] = {Type::BytePtr, Type::Size};
auto reallocFunc = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, reallocArgTypes, false), llvm::Function::ExternalLinkage, "ext_realloc", getModule());
reallocFunc->setDoesNotThrow();
reallocFunc->setDoesNotAlias(0);
reallocFunc->setDoesNotCapture(1);
return reallocFunc;
}
llvm::Function* Array::createExtendFunc()
{
llvm::Type* argTypes[] = {m_array->getType(), Type::Size};
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.extend", getModule());
func->setDoesNotThrow();
func->setDoesNotCapture(1);
auto arrayPtr = &func->getArgumentList().front();
arrayPtr->setName("arrayPtr");
auto newSize = arrayPtr->getNextNode();
newSize->setName("newSize");
InsertPointGuard guard{m_builder};
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func));
auto dataPtr = m_builder.CreateBitCast(arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr");// TODO: Use byte* in Array
auto sizePtr = m_builder.CreateStructGEP(getType(), arrayPtr, 1, "sizePtr");
auto capPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 2, "capPtr");
auto data = m_builder.CreateLoad(dataPtr, "data");
auto size = m_builder.CreateLoad(sizePtr, "size");
auto extSize = m_builder.CreateNUWSub(newSize, size, "extSize");
auto newData = m_reallocFunc.call(m_builder, {data, newSize}, "newData"); // TODO: Check realloc result for null
auto extPtr = m_builder.CreateGEP(newData, size, "extPtr");
m_builder.CreateMemSet(extPtr, m_builder.getInt8(0), extSize, 16);
m_builder.CreateStore(newData, dataPtr);
m_builder.CreateStore(newSize, sizePtr);
m_builder.CreateStore(newSize, capPtr);
m_builder.CreateRetVoid();
return func;
}
llvm::Type* Array::getType()
{
llvm::Type* elementTys[] = {Type::WordPtr, Type::Size, Type::Size};
static auto arrayTy = llvm::StructType::create(elementTys, "Array");
return arrayTy;
}
Array::Array(llvm::IRBuilder<>& _builder, char const* _name) :
CompilerHelper(_builder)
{
m_array = m_builder.CreateAlloca(getType(), nullptr, _name);
m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array);
}
Array::Array(llvm::IRBuilder<>& _builder, llvm::Value* _array) :
CompilerHelper(_builder),
m_array(_array)
{
m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array);
}
void Array::pop(llvm::Value* _count)
{
auto sizePtr = m_builder.CreateStructGEP(getType(), m_array, 1, "sizePtr");
auto size = m_builder.CreateLoad(sizePtr, "size");
auto newSize = m_builder.CreateNUWSub(size, _count, "newSize");
m_builder.CreateStore(newSize, sizePtr);
}
llvm::Value* Array::size(llvm::Value* _array)
{
auto sizePtr = m_builder.CreateStructGEP(getType(), _array ? _array : m_array, 1, "sizePtr");
return m_builder.CreateLoad(sizePtr, "array.size");
}
void Array::extend(llvm::Value* _arrayPtr, llvm::Value* _size)
{
assert(_arrayPtr->getType() == m_array->getType());
assert(_size->getType() == Type::Size);
m_extendFunc.call(m_builder, {_arrayPtr, _size});
}
}
}
}
extern "C"
{
EXPORT void* ext_realloc(void* _data, size_t _size) noexcept
{
return std::realloc(_data, _size);
}
EXPORT void ext_free(void* _data) noexcept
{
std::free(_data);
}
}

72
evmjit/libevmjit/Array.h

@ -1,72 +0,0 @@
#pragma once
#include <functional>
#include "CompilerHelper.h"
namespace dev
{
namespace eth
{
namespace jit
{
class LazyFunction
{
public:
using Creator = std::function<llvm::Function*()>;
LazyFunction(Creator _creator) :
m_creator(_creator)
{}
llvm::Value* call(llvm::IRBuilder<>& _builder, std::initializer_list<llvm::Value*> const& _args, llvm::Twine const& _name = "");
private:
llvm::Function* m_func = nullptr;
Creator m_creator;
};
class Array : public CompilerHelper
{
public:
Array(llvm::IRBuilder<>& _builder, char const* _name);
Array(llvm::IRBuilder<>& _builder, llvm::Value* _array);
void push(llvm::Value* _value) { m_pushFunc.call(m_builder, {m_array, _value}); }
void set(llvm::Value* _index, llvm::Value* _value) { m_setFunc.call(m_builder, {m_array, _index, _value}); }
llvm::Value* get(llvm::Value* _index) { return m_getFunc.call(m_builder, {m_array, _index}); }
void pop(llvm::Value* _count);
llvm::Value* size(llvm::Value* _array = nullptr);
void free() { m_freeFunc.call(m_builder, {m_array}); }
void extend(llvm::Value* _arrayPtr, llvm::Value* _size);
llvm::Value* getPtr(llvm::Value* _arrayPtr, llvm::Value* _index) { return m_getPtrFunc.call(m_builder, {_arrayPtr, _index}); }
llvm::Value* getPointerTo() const { return m_array; }
static llvm::Type* getType();
private:
llvm::Value* m_array = nullptr;
llvm::Function* createArrayPushFunc();
llvm::Function* createArraySetFunc();
llvm::Function* createArrayGetFunc();
llvm::Function* createGetPtrFunc();
llvm::Function* createFreeFunc();
llvm::Function* createExtendFunc();
llvm::Function* getReallocFunc();
LazyFunction m_pushFunc = {[this](){ return createArrayPushFunc(); }};
LazyFunction m_setFunc = {[this](){ return createArraySetFunc(); }};
LazyFunction m_getPtrFunc = {[this](){ return createGetPtrFunc(); }};
LazyFunction m_getFunc = {[this](){ return createArrayGetFunc(); }};
LazyFunction m_freeFunc = {[this](){ return createFreeFunc(); }};
LazyFunction m_extendFunc = {[this](){ return createExtendFunc(); }};
LazyFunction m_reallocFunc = {[this](){ return getReallocFunc(); }};
};
}
}
}

148
evmjit/libevmjit/BasicBlock.cpp

@ -1,148 +0,0 @@
#include "BasicBlock.h"
#include <iostream>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/CFG.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/Support/raw_os_ostream.h>
#include "preprocessor/llvm_includes_end.h"
#include "Type.h"
#include "Utils.h"
namespace dev
{
namespace eth
{
namespace jit
{
BasicBlock::BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc):
m_firstInstrIdx{_firstInstrIdx},
m_begin(_begin),
m_end(_end),
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {"Instr.", std::to_string(_firstInstrIdx)}, _mainFunc))
{}
LocalStack::LocalStack(Stack& _globalStack):
m_global(_globalStack)
{}
void LocalStack::push(llvm::Value* _value)
{
assert(_value->getType() == Type::Word);
m_local.push_back(_value);
m_maxSize = std::max(m_maxSize, size());
}
llvm::Value* LocalStack::pop()
{
auto item = get(0);
assert(!m_local.empty() || !m_input.empty());
if (m_local.size() > 0)
m_local.pop_back();
else
++m_globalPops;
m_minSize = std::min(m_minSize, size());
return item;
}
/**
* Pushes a copy of _index-th element (tos is 0-th elem).
*/
void LocalStack::dup(size_t _index)
{
auto val = get(_index);
push(val);
}
/**
* Swaps tos with _index-th element (tos is 0-th elem).
* _index must be > 0.
*/
void LocalStack::swap(size_t _index)
{
assert(_index > 0);
auto val = get(_index);
auto tos = get(0);
set(_index, tos);
set(0, val);
}
llvm::Value* LocalStack::get(size_t _index)
{
if (_index < m_local.size())
return *(m_local.rbegin() + _index); // count from back
auto idx = _index - m_local.size() + m_globalPops;
if (idx >= m_input.size())
m_input.resize(idx + 1);
auto& item = m_input[idx];
if (!item)
{
item = m_global.get(idx); // Reach an item from global stack
m_minSize = std::min(m_minSize, -static_cast<ssize_t>(idx) - 1); // and remember required stack size
}
return item;
}
void LocalStack::set(size_t _index, llvm::Value* _word)
{
if (_index < m_local.size())
{
*(m_local.rbegin() + _index) = _word;
return;
}
auto idx = _index - m_local.size() + m_globalPops;
assert(idx < m_input.size());
m_input[idx] = _word;
}
void LocalStack::finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb)
{
auto blockTerminator = _bb.getTerminator();
if (!blockTerminator || blockTerminator->getOpcode() != llvm::Instruction::Ret)
{
// Not needed in case of ret instruction. Ret invalidates the stack.
if (blockTerminator)
_builder.SetInsertPoint(blockTerminator);
else
_builder.SetInsertPoint(&_bb);
// Update items fetched from global stack ignoring the poped ones
assert(m_globalPops <= m_input.size()); // pop() always does get()
for (auto i = m_globalPops; i < m_input.size(); ++i)
{
if (m_input[i])
m_global.set(i, m_input[i]);
}
// Add new items
auto pops = m_globalPops; // Copy pops counter to keep original value
for (auto& item: m_local)
{
if (pops) // Override poped global items
m_global.set(--pops, item); // using pops counter as the index
else
m_global.push(item);
}
// Pop not overriden items
if (pops)
m_global.pop(pops);
}
}
}
}
}

86
evmjit/libevmjit/BasicBlock.h

@ -1,86 +0,0 @@
#pragma once
#include <vector>
#include "Common.h"
#include "Stack.h"
namespace dev
{
namespace eth
{
namespace jit
{
using namespace evmjit;
using instr_idx = uint64_t;
class LocalStack
{
public:
explicit LocalStack(Stack& _globalStack);
LocalStack(LocalStack const&) = delete;
void operator=(LocalStack const&) = delete;
/// Pushes value on stack
void push(llvm::Value* _value);
/// Pops and returns top value
llvm::Value* pop();
/// Duplicates _index'th value on stack
void dup(size_t _index);
/// Swaps _index'th value on stack with a value on stack top.
/// @param _index Index of value to be swaped. Must be > 0.
void swap(size_t _index);
ssize_t size() const { return static_cast<ssize_t>(m_local.size()) - static_cast<ssize_t>(m_globalPops); }
ssize_t minSize() const { return m_minSize; }
ssize_t maxSize() const { return m_maxSize; }
/// Finalize local stack: check the requirements and update of the global stack.
void finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb);
private:
/// Gets _index'th value from top (counting from 0)
llvm::Value* get(size_t _index);
/// Sets _index'th value from top (counting from 0)
void set(size_t _index, llvm::Value* _value);
/// Items fetched from global stack. First element matches the top of the global stack.
/// Can contain nulls if some items has been skipped.
std::vector<llvm::Value*> m_input;
/// Local stack items that has not been pushed to global stack. First item is just above global stack.
std::vector<llvm::Value*> m_local;
Stack& m_global; ///< Reference to global stack.
size_t m_globalPops = 0; ///< Number of items poped from global stack. In other words: global - local stack overlap.
ssize_t m_minSize = 0; ///< Minimum reached local stack size. Can be negative.
ssize_t m_maxSize = 0; ///< Maximum reached local stack size.
};
class BasicBlock
{
public:
explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc);
llvm::BasicBlock* llvm() { return m_llvmBB; }
instr_idx firstInstrIdx() const { return m_firstInstrIdx; }
code_iterator begin() const { return m_begin; }
code_iterator end() const { return m_end; }
private:
instr_idx const m_firstInstrIdx = 0; ///< Code index of first instruction in the block
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; ///< Reference to the LLVM BasicBlock
};
}
}
}

9
evmjit/libevmjit/BuildInfo.h.in

@ -1,9 +0,0 @@
#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 LLVM_VERSION "${LLVM_PACKAGE_VERSION}"
#define LLVM_ASSERTIONS "${LLVM_ENABLE_ASSERTIONS}"
#define LLVM_DEBUG ${LLVM_DEBUG}

58
evmjit/libevmjit/CMakeLists.txt

@ -1,58 +0,0 @@
set(TARGET_NAME evmjit)
get_filename_component(EVMJIT_INCLUDE_DIR ../include ABSOLUTE)
set(SOURCES
JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h
JIT-c.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT-c.h
Arith256.cpp Arith256.h
Array.cpp Array.h
BasicBlock.cpp BasicBlock.h
Cache.cpp Cache.h
Common.h
Compiler.cpp Compiler.h
CompilerHelper.cpp CompilerHelper.h
Endianness.cpp Endianness.h
ExecStats.cpp ExecStats.h
Ext.cpp Ext.h
GasMeter.cpp GasMeter.h
Instruction.cpp Instruction.h
Memory.cpp Memory.h
Optimizer.cpp Optimizer.h
RuntimeManager.cpp RuntimeManager.h
Stack.cpp Stack.h
Type.cpp Type.h
Utils.cpp Utils.h
)
source_group("" FILES ${SOURCES})
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
endif()
if(${EVMJIT_VERSION_MAJOR} EQUAL 0)
set(EVMJIT_SOVERSION "0.${EVMJIT_VERSION_MINOR}")
else()
set(EVMJIT_SOVERSION ${EVMJIT_VERSION_MAJOR})
endif()
string(COMPARE EQUAL "${LLVM_ENABLE_ASSERTIONS}" "ON" LLVM_DEBUG)
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}")
add_library(${TARGET_NAME} SHARED ${SOURCES} gen/BuildInfo.gen.h)
set_target_properties(${TARGET_NAME} PROPERTIES
VERSION ${EVMJIT_VERSION} SOVERSION ${EVMJIT_SOVERSION}
FOLDER "libs")
target_include_directories(${TARGET_NAME} PUBLIC ${EVMJIT_INCLUDE_DIR})
target_include_directories(${TARGET_NAME} PRIVATE ${LLVM_INCLUDE_DIRS})
target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/gen)
target_compile_definitions(${TARGET_NAME} PRIVATE ${LLVM_DEFINITIONS})
target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS})
install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin)
install(DIRECTORY ${EVMJIT_INCLUDE_DIR} DESTINATION include)

190
evmjit/libevmjit/Cache.cpp

@ -1,190 +0,0 @@
#include "Cache.h"
#include <mutex>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Instructions.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/Support/Path.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_os_ostream.h>
#include "preprocessor/llvm_includes_end.h"
#include "ExecStats.h"
#include "Utils.h"
#include "BuildInfo.gen.h"
namespace dev
{
namespace evmjit
{
namespace
{
using Guard = std::lock_guard<std::mutex>;
std::mutex x_cacheMutex;
CacheMode g_mode;
std::unique_ptr<llvm::MemoryBuffer> g_lastObject;
JITListener* g_listener;
llvm::StringRef getLibVersionStamp()
{
return EVMJIT_VERSION;
}
}
ObjectCache* Cache::init(CacheMode _mode, JITListener* _listener)
{
Guard g{x_cacheMutex};
g_mode = _mode;
g_listener = _listener;
if (g_mode == CacheMode::clear)
{
Cache::clear();
g_mode = CacheMode::off;
}
if (g_mode != CacheMode::off)
{
static ObjectCache objectCache;
return &objectCache;
}
return nullptr;
}
void Cache::clear()
{
Guard g{x_cacheMutex};
using namespace llvm::sys;
llvm::SmallString<256> cachePath;
path::system_temp_directory(false, cachePath);
path::append(cachePath, "evm_objs");
std::error_code err;
for (auto it = fs::directory_iterator{cachePath.str(), err}; it != fs::directory_iterator{}; it.increment(err))
fs::remove(it->path());
}
void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map<std::string, uint64_t>& _funcCache)
{
Guard g{x_cacheMutex};
// TODO: Cache dir should be in one place
using namespace llvm::sys;
llvm::SmallString<256> cachePath;
path::system_temp_directory(false, cachePath);
path::append(cachePath, "evm_objs");
// Disable listener
auto listener = g_listener;
g_listener = nullptr;
std::error_code err;
for (auto it = fs::directory_iterator{cachePath.str(), err}; it != fs::directory_iterator{}; it.increment(err))
{
auto name = it->path().substr(cachePath.size() + 1);
if (auto module = getObject(name))
{
DLOG(cache) << "Preload: " << name << "\n";
_ee.addModule(std::move(module));
auto addr = _ee.getFunctionAddress(name);
assert(addr);
_funcCache[std::move(name)] = addr;
}
}
g_listener = listener;
}
std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
{
Guard g{x_cacheMutex};
if (g_mode != CacheMode::on && g_mode != CacheMode::read)
return nullptr;
// TODO: Disabled because is not thread-safe.
//if (g_listener)
// g_listener->stateChanged(ExecState::CacheLoad);
DLOG(cache) << id << ": search\n";
if (!CHECK(!g_lastObject))
g_lastObject = nullptr;
llvm::SmallString<256> cachePath;
llvm::sys::path::system_temp_directory(false, cachePath);
llvm::sys::path::append(cachePath, "evm_objs", id);
if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false))
{
auto& buf = r.get();
auto expectedStamp = getLibVersionStamp();
auto stampSize = expectedStamp.size();
auto objStamp = buf->getBufferSize() >= stampSize ? llvm::StringRef{buf->getBufferEnd() - stampSize, stampSize} : llvm::StringRef{};
if (objStamp == expectedStamp)
g_lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer());
else
DLOG(cache) << "Unmatched version: " << objStamp.str() << ", expected " << expectedStamp.str() << "\n";
}
else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory))
DLOG(cache) << r.getError().message(); // TODO: Add warning log
if (g_lastObject) // if object found create fake module
{
DLOG(cache) << id << ": found\n";
auto&& context = llvm::getGlobalContext();
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;
}
DLOG(cache) << id << ": not found\n";
return nullptr;
}
void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBufferRef _object)
{
Guard g{x_cacheMutex};
// Only in "on" and "write" mode
if (g_mode != CacheMode::on && g_mode != CacheMode::write)
return;
// TODO: Disabled because is not thread-safe.
// if (g_listener)
// g_listener->stateChanged(ExecState::CacheWrite);
auto&& id = _module->getModuleIdentifier();
llvm::SmallString<256> cachePath;
llvm::sys::path::system_temp_directory(false, cachePath);
llvm::sys::path::append(cachePath, "evm_objs");
if (llvm::sys::fs::create_directory(cachePath.str()))
DLOG(cache) << "Cannot create cache dir " << cachePath.str().str() << "\n";
llvm::sys::path::append(cachePath, id);
DLOG(cache) << id << ": write\n";
std::error_code error;
llvm::raw_fd_ostream cacheFile(cachePath.c_str(), error, llvm::sys::fs::F_None);
cacheFile << _object.getBuffer() << getLibVersionStamp();
}
std::unique_ptr<llvm::MemoryBuffer> ObjectCache::getObject(llvm::Module const* _module)
{
Guard g{x_cacheMutex};
DLOG(cache) << _module->getModuleIdentifier() << ": use\n";
return std::move(g_lastObject);
}
}
}

59
evmjit/libevmjit/Cache.h

@ -1,59 +0,0 @@
#pragma once
#include <memory>
#include <unordered_map>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/ExecutionEngine/ObjectCache.h>
#include "preprocessor/llvm_includes_end.h"
namespace llvm
{
class ExecutionEngine;
}
namespace dev
{
namespace evmjit
{
class JITListener;
enum class CacheMode
{
on,
off,
read,
write,
clear,
preload
};
class ObjectCache : public llvm::ObjectCache
{
public:
/// notifyObjectCompiled - Provides a pointer to compiled code for Module M.
virtual void notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBufferRef _object) final override;
/// getObjectCopy - Returns a pointer to a newly allocated MemoryBuffer that
/// contains the object which corresponds with Module M, or 0 if an object is
/// not available. The caller owns both the MemoryBuffer returned by this
/// and the memory it references.
virtual std::unique_ptr<llvm::MemoryBuffer> getObject(llvm::Module const* _module) final override;
};
class Cache
{
public:
static ObjectCache* init(CacheMode _mode, JITListener* _listener);
static std::unique_ptr<llvm::Module> getObject(std::string const& id);
/// Clears cache storage
static void clear();
/// Loads all available cached objects to ExecutionEngine
static void preload(llvm::ExecutionEngine& _ee, std::unordered_map<std::string, uint64_t>& _funcCache);
};
}
}

16
evmjit/libevmjit/Common.h

@ -1,16 +0,0 @@
#pragma once
#include <cstdint>
namespace dev
{
namespace evmjit
{
using byte = uint8_t;
using code_iterator = byte const*;
#define UNTESTED assert(false)
}
}

800
evmjit/libevmjit/Compiler.cpp

@ -1,800 +0,0 @@
#include "Compiler.h"
#include <functional>
#include <fstream>
#include <chrono>
#include <sstream>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/CFG.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "Instruction.h"
#include "Type.h"
#include "Memory.h"
#include "Stack.h"
#include "Ext.h"
#include "GasMeter.h"
#include "Utils.h"
#include "Endianness.h"
#include "Arith256.h"
#include "RuntimeManager.h"
namespace dev
{
namespace eth
{
namespace jit
{
static const auto c_destIdxLabel = "destIdx";
Compiler::Compiler(Options const& _options):
m_options(_options),
m_builder(llvm::getGlobalContext())
{
Type::init(m_builder.getContext());
}
std::vector<BasicBlock> Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEnd, llvm::SwitchInst& _jumpTable)
{
/// Helper function that skips push data and finds next iterator (can be the end)
auto skipPushDataAndGetNext = [](code_iterator _curr, code_iterator _end)
{
static const auto push1 = static_cast<size_t>(Instruction::PUSH1);
static const auto push32 = static_cast<size_t>(Instruction::PUSH32);
size_t offset = 1;
if (*_curr >= push1 && *_curr <= push32)
offset += std::min<size_t>(*_curr - push1 + 1, (_end - _curr) - 1);
return _curr + offset;
};
// Skip all STOPs in the end
for (; _codeEnd != _codeBegin; --_codeEnd)
if (*(_codeEnd - 1) != static_cast<byte>(Instruction::STOP))
break;
std::vector<BasicBlock> blocks;
auto begin = _codeBegin; // begin of current block
for (auto curr = begin, next = begin; curr != _codeEnd; curr = next)
{
next = skipPushDataAndGetNext(curr, _codeEnd);
bool isEnd = false;
switch (Instruction(*curr))
{
case Instruction::JUMP:
case Instruction::JUMPI:
case Instruction::RETURN:
case Instruction::STOP:
case Instruction::SUICIDE:
isEnd = true;
break;
default:
break;
}
assert(next <= _codeEnd);
if (next == _codeEnd || Instruction(*next) == Instruction::JUMPDEST)
isEnd = true;
if (isEnd)
{
auto beginIdx = begin - _codeBegin;
blocks.emplace_back(beginIdx, begin, next, m_mainFunc);
if (Instruction(*begin) == Instruction::JUMPDEST)
_jumpTable.addCase(Constant::get(beginIdx), blocks.back().llvm());
begin = next;
}
}
return blocks;
}
void Compiler::resolveJumps()
{
// Iterate through all EVM instructions blocks (skip first 4 - special blocks).
for (auto it = std::next(m_mainFunc->begin(), 4); it != m_mainFunc->end(); ++it)
{
auto jumpTable = llvm::cast<llvm::SwitchInst>(m_jumpTableBB->getTerminator());
auto jumpTableInput = llvm::cast<llvm::PHINode>(m_jumpTableBB->begin());
auto nextBlock = it->getNextNode() != m_mainFunc->end() ? it->getNextNode() : m_stopBB;
auto term = it->getTerminator();
if (!term) // Block may have no terminator if the next instruction is a jump destination.
llvm::IRBuilder<>{it}.CreateBr(nextBlock);
else if (auto jump = llvm::dyn_cast<llvm::BranchInst>(term))
if (jump->getSuccessor(0) == m_jumpTableBB)
{
auto destIdx = llvm::cast<llvm::ValueAsMetadata>(jump->getMetadata(c_destIdxLabel)->getOperand(0))->getValue();
if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(destIdx))
{
// If destination index is a constant do direct jump to the destination block.
auto bb = jumpTable->findCaseValue(constant).getCaseSuccessor();
jump->setSuccessor(0, bb);
}
else
jumpTableInput->addIncoming(destIdx, it); // Fill up PHI node
if (jump->isConditional())
jump->setSuccessor(1, nextBlock); // Set next block for conditional jumps
}
}
}
std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_iterator _end, std::string const& _id)
{
auto module = std::unique_ptr<llvm::Module>(new llvm::Module(_id, m_builder.getContext()));
// Create main function
auto mainFuncType = llvm::FunctionType::get(Type::MainReturn, Type::RuntimePtr, false);
m_mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, _id, module.get());
m_mainFunc->getArgumentList().front().setName("rt");
// Create entry basic block
auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mainFunc);
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
m_abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc);
m_jumpTableBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "JumpTable", m_mainFunc);
m_builder.SetInsertPoint(m_jumpTableBB);
auto target = m_builder.CreatePHI(Type::Word, 16, "target");
auto& jumpTable = *m_builder.CreateSwitch(target, m_abortBB);
m_builder.SetInsertPoint(entryBlock);
auto blocks = createBasicBlocks(_begin, _end, jumpTable);
// Init runtime structures.
RuntimeManager runtimeManager(m_builder, _begin, _end);
GasMeter gasMeter(m_builder, runtimeManager);
Memory memory(runtimeManager, gasMeter);
Ext ext(runtimeManager, memory);
Stack stack(m_builder);
runtimeManager.setStack(stack); // Runtime Manager will free stack memory
Arith256 arith(m_builder);
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));
runtimeManager.setJmpBuf(jmpBuf);
auto firstBB = blocks.empty() ? m_stopBB : blocks.front().llvm();
m_builder.CreateCondBr(normalFlow, firstBB, m_abortBB, Type::expectTrue);
for (auto& block: blocks)
compileBasicBlock(block, runtimeManager, arith, memory, ext, gasMeter, stack);
// Code for special blocks:
m_builder.SetInsertPoint(m_stopBB);
runtimeManager.exit(ReturnCode::Stop);
m_builder.SetInsertPoint(m_abortBB);
runtimeManager.exit(ReturnCode::OutOfGas);
resolveJumps();
return module;
}
void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runtimeManager,
Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter, Stack& _globalStack)
{
m_builder.SetInsertPoint(_basicBlock.llvm());
LocalStack stack{_globalStack};
for (auto it = _basicBlock.begin(); it != _basicBlock.end(); ++it)
{
auto inst = Instruction(*it);
_gasMeter.count(inst);
switch (inst)
{
case Instruction::ADD:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto result = m_builder.CreateAdd(lhs, rhs);
stack.push(result);
break;
}
case Instruction::SUB:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto result = m_builder.CreateSub(lhs, rhs);
stack.push(result);
break;
}
case Instruction::MUL:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = m_builder.CreateMul(lhs, rhs);
stack.push(res);
break;
}
case Instruction::DIV:
{
auto d = stack.pop();
auto n = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
auto r = m_builder.CreateUDiv(d, n);
r = m_builder.CreateSelect(divByZero, Constant::get(0), r);
stack.push(r);
break;
}
case Instruction::SDIV:
{
auto d = stack.pop();
auto n = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1));
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
auto r = m_builder.CreateSDiv(d, n);
r = m_builder.CreateSelect(divByZero, Constant::get(0), r);
auto dNeg = m_builder.CreateSub(Constant::get(0), d);
r = m_builder.CreateSelect(divByMinusOne, dNeg, r); // protect against undef i256.min / -1
stack.push(r);
break;
}
case Instruction::MOD:
{
auto d = stack.pop();
auto n = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
auto r = m_builder.CreateURem(d, n);
r = m_builder.CreateSelect(divByZero, Constant::get(0), r);
stack.push(r);
break;
}
case Instruction::SMOD:
{
auto d = stack.pop();
auto n = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1));
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
auto r = m_builder.CreateSRem(d, n);
r = m_builder.CreateSelect(divByZero, Constant::get(0), r);
r = m_builder.CreateSelect(divByMinusOne, Constant::get(0), r); // protect against undef i256.min / -1
stack.push(r);
break;
}
case Instruction::ADDMOD:
{
auto i512Ty = m_builder.getIntNTy(512);
auto a = stack.pop();
auto b = stack.pop();
auto m = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(m, Constant::get(0));
a = m_builder.CreateZExt(a, i512Ty);
b = m_builder.CreateZExt(b, i512Ty);
m = m_builder.CreateZExt(m, i512Ty);
auto s = m_builder.CreateNUWAdd(a, b);
s = m_builder.CreateURem(s, m);
s = m_builder.CreateTrunc(s, Type::Word);
s = m_builder.CreateSelect(divByZero, Constant::get(0), s);
stack.push(s);
break;
}
case Instruction::MULMOD:
{
auto i512Ty = m_builder.getIntNTy(512);
auto a = stack.pop();
auto b = stack.pop();
auto m = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(m, Constant::get(0));
m = m_builder.CreateZExt(m, i512Ty);
// TODO: Add support for i256 x i256 -> i512 in LowerEVM pass
llvm::Value* p = m_builder.CreateCall(Arith256::getMul512Func(*_basicBlock.llvm()->getParent()->getParent()), {a, b});
p = m_builder.CreateURem(p, m);
p = m_builder.CreateTrunc(p, Type::Word);
p = m_builder.CreateSelect(divByZero, Constant::get(0), p);
stack.push(p);
break;
}
case Instruction::EXP:
{
auto base = stack.pop();
auto exponent = stack.pop();
_gasMeter.countExp(exponent);
auto ret = _arith.exp(base, exponent);
stack.push(ret);
break;
}
case Instruction::NOT:
{
auto value = stack.pop();
auto ret = m_builder.CreateXor(value, Constant::get(-1), "bnot");
stack.push(ret);
break;
}
case Instruction::LT:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res1 = m_builder.CreateICmpULT(lhs, rhs);
auto res256 = m_builder.CreateZExt(res1, Type::Word);
stack.push(res256);
break;
}
case Instruction::GT:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res1 = m_builder.CreateICmpUGT(lhs, rhs);
auto res256 = m_builder.CreateZExt(res1, Type::Word);
stack.push(res256);
break;
}
case Instruction::SLT:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res1 = m_builder.CreateICmpSLT(lhs, rhs);
auto res256 = m_builder.CreateZExt(res1, Type::Word);
stack.push(res256);
break;
}
case Instruction::SGT:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res1 = m_builder.CreateICmpSGT(lhs, rhs);
auto res256 = m_builder.CreateZExt(res1, Type::Word);
stack.push(res256);
break;
}
case Instruction::EQ:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res1 = m_builder.CreateICmpEQ(lhs, rhs);
auto res256 = m_builder.CreateZExt(res1, Type::Word);
stack.push(res256);
break;
}
case Instruction::ISZERO:
{
auto top = stack.pop();
auto iszero = m_builder.CreateICmpEQ(top, Constant::get(0), "iszero");
auto result = m_builder.CreateZExt(iszero, Type::Word);
stack.push(result);
break;
}
case Instruction::AND:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = m_builder.CreateAnd(lhs, rhs);
stack.push(res);
break;
}
case Instruction::OR:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = m_builder.CreateOr(lhs, rhs);
stack.push(res);
break;
}
case Instruction::XOR:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = m_builder.CreateXor(lhs, rhs);
stack.push(res);
break;
}
case Instruction::BYTE:
{
const auto idx = stack.pop();
auto value = Endianness::toBE(m_builder, stack.pop());
auto idxValid = m_builder.CreateICmpULT(idx, Constant::get(32), "idxValid");
auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes");
// TODO: Workaround for LLVM bug. Using big value of index causes invalid memory access.
auto safeIdx = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5));
// TODO: Workaround for LLVM bug. DAG Builder used sext on index instead of zext
safeIdx = m_builder.CreateZExt(safeIdx, Type::Size);
auto byte = m_builder.CreateExtractElement(bytes, safeIdx, "byte");
value = m_builder.CreateZExt(byte, Type::Word);
value = m_builder.CreateSelect(idxValid, value, Constant::get(0));
stack.push(value);
break;
}
case Instruction::SIGNEXTEND:
{
auto idx = stack.pop();
auto word = stack.pop();
auto k32_ = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5), "k_32");
auto k32 = m_builder.CreateZExt(k32_, Type::Size);
auto k32x8 = m_builder.CreateMul(k32, m_builder.getInt64(8), "kx8");
// test for word >> (k * 8 + 7)
auto bitpos = m_builder.CreateAdd(k32x8, m_builder.getInt64(7), "bitpos");
auto bitposEx = m_builder.CreateZExt(bitpos, Type::Word);
auto bitval = m_builder.CreateLShr(word, bitposEx, "bitval");
auto bittest = m_builder.CreateTrunc(bitval, Type::Bool, "bittest");
auto mask_ = m_builder.CreateShl(Constant::get(1), bitposEx);
auto mask = m_builder.CreateSub(mask_, Constant::get(1), "mask");
auto negmask = m_builder.CreateXor(mask, llvm::ConstantInt::getAllOnesValue(Type::Word), "negmask");
auto val1 = m_builder.CreateOr(word, negmask);
auto val0 = m_builder.CreateAnd(word, mask);
auto kInRange = m_builder.CreateICmpULE(idx, llvm::ConstantInt::get(Type::Word, 30));
auto result = m_builder.CreateSelect(kInRange,
m_builder.CreateSelect(bittest, val1, val0),
word);
stack.push(result);
break;
}
case Instruction::SHA3:
{
auto inOff = stack.pop();
auto inSize = stack.pop();
_memory.require(inOff, inSize);
_gasMeter.countSha3Data(inSize);
auto hash = _ext.sha3(inOff, inSize);
stack.push(hash);
break;
}
case Instruction::POP:
{
stack.pop();
break;
}
case Instruction::ANY_PUSH:
{
auto value = readPushData(it, _basicBlock.end());
stack.push(Constant::get(value));
break;
}
case Instruction::ANY_DUP:
{
auto index = static_cast<size_t>(inst) - static_cast<size_t>(Instruction::DUP1);
stack.dup(index);
break;
}
case Instruction::ANY_SWAP:
{
auto index = static_cast<size_t>(inst) - static_cast<size_t>(Instruction::SWAP1) + 1;
stack.swap(index);
break;
}
case Instruction::MLOAD:
{
auto addr = stack.pop();
auto word = _memory.loadWord(addr);
stack.push(word);
break;
}
case Instruction::MSTORE:
{
auto addr = stack.pop();
auto word = stack.pop();
_memory.storeWord(addr, word);
break;
}
case Instruction::MSTORE8:
{
auto addr = stack.pop();
auto word = stack.pop();
_memory.storeByte(addr, word);
break;
}
case Instruction::MSIZE:
{
auto word = _memory.getSize();
stack.push(word);
break;
}
case Instruction::SLOAD:
{
auto index = stack.pop();
auto value = _ext.sload(index);
stack.push(value);
break;
}
case Instruction::SSTORE:
{
auto index = stack.pop();
auto value = stack.pop();
_gasMeter.countSStore(_ext, index, value);
_ext.sstore(index, value);
break;
}
case Instruction::JUMP:
case Instruction::JUMPI:
{
auto destIdx = llvm::MDNode::get(m_builder.getContext(), llvm::ValueAsMetadata::get(stack.pop()));
// Create branch instruction, initially to jump table.
// Destination will be optimized with direct jump during jump resolving if destination index is a constant.
auto jumpInst = (inst == Instruction::JUMP) ?
m_builder.CreateBr(m_jumpTableBB) :
m_builder.CreateCondBr(m_builder.CreateICmpNE(stack.pop(), Constant::get(0), "jump.check"), m_jumpTableBB, nullptr);
// Attach medatada to branch instruction with information about destination index.
jumpInst->setMetadata(c_destIdxLabel, destIdx);
break;
}
case Instruction::JUMPDEST:
{
// Nothing to do
break;
}
case Instruction::PC:
{
auto value = Constant::get(it - _basicBlock.begin() + _basicBlock.firstInstrIdx());
stack.push(value);
break;
}
case Instruction::GAS:
{
_gasMeter.commitCostBlock();
stack.push(m_builder.CreateZExt(_runtimeManager.getGas(), Type::Word));
break;
}
case Instruction::ADDRESS:
case Instruction::CALLER:
case Instruction::ORIGIN:
case Instruction::CALLVALUE:
case Instruction::GASPRICE:
case Instruction::COINBASE:
case Instruction::DIFFICULTY:
case Instruction::GASLIMIT:
case Instruction::NUMBER:
case Instruction::TIMESTAMP:
{
// Pushes an element of runtime data on stack
auto value = _runtimeManager.get(inst);
value = m_builder.CreateZExt(value, Type::Word);
stack.push(value);
break;
}
case Instruction::CODESIZE:
stack.push(_runtimeManager.getCodeSize());
break;
case Instruction::CALLDATASIZE:
stack.push(_runtimeManager.getCallDataSize());
break;
case Instruction::BLOCKHASH:
{
auto number = stack.pop();
auto hash = _ext.blockHash(number);
stack.push(hash);
break;
}
case Instruction::BALANCE:
{
auto address = stack.pop();
auto value = _ext.balance(address);
stack.push(value);
break;
}
case Instruction::EXTCODESIZE:
{
auto addr = stack.pop();
auto codeRef = _ext.extcode(addr);
stack.push(codeRef.size);
break;
}
case Instruction::CALLDATACOPY:
{
auto destMemIdx = stack.pop();
auto srcIdx = stack.pop();
auto reqBytes = stack.pop();
auto srcPtr = _runtimeManager.getCallData();
auto srcSize = _runtimeManager.getCallDataSize();
_memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes);
break;
}
case Instruction::CODECOPY:
{
auto destMemIdx = stack.pop();
auto srcIdx = stack.pop();
auto reqBytes = stack.pop();
auto srcPtr = _runtimeManager.getCode(); // TODO: Code & its size are constants, feature #80814234
auto srcSize = _runtimeManager.getCodeSize();
_memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes);
break;
}
case Instruction::EXTCODECOPY:
{
auto addr = stack.pop();
auto destMemIdx = stack.pop();
auto srcIdx = stack.pop();
auto reqBytes = stack.pop();
auto codeRef = _ext.extcode(addr);
_memory.copyBytes(codeRef.ptr, codeRef.size, srcIdx, destMemIdx, reqBytes);
break;
}
case Instruction::CALLDATALOAD:
{
auto idx = stack.pop();
auto value = _ext.calldataload(idx);
stack.push(value);
break;
}
case Instruction::CREATE:
{
auto endowment = stack.pop();
auto initOff = stack.pop();
auto initSize = stack.pop();
_memory.require(initOff, initSize);
_gasMeter.commitCostBlock();
auto address = _ext.create(endowment, initOff, initSize);
stack.push(address);
break;
}
case Instruction::CALL:
case Instruction::CALLCODE:
{
auto callGas = stack.pop();
auto codeAddress = stack.pop();
auto value = stack.pop();
auto inOff = stack.pop();
auto inSize = stack.pop();
auto outOff = stack.pop();
auto outSize = stack.pop();
_gasMeter.commitCostBlock();
// Require memory for in and out buffers
_memory.require(outOff, outSize); // Out buffer first as we guess it will be after the in one
_memory.require(inOff, inSize);
auto receiveAddress = codeAddress;
if (inst == Instruction::CALLCODE)
receiveAddress = _runtimeManager.get(RuntimeData::Address);
auto ret = _ext.call(callGas, receiveAddress, value, inOff, inSize, outOff, outSize, codeAddress);
_gasMeter.count(m_builder.getInt64(0), _runtimeManager.getJmpBuf(), _runtimeManager.getGasPtr());
stack.push(ret);
break;
}
case Instruction::RETURN:
{
auto index = stack.pop();
auto size = stack.pop();
_memory.require(index, size);
_runtimeManager.registerReturnData(index, size);
_runtimeManager.exit(ReturnCode::Return);
break;
}
case Instruction::SUICIDE:
{
_runtimeManager.registerSuicide(stack.pop());
_runtimeManager.exit(ReturnCode::Suicide);
break;
}
case Instruction::STOP:
{
m_builder.CreateBr(m_stopBB);
break;
}
case Instruction::LOG0:
case Instruction::LOG1:
case Instruction::LOG2:
case Instruction::LOG3:
case Instruction::LOG4:
{
auto beginIdx = stack.pop();
auto numBytes = stack.pop();
_memory.require(beginIdx, numBytes);
// This will commit the current cost block
_gasMeter.countLogData(numBytes);
std::array<llvm::Value*, 4> topics{{}};
auto numTopics = static_cast<size_t>(inst) - static_cast<size_t>(Instruction::LOG0);
for (size_t i = 0; i < numTopics; ++i)
topics[i] = stack.pop();
_ext.log(beginIdx, numBytes, topics);
break;
}
default: // Invalid instruction - abort
m_builder.CreateBr(m_abortBB);
it = _basicBlock.end() - 1; // finish block compilation
}
}
_gasMeter.commitCostBlock();
stack.finalize(m_builder, *_basicBlock.llvm()); // TODO: Use references
m_builder.SetInsertPoint(_basicBlock.llvm()->getFirstNonPHI()); // TODO: Move to LocalStack::finalize
_runtimeManager.checkStackLimit(stack.minSize(), stack.maxSize(), stack.size());
}
}
}
}

59
evmjit/libevmjit/Compiler.h

@ -1,59 +0,0 @@
#pragma once
#include "BasicBlock.h"
namespace dev
{
namespace eth
{
namespace jit
{
class Compiler
{
public:
struct Options
{
/// Rewrite switch instructions to sequences of branches
bool rewriteSwitchToBranches = true;
/// Dump CFG as a .dot file for graphviz
bool dumpCFG = false;
};
Compiler(Options const& _options);
std::unique_ptr<llvm::Module> compile(code_iterator _begin, code_iterator _end, std::string const& _id);
private:
std::vector<BasicBlock> createBasicBlocks(code_iterator _begin, code_iterator _end, llvm::SwitchInst& _switchInst);
void compileBasicBlock(BasicBlock& _basicBlock, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter,
class Stack& _globalStack);
void resolveJumps();
/// Compiler options
Options const& m_options;
/// Helper class for generating IR
llvm::IRBuilder<> m_builder;
/// Stop basic block - terminates execution with STOP code (0)
llvm::BasicBlock* m_stopBB = nullptr;
/// Abort basic block - terminates execution with OOG-like state
llvm::BasicBlock* m_abortBB = nullptr;
/// Block with a jump table.
llvm::BasicBlock* m_jumpTableBB = nullptr;
/// Main program function
llvm::Function* m_mainFunc = nullptr; // TODO: Remove
};
}
}
}

51
evmjit/libevmjit/CompilerHelper.cpp

@ -1,51 +0,0 @@
#include "CompilerHelper.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h"
namespace dev
{
namespace eth
{
namespace jit
{
CompilerHelper::CompilerHelper(llvm::IRBuilder<>& _builder) :
m_builder(_builder)
{}
llvm::Module* CompilerHelper::getModule()
{
assert(m_builder.GetInsertBlock());
assert(m_builder.GetInsertBlock()->getParent()); // BB must be in a function
return m_builder.GetInsertBlock()->getParent()->getParent();
}
llvm::Function* CompilerHelper::getMainFunction()
{
// TODO: Rename or change semantics of getMainFunction() function
assert(m_builder.GetInsertBlock());
auto mainFunc = m_builder.GetInsertBlock()->getParent();
assert(mainFunc);
if (mainFunc == &mainFunc->getParent()->getFunctionList().front()) // Main function is the first one in module
return mainFunc;
return nullptr;
}
llvm::CallInst* CompilerHelper::createCall(llvm::Function* _func, std::initializer_list<llvm::Value*> const& _args)
{
return getBuilder().CreateCall(_func, {_args.begin(), _args.size()});
}
RuntimeHelper::RuntimeHelper(RuntimeManager& _runtimeManager):
CompilerHelper(_runtimeManager.getBuilder()),
m_runtimeManager(_runtimeManager)
{}
}
}
}

64
evmjit/libevmjit/CompilerHelper.h

@ -1,64 +0,0 @@
#pragma once
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IRBuilder.h>
#include "preprocessor/llvm_includes_end.h"
namespace dev
{
namespace eth
{
namespace jit
{
class RuntimeManager;
/// Base class for compiler helpers like Memory, GasMeter, etc.
class CompilerHelper
{
protected:
CompilerHelper(llvm::IRBuilder<>& _builder);
CompilerHelper(const CompilerHelper&) = delete;
CompilerHelper& operator=(CompilerHelper) = delete;
/// Reference to the IR module being compiled
llvm::Module* getModule();
/// Reference to the main module function
llvm::Function* getMainFunction();
/// Reference to parent compiler IR builder
llvm::IRBuilder<>& m_builder;
llvm::IRBuilder<>& getBuilder() { return m_builder; }
llvm::CallInst* createCall(llvm::Function* _func, std::initializer_list<llvm::Value*> const& _args);
friend class RuntimeHelper;
};
/// Compiler helper that depends on runtime data
class RuntimeHelper : public CompilerHelper
{
protected:
RuntimeHelper(RuntimeManager& _runtimeManager);
RuntimeManager& getRuntimeManager() { return m_runtimeManager; }
private:
RuntimeManager& m_runtimeManager;
};
struct InsertPointGuard
{
explicit InsertPointGuard(llvm::IRBuilderBase& _builder): m_builder(_builder), m_insertPoint(_builder.saveIP()) {}
~InsertPointGuard() { m_builder.restoreIP(m_insertPoint); }
private:
llvm::IRBuilderBase& m_builder;
llvm::IRBuilderBase::InsertPoint m_insertPoint;
};
}
}
}

33
evmjit/libevmjit/Endianness.cpp

@ -1,33 +0,0 @@
#include "Endianness.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IntrinsicInst.h>
#include <llvm/Support/Host.h>
#include "preprocessor/llvm_includes_end.h"
#include "Type.h"
namespace dev
{
namespace eth
{
namespace jit
{
llvm::Value* Endianness::bswapIfLE(llvm::IRBuilder<>& _builder, llvm::Value* _word)
{
if (llvm::sys::IsLittleEndianHost)
{
if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(_word))
return _builder.getInt(constant->getValue().byteSwap());
// OPT: Cache func declaration?
auto bswapFunc = llvm::Intrinsic::getDeclaration(_builder.GetInsertBlock()->getParent()->getParent(), llvm::Intrinsic::bswap, Type::Word);
return _builder.CreateCall(bswapFunc, _word);
}
return _word;
}
}
}
}

25
evmjit/libevmjit/Endianness.h

@ -1,25 +0,0 @@
#pragma once
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IRBuilder.h>
#include "preprocessor/llvm_includes_end.h"
namespace dev
{
namespace eth
{
namespace jit
{
struct Endianness
{
static llvm::Value* toBE(llvm::IRBuilder<>& _builder, llvm::Value* _word) { return bswapIfLE(_builder, _word); }
static llvm::Value* toNative(llvm::IRBuilder<>& _builder, llvm::Value* _word) { return bswapIfLE(_builder, _word); }
private:
static llvm::Value* bswapIfLE(llvm::IRBuilder<>& _builder, llvm::Value* _word);
};
}
}
}

95
evmjit/libevmjit/ExecStats.cpp

@ -1,95 +0,0 @@
#include "ExecStats.h"
#include <iostream>
#include <iomanip>
#include <cassert>
#include "Utils.h"
namespace dev
{
namespace evmjit
{
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::Optimization: return "Optimization";
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);
}
}
}
}

68
evmjit/libevmjit/ExecStats.h

@ -1,68 +0,0 @@
#pragma once
#include <memory>
#include <vector>
#include <string>
#include <chrono>
namespace dev
{
namespace evmjit
{
enum class ExecState
{
Started,
CacheLoad,
CacheWrite,
Compilation,
Optimization,
CodeGen,
Execution,
Return,
Finished
};
class JITListener
{
public:
JITListener() = default;
JITListener(JITListener const&) = delete;
JITListener& operator=(JITListener) = delete;
virtual ~JITListener() {}
virtual void executionStarted() {}
virtual void executionEnded() {}
virtual void stateChanged(ExecState) {}
};
class ExecStats : public JITListener
{
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();
};
}
}

210
evmjit/libevmjit/Ext.cpp

@ -1,210 +0,0 @@
#include "Ext.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h"
#include "Memory.h"
#include "Type.h"
#include "Endianness.h"
namespace dev
{
namespace eth
{
namespace jit
{
Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan):
RuntimeHelper(_runtimeManager),
m_memoryMan(_memoryMan)
{
m_funcs = decltype(m_funcs)();
m_argAllocas = decltype(m_argAllocas)();
m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size");
}
using FuncDesc = std::tuple<char const*, llvm::FunctionType*>;
llvm::FunctionType* getFunctionType(llvm::Type* _returnType, std::initializer_list<llvm::Type*> const& _argsTypes)
{
return llvm::FunctionType::get(_returnType, llvm::ArrayRef<llvm::Type*>{_argsTypes.begin(), _argsTypes.size()}, false);
}
std::array<FuncDesc, sizeOf<EnvFunc>::value> const& getEnvFuncDescs()
{
static std::array<FuncDesc, sizeOf<EnvFunc>::value> descs{{
FuncDesc{"env_sload", 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_balance", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, 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::GasPtr, Type::Gas, 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_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})},
}};
return descs;
}
llvm::Function* createFunc(EnvFunc _id, llvm::Module* _module)
{
auto&& desc = getEnvFuncDescs()[static_cast<size_t>(_id)];
return llvm::Function::Create(std::get<1>(desc), llvm::Function::ExternalLinkage, std::get<0>(desc), _module);
}
llvm::Value* Ext::getArgAlloca()
{
auto& a = m_argAllocas[m_argCounter];
if (!a)
{
InsertPointGuard g{getBuilder()};
auto allocaIt = getMainFunction()->front().begin();
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;
}
llvm::Value* Ext::byPtr(llvm::Value* _value)
{
auto a = getArgAlloca();
getBuilder().CreateStore(_value, a);
return a;
}
llvm::CallInst* Ext::createCall(EnvFunc _funcId, std::initializer_list<llvm::Value*> const& _args)
{
auto& func = m_funcs[static_cast<size_t>(_funcId)];
if (!func)
func = createFunc(_funcId, getModule());
m_argCounter = 0;
return getBuilder().CreateCall(func, {_args.begin(), _args.size()});
}
llvm::Value* Ext::sload(llvm::Value* _index)
{
auto ret = getArgAlloca();
createCall(EnvFunc::sload, {getRuntimeManager().getEnvPtr(), byPtr(_index), ret}); // Uses native endianness
return m_builder.CreateLoad(ret);
}
void Ext::sstore(llvm::Value* _index, llvm::Value* _value)
{
createCall(EnvFunc::sstore, {getRuntimeManager().getEnvPtr(), byPtr(_index), byPtr(_value)}); // Uses native endianness
}
llvm::Value* Ext::calldataload(llvm::Value* _idx)
{
auto ret = getArgAlloca();
auto result = m_builder.CreateBitCast(ret, Type::BytePtr);
auto callDataSize = getRuntimeManager().getCallDataSize();
auto callDataSize64 = m_builder.CreateTrunc(callDataSize, Type::Size);
auto idxValid = m_builder.CreateICmpULT(_idx, callDataSize);
auto idx = m_builder.CreateTrunc(m_builder.CreateSelect(idxValid, _idx, callDataSize), Type::Size, "idx");
auto end = m_builder.CreateNUWAdd(idx, m_builder.getInt64(32));
end = m_builder.CreateSelect(m_builder.CreateICmpULE(end, callDataSize64), end, callDataSize64);
auto copySize = m_builder.CreateNUWSub(end, idx);
auto padSize = m_builder.CreateNUWSub(m_builder.getInt64(32), copySize);
auto dataBegin = m_builder.CreateGEP(Type::Byte, getRuntimeManager().getCallData(), idx);
m_builder.CreateMemCpy(result, dataBegin, copySize, 1);
auto pad = m_builder.CreateGEP(Type::Byte, result, copySize);
m_builder.CreateMemSet(pad, m_builder.getInt8(0), padSize, 1);
m_argCounter = 0; // Release args allocas. TODO: This is a bad design
return Endianness::toNative(m_builder, m_builder.CreateLoad(ret));
}
llvm::Value* Ext::balance(llvm::Value* _address)
{
auto address = Endianness::toBE(m_builder, _address);
auto ret = getArgAlloca();
createCall(EnvFunc::balance, {getRuntimeManager().getEnvPtr(), byPtr(address), ret});
return m_builder.CreateLoad(ret);
}
llvm::Value* Ext::blockHash(llvm::Value* _number)
{
auto hash = getArgAlloca();
createCall(EnvFunc::blockhash, {getRuntimeManager().getEnvPtr(), byPtr(_number), hash});
hash = m_builder.CreateLoad(hash);
return Endianness::toNative(getBuilder(), hash);
}
llvm::Value* Ext::create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize)
{
auto ret = getArgAlloca();
auto begin = m_memoryMan.getBytePtr(_initOff);
auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size");
createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), byPtr(_endowment), begin, size, ret});
llvm::Value* address = m_builder.CreateLoad(ret);
address = Endianness::toNative(m_builder, address);
return address;
}
llvm::Value* Ext::call(llvm::Value* _callGas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress)
{
auto receiveAddress = Endianness::toBE(m_builder, _receiveAddress);
auto inBeg = m_memoryMan.getBytePtr(_inOff);
auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size");
auto outBeg = m_memoryMan.getBytePtr(_outOff);
auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size");
auto codeAddress = Endianness::toBE(m_builder, _codeAddress);
auto callGas = m_builder.CreateSelect(
m_builder.CreateICmpULE(_callGas, m_builder.CreateZExt(Constant::gasMax, Type::Word)),
m_builder.CreateTrunc(_callGas, Type::Gas),
Constant::gasMax);
auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), callGas, byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(codeAddress)});
return m_builder.CreateZExt(ret, Type::Word, "ret");
}
llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize)
{
auto begin = m_memoryMan.getBytePtr(_inOff);
auto size = m_builder.CreateTrunc(_inSize, Type::Size, "size");
auto ret = getArgAlloca();
createCall(EnvFunc::sha3, {begin, size, ret});
llvm::Value* hash = m_builder.CreateLoad(ret);
hash = Endianness::toNative(m_builder, hash);
return hash;
}
MemoryRef Ext::extcode(llvm::Value* _addr)
{
auto addr = Endianness::toBE(m_builder, _addr);
auto code = createCall(EnvFunc::extcode, {getRuntimeManager().getEnvPtr(), byPtr(addr), m_size});
auto codeSize = m_builder.CreateLoad(m_size);
auto codeSize256 = m_builder.CreateZExt(codeSize, Type::Word);
return {code, codeSize256};
}
void Ext::log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array<llvm::Value*,4> const& _topics)
{
auto begin = m_memoryMan.getBytePtr(_memIdx);
auto size = m_builder.CreateTrunc(_numBytes, Type::Size, "size");
llvm::Value* args[] = {getRuntimeManager().getEnvPtr(), begin, size, getArgAlloca(), getArgAlloca(), getArgAlloca(), getArgAlloca()};
auto topicArgPtr = &args[3];
for (auto&& topic : _topics)
{
if (topic)
m_builder.CreateStore(Endianness::toBE(m_builder, topic), *topicArgPtr);
else
*topicArgPtr = llvm::ConstantPointerNull::get(Type::WordPtr);
++topicArgPtr;
}
createCall(EnvFunc::log, {args[0], args[1], args[2], args[3], args[4], args[5], args[6]}); // TODO: use std::initializer_list<>
}
}
}
}

79
evmjit/libevmjit/Ext.h

@ -1,79 +0,0 @@
#pragma once
#include <array>
#include "CompilerHelper.h"
namespace dev
{
namespace eth
{
namespace jit
{
class Memory;
struct MemoryRef
{
llvm::Value* ptr;
llvm::Value* size;
};
template<typename _EnumT>
struct sizeOf
{
static const size_t value = static_cast<size_t>(_EnumT::_size);
};
enum class EnvFunc
{
sload,
sstore,
sha3,
balance,
create,
call,
log,
blockhash,
extcode,
_size
};
class Ext : public RuntimeHelper
{
public:
Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan);
llvm::Value* sload(llvm::Value* _index);
void sstore(llvm::Value* _index, llvm::Value* _value);
llvm::Value* balance(llvm::Value* _address);
llvm::Value* calldataload(llvm::Value* _index);
llvm::Value* create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize);
llvm::Value* call(llvm::Value* _callGas, 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* sha3(llvm::Value* _inOff, llvm::Value* _inSize);
MemoryRef extcode(llvm::Value* _addr);
void log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array<llvm::Value*,4> const& _topics);
private:
Memory& m_memoryMan;
llvm::Value* m_size;
std::array<llvm::Function*, sizeOf<EnvFunc>::value> m_funcs;
std::array<llvm::Value*, 8> m_argAllocas;
size_t m_argCounter = 0;
llvm::CallInst* createCall(EnvFunc _funcId, std::initializer_list<llvm::Value*> const& _args);
llvm::Value* getArgAlloca();
llvm::Value* byPtr(llvm::Value* _value);
};
}
}
}

299
evmjit/libevmjit/GasMeter.cpp

@ -1,299 +0,0 @@
#include "GasMeter.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "Ext.h"
#include "RuntimeManager.h"
namespace dev
{
namespace eth
{
namespace jit
{
namespace // Helper functions
{
int64_t const c_stepGas[] = {0, 2, 3, 5, 8, 10, 20};
int64_t const c_expByteGas = 10;
int64_t const c_sha3Gas = 30;
int64_t const c_sha3WordGas = 6;
int64_t const c_sloadGas = 50;
int64_t const c_sstoreSetGas = 20000;
int64_t const c_sstoreResetGas = 5000;
int64_t const c_sstoreClearGas = 5000;
int64_t const c_jumpdestGas = 1;
int64_t const c_logGas = 375;
int64_t const c_logTopicGas = 375;
int64_t const c_logDataGas = 8;
int64_t const c_callGas = 40;
int64_t const c_createGas = 32000;
int64_t const c_memoryGas = 3;
int64_t const c_copyGas = 3;
int64_t getStepCost(Instruction inst)
{
switch (inst)
{
// Tier 0
case Instruction::STOP:
case Instruction::RETURN:
case Instruction::SUICIDE:
case Instruction::SSTORE: // Handle cost of SSTORE separately in GasMeter::countSStore()
return c_stepGas[0];
// Tier 1
case Instruction::ADDRESS:
case Instruction::ORIGIN:
case Instruction::CALLER:
case Instruction::CALLVALUE:
case Instruction::CALLDATASIZE:
case Instruction::CODESIZE:
case Instruction::GASPRICE:
case Instruction::COINBASE:
case Instruction::TIMESTAMP:
case Instruction::NUMBER:
case Instruction::DIFFICULTY:
case Instruction::GASLIMIT:
case Instruction::POP:
case Instruction::PC:
case Instruction::MSIZE:
case Instruction::GAS:
return c_stepGas[1];
// Tier 2
case Instruction::ADD:
case Instruction::SUB:
case Instruction::LT:
case Instruction::GT:
case Instruction::SLT:
case Instruction::SGT:
case Instruction::EQ:
case Instruction::ISZERO:
case Instruction::AND:
case Instruction::OR:
case Instruction::XOR:
case Instruction::NOT:
case Instruction::BYTE:
case Instruction::CALLDATALOAD:
case Instruction::CALLDATACOPY:
case Instruction::CODECOPY:
case Instruction::MLOAD:
case Instruction::MSTORE:
case Instruction::MSTORE8:
case Instruction::ANY_PUSH:
case Instruction::ANY_DUP:
case Instruction::ANY_SWAP:
return c_stepGas[2];
// Tier 3
case Instruction::MUL:
case Instruction::DIV:
case Instruction::SDIV:
case Instruction::MOD:
case Instruction::SMOD:
case Instruction::SIGNEXTEND:
return c_stepGas[3];
// Tier 4
case Instruction::ADDMOD:
case Instruction::MULMOD:
case Instruction::JUMP:
return c_stepGas[4];
// Tier 5
case Instruction::EXP:
case Instruction::JUMPI:
return c_stepGas[5];
// Tier 6
case Instruction::BALANCE:
case Instruction::EXTCODESIZE:
case Instruction::EXTCODECOPY:
case Instruction::BLOCKHASH:
return c_stepGas[6];
case Instruction::SHA3:
return c_sha3Gas;
case Instruction::SLOAD:
return c_sloadGas;
case Instruction::JUMPDEST:
return c_jumpdestGas;
case Instruction::LOG0:
case Instruction::LOG1:
case Instruction::LOG2:
case Instruction::LOG3:
case Instruction::LOG4:
{
auto numTopics = static_cast<int64_t>(inst) - static_cast<int64_t>(Instruction::LOG0);
return c_logGas + numTopics * c_logTopicGas;
}
case Instruction::CALL:
case Instruction::CALLCODE:
return c_callGas;
case Instruction::CREATE:
return c_createGas;
}
return 0; // TODO: Add UNREACHABLE macro
}
}
GasMeter::GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager) :
CompilerHelper(_builder),
m_runtimeManager(_runtimeManager)
{
llvm::Type* gasCheckArgs[] = {Type::Gas->getPointerTo(), Type::Gas, Type::BytePtr};
m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, gasCheckArgs, false), llvm::Function::PrivateLinkage, "gas.check", getModule());
m_gasCheckFunc->setDoesNotThrow();
m_gasCheckFunc->setDoesNotCapture(1);
auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_gasCheckFunc);
auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_gasCheckFunc);
auto outOfGasBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfGas", m_gasCheckFunc);
auto gasPtr = &m_gasCheckFunc->getArgumentList().front();
gasPtr->setName("gasPtr");
auto cost = gasPtr->getNextNode();
cost->setName("cost");
auto jmpBuf = cost->getNextNode();
jmpBuf->setName("jmpBuf");
InsertPointGuard guard(m_builder);
m_builder.SetInsertPoint(checkBB);
auto gas = m_builder.CreateLoad(gasPtr, "gas");
auto gasUpdated = m_builder.CreateNSWSub(gas, cost, "gasUpdated");
auto gasOk = m_builder.CreateICmpSGE(gasUpdated, m_builder.getInt64(0), "gasOk"); // gas >= 0, with gas == 0 we can still do 0 cost instructions
m_builder.CreateCondBr(gasOk, updateBB, outOfGasBB, Type::expectTrue);
m_builder.SetInsertPoint(updateBB);
m_builder.CreateStore(gasUpdated, gasPtr);
m_builder.CreateRetVoid();
m_builder.SetInsertPoint(outOfGasBB);
m_runtimeManager.abort(jmpBuf);
m_builder.CreateUnreachable();
}
void GasMeter::count(Instruction _inst)
{
if (!m_checkCall)
{
// Create gas check call with mocked block cost at begining of current cost-block
m_checkCall = createCall(m_gasCheckFunc, {m_runtimeManager.getGasPtr(), llvm::UndefValue::get(Type::Gas), m_runtimeManager.getJmpBuf()});
}
m_blockCost += getStepCost(_inst);
}
void GasMeter::count(llvm::Value* _cost, llvm::Value* _jmpBuf, llvm::Value* _gasPtr)
{
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, {_gasPtr ? _gasPtr : m_runtimeManager.getGasPtr(), _cost, _jmpBuf ? _jmpBuf : m_runtimeManager.getJmpBuf()});
}
void GasMeter::countExp(llvm::Value* _exponent)
{
// Additional cost is 1 per significant byte of exponent
// lz - leading zeros
// cost = ((256 - lz) + 7) / 8
// OPT: Can gas update be done in exp algorithm?
auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word);
auto lz256 = m_builder.CreateCall(ctlz, {_exponent, m_builder.getInt1(false)});
auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz");
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(m_builder.CreateNUWMul(sigBytes, m_builder.getInt64(c_expByteGas)));
}
void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValue)
{
auto oldValue = _ext.sload(_index);
auto oldValueIsZero = m_builder.CreateICmpEQ(oldValue, Constant::get(0), "oldValueIsZero");
auto newValueIsntZero = m_builder.CreateICmpNE(_newValue, Constant::get(0), "newValueIsntZero");
auto isInsert = m_builder.CreateAnd(oldValueIsZero, newValueIsntZero, "isInsert");
static_assert(c_sstoreResetGas == c_sstoreClearGas, "Update SSTORE gas cost");
auto cost = m_builder.CreateSelect(isInsert, m_builder.getInt64(c_sstoreSetGas), m_builder.getInt64(c_sstoreResetGas), "cost");
count(cost);
}
void GasMeter::countLogData(llvm::Value* _dataLength)
{
assert(m_checkCall);
assert(m_blockCost > 0); // LOGn instruction is already counted
static_assert(c_logDataGas != 1, "Log data gas cost has changed. Update GasMeter.");
count(m_builder.CreateNUWMul(_dataLength, Constant::get(c_logDataGas))); // TODO: Use i64
}
void GasMeter::countSha3Data(llvm::Value* _dataLength)
{
assert(m_checkCall);
assert(m_blockCost > 0); // SHA3 instruction is already counted
// TODO: This round ups to 32 happens in many places
static_assert(c_sha3WordGas != 1, "SHA3 data cost has changed. Update GasMeter");
auto dataLength64 = getBuilder().CreateTrunc(_dataLength, Type::Gas);
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);
count(cost64);
}
void GasMeter::giveBack(llvm::Value* _gas)
{
assert(_gas->getType() == Type::Gas);
m_runtimeManager.setGas(m_builder.CreateAdd(m_runtimeManager.getGas(), _gas));
}
void GasMeter::commitCostBlock()
{
// If any uncommited block
if (m_checkCall)
{
if (m_blockCost == 0) // Do not check 0
{
m_checkCall->eraseFromParent(); // Remove the gas check call
m_checkCall = nullptr;
return;
}
m_checkCall->setArgOperand(1, m_builder.getInt64(m_blockCost)); // Update block cost in gas check call
m_checkCall = nullptr; // End cost-block
m_blockCost = 0;
}
assert(m_blockCost == 0);
}
void GasMeter::countMemory(llvm::Value* _additionalMemoryInWords, llvm::Value* _jmpBuf, llvm::Value* _gasPtr)
{
static_assert(c_memoryGas != 1, "Memory gas cost has changed. Update GasMeter.");
count(_additionalMemoryInWords, _jmpBuf, _gasPtr);
}
void GasMeter::countCopy(llvm::Value* _copyWords)
{
static_assert(c_copyGas != 1, "Copy gas cost has changed. Update GasMeter.");
count(m_builder.CreateNUWMul(_copyWords, m_builder.getInt64(c_copyGas)));
}
}
}
}

64
evmjit/libevmjit/GasMeter.h

@ -1,64 +0,0 @@
#pragma once
#include "CompilerHelper.h"
#include "Instruction.h"
namespace dev
{
namespace eth
{
namespace jit
{
class RuntimeManager;
using namespace evmjit;
class GasMeter : public CompilerHelper // TODO: Use RuntimeHelper
{
public:
GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager);
/// Count step cost of instruction
void count(Instruction _inst);
/// Count additional cost
void count(llvm::Value* _cost, llvm::Value* _jmpBuf = nullptr, llvm::Value* _gasPtr = nullptr);
/// Calculate & count gas cost for SSTORE instruction
void countSStore(class Ext& _ext, llvm::Value* _index, llvm::Value* _newValue);
/// Calculate & count additional gas cost for EXP instruction
void countExp(llvm::Value* _exponent);
/// Count gas cost of LOG data
void countLogData(llvm::Value* _dataLength);
/// Count gas cost of SHA3 data
void countSha3Data(llvm::Value* _dataLength);
/// Finalize cost-block by checking gas needed for the block before the block
void commitCostBlock();
/// Give back an amount of gas not used by a call
void giveBack(llvm::Value* _gas);
/// Generate code that checks the cost of additional memory used by program
void countMemory(llvm::Value* _additionalMemoryInWords, llvm::Value* _jmpBuf, llvm::Value* _gasPtr);
/// Count addional gas cost for memory copy
void countCopy(llvm::Value* _copyWords);
private:
/// Cumulative gas cost of a block of instructions
/// @TODO Handle overflow
int64_t m_blockCost = 0;
llvm::CallInst* m_checkCall = nullptr;
llvm::Function* m_gasCheckFunc = nullptr;
RuntimeManager& m_runtimeManager;
};
}
}
}

39
evmjit/libevmjit/Instruction.cpp

@ -1,39 +0,0 @@
#include "Instruction.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/ADT/APInt.h>
#include "preprocessor/llvm_includes_end.h"
namespace dev
{
namespace evmjit
{
llvm::APInt readPushData(code_iterator& _curr, code_iterator _end)
{
auto pushInst = *_curr;
assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32);
auto numBytes = pushInst - static_cast<size_t>(Instruction::PUSH1) + 1;
llvm::APInt value(256, 0);
++_curr; // Point the data
for (decltype(numBytes) i = 0; i < numBytes; ++i)
{
byte b = (_curr != _end) ? *_curr++ : 0;
value <<= 8;
value |= b;
}
--_curr; // Point the last real byte read
return value;
}
void skipPushData(code_iterator& _curr, code_iterator _end)
{
auto pushInst = *_curr;
assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32);
auto numBytes = pushInst - static_cast<size_t>(Instruction::PUSH1) + 1;
--_end;
for (decltype(numBytes) i = 0; i < numBytes && _curr < _end; ++i, ++_curr) {}
}
}
}

236
evmjit/libevmjit/Instruction.h

@ -1,236 +0,0 @@
#pragma once
#include "Common.h"
namespace llvm
{
class APInt;
}
namespace dev
{
namespace evmjit
{
/// Virtual machine bytecode instruction.
enum class Instruction: uint8_t
{
STOP = 0x00, ///< halts execution
ADD, ///< addition operation
MUL, ///< mulitplication operation
SUB, ///< subtraction operation
DIV, ///< integer division operation
SDIV, ///< signed integer division operation
MOD, ///< modulo remainder operation
SMOD, ///< signed modulo remainder operation
ADDMOD, ///< unsigned modular addition
MULMOD, ///< unsigned modular multiplication
EXP, ///< exponential operation
SIGNEXTEND, ///< extend length of signed integer
LT = 0x10, ///< less-than comparision
GT, ///< greater-than comparision
SLT, ///< signed less-than comparision
SGT, ///< signed greater-than comparision
EQ, ///< equality comparision
ISZERO, ///< simple not operator
AND, ///< bitwise AND operation
OR, ///< bitwise OR operation
XOR, ///< bitwise XOR operation
NOT, ///< bitwise NOT opertation
BYTE, ///< retrieve single byte from word
SHA3 = 0x20, ///< compute SHA3-256 hash
ADDRESS = 0x30, ///< get address of currently executing account
BALANCE, ///< get balance of the given account
ORIGIN, ///< get execution origination address
CALLER, ///< get caller address
CALLVALUE, ///< get deposited value by the instruction/transaction responsible for this execution
CALLDATALOAD, ///< get input data of current environment
CALLDATASIZE, ///< get size of input data in current environment
CALLDATACOPY, ///< copy input data in current environment to memory
CODESIZE, ///< get size of code running in current environment
CODECOPY, ///< copy code running in current environment to memory
GASPRICE, ///< get price of gas in current environment
EXTCODESIZE, ///< get external code size (from another contract)
EXTCODECOPY, ///< copy external code (from another contract)
BLOCKHASH = 0x40, ///< get hash of most recent complete block
COINBASE, ///< get the block's coinbase address
TIMESTAMP, ///< get the block's timestamp
NUMBER, ///< get the block's number
DIFFICULTY, ///< get the block's difficulty
GASLIMIT, ///< get the block's gas limit
POP = 0x50, ///< remove item from stack
MLOAD, ///< load word from memory
MSTORE, ///< save word to memory
MSTORE8, ///< save byte to memory
SLOAD, ///< load word from storage
SSTORE, ///< save word to storage
JUMP, ///< alter the program counter
JUMPI, ///< conditionally alter the program counter
PC, ///< get the program counter
MSIZE, ///< get the size of active memory
GAS, ///< get the amount of available gas
JUMPDEST, ///< set a potential jump destination
PUSH1 = 0x60, ///< place 1 byte item on stack
PUSH2, ///< place 2 byte item on stack
PUSH3, ///< place 3 byte item on stack
PUSH4, ///< place 4 byte item on stack
PUSH5, ///< place 5 byte item on stack
PUSH6, ///< place 6 byte item on stack
PUSH7, ///< place 7 byte item on stack
PUSH8, ///< place 8 byte item on stack
PUSH9, ///< place 9 byte item on stack
PUSH10, ///< place 10 byte item on stack
PUSH11, ///< place 11 byte item on stack
PUSH12, ///< place 12 byte item on stack
PUSH13, ///< place 13 byte item on stack
PUSH14, ///< place 14 byte item on stack
PUSH15, ///< place 15 byte item on stack
PUSH16, ///< place 16 byte item on stack
PUSH17, ///< place 17 byte item on stack
PUSH18, ///< place 18 byte item on stack
PUSH19, ///< place 19 byte item on stack
PUSH20, ///< place 20 byte item on stack
PUSH21, ///< place 21 byte item on stack
PUSH22, ///< place 22 byte item on stack
PUSH23, ///< place 23 byte item on stack
PUSH24, ///< place 24 byte item on stack
PUSH25, ///< place 25 byte item on stack
PUSH26, ///< place 26 byte item on stack
PUSH27, ///< place 27 byte item on stack
PUSH28, ///< place 28 byte item on stack
PUSH29, ///< place 29 byte item on stack
PUSH30, ///< place 30 byte item on stack
PUSH31, ///< place 31 byte item on stack
PUSH32, ///< place 32 byte item on stack
DUP1 = 0x80, ///< copies the highest item in the stack to the top of the stack
DUP2, ///< copies the second highest item in the stack to the top of the stack
DUP3, ///< copies the third highest item in the stack to the top of the stack
DUP4, ///< copies the 4th highest item in the stack to the top of the stack
DUP5, ///< copies the 5th highest item in the stack to the top of the stack
DUP6, ///< copies the 6th highest item in the stack to the top of the stack
DUP7, ///< copies the 7th highest item in the stack to the top of the stack
DUP8, ///< copies the 8th highest item in the stack to the top of the stack
DUP9, ///< copies the 9th highest item in the stack to the top of the stack
DUP10, ///< copies the 10th highest item in the stack to the top of the stack
DUP11, ///< copies the 11th highest item in the stack to the top of the stack
DUP12, ///< copies the 12th highest item in the stack to the top of the stack
DUP13, ///< copies the 13th highest item in the stack to the top of the stack
DUP14, ///< copies the 14th highest item in the stack to the top of the stack
DUP15, ///< copies the 15th highest item in the stack to the top of the stack
DUP16, ///< copies the 16th highest item in the stack to the top of the stack
SWAP1 = 0x90, ///< swaps the highest and second highest value on the stack
SWAP2, ///< swaps the highest and third highest value on the stack
SWAP3, ///< swaps the highest and 4th highest value on the stack
SWAP4, ///< swaps the highest and 5th highest value on the stack
SWAP5, ///< swaps the highest and 6th highest value on the stack
SWAP6, ///< swaps the highest and 7th highest value on the stack
SWAP7, ///< swaps the highest and 8th highest value on the stack
SWAP8, ///< swaps the highest and 9th highest value on the stack
SWAP9, ///< swaps the highest and 10th highest value on the stack
SWAP10, ///< swaps the highest and 11th highest value on the stack
SWAP11, ///< swaps the highest and 12th highest value on the stack
SWAP12, ///< swaps the highest and 13th highest value on the stack
SWAP13, ///< swaps the highest and 14th highest value on the stack
SWAP14, ///< swaps the highest and 15th highest value on the stack
SWAP15, ///< swaps the highest and 16th highest value on the stack
SWAP16, ///< swaps the highest and 17th highest value on the stack
LOG0 = 0xa0, ///< Makes a log entry; no topics.
LOG1, ///< Makes a log entry; 1 topic.
LOG2, ///< Makes a log entry; 2 topics.
LOG3, ///< Makes a log entry; 3 topics.
LOG4, ///< Makes a log entry; 4 topics.
CREATE = 0xf0, ///< create a new account with associated code
CALL, ///< message-call into an account
CALLCODE, ///< message-call with another account's code only
RETURN, ///< halt execution returning output data
SUICIDE = 0xff ///< halt execution and register account for later deletion
};
/// Reads PUSH data from pointed fragment of bytecode and constructs number out of it
/// Reading out of bytecode means reading 0
/// @param _curr is updated and points the last real byte read
llvm::APInt readPushData(code_iterator& _curr, code_iterator _end);
/// Skips PUSH data in pointed fragment of bytecode.
/// @param _curr is updated and points the last real byte skipped
void skipPushData(code_iterator& _curr, code_iterator _end);
#define ANY_PUSH PUSH1: \
case Instruction::PUSH2: \
case Instruction::PUSH3: \
case Instruction::PUSH4: \
case Instruction::PUSH5: \
case Instruction::PUSH6: \
case Instruction::PUSH7: \
case Instruction::PUSH8: \
case Instruction::PUSH9: \
case Instruction::PUSH10: \
case Instruction::PUSH11: \
case Instruction::PUSH12: \
case Instruction::PUSH13: \
case Instruction::PUSH14: \
case Instruction::PUSH15: \
case Instruction::PUSH16: \
case Instruction::PUSH17: \
case Instruction::PUSH18: \
case Instruction::PUSH19: \
case Instruction::PUSH20: \
case Instruction::PUSH21: \
case Instruction::PUSH22: \
case Instruction::PUSH23: \
case Instruction::PUSH24: \
case Instruction::PUSH25: \
case Instruction::PUSH26: \
case Instruction::PUSH27: \
case Instruction::PUSH28: \
case Instruction::PUSH29: \
case Instruction::PUSH30: \
case Instruction::PUSH31: \
case Instruction::PUSH32
#define ANY_DUP DUP1: \
case Instruction::DUP2: \
case Instruction::DUP3: \
case Instruction::DUP4: \
case Instruction::DUP5: \
case Instruction::DUP6: \
case Instruction::DUP7: \
case Instruction::DUP8: \
case Instruction::DUP9: \
case Instruction::DUP10: \
case Instruction::DUP11: \
case Instruction::DUP12: \
case Instruction::DUP13: \
case Instruction::DUP14: \
case Instruction::DUP15: \
case Instruction::DUP16
#define ANY_SWAP SWAP1: \
case Instruction::SWAP2: \
case Instruction::SWAP3: \
case Instruction::SWAP4: \
case Instruction::SWAP5: \
case Instruction::SWAP6: \
case Instruction::SWAP7: \
case Instruction::SWAP8: \
case Instruction::SWAP9: \
case Instruction::SWAP10: \
case Instruction::SWAP11: \
case Instruction::SWAP12: \
case Instruction::SWAP13: \
case Instruction::SWAP14: \
case Instruction::SWAP15: \
case Instruction::SWAP16
}
}

48
evmjit/libevmjit/JIT-c.cpp

@ -1,48 +0,0 @@
#include <evmjit/JIT-c.h>
#include <cassert>
#include <evmjit/JIT.h>
extern "C"
{
using namespace dev::evmjit;
evmjit_context* evmjit_create(evmjit_runtime_data* _data, void* _env)
{
auto data = reinterpret_cast<RuntimeData*>(_data);
auto env = reinterpret_cast<Env*>(_env);
assert(!data && "Pointer to runtime data must not be null");
if (!data)
return nullptr;
// TODO: Make sure ExecutionEngine constructor does not throw + make JIT/ExecutionEngine interface all nothrow
auto context = new(std::nothrow) ExecutionContext{*data, env};
return reinterpret_cast<evmjit_context*>(context);
}
void evmjit_destroy(evmjit_context* _context)
{
auto context = reinterpret_cast<ExecutionContext*>(_context);
delete context;
}
evmjit_return_code evmjit_exec(evmjit_context* _context)
{
auto context = reinterpret_cast<ExecutionContext*>(_context);
assert(!context && "Invalid context");
if (!context)
return UnexpectedException;
try
{
auto returnCode = JIT::exec(*context);
return static_cast<evmjit_return_code>(returnCode);
}
catch(...)
{
return UnexpectedException;
}
}
}

252
evmjit/libevmjit/JIT.cpp

@ -1,252 +0,0 @@
#include "evmjit/JIT.h"
#include <array>
#include <mutex>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include <llvm/ADT/Triple.h>
#include <llvm/ExecutionEngine/MCJIT.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/Host.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/ManagedStatic.h>
#include "preprocessor/llvm_includes_end.h"
#include "Compiler.h"
#include "Optimizer.h"
#include "Cache.h"
#include "ExecStats.h"
#include "Utils.h"
#include "BuildInfo.gen.h"
namespace dev
{
namespace evmjit
{
using namespace eth::jit;
namespace
{
using ExecFunc = ReturnCode(*)(ExecutionContext*);
std::string hash2str(i256 const& _hash)
{
static const auto size = sizeof(_hash);
static const auto hexChars = "0123456789abcdef";
std::string str;
str.resize(size * 2);
auto outIt = str.rbegin(); // reverse for BE
auto& arr = *(std::array<byte, size>*)&_hash;
for (auto b : arr)
{
*(outIt++) = hexChars[b & 0xf];
*(outIt++) = hexChars[b >> 4];
}
return str;
}
void printVersion()
{
std::cout << "Ethereum EVM JIT Compiler (http://github.com/ethereum/evmjit):\n"
<< " EVMJIT version " << EVMJIT_VERSION << "\n"
#ifdef NDEBUG
<< " Optimized build, "
#else
<< " DEBUG build, "
#endif
<< __DATE__ << " (" << __TIME__ << ")\n"
<< std::endl;
}
namespace cl = llvm::cl;
cl::opt<bool> g_optimize{"O", cl::desc{"Optimize"}};
cl::opt<CacheMode> g_cache{"cache", cl::desc{"Cache compiled EVM code on disk"},
cl::values(
clEnumValN(CacheMode::on, "1", "Enabled"),
clEnumValN(CacheMode::off, "0", "Disabled"),
clEnumValN(CacheMode::read, "r", "Read only. No new objects are added to cache."),
clEnumValN(CacheMode::write, "w", "Write only. No objects are loaded from cache."),
clEnumValN(CacheMode::clear, "c", "Clear the cache storage. Cache is disabled."),
clEnumValN(CacheMode::preload, "p", "Preload all cached objects."),
clEnumValEnd)};
cl::opt<bool> g_stats{"st", cl::desc{"Statistics"}};
cl::opt<bool> g_dump{"dump", cl::desc{"Dump LLVM IR module"}};
void parseOptions()
{
static llvm::llvm_shutdown_obj shutdownObj{};
cl::AddExtraVersionPrinter(printVersion);
cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler");
}
class JITImpl
{
std::unique_ptr<llvm::ExecutionEngine> m_engine;
mutable std::mutex x_codeMap;
std::unordered_map<h256, ExecFunc> m_codeMap;
public:
static JITImpl& instance()
{
static JITImpl s_instance;
return s_instance;
}
JITImpl();
llvm::ExecutionEngine& engine() { return *m_engine; }
ExecFunc getExecFunc(h256 const& _codeHash) const;
void mapExecFunc(h256 _codeHash, ExecFunc _funcAddr);
ExecFunc compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash);
};
JITImpl::JITImpl()
{
parseOptions();
bool preloadCache = g_cache == CacheMode::preload;
if (preloadCache)
g_cache = CacheMode::on;
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
auto module = std::unique_ptr<llvm::Module>(new llvm::Module({}, llvm::getGlobalContext()));
// FIXME: LLVM 3.7: test on Windows
auto triple = llvm::Triple(llvm::sys::getProcessTriple());
if (triple.getOS() == llvm::Triple::OSType::Win32)
triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format
module->setTargetTriple(triple.str());
llvm::EngineBuilder builder(std::move(module));
builder.setEngineKind(llvm::EngineKind::JIT);
builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None);
m_engine.reset(builder.create());
// TODO: Update cache listener
m_engine->setObjectCache(Cache::init(g_cache, nullptr));
// FIXME: Disabled during API changes
//if (preloadCache)
// Cache::preload(*m_engine, funcCache);
}
ExecFunc JITImpl::getExecFunc(h256 const& _codeHash) const
{
std::lock_guard<std::mutex> lock{x_codeMap};
auto it = m_codeMap.find(_codeHash);
if (it != m_codeMap.end())
return it->second;
return nullptr;
}
void JITImpl::mapExecFunc(h256 _codeHash, ExecFunc _funcAddr)
{
std::lock_guard<std::mutex> lock{x_codeMap};
m_codeMap.emplace(std::move(_codeHash), _funcAddr);
}
ExecFunc JITImpl::compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash)
{
auto name = hash2str(_codeHash);
auto module = Cache::getObject(name);
if (!module)
{
// TODO: Listener support must be redesigned. These should be a feature of JITImpl
//listener->stateChanged(ExecState::Compilation);
assert(_code || !_codeSize); //TODO: Is it good idea to execute empty code?
module = Compiler{{}}.compile(_code, _code + _codeSize, name);
if (g_optimize)
{
//listener->stateChanged(ExecState::Optimization);
optimize(*module);
}
prepare(*module);
}
if (g_dump)
module->dump();
m_engine->addModule(std::move(module));
//listener->stateChanged(ExecState::CodeGen);
return (ExecFunc)m_engine->getFunctionAddress(name);
}
} // anonymous namespace
bool JIT::isCodeReady(h256 const& _codeHash)
{
return JITImpl::instance().getExecFunc(_codeHash) != nullptr;
}
void JIT::compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash)
{
auto& jit = JITImpl::instance();
auto execFunc = jit.compile(_code, _codeSize, _codeHash);
if (execFunc) // FIXME: What with error?
jit.mapExecFunc(_codeHash, execFunc);
}
ReturnCode JIT::exec(ExecutionContext& _context)
{
//std::unique_ptr<ExecStats> listener{new ExecStats};
//listener->stateChanged(ExecState::Started);
//static StatsCollector statsCollector;
auto& jit = JITImpl::instance();
auto codeHash = _context.codeHash();
auto execFunc = jit.getExecFunc(codeHash);
if (!execFunc)
{
execFunc = jit.compile(_context.code(), _context.codeSize(), codeHash);
if (!execFunc)
return ReturnCode::LLVMError;
jit.mapExecFunc(codeHash, execFunc);
}
//listener->stateChanged(ExecState::Execution);
auto returnCode = execFunc(&_context);
//listener->stateChanged(ExecState::Return);
if (returnCode == ReturnCode::Return)
_context.returnData = _context.getReturnData(); // Save reference to return data
//listener->stateChanged(ExecState::Finished);
// if (g_stats)
// statsCollector.stats.push_back(std::move(listener));
return returnCode;
}
extern "C" void ext_free(void* _data) noexcept;
ExecutionContext::~ExecutionContext() noexcept
{
if (m_memData)
ext_free(m_memData); // Use helper free to check memory leaks
}
bytes_ref ExecutionContext::getReturnData() const
{
auto data = m_data->callData;
auto size = static_cast<size_t>(m_data->callDataSize);
if (data < m_memData || data >= m_memData + m_memSize || size == 0)
{
assert(size == 0); // data can be an invalid pointer only if size is 0
m_data->callData = nullptr;
return {};
}
return bytes_ref{data, size};
}
}
}

246
evmjit/libevmjit/Memory.cpp

@ -1,246 +0,0 @@
#include "Memory.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "Type.h"
#include "GasMeter.h"
#include "Endianness.h"
#include "RuntimeManager.h"
namespace dev
{
namespace eth
{
namespace jit
{
Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter):
RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed
m_memory{getBuilder(), _runtimeManager.getMem()},
m_gasMeter(_gasMeter)
{}
llvm::Function* Memory::getRequireFunc()
{
auto& func = m_require;
if (!func)
{
llvm::Type* argTypes[] = {Array::getType()->getPointerTo(), Type::Word, Type::Word, Type::BytePtr, Type::GasPtr};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule());
func->setDoesNotThrow();
auto mem = &func->getArgumentList().front();
mem->setName("mem");
auto blkOffset = mem->getNextNode();
blkOffset->setName("blkOffset");
auto blkSize = blkOffset->getNextNode();
blkSize->setName("blkSize");
auto jmpBuf = blkSize->getNextNode();
jmpBuf->setName("jmpBuf");
auto gas = jmpBuf->getNextNode();
gas->setName("gas");
auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func);
auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func);
auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func);
auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func);
InsertPointGuard guard(m_builder); // Restores insert point at function exit
// BB "Pre": Ignore checks with size 0
m_builder.SetInsertPoint(preBB);
m_builder.CreateCondBr(m_builder.CreateICmpNE(blkSize, Constant::get(0)), checkBB, returnBB, Type::expectTrue);
// BB "Check"
m_builder.SetInsertPoint(checkBB);
static const auto c_inputMax = uint64_t(1) << 33; // max value of blkSize and blkOffset that will not result in integer overflow in calculations below
auto blkOffsetOk = m_builder.CreateICmpULE(blkOffset, Constant::get(c_inputMax), "blkOffsetOk");
auto blkO = m_builder.CreateSelect(blkOffsetOk, m_builder.CreateTrunc(blkOffset, Type::Size), m_builder.getInt64(c_inputMax), "bklO");
auto blkSizeOk = m_builder.CreateICmpULE(blkSize, Constant::get(c_inputMax), "blkSizeOk");
auto blkS = m_builder.CreateSelect(blkSizeOk, m_builder.CreateTrunc(blkSize, Type::Size), m_builder.getInt64(c_inputMax), "bklS");
auto sizeReq0 = m_builder.CreateNUWAdd(blkO, blkS, "sizeReq0");
auto sizeReq = m_builder.CreateAnd(m_builder.CreateNUWAdd(sizeReq0, m_builder.getInt64(31)), uint64_t(-1) << 5, "sizeReq"); // s' = ((s0 + 31) / 32) * 32
auto sizeCur = m_memory.size(mem);
auto sizeOk = m_builder.CreateICmpULE(sizeReq, sizeCur, "sizeOk");
m_builder.CreateCondBr(sizeOk, returnBB, resizeBB, Type::expectTrue);
// BB "Resize"
m_builder.SetInsertPoint(resizeBB);
// Check gas first
auto w1 = m_builder.CreateLShr(sizeReq, 5);
auto w1s = m_builder.CreateNUWMul(w1, w1);
auto c1 = m_builder.CreateAdd(m_builder.CreateNUWMul(w1, m_builder.getInt64(3)), m_builder.CreateLShr(w1s, 9));
auto w0 = m_builder.CreateLShr(sizeCur, 5);
auto w0s = m_builder.CreateNUWMul(w0, w0);
auto c0 = m_builder.CreateAdd(m_builder.CreateNUWMul(w0, m_builder.getInt64(3)), m_builder.CreateLShr(w0s, 9));
auto cc = m_builder.CreateNUWSub(c1, c0);
auto costOk = m_builder.CreateAnd(blkOffsetOk, blkSizeOk, "costOk");
auto c = m_builder.CreateSelect(costOk, cc, m_builder.getInt64(std::numeric_limits<int64_t>::max()), "c");
m_gasMeter.count(c, jmpBuf, gas);
// Resize
m_memory.extend(mem, sizeReq);
m_builder.CreateBr(returnBB);
// BB "Return"
m_builder.SetInsertPoint(returnBB);
m_builder.CreateRetVoid();
}
return func;
}
llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType)
{
auto isWord = _valueType == Type::Word;
llvm::Type* storeArgs[] = {Array::getType()->getPointerTo(), Type::Word, _valueType};
llvm::Type* loadArgs[] = {Array::getType()->getPointerTo(), Type::Word};
auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload";
auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::Word, loadArgs, false);
auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule());
InsertPointGuard guard(m_builder); // Restores insert point at function exit
m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func));
auto mem = &func->getArgumentList().front();
mem->setName("mem");
auto index = mem->getNextNode();
index->setName("index");
if (_isStore)
{
auto valueArg = index->getNextNode();
valueArg->setName("value");
auto value = isWord ? Endianness::toBE(m_builder, valueArg) : valueArg;
auto memPtr = m_memory.getPtr(mem, m_builder.CreateTrunc(index, Type::Size));
auto valuePtr = m_builder.CreateBitCast(memPtr, _valueType->getPointerTo(), "valuePtr");
m_builder.CreateStore(value, valuePtr);
m_builder.CreateRetVoid();
}
else
{
auto memPtr = m_memory.getPtr(mem, m_builder.CreateTrunc(index, Type::Size));
llvm::Value* ret = m_builder.CreateLoad(memPtr);
ret = Endianness::toNative(m_builder, ret);
m_builder.CreateRet(ret);
}
return func;
}
llvm::Function* Memory::getLoadWordFunc()
{
auto& func = m_loadWord;
if (!func)
func = createFunc(false, Type::Word);
return func;
}
llvm::Function* Memory::getStoreWordFunc()
{
auto& func = m_storeWord;
if (!func)
func = createFunc(true, Type::Word);
return func;
}
llvm::Function* Memory::getStoreByteFunc()
{
auto& func = m_storeByte;
if (!func)
func = createFunc(true, Type::Byte);
return func;
}
llvm::Value* Memory::loadWord(llvm::Value* _addr)
{
require(_addr, Constant::get(Type::Word->getPrimitiveSizeInBits() / 8));
return createCall(getLoadWordFunc(), {getRuntimeManager().getMem(), _addr});
}
void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word)
{
require(_addr, Constant::get(Type::Word->getPrimitiveSizeInBits() / 8));
createCall(getStoreWordFunc(), {getRuntimeManager().getMem(), _addr, _word});
}
void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word)
{
require(_addr, Constant::get(Type::Byte->getPrimitiveSizeInBits() / 8));
auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte");
createCall(getStoreByteFunc(), {getRuntimeManager().getMem(), _addr, byte});
}
llvm::Value* Memory::getData()
{
auto memPtr = m_builder.CreateBitCast(getRuntimeManager().getMem(), Type::BytePtr->getPointerTo());
auto data = m_builder.CreateLoad(memPtr, "data");
assert(data->getType() == Type::BytePtr);
return data;
}
llvm::Value* Memory::getSize()
{
return m_builder.CreateZExt(m_memory.size(), Type::Word, "msize"); // TODO: Allow placing i64 on stack
}
llvm::Value* Memory::getBytePtr(llvm::Value* _index)
{
return m_builder.CreateGEP(getData(), _index, "ptr");
}
void Memory::require(llvm::Value* _offset, llvm::Value* _size)
{
if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(_size))
{
if (!constant->getValue())
return;
}
createCall(getRequireFunc(), {getRuntimeManager().getMem(), _offset, _size, getRuntimeManager().getJmpBuf(), getRuntimeManager().getGasPtr()});
}
void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx,
llvm::Value* _destMemIdx, llvm::Value* _reqBytes)
{
require(_destMemIdx, _reqBytes);
// Additional copy cost
// TODO: This round ups to 32 happens in many places
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);
// Algorithm:
// isOutsideData = idx256 >= size256
// idx64 = trunc idx256
// size64 = trunc size256
// dataLeftSize = size64 - idx64 // safe if not isOutsideData
// reqBytes64 = trunc _reqBytes // require() handles large values
// bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min
// bytesToCopy = select(isOutsideData, 0, bytesToCopy0)
auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize);
auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::Size);
auto size64 = m_builder.CreateTrunc(_srcSize, Type::Size);
auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64);
auto outOfBound = m_builder.CreateICmpUGT(reqBytes, dataLeftSize);
auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes);
auto bytesToCopy = m_builder.CreateSelect(isOutsideData, m_builder.getInt64(0), bytesToCopyInner, "bytesToCopy");
auto bytesToZero = m_builder.CreateNUWSub(reqBytes, bytesToCopy, "bytesToZero");
auto src = m_builder.CreateGEP(_srcPtr, idx64, "src");
auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx");
auto padIdx = m_builder.CreateNUWAdd(dstIdx, bytesToCopy, "padIdx");
auto dst = m_memory.getPtr(getRuntimeManager().getMem(), dstIdx);
auto pad = m_memory.getPtr(getRuntimeManager().getMem(), padIdx);
m_builder.CreateMemCpy(dst, src, bytesToCopy, 0);
m_builder.CreateMemSet(pad, m_builder.getInt8(0), bytesToZero, 0);
}
}
}
}

51
evmjit/libevmjit/Memory.h

@ -1,51 +0,0 @@
#pragma once
#include "Array.h"
namespace dev
{
namespace eth
{
namespace jit
{
class GasMeter;
class Memory : public RuntimeHelper
{
public:
Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter);
llvm::Value* loadWord(llvm::Value* _addr);
void storeWord(llvm::Value* _addr, llvm::Value* _word);
void storeByte(llvm::Value* _addr, llvm::Value* _byte);
llvm::Value* getData();
llvm::Value* getSize();
llvm::Value* getBytePtr(llvm::Value* _index);
void copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIndex,
llvm::Value* _destMemIdx, llvm::Value* _byteCount);
/// Requires the amount of memory to for data defined by offset and size. And counts gas fee for that memory.
void require(llvm::Value* _offset, llvm::Value* _size);
private:
Array m_memory;
GasMeter& m_gasMeter;
llvm::Function* createFunc(bool _isStore, llvm::Type* _type);
llvm::Function* getRequireFunc();
llvm::Function* getLoadWordFunc();
llvm::Function* getStoreWordFunc();
llvm::Function* getStoreByteFunc();
llvm::Function* m_require = nullptr;
llvm::Function* m_loadWord = nullptr;
llvm::Function* m_storeWord = nullptr;
llvm::Function* m_storeByte = nullptr;
};
}
}
}

125
evmjit/libevmjit/Optimizer.cpp

@ -1,125 +0,0 @@
#include "Optimizer.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/BasicBlock.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/IPO.h>
#include "preprocessor/llvm_includes_end.h"
#include "Arith256.h"
#include "Type.h"
namespace dev
{
namespace eth
{
namespace jit
{
bool optimize(llvm::Module& _module)
{
auto pm = llvm::legacy::PassManager{};
pm.add(llvm::createFunctionInliningPass(2, 2));
pm.add(llvm::createCFGSimplificationPass());
pm.add(llvm::createInstructionCombiningPass());
pm.add(llvm::createAggressiveDCEPass());
pm.add(llvm::createLowerSwitchPass());
return pm.run(_module);
}
namespace
{
class LowerEVMPass: public llvm::BasicBlockPass
{
static char ID;
public:
LowerEVMPass():
llvm::BasicBlockPass(ID)
{}
virtual bool runOnBasicBlock(llvm::BasicBlock& _bb) override;
using llvm::BasicBlockPass::doFinalization;
virtual bool doFinalization(llvm::Module& _module) override;
};
char LowerEVMPass::ID = 0;
bool LowerEVMPass::runOnBasicBlock(llvm::BasicBlock& _bb)
{
auto modified = false;
auto module = _bb.getParent()->getParent();
auto i512Ty = llvm::IntegerType::get(_bb.getContext(), 512);
for (auto it = _bb.begin(); it != _bb.end(); )
{
auto& inst = *it++;
llvm::Function* func = nullptr;
if (inst.getType() == Type::Word)
{
switch (inst.getOpcode())
{
case llvm::Instruction::Mul:
func = Arith256::getMulFunc(*module);
break;
case llvm::Instruction::UDiv:
func = Arith256::getUDiv256Func(*module);
break;
case llvm::Instruction::URem:
func = Arith256::getURem256Func(*module);
break;
case llvm::Instruction::SDiv:
func = Arith256::getSDiv256Func(*module);
break;
case llvm::Instruction::SRem:
func = Arith256::getSRem256Func(*module);
break;
}
}
else if (inst.getType() == i512Ty)
{
switch (inst.getOpcode())
{
case llvm::Instruction::URem:
func = Arith256::getURem512Func(*module);
break;
}
}
if (func)
{
auto call = llvm::CallInst::Create(func, {inst.getOperand(0), inst.getOperand(1)}, "", &inst);
inst.replaceAllUsesWith(call);
inst.eraseFromParent();
modified = true;
}
}
return modified;
}
bool LowerEVMPass::doFinalization(llvm::Module&)
{
return false;
}
}
bool prepare(llvm::Module& _module)
{
auto pm = llvm::legacy::PassManager{};
pm.add(llvm::createCFGSimplificationPass());
pm.add(llvm::createDeadCodeEliminationPass());
pm.add(new LowerEVMPass{});
return pm.run(_module);
}
}
}
}

21
evmjit/libevmjit/Optimizer.h

@ -1,21 +0,0 @@
#pragma once
namespace llvm
{
class Module;
}
namespace dev
{
namespace eth
{
namespace jit
{
bool optimize(llvm::Module& _module);
bool prepare(llvm::Module& _module);
}
}
}

302
evmjit/libevmjit/RuntimeManager.cpp

@ -1,302 +0,0 @@
#include "RuntimeManager.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "Stack.h"
#include "Utils.h"
namespace dev
{
namespace eth
{
namespace jit
{
llvm::StructType* RuntimeManager::getRuntimeDataType()
{
static llvm::StructType* type = nullptr;
if (!type)
{
llvm::Type* elems[] =
{
Type::Size, // gas
Type::Size, // gasPrice
Type::BytePtr, // callData
Type::Size, // callDataSize
Type::Word, // address
Type::Word, // caller
Type::Word, // origin
Type::Word, // callValue
Type::Word, // coinBase
Type::Word, // difficulty
Type::Word, // gasLimit
Type::Size, // blockNumber
Type::Size, // blockTimestamp
Type::BytePtr, // code
Type::Size, // codeSize
};
type = llvm::StructType::create(elems, "RuntimeData");
}
return type;
}
llvm::StructType* RuntimeManager::getRuntimeType()
{
static llvm::StructType* type = nullptr;
if (!type)
{
llvm::Type* elems[] =
{
Type::RuntimeDataPtr, // data
Type::EnvPtr, // Env*
Array::getType() // memory
};
type = llvm::StructType::create(elems, "Runtime");
}
return type;
}
namespace
{
llvm::Twine getName(RuntimeData::Index _index)
{
switch (_index)
{
default: return "";
case RuntimeData::Gas: return "msg.gas";
case RuntimeData::GasPrice: return "tx.gasprice";
case RuntimeData::CallData: return "msg.data.ptr";
case RuntimeData::CallDataSize: return "msg.data.size";
case RuntimeData::Address: return "this.address";
case RuntimeData::Caller: return "msg.caller";
case RuntimeData::Origin: return "tx.origin";
case RuntimeData::CallValue: return "msg.value";
case RuntimeData::CoinBase: return "block.coinbase";
case RuntimeData::Difficulty: return "block.difficulty";
case RuntimeData::GasLimit: return "block.gaslimit";
case RuntimeData::Number: return "block.number";
case RuntimeData::Timestamp: return "block.timestamp";
case RuntimeData::Code: return "code.ptr";
case RuntimeData::CodeSize: return "code.size";
}
}
}
RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeBegin, code_iterator _codeEnd):
CompilerHelper(_builder),
m_codeBegin(_codeBegin),
m_codeEnd(_codeEnd)
{
m_longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp);
// Unpack data
auto rtPtr = getRuntimePtr();
m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "dataPtr");
assert(m_dataPtr->getType() == Type::RuntimeDataPtr);
m_memPtr = m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 2, "mem");
assert(m_memPtr->getType() == Array::getType()->getPointerTo());
m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 1), "env");
assert(m_envPtr->getType() == Type::EnvPtr);
m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize");
m_builder.CreateStore(m_builder.getInt64(0), m_stackSize);
auto data = m_builder.CreateLoad(m_dataPtr, "data");
for (unsigned i = 0; i < m_dataElts.size(); ++i)
m_dataElts[i] = m_builder.CreateExtractValue(data, i, getName(RuntimeData::Index(i)));
m_gasPtr = m_builder.CreateAlloca(Type::Gas, nullptr, "gas.ptr");
m_builder.CreateStore(m_dataElts[RuntimeData::Index::Gas], m_gasPtr);
llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::Size, Type::BytePtr};
m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "evm.stack.require", getModule());
m_checkStackLimit->setDoesNotThrow();
m_checkStackLimit->setDoesNotCapture(1);
auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_checkStackLimit);
auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_checkStackLimit);
auto outOfStackBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfStack", m_checkStackLimit);
auto currSizePtr = &m_checkStackLimit->getArgumentList().front();
currSizePtr->setName("currSize");
auto min = currSizePtr->getNextNode();
min->setName("min");
auto max = min->getNextNode();
max->setName("max");
auto diff = max->getNextNode();
diff->setName("diff");
auto jmpBuf = diff->getNextNode();
jmpBuf->setName("jmpBuf");
InsertPointGuard guard{m_builder};
m_builder.SetInsertPoint(checkBB);
auto currSize = m_builder.CreateLoad(currSizePtr, "cur");
auto minSize = m_builder.CreateAdd(currSize, min, "minSize", false, true);
auto maxSize = m_builder.CreateAdd(currSize, max, "maxSize", true, true);
auto minOk = m_builder.CreateICmpSGE(minSize, m_builder.getInt64(0), "min.ok");
auto maxOk = m_builder.CreateICmpULE(maxSize, m_builder.getInt64(1024), "max.ok");
auto ok = m_builder.CreateAnd(minOk, maxOk, "ok");
m_builder.CreateCondBr(ok, updateBB, outOfStackBB, Type::expectTrue);
m_builder.SetInsertPoint(updateBB);
auto newSize = m_builder.CreateNSWAdd(currSize, diff);
m_builder.CreateStore(newSize, currSizePtr);
m_builder.CreateRetVoid();
m_builder.SetInsertPoint(outOfStackBB);
abort(jmpBuf);
m_builder.CreateUnreachable();
}
void RuntimeManager::checkStackLimit(ssize_t _min, ssize_t _max, ssize_t _diff)
{
createCall(m_checkStackLimit, {m_stackSize, m_builder.getInt64(_min), m_builder.getInt64(_max), m_builder.getInt64(_diff), getJmpBuf()});
}
llvm::Value* RuntimeManager::getRuntimePtr()
{
// Expect first argument of a function to be a pointer to Runtime
auto func = m_builder.GetInsertBlock()->getParent();
auto rtPtr = &func->getArgumentList().front();
assert(rtPtr->getType() == Type::RuntimePtr);
return rtPtr;
}
llvm::Value* RuntimeManager::getDataPtr()
{
if (getMainFunction())
return m_dataPtr;
auto rtPtr = getRuntimePtr();
auto dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "data");
assert(dataPtr->getType() == getRuntimeDataType()->getPointerTo());
return dataPtr;
}
llvm::Value* RuntimeManager::getEnvPtr()
{
assert(getMainFunction()); // Available only in main function
return m_envPtr;
}
llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index)
{
auto ptr = getBuilder().CreateStructGEP(getRuntimeDataType(), getDataPtr(), _index);
assert(getRuntimeDataType()->getElementType(_index)->getPointerTo() == ptr->getType());
return ptr;
}
llvm::Value* RuntimeManager::get(RuntimeData::Index _index)
{
return m_dataElts[_index];
}
void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value)
{
auto ptr = getPtr(_index);
assert(ptr->getType() == _value->getType()->getPointerTo());
getBuilder().CreateStore(_value, ptr);
}
void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size)
{
auto memPtr = m_builder.CreateBitCast(getMem(), Type::BytePtr->getPointerTo());
auto mem = getBuilder().CreateLoad(memPtr, "memory");
auto returnDataPtr = getBuilder().CreateGEP(mem, _offset);
set(RuntimeData::ReturnData, returnDataPtr);
auto size64 = getBuilder().CreateTrunc(_size, Type::Size);
set(RuntimeData::ReturnDataSize, size64);
}
void RuntimeManager::registerSuicide(llvm::Value* _balanceAddress)
{
set(RuntimeData::SuicideDestAddress, _balanceAddress);
}
void RuntimeManager::exit(ReturnCode _returnCode)
{
if (m_stack)
m_stack->free();
auto extGasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), getDataPtr(), RuntimeData::Index::Gas, "msg.gas.ptr");
m_builder.CreateStore(getGas(), extGasPtr);
m_builder.CreateRet(Constant::get(_returnCode));
}
void RuntimeManager::abort(llvm::Value* _jmpBuf)
{
auto longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp);
createCall(longjmp, {_jmpBuf});
}
llvm::Value* RuntimeManager::get(Instruction _inst)
{
switch (_inst)
{
default: assert(false); return nullptr;
case Instruction::ADDRESS: return get(RuntimeData::Address);
case Instruction::CALLER: return get(RuntimeData::Caller);
case Instruction::ORIGIN: return get(RuntimeData::Origin);
case Instruction::CALLVALUE: return get(RuntimeData::CallValue);
case Instruction::GASPRICE: return get(RuntimeData::GasPrice);
case Instruction::COINBASE: return get(RuntimeData::CoinBase);
case Instruction::DIFFICULTY: return get(RuntimeData::Difficulty);
case Instruction::GASLIMIT: return get(RuntimeData::GasLimit);
case Instruction::NUMBER: return get(RuntimeData::Number);
case Instruction::TIMESTAMP: return get(RuntimeData::Timestamp);
}
}
llvm::Value* RuntimeManager::getCallData()
{
return get(RuntimeData::CallData);
}
llvm::Value* RuntimeManager::getCode()
{
// 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()
{
return Constant::get(m_codeEnd - m_codeBegin);
}
llvm::Value* RuntimeManager::getCallDataSize()
{
auto value = get(RuntimeData::CallDataSize);
assert(value->getType() == Type::Size);
return getBuilder().CreateZExt(value, Type::Word);
}
llvm::Value* RuntimeManager::getGas()
{
return getBuilder().CreateLoad(getGasPtr(), "gas");
}
llvm::Value* RuntimeManager::getGasPtr()
{
assert(getMainFunction());
return m_gasPtr;
}
llvm::Value* RuntimeManager::getMem()
{
assert(getMainFunction());
return m_memPtr;
}
void RuntimeManager::setGas(llvm::Value* _gas)
{
assert(_gas->getType() == Type::Gas);
getBuilder().CreateStore(_gas, getGasPtr());
}
}
}
}

79
evmjit/libevmjit/RuntimeManager.h

@ -1,79 +0,0 @@
#pragma once
#include <array>
#include "CompilerHelper.h"
#include "Type.h"
#include "Instruction.h"
namespace dev
{
namespace eth
{
namespace jit
{
using namespace evmjit;
class Stack;
class RuntimeManager: public CompilerHelper
{
public:
RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeBegin, code_iterator _codeEnd);
llvm::Value* getRuntimePtr();
llvm::Value* getDataPtr();
llvm::Value* getEnvPtr();
llvm::Value* get(RuntimeData::Index _index);
llvm::Value* get(Instruction _inst);
llvm::Value* getGas();
llvm::Value* getGasPtr();
llvm::Value* getCallData();
llvm::Value* getCode();
llvm::Value* getCodeSize();
llvm::Value* getCallDataSize();
llvm::Value* getJmpBuf() { return m_jmpBuf; }
void setGas(llvm::Value* _gas);
llvm::Value* getMem();
void registerReturnData(llvm::Value* _index, llvm::Value* _size); // TODO: Move to Memory.
void registerSuicide(llvm::Value* _balanceAddress);
void exit(ReturnCode _returnCode);
void abort(llvm::Value* _jmpBuf);
void setStack(Stack& _stack) { m_stack = &_stack; }
void setJmpBuf(llvm::Value* _jmpBuf) { m_jmpBuf = _jmpBuf; }
static llvm::StructType* getRuntimeType();
static llvm::StructType* getRuntimeDataType();
void checkStackLimit(ssize_t _min, ssize_t _max, ssize_t _diff);
private:
llvm::Value* getPtr(RuntimeData::Index _index);
void set(RuntimeData::Index _index, llvm::Value* _value);
llvm::Function* m_longjmp = nullptr;
llvm::Value* m_jmpBuf = nullptr;
llvm::Value* m_dataPtr = nullptr;
llvm::Value* m_gasPtr = nullptr;
llvm::Value* m_memPtr = nullptr;
llvm::Value* m_envPtr = nullptr;
std::array<llvm::Value*, RuntimeData::numElements> m_dataElts;
llvm::Value* m_stackSize = nullptr;
llvm::Function* m_checkStackLimit = nullptr;
code_iterator m_codeBegin = {};
code_iterator m_codeEnd = {};
Stack* m_stack = nullptr;
};
}
}
}

44
evmjit/libevmjit/Stack.cpp

@ -1,44 +0,0 @@
#include "Stack.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Function.h>
#include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h"
#include "Utils.h"
namespace dev
{
namespace eth
{
namespace jit
{
Stack::Stack(llvm::IRBuilder<>& _builder):
CompilerHelper(_builder),
m_stack(_builder, "stack")
{}
llvm::Value* Stack::get(size_t _index)
{
return m_stack.get(m_builder.CreateSub(m_stack.size(), m_builder.getInt64(_index + 1)));
}
void Stack::set(size_t _index, llvm::Value* _value)
{
m_stack.set(m_builder.CreateSub(m_stack.size(), m_builder.getInt64(_index + 1)), _value);
}
void Stack::pop(size_t _count)
{
m_stack.pop(m_builder.getInt64(_count));
}
void Stack::push(llvm::Value* _value)
{
m_stack.push(_value);
}
}
}
}

30
evmjit/libevmjit/Stack.h

@ -1,30 +0,0 @@
#pragma once
#include "Array.h"
namespace dev
{
namespace eth
{
namespace jit
{
class Stack: public CompilerHelper
{
public:
Stack(llvm::IRBuilder<>& builder);
llvm::Value* get(size_t _index);
void set(size_t _index, llvm::Value* _value);
void pop(size_t _count);
void push(llvm::Value* _value);
void free() { m_stack.free(); }
private:
Array m_stack;
};
}
}
}

73
evmjit/libevmjit/Type.cpp

@ -1,73 +0,0 @@
#include "Type.h"
#include <llvm/IR/MDBuilder.h>
#include "RuntimeManager.h"
namespace dev
{
namespace eth
{
namespace jit
{
llvm::IntegerType* Type::Word;
llvm::PointerType* Type::WordPtr;
llvm::IntegerType* Type::Bool;
llvm::IntegerType* Type::Size;
llvm::IntegerType* Type::Gas;
llvm::PointerType* Type::GasPtr;
llvm::IntegerType* Type::Byte;
llvm::PointerType* Type::BytePtr;
llvm::Type* Type::Void;
llvm::IntegerType* Type::MainReturn;
llvm::PointerType* Type::EnvPtr;
llvm::PointerType* Type::RuntimeDataPtr;
llvm::PointerType* Type::RuntimePtr;
llvm::ConstantInt* Constant::gasMax;
llvm::MDNode* Type::expectTrue;
void Type::init(llvm::LLVMContext& _context)
{
if (!Word) // Do init only once
{
Word = llvm::Type::getIntNTy(_context, 256);
WordPtr = Word->getPointerTo();
Bool = llvm::Type::getInt1Ty(_context);
Size = llvm::Type::getInt64Ty(_context);
Gas = Size;
GasPtr = Gas->getPointerTo();
Byte = llvm::Type::getInt8Ty(_context);
BytePtr = Byte->getPointerTo();
Void = llvm::Type::getVoidTy(_context);
MainReturn = llvm::Type::getInt32Ty(_context);
EnvPtr = llvm::StructType::create(_context, "Env")->getPointerTo();
RuntimeDataPtr = RuntimeManager::getRuntimeDataType()->getPointerTo();
RuntimePtr = RuntimeManager::getRuntimeType()->getPointerTo();
Constant::gasMax = llvm::ConstantInt::getSigned(Type::Gas, std::numeric_limits<int64_t>::max());
expectTrue = llvm::MDBuilder{_context}.createBranchWeights(1, 0);
}
}
llvm::ConstantInt* Constant::get(int64_t _n)
{
return llvm::ConstantInt::getSigned(Type::Word, _n);
}
llvm::ConstantInt* Constant::get(llvm::APInt const& _n)
{
return llvm::ConstantInt::get(Type::Word->getContext(), _n);
}
llvm::ConstantInt* Constant::get(ReturnCode _returnCode)
{
return llvm::ConstantInt::get(Type::MainReturn, static_cast<uint64_t>(_returnCode));
}
}
}
}

61
evmjit/libevmjit/Type.h

@ -1,61 +0,0 @@
#pragma once
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Type.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/Metadata.h>
#include "preprocessor/llvm_includes_end.h"
#include "evmjit/JIT.h" // ReturnCode
namespace dev
{
namespace eth
{
namespace jit
{
using namespace evmjit;
struct Type
{
static llvm::IntegerType* Word;
static llvm::PointerType* WordPtr;
static llvm::IntegerType* Bool;
static llvm::IntegerType* Size;
static llvm::IntegerType* Gas;
static llvm::PointerType* GasPtr;
static llvm::IntegerType* Byte;
static llvm::PointerType* BytePtr;
static llvm::Type* Void;
/// Main function return type
static llvm::IntegerType* MainReturn;
static llvm::PointerType* EnvPtr;
static llvm::PointerType* RuntimeDataPtr;
static llvm::PointerType* RuntimePtr;
// TODO: Redesign static LLVM objects
static llvm::MDNode* expectTrue;
static void init(llvm::LLVMContext& _context);
};
struct Constant
{
static llvm::ConstantInt* gasMax;
/// Returns word-size constant
static llvm::ConstantInt* get(int64_t _n);
static llvm::ConstantInt* get(llvm::APInt const& _n);
static llvm::ConstantInt* get(ReturnCode _returnCode);
};
}
}
}

27
evmjit/libevmjit/Utils.cpp

@ -1,27 +0,0 @@
#include "Utils.h"
#include <llvm/Support/Debug.h>
#include "BuildInfo.gen.h"
#if !defined(NDEBUG) // Debug
namespace dev
{
namespace evmjit
{
std::ostream& getLogStream(char const* _channel)
{
static std::ostream nullStream{nullptr};
#if LLVM_DEBUG
return (llvm::DebugFlag && llvm::isCurrentDebugType(_channel)) ? std::cerr : nullStream;
#else
return (void)_channel, nullStream;
#endif
}
}
}
#endif

40
evmjit/libevmjit/Utils.h

@ -1,40 +0,0 @@
#pragma once
#include <iostream>
// The same as assert, but expression is always evaluated and result returned
#define CHECK(expr) (assert(expr), expr)
#if !defined(NDEBUG) // Debug
namespace dev
{
namespace evmjit
{
std::ostream& getLogStream(char const* _channel);
}
}
#define DLOG(CHANNEL) ::dev::evmjit::getLogStream(#CHANNEL)
#else // Release
namespace dev
{
namespace evmjit
{
struct Voider
{
void operator=(std::ostream const&) {}
};
}
}
#define DLOG(CHANNEL) true ? (void)0 : ::dev::evmjit::Voider{} = std::cerr
#endif

7
evmjit/libevmjit/preprocessor/llvm_includes_end.h

@ -1,7 +0,0 @@
#if defined(_MSC_VER)
#pragma warning(pop)
#elif defined(__clang__)
#pragma clang diagnostic pop
#else
#pragma GCC diagnostic pop
#endif

12
evmjit/libevmjit/preprocessor/llvm_includes_start.h

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

38
exp/CMakeLists.txt

@ -1,38 +0,0 @@
cmake_policy(SET CMP0015 NEW)
set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..)
include_directories(${DB_INCLUDE_DIRS})
set(EXECUTABLE exp)
add_executable(${EXECUTABLE} ${SRC_LIST})
target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES})
if (READLINE_FOUND)
target_link_libraries(${EXECUTABLE} ${READLINE_LIBRARIES})
endif()
if (JSONRPC)
target_link_libraries(${EXECUTABLE} web3jsonrpc)
endif()
target_link_libraries(${EXECUTABLE} webthree)
target_link_libraries(${EXECUTABLE} ethereum)
target_link_libraries(${EXECUTABLE} p2p)
if (ETHASHCL)
# target_link_libraries(${EXECUTABLE} ethash-cl)
# target_link_libraries(${EXECUTABLE} ethash)
# target_link_libraries(${EXECUTABLE} OpenCL)
endif()
target_link_libraries(${EXECUTABLE} ethcore)
install( TARGETS ${EXECUTABLE} DESTINATION bin)

402
exp/main.cpp

@ -1,402 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file main.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
* Ethereum client.
*/
#if ETH_ETHASHCL
#define __CL_ENABLE_EXCEPTIONS
#define CL_USE_DEPRECATED_OPENCL_2_0_APIS
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#include <libethash-cl/cl.hpp>
#pragma clang diagnostic pop
#else
#include <libethash-cl/cl.hpp>
#endif
#endif
#include <functional>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#if 0
#include <libdevcore/TrieDB.h>
#include <libdevcore/TrieHash.h>
#include <libdevcore/RangeMask.h>
#include <libdevcore/Log.h>
#include <libdevcore/Common.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/RLP.h>
#include <libdevcore/TransientDirectory.h>
#include <libdevcore/CommonIO.h>
#include <libdevcrypto/SecretStore.h>
#include <libp2p/All.h>
#include <libethcore/Farm.h>
#include <libdevcore/FileSystem.h>
#include <libethereum/All.h>
#include <libethcore/KeyManager.h>
#include <libethereum/AccountDiff.h>
#include <libethereum/DownloadMan.h>
#include <libethereum/Client.h>
#include <liblll/All.h>
#include <libwhisper/WhisperPeer.h>
#include <libwhisper/WhisperHost.h>
#include <test/JsonSpiritHeaders.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace dev::p2p;
using namespace dev::shh;
namespace js = json_spirit;
namespace fs = boost::filesystem;
#else
#include <libethcore/Sealer.h>
#include <libethcore/BasicAuthority.h>
#include <libethcore/BlockInfo.h>
#include <libethcore/Ethash.h>
#include <libethcore/Params.h>
#include <libethereum/All.h>
#include <libethereum/AccountDiff.h>
#include <libethereum/DownloadMan.h>
#include <libethereum/Client.h>
using namespace std;
using namespace dev;
using namespace eth;
#endif
#if 0
int main()
{
BlockInfo bi;
bi.difficulty = c_genesisDifficulty;
bi.gasLimit = c_genesisGasLimit;
bi.number() = 1;
bi.parentHash() = sha3("parentHash");
bytes sealedData;
{
KeyPair kp(sha3("test"));
SealEngineFace* se = BasicAuthority::createSealEngine();
se->setOption("authority", rlp(kp.secret()));
se->setOption("authorities", rlpList(kp.address()));
cdebug << se->sealers();
bool done = false;
se->onSealGenerated([&](SealFace const* seal){
sealedData = seal->sealedHeader(bi);
done = true;
});
se->generateSeal(bi);
while (!done)
this_thread::sleep_for(chrono::milliseconds(50));
BasicAuthority::BlockHeader sealed = BasicAuthority::BlockHeader::fromHeader(sealedData, CheckEverything);
cdebug << sealed.sig();
}
{
SealEngineFace* se = Ethash::createSealEngine();
cdebug << se->sealers();
bool done = false;
se->setSealer("cpu");
se->onSealGenerated([&](SealFace const* seal){
sealedData = seal->sealedHeader(bi);
done = true;
});
se->generateSeal(bi);
while (!done)
this_thread::sleep_for(chrono::milliseconds(50));
Ethash::BlockHeader sealed = Ethash::BlockHeader::fromHeader(sealedData, CheckEverything);
cdebug << sealed.nonce();
}
return 0;
}
#elif 0
int main()
{
cdebug << pbkdf2("password", asBytes("salt"), 1, 32);
cdebug << pbkdf2("password", asBytes("salt"), 1, 16);
cdebug << pbkdf2("password", asBytes("salt"), 2, 16);
cdebug << pbkdf2("testpassword", fromHex("de5742f1f1045c402296422cee5a8a9ecf0ac5bf594deca1170d22aef33a79cf"), 262144, 16);
return 0;
}
#elif 0
int main()
{
cdebug << "EXP";
vector<bytes> data;
for (unsigned i = 0; i < 10000; ++i)
data.push_back(rlp(i));
h256 ret;
DEV_TIMED("triedb")
{
MemoryDB mdb;
GenericTrieDB<MemoryDB> t(&mdb);
t.init();
unsigned i = 0;
for (auto const& d: data)
t.insert(rlp(i++), d);
ret = t.root();
}
cdebug << ret;
DEV_TIMED("hash256")
ret = orderedTrieRoot(data);
cdebug << ret;
}
#elif 0
int main()
{
KeyManager keyman;
if (keyman.exists())
keyman.load("foo");
else
keyman.create("foo");
Address a("9cab1cc4e8fe528267c6c3af664a1adbce810b5f");
// keyman.importExisting(fromUUID("441193ae-a767-f1c3-48ba-dd6610db5ed0"), "{\"name\":\"Gavin Wood - Main identity\"}", "bar", "{\"hint\":\"Not foo.\"}");
// Address a2 = keyman.address(keyman.import(Secret::random(), "Key with no additional security."));
// cdebug << toString(a2);
Address a2("19c486071651b2650449ba3c6a807f316a73e8fe");
cdebug << keyman.accountDetails();
cdebug << "Secret key for " << a << "is" << keyman.secret(a, [](){ return "bar"; });
cdebug << "Secret key for " << a2 << "is" << keyman.secret(a2);
}
#elif 0
int main()
{
DownloadMan man;
DownloadSub s0(man);
DownloadSub s1(man);
DownloadSub s2(man);
man.resetToChain(h256s({u256(0), u256(1), u256(2), u256(3), u256(4), u256(5), u256(6), u256(7), u256(8)}), 0);
assert((s0.nextFetch(2) == h256Set{(u256)7, (u256)8}));
assert((s1.nextFetch(2) == h256Set{(u256)5, (u256)6}));
assert((s2.nextFetch(2) == h256Set{(u256)3, (u256)4}));
s0.noteBlock(u256(8));
s0.doneFetch();
assert((s0.nextFetch(2) == h256Set{(u256)2, (u256)7}));
s1.noteBlock(u256(6));
s1.noteBlock(u256(5));
s1.doneFetch();
assert((s1.nextFetch(2) == h256Set{(u256)0, (u256)1}));
s0.doneFetch(); // TODO: check exact semantics of doneFetch & nextFetch. Not sure if they're right -> doneFetch calls resetFetch which kills all the info of past fetches.
cdebug << s0.nextFetch(2);
assert((s0.nextFetch(2) == h256Set{(u256)3, (u256)4}));
/* RangeMask<unsigned> m(0, 100);
cnote << m;
m += UnsignedRange(3, 10);
cnote << m;
m += UnsignedRange(11, 16);
cnote << m;
m += UnsignedRange(10, 11);
cnote << m;
cnote << ~m;
cnote << (~m).lowest(10);
for (auto i: (~m).lowest(10))
cnote << i;*/
return 0;
}
#elif 0
int main()
{
KeyPair u = KeyPair::create();
KeyPair cb = KeyPair::create();
OverlayDB db;
State s(cb.address(), db, BaseState::Empty);
cnote << s.rootHash();
s.addBalance(u.address(), 1 * ether);
Address c = s.newContract(1000 * ether, compileLLL("(suicide (caller))"));
s.commit();
State before = s;
cnote << "State before transaction: " << before;
Transaction t(0, 10000, 10000, c, bytes(), 0, u.secret());
cnote << "Transaction: " << t;
cnote << s.balance(c);
s.execute(LastHashes(), t.rlp());
cnote << "State after transaction: " << s;
cnote << before.diff(s);
}
#elif 0
int main()
{
GenericFarm<EthashProofOfWork> f;
BlockInfo genesis = CanonBlockChain::genesis();
genesis.difficulty = 1 << 18;
cdebug << genesis.boundary();
auto mine = [](GenericFarm<EthashProofOfWork>& f, BlockInfo const& g, unsigned timeout) {
BlockInfo bi = g;
bool completed = false;
f.onSolutionFound([&](EthashProofOfWork::Solution sol)
{
bi.proof = sol;
return completed = true;
});
f.setWork(bi);
for (unsigned i = 0; !completed && i < timeout * 10; ++i, cout << f.miningProgress() << "\r" << flush)
this_thread::sleep_for(chrono::milliseconds(100));
cout << endl << flush;
cdebug << bi.mixHash << bi.nonce << (Ethash::verify(bi) ? "GOOD" : "bad");
};
Ethash::prep(genesis);
genesis.difficulty = u256(1) << 40;
genesis.noteDirty();
f.startCPU();
mine(f, genesis, 10);
f.startGPU();
cdebug << "Good:";
genesis.difficulty = 1 << 18;
genesis.noteDirty();
mine(f, genesis, 30);
cdebug << "Bad:";
genesis.difficulty = (u256(1) << 40);
genesis.noteDirty();
mine(f, genesis, 30);
f.stop();
return 0;
}
#elif 1
int main()
{
bytes tx = fromHex("f84c01028332dcd58004801ba024843272ee176277535489859cbd275686023fe64aabd158b6fcdf2ae6a1ab6ba02f252a5016a48e5ec8d17aefaf4324d29b9e123fa623dc5a60539b3ad3610c95");
Transaction t(tx, CheckTransaction::None);
Public p = recover(t.signature(), t.sha3(WithoutSignature));
cnote << t.signature().r;
cnote << t.signature().s;
cnote << t.signature().v;
cnote << p;
cnote << toAddress(p);
cnote << t.sender();
}
#elif 0
void mine(State& s, BlockChain const& _bc, SealEngineFace* _se)
{
s.commitToSeal(_bc);
Notified<bytes> sealed;
_se->onSealGenerated([&](bytes const& sealedHeader){ sealed = sealedHeader; });
_se->generateSeal(s.info());
sealed.waitNot({});
s.sealBlock(sealed);
}
int main()
{
cnote << "Testing State...";
KeyPair me = sha3("Gav Wood");
KeyPair myMiner = sha3("Gav's Miner");
// KeyPair you = sha3("123");
Defaults::setDBPath(boost::filesystem::temp_directory_path().string() + "/" + toString(chrono::system_clock::now().time_since_epoch().count()));
using Sealer = Ethash;
CanonBlockChain<Sealer> bc;
auto gbb = bc.headerData(bc.genesisHash());
assert(Sealer::BlockHeader(bc.headerData(bc.genesisHash()), IgnoreSeal, bc.genesisHash(), HeaderData));
SealEngineFace* se = Sealer::createSealEngine();
KeyPair kp(sha3("test"));
se->setOption("authority", rlp(kp.secret()));
se->setOption("authorities", rlpList(kp.address()));
OverlayDB stateDB = State::openDB(bc.genesisHash());
cnote << bc;
Block s = bc.genesisBlock(stateDB);
s.setBeneficiary(myMiner.address());
cnote << s;
// Sync up - this won't do much until we use the last state.
s.sync(bc);
cnote << s;
// Mine to get some ether!
mine(s, bc, se);
bytes minedBlock = s.blockData();
cnote << "Mined block is" << BlockInfo(minedBlock).stateRoot();
bc.import(minedBlock, stateDB);
cnote << bc;
s.sync(bc);
cnote << s;
cnote << "Miner now has" << s.balance(myMiner.address());
s.resetCurrent();
cnote << "Miner now has" << s.balance(myMiner.address());
// Inject a transaction to transfer funds from miner to me.
Transaction t(1000, 10000, 30000, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret());
assert(t.sender() == myMiner.address());
s.execute(bc.lastHashes(), t);
cnote << s;
// Mine to get some ether and set in stone.
s.commitToSeal(bc);
s.commitToSeal(bc);
mine(s, bc, se);
bc.attemptImport(s.blockData(), stateDB);
cnote << bc;
s.sync(bc);
cnote << s;
return 0;
}
#else
int main()
{
string tempDir = boost::filesystem::temp_directory_path().string() + "/" + toString(chrono::system_clock::now().time_since_epoch().count());
KeyPair myMiner = sha3("Gav's Miner");
p2p::Host net("Test");
cdebug << "Path:" << tempDir;
Client c(&net, tempDir);
c.setBeneficiary(myMiner.address());
this_thread::sleep_for(chrono::milliseconds(1000));
c.startMining();
this_thread::sleep_for(chrono::milliseconds(6000));
c.stopMining();
return 0;
}
#endif

1
mix/CMakeLists.txt

@ -1 +0,0 @@
add_custom_target(mix)

21
rlp/CMakeLists.txt

@ -1,21 +0,0 @@
cmake_policy(SET CMP0015 NEW)
set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..)
include_directories(${DB_INCLUDE_DIRS})
set(EXECUTABLE rlp)
add_executable(${EXECUTABLE} ${SRC_LIST})
target_link_libraries(${EXECUTABLE} devcrypto)
target_link_libraries(${EXECUTABLE} ${DB_LIBRARIES})
if (APPLE)
install(TARGETS ${EXECUTABLE} DESTINATION bin)
else()
eth_install_executable(${EXECUTABLE})
endif()

394
rlp/main.cpp

@ -1,394 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file main.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
* RLP tool.
*/
#include <fstream>
#include <iostream>
#include <boost/algorithm/string.hpp>
#include "../test/JsonSpiritHeaders.h"
#include <libdevcore/CommonIO.h>
#include <libdevcore/RLP.h>
#include <libdevcore/SHA3.h>
using namespace std;
using namespace dev;
namespace js = json_spirit;
void help()
{
cout
<< "Usage rlp [OPTIONS] [ <file> | -- ]" << endl
<< "Options:" << endl
<< " -r,--render Render the given RLP. Options:" << endl
<< " --indent <string> Use string as the level indentation (default ' ')." << endl
<< " --hex-ints Render integers in hex." << endl
<< " --string-ints Render integers in the same way as strings." << endl
<< " --ascii-strings Render data as C-style strings or hex depending on content being ASCII." << endl
<< " --force-string Force all data to be rendered as C-style strings." << endl
<< " --force-escape When rendering as C-style strings, force all characters to be escaped." << endl
<< " --force-hex Force all data to be rendered as raw hex." << endl
<< " -l,--list-archive List the items in the RLP list by hash and size." << endl
<< " -e,--extract-archive Extract all items in the RLP list, named by hash." << endl
<< " -c,--create Given a simplified JSON string, output the RLP." << endl
<< "General options:" << endl
<< " -L,--lenience Try not to bomb out early if possible." << endl
<< " -x,--hex,--base-16 Treat input RLP as hex encoded data." << endl
<< " -k,--keccak Output Keccak-256 hash only." << endl
<< " --64,--base-64 Treat input RLP as base-64 encoded data." << endl
<< " -b,--bin,--base-256 Treat input RLP as raw binary data." << endl
<< " -h,--help Print this help message and exit." << endl
<< " -V,--version Show the version and exit." << endl
;
exit(0);
}
void version()
{
cout << "rlp version " << dev::Version << endl;
exit(0);
}
enum class Mode {
ListArchive,
ExtractArchive,
Render,
Create
};
enum class Encoding {
Auto,
Hex,
Base64,
Binary,
Keccak,
};
bool isAscii(string const& _s)
{
// Always hex-encode anything beginning with 0x to avoid ambiguity.
if (_s.size() >= 2 && _s.substr(0, 2) == "0x")
return false;
for (char c: _s)
if (c < 32)
return false;
return true;
}
class RLPStreamer
{
public:
struct Prefs
{
string indent;
bool hexInts = false;
bool stringInts = true;
bool hexPrefix = true;
bool forceString = false;
bool escapeAll = false;
bool forceHex = true;
};
RLPStreamer(ostream& _out, Prefs _p): m_out(_out), m_prefs(_p) {}
void output(RLP const& _d, unsigned _level = 0)
{
if (_d.isNull())
m_out << "null";
else if (_d.isInt() && !m_prefs.stringInts)
if (m_prefs.hexInts)
m_out << (m_prefs.hexPrefix ? "0x" : "") << toHex(toCompactBigEndian(_d.toInt<bigint>(RLP::LaissezFaire), 1), 1);
else
m_out << _d.toInt<bigint>(RLP::LaissezFaire);
else if (_d.isData() || (_d.isInt() && m_prefs.stringInts))
if (m_prefs.forceString || (!m_prefs.forceHex && isAscii(_d.toString())))
m_out << escaped(_d.toString(), m_prefs.escapeAll);
else
m_out << "\"" << (m_prefs.hexPrefix ? "0x" : "") << toHex(_d.toBytes()) << "\"";
else if (_d.isList())
{
m_out << "[";
string newline = "\n";
for (unsigned i = 0; i < _level + 1; ++i)
newline += m_prefs.indent;
int j = 0;
for (auto i: _d)
{
m_out << (j++ ?
(m_prefs.indent.empty() ? ", " : ("," + newline)) :
(m_prefs.indent.empty() ? " " : newline));
output(i, _level + 1);
}
newline = newline.substr(0, newline.size() - m_prefs.indent.size());
m_out << (m_prefs.indent.empty() ? (j ? " ]" : "]") : (j ? newline + "]" : "]"));
}
}
private:
std::ostream& m_out;
Prefs m_prefs;
};
int main(int argc, char** argv)
{
Encoding encoding = Encoding::Auto;
Mode mode = Mode::Render;
string inputFile = "--";
bool lenience = false;
RLPStreamer::Prefs prefs;
for (int i = 1; i < argc; ++i)
{
string arg = argv[i];
if (arg == "-h" || arg == "--help")
help();
else if (arg == "-r" || arg == "--render")
mode = Mode::Render;
else if (arg == "-c" || arg == "--create")
mode = Mode::Create;
else if ((arg == "-i" || arg == "--indent") && argc > i)
prefs.indent = argv[++i];
else if (arg == "--hex-ints")
prefs.hexInts = true;
else if (arg == "--string-ints")
prefs.stringInts = true;
else if (arg == "--ascii-strings")
prefs.forceString = prefs.forceHex = false;
else if (arg == "--force-string")
prefs.forceString = true;
else if (arg == "--force-hex")
prefs.forceHex = true, prefs.forceString = false;
else if (arg == "--force-escape")
prefs.escapeAll = true;
else if (arg == "-n" || arg == "--nice")
prefs.forceString = true, prefs.stringInts = false, prefs.forceHex = false, prefs.indent = " ";
else if (arg == "-l" || arg == "--list-archive")
mode = Mode::ListArchive;
else if (arg == "-e" || arg == "--extract-archive")
mode = Mode::ExtractArchive;
else if (arg == "-L" || arg == "--lenience")
lenience = true;
else if (arg == "-V" || arg == "--version")
version();
else if (arg == "-x" || arg == "--hex" || arg == "--base-16")
encoding = Encoding::Hex;
else if (arg == "-k" || arg == "--keccak")
encoding = Encoding::Keccak;
else if (arg == "--64" || arg == "--base-64")
encoding = Encoding::Base64;
else if (arg == "-b" || arg == "--bin" || arg == "--base-256")
encoding = Encoding::Binary;
else
inputFile = arg;
}
bytes in;
if (inputFile == "--")
for (int i = cin.get(); i != -1; i = cin.get())
in.push_back((byte)i);
else
in = contents(inputFile);
bytes b;
if (mode != Mode::Create)
{
if (encoding == Encoding::Auto)
{
encoding = Encoding::Hex;
for (char b: in)
if (b != '\n' && b != ' ' && b != '\t')
{
if (encoding == Encoding::Hex && (b < '0' || b > '9' ) && (b < 'a' || b > 'f' ) && (b < 'A' || b > 'F' ))
{
cerr << "'" << b << "':" << (int)b << endl;
encoding = Encoding::Base64;
}
if (encoding == Encoding::Base64 && (b < '0' || b > '9' ) && (b < 'a' || b > 'z' ) && (b < 'A' || b > 'Z' ) && b != '+' && b != '/')
{
encoding = Encoding::Binary;
break;
}
}
}
switch (encoding)
{
case Encoding::Hex:
{
string s = asString(in);
boost::algorithm::replace_all(s, " ", "");
boost::algorithm::replace_all(s, "\n", "");
boost::algorithm::replace_all(s, "\t", "");
b = fromHex(s);
break;
}
case Encoding::Base64:
{
string s = asString(in);
boost::algorithm::replace_all(s, " ", "");
boost::algorithm::replace_all(s, "\n", "");
boost::algorithm::replace_all(s, "\t", "");
b = fromBase64(s);
break;
}
default:
swap(b, in);
break;
}
}
try
{
RLP rlp(b);
switch (mode)
{
case Mode::ListArchive:
{
if (!rlp.isList())
{
cout << "Error: Invalid format; RLP data is not a list." << endl;
exit(1);
}
cout << rlp.itemCount() << " items:" << endl;
for (auto i: rlp)
{
if (!i.isData())
{
cout << "Error: Invalid format; RLP list item is not data." << endl;
if (!lenience)
exit(1);
}
cout << " " << i.size() << " bytes: " << sha3(i.data()) << endl;
}
break;
}
case Mode::ExtractArchive:
{
if (!rlp.isList())
{
cout << "Error: Invalid format; RLP data is not a list." << endl;
exit(1);
}
cout << rlp.itemCount() << " items:" << endl;
for (auto i: rlp)
{
if (!i.isData())
{
cout << "Error: Invalid format; RLP list item is not data." << endl;
if (!lenience)
exit(1);
}
ofstream fout;
fout.open(toString(sha3(i.data())));
fout.write(reinterpret_cast<char const*>(i.data().data()), i.data().size());
}
break;
}
case Mode::Render:
{
RLPStreamer s(cout, prefs);
s.output(rlp);
cout << endl;
break;
}
case Mode::Create:
{
vector<js::mValue> v(1);
try {
js::read_string(asString(in), v[0]);
}
catch (...)
{
cerr << "Error: Invalid format; bad JSON." << endl;
exit(1);
}
RLPStream out;
while (!v.empty())
{
auto vb = v.back();
v.pop_back();
switch (vb.type())
{
case js::array_type:
{
js::mArray a = vb.get_array();
out.appendList(a.size());
for (int i = a.size() - 1; i >= 0; --i)
v.push_back(a[i]);
break;
}
case js::str_type:
{
string const& s = vb.get_str();
if (s.size() >= 2 && s.substr(0, 2) == "0x")
out << fromHex(s);
else
{
// assume it's a normal JS escaped string.
bytes ss;
ss.reserve(s.size());
for (unsigned i = 0; i < s.size(); ++i)
if (s[i] == '\\' && i + 1 < s.size())
{
if (s[++i] == 'x' && i + 2 < s.size())
ss.push_back(fromHex(s.substr(i, 2))[0]);
}
else if (s[i] != '\\')
ss.push_back((byte)s[i]);
out << ss;
}
break;
}
case js::int_type:
out << vb.get_int();
break;
default:
cerr << "ERROR: Unsupported type in JSON." << endl;
if (!lenience)
exit(1);
}
}
switch (encoding)
{
case Encoding::Hex: case Encoding::Auto:
cout << toHex(out.out()) << endl;
break;
case Encoding::Base64:
cout << toBase64(&out.out()) << endl;
break;
case Encoding::Binary:
cout.write((char const*)out.out().data(), out.out().size());
break;
case Encoding::Keccak:
cout << sha3(out.out()).hex() << endl;
break;
}
break;
}
default:;
}
}
catch (...)
{
cerr << "Error: Invalid format; bad RLP." << endl;
exit(1);
}
return 0;
}
Loading…
Cancel
Save