Browse Source

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

cl-refactor
CJentzsch 10 years ago
parent
commit
01c80649b8
  1. 19
      alethzero/MainWin.cpp
  2. 4
      alethzero/OurWebThreeStubServer.h
  3. 27
      build.py
  4. 13
      cmake/EthExecutableHelper.cmake
  5. 5
      evmjit/CMakeLists.txt
  6. 3
      evmjit/libevmjit-cpp/CMakeLists.txt
  7. 3
      evmjit/libevmjit-cpp/Env.cpp
  8. 67
      evmjit/libevmjit-cpp/JitVM.cpp
  9. 38
      evmjit/libevmjit-cpp/Utils.h
  10. 524
      evmjit/libevmjit/Arith256.cpp
  11. 29
      evmjit/libevmjit/Arith256.h
  12. 18
      evmjit/libevmjit/CMakeLists.txt
  13. 31
      evmjit/libevmjit/Common.h
  14. 54
      evmjit/libevmjit/Compiler.cpp
  15. 25
      evmjit/libevmjit/ExecutionEngine.cpp
  16. 10
      evmjit/libevmjit/ExecutionEngine.h
  17. 476
      evmjit/libevmjit/Memory.cpp
  18. 17
      evmjit/libevmjit/Runtime.cpp
  19. 105
      evmjit/libevmjit/Runtime.h
  20. 35
      evmjit/libevmjit/RuntimeData.h
  21. 91
      evmjit/libevmjit/RuntimeManager.cpp
  22. 2
      evmjit/libevmjit/RuntimeManager.h
  23. 8
      evmjit/libevmjit/Stack.cpp
  24. 26
      evmjit/libevmjit/Utils.cpp
  25. 3
      evmjit/libevmjit/Utils.h
  26. 47
      evmjit/libevmjit/interface.cpp
  27. 47
      evmjit/libevmjit/interface.h
  28. 3
      libdevcore/CMakeLists.txt
  29. 2
      libdevcore/Common.cpp
  30. 2
      libdevcrypto/TrieDB.h
  31. 51
      libethcore/CommonJS.cpp
  32. 40
      libethcore/CommonJS.h
  33. 2
      libethereum/All.h
  34. 62
      libethereum/BlockChain.cpp
  35. 18
      libethereum/BlockChain.h
  36. 80
      libethereum/CanonBlockChain.cpp
  37. 76
      libethereum/CanonBlockChain.h
  38. 7
      libethereum/Client.cpp
  39. 6
      libethereum/Client.h
  40. 7
      libethereum/LogFilter.cpp
  41. 16
      libethereum/LogFilter.h
  42. 11
      libethereum/State.cpp
  43. 4
      libethereum/State.h
  44. 2
      libjsqrc/ethereumjs/dist/ethereum.js
  45. 2
      libjsqrc/ethereumjs/dist/ethereum.js.map
  46. 2
      libjsqrc/ethereumjs/dist/ethereum.min.js
  47. 2
      libjsqrc/ethereumjs/lib/jsonrpc.js
  48. 15
      libjsqrc/ethereumjs/test/jsonrpc.isValidResponse.js
  49. 12
      libjsqrc/natspec.js
  50. 2
      libp2p/Capability.cpp
  51. 6
      libp2p/Host.cpp
  52. 17
      libp2p/Session.cpp
  53. 92
      libserpent/compiler.cpp
  54. 5
      libserpent/compiler.h
  55. 7
      libserpent/util.cpp
  56. 2
      libsolidity/AST.cpp
  57. 7
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  58. 4
      libweb3jsonrpc/WebThreeStubServerBase.h
  59. 45
      macdeployfix.sh
  60. 14
      mix/AppContext.cpp
  61. 4
      mix/AppContext.h
  62. 41
      mix/ClientModel.cpp
  63. 5
      mix/ClientModel.h
  64. 2
      mix/ContractCallDataEncoder.cpp
  65. 2
      mix/DebuggingStateWrapper.cpp
  66. 222
      mix/MixClient.cpp
  67. 30
      mix/MixClient.h
  68. 2
      mix/QBigInt.cpp
  69. 2
      mix/QBigInt.h
  70. 2
      mix/QVariableDefinition.cpp
  71. 3
      mix/qml/MainContent.qml
  72. 15
      mix/qml/WebCodeEditor.qml
  73. 2
      mix/qml/html/cm/codemirror.js
  74. 20
      mix/qml/html/codeeditor.js
  75. 1
      mix/qml/main.qml
  76. 5
      pullSerpent.sh
  77. 5
      sc/cmdline.cpp
  78. 10
      test/SolidityEndToEndTest.cpp
  79. 36
      test/SolidityNameAndTypeResolution.cpp
  80. 18
      test/SolidityParser.cpp
  81. 5
      test/commonjs.cpp
  82. 2
      test/fork.cpp
  83. 8
      test/genesis.cpp
  84. 2
      test/jsonrpc.cpp
  85. 2
      test/state.cpp
  86. 4
      test/stateOriginal.cpp
  87. 2
      test/txTest.cpp
  88. 6
      third/MainWin.cpp

19
alethzero/MainWin.cpp

