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/util.h>
#include <libdevcrypto/FileSystem.h>
#include <libdevcore/CommonJS.h>
#include <libethcore/CommonJS.h>
#include <liblll/Compiler.h>
#include <liblll/CodeFragment.h>
#include <libsolidity/Scanner.h>
@ -41,7 +41,7 @@
#include <libsolidity/SourceReferenceFormatter.h>
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include <libethereum/BlockChain.h>
#include <libethereum/CanonBlockChain.h>
#include <libethereum/ExtVM.h>
#include <libethereum/Client.h>
#include <libethereum/Utility.h>
@ -136,9 +136,9 @@ Main::Main(QWidget *parent) :
#endif
m_servers.append(QString::fromStdString(Host::pocHost() + ":30303"));
cerr << "State root: " << BlockChain::genesis().stateRoot << endl;
auto block = BlockChain::createGenesisBlock();
cerr << "Block Hash: " << BlockChain::genesis().hash << endl;
cerr << "State root: " << CanonBlockChain::genesis().stateRoot << endl;
auto block = CanonBlockChain::createGenesisBlock();
cerr << "Block Hash: " << CanonBlockChain::genesis().hash << endl;
cerr << "Block RLP: " << RLP(block) << endl;
cerr << "Block Hex: " << toHex(block) << 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"));
}
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
{
@ -1189,7 +1189,10 @@ void Main::timerEvent(QTimerEvent*)
{
auto ls = ethereum()->checkWatch(i.first);
if (ls.size())
{
cnote << "FIRING WATCH" << i.first << ls.size();
i.second(ls);
}
}
}
@ -1202,7 +1205,7 @@ string Main::renderDiff(StateDiff const& _d) const
{
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>";
if (!ad.exist.to())
continue;
@ -1210,7 +1213,7 @@ string Main::renderDiff(StateDiff const& _d) const
if (ad.balance)
{
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>";
}
if (ad.nonce)

4
alethzero/OurWebThreeStubServer.h

@ -20,7 +20,7 @@
*/
#include <QtCore/QObject>
#include <libdevcore/CommonJS.h>
#include <libethcore/CommonJS.h>
#include <libdevcrypto/Common.h>
#include <libweb3jsonrpc/WebThreeStubServer.h>
@ -35,7 +35,7 @@ public:
std::vector<dev::KeyPair> const& _accounts, Main* main);
virtual std::string shh_newIdentity() override;
virtual bool authenticate(dev::TransactionSkeleton const& _t);
virtual bool authenticate(dev::eth::TransactionSkeleton const& _t);
signals:
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)
# First have qt5 install plugins and frameworks
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}
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
@ -82,19 +83,11 @@ macro(eth_install_executable EXECUTABLE)
set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/\$ENV{CONFIGURATION}/${EXECUTABLE}.app")
endif ()
# TODO check, how fixup_bundle works and if it is required
install(CODE "
include(BundleUtilities)
set(BU_CHMOD_BUNDLE_ITEMS 1)
fixup_bundle(\"${APP_BUNDLE_PATH}\" \"${BUNDLELIBS}\" \"../libqethereum ../libethereum ../secp256k1\")
verify_app(\"${APP_BUNDLE_PATH}\")
" 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")
# copy all dlls to executable directory

5
evmjit/CMakeLists.txt

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

3
evmjit/libevmjit-cpp/CMakeLists.txt

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

3
evmjit/libevmjit-cpp/Env.cpp

@ -3,7 +3,7 @@
#include <libevm/FeeStructure.h>
#include <libevm/ExtVMFace.h>
#include <evmjit/libevmjit/Utils.h>
#include "Utils.h"
extern "C"
{
@ -16,7 +16,6 @@ extern "C"
using namespace dev;
using namespace dev::eth;
using jit::i256;
using jit::eth2llvm;
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 <libevm/VM.h>
#include <evmjit/libevmjit/ExecutionEngine.h>
#include <evmjit/libevmjit/Utils.h>
#include "Utils.h"
namespace dev
{
namespace eth
{
extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below
bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t)
{
using namespace jit;
m_data.set(RuntimeData::Gas, m_gas);
m_data.set(RuntimeData::Address, fromAddress(_ext.myAddress));
m_data.set(RuntimeData::Caller, fromAddress(_ext.caller));
m_data.set(RuntimeData::Origin, fromAddress(_ext.origin));
m_data.set(RuntimeData::CallValue, _ext.value);
m_data.set(RuntimeData::CallDataSize, _ext.data.size());
m_data.set(RuntimeData::GasPrice, _ext.gasPrice);
m_data.set(RuntimeData::CoinBase, fromAddress(_ext.currentBlock.coinbaseAddress));
m_data.set(RuntimeData::TimeStamp, _ext.currentBlock.timestamp);
m_data.set(RuntimeData::Number, _ext.currentBlock.number);
m_data.set(RuntimeData::Difficulty, _ext.currentBlock.difficulty);
m_data.set(RuntimeData::GasLimit, _ext.currentBlock.gasLimit);
m_data.set(RuntimeData::CodeSize, _ext.code.size());
m_data.callData = _ext.data.data();
m_data.code = _ext.code.data();
if (m_gas > std::numeric_limits<decltype(m_data.gas)>::max())
BOOST_THROW_EXCEPTION(OutOfGas()); // Do not accept requests with gas > 2^63 (int64 max) // TODO: Return "not accepted" exception to allow interpreted handle that
if (_ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max())
BOOST_THROW_EXCEPTION(OutOfGas());
if (_ext.currentBlock.number > std::numeric_limits<decltype(m_data.number)>::max())
BOOST_THROW_EXCEPTION(OutOfGas());
if (_ext.currentBlock.timestamp > std::numeric_limits<decltype(m_data.timestamp)>::max())
BOOST_THROW_EXCEPTION(OutOfGas());
m_data.gas = static_cast<decltype(m_data.gas)>(m_gas);
m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice);
m_data.callData = _ext.data.data();
m_data.callDataSize = _ext.data.size();
m_data.address = 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 exitCode = m_engine.run(_ext.code, &m_data, env);
switch (exitCode)
{
case ReturnCode::Suicide:
_ext.suicide(right160(m_data.get(RuntimeData::SuicideDestAddress)));
_ext.suicide(right160(llvm2eth(m_data.address)));
break;
case ReturnCode::BadJumpDestination:
@ -45,24 +60,16 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t)
BOOST_THROW_EXCEPTION(StackTooSmall());
case ReturnCode::BadInstruction:
BOOST_THROW_EXCEPTION(BadInstruction());
case ReturnCode::LinkerWorkaround: // never happens
env_sload(); // but forces linker to include env_* JIT callback functions
break;
default:
break;
}
m_gas = llvm2eth(m_data.elems[RuntimeData::Gas]);
return {m_engine.returnData.data(), m_engine.returnData.size()}; // TODO: This all bytesConstRef is problematic, review.
m_gas = m_data.gas; // TODO: Remove m_gas field
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 "Runtime.h"
#include "Type.h"
#include "Endianness.h"
#include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h>
#include <iostream>
namespace dev
{
@ -24,35 +27,257 @@ Arith256::Arith256(llvm::IRBuilder<>& _builder) :
using Linkage = GlobalValue::LinkageTypes;
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_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);
m_builder.CreateStore(_arg2, m_arg2);
m_builder.CreateCall3(_op, m_arg1, m_arg2, m_result);
return m_builder.CreateLoad(m_result);
auto& func = _type == Type::Word ? m_div : m_div512;
if (!func)
{
// 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(_arg2, m_arg2);
m_builder.CreateStore(_arg3, m_arg3);
m_builder.CreateCall4(_op, m_arg1, m_arg2, m_arg3, m_result);
m_builder.CreateCall3(_op, m_arg1, m_arg2, 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);
}
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)
{
return binaryOp(m_sdiv, _arg1, _arg2);
}
auto yIsNeg = m_builder.CreateICmpSLT(_y, Constant::get(0));
auto yNeg = m_builder.CreateSub(Constant::get(0), _y);
auto yAbs = m_builder.CreateSelect(yIsNeg, yNeg, _y);
llvm::Value* Arith256::smod(llvm::Value* _arg1, llvm::Value* _arg2)
{
return binaryOp(m_smod, _arg1, _arg2);
auto res = div(xAbs, yAbs);
// 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)
{
return binaryOp(m_exp, _arg1, _arg2);
return createCall(getExpFunc(), {_arg1, _arg2});
}
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)
{
return ternaryOp(m_mulmod, _arg1, _arg2, _arg3);
return createCall(getMulModFunc(), {_arg1, _arg2, _arg3});
}
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;
static const u256 c_send = (u256)1 << 255;
if (_u < c_send)
return (s256)_u;
else
return (s256)-(c_end - _u);
auto t1 = (uint128) x.lo * y.lo;
auto t2 = (uint128) x.lo * y.mid;
auto t3 = (uint128) x.lo * y.hi;
auto t4 = (uint128) x.mid * y.lo;
auto t5 = (uint128) x.mid * y.mid;
auto t6 = (uint128) x.mid * y.hi;
auto t7 = x.hi * y.lo;
auto t8 = x.hi * y.mid;
auto lo = (uint64_t) t1;
auto m1 = (t1 >> 64) + (uint64_t) t2;
auto m2 = (uint64_t) m1;
auto mid = (uint128) m2 + (uint64_t) t4;
auto hi = (t2 >> 64) + t3 + (t4 >> 64) + t5 + (t6 << 64) + t7
+ (t8 << 64) + (m1 >> 64) + (mid >> 64);
return {lo, (uint64_t)mid, hi};
}
inline u256 s2u(s256 _u)
uint512 mul512(uint256 x, uint256 y)
{
static const bigint c_end = (bigint)1 << 256;
if (_u >= 0)
return (u256)_u;
else
return (u256)(c_end + _u);
auto x_lo = (uint128) x;
auto y_lo = (uint128) y;
auto t1 = mul(x_lo, y_lo);
auto t2 = mul(x_lo, y.hi);
auto t3 = mul(x.hi, y_lo);
auto t4 = mul(x.hi, y.hi);
auto lo = (uint128) t1;
auto mid = (uint256) t1.hi + (uint128) t2 + (uint128) t3;
auto hi = (uint256)t2.hi + t3.hi + t4 + mid.hi;
return {lo, (uint128)mid, hi};
}
}
@ -124,77 +487,22 @@ namespace
}
}
extern "C"
{
using namespace dev::eth::jit;
EXPORT void arith_mul(i256* _arg1, i256* _arg2, i256* o_result)
{
auto arg1 = llvm2eth(*_arg1);
auto arg2 = llvm2eth(*_arg2);
*o_result = eth2llvm(arg1 * arg2);
}
EXPORT void arith_div(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(arg2 == 0 ? arg2 : arg1 / arg2);
std::cerr << "DEBUG " << z << ": " << d << c << b << a << std::endl;
}
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);
auto arg2 = llvm2eth(*_arg2);
*o_result = eth2llvm(arg2 == 0 ? arg2 : arg1 % arg2);
*o_result = mul(*_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);
auto arg2 = llvm2eth(*_arg2);
*o_result = eth2llvm(arg2 == 0 ? arg2 : s2u(u2s(arg1) / u2s(arg2)));
*o_result = mul512(*_arg1, *_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:
Arith256(llvm::IRBuilder<>& _builder);
virtual ~Arith256();
llvm::Value* mul(llvm::Value* _arg1, llvm::Value* _arg2);
llvm::Value* div(llvm::Value* _arg1, llvm::Value* _arg2);
llvm::Value* mod(llvm::Value* _arg1, llvm::Value* _arg2);
llvm::Value* sdiv(llvm::Value* _arg1, llvm::Value* _arg2);
llvm::Value* smod(llvm::Value* _arg1, llvm::Value* _arg2);
std::pair<llvm::Value*, llvm::Value*> div(llvm::Value* _arg1, llvm::Value* _arg2);
std::pair<llvm::Value*, llvm::Value*> sdiv(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* addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3);
void debug(llvm::Value* _value, char _c);
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* ternaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3);
llvm::Function* m_mul;
llvm::Function* m_div;
llvm::Function* m_mod;
llvm::Function* m_sdiv;
llvm::Function* m_smod;
llvm::Function* m_exp;
llvm::Function* m_mulmod;
llvm::Function* m_addmod;
llvm::Function* m_div = nullptr;
llvm::Function* m_div512 = nullptr;
llvm::Function* m_exp = nullptr;
llvm::Function* m_addmod = nullptr;
llvm::Function* m_mulmod = nullptr;
llvm::Function* m_debug = nullptr;
llvm::Value* m_arg1;
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)
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})
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(${Boost_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS})
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib)
install(FILES ${INTERFACE_HEADERS} DESTINATION include/${TARGET_NAME})
install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin)
#install(FILES ${INTERFACE_HEADERS} DESTINATION include/${TARGET_NAME})

31
evmjit/libevmjit/Common.h

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

54
evmjit/libevmjit/Compiler.cpp

@ -4,6 +4,7 @@
#include <functional>
#include <fstream>
#include <chrono>
#include <sstream>
#include <llvm/ADT/PostOrderIterator.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);
}
@ -272,7 +274,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.div(lhs, rhs);
stack.push(res);
stack.push(res.first);
break;
}
@ -281,7 +283,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.sdiv(lhs, rhs);
stack.push(res);
stack.push(res.first);
break;
}
@ -289,8 +291,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.mod(lhs, rhs);
stack.push(res);
auto res = _arith.div(lhs, rhs);
stack.push(res.second);
break;
}
@ -298,8 +300,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.smod(lhs, rhs);
stack.push(res);
auto res = _arith.sdiv(lhs, rhs);
stack.push(res.second);
break;
}
@ -455,14 +457,15 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
// test for word >> (k * 8 + 7)
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 bittest = m_builder.CreateICmpUGT(bitresult, Constant::get(0));
// FIXME: The following does not work - LLVM bug, report!
//auto bitval = m_builder.CreateLShr(word, bitpos, "bitval");
//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 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::ORIGIN:
case Instruction::CALLVALUE:
case Instruction::CALLDATASIZE:
case Instruction::CODESIZE:
case Instruction::GASPRICE:
case Instruction::COINBASE:
case Instruction::TIMESTAMP:
case Instruction::NUMBER:
case Instruction::DIFFICULTY:
case Instruction::GASLIMIT:
case Instruction::NUMBER:
case Instruction::TIMESTAMP:
{
// 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;
}
case Instruction::CODESIZE:
// TODO: Use constant
stack.push(_runtimeManager.getCodeSize());
break;
case Instruction::CALLDATASIZE:
stack.push(_runtimeManager.getCallDataSize());
break;
case Instruction::BLOCKHASH:
{
auto number = stack.pop();
@ -681,7 +693,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
auto reqBytes = stack.pop();
auto srcPtr = _runtimeManager.getCallData();
auto srcSize = _runtimeManager.get(RuntimeData::CallDataSize);
auto srcSize = _runtimeManager.getCallDataSize();
_memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes);
break;
@ -694,7 +706,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
auto reqBytes = stack.pop();
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);
break;
@ -852,6 +864,18 @@ void Compiler::removeDeadBlocks()
}
}
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)