@ -33,7 +33,7 @@
#include <libserpent/funcs.h> #include <libserpent/funcs.h>
#include <libserpent/util.h> #include <libserpent/util.h>
#include <libdevcrypto/FileSystem.h> #include <libdevcrypto/FileSystem.h>
#include <libdevcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <liblll/Compiler.h> #include <liblll/Compiler.h>
#include <liblll/CodeFragment.h> #include <liblll/CodeFragment.h>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
@ -41,7 +41,7 @@
#include <libsolidity/SourceReferenceFormatter.h> #include <libsolidity/SourceReferenceFormatter.h>
#include <libevm/VM.h> #include <libevm/VM.h>
#include <libevm/VMFactory.h> #include <libevm/VMFactory.h>
#include <libethereum/BlockChain.h> #include <libethereum/CanonBlockChain.h>
#include <libethereum/ExtVM.h> #include <libethereum/ExtVM.h>
#include <libethereum/Client.h> #include <libethereum/Client.h>
#include <libethereum/Utility.h> #include <libethereum/Utility.h>
@ -136,9 +136,9 @@ Main::Main(QWidget *parent) :
#endif #endif
m_servers.append(QString::fromStdString(Host::pocHost() + ":30303")); m_servers.append(QString::fromStdString(Host::pocHost() + ":30303"));
cerr << "State root: " << BlockChain::genesis().stateRoot << endl; cerr << "State root: " << CanonBlockChain::genesis().stateRoot << endl;
auto block = BlockChain::createGenesisBlock(); auto block = CanonBlockChain::createGenesisBlock();
cerr << "Block Hash: " << BlockChain::genesis().hash << endl; cerr << "Block Hash: " << CanonBlockChain::genesis().hash << endl;
cerr << "Block RLP: " << RLP(block) << endl; cerr << "Block RLP: " << RLP(block) << endl;
cerr << "Block Hex: " << toHex(block) << endl; cerr << "Block Hex: " << toHex(block) << endl;
cerr << "Network protocol version: " << c_protocolVersion << endl; cerr << "Network protocol version: " << c_protocolVersion << endl;
@ -1047,7 +1047,7 @@ void Main::refreshBlockCount()
ui->blockCount->setText(QString("%6 #%1 @%3 T%2 PV%4 D%5").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff)).arg(c_protocolVersion).arg(c_databaseVersion).arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet")); ui->blockCount->setText(QString("%6 #%1 @%3 T%2 PV%4 D%5").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff)).arg(c_protocolVersion).arg(c_databaseVersion).arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet"));
} }
static bool blockMatch(string const& _f, BlockDetails const& _b, h256 _h, BlockChain const& _bc) static bool blockMatch(string const& _f, BlockDetails const& _b, h256 _h, CanonBlockChain const& _bc)
{ {
try try
{ {
@ -1189,7 +1189,10 @@ void Main::timerEvent(QTimerEvent*)
{ {
auto ls = ethereum()->checkWatch(i.first); auto ls = ethereum()->checkWatch(i.first);
if (ls.size()) if (ls.size())
{
cnote << "FIRING WATCH" << i.first << ls.size();
i.second(ls); i.second(ls);
}
} }
} }
@ -1202,7 +1205,7 @@ string Main::renderDiff(StateDiff const& _d) const
{ {
s << "<hr/>"; s << "<hr/>";
AccountDiff const& ad = i.second; AccountDiff ad = i.second;
s << "<code style=\"white-space: pre; font-weight: bold\">" << lead(ad.changeType()) << " </code>" << " <b>" << render(i.first).toStdString() << "</b>"; s << "<code style=\"white-space: pre; font-weight: bold\">" << lead(ad.changeType()) << " </code>" << " <b>" << render(i.first).toStdString() << "</b>";
if (!ad.exist.to()) if (!ad.exist.to())
continue; continue;
@ -1210,7 +1213,7 @@ string Main::renderDiff(StateDiff const& _d) const
if (ad.balance) if (ad.balance)
{ {
s << "<br/>" << indent << "Balance " << dec << ad.balance.to() << " [=" << formatBalance(ad.balance.to()) << "]"; s << "<br/>" << indent << "Balance " << dec << ad.balance.to() << " [=" << formatBalance(ad.balance.to()) << "]";
auto d = (((dev::bigint)ad.balance.to()) - ((dev::bigint)ad.balance.from())); bigint d = (dev::bigint)ad.balance.to() - (dev::bigint)ad.balance.from();
s << " <b>" << showpos << dec << d << " [=" << formatBalance(d) << "]" << noshowpos << "</b>"; s << " <b>" << showpos << dec << d << " [=" << formatBalance(d) << "]" << noshowpos << "</b>";
} }
if (ad.nonce) if (ad.nonce)

4
alethzero/OurWebThreeStubServer.h

@ -20,7 +20,7 @@
*/ */
#include <QtCore/QObject> #include <QtCore/QObject>
#include <libdevcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <libdevcrypto/Common.h> #include <libdevcrypto/Common.h>
#include <libweb3jsonrpc/WebThreeStubServer.h> #include <libweb3jsonrpc/WebThreeStubServer.h>
@ -35,7 +35,7 @@ public:
std::vector<dev::KeyPair> const& _accounts, Main* main); std::vector<dev::KeyPair> const& _accounts, Main* main);
virtual std::string shh_newIdentity() override; virtual std::string shh_newIdentity() override;
virtual bool authenticate(dev::TransactionSkeleton const& _t); virtual bool authenticate(dev::eth::TransactionSkeleton const& _t);
signals: signals:
void onNewId(QString _s); void onNewId(QString _s);

27
build.py

@ -1,27 +0,0 @@
#!/usr/bin/python
# cpp-ethereum build script
# to be used from CI server, or to build locally
# uses python instead of bash script for better cross-platform support
# TODO Initial version. Needs much more improvements
import argparse
import os
import subprocess
def build_dependencies():
if os.path.exists("extdep"):
os.chdir("extdep")
if not os.path.exists("build"):
os.makedirs("build")
os.chdir("build")
subprocess.check_call(["cmake", ".."])
subprocess.check_call("make")
parser = argparse.ArgumentParser()
parser.add_argument("cmd", help="what to build")
args = parser.parse_args()
if args.cmd == "dep":
build_dependencies()

13
cmake/EthExecutableHelper.cmake

@ -71,8 +71,9 @@ macro(eth_install_executable EXECUTABLE)
if (APPLE) if (APPLE)
# First have qt5 install plugins and frameworks # First have qt5 install plugins and frameworks
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND ${MACDEPLOYQT_APP} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app ${eth_qml_dir} COMMAND ${MACDEPLOYQT_APP} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app -executable=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app/Contents/MacOS/${EXECUTABLE} ${eth_qml_dir}
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
COMMAND sh ${CMAKE_SOURCE_DIR}/macdeployfix.sh ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app/Contents
) )
# This tool and next will inspect linked libraries in order to determine which dependencies are required # This tool and next will inspect linked libraries in order to determine which dependencies are required
@ -82,19 +83,11 @@ macro(eth_install_executable EXECUTABLE)
set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/\$ENV{CONFIGURATION}/${EXECUTABLE}.app") set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/\$ENV{CONFIGURATION}/${EXECUTABLE}.app")
endif () endif ()
# TODO check, how fixup_bundle works and if it is required
install(CODE " install(CODE "
include(BundleUtilities) include(BundleUtilities)
set(BU_CHMOD_BUNDLE_ITEMS 1) set(BU_CHMOD_BUNDLE_ITEMS 1)
fixup_bundle(\"${APP_BUNDLE_PATH}\" \"${BUNDLELIBS}\" \"../libqethereum ../libethereum ../secp256k1\") verify_app(\"${APP_BUNDLE_PATH}\")
" COMPONENT RUNTIME ) " COMPONENT RUNTIME )
# Cleanup duplicate libs from macdeployqt
install(CODE "
file(GLOB LINGER_RM \"${APP_BUNDLE_PATH}/Contents/Frameworks/*.dylib\")
if (LINGER_RM)
file(REMOVE \${LINGER_RM})
endif ()
")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# copy all dlls to executable directory # copy all dlls to executable directory

5
evmjit/CMakeLists.txt

@ -4,7 +4,7 @@ project(evmjit)
set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_property(GLOBAL PROPERTY USE_FOLDERS ON)
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
else() else()
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -fPIC") set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -fPIC")
endif() endif()
@ -32,9 +32,6 @@ else()
link_directories(/usr/lib/llvm-3.5/lib) link_directories(/usr/lib/llvm-3.5/lib)
endif() endif()
# Boost
find_package(Boost REQUIRED)
add_subdirectory(libevmjit) add_subdirectory(libevmjit)
if(EVMJIT_CPP) if(EVMJIT_CPP)

3
evmjit/libevmjit-cpp/CMakeLists.txt

@ -1,5 +1,8 @@
set(TARGET_NAME evmjit-cpp) set(TARGET_NAME evmjit-cpp)
# Boost
find_package(Boost REQUIRED)
set(SOURCES set(SOURCES
Env.cpp Env.cpp
JitVM.cpp JitVM.h JitVM.cpp JitVM.h

3
evmjit/libevmjit-cpp/Env.cpp

@ -3,7 +3,7 @@
#include <libevm/FeeStructure.h> #include <libevm/FeeStructure.h>
#include <libevm/ExtVMFace.h> #include <libevm/ExtVMFace.h>
#include <evmjit/libevmjit/Utils.h> #include "Utils.h"
extern "C" extern "C"
{ {
@ -16,7 +16,6 @@ extern "C"
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
using jit::i256; using jit::i256;
using jit::eth2llvm;
EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value) EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value)
{ {

67
evmjit/libevmjit-cpp/JitVM.cpp

@ -2,39 +2,54 @@
#include "JitVM.h" #include "JitVM.h"
#include <libevm/VM.h> #include <libevm/VM.h>
#include <evmjit/libevmjit/ExecutionEngine.h> #include <evmjit/libevmjit/ExecutionEngine.h>
#include <evmjit/libevmjit/Utils.h> #include "Utils.h"
namespace dev namespace dev
{ {
namespace eth namespace eth
{ {
extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below
bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t) bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t)
{ {
using namespace jit; using namespace jit;
m_data.set(RuntimeData::Gas, m_gas); if (m_gas > std::numeric_limits<decltype(m_data.gas)>::max())
m_data.set(RuntimeData::Address, fromAddress(_ext.myAddress)); BOOST_THROW_EXCEPTION(OutOfGas()); // Do not accept requests with gas > 2^63 (int64 max) // TODO: Return "not accepted" exception to allow interpreted handle that
m_data.set(RuntimeData::Caller, fromAddress(_ext.caller));
m_data.set(RuntimeData::Origin, fromAddress(_ext.origin)); if (_ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max())
m_data.set(RuntimeData::CallValue, _ext.value); BOOST_THROW_EXCEPTION(OutOfGas());
m_data.set(RuntimeData::CallDataSize, _ext.data.size());
m_data.set(RuntimeData::GasPrice, _ext.gasPrice); if (_ext.currentBlock.number > std::numeric_limits<decltype(m_data.number)>::max())
m_data.set(RuntimeData::CoinBase, fromAddress(_ext.currentBlock.coinbaseAddress)); BOOST_THROW_EXCEPTION(OutOfGas());
m_data.set(RuntimeData::TimeStamp, _ext.currentBlock.timestamp);
m_data.set(RuntimeData::Number, _ext.currentBlock.number); if (_ext.currentBlock.timestamp > std::numeric_limits<decltype(m_data.timestamp)>::max())
m_data.set(RuntimeData::Difficulty, _ext.currentBlock.difficulty); BOOST_THROW_EXCEPTION(OutOfGas());
m_data.set(RuntimeData::GasLimit, _ext.currentBlock.gasLimit);
m_data.set(RuntimeData::CodeSize, _ext.code.size());
m_data.callData = _ext.data.data(); m_data.gas = static_cast<decltype(m_data.gas)>(m_gas);
m_data.code = _ext.code.data(); 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 = eth2llvm(fromAddress(_ext.myAddress));
m_data.caller = eth2llvm(fromAddress(_ext.caller));
m_data.origin = eth2llvm(fromAddress(_ext.origin));
m_data.callValue = eth2llvm(_ext.value);
m_data.coinBase = eth2llvm(fromAddress(_ext.currentBlock.coinbaseAddress));
m_data.difficulty = eth2llvm(_ext.currentBlock.difficulty);
m_data.gasLimit = eth2llvm(_ext.currentBlock.gasLimit);
m_data.number = static_cast<decltype(m_data.number)>(_ext.currentBlock.number);
m_data.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.currentBlock.timestamp);
m_data.code = _ext.code.data();
m_data.codeSize = _ext.code.size();
auto env = reinterpret_cast<Env*>(&_ext); auto env = reinterpret_cast<Env*>(&_ext);
auto exitCode = m_engine.run(_ext.code, &m_data, env); auto exitCode = m_engine.run(_ext.code, &m_data, env);
switch (exitCode) switch (exitCode)
{ {
case ReturnCode::Suicide: case ReturnCode::Suicide:
_ext.suicide(right160(m_data.get(RuntimeData::SuicideDestAddress))); _ext.suicide(right160(llvm2eth(m_data.address)));
break; break;
case ReturnCode::BadJumpDestination: case ReturnCode::BadJumpDestination:
@ -45,24 +60,16 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t)
BOOST_THROW_EXCEPTION(StackTooSmall()); BOOST_THROW_EXCEPTION(StackTooSmall());
case ReturnCode::BadInstruction: case ReturnCode::BadInstruction:
BOOST_THROW_EXCEPTION(BadInstruction()); BOOST_THROW_EXCEPTION(BadInstruction());
case ReturnCode::LinkerWorkaround: // never happens
env_sload(); // but forces linker to include env_* JIT callback functions
break;
default: default:
break; break;
} }
m_gas = llvm2eth(m_data.elems[RuntimeData::Gas]); m_gas = m_data.gas; // TODO: Remove m_gas field
return {m_engine.returnData.data(), m_engine.returnData.size()}; // TODO: This all bytesConstRef is problematic, review. return {std::get<0>(m_engine.returnData), std::get<1>(m_engine.returnData)};
} }
} }
} }
namespace
{
// MSVS linker ignores export symbols in Env.cpp if nothing points at least one of them
extern "C" void env_sload();
void linkerWorkaround()
{
env_sload();
(void)&linkerWorkaround; // suppress unused function warning from GCC
}
}

38
evmjit/libevmjit-cpp/Utils.h

@ -0,0 +1,38 @@
#pragma once
#include <evmjit/libevmjit/Common.h>
namespace dev
{
namespace eth
{
inline u256 llvm2eth(jit::i256 _i)
{
u256 u = 0;
u |= _i.d;
u <<= 64;
u |= _i.c;
u <<= 64;
u |= _i.b;
u <<= 64;
u |= _i.a;
return u;
}
inline jit::i256 eth2llvm(u256 _u)
{
jit::i256 i;
u256 mask = 0xFFFFFFFFFFFFFFFF;
i.a = static_cast<uint64_t>(_u & mask);
_u >>= 64;
i.b = static_cast<uint64_t>(_u & mask);
_u >>= 64;
i.c = static_cast<uint64_t>(_u & mask);
_u >>= 64;
i.d = static_cast<uint64_t>(_u & mask);
return i;
}
}
}

524
evmjit/libevmjit/Arith256.cpp

@ -1,8 +1,11 @@
#include "Arith256.h" #include "Arith256.h"
#include "Runtime.h" #include "Runtime.h"
#include "Type.h" #include "Type.h"
#include "Endianness.h"
#include <llvm/IR/Function.h> #include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h>
#include <iostream>
namespace dev namespace dev
{ {
@ -24,35 +27,257 @@ Arith256::Arith256(llvm::IRBuilder<>& _builder) :
using Linkage = GlobalValue::LinkageTypes; using Linkage = GlobalValue::LinkageTypes;
llvm::Type* arg2Types[] = {Type::WordPtr, Type::WordPtr, Type::WordPtr}; llvm::Type* arg2Types[] = {Type::WordPtr, Type::WordPtr, Type::WordPtr};
llvm::Type* arg3Types[] = {Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr};
m_mul = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_mul", getModule()); m_mul = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_mul", getModule());
m_div = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_div", getModule());
m_mod = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_mod", getModule());
m_sdiv = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_sdiv", getModule());
m_smod = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_smod", getModule());
m_exp = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_exp", getModule());
m_addmod = Function::Create(FunctionType::get(Type::Void, arg3Types, false), Linkage::ExternalLinkage, "arith_addmod", getModule());
m_mulmod = Function::Create(FunctionType::get(Type::Void, arg3Types, false), Linkage::ExternalLinkage, "arith_mulmod", getModule());
} }
Arith256::~Arith256() 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, {_value, m_builder.getInt8(_c)});
}
llvm::Value* Arith256::binaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2) llvm::Function* Arith256::getDivFunc(llvm::Type* _type)
{ {
m_builder.CreateStore(_arg1, m_arg1); auto& func = _type == Type::Word ? m_div : m_div512;
m_builder.CreateStore(_arg2, m_arg2);
m_builder.CreateCall3(_op, m_arg1, m_arg2, m_result); if (!func)
return m_builder.CreateLoad(m_result); {
// 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 reminder
llvm::Type* argTypes[] = {_type, _type};
auto retType = llvm::StructType::get(m_builder.getContext(), llvm::ArrayRef<llvm::Type*>{argTypes});
auto funcName = _type == Type::Word ? "div" : "div512";
func = llvm::Function::Create(llvm::FunctionType::get(retType, argTypes, false), llvm::Function::PrivateLinkage, funcName, getModule());
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");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", func);
auto mainBB = llvm::BasicBlock::Create(m_builder.getContext(), "Main", func);
auto loopBB = llvm::BasicBlock::Create(m_builder.getContext(), "Loop", func);
auto continueBB = llvm::BasicBlock::Create(m_builder.getContext(), "Continue", func);
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func);
m_builder.SetInsertPoint(entryBB);
auto yNonZero = m_builder.CreateICmpNE(yArg, zero);
auto yLEx = m_builder.CreateICmpULE(yArg, x);
auto r0 = m_builder.CreateSelect(yNonZero, x, zero, "r0");
m_builder.CreateCondBr(m_builder.CreateAnd(yLEx, yNonZero), mainBB, returnBB);
m_builder.SetInsertPoint(mainBB);
auto ctlzIntr = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, _type);
// both y and r are non-zero
auto yLz = m_builder.CreateCall2(ctlzIntr, yArg, m_builder.getInt1(true), "y.lz");
auto rLz = m_builder.CreateCall2(ctlzIntr, r0, m_builder.getInt1(true), "r.lz");
auto i0 = m_builder.CreateNUWSub(yLz, rLz, "i0");
auto shlBy0 = m_builder.CreateICmpEQ(i0, zero);
auto y0 = m_builder.CreateShl(yArg, i0);
y0 = m_builder.CreateSelect(shlBy0, yArg, y0, "y0"); // Workaround for LLVM bug: shl by 0 produces wrong result
m_builder.CreateBr(loopBB);
m_builder.SetInsertPoint(loopBB);
auto yPhi = m_builder.CreatePHI(_type, 2, "y.phi");
auto rPhi = m_builder.CreatePHI(_type, 2, "r.phi");
auto iPhi = m_builder.CreatePHI(_type, 2, "i.phi");
auto qPhi = m_builder.CreatePHI(_type, 2, "q.phi");
auto rUpdate = m_builder.CreateNUWSub(rPhi, yPhi);
auto qUpdate = m_builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0
auto rGEy = m_builder.CreateICmpUGE(rPhi, yPhi);
auto r1 = m_builder.CreateSelect(rGEy, rUpdate, rPhi, "r1");
auto q1 = m_builder.CreateSelect(rGEy, qUpdate, qPhi, "q");
auto iZero = m_builder.CreateICmpEQ(iPhi, zero);
m_builder.CreateCondBr(iZero, returnBB, continueBB);
m_builder.SetInsertPoint(continueBB);
auto i2 = m_builder.CreateNUWSub(iPhi, one);
auto q2 = m_builder.CreateShl(q1, one);
auto y2 = m_builder.CreateLShr(yPhi, one);
m_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);
m_builder.SetInsertPoint(returnBB);
auto qRet = m_builder.CreatePHI(_type, 2, "q.ret");
qRet->addIncoming(zero, entryBB);
qRet->addIncoming(q1, loopBB);
auto rRet = m_builder.CreatePHI(_type, 2, "r.ret");
rRet->addIncoming(r0, entryBB);
rRet->addIncoming(r1, loopBB);
auto ret = m_builder.CreateInsertValue(llvm::UndefValue::get(retType), qRet, 0, "ret0");
ret = m_builder.CreateInsertValue(ret, rRet, 1, "ret");
m_builder.CreateRet(ret);
}
return func;
} }
llvm::Value* Arith256::ternaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3) 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, "arith.exp", getModule());
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);
auto a1 = m_builder.CreateAlloca(Type::Word, nullptr, "a1");
auto a2 = m_builder.CreateAlloca(Type::Word, nullptr, "a2");
auto a3 = m_builder.CreateAlloca(Type::Word, nullptr, "a3");
m_builder.CreateBr(headerBB);
m_builder.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);
m_builder.CreateStore(r, a1);
m_builder.CreateStore(b, a2);
createCall(m_mul, {a1, a2, a3});
auto r0 = m_builder.CreateLoad(a3, "r0");
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);
m_builder.CreateStore(b, a1);
m_builder.CreateStore(b, a2);
createCall(m_mul, {a1, a2, a3});
auto b1 = m_builder.CreateLoad(a3, "b1");
auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1");
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::Function* Arith256::getAddModFunc()
{
if (!m_addmod)
{
auto i512Ty = m_builder.getIntNTy(512);
llvm::Type* argTypes[] = {Type::Word, Type::Word, Type::Word};
m_addmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "addmod", getModule());
auto x = &m_addmod->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto mod = y->getNextNode();
mod->setName("m");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_addmod);
m_builder.SetInsertPoint(entryBB);
auto x512 = m_builder.CreateZExt(x, i512Ty, "x512");
auto y512 = m_builder.CreateZExt(y, i512Ty, "y512");
auto m512 = m_builder.CreateZExt(mod, i512Ty, "m512");
auto s = m_builder.CreateAdd(x512, y512, "s");
auto d = createCall(getDivFunc(i512Ty), {s, m512});
auto r = m_builder.CreateExtractValue(d, 1, "r");
m_builder.CreateRet(m_builder.CreateTrunc(r, Type::Word));
}
return m_addmod;
}
llvm::Function* Arith256::getMulModFunc()
{
if (!m_mulmod)
{
llvm::Type* argTypes[] = {Type::Word, Type::Word, Type::Word};
m_mulmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mulmod", getModule());
auto i512Ty = m_builder.getIntNTy(512);
llvm::Type* mul512ArgTypes[] = {Type::WordPtr, Type::WordPtr, i512Ty->getPointerTo()};
auto mul512 = llvm::Function::Create(llvm::FunctionType::get(Type::Void, mul512ArgTypes, false), llvm::Function::ExternalLinkage, "arith_mul512", getModule());
auto x = &m_mulmod->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto mod = y->getNextNode();
mod->setName("mod");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mulmod);
m_builder.SetInsertPoint(entryBB);
auto a1 = m_builder.CreateAlloca(Type::Word);
auto a2 = m_builder.CreateAlloca(Type::Word);
auto a3 = m_builder.CreateAlloca(i512Ty);
m_builder.CreateStore(x, a1);
m_builder.CreateStore(y, a2);
createCall(mul512, {a1, a2, a3});
auto p = m_builder.CreateLoad(a3, "p");
auto m = m_builder.CreateZExt(mod, i512Ty, "m");
auto d = createCall(getDivFunc(i512Ty), {p, m});
auto r = m_builder.CreateExtractValue(d, 1, "r");
m_builder.CreateRet(m_builder.CreateTrunc(r, Type::Word));
}
return m_mulmod;
}
llvm::Value* Arith256::binaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2)
{ {
m_builder.CreateStore(_arg1, m_arg1); m_builder.CreateStore(_arg1, m_arg1);
m_builder.CreateStore(_arg2, m_arg2); m_builder.CreateStore(_arg2, m_arg2);
m_builder.CreateStore(_arg3, m_arg3); m_builder.CreateCall3(_op, m_arg1, m_arg2, m_result);
m_builder.CreateCall4(_op, m_arg1, m_arg2, m_arg3, m_result);
return m_builder.CreateLoad(m_result); return m_builder.CreateLoad(m_result);
} }
@ -61,62 +286,200 @@ llvm::Value* Arith256::mul(llvm::Value* _arg1, llvm::Value* _arg2)
return binaryOp(m_mul, _arg1, _arg2); return binaryOp(m_mul, _arg1, _arg2);
} }
llvm::Value* Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2) std::pair<llvm::Value*, llvm::Value*> Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2)
{ {
return binaryOp(m_div, _arg1, _arg2); auto div = m_builder.CreateExtractValue(createCall(getDivFunc(Type::Word), {_arg1, _arg2}), 0, "div");
auto mod = m_builder.CreateExtractValue(createCall(getDivFunc(Type::Word), {_arg1, _arg2}), 1, "mod");
return std::make_pair(div, mod);
} }
llvm::Value* Arith256::mod(llvm::Value* _arg1, llvm::Value* _arg2) std::pair<llvm::Value*, llvm::Value*> Arith256::sdiv(llvm::Value* _x, llvm::Value* _y)
{ {
return binaryOp(m_mod, _arg1, _arg2); auto xIsNeg = m_builder.CreateICmpSLT(_x, Constant::get(0));
} auto xNeg = m_builder.CreateSub(Constant::get(0), _x);
auto xAbs = m_builder.CreateSelect(xIsNeg, xNeg, _x);
llvm::Value* Arith256::sdiv(llvm::Value* _arg1, llvm::Value* _arg2) auto yIsNeg = m_builder.CreateICmpSLT(_y, Constant::get(0));
{ auto yNeg = m_builder.CreateSub(Constant::get(0), _y);
return binaryOp(m_sdiv, _arg1, _arg2); auto yAbs = m_builder.CreateSelect(yIsNeg, yNeg, _y);
}
llvm::Value* Arith256::smod(llvm::Value* _arg1, llvm::Value* _arg2) auto res = div(xAbs, yAbs);
{
return binaryOp(m_smod, _arg1, _arg2); // the reminder has the same sign as dividend
auto rNeg = m_builder.CreateSub(Constant::get(0), res.second);
res.second = m_builder.CreateSelect(xIsNeg, rNeg, res.second);
auto qNeg = m_builder.CreateSub(Constant::get(0), res.first);
auto xyOpposite = m_builder.CreateXor(xIsNeg, yIsNeg);
res.first = m_builder.CreateSelect(xyOpposite, qNeg, res.first);
return res;
} }
llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2) llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2)
{ {
return binaryOp(m_exp, _arg1, _arg2); return createCall(getExpFunc(), {_arg1, _arg2});
} }
llvm::Value* Arith256::addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3) llvm::Value* Arith256::addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3)
{ {
return ternaryOp(m_addmod, _arg1, _arg2, _arg3); return createCall(getAddModFunc(), {_arg1, _arg2, _arg3});
} }
llvm::Value* Arith256::mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3) llvm::Value* Arith256::mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3)
{ {
return ternaryOp(m_mulmod, _arg1, _arg2, _arg3); return createCall(getMulModFunc(), {_arg1, _arg2, _arg3});
} }
namespace namespace
{ {
using s256 = boost::multiprecision::int256_t; #ifdef __SIZEOF_INT128__
using uint128 = __uint128_t;
#else
struct uint128
{
uint64_t lo = 0;
uint64_t hi = 0;
uint128(uint64_t lo) : lo(lo) {}
uint128 operator+(uint128 a)
{
uint128 r = 0;
bool overflow = lo > std::numeric_limits<uint64_t>::max() - a.lo;
r.lo = lo + a.lo;
r.hi = hi + a.hi + overflow;
return r;
}
uint128 operator>>(int s)
{
assert(s == 64);
return hi;
}
uint128 operator<<(int s)
{
assert(s == 64);
uint128 r = 0;
r.hi = lo;
return r;
}
explicit operator uint64_t() { return lo; }
static uint128 mul(uint64_t a, uint64_t b)
{
auto x_lo = 0xFFFFFFFF & a;
auto y_lo = 0xFFFFFFFF & b;
auto x_hi = a >> 32;
auto y_hi = b >> 32;
auto t1 = x_lo * y_lo;
auto t2 = x_lo * y_hi;
auto t3 = x_hi * y_lo;
auto t4 = x_hi * y_hi;
auto lo = (uint32_t)t1;
auto mid = (uint64_t)(t1 >> 32) + (uint32_t)t2 + (uint32_t)t3;
auto hi = (uint64_t)(t2 >> 32) + (t3 >> 32) + t4 + (mid >> 32);
uint128 r = 0;
r.lo = (uint64_t)lo + (mid << 32);
r.hi = hi;
return r;
}
uint128 operator*(uint128 a)
{
auto t1 = mul(lo, a.lo);
auto t2 = mul(lo, a.hi);
auto t3 = mul(hi, a.lo);
return t1 + (t2 << 64) + (t3 << 64);
}
};
#endif
struct uint256
{
uint64_t lo = 0;
uint64_t mid = 0;
uint128 hi = 0;
uint256(uint64_t lo, uint64_t mid, uint128 hi): lo(lo), mid(mid), hi(hi) {}
uint256(uint128 n)
{
lo = (uint64_t) n;
mid = (uint64_t) (n >> 64);
}
explicit operator uint128()
{
uint128 r = lo;
r |= ((uint128) mid) << 64;
return r;
}
uint256 operator+(uint256 a)
{
auto _lo = (uint128) lo + a.lo;
auto _mid = (uint128) mid + a.mid + (_lo >> 64);
auto _hi = hi + a.hi + (_mid >> 64);
return {(uint64_t)_lo, (uint64_t)_mid, _hi};
}
uint256 lo2hi()
{
hi = (uint128)*this;
lo = 0;
mid = 0;
return *this;
}
};
struct uint512
{
uint128 lo;
uint128 mid;
uint256 hi;
};
inline s256 u2s(u256 _u) uint256 mul(uint256 x, uint256 y)
{ {
static const bigint c_end = (bigint)1 << 256; auto t1 = (uint128) x.lo * y.lo;
static const u256 c_send = (u256)1 << 255; auto t2 = (uint128) x.lo * y.mid;
if (_u < c_send) auto t3 = (uint128) x.lo * y.hi;
return (s256)_u; auto t4 = (uint128) x.mid * y.lo;
else auto t5 = (uint128) x.mid * y.mid;
return (s256)-(c_end - _u); auto t6 = (uint128) x.mid * y.hi;
auto t7 = x.hi * y.lo;
auto t8 = x.hi * y.mid;
auto lo = (uint64_t) t1;
auto m1 = (t1 >> 64) + (uint64_t) t2;
auto m2 = (uint64_t) m1;
auto mid = (uint128) m2 + (uint64_t) t4;
auto hi = (t2 >> 64) + t3 + (t4 >> 64) + t5 + (t6 << 64) + t7
+ (t8 << 64) + (m1 >> 64) + (mid >> 64);
return {lo, (uint64_t)mid, hi};
} }
inline u256 s2u(s256 _u) uint512 mul512(uint256 x, uint256 y)
{ {
static const bigint c_end = (bigint)1 << 256; auto x_lo = (uint128) x;
if (_u >= 0) auto y_lo = (uint128) y;
return (u256)_u;
else auto t1 = mul(x_lo, y_lo);
return (u256)(c_end + _u); auto t2 = mul(x_lo, y.hi);
auto t3 = mul(x.hi, y_lo);
auto t4 = mul(x.hi, y.hi);
auto lo = (uint128) t1;
auto mid = (uint256) t1.hi + (uint128) t2 + (uint128) t3;
auto hi = (uint256)t2.hi + t3.hi + t4 + mid.hi;
return {lo, (uint128)mid, hi};
} }
} }
@ -124,77 +487,22 @@ namespace
} }
} }
extern "C" extern "C"
{ {
using namespace dev::eth::jit; using namespace dev::eth::jit;
EXPORT void arith_mul(i256* _arg1, i256* _arg2, i256* o_result) EXPORT void debug(uint64_t a, uint64_t b, uint64_t c, uint64_t d, char z)
{
auto arg1 = llvm2eth(*_arg1);
auto arg2 = llvm2eth(*_arg2);
*o_result = eth2llvm(arg1 * arg2);
}
EXPORT void arith_div(i256* _arg1, i256* _arg2, i256* o_result)
{ {
auto arg1 = llvm2eth(*_arg1); std::cerr << "DEBUG " << z << ": " << d << c << b << a << std::endl;
auto arg2 = llvm2eth(*_arg2);
*o_result = eth2llvm(arg2 == 0 ? arg2 : arg1 / arg2);
} }
EXPORT void arith_mod(i256* _arg1, i256* _arg2, i256* o_result) EXPORT void arith_mul(uint256* _arg1, uint256* _arg2, uint256* o_result)
{ {
auto arg1 = llvm2eth(*_arg1); *o_result = mul(*_arg1, *_arg2);
auto arg2 = llvm2eth(*_arg2);
*o_result = eth2llvm(arg2 == 0 ? arg2 : arg1 % arg2);
} }
EXPORT void arith_sdiv(i256* _arg1, i256* _arg2, i256* o_result) EXPORT void arith_mul512(uint256* _arg1, uint256* _arg2, uint512* o_result)
{ {
auto arg1 = llvm2eth(*_arg1); *o_result = mul512(*_arg1, *_arg2);
auto arg2 = llvm2eth(*_arg2);
*o_result = eth2llvm(arg2 == 0 ? arg2 : s2u(u2s(arg1) / u2s(arg2)));
} }
EXPORT void arith_smod(i256* _arg1, i256* _arg2, i256* o_result)
{
auto arg1 = llvm2eth(*_arg1);
auto arg2 = llvm2eth(*_arg2);
*o_result = eth2llvm(arg2 == 0 ? arg2 : s2u(u2s(arg1) % u2s(arg2)));
}
EXPORT void arith_exp(i256* _arg1, i256* _arg2, i256* o_result)
{
bigint left = llvm2eth(*_arg1);
bigint right = llvm2eth(*_arg2);
auto ret = static_cast<u256>(boost::multiprecision::powm(left, right, bigint(2) << 256));
*o_result = eth2llvm(ret);
}
EXPORT void arith_mulmod(i256* _arg1, i256* _arg2, i256* _arg3, i256* o_result)
{
auto arg1 = llvm2eth(*_arg1);
auto arg2 = llvm2eth(*_arg2);
auto arg3 = llvm2eth(*_arg3);
if (arg3 != 0)
*o_result = eth2llvm(u256((bigint(arg1) * bigint(arg2)) % arg3));
else
*o_result = {};
}
EXPORT void arith_addmod(i256* _arg1, i256* _arg2, i256* _arg3, i256* o_result)
{
auto arg1 = llvm2eth(*_arg1);
auto arg2 = llvm2eth(*_arg2);
auto arg3 = llvm2eth(*_arg3);
if (arg3 != 0)
*o_result = eth2llvm(u256((bigint(arg1) + bigint(arg2)) % arg3));
else
*o_result = {};
}
} }

29
evmjit/libevmjit/Arith256.h

@ -13,29 +13,32 @@ class Arith256 : public CompilerHelper
{ {
public: public:
Arith256(llvm::IRBuilder<>& _builder); Arith256(llvm::IRBuilder<>& _builder);
virtual ~Arith256();
llvm::Value* mul(llvm::Value* _arg1, llvm::Value* _arg2); llvm::Value* mul(llvm::Value* _arg1, llvm::Value* _arg2);
llvm::Value* div(llvm::Value* _arg1, llvm::Value* _arg2); std::pair<llvm::Value*, llvm::Value*> div(llvm::Value* _arg1, llvm::Value* _arg2);
llvm::Value* mod(llvm::Value* _arg1, llvm::Value* _arg2); std::pair<llvm::Value*, llvm::Value*> sdiv(llvm::Value* _arg1, llvm::Value* _arg2);
llvm::Value* sdiv(llvm::Value* _arg1, llvm::Value* _arg2);
llvm::Value* smod(llvm::Value* _arg1, llvm::Value* _arg2);
llvm::Value* exp(llvm::Value* _arg1, llvm::Value* _arg2); llvm::Value* exp(llvm::Value* _arg1, llvm::Value* _arg2);
llvm::Value* mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3); llvm::Value* mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3);
llvm::Value* addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3); llvm::Value* addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3);
void debug(llvm::Value* _value, char _c);
private: private:
llvm::Function* getDivFunc(llvm::Type* _type);
llvm::Function* getExpFunc();
llvm::Function* getAddModFunc();
llvm::Function* getMulModFunc();
llvm::Value* binaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2); llvm::Value* binaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2);
llvm::Value* ternaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3);
llvm::Function* m_mul; llvm::Function* m_mul;
llvm::Function* m_div;
llvm::Function* m_mod; llvm::Function* m_div = nullptr;
llvm::Function* m_sdiv; llvm::Function* m_div512 = nullptr;
llvm::Function* m_smod; llvm::Function* m_exp = nullptr;
llvm::Function* m_exp; llvm::Function* m_addmod = nullptr;
llvm::Function* m_mulmod; llvm::Function* m_mulmod = nullptr;
llvm::Function* m_addmod; llvm::Function* m_debug = nullptr;
llvm::Value* m_arg1; llvm::Value* m_arg1;
llvm::Value* m_arg2; llvm::Value* m_arg2;

18
evmjit/libevmjit/CMakeLists.txt

@ -11,15 +11,25 @@ if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set_source_files_properties(Cache.cpp PROPERTIES COMPILE_FLAGS -fno-rtti) set_source_files_properties(Cache.cpp PROPERTIES COMPILE_FLAGS -fno-rtti)
endif() endif()
find_package(Git)
if(GIT_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} describe --dirty --always
OUTPUT_VARIABLE EVMJIT_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
if(NOT EVMJIT_VERSION)
set(EVMJIT_VERSION "unknown")
endif()
message("EVM JIT version: ${EVMJIT_VERSION}")
add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS}) add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS})
set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs") set_target_properties(${TARGET_NAME} PROPERTIES VERSION ${EVMJIT_VERSION} FOLDER "libs")
include_directories(${LLVM_INCLUDE_DIRS}) include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS}) target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS})
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib) install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin)
install(FILES ${INTERFACE_HEADERS} DESTINATION include/${TARGET_NAME}) #install(FILES ${INTERFACE_HEADERS} DESTINATION include/${TARGET_NAME})

31
evmjit/libevmjit/Common.h