25
evmjit/libevmjit/ExecutionEngine.cpp

@ -1,6 +1,7 @@
#include "ExecutionEngine.h"
#include <chrono>
#include <cstdlib> // env options
#include <llvm/IR/Module.h>
#include <llvm/ADT/Triple.h>
@ -19,13 +20,6 @@
#include "Compiler.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 eth
@ -67,13 +61,21 @@ std::string codeHash(bytes const& _code)
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)
{
static std::unique_ptr<llvm::ExecutionEngine> ee; // TODO: Use Managed Objects from LLVM?
static auto debugDumpModule = DEBUG_ENV_OPTION(EVMJIT_DUMP_MODULE);
static bool objectCacheEnabled = !DEBUG_ENV_OPTION(EVMJIT_CACHE_OFF);
static auto debugDumpModule = getEnvOption("EVMJIT_DUMP", false);
static auto objectCacheEnabled = getEnvOption("EVMJIT_CACHE", true);
auto mainFuncName = codeHash(_code);
EntryFuncPtr entryFuncPtr{};
@ -136,7 +138,10 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en
auto returnCode = runEntryFunc(entryFuncPtr, &runtime);
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();
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;
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 <vector>
#include <iostream>
#include <iomanip>
#include <cstdint>
#include <cassert>
#include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h>
#include "Type.h"
#include "Runtime.h"
#include "GasMeter.h"
#include "Endianness.h"
#include "RuntimeManager.h"
namespace dev
{
namespace eth
{
namespace jit
{
Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter):
RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed
m_gasMeter(_gasMeter)
{
llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr};
m_resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule());
llvm::AttrBuilder attrBuilder;
attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly);
m_resize->setAttributes(llvm::AttributeSet::get(m_resize->getContext(), 1, attrBuilder));
m_require = createRequireFunc(_gasMeter);
m_loadWord = createFunc(false, Type::Word, _gasMeter);
m_storeWord = createFunc(true, Type::Word, _gasMeter);
m_storeByte = createFunc(true, Type::Byte, _gasMeter);
}
llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter)
{
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 rt = func->arg_begin();
rt->setName("rt");
auto offset = rt->getNextNode();
offset->setName("offset");
auto size = offset->getNextNode();
size->setName("size");
auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func);
auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func);
auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func);
auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func);
InsertPointGuard guard(m_builder); // Restores insert point at function exit
// BB "Pre": Ignore checks with size 0
m_builder.SetInsertPoint(preBB);
auto sizeIsZero = m_builder.CreateICmpEQ(size, Constant::get(0));
m_builder.CreateCondBr(sizeIsZero, returnBB, checkBB);
// BB "Check"
m_builder.SetInsertPoint(checkBB);
auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word);
auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res");
auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq");
auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1");
auto rtPtr = getRuntimeManager().getRuntimePtr();
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4);
auto currSize = m_builder.CreateLoad(sizePtr, "currSize");
auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall");
auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded");
m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights?
// BB "Resize"
m_builder.SetInsertPoint(resizeBB);
// Check gas first
uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res");
auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0);
auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2");
auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow");
wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired);
wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq");
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 newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords");
_gasMeter.countMemory(newWords);
// Resize
m_builder.CreateStore(sizeRequired, sizePtr);
auto newData = m_builder.CreateCall2(m_resize, rt, sizePtr, "newData");
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3);
m_builder.CreateStore(newData, dataPtr);
m_builder.CreateBr(returnBB);
// BB "Return"
m_builder.SetInsertPoint(returnBB);
m_builder.CreateRetVoid();
return func;
}
llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&)
{
auto isWord = _valueType == Type::Word;
llvm::Type* storeArgs[] = {Type::RuntimePtr, Type::Word, _valueType};
llvm::Type* loadArgs[] = {Type::RuntimePtr, Type::Word};
auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload";
auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::Word, loadArgs, false);
auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule());
InsertPointGuard guard(m_builder); // Restores insert point at function exit
m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func));
auto rt = func->arg_begin();
rt->setName("rt");
auto index = rt->getNextNode();
index->setName("index");
auto valueSize = _valueType->getPrimitiveSizeInBits() / 8;
this->require(index, Constant::get(valueSize));
auto ptr = getBytePtr(index);
if (isWord)
ptr = m_builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr");
if (_isStore)
{
llvm::Value* value = index->getNextNode();
value->setName("value");
if (isWord)
value = Endianness::toBE(m_builder, value);
m_builder.CreateStore(value, ptr);
m_builder.CreateRetVoid();
}
else
{
llvm::Value* ret = m_builder.CreateLoad(ptr);
ret = Endianness::toNative(m_builder, ret);
m_builder.CreateRet(ret);
}
return func;
}
llvm::Value* Memory::loadWord(llvm::Value* _addr)
{
return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr});
}
void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word)
{
createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word});
}
void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word)
{
auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte");
createCall(m_storeByte, {getRuntimeManager().getRuntimePtr(), _addr, byte});
}
llvm::Value* Memory::getData()
{
auto rtPtr = getRuntimeManager().getRuntimePtr();
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3);
return m_builder.CreateLoad(dataPtr, "data");
}
llvm::Value* Memory::getSize()
{
auto rtPtr = getRuntimeManager().getRuntimePtr();
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4);
return m_builder.CreateLoad(sizePtr, "size");
}
llvm::Value* Memory::getBytePtr(llvm::Value* _index)
{
return m_builder.CreateGEP(getData(), _index, "ptr");
}
void Memory::require(llvm::Value* _offset, llvm::Value* _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)
{
require(_destMemIdx, _reqBytes);
// Additional copy cost
// TODO: This round ups to 32 happens in many places
auto copyWords = m_builder.CreateUDiv(m_builder.CreateAdd(_reqBytes, Constant::get(31)), Constant::get(32));
m_gasMeter.countCopy(copyWords);
// Algorithm:
// isOutsideData = idx256 >= size256
// idx64 = trunc idx256
// size64 = trunc size256
// dataLeftSize = size64 - idx64 // safe if not isOutsideData
// reqBytes64 = trunc _reqBytes // require() handles large values
// bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min
// bytesToCopy = select(isOutsideData, 0, bytesToCopy0)
auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize);
auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::lowPrecision);
auto size64 = m_builder.CreateTrunc(_srcSize, Type::lowPrecision);
auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64);
auto reqBytes64 = m_builder.CreateTrunc(_reqBytes, Type::lowPrecision);
auto outOfBound = m_builder.CreateICmpUGT(reqBytes64, dataLeftSize);
auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes64);
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");
m_builder.CreateMemCpy(dst, src, bytesToCopy, 0);
}
}
}
}
extern "C"
{
using namespace dev::eth::jit;
EXPORT byte* mem_resize(Runtime* _rt, i256* _size) // TODO: Use uint64 as size OR use realloc in LLVM IR
{
auto size = _size->a; // Trunc to 64-bit
auto& memory = _rt->getMemory();
memory.resize(size);
return memory.data();
}
}
#include "Memory.h"
#include <vector>
#include <iostream>
#include <iomanip>
#include <cstdint>
#include <cassert>
#include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h>
#include "Type.h"
#include "Runtime.h"
#include "GasMeter.h"
#include "Endianness.h"
#include "RuntimeManager.h"
namespace dev
{
namespace eth
{
namespace jit
{
Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter):
RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed
m_gasMeter(_gasMeter)
{
llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr};
m_resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule());
llvm::AttrBuilder attrBuilder;
attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly);
m_resize->setAttributes(llvm::AttributeSet::get(m_resize->getContext(), 1, attrBuilder));
m_require = createRequireFunc(_gasMeter);
m_loadWord = createFunc(false, Type::Word, _gasMeter);
m_storeWord = createFunc(true, Type::Word, _gasMeter);
m_storeByte = createFunc(true, Type::Byte, _gasMeter);
}
llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter)
{
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 rt = func->arg_begin();
rt->setName("rt");
auto offset = rt->getNextNode();
offset->setName("offset");
auto size = offset->getNextNode();
size->setName("size");
auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func);
auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func);
auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func);
auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func);
InsertPointGuard guard(m_builder); // Restores insert point at function exit
// BB "Pre": Ignore checks with size 0
m_builder.SetInsertPoint(preBB);
auto sizeIsZero = m_builder.CreateICmpEQ(size, Constant::get(0));
m_builder.CreateCondBr(sizeIsZero, returnBB, checkBB);
// BB "Check"
m_builder.SetInsertPoint(checkBB);
auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word);
auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res");
auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq");
auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1");
auto rtPtr = getRuntimeManager().getRuntimePtr();
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4);
auto currSize = m_builder.CreateLoad(sizePtr, "currSize");
auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall");
auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded");
m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights?
// BB "Resize"
m_builder.SetInsertPoint(resizeBB);
// Check gas first
uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res");
auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0);
auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2");
auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow");
wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired);
wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq");
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 newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords");
_gasMeter.countMemory(newWords);
// Resize
m_builder.CreateStore(sizeRequired, sizePtr);
auto newData = m_builder.CreateCall2(m_resize, rt, sizePtr, "newData");
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3);
m_builder.CreateStore(newData, dataPtr);
m_builder.CreateBr(returnBB);
// BB "Return"
m_builder.SetInsertPoint(returnBB);
m_builder.CreateRetVoid();
return func;
}
llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&)
{
auto isWord = _valueType == Type::Word;
llvm::Type* storeArgs[] = {Type::RuntimePtr, Type::Word, _valueType};
llvm::Type* loadArgs[] = {Type::RuntimePtr, Type::Word};
auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload";
auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::Word, loadArgs, false);
auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule());
InsertPointGuard guard(m_builder); // Restores insert point at function exit
m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func));
auto rt = func->arg_begin();
rt->setName("rt");
auto index = rt->getNextNode();
index->setName("index");
auto valueSize = _valueType->getPrimitiveSizeInBits() / 8;
this->require(index, Constant::get(valueSize));
auto ptr = getBytePtr(index);
if (isWord)
ptr = m_builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr");
if (_isStore)
{
llvm::Value* value = index->getNextNode();
value->setName("value");
if (isWord)
value = Endianness::toBE(m_builder, value);
m_builder.CreateStore(value, ptr);
m_builder.CreateRetVoid();
}
else
{
llvm::Value* ret = m_builder.CreateLoad(ptr);
ret = Endianness::toNative(m_builder, ret);
m_builder.CreateRet(ret);
}
return func;
}
llvm::Value* Memory::loadWord(llvm::Value* _addr)
{
return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr});
}
void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word)
{
createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word});
}
void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word)
{
auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte");
createCall(m_storeByte, {getRuntimeManager().getRuntimePtr(), _addr, byte});
}
llvm::Value* Memory::getData()
{
auto rtPtr = getRuntimeManager().getRuntimePtr();
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3);
return m_builder.CreateLoad(dataPtr, "data");
}
llvm::Value* Memory::getSize()
{
auto rtPtr = getRuntimeManager().getRuntimePtr();
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4);
return m_builder.CreateLoad(sizePtr, "size");
}
llvm::Value* Memory::getBytePtr(llvm::Value* _index)
{
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)
{
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)
{
require(_destMemIdx, _reqBytes);
// Additional copy cost
// TODO: This round ups to 32 happens in many places
auto copyWords = m_builder.CreateUDiv(m_builder.CreateAdd(_reqBytes, Constant::get(31)), Constant::get(32));
m_gasMeter.countCopy(copyWords);
// Algorithm:
// isOutsideData = idx256 >= size256
// idx64 = trunc idx256
// size64 = trunc size256
// dataLeftSize = size64 - idx64 // safe if not isOutsideData
// reqBytes64 = trunc _reqBytes // require() handles large values
// bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min
// bytesToCopy = select(isOutsideData, 0, bytesToCopy0)
auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize);
auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::lowPrecision);
auto size64 = m_builder.CreateTrunc(_srcSize, Type::lowPrecision);
auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64);
auto reqBytes64 = m_builder.CreateTrunc(_reqBytes, Type::lowPrecision);
auto outOfBound = m_builder.CreateICmpUGT(reqBytes64, dataLeftSize);
auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes64);
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 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;
EXPORT byte* mem_resize(Runtime* _rt, i256* _size) // TODO: Use uint64 as size OR use realloc in LLVM IR
{
auto size = _size->a; // Trunc to 64-bit
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)
{}
bytes Runtime::getReturnData() const // FIXME: Reconsider returning by copy
bytes_ref Runtime::getReturnData() const
{
// TODO: Handle large indexes
auto offset = static_cast<size_t>(llvm2eth(m_data.elems[RuntimeData::ReturnDataOffset]));
auto size = static_cast<size_t>(llvm2eth(m_data.elems[RuntimeData::ReturnDataSize]));
auto data = m_data.callData;
auto size = static_cast<size_t>(m_data.callDataSize);
assert(offset + size <= m_memory.size() || size == 0);
if (offset + size > m_memory.size())
if (data < m_memory.data() || data >= m_memory.data() + m_memory.size() || size == 0)
{
assert(size == 0); // data can be an invalid pointer only if size is 0
m_data.callData = nullptr;
return {};
}
auto dataBeg = m_memory.begin() + offset;
return {dataBeg, dataBeg + size};
return bytes_ref{data, size};
}
}

105
evmjit/libevmjit/Runtime.h

@ -1,59 +1,46 @@
#pragma once
#include <csetjmp>
#include <vector>
#include "Instruction.h"
#include "CompilerHelper.h"
#include "Utils.h"
#include "Type.h"
#include "RuntimeData.h"
#ifdef _MSC_VER
#define EXPORT __declspec(dllexport)
#else
#define EXPORT
#endif
namespace dev
{
namespace eth
{
namespace jit
{
using StackImpl = std::vector<i256>;
using MemoryImpl = bytes;
using jmp_buf_ref = decltype(&std::jmp_buf{}[0]);
class Runtime
{
public:
Runtime(RuntimeData* _data, Env* _env);
Runtime(const Runtime&) = delete;
Runtime& operator=(const Runtime&) = delete;
StackImpl& getStack() { return m_stack; }
MemoryImpl& getMemory() { return m_memory; }
Env* getEnvPtr() { return &m_env; }
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;
};
}
}
}
#pragma once
#include <csetjmp>
#include "RuntimeData.h"
namespace dev
{
namespace eth
{
namespace jit
{
using StackImpl = std::vector<i256>;
using MemoryImpl = bytes;
using jmp_buf_ref = decltype(&std::jmp_buf{}[0]);
class Runtime
{
public:
Runtime(RuntimeData* _data, Env* _env);
Runtime(const Runtime&) = delete;
Runtime& operator=(const Runtime&) = delete;
StackImpl& getStack() { return m_stack; }
MemoryImpl& getMemory() { return m_memory; }
Env* getEnvPtr() { return &m_env; }
bytes_ref 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
{
Gas,
GasPrice,
CallData,
CallDataSize,
Address,
Caller,
Origin,
CallValue,
CallDataSize,
GasPrice,
CoinBase,
TimeStamp,
Number,
Difficulty,
GasLimit,
Number,
Timestamp,
Code,
CodeSize,
_size,
ReturnDataOffset = CallValue, // Reuse 2 fields for return data reference
ReturnDataSize = CallDataSize,
SuicideDestAddress = Address, ///< Suicide balance destination address
SuicideDestAddress = Address, ///< Suicide balance destination address
ReturnData = CallData, ///< Return data pointer (set only in case of RETURN)
ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN)
};
i256 elems[_size] = {};
int64_t gas = 0;
int64_t gasPrice = 0;
byte const* callData = nullptr;
uint64_t callDataSize = 0;
i256 address;
i256 caller;
i256 origin;
i256 callValue;
i256 coinBase;
i256 difficulty;
i256 gasLimit;
uint64_t number = 0;
int64_t timestamp = 0;
byte const* code = nullptr;
void set(Index _index, u256 _value) { elems[_index] = eth2llvm(_value); }
u256 get(Index _index) { return llvm2eth(elems[_index]); }
uint64_t codeSize = 0;
};
/// VM Environment (ExtVM) opaque type

91
evmjit/libevmjit/RuntimeManager.cpp

@ -22,9 +22,21 @@ llvm::StructType* RuntimeManager::getRuntimeDataType()
{
llvm::Type* elems[] =
{
llvm::ArrayType::get(Type::Word, RuntimeData::_size), // i256[]
Type::BytePtr, // callData
Type::BytePtr // code
Type::Size, // gas
Type::Size, // gasPrice
Type::BytePtr, // callData
Type::Size, // callDataSize
Type::Word, // address
Type::Word, // caller
Type::Word, // origin
Type::Word, // callValue
Type::Word, // coinBase
Type::Word, // difficulty
Type::Word, // gasLimit
Type::Size, // blockNumber
Type::Size, // blockTimestamp
Type::BytePtr, // code
Type::Size, // codeSize
};
type = llvm::StructType::create(elems, "RuntimeData");
}
@ -56,19 +68,21 @@ llvm::Twine getName(RuntimeData::Index _index)
switch (_index)
{
default: return "data";
case RuntimeData::Gas: return "gas";
case RuntimeData::Address: return "address";
case RuntimeData::Caller: return "caller";
case RuntimeData::Origin: return "origin";
case RuntimeData::CallValue: return "callvalue";
case RuntimeData::CallDataSize: return "calldatasize";
case RuntimeData::GasPrice: return "gasprice";
case RuntimeData::CoinBase: return "coinbase";
case RuntimeData::TimeStamp: return "timestamp";
case RuntimeData::Number: return "number";
case RuntimeData::Difficulty: return "difficulty";
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;
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()
@ -111,24 +127,33 @@ llvm::Value* RuntimeManager::getEnvPtr()
llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index)
{
llvm::Value* idxList[] = {m_builder.getInt32(0), m_builder.getInt32(0), m_builder.getInt32(_index)};
return m_builder.CreateInBoundsGEP(getDataPtr(), idxList, getName(_index) + "Ptr");
auto ptr = getBuilder().CreateStructGEP(getDataPtr(), _index);
assert(getRuntimeDataType()->getElementType(_index)->getPointerTo() == ptr->getType());
return ptr;
}
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)
{
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)
{
set(RuntimeData::ReturnDataOffset, _offset);
set(RuntimeData::ReturnDataSize, _size);
auto memPtr = getBuilder().CreateStructGEP(getRuntimePtr(), 3);
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)
@ -146,32 +171,41 @@ llvm::Value* RuntimeManager::get(Instruction _inst)
switch (_inst)
{
default: assert(false); return nullptr;
case Instruction::GAS: return get(RuntimeData::Gas);
case Instruction::ADDRESS: return get(RuntimeData::Address);
case Instruction::CALLER: return get(RuntimeData::Caller);
case Instruction::ORIGIN: return get(RuntimeData::Origin);
case Instruction::CALLVALUE: return get(RuntimeData::CallValue);
case Instruction::CALLDATASIZE: return get(RuntimeData::CallDataSize);
case Instruction::GASPRICE: return get(RuntimeData::GasPrice);
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::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()
{
auto ptr = getBuilder().CreateStructGEP(getDataPtr(), 1, "calldataPtr");
return getBuilder().CreateLoad(ptr, "calldata");
return get(RuntimeData::CallData);
}
llvm::Value* RuntimeManager::getCode()
{
auto ptr = getBuilder().CreateStructGEP(getDataPtr(), 2, "codePtr");
return getBuilder().CreateLoad(ptr, "code");
return get(RuntimeData::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()
@ -182,14 +216,15 @@ llvm::Value* RuntimeManager::getJmpBuf()
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)
{
llvm::Value* idxList[] = {m_builder.getInt32(0), m_builder.getInt32(0), m_builder.getInt32(RuntimeData::Gas)};
auto ptr = m_builder.CreateInBoundsGEP(getDataPtr(), idxList, "gasPtr");
m_builder.CreateStore(_gas, ptr);
auto newGas = getBuilder().CreateTrunc(_gas, Type::Size);
set(RuntimeData::Gas, newGas);
}
}

2
evmjit/libevmjit/RuntimeManager.h

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

8
evmjit/libevmjit/Stack.cpp

@ -73,7 +73,7 @@ extern "C"
{
auto& stack = _rt->getStack();
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());
}
@ -92,7 +92,7 @@ extern "C"
auto& stack = _rt->getStack();
// TODO: encode _index and stack size in the return code
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);
}
@ -102,7 +102,7 @@ extern "C"
auto& stack = _rt->getStack();
// TODO: encode _index and stack size in the return code
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;
}
@ -116,7 +116,7 @@ extern "C"
index = std::numeric_limits<decltype(index)>::max(); // set max to fill with 0 leter
auto data = _rtData->callData;
auto size = _rtData->elems[RuntimeData::CallDataSize].a;
auto size = _rtData->callDataSize;
for (auto i = 0; i < 32; ++i)
{
if (index < size)

26
evmjit/libevmjit/Utils.cpp

@ -8,32 +8,6 @@ namespace eth
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::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"
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;
auto codeSize = data->elems[RuntimeData::CodeSize].a;
bytes bytecode;
bytecode.insert(bytecode.end(), codePtr, codePtr + codeSize);
EXPORT int evmjit_run(ExecutionEngine* _engine, RuntimeData* _data, Env* _env) noexcept
{
try
{
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));
evmjit_result result = {static_cast<int32_t>(returnCode), 0, nullptr};
if (returnCode == ReturnCode::Return && !engine.returnData.empty())
auto returnCode = _engine->run(bytecode, _data, _env);
return static_cast<int>(returnCode);
}
catch(...)
{
// TODO: Optimized returning data. Allocating memory on client side by callback function might be a good idea
result.returnDataSize = engine.returnData.size();
result.returnData = std::malloc(result.returnDataSize);
std::memcpy(result.returnData, engine.returnData.data(), result.returnDataSize);
return static_cast<int>(ReturnCode::UnexpectedException);
}
return result;
}
}