@ -1,7 +1,14 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <boost/multiprecision/cpp_int.hpp> #include <tuple>
#include <cstdint>
#ifdef _MSC_VER
#define EXPORT __declspec(dllexport)
#else
#define EXPORT
#endif
namespace dev namespace dev
{ {
@ -12,8 +19,7 @@ namespace jit
using byte = uint8_t; using byte = uint8_t;
using bytes = std::vector<byte>; using bytes = std::vector<byte>;
using u256 = boost::multiprecision::uint256_t; using bytes_ref = std::tuple<byte const*, size_t>;
using bigint = boost::multiprecision::cpp_int;
struct NoteChannel {}; // FIXME: Use some log library? struct NoteChannel {}; // FIXME: Use some log library?
@ -23,18 +29,21 @@ enum class ReturnCode
Return = 1, Return = 1,
Suicide = 2, Suicide = 2,
BadJumpDestination = 101, OutOfGas = -1,
OutOfGas = 102, BadJumpDestination = -2,
StackTooSmall = 103, StackTooSmall = -3,
BadInstruction = 104, BadInstruction = -4,
LLVMConfigError = -5,
LLVMCompileError = -6,
LLVMLinkError = -7,
UnexpectedException = -8,
LLVMConfigError = 201, LinkerWorkaround = -299,
LLVMCompileError = 202,
LLVMLinkError = 203,
}; };
/// Representation of 256-bit value binary compatible with LLVM i256 /// Representation of 256-bit value binary compatible with LLVM i256
// TODO: Replace with h256
struct i256 struct i256
{ {
uint64_t a = 0; uint64_t a = 0;

54
evmjit/libevmjit/Compiler.cpp

@ -4,6 +4,7 @@
#include <functional> #include <functional>
#include <fstream> #include <fstream>
#include <chrono> #include <chrono>
#include <sstream>
#include <llvm/ADT/PostOrderIterator.h> #include <llvm/ADT/PostOrderIterator.h>
#include <llvm/IR/CFG.h> #include <llvm/IR/CFG.h>
@ -90,6 +91,7 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
} }
} }
// TODO: Create Stop basic block on demand
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc); m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
} }
@ -272,7 +274,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
auto lhs = stack.pop(); auto lhs = stack.pop();
auto rhs = stack.pop(); auto rhs = stack.pop();
auto res = _arith.div(lhs, rhs); auto res = _arith.div(lhs, rhs);
stack.push(res); stack.push(res.first);
break; break;
} }
@ -281,7 +283,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
auto lhs = stack.pop(); auto lhs = stack.pop();
auto rhs = stack.pop(); auto rhs = stack.pop();
auto res = _arith.sdiv(lhs, rhs); auto res = _arith.sdiv(lhs, rhs);
stack.push(res); stack.push(res.first);
break; break;
} }
@ -289,8 +291,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
{ {
auto lhs = stack.pop(); auto lhs = stack.pop();
auto rhs = stack.pop(); auto rhs = stack.pop();
auto res = _arith.mod(lhs, rhs); auto res = _arith.div(lhs, rhs);
stack.push(res); stack.push(res.second);
break; break;
} }
@ -298,8 +300,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
{ {
auto lhs = stack.pop(); auto lhs = stack.pop();
auto rhs = stack.pop(); auto rhs = stack.pop();
auto res = _arith.smod(lhs, rhs); auto res = _arith.sdiv(lhs, rhs);
stack.push(res); stack.push(res.second);
break; break;
} }
@ -455,14 +457,15 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
// test for word >> (k * 8 + 7) // test for word >> (k * 8 + 7)
auto bitpos = m_builder.CreateAdd(k32x8, m_builder.getInt64(7), "bitpos"); auto bitpos = m_builder.CreateAdd(k32x8, m_builder.getInt64(7), "bitpos");
auto bittester = m_builder.CreateShl(Constant::get(1), bitpos); auto bitposEx = m_builder.CreateZExt(bitpos, Type::Word);
auto bittester = m_builder.CreateShl(Constant::get(1), bitposEx);
auto bitresult = m_builder.CreateAnd(word, bittester); auto bitresult = m_builder.CreateAnd(word, bittester);
auto bittest = m_builder.CreateICmpUGT(bitresult, Constant::get(0)); auto bittest = m_builder.CreateICmpUGT(bitresult, Constant::get(0));
// FIXME: The following does not work - LLVM bug, report! // FIXME: The following does not work - LLVM bug, report!
//auto bitval = m_builder.CreateLShr(word, bitpos, "bitval"); //auto bitval = m_builder.CreateLShr(word, bitpos, "bitval");
//auto bittest = m_builder.CreateTrunc(bitval, Type::Bool, "bittest"); //auto bittest = m_builder.CreateTrunc(bitval, Type::Bool, "bittest");
auto mask_ = m_builder.CreateShl(Constant::get(1), bitpos); auto mask_ = m_builder.CreateShl(Constant::get(1), bitposEx);
auto mask = m_builder.CreateSub(mask_, Constant::get(1), "mask"); auto mask = m_builder.CreateSub(mask_, Constant::get(1), "mask");
auto negmask = m_builder.CreateXor(mask, llvm::ConstantInt::getAllOnesValue(Type::Word), "negmask"); auto negmask = m_builder.CreateXor(mask, llvm::ConstantInt::getAllOnesValue(Type::Word), "negmask");
@ -636,20 +639,29 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
case Instruction::CALLER: case Instruction::CALLER:
case Instruction::ORIGIN: case Instruction::ORIGIN:
case Instruction::CALLVALUE: case Instruction::CALLVALUE:
case Instruction::CALLDATASIZE:
case Instruction::CODESIZE:
case Instruction::GASPRICE: case Instruction::GASPRICE:
case Instruction::COINBASE: case Instruction::COINBASE:
case Instruction::TIMESTAMP:
case Instruction::NUMBER:
case Instruction::DIFFICULTY: case Instruction::DIFFICULTY:
case Instruction::GASLIMIT: case Instruction::GASLIMIT:
case Instruction::NUMBER:
case Instruction::TIMESTAMP:
{ {
// Pushes an element of runtime data on stack // Pushes an element of runtime data on stack
stack.push(_runtimeManager.get(inst)); auto value = _runtimeManager.get(inst);
value = m_builder.CreateZExt(value, Type::Word);
stack.push(value);
break; break;
} }
case Instruction::CODESIZE:
// TODO: Use constant
stack.push(_runtimeManager.getCodeSize());
break;
case Instruction::CALLDATASIZE:
stack.push(_runtimeManager.getCallDataSize());
break;
case Instruction::BLOCKHASH: case Instruction::BLOCKHASH:
{ {
auto number = stack.pop(); auto number = stack.pop();
@ -681,7 +693,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
auto reqBytes = stack.pop(); auto reqBytes = stack.pop();
auto srcPtr = _runtimeManager.getCallData(); auto srcPtr = _runtimeManager.getCallData();
auto srcSize = _runtimeManager.get(RuntimeData::CallDataSize); auto srcSize = _runtimeManager.getCallDataSize();
_memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); _memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes);
break; break;
@ -694,7 +706,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
auto reqBytes = stack.pop(); auto reqBytes = stack.pop();
auto srcPtr = _runtimeManager.getCode(); // TODO: Code & its size are constants, feature #80814234 auto srcPtr = _runtimeManager.getCode(); // TODO: Code & its size are constants, feature #80814234
auto srcSize = _runtimeManager.get(RuntimeData::CodeSize); auto srcSize = _runtimeManager.getCodeSize();
_memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); _memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes);
break; break;
@ -852,6 +864,18 @@ void Compiler::removeDeadBlocks()
} }
} }
while (sthErased); while (sthErased);
if (m_jumpTableBlock && llvm::pred_begin(m_jumpTableBlock->llvm()) == llvm::pred_end(m_jumpTableBlock->llvm()))
{
m_jumpTableBlock->llvm()->eraseFromParent();
m_jumpTableBlock.reset();
}
if (m_badJumpBlock && llvm::pred_begin(m_badJumpBlock->llvm()) == llvm::pred_end(m_badJumpBlock->llvm()))
{
m_badJumpBlock->llvm()->eraseFromParent();
m_badJumpBlock.reset();
}
} }
void Compiler::dumpCFGifRequired(std::string const& _dotfilePath) void Compiler::dumpCFGifRequired(std::string const& _dotfilePath)

25
evmjit/libevmjit/ExecutionEngine.cpp

@ -1,6 +1,7 @@
#include "ExecutionEngine.h" #include "ExecutionEngine.h"
#include <chrono> #include <chrono>
#include <cstdlib> // env options
#include <llvm/IR/Module.h> #include <llvm/IR/Module.h>
#include <llvm/ADT/Triple.h> #include <llvm/ADT/Triple.h>
@ -19,13 +20,6 @@
#include "Compiler.h" #include "Compiler.h"
#include "Cache.h" #include "Cache.h"
#if defined(NDEBUG)
#define DEBUG_ENV_OPTION(name) false
#else
#include <cstdlib>
#define DEBUG_ENV_OPTION(name) (std::getenv(#name) != nullptr)
#endif
namespace dev namespace dev
{ {
namespace eth namespace eth
@ -67,13 +61,21 @@ std::string codeHash(bytes const& _code)
return std::to_string(hash); return std::to_string(hash);
} }
bool getEnvOption(char const* _name, bool _default)
{
auto var = std::getenv(_name);
if (!var)
return _default;
return std::strtol(var, nullptr, 10) != 0;
}
} }
ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _env) ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _env)
{ {
static std::unique_ptr<llvm::ExecutionEngine> ee; // TODO: Use Managed Objects from LLVM? static std::unique_ptr<llvm::ExecutionEngine> ee; // TODO: Use Managed Objects from LLVM?
static auto debugDumpModule = DEBUG_ENV_OPTION(EVMJIT_DUMP_MODULE); static auto debugDumpModule = getEnvOption("EVMJIT_DUMP", false);
static bool objectCacheEnabled = !DEBUG_ENV_OPTION(EVMJIT_CACHE_OFF); static auto objectCacheEnabled = getEnvOption("EVMJIT_CACHE", true);
auto mainFuncName = codeHash(_code); auto mainFuncName = codeHash(_code);
EntryFuncPtr entryFuncPtr{}; EntryFuncPtr entryFuncPtr{};
@ -136,7 +138,10 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en
auto returnCode = runEntryFunc(entryFuncPtr, &runtime); auto returnCode = runEntryFunc(entryFuncPtr, &runtime);
if (returnCode == ReturnCode::Return) if (returnCode == ReturnCode::Return)
this->returnData = runtime.getReturnData(); {
returnData = runtime.getReturnData(); // Save reference to return data
std::swap(m_memory, runtime.getMemory()); // Take ownership of memory
}
auto executionEndTime = std::chrono::high_resolution_clock::now(); auto executionEndTime = std::chrono::high_resolution_clock::now();
clog(JIT) << " + " << std::chrono::duration_cast<std::chrono::milliseconds>(executionEndTime - executionStartTime).count() << " ms\n"; clog(JIT) << " + " << std::chrono::duration_cast<std::chrono::milliseconds>(executionEndTime - executionStartTime).count() << " ms\n";

10
evmjit/libevmjit/ExecutionEngine.h

@ -16,9 +16,15 @@ public:
ExecutionEngine(ExecutionEngine const&) = delete; ExecutionEngine(ExecutionEngine const&) = delete;
void operator=(ExecutionEngine) = delete; void operator=(ExecutionEngine) = delete;
ReturnCode run(bytes const& _code, RuntimeData* _data, Env* _env); EXPORT ReturnCode run(bytes const& _code, RuntimeData* _data, Env* _env);
bytes returnData; /// Reference to returned data (RETURN opcode used)
bytes_ref returnData;
private:
/// After execution, if RETURN used, memory is moved there
/// to allow client copy the returned data
bytes m_memory;
}; };
} }

476
evmjit/libevmjit/Memory.cpp

@ -1,237 +1,239 @@
#include "Memory.h" #include "Memory.h"
#include <vector> #include <vector>
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <cstdint> #include <cstdint>
#include <cassert> #include <cassert>
#include <llvm/IR/GlobalVariable.h> #include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/Function.h> #include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include "Type.h" #include "Type.h"
#include "Runtime.h" #include "Runtime.h"
#include "GasMeter.h" #include "GasMeter.h"
#include "Endianness.h" #include "Endianness.h"
#include "RuntimeManager.h" #include "RuntimeManager.h"
namespace dev namespace dev
{ {
namespace eth namespace eth
{ {
namespace jit namespace jit
{ {
Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter): Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter):
RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed
m_gasMeter(_gasMeter) m_gasMeter(_gasMeter)
{ {
llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr}; llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr};
m_resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule()); m_resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule());
llvm::AttrBuilder attrBuilder; llvm::AttrBuilder attrBuilder;
attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly); attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly);
m_resize->setAttributes(llvm::AttributeSet::get(m_resize->getContext(), 1, attrBuilder)); m_resize->setAttributes(llvm::AttributeSet::get(m_resize->getContext(), 1, attrBuilder));
m_require = createRequireFunc(_gasMeter); m_require = createRequireFunc(_gasMeter);
m_loadWord = createFunc(false, Type::Word, _gasMeter); m_loadWord = createFunc(false, Type::Word, _gasMeter);
m_storeWord = createFunc(true, Type::Word, _gasMeter); m_storeWord = createFunc(true, Type::Word, _gasMeter);
m_storeByte = createFunc(true, Type::Byte, _gasMeter); m_storeByte = createFunc(true, Type::Byte, _gasMeter);
} }
llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter) llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter)
{ {
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word}; llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word};
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule()); auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule());
auto rt = func->arg_begin(); auto rt = func->arg_begin();
rt->setName("rt"); rt->setName("rt");
auto offset = rt->getNextNode(); auto offset = rt->getNextNode();
offset->setName("offset"); offset->setName("offset");
auto size = offset->getNextNode(); auto size = offset->getNextNode();
size->setName("size"); size->setName("size");
auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func); auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func);
auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func);
auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func); auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func);
auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func); auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func);
InsertPointGuard guard(m_builder); // Restores insert point at function exit InsertPointGuard guard(m_builder); // Restores insert point at function exit
// BB "Pre": Ignore checks with size 0 // BB "Pre": Ignore checks with size 0
m_builder.SetInsertPoint(preBB); m_builder.SetInsertPoint(preBB);
auto sizeIsZero = m_builder.CreateICmpEQ(size, Constant::get(0)); auto sizeIsZero = m_builder.CreateICmpEQ(size, Constant::get(0));
m_builder.CreateCondBr(sizeIsZero, returnBB, checkBB); m_builder.CreateCondBr(sizeIsZero, returnBB, checkBB);
// BB "Check" // BB "Check"
m_builder.SetInsertPoint(checkBB); m_builder.SetInsertPoint(checkBB);
auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word); auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word);
auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res"); auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res");
auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq"); auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq");
auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1"); auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1");
auto rtPtr = getRuntimeManager().getRuntimePtr(); auto rtPtr = getRuntimeManager().getRuntimePtr();
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4);
auto currSize = m_builder.CreateLoad(sizePtr, "currSize"); auto currSize = m_builder.CreateLoad(sizePtr, "currSize");
auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall"); auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall");
auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded"); auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded");
m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights? m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights?
// BB "Resize" // BB "Resize"
m_builder.SetInsertPoint(resizeBB); m_builder.SetInsertPoint(resizeBB);
// Check gas first // Check gas first
uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res"); uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res");
auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0); auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0);
auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2"); auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2");
auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow"); auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow");
wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired); wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired);
wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq"); wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq");
sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq"); sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq");
auto words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k auto words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k
auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords"); auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords");
_gasMeter.countMemory(newWords); _gasMeter.countMemory(newWords);
// Resize // Resize
m_builder.CreateStore(sizeRequired, sizePtr); m_builder.CreateStore(sizeRequired, sizePtr);
auto newData = m_builder.CreateCall2(m_resize, rt, sizePtr, "newData"); auto newData = m_builder.CreateCall2(m_resize, rt, sizePtr, "newData");
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3);
m_builder.CreateStore(newData, dataPtr); m_builder.CreateStore(newData, dataPtr);
m_builder.CreateBr(returnBB); m_builder.CreateBr(returnBB);
// BB "Return" // BB "Return"
m_builder.SetInsertPoint(returnBB); m_builder.SetInsertPoint(returnBB);
m_builder.CreateRetVoid(); m_builder.CreateRetVoid();
return func; return func;
} }
llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&) llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&)
{ {
auto isWord = _valueType == Type::Word; auto isWord = _valueType == Type::Word;
llvm::Type* storeArgs[] = {Type::RuntimePtr, Type::Word, _valueType}; llvm::Type* storeArgs[] = {Type::RuntimePtr, Type::Word, _valueType};
llvm::Type* loadArgs[] = {Type::RuntimePtr, Type::Word}; llvm::Type* loadArgs[] = {Type::RuntimePtr, Type::Word};
auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload"; 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 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()); auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule());
InsertPointGuard guard(m_builder); // Restores insert point at function exit InsertPointGuard guard(m_builder); // Restores insert point at function exit
m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func)); m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func));
auto rt = func->arg_begin(); auto rt = func->arg_begin();
rt->setName("rt"); rt->setName("rt");
auto index = rt->getNextNode(); auto index = rt->getNextNode();
index->setName("index"); index->setName("index");
auto valueSize = _valueType->getPrimitiveSizeInBits() / 8; auto valueSize = _valueType->getPrimitiveSizeInBits() / 8;
this->require(index, Constant::get(valueSize)); this->require(index, Constant::get(valueSize));
auto ptr = getBytePtr(index); auto ptr = getBytePtr(index);
if (isWord) if (isWord)
ptr = m_builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr"); ptr = m_builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr");
if (_isStore) if (_isStore)
{ {
llvm::Value* value = index->getNextNode(); llvm::Value* value = index->getNextNode();
value->setName("value"); value->setName("value");
if (isWord) if (isWord)
value = Endianness::toBE(m_builder, value); value = Endianness::toBE(m_builder, value);
m_builder.CreateStore(value, ptr); m_builder.CreateStore(value, ptr);
m_builder.CreateRetVoid(); m_builder.CreateRetVoid();
} }
else else
{ {
llvm::Value* ret = m_builder.CreateLoad(ptr); llvm::Value* ret = m_builder.CreateLoad(ptr);
ret = Endianness::toNative(m_builder, ret); ret = Endianness::toNative(m_builder, ret);
m_builder.CreateRet(ret); m_builder.CreateRet(ret);
} }
return func; return func;
} }
llvm::Value* Memory::loadWord(llvm::Value* _addr) llvm::Value* Memory::loadWord(llvm::Value* _addr)
{ {
return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr}); return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr});
} }
void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word)
{ {
createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word}); createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word});
} }
void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word) void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word)
{ {
auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte");
createCall(m_storeByte, {getRuntimeManager().getRuntimePtr(), _addr, byte}); createCall(m_storeByte, {getRuntimeManager().getRuntimePtr(), _addr, byte});
} }
llvm::Value* Memory::getData() llvm::Value* Memory::getData()
{ {
auto rtPtr = getRuntimeManager().getRuntimePtr(); auto rtPtr = getRuntimeManager().getRuntimePtr();
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3);
return m_builder.CreateLoad(dataPtr, "data"); return m_builder.CreateLoad(dataPtr, "data");
} }
llvm::Value* Memory::getSize() llvm::Value* Memory::getSize()
{ {
auto rtPtr = getRuntimeManager().getRuntimePtr(); auto rtPtr = getRuntimeManager().getRuntimePtr();
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4);
return m_builder.CreateLoad(sizePtr, "size"); return m_builder.CreateLoad(sizePtr, "size");
} }
llvm::Value* Memory::getBytePtr(llvm::Value* _index) llvm::Value* Memory::getBytePtr(llvm::Value* _index)
{ {
return m_builder.CreateGEP(getData(), _index, "ptr"); auto idx = m_builder.CreateTrunc(_index, Type::Size, "idx"); // Never allow memory index be a type bigger than i64
} return m_builder.CreateGEP(getData(), idx, "ptr");
}
void Memory::require(llvm::Value* _offset, llvm::Value* _size)
{ void Memory::require(llvm::Value* _offset, llvm::Value* _size)
createCall(m_require, {getRuntimeManager().getRuntimePtr(), _offset, _size}); {
} createCall(m_require, {getRuntimeManager().getRuntimePtr(), _offset, _size});
}
void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx,
llvm::Value* _destMemIdx, llvm::Value* _reqBytes) void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx,
{ llvm::Value* _destMemIdx, llvm::Value* _reqBytes)
require(_destMemIdx, _reqBytes); {
require(_destMemIdx, _reqBytes);
// Additional copy cost
// TODO: This round ups to 32 happens in many places // Additional copy cost
auto copyWords = m_builder.CreateUDiv(m_builder.CreateAdd(_reqBytes, Constant::get(31)), Constant::get(32)); // TODO: This round ups to 32 happens in many places
m_gasMeter.countCopy(copyWords); auto copyWords = m_builder.CreateUDiv(m_builder.CreateAdd(_reqBytes, Constant::get(31)), Constant::get(32));
m_gasMeter.countCopy(copyWords);
// Algorithm:
// isOutsideData = idx256 >= size256 // Algorithm:
// idx64 = trunc idx256 // isOutsideData = idx256 >= size256
// size64 = trunc size256 // idx64 = trunc idx256
// dataLeftSize = size64 - idx64 // safe if not isOutsideData // size64 = trunc size256
// reqBytes64 = trunc _reqBytes // require() handles large values // dataLeftSize = size64 - idx64 // safe if not isOutsideData
// bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min // reqBytes64 = trunc _reqBytes // require() handles large values
// bytesToCopy = select(isOutsideData, 0, bytesToCopy0) // 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::lowPrecision); auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize);
auto size64 = m_builder.CreateTrunc(_srcSize, Type::lowPrecision); auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::lowPrecision);
auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64); auto size64 = m_builder.CreateTrunc(_srcSize, Type::lowPrecision);
auto reqBytes64 = m_builder.CreateTrunc(_reqBytes, Type::lowPrecision); auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64);
auto outOfBound = m_builder.CreateICmpUGT(reqBytes64, dataLeftSize); auto reqBytes64 = m_builder.CreateTrunc(_reqBytes, Type::lowPrecision);
auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes64); auto outOfBound = m_builder.CreateICmpUGT(reqBytes64, dataLeftSize);
auto zero64 = llvm::ConstantInt::get(Type::lowPrecision, 0); // TODO: Cache common constants auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes64);
auto bytesToCopy = m_builder.CreateSelect(isOutsideData, zero64, bytesToCopyInner); auto zero64 = llvm::ConstantInt::get(Type::lowPrecision, 0); // TODO: Cache common constants
auto bytesToCopy = m_builder.CreateSelect(isOutsideData, zero64, bytesToCopyInner);
auto src = m_builder.CreateGEP(_srcPtr, idx64, "src");
auto dst = m_builder.CreateGEP(getData(), _destMemIdx, "dst"); auto src = m_builder.CreateGEP(_srcPtr, idx64, "src");
m_builder.CreateMemCpy(dst, src, bytesToCopy, 0); auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); // Never allow memory index be a type bigger than i64
} auto dst = m_builder.CreateGEP(getData(), dstIdx, "dst");
m_builder.CreateMemCpy(dst, src, bytesToCopy, 0);
} }
}
} }
}
}
extern "C"
{
using namespace dev::eth::jit; extern "C"
{
EXPORT byte* mem_resize(Runtime* _rt, i256* _size) // TODO: Use uint64 as size OR use realloc in LLVM IR using namespace dev::eth::jit;
{
auto size = _size->a; // Trunc to 64-bit EXPORT byte* mem_resize(Runtime* _rt, i256* _size) // TODO: Use uint64 as size OR use realloc in LLVM IR
auto& memory = _rt->getMemory(); {
memory.resize(size); auto size = _size->a; // Trunc to 64-bit
return memory.data(); auto& memory = _rt->getMemory();
} memory.resize(size);
} return memory.data();
}
}