47
evmjit/libevmjit/interface.h

@ -1,53 +1,12 @@
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct evmjit_result
{
int32_t returnCode;
uint64_t returnDataSize;
void* returnData;
void* evmjit_create();
int evmjit_run(void* _jit, void* _data, void* _env);
void evmjit_destroy(void* _jit);
} 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
}

3
libdevcore/CMakeLists.txt

@ -26,9 +26,8 @@ else()
endif()
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_CHRONO_LIBRARIES})
if (APPLE)
find_package(Threads REQUIRED)

2
libdevcore/Common.cpp

@ -27,7 +27,7 @@ using 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.
* This version uses an database backend.
* This version uses a database backend.
* Usage:
* @code
* GenericTrieDB<MyDB> t(&myDB);

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

@ -72,6 +72,31 @@ bytes unpadLeft(bytes _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)
{
unsigned inc = 0;
@ -98,31 +123,6 @@ std::string prettyU256(u256 _n)
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)
{
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
#include <string>
#include <libethereum/Interface.h>
#include "Common.h"
#include "CommonData.h"
#include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/CommonIO.h>
#include "CommonEth.h"
namespace dev
{
@ -94,16 +96,8 @@ template <unsigned N> boost::multiprecision::number<boost::multiprecision::cpp_i
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 std::string jsToBinary(std::string const& _s)
{
return dev::toString(unpadded(jsToBytes(_s)));
}
inline std::string jsToDecimal(std::string const& _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);
}
}
// 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
{
Address from;
@ -136,3 +153,4 @@ struct TransactionSkeleton
};
}
}

2
libethereum/All.h

@ -1,7 +1,7 @@
#pragma once
#include "Account.h"
#include "BlockChain.h"
#include "CanonBlockChain.h"
#include "Client.h"
#include "Defaults.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;
}
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)
{
#if ALL_COMPILERS_ARE_CPP11_COMPLIANT
@ -91,31 +67,11 @@ ldb::Slice dev::eth::toSlice(h256 _h, unsigned _sub)
#endif
}
bytes BlockChain::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();
}
BlockChain::BlockChain(std::string _path, bool _killExisting)
BlockChain::BlockChain(bytes const& _genesisBlock, std::string _path, bool _killExisting)
{
// Initialise with the genesis as the last block on the longest chain.
m_genesisHash = BlockChain::genesis().hash;
m_genesisBlock = BlockChain::createGenesisBlock();
m_genesisBlock = _genesisBlock;
m_genesisHash = sha3(RLP(m_genesisBlock)[0].data());
open(_path, _killExisting);
}
@ -353,7 +309,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
}
#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;
// 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
{
// cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged();
// cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged();
if (!_from || !_to)
{
return h256s();
@ -386,14 +342,14 @@ h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, boo
h256s back;
unsigned fn = details(_from).number;
unsigned tn = details(_to).number;
// cdebug << "treeRoute" << fn << "..." << tn;
// cdebug << "treeRoute" << fn << "..." << tn;
while (fn > tn)
{
if (_pre)
ret.push_back(_from);
_from = details(_from).parent;
fn--;
// cdebug << "from:" << fn << _from.abridged();
// cdebug << "from:" << fn << _from.abridged();
}
while (fn < tn)
{
@ -401,7 +357,7 @@ h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, boo
back.push_back(_to);
_to = details(_to).parent;
tn--;
// cdebug << "to:" << tn << _to.abridged();
// cdebug << "to:" << tn << _to.abridged();
}
while (_from != _to)
{
@ -415,7 +371,7 @@ h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, boo
back.push_back(_to);
fn--;
tn--;
// cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged();
// cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged();
}
if (o_common)
*o_common = _from;

18
libethereum/BlockChain.h

@ -69,8 +69,7 @@ ldb::Slice toSlice(h256 _h, unsigned _sub = 0);
class BlockChain
{
public:
BlockChain(bool _killExisting = false): BlockChain(std::string(), _killExisting) {}
BlockChain(std::string _path, bool _killExisting = false);
BlockChain(bytes const& _genesisBlock, std::string _path, bool _killExisting);
~BlockChain();
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
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.
h256s attemptImport(bytes const& _block, OverlayDB const& _stateDB) noexcept;
@ -131,13 +130,6 @@ public:
/// togther with all their quoted uncles.
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
* 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);
if (s.empty())
{
// cout << "Not found in DB: " << _h << endl;
// cout << "Not found in DB: " << _h << endl;
return _n;
}
@ -208,10 +200,6 @@ private:
ldb::WriteOptions m_writeOptions;
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);

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

6
libethereum/Client.h

@ -34,7 +34,7 @@
#include <libdevcore/Worker.h>
#include <libevm/FeeStructure.h>
#include <libp2p/Common.h>
#include "BlockChain.h"
#include "CanonBlockChain.h"
#include "TransactionQueue.h"
#include "State.h"
#include "CommonNet.h"
@ -228,7 +228,7 @@ public:
/// Get the object representing the current state of Ethereum.
dev::eth::State postState() const { ReadGuard l(x_stateDB); return m_postMine; }
/// Get the object representing the current canonical blockchain.
BlockChain const& blockChain() const { return m_bc; }
CanonBlockChain const& blockChain() const { return m_bc; }
// Mining stuff:
@ -308,7 +308,7 @@ private:
State asOf(unsigned _h) const;
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.
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::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
{
_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 eth
{
class LogFilter;
}
/// Simple stream output for the StateDiff.
std::ostream& operator<<(std::ostream& _out, dev::eth::LogFilter const& _s);
namespace eth
{
@ -56,14 +65,17 @@ public:
LogFilter withEarliest(int _e) { m_earliest = _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:
AddressSet m_addresses;
std::array<h256Set, 4> m_topics;
int m_earliest = 0;
int m_latest = -1;
unsigned m_max;
unsigned m_skip;
unsigned m_max = 10;
unsigned m_skip = 0;
};
}
}

11
libethereum/State.cpp

@ -35,6 +35,7 @@
#include "ExtVM.h"
#include "Executive.h"
#include "CachedAddressState.h"
#include "CanonBlockChain.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
@ -74,18 +75,16 @@ State::State(Address _coinbaseAddress, OverlayDB const& _db, BaseState _bs):
paranoia("beginning of normal construction.", true);
if (_bs == BaseState::Genesis)
if (_bs == BaseState::CanonGenesis)
{
dev::eth::commit(genesisState(), m_db, m_state);
m_db.commit();
paranoia("after DB commit of normal construction.", true);
m_previousBlock = BlockChain::genesis();
m_previousBlock = CanonBlockChain::genesis();
}
else
{
m_previousBlock.setEmpty();
}
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.)
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.
bi.populate(_bc.block(bi.parentHash)); // move to parent.
@ -698,7 +697,7 @@ void State::commitToMine(BlockChain const& _bc)
RLPStream unclesData;
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.
// 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 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.
@ -68,7 +68,7 @@ class State
public:
/// 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.
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.jsonrpc === '2.0' &&
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

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.jsonrpc === '2.0' &&
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

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