17
evmjit/libevmjit/Runtime.cpp

@ -18,18 +18,19 @@ Runtime::Runtime(RuntimeData* _data, Env* _env) :
m_currJmpBuf(m_jmpBuf) m_currJmpBuf(m_jmpBuf)
{} {}
bytes Runtime::getReturnData() const // FIXME: Reconsider returning by copy bytes_ref Runtime::getReturnData() const
{ {
// TODO: Handle large indexes auto data = m_data.callData;
auto offset = static_cast<size_t>(llvm2eth(m_data.elems[RuntimeData::ReturnDataOffset])); auto size = static_cast<size_t>(m_data.callDataSize);
auto size = static_cast<size_t>(llvm2eth(m_data.elems[RuntimeData::ReturnDataSize]));
assert(offset + size <= m_memory.size() || size == 0); if (data < m_memory.data() || data >= m_memory.data() + m_memory.size() || size == 0)
if (offset + size > m_memory.size()) {
assert(size == 0); // data can be an invalid pointer only if size is 0
m_data.callData = nullptr;
return {}; return {};
}
auto dataBeg = m_memory.begin() + offset; return bytes_ref{data, size};
return {dataBeg, dataBeg + size};
} }
} }

105
evmjit/libevmjit/Runtime.h

@ -1,59 +1,46 @@
#pragma once #pragma once
#include <csetjmp> #include <csetjmp>
#include <vector> #include "RuntimeData.h"
#include "Instruction.h" namespace dev
#include "CompilerHelper.h" {
#include "Utils.h" namespace eth
#include "Type.h" {
#include "RuntimeData.h" namespace jit
{
#ifdef _MSC_VER using StackImpl = std::vector<i256>;
#define EXPORT __declspec(dllexport) using MemoryImpl = bytes;
#else using jmp_buf_ref = decltype(&std::jmp_buf{}[0]);
#define EXPORT
#endif class Runtime
{
namespace dev public:
{ Runtime(RuntimeData* _data, Env* _env);
namespace eth
{ Runtime(const Runtime&) = delete;
namespace jit Runtime& operator=(const Runtime&) = delete;
{
StackImpl& getStack() { return m_stack; }
using StackImpl = std::vector<i256>; MemoryImpl& getMemory() { return m_memory; }
using MemoryImpl = bytes; Env* getEnvPtr() { return &m_env; }
using jmp_buf_ref = decltype(&std::jmp_buf{}[0]);
bytes_ref getReturnData() const;
class Runtime jmp_buf_ref getJmpBuf() { return m_jmpBuf; }
{
public: private:
Runtime(RuntimeData* _data, Env* _env); RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract.
Env& m_env; ///< Pointer to environment proxy. Expected by compiled contract.
Runtime(const Runtime&) = delete; jmp_buf_ref m_currJmpBuf; ///< Pointer to jump buffer. Expected by compiled contract.
Runtime& operator=(const Runtime&) = delete; byte* m_memoryData = nullptr;
i256 m_memorySize;
StackImpl& getStack() { return m_stack; } std::jmp_buf m_jmpBuf;
MemoryImpl& getMemory() { return m_memory; } StackImpl m_stack;
Env* getEnvPtr() { return &m_env; } MemoryImpl m_memory;
};
bytes getReturnData() const;
jmp_buf_ref getJmpBuf() { return m_jmpBuf; } }
}
private: }
RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract.
Env& m_env; ///< Pointer to environment proxy. Expected by compiled contract.
jmp_buf_ref m_currJmpBuf; ///< Pointer to jump buffer. Expected by compiled contract.
byte* m_memoryData = nullptr;
i256 m_memorySize;
std::jmp_buf m_jmpBuf;
StackImpl m_stack;
MemoryImpl m_memory;
};
}
}
}

35
evmjit/libevmjit/RuntimeData.h

@ -15,32 +15,41 @@ struct RuntimeData
enum Index enum Index
{ {
Gas, Gas,
GasPrice,
CallData,
CallDataSize,
Address, Address,
Caller, Caller,
Origin, Origin,
CallValue, CallValue,
CallDataSize,
GasPrice,
CoinBase, CoinBase,
TimeStamp,
Number,
Difficulty, Difficulty,
GasLimit, GasLimit,
Number,
Timestamp,
Code,
CodeSize, CodeSize,
_size, SuicideDestAddress = Address, ///< Suicide balance destination address
ReturnData = CallData, ///< Return data pointer (set only in case of RETURN)
ReturnDataOffset = CallValue, // Reuse 2 fields for return data reference ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN)
ReturnDataSize = CallDataSize,
SuicideDestAddress = Address, ///< Suicide balance destination address
}; };
i256 elems[_size] = {}; int64_t gas = 0;
int64_t gasPrice = 0;
byte const* callData = nullptr; 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; byte const* code = nullptr;
uint64_t codeSize = 0;
void set(Index _index, u256 _value) { elems[_index] = eth2llvm(_value); }
u256 get(Index _index) { return llvm2eth(elems[_index]); }
}; };
/// VM Environment (ExtVM) opaque type /// VM Environment (ExtVM) opaque type

91
evmjit/libevmjit/RuntimeManager.cpp

@ -22,9 +22,21 @@ llvm::StructType* RuntimeManager::getRuntimeDataType()
{ {
llvm::Type* elems[] = llvm::Type* elems[] =
{ {
llvm::ArrayType::get(Type::Word, RuntimeData::_size), // i256[] Type::Size, // gas
Type::BytePtr, // callData Type::Size, // gasPrice
Type::BytePtr // code 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"); type = llvm::StructType::create(elems, "RuntimeData");
} }
@ -56,19 +68,21 @@ llvm::Twine getName(RuntimeData::Index _index)
switch (_index) switch (_index)
{ {
default: return "data"; default: return "data";
case RuntimeData::Gas: return "gas";
case RuntimeData::Address: return "address"; case RuntimeData::Address: return "address";
case RuntimeData::Caller: return "caller"; case RuntimeData::Caller: return "caller";
case RuntimeData::Origin: return "origin"; case RuntimeData::Origin: return "origin";
case RuntimeData::CallValue: return "callvalue"; case RuntimeData::CallValue: return "callvalue";
case RuntimeData::CallDataSize: return "calldatasize";
case RuntimeData::GasPrice: return "gasprice"; case RuntimeData::GasPrice: return "gasprice";
case RuntimeData::CoinBase: return "coinbase"; case RuntimeData::CoinBase: return "coinbase";
case RuntimeData::TimeStamp: return "timestamp";
case RuntimeData::Number: return "number";
case RuntimeData::Difficulty: return "difficulty"; case RuntimeData::Difficulty: return "difficulty";
case RuntimeData::GasLimit: return "gaslimit"; case RuntimeData::GasLimit: return "gaslimit";
case RuntimeData::CodeSize: return "codesize"; case RuntimeData::CallData: return "callData";
case RuntimeData::Code: return "code";
case RuntimeData::CodeSize: return "code";
case RuntimeData::CallDataSize: return "callDataSize";
case RuntimeData::Gas: return "gas";
case RuntimeData::Number: return "number";
case RuntimeData::Timestamp: return "timestamp";
} }
} }
} }
@ -100,7 +114,9 @@ llvm::Value* RuntimeManager::getDataPtr()
return m_dataPtr; return m_dataPtr;
auto rtPtr = getRuntimePtr(); auto rtPtr = getRuntimePtr();
return m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 0), "data"); auto dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 0), "data");
assert(dataPtr->getType() == getRuntimeDataType()->getPointerTo());
return dataPtr;
} }
llvm::Value* RuntimeManager::getEnvPtr() llvm::Value* RuntimeManager::getEnvPtr()
@ -111,24 +127,33 @@ llvm::Value* RuntimeManager::getEnvPtr()
llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index) llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index)
{ {
llvm::Value* idxList[] = {m_builder.getInt32(0), m_builder.getInt32(0), m_builder.getInt32(_index)}; auto ptr = getBuilder().CreateStructGEP(getDataPtr(), _index);
return m_builder.CreateInBoundsGEP(getDataPtr(), idxList, getName(_index) + "Ptr"); assert(getRuntimeDataType()->getElementType(_index)->getPointerTo() == ptr->getType());
return ptr;
} }
llvm::Value* RuntimeManager::get(RuntimeData::Index _index) llvm::Value* RuntimeManager::get(RuntimeData::Index _index)
{ {
return m_builder.CreateLoad(getPtr(_index), getName(_index)); return getBuilder().CreateLoad(getPtr(_index), getName(_index));
} }
void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value) void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value)
{ {
m_builder.CreateStore(_value, getPtr(_index)); auto ptr = getPtr(_index);
assert(ptr->getType() == _value->getType()->getPointerTo());
getBuilder().CreateStore(_value, ptr);
} }
void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size) void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size)
{ {
set(RuntimeData::ReturnDataOffset, _offset); auto memPtr = getBuilder().CreateStructGEP(getRuntimePtr(), 3);
set(RuntimeData::ReturnDataSize, _size); auto mem = getBuilder().CreateLoad(memPtr, "memory");
auto idx = m_builder.CreateTrunc(_offset, Type::Size, "idx"); // Never allow memory index be a type bigger than i64 // TODO: Report bug & fix to LLVM
auto returnDataPtr = getBuilder().CreateGEP(mem, idx);
set(RuntimeData::ReturnData, returnDataPtr);
auto size64 = getBuilder().CreateTrunc(_size, Type::Size);
set(RuntimeData::ReturnDataSize, size64);
} }
void RuntimeManager::registerSuicide(llvm::Value* _balanceAddress) void RuntimeManager::registerSuicide(llvm::Value* _balanceAddress)
@ -146,32 +171,41 @@ llvm::Value* RuntimeManager::get(Instruction _inst)
switch (_inst) switch (_inst)
{ {
default: assert(false); return nullptr; default: assert(false); return nullptr;
case Instruction::GAS: return get(RuntimeData::Gas);
case Instruction::ADDRESS: return get(RuntimeData::Address); case Instruction::ADDRESS: return get(RuntimeData::Address);
case Instruction::CALLER: return get(RuntimeData::Caller); case Instruction::CALLER: return get(RuntimeData::Caller);
case Instruction::ORIGIN: return get(RuntimeData::Origin); case Instruction::ORIGIN: return get(RuntimeData::Origin);
case Instruction::CALLVALUE: return get(RuntimeData::CallValue); case Instruction::CALLVALUE: return get(RuntimeData::CallValue);
case Instruction::CALLDATASIZE: return get(RuntimeData::CallDataSize);
case Instruction::GASPRICE: return get(RuntimeData::GasPrice); case Instruction::GASPRICE: return get(RuntimeData::GasPrice);
case Instruction::COINBASE: return get(RuntimeData::CoinBase); case Instruction::COINBASE: return get(RuntimeData::CoinBase);
case Instruction::TIMESTAMP: return get(RuntimeData::TimeStamp);
case Instruction::NUMBER: return get(RuntimeData::Number);
case Instruction::DIFFICULTY: return get(RuntimeData::Difficulty); case Instruction::DIFFICULTY: return get(RuntimeData::Difficulty);
case Instruction::GASLIMIT: return get(RuntimeData::GasLimit); case Instruction::GASLIMIT: return get(RuntimeData::GasLimit);
case Instruction::CODESIZE: return get(RuntimeData::CodeSize); case Instruction::NUMBER: return get(RuntimeData::Number);
case Instruction::TIMESTAMP: return get(RuntimeData::Timestamp);
} }
} }
llvm::Value* RuntimeManager::getCallData() llvm::Value* RuntimeManager::getCallData()
{ {
auto ptr = getBuilder().CreateStructGEP(getDataPtr(), 1, "calldataPtr"); return get(RuntimeData::CallData);
return getBuilder().CreateLoad(ptr, "calldata");
} }
llvm::Value* RuntimeManager::getCode() llvm::Value* RuntimeManager::getCode()
{ {
auto ptr = getBuilder().CreateStructGEP(getDataPtr(), 2, "codePtr"); return get(RuntimeData::Code);
return getBuilder().CreateLoad(ptr, "code"); }
llvm::Value* RuntimeManager::getCodeSize()
{
auto value = get(RuntimeData::CodeSize);
assert(value->getType() == Type::Size);
return getBuilder().CreateZExt(value, Type::Word);
}
llvm::Value* RuntimeManager::getCallDataSize()
{
auto value = get(RuntimeData::CallDataSize);
assert(value->getType() == Type::Size);
return getBuilder().CreateZExt(value, Type::Word);
} }
llvm::Value* RuntimeManager::getJmpBuf() llvm::Value* RuntimeManager::getJmpBuf()
@ -182,14 +216,15 @@ llvm::Value* RuntimeManager::getJmpBuf()
llvm::Value* RuntimeManager::getGas() llvm::Value* RuntimeManager::getGas()
{ {
return get(RuntimeData::Gas); auto value = get(RuntimeData::Gas);
assert(value->getType() == Type::Size);
return getBuilder().CreateZExt(value, Type::Word);
} }
void RuntimeManager::setGas(llvm::Value* _gas) void RuntimeManager::setGas(llvm::Value* _gas)
{ {
llvm::Value* idxList[] = {m_builder.getInt32(0), m_builder.getInt32(0), m_builder.getInt32(RuntimeData::Gas)}; auto newGas = getBuilder().CreateTrunc(_gas, Type::Size);
auto ptr = m_builder.CreateInBoundsGEP(getDataPtr(), idxList, "gasPtr"); set(RuntimeData::Gas, newGas);
m_builder.CreateStore(_gas, ptr);
} }
} }

2
evmjit/libevmjit/RuntimeManager.h

@ -26,6 +26,8 @@ public:
llvm::Value* getGas(); // TODO: Remove llvm::Value* getGas(); // TODO: Remove
llvm::Value* getCallData(); llvm::Value* getCallData();
llvm::Value* getCode(); llvm::Value* getCode();
llvm::Value* getCodeSize();
llvm::Value* getCallDataSize();
void setGas(llvm::Value* _gas); void setGas(llvm::Value* _gas);
void registerReturnData(llvm::Value* _index, llvm::Value* _size); void registerReturnData(llvm::Value* _index, llvm::Value* _size);

8
evmjit/libevmjit/Stack.cpp

@ -73,7 +73,7 @@ extern "C"
{ {
auto& stack = _rt->getStack(); auto& stack = _rt->getStack();
if (stack.size() < _count) if (stack.size() < _count)
longjmp(_rt->getJmpBuf(), static_cast<uint64_t>(ReturnCode::StackTooSmall)); longjmp(_rt->getJmpBuf(), static_cast<int>(ReturnCode::StackTooSmall));
stack.erase(stack.end() - _count, stack.end()); stack.erase(stack.end() - _count, stack.end());
} }
@ -92,7 +92,7 @@ extern "C"
auto& stack = _rt->getStack(); auto& stack = _rt->getStack();
// TODO: encode _index and stack size in the return code // TODO: encode _index and stack size in the return code
if (stack.size() <= _index) if (stack.size() <= _index)
longjmp(_rt->getJmpBuf(), static_cast<uint64_t>(ReturnCode::StackTooSmall)); longjmp(_rt->getJmpBuf(), static_cast<int>(ReturnCode::StackTooSmall));
*o_ret = *(stack.rbegin() + _index); *o_ret = *(stack.rbegin() + _index);
} }
@ -102,7 +102,7 @@ extern "C"
auto& stack = _rt->getStack(); auto& stack = _rt->getStack();
// TODO: encode _index and stack size in the return code // TODO: encode _index and stack size in the return code
if (stack.size() <= _index) if (stack.size() <= _index)
longjmp(_rt->getJmpBuf(), static_cast<uint64_t>(ReturnCode::StackTooSmall)); longjmp(_rt->getJmpBuf(), static_cast<int>(ReturnCode::StackTooSmall));
*(stack.rbegin() + _index) = *_word; *(stack.rbegin() + _index) = *_word;
} }
@ -116,7 +116,7 @@ extern "C"
index = std::numeric_limits<decltype(index)>::max(); // set max to fill with 0 leter index = std::numeric_limits<decltype(index)>::max(); // set max to fill with 0 leter
auto data = _rtData->callData; auto data = _rtData->callData;
auto size = _rtData->elems[RuntimeData::CallDataSize].a; auto size = _rtData->callDataSize;
for (auto i = 0; i < 32; ++i) for (auto i = 0; i < 32; ++i)
{ {
if (index < size) if (index < size)

26
evmjit/libevmjit/Utils.cpp

@ -8,32 +8,6 @@ namespace eth
namespace jit namespace jit
{ {
u256 llvm2eth(i256 _i)
{
u256 u = 0;
u |= _i.d;
u <<= 64;
u |= _i.c;
u <<= 64;
u |= _i.b;
u <<= 64;
u |= _i.a;
return u;
}
i256 eth2llvm(u256 _u)
{
i256 i;
u256 mask = 0xFFFFFFFFFFFFFFFF;
i.a = static_cast<uint64_t>(_u & mask);
_u >>= 64;
i.b = static_cast<uint64_t>(_u & mask);
_u >>= 64;
i.c = static_cast<uint64_t>(_u & mask);
_u >>= 64;
i.d = static_cast<uint64_t>(_u & mask);
return i;
}
} }
} }

3
evmjit/libevmjit/Utils.h

@ -14,9 +14,6 @@ struct JIT: public NoteChannel { static const char* name() { return "JIT"; } };
//#define clog(CHANNEL) std::cerr //#define clog(CHANNEL) std::cerr
#define clog(CHANNEL) std::ostream(nullptr) #define clog(CHANNEL) std::ostream(nullptr)
u256 llvm2eth(i256);
i256 eth2llvm(u256);
} }
} }
} }

47
evmjit/libevmjit/interface.cpp

@ -1,34 +1,41 @@
#include "interface.h"
#include <cstdio>
#include "ExecutionEngine.h" #include "ExecutionEngine.h"
extern "C" extern "C"
{ {
evmjit_result evmjit_run(void* _data, void* _env) using namespace dev::eth::jit;
{
using namespace dev::eth::jit;
auto data = static_cast<RuntimeData*>(_data); #ifdef _MSC_VER
#define _ALLOW_KEYWORD_MACROS
#define noexcept throw()
#endif
ExecutionEngine engine; EXPORT void* evmjit_create() noexcept
{
return new(std::nothrow) ExecutionEngine;
}
EXPORT void evmjit_destroy(ExecutionEngine* _engine) noexcept
{
delete _engine;
}
auto codePtr = data->code; EXPORT int evmjit_run(ExecutionEngine* _engine, RuntimeData* _data, Env* _env) noexcept
auto codeSize = data->elems[RuntimeData::CodeSize].a; {
bytes bytecode; try
bytecode.insert(bytecode.end(), codePtr, codePtr + codeSize); {
auto codePtr = _data->code;
auto codeSize = _data->codeSize;
bytes bytecode;
bytecode.insert(bytecode.end(), codePtr, codePtr + codeSize);
auto returnCode = engine.run(bytecode, data, static_cast<Env*>(_env)); auto returnCode = _engine->run(bytecode, _data, _env);
evmjit_result result = {static_cast<int32_t>(returnCode), 0, nullptr}; return static_cast<int>(returnCode);
if (returnCode == ReturnCode::Return && !engine.returnData.empty()) }
catch(...)
{ {
// TODO: Optimized returning data. Allocating memory on client side by callback function might be a good idea return static_cast<int>(ReturnCode::UnexpectedException);
result.returnDataSize = engine.returnData.size();
result.returnData = std::malloc(result.returnDataSize);
std::memcpy(result.returnData, engine.returnData.data(), result.returnDataSize);
} }
return result;
} }
} }

47
evmjit/libevmjit/interface.h

@ -1,53 +1,12 @@
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
typedef struct evmjit_result void* evmjit_create();
{ int evmjit_run(void* _jit, void* _data, void* _env);
int32_t returnCode; void evmjit_destroy(void* _jit);
uint64_t returnDataSize;
void* returnData;
} evmjit_result;
evmjit_result evmjit_run(void* _data, void* _env);
// JIT object opaque type
typedef struct evm_jit evm_jit;
// Contract execution return code
typedef int evm_jit_return_code;
// Host-endian 256-bit integer type
typedef struct i256 i256;
struct i256
{
char b[33];
};
// Big-endian right aligned 256-bit hash
typedef struct h256 h256;
// Runtime data struct - must be provided by external language (Go, C++, Python)
typedef struct evm_jit_rt evm_jit_rt;
// Runtime callback functions - implementations must be provided by external language (Go, C++, Python)
void evm_jit_rt_sload(evm_jit_rt* _rt, i256* _index, i256* _ret);
void evm_jit_rt_sstore(evm_jit_rt* _rt, i256* _index, i256* _value);
void evm_jit_rt_balance(evm_jit_rt* _rt, h256* _address, i256* _ret);
// And so on...
evm_jit* evm_jit_create(evm_jit_rt* _runtime_data);
evm_jit_return_code evm_jit_execute(evm_jit* _jit);
void evm_jit_get_return_data(evm_jit* _jit, char* _return_data_offset, size_t* _return_data_size);
void evm_jit_destroy(evm_jit* _jit);
#ifdef __cplusplus #ifdef __cplusplus
} }

3
libdevcore/CMakeLists.txt

@ -26,9 +26,8 @@ else()
endif() endif()
target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${Boost_DATE_TIME_LIBRARIES}) #target_link_libraries(${EXECUTABLE} ${Boost_DATE_TIME_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${Boost_SYSTEM_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${Boost_SYSTEM_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${Boost_CHRONO_LIBRARIES})
if (APPLE) if (APPLE)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)

2
libdevcore/Common.cpp

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

2
libdevcrypto/TrieDB.h