@ -124,5 +124,20 @@ describe('jsonrpc', function () {
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);
};
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
/// @returns hashmap with all contract's method input variables
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) {
acc[current.name] = params[index];
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)
{
clogS(NetConnect) << "Disabling capability '" << m_host->name() << "'. Reason:" << _problem;
clogS(NetWarn) << "DISABLE: Disabling capability '" << m_host->name() << "'. Reason:" << _problem;
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
{
while (isWorking() && !m_run)
this_thread::sleep_for(chrono::milliseconds(50));
if (!m_run)
return;
@ -409,6 +411,8 @@ void Host::connect(std::string const& _addr, unsigned short _port) noexcept
void Host::connect(bi::tcp::endpoint const& _ep)
{
while (isWorking() && !m_run)
this_thread::sleep_for(chrono::milliseconds(50));
if (!m_run)
return;
@ -430,6 +434,8 @@ void Host::connect(bi::tcp::endpoint const& _ep)
void Host::connect(std::shared_ptr<Node> const& _n)
{
while (isWorking() && !m_run)
this_thread::sleep_for(chrono::milliseconds(50));
if (!m_run)
return;

17
libp2p/Session.cpp

@ -273,24 +273,24 @@ bool Session::interpret(RLP const& _r)
}
case PingPacket:
{
clogS(NetTriviaSummary) << "Ping";
clogS(NetTriviaSummary) << "Ping";
RLPStream s;
sealAndSend(prep(s, PongPacket));
break;
}
case PongPacket:
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;
case GetPeersPacket:
{
clogS(NetTriviaSummary) << "GetPeers";
clogS(NetTriviaSummary) << "GetPeers";
m_theyRequestedNodes = true;
serviceNodesRequest();
break;
}
case PeersPacket:
clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)";
clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)";
m_weRequestedNodes = false;
for (unsigned i = 1; i < _r.itemCount(); ++i)
{
@ -369,8 +369,13 @@ bool Session::interpret(RLP const& _r)
{
auto id = _r[0].toInt<unsigned>();
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))
return true;
if (id >= i.second->m_idOffset && id - i.second->m_idOffset < i.second->hostCapability()->messageCount())
{
if (i.second->m_enabled)
return i.second->interpret(id - i.second->m_idOffset, _r);
else
return true;
}
return false;
}
}

92
libserpent/compiler.cpp

@ -77,43 +77,12 @@ Node popwrap(Node node) {
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
programData opcodeify(Node node,
programAux aux=Aux(),
programVerticalAux vaux=verticalAux()) {
std::string symb = "_"+mkUniqueToken();
Metadata m = node.metadata;
// Get variables
if (!aux.vars.size()) {
aux.vars = getVariables(node);
aux.nextVarMem = aux.vars.size() * 32 + 32;
}
// Numbers
if (node.type == TOKEN) {
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") {
std::string varname = node.args[0].val;
// 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);
//std::cerr << varname << " " << printSimple(varNode) << "\n";
// Set variable
@ -173,8 +146,7 @@ programData opcodeify(Node node,
}
// Comments do nothing
else if (node.val == "comment") {
Node* nodelist = nullptr;
return pd(aux, multiToken(nodelist, 0, m), 0);
return pd(aux, astnode("_", m), 0);
}
// Custom operation sequence
// 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
programAux buildDict(Node program, programAux aux, int labelLength) {
void buildDict(Node program, programAux &aux, int labelLength) {
Metadata m = program.metadata;
// Token
if (program.type == TOKEN) {
@ -388,30 +360,24 @@ programAux buildDict(Node program, programAux aux, int labelLength) {
}
// A sub-program (ie. LLL)
else if (program.val == "____CODE") {
programAux auks = Aux();
int step = aux.step;
aux.step = 0;
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();
it != auks.vars.end();
it++) {
aux.vars[(*it).first] = (*it).second;
}
aux.step += auks.step;
aux.step += step;
}
// Normal sub-block
else {
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
Node substDict(Node program, programAux aux, int labelLength) {
void substDict(Node program, programAux aux, int labelLength, std::vector<Node> &out) {
Metadata m = program.metadata;
std::vector<Node> out;
std::vector<Node> inner;
if (program.type == TOKEN) {
if (program.val[0] == '$') {
@ -428,46 +394,32 @@ Node substDict(Node program, programAux aux, int labelLength) {
dist = decimalSub(end, start);
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 (isNumberLike(program)) {
inner = toByteArr(program.val, m);
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 {
for (unsigned i = 0; i < program.args.size(); i++) {
Node n = substDict(program.args[i], aux, labelLength);
if (n.type == TOKEN || n.args.size()) out.push_back(n);
substDict(program.args[i], aux, labelLength, out);
}
}
return astnode("_", out, m);
}
// Compiled fragtree -> compiled fragtree without labels
Node dereference(Node program) {
std::vector<Node> dereference(Node program) {
int sz = treeSize(program) * 4;
int labelLength = 1;
while (sz >= 256) { labelLength += 1; sz /= 256; }
programAux aux = buildDict(program, Aux(), labelLength);
return substDict(program, aux, labelLength);
}
// Dereferenced fragtree -> opcodes
std::vector<Node> flatten(Node derefed) {
programAux aux = Aux();
buildDict(program, aux, labelLength);
std::vector<Node> o;
if (derefed.type == TOKEN) {
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]);
}
}
substDict(program, aux, labelLength, o);
return o;
}
@ -512,12 +464,12 @@ std::vector<Node> deserialize(std::string ser) {
// Fragtree -> bin
std::string assemble(Node fragTree) {
return serialize(flatten(dereference(fragTree)));
return serialize(dereference(fragTree));
}
// Fragtree -> tokens
std::vector<Node> prettyAssemble(Node fragTree) {
return flatten(dereference(fragTree));
return dereference(fragTree);
}
// LLL -> bin

5
libserpent/compiler.h

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

7
libserpent/util.cpp

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

2
libsolidity/AST.cpp

@ -505,7 +505,7 @@ void FunctionCall::checkTypeRequirements()
// check duplicate names
for (size_t i = 0; i < m_names.size(); i++) {
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."));
}
}

7
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -28,7 +28,7 @@
#include <liblll/Compiler.h>
#include <libethereum/Client.h>
#include <libwebthree/WebThree.h>
#include <libdevcore/CommonJS.h>
#include <libethcore/CommonJS.h>
#include <libwhisper/Message.h>
#include <libwhisper/WhisperHost.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)
{
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)

4
libweb3jsonrpc/WebThreeStubServerBase.h

@ -36,9 +36,9 @@ namespace dev
{
class WebThreeNetworkFace;
class KeyPair;
struct TransactionSkeleton;
namespace eth
{
struct TransactionSkeleton;
class Interface;
}
namespace shh
@ -122,7 +122,7 @@ public:
std::map<dev::Public, dev::Secret> const& ids() const { return m_ids; }
protected:
virtual bool authenticate(dev::TransactionSkeleton const& _t);
virtual bool authenticate(dev::eth::TransactionSkeleton const& _t);
protected:
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 <QQmlContext>
#include <QQmlApplicationEngine>
#include <QQuickWindow>
#include <QWindow>
#include "CodeModel.h"
#include "FileIo.h"
#include "ClientModel.h"
#include "CodeEditorExtensionManager.h"
#include "Exceptions.h"
#include "AppContext.h"
#include "QEther.h"
#include "QVariableDefinition.h"
#include "HttpServer.h"
#include "AppContext.h"
using namespace dev;
using namespace dev::eth;
@ -49,6 +50,7 @@ AppContext::AppContext(QQmlApplicationEngine* _engine)
m_codeModel.reset(new CodeModel(this));
m_clientModel.reset(new ClientModel(this));
m_fileIo.reset(new FileIo());
connect(QApplication::clipboard(), &QClipboard::dataChanged, [this] { emit clipboardChanged();});
}
AppContext::~AppContext()
@ -82,7 +84,7 @@ void AppContext::load()
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer");
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"));
appLoaded();
}
@ -105,6 +107,12 @@ void AppContext::displayMessageDialog(QString _title, QString _message)
QMetaObject::invokeMethod(dialogWin, "open");
}
QString AppContext::clipboard() const
{
QClipboard *clipboard = QApplication::clipboard();
return clipboard->text();
}
void AppContext::toClipboard(QString _text)
{
QClipboard *clipboard = QApplication::clipboard();

4
mix/AppContext.h

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

41
mix/ClientModel.cpp

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

5
mix/ClientModel.h

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

2
mix/ContractCallDataEncoder.cpp

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

2
mix/DebuggingStateWrapper.cpp

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

222
mix/MixClient.cpp

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

30
mix/MixClient.h

@ -24,8 +24,10 @@
#pragma once
#include <vector>
#include <string>
#include <libethereum/Interface.h>
#include <libethereum/Client.h>
#include <libethereum/CanonBlockChain.h>
#include "MachineStates.h"
namespace dev
@ -33,26 +35,18 @@ namespace dev
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
{
public:
MixClient();
MixClient(std::string const& _dbPath);
virtual ~MixClient();
/// Reset state to the empty state with given balance.
void resetState(u256 _balance);
KeyPair const& userAccount() const { return m_userAccount; }
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
void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override;
@ -94,18 +88,22 @@ public:
private:
void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state);
void validateBlock(int _block) const;
void noteChanged(h256Set const& _filters);
dev::eth::State const& asOf(int _block) const;
dev::eth::State asOf(int _block) const;
KeyPair m_userAccount;
eth::State m_state;
eth::State m_startState;
OverlayDB m_stateDB;
eth::CanonBlockChain m_bc;
mutable boost::shared_mutex x_state;
mutable std::mutex m_filterLock;
std::map<h256, dev::eth::InstalledFilter> m_filters;
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.hpp>
#include <libdevcore/CommonJS.h>
#include <libethcore/CommonJS.h>
#include "QBigInt.h"
using namespace dev;

2
mix/QBigInt.h

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

2
mix/QVariableDefinition.cpp

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

3
mix/qml/MainContent.qml

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

15
mix/qml/WebCodeEditor.qml

@ -4,6 +4,7 @@ import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
import CodeEditorExtensionManager 1.0
import QtWebEngine 1.0
import QtWebEngine.experimental 1.0
Item {
signal editorTextChanged
@ -29,6 +30,18 @@ Item {
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
id: codeEditorView
anchors.fill: parent
@ -36,6 +49,7 @@ Item {
id: editorBrowser
url: "qrc:///qml/html/codeeditor.html"
anchors.fill: parent
experimental.settings.javascriptCanAccessClipboard: true
onJavaScriptConsoleMessage: {
console.log("editor: " + sourceID + ":" + lineNumber + ":" + message);
}
@ -47,6 +61,7 @@ Item {
setText(currentText, currentMode);
runJavaScript("getTextChanged()", function(result) { });
pollTimer.running = true;
syncClipboard();
}
}

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

@ -29,7 +29,7 @@
var ie = ie_upto10 || ie_11up;
var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]);
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 presto = /Opera\//.test(navigator.userAgent);
var safari = /Apple Computer/.test(navigator.vendor);

20
mix/qml/html/codeeditor.js

@ -6,6 +6,7 @@ var editor = CodeMirror(document.body, {
autofocus: true,
});
editor.setOption("theme", "solarized dark");
editor.setOption("indentUnit", 4);
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() {
return editor.changeRegistered;
};
getText = function() {
editor.changeRegistered = false;
return editor.getValue();
@ -42,3 +56,7 @@ setText = function(text) {
setMode = function(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.Layouts 1.1
import QtQuick.Window 2.1
import QtQuick.PrivateWidgets 1.1
import Qt.labs.settings 1.0
import org.ethereum.qml.QEther 1.0

5
pullSerpent.sh

@ -2,10 +2,11 @@
opwd="$PWD"
cd ../serpent
git stash
git pull
git stash pop
cp bignum.* compiler.* funcs.* lllparser.* opcodes.h parser.* rewriter.* tokenize.* util.* ../cpp-ethereum/libserpent/
cp cmdline.* "$opwd/sc/"
cp pyserpent.* "$opwd/libpyserpent/"
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";
}
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") {
std::cout << printTokens(prettyAssemble(parseLLL(input, true))) <<"\n";
@ -88,9 +88,6 @@ int main(int argv, char** argc) {
else if (command == "serialize") {
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") {
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_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)
{
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_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()
}

18
test/SolidityParser.cpp

@ -124,6 +124,24 @@ BOOST_AUTO_TEST_CASE(single_function_param)
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)
{
ASTPointer<ContractDefinition> contract;

5
test/commonjs.cpp

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

2
test/fork.cpp

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

8
test/genesis.cpp

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

2
test/jsonrpc.cpp

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

2
test/state.cpp

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

4
test/stateOriginal.cpp

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

2
test/txTest.cpp

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

6
third/MainWin.cpp

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

Loading…
Cancel
Save