@ -49,7 +49,7 @@ extern const h256 EmptyTrie;
/** /**
* @brief Merkle Patricia Tree "Trie": a modifed base-16 Radix tree. * @brief Merkle Patricia Tree "Trie": a modifed base-16 Radix tree.
* This version uses an database backend. * This version uses a database backend.
* Usage: * Usage:
* @code * @code
* GenericTrieDB<MyDB> t(&myDB); * GenericTrieDB<MyDB> t(&myDB);

51
libdevcore/CommonJS.cpp → libethcore/CommonJS.cpp

@ -72,6 +72,31 @@ bytes unpadLeft(bytes _b)
return _b; return _b;
} }
std::string fromRaw(h256 _n, unsigned* _inc)
{
if (_n)
{
std::string s((char const*)_n.data(), 32);
auto l = s.find_first_of('\0');
if (!l)
return "";
if (l != std::string::npos)
{
auto p = s.find_first_not_of('\0', l);
if (!(p == std::string::npos || (_inc && p == 31)))
return "";
if (_inc)
*_inc = (byte)s[31];
s.resize(l);
}
for (auto i: s)
if (i < 32)
return "";
return s;
}
return "";
}
std::string prettyU256(u256 _n) std::string prettyU256(u256 _n)
{ {
unsigned inc = 0; unsigned inc = 0;
@ -98,31 +123,6 @@ std::string prettyU256(u256 _n)
return s.str(); return s.str();
} }
std::string fromRaw(h256 _n, unsigned* _inc)
{
if (_n)
{
std::string s((char const*)_n.data(), 32);
auto l = s.find_first_of('\0');
if (!l)
return "";
if (l != std::string::npos)
{
auto p = s.find_first_not_of('\0', l);
if (!(p == std::string::npos || (_inc && p == 31)))
return "";
if (_inc)
*_inc = (byte)s[31];
s.resize(l);
}
for (auto i: s)
if (i < 32)
return "";
return s;
}
return "";
}
Address fromString(std::string const& _sn) Address fromString(std::string const& _sn)
{ {
if (_sn.size() == 40) if (_sn.size() == 40)
@ -132,3 +132,4 @@ Address fromString(std::string const& _sn)
} }
} }

40
libdevcore/CommonJS.h → libethcore/CommonJS.h

@ -24,9 +24,11 @@
#pragma once #pragma once
#include <string> #include <string>
#include <libethereum/Interface.h> #include <libdevcore/Common.h>
#include "Common.h" #include <libdevcore/FixedHash.h>
#include "CommonData.h" #include <libdevcore/CommonData.h>
#include <libdevcore/CommonIO.h>
#include "CommonEth.h"
namespace dev namespace dev
{ {
@ -94,16 +96,8 @@ template <unsigned N> boost::multiprecision::number<boost::multiprecision::cpp_i
return 0; // FAIL return 0; // FAIL
} }
inline Address jsToAddress(std::string const& _s) { return jsToFixed<sizeof(dev::Address)>(_s); }
inline Public jsToPublic(std::string const& _s) { return jsToFixed<sizeof(dev::Public)>(_s); }
inline Secret jsToSecret(std::string const& _s) { return jsToFixed<sizeof(dev::Secret)>(_s); }
inline u256 jsToU256(std::string const& _s) { return jsToInt<32>(_s); } inline u256 jsToU256(std::string const& _s) { return jsToInt<32>(_s); }
inline std::string jsToBinary(std::string const& _s)
{
return dev::toString(unpadded(jsToBytes(_s)));
}
inline std::string jsToDecimal(std::string const& _s) inline std::string jsToDecimal(std::string const& _s)
{ {
return dev::toString(jsToU256(_s)); return dev::toString(jsToU256(_s));
@ -125,6 +119,29 @@ inline double jsFromFixed(std::string const& _s)
return (double)jsToU256(_s) / (double)(dev::u256(1) << 128); return (double)jsToU256(_s) / (double)(dev::u256(1) << 128);
} }
}
// devcrypto
#include <libdevcrypto/Common.h>
namespace dev
{
inline Public jsToPublic(std::string const& _s) { return jsToFixed<sizeof(dev::Public)>(_s); }
inline Secret jsToSecret(std::string const& _s) { return jsToFixed<sizeof(dev::Secret)>(_s); }
}
// ethcore
namespace dev
{
namespace eth
{
inline Address jsToAddress(std::string const& _s) { return jsToFixed<sizeof(dev::Address)>(_s); }
struct TransactionSkeleton struct TransactionSkeleton
{ {
Address from; Address from;
@ -136,3 +153,4 @@ struct TransactionSkeleton
}; };
} }
}

2
libethereum/All.h

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "Account.h" #include "Account.h"
#include "BlockChain.h" #include "CanonBlockChain.h"
#include "Client.h" #include "Client.h"
#include "Defaults.h" #include "Defaults.h"
#include "Executive.h" #include "Executive.h"

62
libethereum/BlockChain.cpp

@ -53,30 +53,6 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc)
return _out; return _out;
} }
std::map<Address, Account> const& dev::eth::genesisState()
{
static std::map<Address, Account> s_ret;
if (s_ret.empty())
{
// Initialise.
for (auto i: vector<string>({
"dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6",
"e6716f9544a56c530d868e4bfbacb172315bdead",
"b9c015918bdaba24b4ff057a92a3873d6eb201be",
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4",
"2ef47100e0787b915105fd5e3f4ff6752079d5cb",
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826",
"6c386a4b26f73c802f34673f7248bb118f97424a",
"e4157b34ea9615cfbde6b4fda419828124b70c78"
}))
s_ret[Address(fromHex(i))] = Account(u256(1) << 200, Account::NormalCreation);
}
return s_ret;
}
std::unique_ptr<BlockInfo> BlockChain::s_genesis;
boost::shared_mutex BlockChain::x_genesis;
ldb::Slice dev::eth::toSlice(h256 _h, unsigned _sub) ldb::Slice dev::eth::toSlice(h256 _h, unsigned _sub)
{ {
#if ALL_COMPILERS_ARE_CPP11_COMPLIANT #if ALL_COMPILERS_ARE_CPP11_COMPLIANT
@ -91,31 +67,11 @@ ldb::Slice dev::eth::toSlice(h256 _h, unsigned _sub)
#endif #endif
} }
bytes BlockChain::createGenesisBlock() BlockChain::BlockChain(bytes const& _genesisBlock, std::string _path, bool _killExisting)
{
RLPStream block(3);
h256 stateRoot;
{
MemoryDB db;
TrieDB<Address, MemoryDB> state(&db);
state.init();
dev::eth::commit(genesisState(), db, state);
stateRoot = state.root();
}
block.appendList(14)
<< h256() << EmptyListSHA3 << h160() << stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_genesisDifficulty << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42));
block.appendRaw(RLPEmptyList);
block.appendRaw(RLPEmptyList);
return block.out();
}
BlockChain::BlockChain(std::string _path, bool _killExisting)
{ {
// Initialise with the genesis as the last block on the longest chain. // Initialise with the genesis as the last block on the longest chain.
m_genesisHash = BlockChain::genesis().hash; m_genesisBlock = _genesisBlock;
m_genesisBlock = BlockChain::createGenesisBlock(); m_genesisHash = sha3(RLP(m_genesisBlock)[0].data());
open(_path, _killExisting); open(_path, _killExisting);
} }
@ -353,7 +309,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
} }
#endif #endif
// cnote << "Parent " << bi.parentHash << " has " << details(bi.parentHash).children.size() << " children."; // cnote << "Parent " << bi.parentHash << " has " << details(bi.parentHash).children.size() << " children.";
h256s ret; h256s ret;
// This might be the new best block... // This might be the new best block...
@ -377,7 +333,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, bool _post) const h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, bool _post) const
{ {
// cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged(); // cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged();
if (!_from || !_to) if (!_from || !_to)
{ {
return h256s(); return h256s();
@ -386,14 +342,14 @@ h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, boo
h256s back; h256s back;
unsigned fn = details(_from).number; unsigned fn = details(_from).number;
unsigned tn = details(_to).number; unsigned tn = details(_to).number;
// cdebug << "treeRoute" << fn << "..." << tn; // cdebug << "treeRoute" << fn << "..." << tn;
while (fn > tn) while (fn > tn)
{ {
if (_pre) if (_pre)
ret.push_back(_from); ret.push_back(_from);
_from = details(_from).parent; _from = details(_from).parent;
fn--; fn--;
// cdebug << "from:" << fn << _from.abridged(); // cdebug << "from:" << fn << _from.abridged();
} }
while (fn < tn) while (fn < tn)
{ {
@ -401,7 +357,7 @@ h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, boo
back.push_back(_to); back.push_back(_to);
_to = details(_to).parent; _to = details(_to).parent;
tn--; tn--;
// cdebug << "to:" << tn << _to.abridged(); // cdebug << "to:" << tn << _to.abridged();
} }
while (_from != _to) while (_from != _to)
{ {
@ -415,7 +371,7 @@ h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, boo
back.push_back(_to); back.push_back(_to);
fn--; fn--;
tn--; tn--;
// cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged(); // cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged();
} }
if (o_common) if (o_common)
*o_common = _from; *o_common = _from;

18
libethereum/BlockChain.h

@ -69,8 +69,7 @@ ldb::Slice toSlice(h256 _h, unsigned _sub = 0);
class BlockChain class BlockChain
{ {
public: public:
BlockChain(bool _killExisting = false): BlockChain(std::string(), _killExisting) {} BlockChain(bytes const& _genesisBlock, std::string _path, bool _killExisting);
BlockChain(std::string _path, bool _killExisting = false);
~BlockChain(); ~BlockChain();
void reopen(std::string _path, bool _killExisting = false) { close(); open(_path, _killExisting); } void reopen(std::string _path, bool _killExisting = false) { close(); open(_path, _killExisting); }
@ -82,7 +81,7 @@ public:
/// Sync the chain with any incoming blocks. All blocks should, if processed in order /// Sync the chain with any incoming blocks. All blocks should, if processed in order
h256s sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max); h256s sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max);
/// Attempt to import the given block directly into the BlockChain and sync with the state DB. /// Attempt to import the given block directly into the CanonBlockChain and sync with the state DB.
/// @returns the block hashes of any blocks that came into/went out of the canonical block chain. /// @returns the block hashes of any blocks that came into/went out of the canonical block chain.
h256s attemptImport(bytes const& _block, OverlayDB const& _stateDB) noexcept; h256s attemptImport(bytes const& _block, OverlayDB const& _stateDB) noexcept;
@ -131,13 +130,6 @@ public:
/// togther with all their quoted uncles. /// togther with all their quoted uncles.
h256Set allUnclesFrom(h256 _parent) const; h256Set allUnclesFrom(h256 _parent) const;
/// @returns the genesis block header.
static BlockInfo const& genesis() { UpgradableGuard l(x_genesis); if (!s_genesis) { auto gb = createGenesisBlock(); UpgradeGuard ul(l); s_genesis.reset(new BlockInfo); s_genesis->populate(&gb); } return *s_genesis; }
/// @returns the genesis block as its RLP-encoded byte array.
/// @note This is slow as it's constructed anew each call. Consider genesis() instead.
static bytes createGenesisBlock();
/** @returns the hash of all blocks between @a _from and @a _to, all blocks are ordered first by a number of /** @returns the hash of all blocks between @a _from and @a _to, all blocks are ordered first by a number of
* blocks that are parent-to-child, then two sibling blocks, then a number of blocks that are child-to-parent. * blocks that are parent-to-child, then two sibling blocks, then a number of blocks that are child-to-parent.
* *
@ -171,7 +163,7 @@ private:
m_extrasDB->Get(m_readOptions, toSlice(_h, N), &s); m_extrasDB->Get(m_readOptions, toSlice(_h, N), &s);
if (s.empty()) if (s.empty())
{ {
// cout << "Not found in DB: " << _h << endl; // cout << "Not found in DB: " << _h << endl;
return _n; return _n;
} }
@ -208,10 +200,6 @@ private:
ldb::WriteOptions m_writeOptions; ldb::WriteOptions m_writeOptions;
friend std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc); friend std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc);
/// Static genesis info and its lock.
static boost::shared_mutex x_genesis;
static std::unique_ptr<BlockInfo> s_genesis;
}; };
std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc); std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc);

80
libethereum/CanonBlockChain.cpp

@ -1,12 +1,86 @@
/*
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 CanonBlockChain.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "CanonBlockChain.h" #include "CanonBlockChain.h"
CanonBlockChain::CanonBlockChain() #include <boost/filesystem.hpp>
{ #include <libdevcore/Common.h>
#include <libdevcore/RLP.h>
#include <libdevcrypto/FileSystem.h>
#include <libethcore/Exceptions.h>
#include <libethcore/ProofOfWork.h>
#include <libethcore/BlockInfo.h>
#include <liblll/Compiler.h>
#include "State.h"
#include "Defaults.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
#define ETH_CATCH 1
std::map<Address, Account> const& dev::eth::genesisState()
{
static std::map<Address, Account> s_ret;
if (s_ret.empty())
{
// Initialise.
for (auto i: vector<string>({
"dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6",
"e6716f9544a56c530d868e4bfbacb172315bdead",
"b9c015918bdaba24b4ff057a92a3873d6eb201be",
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4",
"2ef47100e0787b915105fd5e3f4ff6752079d5cb",
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826",
"6c386a4b26f73c802f34673f7248bb118f97424a",
"e4157b34ea9615cfbde6b4fda419828124b70c78"
}))
s_ret[Address(fromHex(i))] = Account(u256(1) << 200, Account::NormalCreation);
}
return s_ret;
} }
CanonBlockChain::~CanonBlockChain() std::unique_ptr<BlockInfo> CanonBlockChain::s_genesis;
boost::shared_mutex CanonBlockChain::x_genesis;
bytes CanonBlockChain::createGenesisBlock()
{ {
RLPStream block(3);
h256 stateRoot;
{
MemoryDB db;
TrieDB<Address, MemoryDB> state(&db);
state.init();
dev::eth::commit(genesisState(), db, state);
stateRoot = state.root();
}
block.appendList(14)
<< h256() << EmptyListSHA3 << h160() << stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_genesisDifficulty << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42));
block.appendRaw(RLPEmptyList);
block.appendRaw(RLPEmptyList);
return block.out();
} }
CanonBlockChain::CanonBlockChain(std::string _path, bool _killExisting): BlockChain(CanonBlockChain::createGenesisBlock(), _path, _killExisting)
{
}

76
libethereum/CanonBlockChain.h

@ -1,12 +1,76 @@
#ifndef CANONBLOCKCHAIN_H /*
#define CANONBLOCKCHAIN_H 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.
class CanonBlockChain 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 CanonBlockChain.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#pragma warning(push)
#pragma warning(disable: 4100 4267)
#include <leveldb/db.h>
#pragma warning(pop)
#include <mutex>
#include <libdevcore/Log.h>
#include <libdevcore/Exceptions.h>
#include <libethcore/CommonEth.h>
#include <libethcore/BlockInfo.h>
#include <libdevcore/Guards.h>
#include "BlockDetails.h"
#include "Account.h"
#include "BlockQueue.h"
#include "BlockChain.h"
namespace ldb = leveldb;
namespace dev
{
namespace eth
{
// TODO: Move all this Genesis stuff into Genesis.h/.cpp
std::map<Address, Account> const& genesisState();
/**
* @brief Implements the blockchain database. All data this gives is disk-backed.
* @threadsafe
* @todo Make not memory hog (should actually act as a cache and deallocate old entries).
*/
class CanonBlockChain: public BlockChain
{ {
public: public:
CanonBlockChain(); CanonBlockChain(bool _killExisting = false): CanonBlockChain(std::string(), _killExisting) {}
~CanonBlockChain(); CanonBlockChain(std::string _path, bool _killExisting = false);
~CanonBlockChain() {}
/// @returns the genesis block header.
static BlockInfo const& genesis() { UpgradableGuard l(x_genesis); if (!s_genesis) { auto gb = createGenesisBlock(); UpgradeGuard ul(l); s_genesis.reset(new BlockInfo); s_genesis->populate(&gb); } return *s_genesis; }
/// @returns the genesis block as its RLP-encoded byte array.
/// @note This is slow as it's constructed anew each call. Consider genesis() instead.
static bytes createGenesisBlock();
private:
/// Static genesis info and its lock.
static boost::shared_mutex x_genesis;
static std::unique_ptr<BlockInfo> s_genesis;
}; };
#endif // CANONBLOCKCHAIN_H }
}

7
libethereum/Client.cpp

@ -182,7 +182,7 @@ unsigned Client::installWatch(h256 _h)
Guard l(m_filterLock); Guard l(m_filterLock);
ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0; ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0;
m_watches[ret] = ClientWatch(_h); m_watches[ret] = ClientWatch(_h);
cwatch << "+++" << ret << _h; cwatch << "+++" << ret << _h.abridged();
} }
auto ch = logs(ret); auto ch = logs(ret);
if (ch.empty()) if (ch.empty())
@ -200,7 +200,10 @@ unsigned Client::installWatch(LogFilter const& _f)
{ {
Guard l(m_filterLock); Guard l(m_filterLock);
if (!m_filters.count(h)) if (!m_filters.count(h))
{
cwatch << "FFF" << _f << h.abridged();
m_filters.insert(make_pair(h, _f)); m_filters.insert(make_pair(h, _f));
}
} }
return installWatch(h); return installWatch(h);
} }
@ -226,7 +229,7 @@ void Client::uninstallWatch(unsigned _i)
void Client::noteChanged(h256Set const& _filters) void Client::noteChanged(h256Set const& _filters)
{ {
Guard l(m_filterLock); Guard l(m_filterLock);
// cnote << "noteChanged(" << _filters << ")"; cnote << "noteChanged(" << _filters << ")";
// accrue all changes left in each filter into the watches. // accrue all changes left in each filter into the watches.
for (auto& i: m_watches) for (auto& i: m_watches)
if (_filters.count(i.second.id)) if (_filters.count(i.second.id))

6
libethereum/Client.h

@ -34,7 +34,7 @@
#include <libdevcore/Worker.h> #include <libdevcore/Worker.h>
#include <libevm/FeeStructure.h> #include <libevm/FeeStructure.h>
#include <libp2p/Common.h> #include <libp2p/Common.h>
#include "BlockChain.h" #include "CanonBlockChain.h"
#include "TransactionQueue.h" #include "TransactionQueue.h"
#include "State.h" #include "State.h"
#include "CommonNet.h" #include "CommonNet.h"
@ -228,7 +228,7 @@ public:
/// Get the object representing the current state of Ethereum. /// Get the object representing the current state of Ethereum.
dev::eth::State postState() const { ReadGuard l(x_stateDB); return m_postMine; } dev::eth::State postState() const { ReadGuard l(x_stateDB); return m_postMine; }
/// Get the object representing the current canonical blockchain. /// Get the object representing the current canonical blockchain.
BlockChain const& blockChain() const { return m_bc; } CanonBlockChain const& blockChain() const { return m_bc; }
// Mining stuff: // Mining stuff:
@ -308,7 +308,7 @@ private:
State asOf(unsigned _h) const; State asOf(unsigned _h) const;
VersionChecker m_vc; ///< Dummy object to check & update the protocol version. VersionChecker m_vc; ///< Dummy object to check & update the protocol version.
BlockChain m_bc; ///< Maintains block database. CanonBlockChain m_bc; ///< Maintains block database.
TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain.
BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported). BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported).

7
libethereum/LogFilter.cpp

@ -27,6 +27,13 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
std::ostream& dev::operator<<(std::ostream& _out, LogFilter const& _s)
{
// TODO
_out << "(@" << _s.m_addresses << "#" << _s.m_topics << ">" << _s.m_earliest << "-" << _s.m_latest << "< +" << _s.m_skip << "^" << _s.m_max << ")";
return _out;
}
void LogFilter::streamRLP(RLPStream& _s) const void LogFilter::streamRLP(RLPStream& _s) const
{ {
_s.appendList(6) << m_addresses << m_topics << m_earliest << m_latest << m_max << m_skip; _s.appendList(6) << m_addresses << m_topics << m_earliest << m_latest << m_max << m_skip;

16
libethereum/LogFilter.h

@ -28,6 +28,15 @@
namespace dev namespace dev
{ {
namespace eth
{
class LogFilter;
}
/// Simple stream output for the StateDiff.
std::ostream& operator<<(std::ostream& _out, dev::eth::LogFilter const& _s);
namespace eth namespace eth
{ {
@ -56,14 +65,17 @@ public:
LogFilter withEarliest(int _e) { m_earliest = _e; return *this; } LogFilter withEarliest(int _e) { m_earliest = _e; return *this; }
LogFilter withLatest(int _e) { m_latest = _e; return *this; } LogFilter withLatest(int _e) { m_latest = _e; return *this; }
friend std::ostream& dev::operator<<(std::ostream& _out, dev::eth::LogFilter const& _s);
private: private:
AddressSet m_addresses; AddressSet m_addresses;
std::array<h256Set, 4> m_topics; std::array<h256Set, 4> m_topics;
int m_earliest = 0; int m_earliest = 0;
int m_latest = -1; int m_latest = -1;
unsigned m_max; unsigned m_max = 10;
unsigned m_skip; unsigned m_skip = 0;
}; };
} }
} }

11
libethereum/State.cpp

@ -35,6 +35,7 @@
#include "ExtVM.h" #include "ExtVM.h"
#include "Executive.h" #include "Executive.h"
#include "CachedAddressState.h" #include "CachedAddressState.h"
#include "CanonBlockChain.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
@ -74,18 +75,16 @@ State::State(Address _coinbaseAddress, OverlayDB const& _db, BaseState _bs):
paranoia("beginning of normal construction.", true); paranoia("beginning of normal construction.", true);
if (_bs == BaseState::Genesis) if (_bs == BaseState::CanonGenesis)
{ {
dev::eth::commit(genesisState(), m_db, m_state); dev::eth::commit(genesisState(), m_db, m_state);
m_db.commit(); m_db.commit();
paranoia("after DB commit of normal construction.", true); paranoia("after DB commit of normal construction.", true);
m_previousBlock = BlockChain::genesis(); m_previousBlock = CanonBlockChain::genesis();
} }
else else
{
m_previousBlock.setEmpty(); m_previousBlock.setEmpty();
}
resetCurrent(); resetCurrent();
@ -304,7 +303,7 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi)
// (Most recent state dump might end up being genesis.) // (Most recent state dump might end up being genesis.)
std::vector<h256> chain; std::vector<h256> chain;
while (bi.stateRoot != BlockChain::genesis().hash && m_db.lookup(bi.stateRoot).empty()) // while we don't have the state root of the latest block... while (bi.number != 0 && m_db.lookup(bi.stateRoot).empty()) // while we don't have the state root of the latest block...
{ {
chain.push_back(bi.hash); // push back for later replay. chain.push_back(bi.hash); // push back for later replay.
bi.populate(_bc.block(bi.parentHash)); // move to parent. bi.populate(_bc.block(bi.parentHash)); // move to parent.
@ -698,7 +697,7 @@ void State::commitToMine(BlockChain const& _bc)
RLPStream unclesData; RLPStream unclesData;
unsigned unclesCount = 0; unsigned unclesCount = 0;
if (m_previousBlock != BlockChain::genesis()) if (m_previousBlock.number != 0)
{ {
// Find great-uncles (or second-cousins or whatever they are) - children of great-grandparents, great-great-grandparents... that were not already uncles in previous generations. // Find great-uncles (or second-cousins or whatever they are) - children of great-grandparents, great-great-grandparents... that were not already uncles in previous generations.
// cout << "Checking " << m_previousBlock.hash << ", parent=" << m_previousBlock.parentHash << endl; // cout << "Checking " << m_previousBlock.hash << ", parent=" << m_previousBlock.parentHash << endl;

4
libethereum/State.h

@ -53,7 +53,7 @@ struct StateTrace: public LogChannel { static const char* name() { return "=S=";
struct StateDetail: public LogChannel { static const char* name() { return "/S/"; } static const int verbosity = 14; }; struct StateDetail: public LogChannel { static const char* name() { return "/S/"; } static const int verbosity = 14; };
struct StateSafeExceptions: public LogChannel { static const char* name() { return "(S)"; } static const int verbosity = 21; }; struct StateSafeExceptions: public LogChannel { static const char* name() { return "(S)"; } static const int verbosity = 21; };
enum class BaseState { Empty, Genesis }; enum class BaseState { Empty, CanonGenesis };
/** /**
* @brief Model of the current state of the ledger. * @brief Model of the current state of the ledger.
@ -68,7 +68,7 @@ class State
public: public:
/// Construct state object. /// Construct state object.
State(Address _coinbaseAddress = Address(), OverlayDB const& _db = OverlayDB(), BaseState _bs = BaseState::Genesis); State(Address _coinbaseAddress = Address(), OverlayDB const& _db = OverlayDB(), BaseState _bs = BaseState::CanonGenesis);
/// Construct state object from arbitrary point in blockchain. /// Construct state object from arbitrary point in blockchain.
State(OverlayDB const& _db, BlockChain const& _bc, h256 _hash); State(OverlayDB const& _db, BlockChain const& _bc, h256 _hash);

2
libjsqrc/ethereumjs/dist/ethereum.js

@ -979,7 +979,7 @@ var isValidResponse = function (response) {
!response.error && !response.error &&
response.jsonrpc === '2.0' && response.jsonrpc === '2.0' &&
typeof response.id === 'number' && typeof response.id === 'number' &&
(!!response.result || typeof response.result === 'boolean'); response.result !== undefined; // only undefined is not valid json object
}; };
/// Should be called to create batch payload object /// Should be called to create batch payload object

2
libjsqrc/ethereumjs/dist/ethereum.js.map

File diff suppressed because one or more lines are too long

2
libjsqrc/ethereumjs/dist/ethereum.min.js

File diff suppressed because one or more lines are too long

2
libjsqrc/ethereumjs/lib/jsonrpc.js

@ -45,7 +45,7 @@ var isValidResponse = function (response) {
!response.error && !response.error &&
response.jsonrpc === '2.0' && response.jsonrpc === '2.0' &&
typeof response.id === 'number' && typeof response.id === 'number' &&
(!!response.result || typeof response.result === 'boolean'); response.result !== undefined; // only undefined is not valid json object
}; };
/// Should be called to create batch payload object /// Should be called to create batch payload object

15
libjsqrc/ethereumjs/test/jsonrpc.isValidResponse.js

@ -124,5 +124,20 @@ describe('jsonrpc', function () {
assert.equal(valid, true); assert.equal(valid, true);
}); });
it('should validate jsonrpc response with result field === 0', function () {
// given
var response = {
jsonrpc: '2.0',
id: 1,
result: 0
};
// when
var valid = jsonrpc.isValidResponse(response);
// then
assert.equal(valid, true);
});
}); });
}); });

12
libjsqrc/natspec.js

@ -29,10 +29,20 @@ var getContractMethods = function (address, abi) {
return web3.eth.contract(address, abi); return web3.eth.contract(address, abi);
}; };
var getMethodWithName = function(abi, name) {
for (var i = 0; i < abi.length; i++) {
if (abi[i].name === name) {
return abi[i];
}
}
console.warn('could not find method with name: ' + name);
return undefined;
};
/// Function called to get all contract method input variables /// Function called to get all contract method input variables
/// @returns hashmap with all contract's method input variables /// @returns hashmap with all contract's method input variables
var getContractInputParams = function (abi, methodName, params) { var getContractInputParams = function (abi, methodName, params) {
var method = web3.abi.getMethodWithName(abi, methodName); var method = getMethodWithName(abi, methodName);
return method.inputs.reduce(function (acc, current, index) { return method.inputs.reduce(function (acc, current, index) {
acc[current.name] = params[index]; acc[current.name] = params[index];
return acc; return acc;

2
libp2p/Capability.cpp

@ -39,7 +39,7 @@ Capability::Capability(Session* _s, HostCapabilityFace* _h, unsigned _idOffset):
void Capability::disable(std::string const& _problem) void Capability::disable(std::string const& _problem)
{ {
clogS(NetConnect) << "Disabling capability '" << m_host->name() << "'. Reason:" << _problem; clogS(NetWarn) << "DISABLE: Disabling capability '" << m_host->name() << "'. Reason:" << _problem;
m_enabled = false; m_enabled = false;
} }

6
libp2p/Host.cpp

@ -378,6 +378,8 @@ string Host::pocHost()
void Host::connect(std::string const& _addr, unsigned short _port) noexcept void Host::connect(std::string const& _addr, unsigned short _port) noexcept
{ {
while (isWorking() && !m_run)
this_thread::sleep_for(chrono::milliseconds(50));
if (!m_run) if (!m_run)
return; return;
@ -409,6 +411,8 @@ void Host::connect(std::string const& _addr, unsigned short _port) noexcept
void Host::connect(bi::tcp::endpoint const& _ep) void Host::connect(bi::tcp::endpoint const& _ep)
{ {
while (isWorking() && !m_run)
this_thread::sleep_for(chrono::milliseconds(50));
if (!m_run) if (!m_run)
return; return;
@ -430,6 +434,8 @@ void Host::connect(bi::tcp::endpoint const& _ep)
void Host::connect(std::shared_ptr<Node> const& _n) void Host::connect(std::shared_ptr<Node> const& _n)
{ {
while (isWorking() && !m_run)
this_thread::sleep_for(chrono::milliseconds(50));
if (!m_run) if (!m_run)
return; return;

17
libp2p/Session.cpp

@ -273,24 +273,24 @@ bool Session::interpret(RLP const& _r)
} }
case PingPacket: case PingPacket:
{ {
clogS(NetTriviaSummary) << "Ping"; clogS(NetTriviaSummary) << "Ping";
RLPStream s; RLPStream s;
sealAndSend(prep(s, PongPacket)); sealAndSend(prep(s, PongPacket));
break; break;
} }
case PongPacket: case PongPacket:
m_info.lastPing = std::chrono::steady_clock::now() - m_ping; m_info.lastPing = std::chrono::steady_clock::now() - m_ping;
clogS(NetTriviaSummary) << "Latency: " << chrono::duration_cast<chrono::milliseconds>(m_info.lastPing).count() << " ms"; clogS(NetTriviaSummary) << "Latency: " << chrono::duration_cast<chrono::milliseconds>(m_info.lastPing).count() << " ms";
break; break;
case GetPeersPacket: case GetPeersPacket:
{ {
clogS(NetTriviaSummary) << "GetPeers"; clogS(NetTriviaSummary) << "GetPeers";
m_theyRequestedNodes = true; m_theyRequestedNodes = true;
serviceNodesRequest(); serviceNodesRequest();
break; break;
} }
case PeersPacket: case PeersPacket:
clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)"; clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)";
m_weRequestedNodes = false; m_weRequestedNodes = false;
for (unsigned i = 1; i < _r.itemCount(); ++i) for (unsigned i = 1; i < _r.itemCount(); ++i)
{ {
@ -369,8 +369,13 @@ bool Session::interpret(RLP const& _r)
{ {
auto id = _r[0].toInt<unsigned>(); auto id = _r[0].toInt<unsigned>();
for (auto const& i: m_capabilities) for (auto const& i: m_capabilities)
if (i.second->m_enabled && id >= i.second->m_idOffset && id - i.second->m_idOffset < i.second->hostCapability()->messageCount() && i.second->interpret(id - i.second->m_idOffset, _r)) if (id >= i.second->m_idOffset && id - i.second->m_idOffset < i.second->hostCapability()->messageCount())
return true; {
if (i.second->m_enabled)
return i.second->interpret(id - i.second->m_idOffset, _r);
else
return true;
}
return false; return false;
} }
} }

92
libserpent/compiler.cpp

@ -77,43 +77,12 @@ Node popwrap(Node node) {
return multiToken(nodelist, 2, node.metadata); return multiToken(nodelist, 2, node.metadata);
} }
// Grabs variables
mss getVariables(Node node, mss cur=mss()) {
Metadata m = node.metadata;
// Tokens don't contain any variables
if (node.type == TOKEN)
return cur;
// Don't descend into call fragments
else if (node.val == "lll")
return getVariables(node.args[1], cur);
// At global scope get/set/ref also declare
else if (node.val == "get" || node.val == "set" || node.val == "ref") {
if (node.args[0].type != TOKEN)
err("Variable name must be simple token,"
" not complex expression! " + printSimple(node.args[0]), m);
if (!cur.count(node.args[0].val)) {
cur[node.args[0].val] = utd(cur.size() * 32 + 32);
//std::cerr << node.args[0].val << " " << cur[node.args[0].val] << "\n";
}
}
// Recursively process children
for (unsigned i = 0; i < node.args.size(); i++) {
cur = getVariables(node.args[i], cur);
}
return cur;
}
// Turns LLL tree into tree of code fragments // Turns LLL tree into tree of code fragments
programData opcodeify(Node node, programData opcodeify(Node node,
programAux aux=Aux(), programAux aux=Aux(),
programVerticalAux vaux=verticalAux()) { programVerticalAux vaux=verticalAux()) {
std::string symb = "_"+mkUniqueToken(); std::string symb = "_"+mkUniqueToken();
Metadata m = node.metadata; Metadata m = node.metadata;
// Get variables
if (!aux.vars.size()) {
aux.vars = getVariables(node);
aux.nextVarMem = aux.vars.size() * 32 + 32;
}
// Numbers // Numbers
if (node.type == TOKEN) { if (node.type == TOKEN) {
return pd(aux, nodeToNumeric(node), 1); return pd(aux, nodeToNumeric(node), 1);
@ -121,6 +90,10 @@ programData opcodeify(Node node,
else if (node.val == "ref" || node.val == "get" || node.val == "set") { else if (node.val == "ref" || node.val == "get" || node.val == "set") {
std::string varname = node.args[0].val; std::string varname = node.args[0].val;
// Determine reference to variable // Determine reference to variable
if (!aux.vars.count(node.args[0].val)) {
aux.vars[node.args[0].val] = utd(aux.nextVarMem);
aux.nextVarMem += 32;
}
Node varNode = tkn(aux.vars[varname], m); Node varNode = tkn(aux.vars[varname], m);
//std::cerr << varname << " " << printSimple(varNode) << "\n"; //std::cerr << varname << " " << printSimple(varNode) << "\n";
// Set variable // Set variable
@ -173,8 +146,7 @@ programData opcodeify(Node node,
} }
// Comments do nothing // Comments do nothing
else if (node.val == "comment") { else if (node.val == "comment") {
Node* nodelist = nullptr; return pd(aux, astnode("_", m), 0);
return pd(aux, multiToken(nodelist, 0, m), 0);
} }
// Custom operation sequence // Custom operation sequence
// eg. (ops bytez id msize swap1 msize add 0 swap1 mstore) == alloc // eg. (ops bytez id msize swap1 msize add 0 swap1 mstore) == alloc
@ -371,7 +343,7 @@ Node buildFragmentTree(Node node) {
// Builds a dictionary mapping labels to variable names // Builds a dictionary mapping labels to variable names
programAux buildDict(Node program, programAux aux, int labelLength) { void buildDict(Node program, programAux &aux, int labelLength) {
Metadata m = program.metadata; Metadata m = program.metadata;
// Token // Token
if (program.type == TOKEN) { if (program.type == TOKEN) {
@ -388,30 +360,24 @@ programAux buildDict(Node program, programAux aux, int labelLength) {
} }
// A sub-program (ie. LLL) // A sub-program (ie. LLL)
else if (program.val == "____CODE") { else if (program.val == "____CODE") {
programAux auks = Aux(); int step = aux.step;
aux.step = 0;
for (unsigned i = 0; i < program.args.size(); i++) { for (unsigned i = 0; i < program.args.size(); i++) {
auks = buildDict(program.args[i], auks, labelLength); buildDict(program.args[i], aux, labelLength);
} }
for (std::map<std::string,std::string>::iterator it=auks.vars.begin(); aux.step += step;
it != auks.vars.end();
it++) {
aux.vars[(*it).first] = (*it).second;
}
aux.step += auks.step;
} }
// Normal sub-block // Normal sub-block
else { else {
for (unsigned i = 0; i < program.args.size(); i++) { for (unsigned i = 0; i < program.args.size(); i++) {
aux = buildDict(program.args[i], aux, labelLength); buildDict(program.args[i], aux, labelLength);
} }
} }
return aux;
} }
// Applies that dictionary // Applies that dictionary
Node substDict(Node program, programAux aux, int labelLength) { void substDict(Node program, programAux aux, int labelLength, std::vector<Node> &out) {
Metadata m = program.metadata; Metadata m = program.metadata;
std::vector<Node> out;
std::vector<Node> inner; std::vector<Node> inner;
if (program.type == TOKEN) { if (program.type == TOKEN) {
if (program.val[0] == '$') { if (program.val[0] == '$') {
@ -428,46 +394,32 @@ Node substDict(Node program, programAux aux, int labelLength) {
dist = decimalSub(end, start); dist = decimalSub(end, start);
inner = toByteArr(dist, m, labelLength); inner = toByteArr(dist, m, labelLength);
} }
out.push_back(astnode("_", inner, m)); for (unsigned i = 0; i < inner.size(); i++) out.push_back(inner[i]);
} }
else if (program.val[0] == '~') { } else if (program.val[0] == '~') { }
else if (isNumberLike(program)) { else if (isNumberLike(program)) {
inner = toByteArr(program.val, m); inner = toByteArr(program.val, m);
out.push_back(token("PUSH"+unsignedToDecimal(inner.size()))); out.push_back(token("PUSH"+unsignedToDecimal(inner.size())));
out.push_back(astnode("_", inner, m)); for (unsigned i = 0; i < inner.size(); i++) out.push_back(inner[i]);
} }
else return program; else out.push_back(program);
} }
else { else {
for (unsigned i = 0; i < program.args.size(); i++) { for (unsigned i = 0; i < program.args.size(); i++) {
Node n = substDict(program.args[i], aux, labelLength); substDict(program.args[i], aux, labelLength, out);
if (n.type == TOKEN || n.args.size()) out.push_back(n);
} }
} }
return astnode("_", out, m);
} }
// Compiled fragtree -> compiled fragtree without labels // Compiled fragtree -> compiled fragtree without labels
Node dereference(Node program) { std::vector<Node> dereference(Node program) {
int sz = treeSize(program) * 4; int sz = treeSize(program) * 4;
int labelLength = 1; int labelLength = 1;
while (sz >= 256) { labelLength += 1; sz /= 256; } while (sz >= 256) { labelLength += 1; sz /= 256; }
programAux aux = buildDict(program, Aux(), labelLength); programAux aux = Aux();
return substDict(program, aux, labelLength); buildDict(program, aux, labelLength);
}
// Dereferenced fragtree -> opcodes
std::vector<Node> flatten(Node derefed) {
std::vector<Node> o; std::vector<Node> o;
if (derefed.type == TOKEN) { substDict(program, aux, labelLength, o);
o.push_back(derefed);
}
else {
for (unsigned i = 0; i < derefed.args.size(); i++) {
std::vector<Node> oprime = flatten(derefed.args[i]);
for (unsigned j = 0; j < oprime.size(); j++) o.push_back(oprime[j]);
}
}
return o; return o;
} }
@ -512,12 +464,12 @@ std::vector<Node> deserialize(std::string ser) {
// Fragtree -> bin // Fragtree -> bin
std::string assemble(Node fragTree) { std::string assemble(Node fragTree) {
return serialize(flatten(dereference(fragTree))); return serialize(dereference(fragTree));
} }
// Fragtree -> tokens // Fragtree -> tokens
std::vector<Node> prettyAssemble(Node fragTree) { std::vector<Node> prettyAssemble(Node fragTree) {
return flatten(dereference(fragTree)); return dereference(fragTree);
} }
// LLL -> bin // LLL -> bin

5
libserpent/compiler.h

@ -8,14 +8,11 @@
#include "util.h" #include "util.h"
// Compiled fragtree -> compiled fragtree without labels // Compiled fragtree -> compiled fragtree without labels
Node dereference(Node program); std::vector<Node> dereference(Node program);
// LLL -> fragtree // LLL -> fragtree
Node buildFragmentTree(Node program); Node buildFragmentTree(Node program);
// Dereferenced fragtree -> opcodes
std::vector<Node> flatten(Node derefed);
// opcodes -> bin // opcodes -> bin
std::string serialize(std::vector<Node> codons); std::string serialize(std::vector<Node> codons);

7
libserpent/util.cpp

@ -5,7 +5,6 @@
#include "util.h" #include "util.h"
#include "bignum.h" #include "bignum.h"
#include <fstream> #include <fstream>
#include <string>
#include <cerrno> #include <cerrno>
//Token or value node constructor //Token or value node constructor
@ -260,7 +259,7 @@ std::string get_file_contents(std::string filename)
{ {
std::string contents; std::string contents;
in.seekg(0, std::ios::end); in.seekg(0, std::ios::end);
contents.resize((unsigned)in.tellg()); contents.resize(in.tellg());
in.seekg(0, std::ios::beg); in.seekg(0, std::ios::beg);
in.read(&contents[0], contents.size()); in.read(&contents[0], contents.size());
in.close(); in.close();
@ -274,7 +273,7 @@ void err(std::string errtext, Metadata met) {
std::string err = "Error (file \"" + met.file + "\", line " + std::string err = "Error (file \"" + met.file + "\", line " +
unsignedToDecimal(met.ln + 1) + ", char " + unsignedToDecimal(met.ch) + unsignedToDecimal(met.ln + 1) + ", char " + unsignedToDecimal(met.ch) +
"): " + errtext; "): " + errtext;
std::cerr << err << "\n"; std::cerr << err << std::endl;
throw(err); throw(err);
} }
@ -283,7 +282,7 @@ void warn(std::string errtext, Metadata met) {
std::string err = "Warning (file \"" + met.file + "\", line " + std::string err = "Warning (file \"" + met.file + "\", line " +
unsignedToDecimal(met.ln + 1) + ", char " + unsignedToDecimal(met.ch) + unsignedToDecimal(met.ln + 1) + ", char " + unsignedToDecimal(met.ch) +
"): " + errtext; "): " + errtext;
std::cerr << err << "\n"; std::cerr << err << std::endl;
} }
//Bin to hex //Bin to hex

2
libsolidity/AST.cpp

@ -505,7 +505,7 @@ void FunctionCall::checkTypeRequirements()
// check duplicate names // check duplicate names
for (size_t i = 0; i < m_names.size(); i++) { for (size_t i = 0; i < m_names.size(); i++) {
for (size_t j = i + 1; j < m_names.size(); j++) { for (size_t j = i + 1; j < m_names.size(); j++) {
if (m_names[i] == m_names[j]) if (*m_names[i] == *m_names[j])
BOOST_THROW_EXCEPTION(createTypeError("Duplicate named argument.")); BOOST_THROW_EXCEPTION(createTypeError("Duplicate named argument."));
} }
} }

7
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -28,7 +28,7 @@
#include <liblll/Compiler.h> #include <liblll/Compiler.h>
#include <libethereum/Client.h> #include <libethereum/Client.h>
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
#include <libdevcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <libwhisper/Message.h> #include <libwhisper/Message.h>
#include <libwhisper/WhisperHost.h> #include <libwhisper/WhisperHost.h>
#include <libserpent/funcs.h> #include <libserpent/funcs.h>
@ -342,7 +342,10 @@ std::string WebThreeStubServerBase::eth_call(Json::Value const& _json)
Json::Value WebThreeStubServerBase::eth_changed(int const& _id) Json::Value WebThreeStubServerBase::eth_changed(int const& _id)
{ {
return toJson(client()->checkWatch(_id)); auto entries = client()->checkWatch(_id);
if (entries.size())
cnote << "FIRING WATCH" << _id << entries.size();
return toJson(entries);
} }
std::string WebThreeStubServerBase::eth_codeAt(string const& _address) std::string WebThreeStubServerBase::eth_codeAt(string const& _address)

4
libweb3jsonrpc/WebThreeStubServerBase.h

@ -36,9 +36,9 @@ namespace dev
{ {
class WebThreeNetworkFace; class WebThreeNetworkFace;
class KeyPair; class KeyPair;
struct TransactionSkeleton;
namespace eth namespace eth
{ {
struct TransactionSkeleton;
class Interface; class Interface;
} }
namespace shh namespace shh
@ -122,7 +122,7 @@ public:
std::map<dev::Public, dev::Secret> const& ids() const { return m_ids; } std::map<dev::Public, dev::Secret> const& ids() const { return m_ids; }
protected: protected:
virtual bool authenticate(dev::TransactionSkeleton const& _t); virtual bool authenticate(dev::eth::TransactionSkeleton const& _t);
protected: protected:
virtual dev::eth::Interface* client() = 0; virtual dev::eth::Interface* client() = 0;

45
macdeployfix.sh

@ -0,0 +1,45 @@
#!/bin/bash
# solves problem with macdeployqt on Qt 5.4 RC
# http://qt-project.org/forums/viewthread/50118
BUILD_FOLDER_PATH=$1
BUILD_QML_FOLDER_PATH="$BUILD_FOLDER_PATH/Resources/qml"
BUILD_PLUGINS_FOLDER_PATH="$BUILD_FOLDER_PATH/PlugIns"
if [ ! -d ${BUILD_QML_FOLDER_PATH} ]; then
# we are not using any qml files
# gracefully exit
exit 0
fi
declare -a BROKEN_FILES;
k=0;
for j in $(find ${BUILD_QML_FOLDER_PATH} -name *.dylib); do
BROKEN_FILES[${k}]=$j
((k=k+1))
done
for i in "${BROKEN_FILES[@]}"; do
REPLACE_STRING="$BUILD_FOLDER_PATH/"
APP_CONTENT_FILE=${i//$REPLACE_STRING/""}
IFS='/' read -a array <<< "$APP_CONTENT_FILE"
LENGTH=${#array[@]}
LAST_ITEM_INDEX=$((LENGTH-1))
FILE=${array[${LENGTH} - 1]}
ORIGINE_PATH=$(find ${BUILD_PLUGINS_FOLDER_PATH} -name ${FILE})
ORIGINE_PATH=${ORIGINE_PATH//$REPLACE_STRING/""}
s=""
for((l=0;l<${LAST_ITEM_INDEX};l++)) do
s=$s"../"
done
s=$s$ORIGINE_PATH
echo "s: $s"
REMOVE_BROKEN_ALIAS=$(rm -rf $i)
RESULT=$(ln -s $s $i)
done

14
mix/AppContext.cpp

@ -27,15 +27,16 @@
#include <QQmlComponent> #include <QQmlComponent>
#include <QQmlContext> #include <QQmlContext>
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QQuickWindow> #include <QWindow>
#include "CodeModel.h" #include "CodeModel.h"
#include "FileIo.h" #include "FileIo.h"
#include "ClientModel.h" #include "ClientModel.h"
#include "CodeEditorExtensionManager.h" #include "CodeEditorExtensionManager.h"
#include "Exceptions.h" #include "Exceptions.h"
#include "AppContext.h"
#include "QEther.h" #include "QEther.h"
#include "QVariableDefinition.h"
#include "HttpServer.h" #include "HttpServer.h"
#include "AppContext.h"
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
@ -49,6 +50,7 @@ AppContext::AppContext(QQmlApplicationEngine* _engine)
m_codeModel.reset(new CodeModel(this)); m_codeModel.reset(new CodeModel(this));
m_clientModel.reset(new ClientModel(this)); m_clientModel.reset(new ClientModel(this));
m_fileIo.reset(new FileIo()); m_fileIo.reset(new FileIo());
connect(QApplication::clipboard(), &QClipboard::dataChanged, [this] { emit clipboardChanged();});
} }
AppContext::~AppContext() AppContext::~AppContext()
@ -82,7 +84,7 @@ void AppContext::load()
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager"); qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer"); qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer");
m_applicationEngine->load(QUrl("qrc:/qml/main.qml")); m_applicationEngine->load(QUrl("qrc:/qml/main.qml"));
QQuickWindow *window = qobject_cast<QQuickWindow *>(m_applicationEngine->rootObjects().at(0)); QWindow *window = qobject_cast<QWindow *>(m_applicationEngine->rootObjects().at(0));
window->setIcon(QIcon(":/res/mix_256x256x32.png")); window->setIcon(QIcon(":/res/mix_256x256x32.png"));
appLoaded(); appLoaded();
} }
@ -105,6 +107,12 @@ void AppContext::displayMessageDialog(QString _title, QString _message)
QMetaObject::invokeMethod(dialogWin, "open"); QMetaObject::invokeMethod(dialogWin, "open");
} }
QString AppContext::clipboard() const
{
QClipboard *clipboard = QApplication::clipboard();
return clipboard->text();
}
void AppContext::toClipboard(QString _text) void AppContext::toClipboard(QString _text)
{ {
QClipboard *clipboard = QApplication::clipboard(); QClipboard *clipboard = QApplication::clipboard();

4
mix/AppContext.h

@ -48,6 +48,7 @@ class FileIo;
class AppContext: public QObject class AppContext: public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString clipboard READ clipboard WRITE toClipboard NOTIFY clipboardChanged)
public: public:
AppContext(QQmlApplicationEngine* _engine); AppContext(QQmlApplicationEngine* _engine);
@ -64,10 +65,13 @@ public:
void displayMessageDialog(QString _title, QString _message); void displayMessageDialog(QString _title, QString _message);
/// Copy text to clipboard /// Copy text to clipboard
Q_INVOKABLE void toClipboard(QString _text); Q_INVOKABLE void toClipboard(QString _text);
/// Get text from clipboard
QString clipboard() const;
signals: signals:
/// Triggered once components have been loaded /// Triggered once components have been loaded
void appLoaded(); void appLoaded();
void clipboardChanged();
private: private:
QQmlApplicationEngine* m_applicationEngine; //owned by app QQmlApplicationEngine* m_applicationEngine; //owned by app

41
mix/ClientModel.cpp

@ -22,14 +22,16 @@
#include <QDebug> #include <QDebug>
#include <QQmlContext> #include <QQmlContext>
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QStandardPaths>
#include <jsonrpccpp/server.h> #include <jsonrpccpp/server.h>
#include <libdevcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <libethereum/Transaction.h> #include <libethereum/Transaction.h>
#include "AppContext.h" #include "AppContext.h"
#include "DebuggingStateWrapper.h" #include "DebuggingStateWrapper.h"
#include "Exceptions.h" #include "Exceptions.h"
#include "QContractDefinition.h" #include "QContractDefinition.h"
#include "QVariableDeclaration.h" #include "QVariableDeclaration.h"
#include "QVariableDefinition.h"
#include "ContractCallDataEncoder.h" #include "ContractCallDataEncoder.h"
#include "CodeModel.h" #include "CodeModel.h"
#include "ClientModel.h" #include "ClientModel.h"
@ -83,7 +85,7 @@ ClientModel::ClientModel(AppContext* _context):
qRegisterMetaType<TransactionLogEntry*>("TransactionLogEntry"); qRegisterMetaType<TransactionLogEntry*>("TransactionLogEntry");
connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection); connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection);
m_client.reset(new MixClient()); m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString()));
m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), std::vector<dev::KeyPair> { m_client->userAccount() }, m_client.get())); m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), std::vector<dev::KeyPair> { m_client->userAccount() }, m_client.get()));
connect(m_web3Server.get(), &Web3Server::newTransaction, this, &ClientModel::onNewTransaction, Qt::DirectConnection); connect(m_web3Server.get(), &Web3Server::newTransaction, this, &ClientModel::onNewTransaction, Qt::DirectConnection);
@ -111,8 +113,25 @@ QString ClientModel::apiCall(QString const& _message)
void ClientModel::mine() void ClientModel::mine()
{ {
m_client->mine(); if (m_running)
newBlock(); BOOST_THROW_EXCEPTION(ExecutionStateException());
m_running = true;
emit runStarted();
emit runStateChanged();
QtConcurrent::run([=]()
{
try
{
m_client->mine();
newBlock();
}
catch (...)
{
std::cerr << boost::current_exception_diagnostic_information();
emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information()));
}
m_running = false;
});
} }
QString ClientModel::contractAddress() const QString ClientModel::contractAddress() const
@ -145,7 +164,7 @@ void ClientModel::setupState(QVariantMap _state)
TransactionSettings transactionSettings(functionId, transaction.value("url").toString()); TransactionSettings transactionSettings(functionId, transaction.value("url").toString());
transactionSettings.gasPrice = 10000000000000; transactionSettings.gasPrice = 10000000000000;
transactionSettings.gas = 125000; transactionSettings.gas = 125000;
transactionSettings.value = 100; transactionSettings.value = 0;
transactionSequence.push_back(transactionSettings); transactionSequence.push_back(transactionSettings);
} }
else else
@ -242,11 +261,13 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
} }
catch(boost::exception const&) catch(boost::exception const&)
{ {
std::cerr << boost::current_exception_diagnostic_information();
emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information()));
} }
catch(std::exception const& e) catch(std::exception const& e)
{ {
std::cerr << boost::current_exception_diagnostic_information();
emit runFailed(e.what()); emit runFailed(e.what());
} }
m_running = false; m_running = false;
@ -256,7 +277,7 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
void ClientModel::showDebugger() void ClientModel::showDebugger()
{ {
ExecutionResult const& last = m_client->record().back().transactions.back(); ExecutionResult const& last = m_client->lastExecution();
showDebuggerForTransaction(last); showDebuggerForTransaction(last);
} }
@ -289,7 +310,7 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
void ClientModel::debugTransaction(unsigned _block, unsigned _index) void ClientModel::debugTransaction(unsigned _block, unsigned _index)
{ {
auto const& t = m_client->record().at(_block).transactions.at(_index); auto const& t = m_client->execution(_block, _index);
showDebuggerForTransaction(t); showDebuggerForTransaction(t);
} }
@ -320,9 +341,9 @@ void ClientModel::onStateReset()
void ClientModel::onNewTransaction() void ClientModel::onNewTransaction()
{ {
unsigned block = m_client->number(); unsigned block = m_client->number() + 1;
unsigned index = m_client->record().back().transactions.size() - 1; unsigned index = m_client->pendingExecutions().size() - 1;
ExecutionResult const& tr = m_client->record().back().transactions.back(); ExecutionResult const& tr = m_client->lastExecution();
QString address = QString::fromStdString(toJS(tr.address)); QString address = QString::fromStdString(toJS(tr.address));
QString value = QString::fromStdString(dev::toString(tr.value)); QString value = QString::fromStdString(dev::toString(tr.value));
QString contract = address; QString contract = address;

5
mix/ClientModel.h

@ -26,8 +26,7 @@
#include <atomic> #include <atomic>
#include <map> #include <map>
#include <QString> #include <QString>
#include "MixClient.h" #include "MachineStates.h"
#include "QVariableDefinition.h"
namespace dev namespace dev
{ {
@ -39,6 +38,8 @@ class Web3Server;
class RpcConnector; class RpcConnector;
class QEther; class QEther;
class QDebugData; class QDebugData;
class MixClient;
class QVariableDefinition;
/// Backend transaction config class /// Backend transaction config class
struct TransactionSettings struct TransactionSettings

2
mix/ContractCallDataEncoder.cpp

@ -23,7 +23,7 @@
#include <QDebug> #include <QDebug>
#include <QMap> #include <QMap>
#include <QStringList> #include <QStringList>
#include <libdevcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include "QVariableDeclaration.h" #include "QVariableDeclaration.h"
#include "QVariableDefinition.h" #include "QVariableDefinition.h"

2
mix/DebuggingStateWrapper.cpp

@ -26,7 +26,7 @@
#include <QQmlEngine> #include <QQmlEngine>
#include <QVariantList> #include <QVariantList>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <libdevcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <libdevcrypto/Common.h> #include <libdevcrypto/Common.h>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>

222
mix/MixClient.cpp

@ -23,7 +23,7 @@
#include <vector> #include <vector>
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
#include <libethereum/BlockChain.h> #include <libethereum/CanonBlockChain.h>
#include <libethereum/Transaction.h> #include <libethereum/Transaction.h>
#include <libethereum/Executive.h> #include <libethereum/Executive.h>
#include <libethereum/ExtVM.h> #include <libethereum/ExtVM.h>
@ -36,35 +36,50 @@ using namespace dev;
using namespace dev::eth; using namespace dev::eth;
using namespace dev::mix; using namespace dev::mix;
const Secret c_stdSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074"); const Secret c_userAccountSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074");
MixClient::MixClient(): MixClient::MixClient(std::string const& _dbPath):
m_userAccount(c_stdSecret) m_userAccount(c_userAccountSecret), m_bc(_dbPath, true), m_dbPath(_dbPath), m_minigThreads(0)
{
//TODO: put this into genesis block somehow
//resetState(10000000 * ether);
}
MixClient::~MixClient()
{ {
resetState(10000000 * ether);
} }
void MixClient::resetState(u256 _balance) void MixClient::resetState(u256 _balance)
{ {
WriteGuard l(x_state); (void) _balance;
Guard fl(m_filterLock); {
m_filters.clear(); WriteGuard l(x_state);
m_watches.clear(); Guard fl(m_filterLock);
m_state = eth::State(m_userAccount.address(), m_stateDB, BaseState::Genesis); m_filters.clear();
m_state.addBalance(m_userAccount.address(), _balance); m_watches.clear();
Block genesis; m_bc.reopen(m_dbPath, true);
genesis.state = m_state; m_state = eth::State();
Block open; m_stateDB = OverlayDB();
m_blocks = Blocks { genesis, open }; //last block contains a list of pending transactions to be finalized m_state = eth::State(m_userAccount.address(), m_stateDB, BaseState::CanonGenesis);
// m_lastHashes.clear(); m_state.sync(m_bc);
// m_lastHashes.resize(256); m_startState = m_state;
// m_lastHashes[0] = genesis.hash; m_pendingExecutions.clear();
}
mine();
} }
void MixClient::executeTransaction(Transaction const& _t, State& _state) void MixClient::executeTransaction(Transaction const& _t, State& _state)
{ {
bytes rlp = _t.rlp(); bytes rlp = _t.rlp();
Executive execution(_state, LastHashes(), 0);
// do debugging run first
LastHashes lastHashes(256);
lastHashes[0] = m_bc.numberHash(m_bc.number());
for (unsigned i = 1; i < 256; ++i)
lastHashes[i] = lastHashes[i - 1] ? m_bc.details(lastHashes[i - 1]).parent : h256();
State execState = _state;
Executive execution(execState, lastHashes, 0);
execution.setup(&rlp); execution.setup(&rlp);
std::vector<MachineState> machineStates; std::vector<MachineState> machineStates;
std::vector<unsigned> levels; std::vector<unsigned> levels;
@ -130,21 +145,25 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state)
d.value = _t.value(); d.value = _t.value();
if (_t.isCreation()) if (_t.isCreation())
d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce()))); d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce())));
d.receipt = TransactionReceipt(m_state.rootHash(), execution.gasUsed(), execution.logs()); //TODO: track gas usage d.receipt = TransactionReceipt(execState.rootHash(), execution.gasUsed(), execution.logs()); //TODO: track gas usage
m_blocks.back().transactions.emplace_back(d); m_pendingExecutions.emplace_back(std::move(d));
// execute on a state
_state.execute(lastHashes, rlp, nullptr, true);
// collect watches
h256Set changed; h256Set changed;
Guard l(m_filterLock); Guard l(m_filterLock);
for (std::pair<h256 const, eth::InstalledFilter>& i: m_filters) for (std::pair<h256 const, eth::InstalledFilter>& i: m_filters)
if ((unsigned)i.second.filter.latest() > m_blocks.size() - 1) if ((unsigned)i.second.filter.latest() > m_bc.number())
{ {
// acceptable number. // acceptable number.
auto m = i.second.filter.matches(d.receipt); auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1));
if (m.size()) if (m.size())
{ {
// filter catches them // filter catches them
for (LogEntry const& l: m) for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, m_blocks.size())); i.second.changes.push_back(LocalisedLogEntry(l, m_bc.number() + 1));
changed.insert(i.first); changed.insert(i.first);
} }
} }
@ -152,44 +171,56 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state)
noteChanged(changed); noteChanged(changed);
} }
void MixClient::validateBlock(int _block) const
{
if (_block != -1 && _block != 0 && (unsigned)_block >= m_blocks.size() - 1)
BOOST_THROW_EXCEPTION(InvalidBlockException() << BlockIndex(_block));
}
void MixClient::mine() void MixClient::mine()
{ {
WriteGuard l(x_state); WriteGuard l(x_state);
Block& block = m_blocks.back(); m_state.commitToMine(m_bc);
m_state.mine(0, true); while (!m_state.mine(100, true).completed) {}
m_state.completeMine(); m_state.completeMine();
m_state.commitToMine(BlockChain()); m_bc.import(m_state.blockData(), m_stateDB);
m_state.cleanup(true); m_state.sync(m_bc);
block.state = m_state; //m_state.cleanup(true);
block.info = m_state.info(); m_startState = m_state;
block.hash = block.info.hash; m_executions.emplace_back(std::move(m_pendingExecutions));
m_blocks.push_back(Block());
h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter }; h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter };
noteChanged(changed); noteChanged(changed);
} }
State const& MixClient::asOf(int _block) const ExecutionResult const& MixClient::execution(unsigned _block, unsigned _transaction) const
{
if (_block == m_bc.number() + 1)
return m_pendingExecutions.at(_transaction);
return m_executions.at(_block).at(_transaction);
}
ExecutionResult const& MixClient::lastExecution() const
{
if (m_pendingExecutions.size() > 0)
return m_pendingExecutions.back();
return m_executions.back().back();
}
ExecutionResults const& MixClient::pendingExecutions() const
{ {
validateBlock(_block); return m_pendingExecutions;
}
State MixClient::asOf(int _block) const
{
ReadGuard l(x_state);
if (_block == 0) if (_block == 0)
return m_blocks[m_blocks.size() - 2].state;
else if (_block == -1)
return m_state; return m_state;
else if (_block == -1)
return m_startState;
else else
return m_blocks[_block].state; return State(m_stateDB, m_bc, m_bc.numberHash(_block));
} }
void MixClient::transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) void MixClient::transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
{ {
WriteGuard l(x_state); WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret)); u256 n = m_state.transactionsFrom(toAddress(_secret));
_gasPrice = 0; //TODO: remove after fixing setBalance
Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret);
executeTransaction(t, m_state); executeTransaction(t, m_state);
} }
@ -198,6 +229,7 @@ Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init,
{ {
WriteGuard l(x_state); WriteGuard l(x_state);
u256 n = m_state.transactionsFrom(toAddress(_secret)); u256 n = m_state.transactionsFrom(toAddress(_secret));
_gasPrice = 0; //TODO: remove after fixing setBalance
eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret); eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret);
executeTransaction(t, m_state); executeTransaction(t, m_state);
Address address = right160(sha3(rlpList(t.sender(), t.nonce()))); Address address = right160(sha3(rlpList(t.sender(), t.nonce())));
@ -228,36 +260,31 @@ bytes MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _
bytes rlp = t.rlp(); bytes rlp = t.rlp();
WriteGuard lw(x_state); //TODO: lock is required only for last execution state WriteGuard lw(x_state); //TODO: lock is required only for last execution state
executeTransaction(t, temp); executeTransaction(t, temp);
return m_blocks.back().transactions.back().returnValue; return m_pendingExecutions.back().returnValue;
} }
u256 MixClient::balanceAt(Address _a, int _block) const u256 MixClient::balanceAt(Address _a, int _block) const
{ {
ReadGuard l(x_state);
return asOf(_block).balance(_a); return asOf(_block).balance(_a);
} }
u256 MixClient::countAt(Address _a, int _block) const u256 MixClient::countAt(Address _a, int _block) const
{ {
ReadGuard l(x_state);
return asOf(_block).transactionsFrom(_a); return asOf(_block).transactionsFrom(_a);
} }
u256 MixClient::stateAt(Address _a, u256 _l, int _block) const u256 MixClient::stateAt(Address _a, u256 _l, int _block) const
{ {
ReadGuard l(x_state);
return asOf(_block).storage(_a, _l); return asOf(_block).storage(_a, _l);
} }
bytes MixClient::codeAt(Address _a, int _block) const bytes MixClient::codeAt(Address _a, int _block) const
{ {
ReadGuard l(x_state);
return asOf(_block).code(_a); return asOf(_block).code(_a);
} }
std::map<u256, u256> MixClient::storageAt(Address _a, int _block) const std::map<u256, u256> MixClient::storageAt(Address _a, int _block) const
{ {
ReadGuard l(x_state);
return asOf(_block).storage(_a); return asOf(_block).storage(_a);
} }
@ -274,23 +301,40 @@ eth::LocalisedLogEntries MixClient::logs(unsigned _watchId) const
eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const
{ {
LocalisedLogEntries ret; LocalisedLogEntries ret;
unsigned lastBlock = m_blocks.size() - 1; //last block contains pending transactions unsigned lastBlock = m_bc.number();
unsigned block = std::min<unsigned>(lastBlock, (unsigned)_f.latest()); unsigned block = std::min<unsigned>(lastBlock, (unsigned)_f.latest());
unsigned end = std::min(lastBlock, std::min(block, (unsigned)_f.earliest())); unsigned end = std::min(lastBlock, std::min(block, (unsigned)_f.earliest()));
for (; ret.size() != _f.max() && block != end; block--) unsigned skip = _f.skip();
// Pending transactions
if (block > m_bc.number())
{ {
bool pendingBlock = (block == lastBlock); ReadGuard l(x_state);
if (pendingBlock || _f.matches(m_blocks[block].info.logBloom)) for (unsigned i = 0; i < m_state.pending().size(); ++i)
for (ExecutionResult const& t: m_blocks[block].transactions) {
if (pendingBlock || _f.matches(t.receipt.bloom())) // Might have a transaction that contains a matching log.
TransactionReceipt const& tr = m_state.receipt(i);
LogEntries logEntries = _f.matches(tr);
for (unsigned entry = 0; entry < logEntries.size() && ret.size() != _f.max(); ++entry)
ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block));
skip -= std::min(skip, static_cast<unsigned>(logEntries.size()));
}
block = m_bc.number();
}
// The rest
auto h = m_bc.numberHash(block);
for (; ret.size() != block && block != end; block--)
{
if (_f.matches(m_bc.info(h).logBloom))
for (TransactionReceipt receipt: m_bc.receipts(h).receipts)
if (_f.matches(receipt.bloom()))
{ {
LogEntries logEntries = _f.matches(t.receipt); LogEntries logEntries = _f.matches(receipt);
if (logEntries.size()) for (unsigned entry = skip; entry < logEntries.size() && ret.size() != _f.max(); ++entry)
{ ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block));
for (unsigned entry = _f.skip(); entry < logEntries.size() && ret.size() != _f.max(); ++entry) skip -= std::min(skip, static_cast<unsigned>(logEntries.size()));
ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block));
}
} }
h = m_bc.details(h).parent;
} }
return ret; return ret;
} }
@ -372,66 +416,65 @@ LocalisedLogEntries MixClient::checkWatch(unsigned _watchId)
h256 MixClient::hashFromNumber(unsigned _number) const h256 MixClient::hashFromNumber(unsigned _number) const
{ {
validateBlock(_number); return m_bc.numberHash(_number);
return m_blocks[_number].hash;
} }
eth::BlockInfo MixClient::blockInfo(h256 _hash) const eth::BlockInfo MixClient::blockInfo(h256 _hash) const
{ {
(void)_hash; return BlockInfo(m_bc.block(_hash));
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::blockInfo"));
} }
eth::BlockDetails MixClient::blockDetails(h256 _hash) const eth::BlockDetails MixClient::blockDetails(h256 _hash) const
{ {
(void)_hash; return m_bc.details(_hash);
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::blockDetails"));
} }
eth::Transaction MixClient::transaction(h256 _blockHash, unsigned _i) const eth::Transaction MixClient::transaction(h256 _blockHash, unsigned _i) const
{ {
(void)_blockHash; auto bl = m_bc.block(_blockHash);
(void)_i; RLP b(bl);
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::transaction")); if (_i < b[1].itemCount())
return Transaction(b[1][_i].data(), CheckSignature::Range);
else
return Transaction();
} }
eth::BlockInfo MixClient::uncle(h256 _blockHash, unsigned _i) const eth::BlockInfo MixClient::uncle(h256 _blockHash, unsigned _i) const
{ {
(void)_blockHash; auto bl = m_bc.block(_blockHash);
(void)_i; RLP b(bl);
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::uncle")); if (_i < b[2].itemCount())
return BlockInfo::fromHeader(b[2][_i].data());
else
return BlockInfo();
} }
unsigned MixClient::number() const unsigned MixClient::number() const
{ {
return m_blocks.size() - 1; return m_bc.number();
} }
eth::Transactions MixClient::pending() const eth::Transactions MixClient::pending() const
{ {
return eth::Transactions(); return m_state.pending();
} }
eth::StateDiff MixClient::diff(unsigned _txi, h256 _block) const eth::StateDiff MixClient::diff(unsigned _txi, h256 _block) const
{ {
(void)_txi; State st(m_stateDB, m_bc, _block);
(void)_block; return st.fromPending(_txi).diff(st.fromPending(_txi + 1));
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::diff"));
} }
eth::StateDiff MixClient::diff(unsigned _txi, int _block) const eth::StateDiff MixClient::diff(unsigned _txi, int _block) const
{ {
(void)_txi; State st = asOf(_block);
(void)_block; return st.fromPending(_txi).diff(st.fromPending(_txi + 1));
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::diff"));
} }
Addresses MixClient::addresses(int _block) const Addresses MixClient::addresses(int _block) const
{ {
validateBlock(_block);
ReadGuard l(x_state);
Addresses ret; Addresses ret;
for (auto const& i: m_state.addresses()) for (auto const& i: asOf(_block).addresses())
ret.push_back(i.first); ret.push_back(i.first);
return ret; return ret;
} }
@ -456,23 +499,22 @@ Address MixClient::address() const
void MixClient::setMiningThreads(unsigned _threads) void MixClient::setMiningThreads(unsigned _threads)
{ {
(void)_threads; m_minigThreads = _threads;
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::setMiningThreads"));
} }
unsigned MixClient::miningThreads() const unsigned MixClient::miningThreads() const
{ {
return 0; return m_minigThreads;
} }
void MixClient::startMining() void MixClient::startMining()
{ {
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::startMining")); //no-op
} }
void MixClient::stopMining() void MixClient::stopMining()
{ {
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::stopMining")); //no-op
} }
bool MixClient::isMining() bool MixClient::isMining()

30
mix/MixClient.h

@ -24,8 +24,10 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <string>
#include <libethereum/Interface.h> #include <libethereum/Interface.h>
#include <libethereum/Client.h> #include <libethereum/Client.h>
#include <libethereum/CanonBlockChain.h>
#include "MachineStates.h" #include "MachineStates.h"
namespace dev namespace dev
@ -33,26 +35,18 @@ namespace dev
namespace mix namespace mix
{ {
struct Block
{
ExecutionResults transactions;
h256 hash;
dev::eth::State state;
dev::eth::BlockInfo info;
};
using Blocks = std::vector<Block>;
class MixClient: public dev::eth::Interface class MixClient: public dev::eth::Interface
{ {
public: public:
MixClient(); MixClient(std::string const& _dbPath);
virtual ~MixClient();
/// Reset state to the empty state with given balance. /// Reset state to the empty state with given balance.
void resetState(u256 _balance); void resetState(u256 _balance);
KeyPair const& userAccount() const { return m_userAccount; } KeyPair const& userAccount() const { return m_userAccount; }
void mine(); void mine();
Blocks const& record() const { return m_blocks; } ExecutionResult const& execution(unsigned _block, unsigned _transaction) const;
ExecutionResult const& lastExecution() const;
ExecutionResults const& pendingExecutions() const;
//dev::eth::Interface //dev::eth::Interface
void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override; void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override;
@ -94,18 +88,22 @@ public:
private: private:
void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state); void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state);
void validateBlock(int _block) const;
void noteChanged(h256Set const& _filters); void noteChanged(h256Set const& _filters);
dev::eth::State const& asOf(int _block) const; dev::eth::State asOf(int _block) const;
KeyPair m_userAccount; KeyPair m_userAccount;
eth::State m_state; eth::State m_state;
eth::State m_startState;
OverlayDB m_stateDB; OverlayDB m_stateDB;
eth::CanonBlockChain m_bc;
mutable boost::shared_mutex x_state; mutable boost::shared_mutex x_state;
mutable std::mutex m_filterLock; mutable std::mutex m_filterLock;
std::map<h256, dev::eth::InstalledFilter> m_filters; std::map<h256, dev::eth::InstalledFilter> m_filters;
std::map<unsigned, dev::eth::ClientWatch> m_watches; std::map<unsigned, dev::eth::ClientWatch> m_watches;
Blocks m_blocks; std::vector<ExecutionResults> m_executions;
ExecutionResults m_pendingExecutions;
std::string m_dbPath;
unsigned m_minigThreads;
}; };
} }

2
mix/QBigInt.cpp

@ -21,7 +21,7 @@
#include <boost/variant/multivisitors.hpp> #include <boost/variant/multivisitors.hpp>
#include <boost/variant.hpp> #include <boost/variant.hpp>
#include <libdevcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include "QBigInt.h" #include "QBigInt.h"
using namespace dev; using namespace dev;

2
mix/QBigInt.h

@ -26,7 +26,7 @@
#include "boost/variant/multivisitors.hpp" #include "boost/variant/multivisitors.hpp"
#include <QObject> #include <QObject>
#include <QQmlEngine> #include <QQmlEngine>
#include <libdevcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
using namespace dev; using namespace dev;

2
mix/QVariableDefinition.cpp

@ -20,7 +20,7 @@
*/ */
#include <libdevcore/CommonData.h> #include <libdevcore/CommonData.h>
#include <libdevcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include "QVariableDefinition.h" #include "QVariableDefinition.h"
using namespace dev::mix; using namespace dev::mix;

3
mix/qml/MainContent.qml

@ -59,8 +59,7 @@ Rectangle {
} }
function hideRightView() { function hideRightView() {
if (rightView.visible) rightView.visible = false;
rightView.hide();
} }
function toggleWebPreview() { function toggleWebPreview() {

15
mix/qml/WebCodeEditor.qml

@ -4,6 +4,7 @@ import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Styles 1.1
import CodeEditorExtensionManager 1.0 import CodeEditorExtensionManager 1.0
import QtWebEngine 1.0 import QtWebEngine 1.0
import QtWebEngine.experimental 1.0
Item { Item {
signal editorTextChanged signal editorTextChanged
@ -29,6 +30,18 @@ Item {
return currentText; return currentText;
} }
function syncClipboard() {
if (Qt.platform.os == "osx") {
var text = appContext.clipboard;
editorBrowser.runJavaScript("setClipboardBase64(\"" + Qt.btoa(text) + "\")");
}
}
Connections {
target: appContext
onClipboardChanged: syncClipboard()
}
anchors.top: parent.top anchors.top: parent.top
id: codeEditorView id: codeEditorView
anchors.fill: parent anchors.fill: parent
@ -36,6 +49,7 @@ Item {
id: editorBrowser id: editorBrowser
url: "qrc:///qml/html/codeeditor.html" url: "qrc:///qml/html/codeeditor.html"
anchors.fill: parent anchors.fill: parent
experimental.settings.javascriptCanAccessClipboard: true
onJavaScriptConsoleMessage: { onJavaScriptConsoleMessage: {
console.log("editor: " + sourceID + ":" + lineNumber + ":" + message); console.log("editor: " + sourceID + ":" + lineNumber + ":" + message);
} }
@ -47,6 +61,7 @@ Item {
setText(currentText, currentMode); setText(currentText, currentMode);
runJavaScript("getTextChanged()", function(result) { }); runJavaScript("getTextChanged()", function(result) { });
pollTimer.running = true; pollTimer.running = true;
syncClipboard();
} }
} }

2
mix/qml/html/cm/codemirror.js

@ -29,7 +29,7 @@
var ie = ie_upto10 || ie_11up; var ie = ie_upto10 || ie_11up;
var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]); var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]);
var webkit = /WebKit\//.test(navigator.userAgent); var webkit = /WebKit\//.test(navigator.userAgent);
var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent); var qtwebkit = true;
var chrome = /Chrome\//.test(navigator.userAgent); var chrome = /Chrome\//.test(navigator.userAgent);
var presto = /Opera\//.test(navigator.userAgent); var presto = /Opera\//.test(navigator.userAgent);
var safari = /Apple Computer/.test(navigator.vendor); var safari = /Apple Computer/.test(navigator.vendor);

20
mix/qml/html/codeeditor.js

@ -6,6 +6,7 @@ var editor = CodeMirror(document.body, {
autofocus: true, autofocus: true,
}); });
editor.setOption("theme", "solarized dark"); editor.setOption("theme", "solarized dark");
editor.setOption("indentUnit", 4); editor.setOption("indentUnit", 4);
editor.setOption("indentWithTabs", true); editor.setOption("indentWithTabs", true);
@ -18,11 +19,24 @@ editor.on("change", function(eMirror, object) {
}); });
var mac = /Mac/.test(navigator.platform);
if (mac === true) {
editor.setOption("extraKeys", {
"Cmd-V": function(cm) {
cm.replaceSelection(clipboard);
},
"Cmd-X": function(cm) {
window.document.execCommand("cut");
},
"Cmd-C": function(cm) {
window.document.execCommand("copy");
}});
}
getTextChanged = function() { getTextChanged = function() {
return editor.changeRegistered; return editor.changeRegistered;
}; };
getText = function() { getText = function() {
editor.changeRegistered = false; editor.changeRegistered = false;
return editor.getValue(); return editor.getValue();
@ -42,3 +56,7 @@ setText = function(text) {
setMode = function(mode) { setMode = function(mode) {
this.editor.setOption("mode", mode); this.editor.setOption("mode", mode);
}; };
setClipboardBase64 = function(text) {
clipboard = window.atob(text);
};

1
mix/qml/main.qml

@ -4,6 +4,7 @@ import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1 import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Window 2.1 import QtQuick.Window 2.1
import QtQuick.PrivateWidgets 1.1
import Qt.labs.settings 1.0 import Qt.labs.settings 1.0
import org.ethereum.qml.QEther 1.0 import org.ethereum.qml.QEther 1.0

5
pullSerpent.sh

@ -2,10 +2,11 @@
opwd="$PWD" opwd="$PWD"
cd ../serpent cd ../serpent
git stash
git pull git pull
git stash pop
cp bignum.* compiler.* funcs.* lllparser.* opcodes.h parser.* rewriter.* tokenize.* util.* ../cpp-ethereum/libserpent/ cp bignum.* compiler.* funcs.* lllparser.* opcodes.h parser.* rewriter.* tokenize.* util.* ../cpp-ethereum/libserpent/
cp cmdline.* "$opwd/sc/" cp cmdline.* "$opwd/sc/"
cp pyserpent.* "$opwd/libpyserpent/"
cd "$opwd" cd "$opwd"
perl -i -p -e 's:include "funcs.h":include <libserpent/funcs.h>:gc' sc/* libpyserpent/* perl -i -p -e 's:include "funcs.h":include <libserpent/funcs.h>:gc' sc/*

5
sc/cmdline.cpp

@ -68,7 +68,7 @@ int main(int argv, char** argc) {
std::cout << binToHex(compileLLL(parseLLL(input, true))) << "\n"; std::cout << binToHex(compileLLL(parseLLL(input, true))) << "\n";
} }
else if (command == "dereference") { else if (command == "dereference") {
std::cout << printAST(dereference(parseLLL(input, true)), haveSec) <<"\n"; std::cout << printTokens(dereference(parseLLL(input, true))) <<"\n";
} }
else if (command == "pretty_assemble") { else if (command == "pretty_assemble") {
std::cout << printTokens(prettyAssemble(parseLLL(input, true))) <<"\n"; std::cout << printTokens(prettyAssemble(parseLLL(input, true))) <<"\n";
@ -88,9 +88,6 @@ int main(int argv, char** argc) {
else if (command == "serialize") { else if (command == "serialize") {
std::cout << binToHex(serialize(tokenize(input, Metadata(), false))) << "\n"; std::cout << binToHex(serialize(tokenize(input, Metadata(), false))) << "\n";
} }
else if (command == "flatten") {
std::cout << printTokens(flatten(parseLLL(input, true))) << "\n";
}
else if (command == "deserialize") { else if (command == "deserialize") {
std::cout << printTokens(deserialize(hexToBin(input))) << "\n"; std::cout << printTokens(deserialize(hexToBin(input))) << "\n";
} }

10
test/SolidityEndToEndTest.cpp

@ -102,6 +102,16 @@ BOOST_AUTO_TEST_CASE(named_args)
BOOST_CHECK(callContractFunction("b()", bytes()) == toBigEndian(u256(123))); BOOST_CHECK(callContractFunction("b()", bytes()) == toBigEndian(u256(123)));
} }
BOOST_AUTO_TEST_CASE(disorder_named_args)
{
char const* sourceCode = "contract test {\n"
" function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; }\n"
" function b() returns (uint r) { r = a({c: 3, a: 1, b: 2}); }\n"
"}\n";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("b()", bytes()) == toBigEndian(u256(123)));
}
BOOST_AUTO_TEST_CASE(while_loop) BOOST_AUTO_TEST_CASE(while_loop)
{ {
char const* sourceCode = "contract test {\n" char const* sourceCode = "contract test {\n"

36
test/SolidityNameAndTypeResolution.cpp

@ -868,6 +868,42 @@ BOOST_AUTO_TEST_CASE(access_to_protected_state_variable)
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
} }
BOOST_AUTO_TEST_CASE(error_count_in_named_args)
{
char const* sourceCode = "contract test {\n"
" function a(uint a, uint b) returns (uint r) { r = a + b; }\n"
" function b() returns (uint r) { r = a({a: 1}); }\n"
"}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
}
BOOST_AUTO_TEST_CASE(empty_in_named_args)
{
char const* sourceCode = "contract test {\n"
" function a(uint a, uint b) returns (uint r) { r = a + b; }\n"
" function b() returns (uint r) { r = a({}); }\n"
"}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
}
BOOST_AUTO_TEST_CASE(duplicate_parameter_names_in_named_args)
{
char const* sourceCode = "contract test {\n"
" function a(uint a, uint b) returns (uint r) { r = a + b; }\n"
" function b() returns (uint r) { r = a({a: 1, a: 2}); }\n"
"}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
}
BOOST_AUTO_TEST_CASE(invalid_parameter_names_in_named_args)
{
char const* sourceCode = "contract test {\n"
" function a(uint a, uint b) returns (uint r) { r = a + b; }\n"
" function b() returns (uint r) { r = a({a: 1, c: 2}); }\n"
"}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

18
test/SolidityParser.cpp

@ -124,6 +124,24 @@ BOOST_AUTO_TEST_CASE(single_function_param)
BOOST_CHECK_NO_THROW(parseText(text)); BOOST_CHECK_NO_THROW(parseText(text));
} }
BOOST_AUTO_TEST_CASE(missing_parameter_name_in_named_args)
{
char const* text = "contract test {\n"
" function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; }\n"
" function b() returns (uint r) { r = a({: 1, : 2, : 3}); }\n"
"}\n";
BOOST_CHECK_THROW(parseText(text), ParserError);
}
BOOST_AUTO_TEST_CASE(missing_argument_in_named_args)
{
char const* text = "contract test {\n"
" function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; }\n"
" function b() returns (uint r) { r = a({a: , b: , c: }); }\n"
"}\n";
BOOST_CHECK_THROW(parseText(text), ParserError);
}
BOOST_AUTO_TEST_CASE(function_natspec_documentation) BOOST_AUTO_TEST_CASE(function_natspec_documentation)
{ {
ASTPointer<ContractDefinition> contract; ASTPointer<ContractDefinition> contract;

5
test/commonjs.cpp

@ -20,7 +20,8 @@
*/ */
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <libdevcore/CommonJS.h> #include <libdevcore/Log.h>
#include <libethcore/CommonJS.h>
BOOST_AUTO_TEST_SUITE(commonjs) BOOST_AUTO_TEST_SUITE(commonjs)
using namespace std; using namespace std;
@ -41,7 +42,7 @@ BOOST_AUTO_TEST_CASE(jsToAddress)
cnote << "Testing jsToPublic..."; cnote << "Testing jsToPublic...";
KeyPair kp = KeyPair::create(); KeyPair kp = KeyPair::create();
string string = toJS(kp.address()); string string = toJS(kp.address());
Address address = dev::jsToAddress(string); Address address = dev::eth::jsToAddress(string);
BOOST_CHECK_EQUAL(kp.address(), address); BOOST_CHECK_EQUAL(kp.address(), address);
} }

2
test/fork.cpp

@ -23,7 +23,7 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <boost/filesystem/operations.hpp> #include <boost/filesystem/operations.hpp>
#include <libethereum/Client.h> #include <libethereum/Client.h>
#include <libethereum/BlockChain.h> #include <libethereum/CanonBlockChain.h>
#include <libethereum/EthereumHost.h> #include <libethereum/EthereumHost.h>
#include "TestHelper.h" #include "TestHelper.h"
using namespace std; using namespace std;

8
test/genesis.cpp

@ -24,7 +24,7 @@
#include <random> #include <random>
#include "JsonSpiritHeaders.h" #include "JsonSpiritHeaders.h"
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libethereum/BlockChain.h> #include <libethereum/CanonBlockChain.h>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include "TestHelper.h" #include "TestHelper.h"
@ -58,9 +58,9 @@ BOOST_AUTO_TEST_CASE(genesis_tests)
js::mObject o = v.get_obj(); js::mObject o = v.get_obj();
BOOST_CHECK_EQUAL(BlockChain::genesis().stateRoot, h256(o["genesis_state_root"].get_str())); BOOST_CHECK_EQUAL(CanonBlockChain::genesis().stateRoot, h256(o["genesis_state_root"].get_str()));
BOOST_CHECK_EQUAL(toHex(BlockChain::createGenesisBlock()), toHex(fromHex(o["genesis_rlp_hex"].get_str()))); BOOST_CHECK_EQUAL(toHex(CanonBlockChain::createGenesisBlock()), toHex(fromHex(o["genesis_rlp_hex"].get_str())));
BOOST_CHECK_EQUAL(BlockInfo::headerHash(BlockChain::createGenesisBlock()), h256(o["genesis_hash"].get_str())); BOOST_CHECK_EQUAL(BlockInfo::headerHash(CanonBlockChain::createGenesisBlock()), h256(o["genesis_hash"].get_str()));
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

2
test/jsonrpc.cpp

@ -28,7 +28,7 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libdevcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
#include <libweb3jsonrpc/WebThreeStubServer.h> #include <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h> #include <jsonrpccpp/server/connectors/httpserver.h>

2
test/state.cpp

@ -24,7 +24,7 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include "JsonSpiritHeaders.h" #include "JsonSpiritHeaders.h"
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libethereum/BlockChain.h> #include <libethereum/CanonBlockChain.h>
#include <libethereum/State.h> #include <libethereum/State.h>
#include <libethereum/ExtVM.h> #include <libethereum/ExtVM.h>
#include <libethereum/Defaults.h> #include <libethereum/Defaults.h>

4
test/stateOriginal.cpp

@ -22,7 +22,7 @@
#include <boost/filesystem/operations.hpp> #include <boost/filesystem/operations.hpp>
#include <secp256k1/secp256k1.h> #include <secp256k1/secp256k1.h>
#include <libethereum/BlockChain.h> #include <libethereum/CanonBlockChain.h>
#include <libethereum/State.h> #include <libethereum/State.h>
#include <libethereum/Defaults.h> #include <libethereum/Defaults.h>
using namespace std; using namespace std;
@ -40,7 +40,7 @@ int stateTest()
Defaults::setDBPath(boost::filesystem::temp_directory_path().string()); Defaults::setDBPath(boost::filesystem::temp_directory_path().string());
OverlayDB stateDB = State::openDB(); OverlayDB stateDB = State::openDB();
BlockChain bc; CanonBlockChain bc;
State s(myMiner.address(), stateDB); State s(myMiner.address(), stateDB);
cout << bc; cout << bc;

2
test/txTest.cpp

@ -23,7 +23,7 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <boost/filesystem/operations.hpp> #include <boost/filesystem/operations.hpp>
#include <libethereum/Client.h> #include <libethereum/Client.h>
#include <libethereum/BlockChain.h> #include <libethereum/CanonBlockChain.h>
#include <libethereum/EthereumHost.h> #include <libethereum/EthereumHost.h>
#include "TestHelper.h" #include "TestHelper.h"
using namespace std; using namespace std;

6
third/MainWin.cpp

@ -34,7 +34,7 @@
#include <liblll/Compiler.h> #include <liblll/Compiler.h>
#include <liblll/CodeFragment.h> #include <liblll/CodeFragment.h>
#include <libevm/VM.h> #include <libevm/VM.h>
#include <libethereum/BlockChain.h> #include <libethereum/CanonBlockChain.h>
#include <libethereum/ExtVM.h> #include <libethereum/ExtVM.h>
#include <libethereum/Client.h> #include <libethereum/Client.h>
#include <libethereum/EthereumHost.h> #include <libethereum/EthereumHost.h>
@ -97,8 +97,8 @@ Main::Main(QWidget *parent) :
setWindowFlags(Qt::Window); setWindowFlags(Qt::Window);
ui->setupUi(this); ui->setupUi(this);
cerr << "State root: " << BlockChain::genesis().stateRoot << endl; cerr << "State root: " << CanonBlockChain::genesis().stateRoot << endl;
auto gb = BlockChain::createGenesisBlock(); auto gb = CanonBlockChain::createGenesisBlock();
cerr << "Block Hash: " << sha3(gb) << endl; cerr << "Block Hash: " << sha3(gb) << endl;
cerr << "Block RLP: " << RLP(gb) << endl; cerr << "Block RLP: " << RLP(gb) << endl;
cerr << "Block Hex: " << toHex(gb) << endl; cerr << "Block Hex: " << toHex(gb) << endl;

Loading…
Cancel
Save