Browse Source

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

cl-refactor
CJentzsch 10 years ago
parent
commit
27d3b980de
  1. 6
      CMakeLists.txt
  2. 2
      alethzero/CMakeLists.txt
  3. 28
      alethzero/MainWin.cpp
  4. 4
      alethzero/OurWebThreeStubServer.h
  5. 27
      build.py
  6. 13
      cmake/EthExecutableHelper.cmake
  7. 5
      evmjit/CMakeLists.txt
  8. 3
      evmjit/libevmjit-cpp/CMakeLists.txt
  9. 3
      evmjit/libevmjit-cpp/Env.cpp
  10. 67
      evmjit/libevmjit-cpp/JitVM.cpp
  11. 38
      evmjit/libevmjit-cpp/Utils.h
  12. 524
      evmjit/libevmjit/Arith256.cpp
  13. 29
      evmjit/libevmjit/Arith256.h
  14. 18
      evmjit/libevmjit/CMakeLists.txt
  15. 31
      evmjit/libevmjit/Common.h
  16. 54
      evmjit/libevmjit/Compiler.cpp
  17. 25
      evmjit/libevmjit/ExecutionEngine.cpp
  18. 10
      evmjit/libevmjit/ExecutionEngine.h
  19. 476
      evmjit/libevmjit/Memory.cpp
  20. 17
      evmjit/libevmjit/Runtime.cpp
  21. 105
      evmjit/libevmjit/Runtime.h
  22. 35
      evmjit/libevmjit/RuntimeData.h
  23. 91
      evmjit/libevmjit/RuntimeManager.cpp
  24. 2
      evmjit/libevmjit/RuntimeManager.h
  25. 8
      evmjit/libevmjit/Stack.cpp
  26. 26
      evmjit/libevmjit/Utils.cpp
  27. 3
      evmjit/libevmjit/Utils.h
  28. 47
      evmjit/libevmjit/interface.cpp
  29. 47
      evmjit/libevmjit/interface.h
  30. 3
      libdevcore/CMakeLists.txt
  31. 2
      libdevcore/Common.cpp
  32. 9
      libdevcore/CommonData.h
  33. 2
      libdevcrypto/TrieDB.h
  34. 21
      libethcore/CommonEth.cpp
  35. 2
      libethcore/CommonEth.h
  36. 51
      libethcore/CommonJS.cpp
  37. 40
      libethcore/CommonJS.h
  38. 2
      libethereum/All.h
  39. 62
      libethereum/BlockChain.cpp
  40. 18
      libethereum/BlockChain.h
  41. 86
      libethereum/CanonBlockChain.cpp
  42. 76
      libethereum/CanonBlockChain.h
  43. 42
      libethereum/Client.cpp
  44. 13
      libethereum/Client.h
  45. 7
      libethereum/LogFilter.cpp
  46. 16
      libethereum/LogFilter.h
  47. 54
      libethereum/State.cpp
  48. 14
      libethereum/State.h
  49. 2
      libjsqrc/ethereumjs/bower.json
  50. 374
      libjsqrc/ethereumjs/dist/ethereum.js
  51. 22
      libjsqrc/ethereumjs/dist/ethereum.js.map
  52. 2
      libjsqrc/ethereumjs/dist/ethereum.min.js
  53. 66
      libjsqrc/ethereumjs/example/event_inc.html
  54. 23
      libjsqrc/ethereumjs/lib/const.js
  55. 11
      libjsqrc/ethereumjs/lib/contract.js
  56. 72
      libjsqrc/ethereumjs/lib/event.js
  57. 7
      libjsqrc/ethereumjs/lib/filter.js
  58. 32
      libjsqrc/ethereumjs/lib/httpsync.js
  59. 65
      libjsqrc/ethereumjs/lib/jsonrpc.js
  60. 44
      libjsqrc/ethereumjs/lib/providermanager.js
  61. 3
      libjsqrc/ethereumjs/lib/qtsync.js
  62. 33
      libjsqrc/ethereumjs/lib/utils.js
  63. 62
      libjsqrc/ethereumjs/lib/web3.js
  64. 2
      libjsqrc/ethereumjs/package.json
  65. 438
      libjsqrc/ethereumjs/test/abi.inputParser.js
  66. 461
      libjsqrc/ethereumjs/test/abi.outputParser.js
  67. 2
      libjsqrc/ethereumjs/test/db.methods.js
  68. 2
      libjsqrc/ethereumjs/test/eth.methods.js
  69. 125
      libjsqrc/ethereumjs/test/event.inputParser.js
  70. 124
      libjsqrc/ethereumjs/test/event.js
  71. 81
      libjsqrc/ethereumjs/test/event.outputParser.js
  72. 143
      libjsqrc/ethereumjs/test/jsonrpc.isValidResponse.js
  73. 47
      libjsqrc/ethereumjs/test/jsonrpc.toBatchPayload.js
  74. 40
      libjsqrc/ethereumjs/test/jsonrpc.toPayload.js
  75. 2
      libjsqrc/ethereumjs/test/shh.methods.js
  76. 0
      libjsqrc/ethereumjs/test/test.utils.js
  77. 42
      libjsqrc/ethereumjs/test/utils.extractDisplayName.js
  78. 55
      libjsqrc/ethereumjs/test/utils.extractTypeName.js
  79. 2
      libjsqrc/ethereumjs/test/web3.methods.js
  80. 12
      libjsqrc/natspec.js
  81. 2
      libp2p/Capability.cpp
  82. 6
      libp2p/Host.cpp
  83. 17
      libp2p/Session.cpp
  84. 13
      libqwebthree/QWebThree.cpp
  85. 92
      libserpent/compiler.cpp
  86. 5
      libserpent/compiler.h
  87. 7
      libserpent/util.cpp
  88. 45
      libsolidity/AST.cpp
  89. 41
      libsolidity/AST.h
  90. 4
      libsolidity/Compiler.cpp
  91. 72
      libsolidity/ExpressionCompiler.cpp
  92. 4
      libsolidity/ExpressionCompiler.h
  93. 89
      libsolidity/Parser.cpp
  94. 7
      libsolidity/Parser.h
  95. 2
      libsolidity/Token.h
  96. 23
      libsolidity/Types.cpp
  97. 7
      libsolidity/grammar.txt
  98. 13
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  99. 4
      libweb3jsonrpc/WebThreeStubServerBase.h
  100. 45
      macdeployfix.sh

6
CMakeLists.txt

@ -131,11 +131,15 @@ endif()
add_subdirectory(libdevcore) add_subdirectory(libdevcore)
add_subdirectory(libevmcore) add_subdirectory(libevmcore)
add_subdirectory(liblll) add_subdirectory(liblll)
add_subdirectory(libserpent) if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
add_subdirectory(libserpent)
endif ()
add_subdirectory(libsolidity) add_subdirectory(libsolidity)
add_subdirectory(lllc) add_subdirectory(lllc)
add_subdirectory(solc) add_subdirectory(solc)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
add_subdirectory(sc) add_subdirectory(sc)
endif()
if (JSONRPC) if (JSONRPC)
add_subdirectory(libweb3jsonrpc) add_subdirectory(libweb3jsonrpc)

2
alethzero/CMakeLists.txt

@ -40,7 +40,9 @@ target_link_libraries(${EXECUTABLE} evm)
target_link_libraries(${EXECUTABLE} ethcore) target_link_libraries(${EXECUTABLE} ethcore)
target_link_libraries(${EXECUTABLE} devcrypto) target_link_libraries(${EXECUTABLE} devcrypto)
target_link_libraries(${EXECUTABLE} secp256k1) target_link_libraries(${EXECUTABLE} secp256k1)
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
target_link_libraries(${EXECUTABLE} serpent) target_link_libraries(${EXECUTABLE} serpent)
endif()
target_link_libraries(${EXECUTABLE} lll) target_link_libraries(${EXECUTABLE} lll)
target_link_libraries(${EXECUTABLE} solidity) target_link_libraries(${EXECUTABLE} solidity)
target_link_libraries(${EXECUTABLE} evmcore) target_link_libraries(${EXECUTABLE} evmcore)

28
alethzero/MainWin.cpp

@ -30,10 +30,12 @@
#include <QtCore/QtCore> #include <QtCore/QtCore>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <test/JsonSpiritHeaders.h> #include <test/JsonSpiritHeaders.h>
#ifndef _MSC_VER
#include <libserpent/funcs.h> #include <libserpent/funcs.h>
#include <libserpent/util.h> #include <libserpent/util.h>
#endif
#include <libdevcrypto/FileSystem.h> #include <libdevcrypto/FileSystem.h>
#include <libdevcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <liblll/Compiler.h> #include <liblll/Compiler.h>
#include <liblll/CodeFragment.h> #include <liblll/CodeFragment.h>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
@ -41,7 +43,7 @@
#include <libsolidity/SourceReferenceFormatter.h> #include <libsolidity/SourceReferenceFormatter.h>
#include <libevm/VM.h> #include <libevm/VM.h>
#include <libevm/VMFactory.h> #include <libevm/VMFactory.h>
#include <libethereum/BlockChain.h> #include <libethereum/CanonBlockChain.h>
#include <libethereum/ExtVM.h> #include <libethereum/ExtVM.h>
#include <libethereum/Client.h> #include <libethereum/Client.h>
#include <libethereum/Utility.h> #include <libethereum/Utility.h>
@ -136,9 +138,9 @@ Main::Main(QWidget *parent) :
#endif #endif
m_servers.append(QString::fromStdString(Host::pocHost() + ":30303")); m_servers.append(QString::fromStdString(Host::pocHost() + ":30303"));
cerr << "State root: " << BlockChain::genesis().stateRoot << endl; cerr << "State root: " << CanonBlockChain::genesis().stateRoot << endl;
auto block = BlockChain::createGenesisBlock(); auto block = CanonBlockChain::createGenesisBlock();
cerr << "Block Hash: " << BlockChain::genesis().hash << endl; cerr << "Block Hash: " << CanonBlockChain::genesis().hash << endl;
cerr << "Block RLP: " << RLP(block) << endl; cerr << "Block RLP: " << RLP(block) << endl;
cerr << "Block Hex: " << toHex(block) << endl; cerr << "Block Hex: " << toHex(block) << endl;
cerr << "Network protocol version: " << c_protocolVersion << endl; cerr << "Network protocol version: " << c_protocolVersion << endl;
@ -1047,7 +1049,7 @@ void Main::refreshBlockCount()
ui->blockCount->setText(QString("%6 #%1 @%3 T%2 PV%4 D%5").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff)).arg(c_protocolVersion).arg(c_databaseVersion).arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet")); ui->blockCount->setText(QString("%6 #%1 @%3 T%2 PV%4 D%5").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff)).arg(c_protocolVersion).arg(c_databaseVersion).arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet"));
} }
static bool blockMatch(string const& _f, BlockDetails const& _b, h256 _h, BlockChain const& _bc) static bool blockMatch(string const& _f, BlockDetails const& _b, h256 _h, CanonBlockChain const& _bc)
{ {
try try
{ {
@ -1189,7 +1191,10 @@ void Main::timerEvent(QTimerEvent*)
{ {
auto ls = ethereum()->checkWatch(i.first); auto ls = ethereum()->checkWatch(i.first);
if (ls.size()) if (ls.size())
{
cnote << "FIRING WATCH" << i.first << ls.size();
i.second(ls); i.second(ls);
}
} }
} }
@ -1202,15 +1207,16 @@ string Main::renderDiff(StateDiff const& _d) const
{ {
s << "<hr/>"; s << "<hr/>";
AccountDiff const& ad = i.second; AccountDiff ad = i.second;
s << "<code style=\"white-space: pre; font-weight: bold\">" << lead(ad.changeType()) << " </code>" << " <b>" << render(i.first).toStdString() << "</b>"; s << "<code style=\"white-space: pre; font-weight: bold\">" << lead(ad.changeType()) << " </code>" << " <b>" << render(i.first).toStdString() << "</b>";
if (!ad.exist.to()) if (!ad.exist.to())
continue; continue;
if (ad.balance) if (ad.balance)
{ {
s << "<br/>" << indent << "Balance " << dec << formatBalance(ad.balance.to()); s << "<br/>" << indent << "Balance " << dec << ad.balance.to() << " [=" << formatBalance(ad.balance.to()) << "]";
s << " <b>" << showpos << (((dev::bigint)ad.balance.to()) - ((dev::bigint)ad.balance.from())) << noshowpos << "</b>"; bigint d = (dev::bigint)ad.balance.to() - (dev::bigint)ad.balance.from();
s << " <b>" << showpos << dec << d << " [=" << formatBalance(d) << "]" << noshowpos << "</b>";
} }
if (ad.nonce) if (ad.nonce)
{ {
@ -1219,7 +1225,7 @@ string Main::renderDiff(StateDiff const& _d) const
} }
if (ad.code) if (ad.code)
{ {
s << "<br/>" << indent << "Code " << hex << ad.code.to().size() << " bytes"; s << "<br/>" << indent << "Code " << dec << ad.code.to().size() << " bytes";
if (ad.code.from().size()) if (ad.code.from().size())
s << " (" << ad.code.from().size() << " bytes)"; s << " (" << ad.code.from().size() << " bytes)";
} }
@ -1715,6 +1721,7 @@ void Main::on_data_textChanged()
solidity = "<h4>Solidity</h4><pre>Uncaught exception.</pre>"; solidity = "<h4>Solidity</h4><pre>Uncaught exception.</pre>";
} }
} }
#ifndef _MSC_VER
else if (sourceIsSerpent(src)) else if (sourceIsSerpent(src))
{ {
try try
@ -1728,6 +1735,7 @@ void Main::on_data_textChanged()
errors.push_back("Serpent " + err); errors.push_back("Serpent " + err);
} }
} }
#endif
else else
{ {
m_data = compileLLL(src, m_enableOptimizer, &errors); m_data = compileLLL(src, m_enableOptimizer, &errors);

4
alethzero/OurWebThreeStubServer.h

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

27
build.py

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

13
cmake/EthExecutableHelper.cmake

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

5
evmjit/CMakeLists.txt

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

3
evmjit/libevmjit-cpp/CMakeLists.txt

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

3
evmjit/libevmjit-cpp/Env.cpp

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

67
evmjit/libevmjit-cpp/JitVM.cpp

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

38
evmjit/libevmjit-cpp/Utils.h

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

524
evmjit/libevmjit/Arith256.cpp

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

29
evmjit/libevmjit/Arith256.h

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

18
evmjit/libevmjit/CMakeLists.txt

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

31
evmjit/libevmjit/Common.h

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

54
evmjit/libevmjit/Compiler.cpp

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

25
evmjit/libevmjit/ExecutionEngine.cpp

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

10
evmjit/libevmjit/ExecutionEngine.h

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

476
evmjit/libevmjit/Memory.cpp

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

17
evmjit/libevmjit/Runtime.cpp

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

105
evmjit/libevmjit/Runtime.h

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

35
evmjit/libevmjit/RuntimeData.h

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

91
evmjit/libevmjit/RuntimeManager.cpp

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

2
evmjit/libevmjit/RuntimeManager.h

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

8
evmjit/libevmjit/Stack.cpp

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

26
evmjit/libevmjit/Utils.cpp

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

3
evmjit/libevmjit/Utils.h

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

47
evmjit/libevmjit/interface.cpp

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

47
evmjit/libevmjit/interface.h

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

3
libdevcore/CMakeLists.txt

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

2
libdevcore/Common.cpp

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

9
libdevcore/CommonData.h

@ -246,4 +246,13 @@ inline std::set<_T> operator+(std::set<_T> const& _a, std::set<_T> const& _b)
/// Make normal string from fixed-length string. /// Make normal string from fixed-length string.
std::string toString(string32 const& _s); std::string toString(string32 const& _s);
template<class T, class U>
std::vector<T> keysOf(std::map<T, U> const& _m)
{
std::vector<T> ret;
for (auto const& i: _m)
ret.push_back(i.first);
return ret;
}
} }

2
libdevcrypto/TrieDB.h

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

21
libethcore/CommonEth.cpp

@ -63,22 +63,31 @@ vector<pair<u256, string>> const& units()
return g_units; return g_units;
} }
std::string formatBalance(u256 _b) std::string formatBalance(bigint const& _b)
{ {
ostringstream ret; ostringstream ret;
if (_b > g_units[0].first * 10000) u256 b;
if (_b < 0)
{ {
ret << (_b / g_units[0].first) << " " << g_units[0].second; ret << "-";
b = (u256)-_b;
}
else
b = (u256)_b;
if (b > g_units[0].first * 10000)
{
ret << (b / g_units[0].first) << " " << g_units[0].second;
return ret.str(); return ret.str();
} }
ret << setprecision(5); ret << setprecision(5);
for (auto const& i: g_units) for (auto const& i: g_units)
if (i.first != 1 && _b >= i.first * 100) if (i.first != 1 && b >= i.first * 100)
{ {
ret << (double(_b / (i.first / 1000)) / 1000.0) << " " << i.second; ret << (double(b / (i.first / 1000)) / 1000.0) << " " << i.second;
return ret.str(); return ret.str();
} }
ret << _b << " wei"; ret << b << " wei";
return ret.str(); return ret.str();
} }

2
libethcore/CommonEth.h

@ -39,7 +39,7 @@ extern const unsigned c_protocolVersion;
extern const unsigned c_databaseVersion; extern const unsigned c_databaseVersion;
/// User-friendly string representation of the amount _b in wei. /// User-friendly string representation of the amount _b in wei.
std::string formatBalance(u256 _b); std::string formatBalance(bigint const& _b);
/// Get information concerning the currency denominations. /// Get information concerning the currency denominations.
std::vector<std::pair<u256, std::string>> const& units(); std::vector<std::pair<u256, std::string>> const& units();

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

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

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

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

2
libethereum/All.h

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

62
libethereum/BlockChain.cpp

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

18
libethereum/BlockChain.h

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

86
libethereum/CanonBlockChain.cpp

@ -0,0 +1,86 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file CanonBlockChain.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "CanonBlockChain.h"
#include <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;
}
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

@ -0,0 +1,76 @@
/*
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.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(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;
};
}
}

42
libethereum/Client.cpp

@ -182,7 +182,7 @@ unsigned Client::installWatch(h256 _h)
Guard l(m_filterLock); Guard l(m_filterLock);
ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0; ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0;
m_watches[ret] = ClientWatch(_h); m_watches[ret] = ClientWatch(_h);
cwatch << "+++" << ret << _h; cwatch << "+++" << ret << _h.abridged();
} }
auto ch = logs(ret); auto ch = logs(ret);
if (ch.empty()) if (ch.empty())
@ -200,7 +200,10 @@ unsigned Client::installWatch(LogFilter const& _f)
{ {
Guard l(m_filterLock); Guard l(m_filterLock);
if (!m_filters.count(h)) if (!m_filters.count(h))
{
cwatch << "FFF" << _f << h.abridged();
m_filters.insert(make_pair(h, _f)); m_filters.insert(make_pair(h, _f));
}
} }
return installWatch(h); return installWatch(h);
} }
@ -220,13 +223,17 @@ void Client::uninstallWatch(unsigned _i)
auto fit = m_filters.find(id); auto fit = m_filters.find(id);
if (fit != m_filters.end()) if (fit != m_filters.end())
if (!--fit->second.refCount) if (!--fit->second.refCount)
{
cwatch << "*X*" << fit->first << ":" << fit->second.filter;
m_filters.erase(fit); m_filters.erase(fit);
}
} }
void Client::noteChanged(h256Set const& _filters) void Client::noteChanged(h256Set const& _filters)
{ {
Guard l(m_filterLock); Guard l(m_filterLock);
cnote << "noteChanged(" << _filters << ")"; if (_filters.size())
cnote << "noteChanged(" << _filters << ")";
// accrue all changes left in each filter into the watches. // accrue all changes left in each filter into the watches.
for (auto& i: m_watches) for (auto& i: m_watches)
if (_filters.count(i.second.id)) if (_filters.count(i.second.id))
@ -247,8 +254,11 @@ LocalisedLogEntries Client::peekWatch(unsigned _watchId) const
Guard l(m_filterLock); Guard l(m_filterLock);
try { try {
return m_watches.at(_watchId).changes; auto& w = m_watches.at(_watchId);
w.lastPoll = chrono::system_clock::now();
return w.changes;
} catch (...) {} } catch (...) {}
return LocalisedLogEntries(); return LocalisedLogEntries();
} }
@ -258,7 +268,9 @@ LocalisedLogEntries Client::checkWatch(unsigned _watchId)
LocalisedLogEntries ret; LocalisedLogEntries ret;
try { try {
std::swap(ret, m_watches.at(_watchId).changes); auto& w = m_watches.at(_watchId);
std::swap(ret, w.changes);
w.lastPoll = chrono::system_clock::now();
} catch (...) {} } catch (...) {}
return ret; return ret;
@ -361,13 +373,12 @@ void Client::setupState(State& _s)
cwork << "SETUP MINE"; cwork << "SETUP MINE";
_s = m_postMine; _s = m_postMine;
} }
_s.setUncles(m_bc);
if (m_paranoia) if (m_paranoia)
{ {
if (_s.amIJustParanoid(m_bc)) if (_s.amIJustParanoid(m_bc))
{ {
cnote << "I'm just paranoid. Block is fine."; cnote << "I'm just paranoid. Block is fine.";
_s.commitToMine(); _s.commitToMine(m_bc);
} }
else else
{ {
@ -375,7 +386,7 @@ void Client::setupState(State& _s)
} }
} }
else else
_s.commitToMine(); _s.commitToMine(m_bc);
} }
void Client::transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) void Client::transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice)
@ -554,6 +565,23 @@ void Client::doWork()
cworkout << "WORK"; cworkout << "WORK";
this_thread::sleep_for(chrono::milliseconds(100)); this_thread::sleep_for(chrono::milliseconds(100));
if (chrono::system_clock::now() - m_lastGarbageCollection > chrono::seconds(5))
{
// garbage collect on watches
vector<unsigned> toUninstall;
{
Guard l(m_filterLock);
for (auto key: keysOf(m_watches))
if (chrono::system_clock::now() - m_watches[key].lastPoll > chrono::seconds(20))
{
toUninstall.push_back(key);
cnote << "GC: Uninstall" << key << "(" << chrono::duration_cast<chrono::seconds>(chrono::system_clock::now() - m_watches[key].lastPoll).count() << "s old)";
}
}
for (auto i: toUninstall)
uninstallWatch(i);
m_lastGarbageCollection = chrono::system_clock::now();
}
} }
unsigned Client::numberOf(int _n) const unsigned Client::numberOf(int _n) const

13
libethereum/Client.h

@ -34,7 +34,7 @@
#include <libdevcore/Worker.h> #include <libdevcore/Worker.h>
#include <libevm/FeeStructure.h> #include <libevm/FeeStructure.h>
#include <libp2p/Common.h> #include <libp2p/Common.h>
#include "BlockChain.h" #include "CanonBlockChain.h"
#include "TransactionQueue.h" #include "TransactionQueue.h"
#include "State.h" #include "State.h"
#include "CommonNet.h" #include "CommonNet.h"
@ -89,11 +89,12 @@ static const LocalisedLogEntry InitialChange(SpecialLogEntry, 0);
struct ClientWatch struct ClientWatch
{ {
ClientWatch() {} ClientWatch(): lastPoll(std::chrono::system_clock::now()) {}
explicit ClientWatch(h256 _id): id(_id) {} explicit ClientWatch(h256 _id): id(_id), lastPoll(std::chrono::system_clock::now()) {}
h256 id; h256 id;
LocalisedLogEntries changes = LocalisedLogEntries{ InitialChange }; LocalisedLogEntries changes = LocalisedLogEntries{ InitialChange };
mutable std::chrono::system_clock::time_point lastPoll = std::chrono::system_clock::now();
}; };
struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 7; }; struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 7; };
@ -228,7 +229,7 @@ public:
/// Get the object representing the current state of Ethereum. /// Get the object representing the current state of Ethereum.
dev::eth::State postState() const { ReadGuard l(x_stateDB); return m_postMine; } dev::eth::State postState() const { ReadGuard l(x_stateDB); return m_postMine; }
/// Get the object representing the current canonical blockchain. /// Get the object representing the current canonical blockchain.
BlockChain const& blockChain() const { return m_bc; } CanonBlockChain const& blockChain() const { return m_bc; }
// Mining stuff: // Mining stuff:
@ -308,7 +309,7 @@ private:
State asOf(unsigned _h) const; State asOf(unsigned _h) const;
VersionChecker m_vc; ///< Dummy object to check & update the protocol version. VersionChecker m_vc; ///< Dummy object to check & update the protocol version.
BlockChain m_bc; ///< Maintains block database. CanonBlockChain m_bc; ///< Maintains block database.
TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain.
BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported). BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported).
@ -328,6 +329,8 @@ private:
mutable std::mutex m_filterLock; mutable std::mutex m_filterLock;
std::map<h256, InstalledFilter> m_filters; std::map<h256, InstalledFilter> m_filters;
std::map<unsigned, ClientWatch> m_watches; std::map<unsigned, ClientWatch> m_watches;
mutable std::chrono::system_clock::time_point m_lastGarbageCollection;
}; };
} }

7
libethereum/LogFilter.cpp

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

16
libethereum/LogFilter.h

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

54
libethereum/State.cpp

@ -35,6 +35,7 @@
#include "ExtVM.h" #include "ExtVM.h"
#include "Executive.h" #include "Executive.h"
#include "CachedAddressState.h" #include "CachedAddressState.h"
#include "CanonBlockChain.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
@ -74,18 +75,16 @@ State::State(Address _coinbaseAddress, OverlayDB const& _db, BaseState _bs):
paranoia("beginning of normal construction.", true); paranoia("beginning of normal construction.", true);
if (_bs == BaseState::Genesis) if (_bs == BaseState::CanonGenesis)
{ {
dev::eth::commit(genesisState(), m_db, m_state); dev::eth::commit(genesisState(), m_db, m_state);
m_db.commit(); m_db.commit();
paranoia("after DB commit of normal construction.", true); paranoia("after DB commit of normal construction.", true);
m_previousBlock = BlockChain::genesis(); m_previousBlock = CanonBlockChain::genesis();
} }
else else
{
m_previousBlock.setEmpty(); m_previousBlock.setEmpty();
}
resetCurrent(); resetCurrent();
@ -304,7 +303,7 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi)
// (Most recent state dump might end up being genesis.) // (Most recent state dump might end up being genesis.)
std::vector<h256> chain; std::vector<h256> chain;
while (bi.stateRoot != BlockChain::genesis().hash && m_db.lookup(bi.stateRoot).empty()) // while we don't have the state root of the latest block... while (bi.number != 0 && m_db.lookup(bi.stateRoot).empty()) // while we don't have the state root of the latest block...
{ {
chain.push_back(bi.hash); // push back for later replay. chain.push_back(bi.hash); // push back for later replay.
bi.populate(_bc.block(bi.parentHash)); // move to parent. bi.populate(_bc.block(bi.parentHash)); // move to parent.
@ -639,8 +638,7 @@ void State::uncommitToMine()
bool State::amIJustParanoid(BlockChain const& _bc) bool State::amIJustParanoid(BlockChain const& _bc)
{ {
setUncles(_bc); commitToMine(_bc);
commitToMine();
// Update difficulty according to timestamp. // Update difficulty according to timestamp.
m_currentBlock.difficulty = m_currentBlock.calculateDifficulty(m_previousBlock); m_currentBlock.difficulty = m_currentBlock.calculateDifficulty(m_previousBlock);
@ -683,11 +681,23 @@ LogBloom State::logBloom() const
return ret; return ret;
} }
void State::setUncles(BlockChain const& _bc) void State::commitToMine(BlockChain const& _bc)
{ {
uncommitToMine();
// cnote << "Committing to mine on block" << m_previousBlock.hash.abridged();
#ifdef ETH_PARANOIA
commit();
cnote << "Pre-reward stateRoot:" << m_state.root();
#endif
m_lastTx = m_db;
Addresses uncleAddresses;
RLPStream unclesData; RLPStream unclesData;
unsigned unclesCount = 0; unsigned unclesCount = 0;
if (m_previousBlock != BlockChain::genesis()) if (m_previousBlock.number != 0)
{ {
// Find great-uncles (or second-cousins or whatever they are) - children of great-grandparents, great-great-grandparents... that were not already uncles in previous generations. // Find great-uncles (or second-cousins or whatever they are) - children of great-grandparents, great-great-grandparents... that were not already uncles in previous generations.
// cout << "Checking " << m_previousBlock.hash << ", parent=" << m_previousBlock.parentHash << endl; // cout << "Checking " << m_previousBlock.hash << ", parent=" << m_previousBlock.parentHash << endl;
@ -703,26 +713,11 @@ void State::setUncles(BlockChain const& _bc)
BlockInfo ubi(_bc.block(u)); BlockInfo ubi(_bc.block(u));
ubi.streamRLP(unclesData, WithNonce); ubi.streamRLP(unclesData, WithNonce);
++unclesCount; ++unclesCount;
uncleAddresses.push_back(ubi.coinbaseAddress);
} }
} }
} }
RLPStream(unclesCount).appendRaw(unclesData.out(), unclesCount).swapOut(m_currentUncles);
m_currentBlock.sha3Uncles = sha3(m_currentUncles);
}
void State::commitToMine()
{
uncommitToMine();
// cnote << "Committing to mine on block" << m_previousBlock.hash.abridged();
#ifdef ETH_PARANOIA
commit();
cnote << "Pre-reward stateRoot:" << m_state.root();
#endif
m_lastTx = m_db;
MemoryDB tm; MemoryDB tm;
GenericTrieDB<MemoryDB> transactionsTrie(&tm); GenericTrieDB<MemoryDB> transactionsTrie(&tm);
transactionsTrie.init(); transactionsTrie.init();
@ -752,13 +747,12 @@ void State::commitToMine()
txs.swapOut(m_currentTxs); txs.swapOut(m_currentTxs);
RLPStream(unclesCount).appendRaw(unclesData.out(), unclesCount).swapOut(m_currentUncles);
m_currentBlock.transactionsRoot = transactionsTrie.root(); m_currentBlock.transactionsRoot = transactionsTrie.root();
m_currentBlock.receiptsRoot = receiptsTrie.root(); m_currentBlock.receiptsRoot = receiptsTrie.root();
m_currentBlock.logBloom = logBloom(); m_currentBlock.logBloom = logBloom();
m_currentBlock.sha3Uncles = sha3(m_currentUncles);
Addresses uncleAddresses;
for (const auto& r: RLP(m_currentUncles))
uncleAddresses.push_back(BlockInfo::fromHeader(r.data()).coinbaseAddress);
// Apply rewards last of all. // Apply rewards last of all.
applyRewards(uncleAddresses); applyRewards(uncleAddresses);
@ -805,7 +799,7 @@ void State::completeMine()
ret.appendRaw(m_currentTxs); ret.appendRaw(m_currentTxs);
ret.appendRaw(m_currentUncles); ret.appendRaw(m_currentUncles);
ret.swapOut(m_currentBytes); ret.swapOut(m_currentBytes);
m_currentBlock.hash = sha3(m_currentBytes); m_currentBlock.hash = sha3(RLP(m_currentBytes)[0].data());
cnote << "Mined " << m_currentBlock.hash.abridged() << "(parent: " << m_currentBlock.parentHash.abridged() << ")"; cnote << "Mined " << m_currentBlock.hash.abridged() << "(parent: " << m_currentBlock.parentHash.abridged() << ")";
// Quickly reset the transactions. // Quickly reset the transactions.

14
libethereum/State.h

@ -53,7 +53,7 @@ struct StateTrace: public LogChannel { static const char* name() { return "=S=";
struct StateDetail: public LogChannel { static const char* name() { return "/S/"; } static const int verbosity = 14; }; struct StateDetail: public LogChannel { static const char* name() { return "/S/"; } static const int verbosity = 14; };
struct StateSafeExceptions: public LogChannel { static const char* name() { return "(S)"; } static const int verbosity = 21; }; struct StateSafeExceptions: public LogChannel { static const char* name() { return "(S)"; } static const int verbosity = 21; };
enum class BaseState { Empty, Genesis }; enum class BaseState { Empty, CanonGenesis };
/** /**
* @brief Model of the current state of the ledger. * @brief Model of the current state of the ledger.
@ -68,7 +68,7 @@ class State
public: public:
/// Construct state object. /// Construct state object.
State(Address _coinbaseAddress = Address(), OverlayDB const& _db = OverlayDB(), BaseState _bs = BaseState::Genesis); State(Address _coinbaseAddress = Address(), OverlayDB const& _db = OverlayDB(), BaseState _bs = BaseState::CanonGenesis);
/// Construct state object from arbitrary point in blockchain. /// Construct state object from arbitrary point in blockchain.
State(OverlayDB const& _db, BlockChain const& _bc, h256 _hash); State(OverlayDB const& _db, BlockChain const& _bc, h256 _hash);
@ -105,16 +105,13 @@ public:
/// @returns true if all is ok. If it's false, worry. /// @returns true if all is ok. If it's false, worry.
bool amIJustParanoid(BlockChain const& _bc); bool amIJustParanoid(BlockChain const& _bc);
/// @brief Loads current block uncles from blockchain
void setUncles(BlockChain const& _bc);
/// Prepares the current state for mining. /// Prepares the current state for mining.
/// Commits all transactions into the trie, compiles uncles and transactions list, applies all /// Commits all transactions into the trie, compiles uncles and transactions list, applies all
/// rewards and populates the current block header with the appropriate hashes. /// rewards and populates the current block header with the appropriate hashes.
/// The only thing left to do after this is to actually mine(). /// The only thing left to do after this is to actually mine().
/// ///
/// This may be called multiple times and without issue. /// This may be called multiple times and without issue.
void commitToMine(); void commitToMine(BlockChain const& _bc);
/// Attempt to find valid nonce for block that this state represents. /// Attempt to find valid nonce for block that this state represents.
/// This function is thread-safe. You can safely have other interactions with this object while it is happening. /// This function is thread-safe. You can safely have other interactions with this object while it is happening.
@ -125,11 +122,14 @@ public:
/** Commit to DB and build the final block if the previous call to mine()'s result is completion. /** Commit to DB and build the final block if the previous call to mine()'s result is completion.
* Typically looks like: * Typically looks like:
* @code * @code
* while (notYetMined)
* {
* // lock * // lock
* commitToMine(); * commitToMine(_blockChain); // will call uncommitToMine if a repeat.
* // unlock * // unlock
* MineInfo info; * MineInfo info;
* for (info.complete = false; !info.complete; info = mine()) {} * for (info.complete = false; !info.complete; info = mine()) {}
* }
* // lock * // lock
* completeMine(); * completeMine();
* // unlock * // unlock

2
libjsqrc/ethereumjs/bower.json

@ -1,7 +1,7 @@
{ {
"name": "ethereum.js", "name": "ethereum.js",
"namespace": "ethereum", "namespace": "ethereum",
"version": "0.0.11", "version": "0.0.13",
"description": "Ethereum Compatible JavaScript API", "description": "Ethereum Compatible JavaScript API",
"main": ["./dist/ethereum.js", "./dist/ethereum.min.js"], "main": ["./dist/ethereum.js", "./dist/ethereum.min.js"],
"dependencies": { "dependencies": {

374
libjsqrc/ethereumjs/dist/ethereum.js

@ -210,7 +210,7 @@ module.exports = {
}; };
},{"./const":2,"./formatters":6,"./types":10,"./utils":11,"./web3":12}],2:[function(require,module,exports){ },{"./const":2,"./formatters":6,"./types":11,"./utils":12,"./web3":13}],2:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -238,9 +238,32 @@ if ("build" !== 'build') {/*
var BigNumber = require('bignumber.js'); // jshint ignore:line var BigNumber = require('bignumber.js'); // jshint ignore:line
*/} */}
var ETH_UNITS = [
'wei',
'Kwei',
'Mwei',
'Gwei',
'szabo',
'finney',
'ether',
'grand',
'Mether',
'Gether',
'Tether',
'Pether',
'Eether',
'Zether',
'Yether',
'Nether',
'Dether',
'Vether',
'Uether'
];
module.exports = { module.exports = {
ETH_PADDING: 32, ETH_PADDING: 32,
ETH_SIGNATURE_LENGTH: 4, ETH_SIGNATURE_LENGTH: 4,
ETH_UNITS: ETH_UNITS,
ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN } ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }
}; };
@ -368,6 +391,11 @@ var addFunctionsToContract = function (contract, desc, address) {
var addEventRelatedPropertiesToContract = function (contract, desc, address) { var addEventRelatedPropertiesToContract = function (contract, desc, address) {
contract.address = address; contract.address = address;
contract._onWatchEventResult = function (data) {
var matchingEvent = event.getMatchingEvent(utils.filterEvents(desc));
var parser = eventImpl.outputParser(matchingEvent);
return parser(data);
};
Object.defineProperty(contract, 'topic', { Object.defineProperty(contract, 'topic', {
get: function() { get: function() {
@ -386,8 +414,12 @@ var addEventsToContract = function (contract, desc, address) {
var impl = function () { var impl = function () {
var params = Array.prototype.slice.call(arguments); var params = Array.prototype.slice.call(arguments);
var signature = abi.eventSignatureFromAscii(e.name); var signature = abi.eventSignatureFromAscii(e.name);
var event = eventImpl(address, signature, e); var event = eventImpl.inputParser(address, signature, e);
var o = event.apply(null, params); var o = event.apply(null, params);
o._onWatchEventResult = function (data) {
var parser = eventImpl.outputParser(e);
return parser(data);
};
return web3.eth.watch(o); return web3.eth.watch(o);
}; };
@ -455,7 +487,7 @@ var contract = function (address, desc) {
module.exports = contract; module.exports = contract;
},{"./abi":1,"./event":4,"./utils":11,"./web3":12}],4:[function(require,module,exports){ },{"./abi":1,"./event":4,"./utils":12,"./web3":13}],4:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -481,6 +513,16 @@ module.exports = contract;
var abi = require('./abi'); var abi = require('./abi');
var utils = require('./utils'); var utils = require('./utils');
/// filter inputs array && returns only indexed (or not) inputs
/// @param inputs array
/// @param bool if result should be an array of indexed params on not
/// @returns array of (not?) indexed params
var filterInputs = function (inputs, indexed) {
return inputs.filter(function (current) {
return current.indexed === indexed;
});
};
var inputWithName = function (inputs, name) { var inputWithName = function (inputs, name) {
var index = utils.findIndex(inputs, function (input) { var index = utils.findIndex(inputs, function (input) {
return input.name === name; return input.name === name;
@ -496,7 +538,7 @@ var inputWithName = function (inputs, name) {
var indexedParamsToTopics = function (event, indexed) { var indexedParamsToTopics = function (event, indexed) {
// sort keys? // sort keys?
return Object.keys(indexed).map(function (key) { return Object.keys(indexed).map(function (key) {
var inputs = [inputWithName(event.inputs, key)]; var inputs = [inputWithName(filterInputs(event.inputs, true), key)];
var value = indexed[key]; var value = indexed[key];
if (value instanceof Array) { if (value instanceof Array) {
@ -508,7 +550,7 @@ var indexedParamsToTopics = function (event, indexed) {
}); });
}; };
var implementationOfEvent = function (address, signature, event) { var inputParser = function (address, signature, event) {
// valid options are 'earliest', 'latest', 'offset' and 'max', as defined for 'eth.watch' // valid options are 'earliest', 'latest', 'offset' and 'max', as defined for 'eth.watch'
return function (indexed, options) { return function (indexed, options) {
@ -523,10 +565,66 @@ var implementationOfEvent = function (address, signature, event) {
}; };
}; };
module.exports = implementationOfEvent; var getArgumentsObject = function (inputs, indexed, notIndexed) {
var indexedCopy = indexed.slice();
var notIndexedCopy = notIndexed.slice();
return inputs.reduce(function (acc, current) {
var value;
if (current.indexed)
value = indexed.splice(0, 1)[0];
else
value = notIndexed.splice(0, 1)[0];
acc[current.name] = value;
return acc;
}, {});
};
var outputParser = function (event) {
return function (output) {
var result = {
event: utils.extractDisplayName(event.name),
number: output.number,
args: {}
};
if (!output.topic) {
return result;
}
var indexedOutputs = filterInputs(event.inputs, true);
var indexedData = "0x" + output.topic.slice(1, output.topic.length).map(function (topic) { return topic.slice(2); }).join("");
var indexedRes = abi.formatOutput(indexedOutputs, indexedData);
var notIndexedOutputs = filterInputs(event.inputs, false);
var notIndexedRes = abi.formatOutput(notIndexedOutputs, output.data);
result.args = getArgumentsObject(event.inputs, indexedRes, notIndexedRes);
},{"./abi":1,"./utils":11}],5:[function(require,module,exports){ return result;
};
};
var getMatchingEvent = function (events, payload) {
for (var i = 0; i < events.length; i++) {
var signature = abi.eventSignatureFromAscii(events[i].name);
if (signature === payload.topic[0]) {
return events[i];
}
}
return undefined;
};
module.exports = {
inputParser: inputParser,
outputParser: outputParser,
getMatchingEvent: getMatchingEvent
};
},{"./abi":1,"./utils":12}],5:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -565,6 +663,8 @@ var Filter = function(options, impl) {
if (options.topics) { if (options.topics) {
console.warn('"topics" is deprecated, use "topic" instead'); console.warn('"topics" is deprecated, use "topic" instead');
} }
this._onWatchResult = options._onWatchEventResult;
// evaluate lazy properties // evaluate lazy properties
options = { options = {
@ -583,7 +683,7 @@ var Filter = function(options, impl) {
this.callbacks = []; this.callbacks = [];
this.id = impl.newFilter(options); this.id = impl.newFilter(options);
web3.provider.startPolling({call: impl.changed, args: [this.id]}, this.id, this.trigger.bind(this)); web3.provider.startPolling({method: impl.changed, params: [this.id]}, this.id, this.trigger.bind(this));
}; };
/// alias for changed* /// alias for changed*
@ -603,7 +703,8 @@ Filter.prototype.changed = function(callback) {
Filter.prototype.trigger = function(messages) { Filter.prototype.trigger = function(messages) {
for (var i = 0; i < this.callbacks.length; i++) { for (var i = 0; i < this.callbacks.length; i++) {
for (var j = 0; j < messages.length; j++) { for (var j = 0; j < messages.length; j++) {
this.callbacks[i].call(this, messages[j]); var message = this._onWatchResult ? this._onWatchResult(messages[j]) : messages[j];
this.callbacks[i].call(this, message);
} }
} }
}; };
@ -626,7 +727,7 @@ Filter.prototype.logs = function () {
module.exports = Filter; module.exports = Filter;
},{"./web3":12}],6:[function(require,module,exports){ },{"./web3":13}],6:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -782,7 +883,7 @@ module.exports = {
}; };
},{"./const":2,"./utils":11}],7:[function(require,module,exports){ },{"./const":2,"./utils":12}],7:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -815,40 +916,16 @@ var HttpSyncProvider = function (host) {
this.host = host || 'http://localhost:8080'; this.host = host || 'http://localhost:8080';
}; };
/// Transforms inner message to proper jsonrpc object
/// @param inner message object
/// @returns jsonrpc object
function formatJsonRpcObject(object) {
return {
jsonrpc: '2.0',
method: object.call,
params: object.args,
id: object._id
};
}
/// Transforms jsonrpc object to inner message
/// @param incoming jsonrpc message
/// @returns inner message object
function formatJsonRpcMessage(message) {
var object = JSON.parse(message);
return {
_id: object.id,
data: object.result,
error: object.error
};
}
HttpSyncProvider.prototype.send = function (payload) { HttpSyncProvider.prototype.send = function (payload) {
var data = formatJsonRpcObject(payload); //var data = formatJsonRpcObject(payload);
var request = new XMLHttpRequest(); var request = new XMLHttpRequest();
request.open('POST', this.host, false); request.open('POST', this.host, false);
request.send(JSON.stringify(data)); request.send(JSON.stringify(payload));
// check request.status // check request.status
return request.responseText; var result = request.responseText;
return JSON.parse(result);
}; };
module.exports = HttpSyncProvider; module.exports = HttpSyncProvider;
@ -871,6 +948,73 @@ module.exports = HttpSyncProvider;
You should have received a copy of the GNU Lesser General Public License You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file jsonrpc.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
var messageId = 1;
/// Should be called to valid json create payload object
/// @param method of jsonrpc call, required
/// @param params, an array of method params, optional
/// @returns valid jsonrpc payload object
var toPayload = function (method, params) {
if (!method)
console.error('jsonrpc method should be specified!');
return {
jsonrpc: '2.0',
method: method,
params: params || [],
id: messageId++
};
};
/// Should be called to check if jsonrpc response is valid
/// @returns true if response is valid, otherwise false
var isValidResponse = function (response) {
return !!response &&
!response.error &&
response.jsonrpc === '2.0' &&
typeof response.id === 'number' &&
response.result !== undefined; // only undefined is not valid json object
};
/// Should be called to create batch payload object
/// @param messages, an array of objects with method (required) and params (optional) fields
var toBatchPayload = function (messages) {
return messages.map(function (message) {
return toPayload(message.method, message.params);
});
};
module.exports = {
toPayload: toPayload,
isValidResponse: isValidResponse,
toBatchPayload: toBatchPayload
};
},{}],9:[function(require,module,exports){
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file providermanager.js /** @file providermanager.js
* @authors: * @authors:
* Jeffrey Wilcke <jeff@ethdev.com> * Jeffrey Wilcke <jeff@ethdev.com>
@ -880,7 +1024,9 @@ module.exports = HttpSyncProvider;
* @date 2014 * @date 2014
*/ */
var web3 = require('./web3'); // jshint ignore:line var web3 = require('./web3');
var jsonrpc = require('./jsonrpc');
/** /**
* Provider manager object prototype * Provider manager object prototype
@ -894,48 +1040,38 @@ var web3 = require('./web3'); // jshint ignore:line
var ProviderManager = function() { var ProviderManager = function() {
this.polls = []; this.polls = [];
this.provider = undefined; this.provider = undefined;
this.id = 1;
var self = this; var self = this;
var poll = function () { var poll = function () {
if (self.provider) { self.polls.forEach(function (data) {
self.polls.forEach(function (data) { var result = self.send(data.data);
data.data._id = self.id;
self.id++; if (!(result instanceof Array) || result.length === 0) {
var result = self.provider.send(data.data); return;
}
result = JSON.parse(result);
data.callback(result);
// dont call the callback if result is not an array, or empty one });
if (result.error || !(result.result instanceof Array) || result.result.length === 0) {
return;
}
data.callback(result.result);
});
}
setTimeout(poll, 1000); setTimeout(poll, 1000);
}; };
poll(); poll();
}; };
/// sends outgoing requests /// sends outgoing requests
/// @params data - an object with at least 'method' property
ProviderManager.prototype.send = function(data) { ProviderManager.prototype.send = function(data) {
var payload = jsonrpc.toPayload(data.method, data.params);
data.args = data.args || [];
data._id = this.id++;
if (this.provider === undefined) { if (this.provider === undefined) {
console.error('provider is not set'); console.error('provider is not set');
return null; return null;
} }
//TODO: handle error here? var result = this.provider.send(payload);
var result = this.provider.send(data);
result = JSON.parse(result);
if (result.error) { if (!jsonrpc.isValidResponse(result)) {
console.log(result.error); console.log(result);
return null; return null;
} }
@ -966,7 +1102,7 @@ ProviderManager.prototype.stopPolling = function (pollId) {
module.exports = ProviderManager; module.exports = ProviderManager;
},{"./web3":12}],9:[function(require,module,exports){ },{"./jsonrpc":8,"./web3":13}],10:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -994,13 +1130,14 @@ var QtSyncProvider = function () {
}; };
QtSyncProvider.prototype.send = function (payload) { QtSyncProvider.prototype.send = function (payload) {
return navigator.qt.callMethod(JSON.stringify(payload)); var result = navigator.qt.callMethod(JSON.stringify(payload));
return JSON.parse(result);
}; };
module.exports = QtSyncProvider; module.exports = QtSyncProvider;
},{}],10:[function(require,module,exports){ },{}],11:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1081,7 +1218,7 @@ module.exports = {
}; };
},{"./formatters":6}],11:[function(require,module,exports){ },{"./formatters":6}],12:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1104,6 +1241,8 @@ module.exports = {
* @date 2015 * @date 2015
*/ */
var c = require('./const');
/// Finds first index of array element matching pattern /// Finds first index of array element matching pattern
/// @param array /// @param array
/// @param callback pattern /// @param callback pattern
@ -1166,7 +1305,7 @@ var extractDisplayName = function (name) {
var extractTypeName = function (name) { var extractTypeName = function (name) {
/// TODO: make it invulnerable /// TODO: make it invulnerable
var length = name.indexOf('('); var length = name.indexOf('(');
return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)) : ""; return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)).replace(' ', '') : "";
}; };
/// Filters all function from input abi /// Filters all function from input abi
@ -1185,6 +1324,32 @@ var filterEvents = function (json) {
}); });
}; };
/// used to transform value/string to eth string
/// TODO: use BigNumber.js to parse int
/// TODO: add tests for it!
var toEth = function (str) {
var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;
var unit = 0;
var units = c.ETH_UNITS;
while (val > 3000 && unit < units.length - 1)
{
val /= 1000;
unit++;
}
var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2);
var replaceFunction = function($0, $1, $2) {
return $1 + ',' + $2;
};
while (true) {
var o = s;
s = s.replace(/(\d)(\d\d\d[\.\,])/, replaceFunction);
if (o === s)
break;
}
return s + ' ' + units[unit];
};
module.exports = { module.exports = {
findIndex: findIndex, findIndex: findIndex,
toAscii: toAscii, toAscii: toAscii,
@ -1192,11 +1357,12 @@ module.exports = {
extractDisplayName: extractDisplayName, extractDisplayName: extractDisplayName,
extractTypeName: extractTypeName, extractTypeName: extractTypeName,
filterFunctions: filterFunctions, filterFunctions: filterFunctions,
filterEvents: filterEvents filterEvents: filterEvents,
toEth: toEth
}; };
},{}],12:[function(require,module,exports){ },{"./const":2}],13:[function(require,module,exports){
/* /*
This file is part of ethereum.js. This file is part of ethereum.js.
@ -1228,28 +1394,6 @@ if ("build" !== 'build') {/*
var utils = require('./utils'); var utils = require('./utils');
var ETH_UNITS = [
'wei',
'Kwei',
'Mwei',
'Gwei',
'szabo',
'finney',
'ether',
'grand',
'Mether',
'Gether',
'Tether',
'Pether',
'Eether',
'Zether',
'Yether',
'Nether',
'Dether',
'Vether',
'Uether'
];
/// @returns an array of objects describing web3 api methods /// @returns an array of objects describing web3 api methods
var web3Methods = function () { var web3Methods = function () {
return [ return [
@ -1357,8 +1501,8 @@ var setupMethods = function (obj, methods) {
var args = Array.prototype.slice.call(arguments); var args = Array.prototype.slice.call(arguments);
var call = typeof method.call === 'function' ? method.call(args) : method.call; var call = typeof method.call === 'function' ? method.call(args) : method.call;
return web3.provider.send({ return web3.provider.send({
call: call, method: call,
args: args params: args
}); });
}; };
}); });
@ -1371,15 +1515,15 @@ var setupProperties = function (obj, properties) {
var proto = {}; var proto = {};
proto.get = function () { proto.get = function () {
return web3.provider.send({ return web3.provider.send({
call: property.getter method: property.getter
}); });
}; };
if (property.setter) { if (property.setter) {
proto.set = function (val) { proto.set = function (val) {
return web3.provider.send({ return web3.provider.send({
call: property.setter, method: property.setter,
args: [val] params: [val]
}); });
}; };
} }
@ -1412,29 +1556,7 @@ var web3 = {
}, },
/// used to transform value/string to eth string /// used to transform value/string to eth string
/// TODO: use BigNumber.js to parse int toEth: utils.toEth,
toEth: function(str) {
var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;
var unit = 0;
var units = ETH_UNITS;
while (val > 3000 && unit < units.length - 1)
{
val /= 1000;
unit++;
}
var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2);
var replaceFunction = function($0, $1, $2) {
return $1 + ',' + $2;
};
while (true) {
var o = s;
s = s.replace(/(\d)(\d\d\d[\.\,])/, replaceFunction);
if (o === s)
break;
}
return s + ' ' + units[unit];
},
/// eth object prototype /// eth object prototype
eth: { eth: {
@ -1470,11 +1592,6 @@ var web3 = {
return new web3.filter(filter, shhWatch); return new web3.filter(filter, shhWatch);
} }
}, },
/// @returns true if provider is installed
haveProvider: function() {
return !!web3.provider.provider;
}
}; };
/// setups all api methods /// setups all api methods
@ -1497,14 +1614,13 @@ var shhWatch = {
setupMethods(shhWatch, shhWatchMethods()); setupMethods(shhWatch, shhWatchMethods());
web3.setProvider = function(provider) { web3.setProvider = function(provider) {
//provider.onmessage = messageHandler; // there will be no async calls, to remove
web3.provider.set(provider); web3.provider.set(provider);
}; };
module.exports = web3; module.exports = web3;
},{"./utils":11}],"web3":[function(require,module,exports){ },{"./utils":12}],"web3":[function(require,module,exports){
var web3 = require('./lib/web3'); var web3 = require('./lib/web3');
var ProviderManager = require('./lib/providermanager'); var ProviderManager = require('./lib/providermanager');
web3.provider = new ProviderManager(); web3.provider = new ProviderManager();
@ -1517,7 +1633,7 @@ web3.abi = require('./lib/abi');
module.exports = web3; module.exports = web3;
},{"./lib/abi":1,"./lib/contract":3,"./lib/filter":5,"./lib/httpsync":7,"./lib/providermanager":8,"./lib/qtsync":9,"./lib/web3":12}]},{},["web3"]) },{"./lib/abi":1,"./lib/contract":3,"./lib/filter":5,"./lib/httpsync":7,"./lib/providermanager":9,"./lib/qtsync":10,"./lib/web3":13}]},{},["web3"])
//# sourceMappingURL=ethereum.js.map //# sourceMappingURL=ethereum.js.map

22
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

66
libjsqrc/ethereumjs/example/event_inc.html

@ -0,0 +1,66 @@
<!doctype>
<html>
<head>
<script type="text/javascript" src="js/bignumber.js/bignumber.min.js"></script>
<script type="text/javascript" src="../dist/ethereum.js"></script>
<script type="text/javascript">
var web3 = require('web3');
web3.setProvider(new web3.providers.HttpSyncProvider('http://localhost:8080'));
var source = "" +
"contract Contract { " +
" event Incremented(bool indexed odd, uint x); " +
" function Contract() { " +
" x = 69; " +
" } " +
" function inc() { " +
" ++x; " +
" Incremented(x % 2 == 1, x); " +
" } " +
" uint x; " +
"}";
var desc = [{
"type":"event",
"name":"Incremented",
"inputs": [{"name":"odd","type":"bool","indexed":true},{"name":"x","type":"uint","indexed":false}],
}, {
"type":"function",
"name":"inc",
"inputs": [],
"outputs": []
}];
var address;
var contract;
var update = function (x) {
document.getElementById('result').innerText = JSON.stringify(x);
};
var createContract = function () {
address = web3.eth.transact({code: web3.eth.solidity(source)});
contract = web3.eth.contract(address, desc);
contract.Incremented({odd: true}).changed(update);
};
var callContract = function () {
contract.call().inc();
};
</script>
</head>
<body>
<div>
<button type="button" onClick="createContract();">create contract</button>
</div>
<div>
<button type="button" onClick="callContract();">test1</button>
</div>
<div id="result">
</div>
</body>
</html>

23
libjsqrc/ethereumjs/lib/const.js

@ -25,9 +25,32 @@ if (process.env.NODE_ENV !== 'build') {
var BigNumber = require('bignumber.js'); // jshint ignore:line var BigNumber = require('bignumber.js'); // jshint ignore:line
} }
var ETH_UNITS = [
'wei',
'Kwei',
'Mwei',
'Gwei',
'szabo',
'finney',
'ether',
'grand',
'Mether',
'Gether',
'Tether',
'Pether',
'Eether',
'Zether',
'Yether',
'Nether',
'Dether',
'Vether',
'Uether'
];
module.exports = { module.exports = {
ETH_PADDING: 32, ETH_PADDING: 32,
ETH_SIGNATURE_LENGTH: 4, ETH_SIGNATURE_LENGTH: 4,
ETH_UNITS: ETH_UNITS,
ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN } ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }
}; };

11
libjsqrc/ethereumjs/lib/contract.js

@ -120,6 +120,11 @@ var addFunctionsToContract = function (contract, desc, address) {
var addEventRelatedPropertiesToContract = function (contract, desc, address) { var addEventRelatedPropertiesToContract = function (contract, desc, address) {
contract.address = address; contract.address = address;
contract._onWatchEventResult = function (data) {
var matchingEvent = event.getMatchingEvent(utils.filterEvents(desc));
var parser = eventImpl.outputParser(matchingEvent);
return parser(data);
};
Object.defineProperty(contract, 'topic', { Object.defineProperty(contract, 'topic', {
get: function() { get: function() {
@ -138,8 +143,12 @@ var addEventsToContract = function (contract, desc, address) {
var impl = function () { var impl = function () {
var params = Array.prototype.slice.call(arguments); var params = Array.prototype.slice.call(arguments);
var signature = abi.eventSignatureFromAscii(e.name); var signature = abi.eventSignatureFromAscii(e.name);
var event = eventImpl(address, signature, e); var event = eventImpl.inputParser(address, signature, e);
var o = event.apply(null, params); var o = event.apply(null, params);
o._onWatchEventResult = function (data) {
var parser = eventImpl.outputParser(e);
return parser(data);
};
return web3.eth.watch(o); return web3.eth.watch(o);
}; };

72
libjsqrc/ethereumjs/lib/event.js

@ -23,6 +23,16 @@
var abi = require('./abi'); var abi = require('./abi');
var utils = require('./utils'); var utils = require('./utils');
/// filter inputs array && returns only indexed (or not) inputs
/// @param inputs array
/// @param bool if result should be an array of indexed params on not
/// @returns array of (not?) indexed params
var filterInputs = function (inputs, indexed) {
return inputs.filter(function (current) {
return current.indexed === indexed;
});
};
var inputWithName = function (inputs, name) { var inputWithName = function (inputs, name) {
var index = utils.findIndex(inputs, function (input) { var index = utils.findIndex(inputs, function (input) {
return input.name === name; return input.name === name;
@ -38,7 +48,7 @@ var inputWithName = function (inputs, name) {
var indexedParamsToTopics = function (event, indexed) { var indexedParamsToTopics = function (event, indexed) {
// sort keys? // sort keys?
return Object.keys(indexed).map(function (key) { return Object.keys(indexed).map(function (key) {
var inputs = [inputWithName(event.inputs, key)]; var inputs = [inputWithName(filterInputs(event.inputs, true), key)];
var value = indexed[key]; var value = indexed[key];
if (value instanceof Array) { if (value instanceof Array) {
@ -50,7 +60,7 @@ var indexedParamsToTopics = function (event, indexed) {
}); });
}; };
var implementationOfEvent = function (address, signature, event) { var inputParser = function (address, signature, event) {
// valid options are 'earliest', 'latest', 'offset' and 'max', as defined for 'eth.watch' // valid options are 'earliest', 'latest', 'offset' and 'max', as defined for 'eth.watch'
return function (indexed, options) { return function (indexed, options) {
@ -65,5 +75,61 @@ var implementationOfEvent = function (address, signature, event) {
}; };
}; };
module.exports = implementationOfEvent; var getArgumentsObject = function (inputs, indexed, notIndexed) {
var indexedCopy = indexed.slice();
var notIndexedCopy = notIndexed.slice();
return inputs.reduce(function (acc, current) {
var value;
if (current.indexed)
value = indexed.splice(0, 1)[0];
else
value = notIndexed.splice(0, 1)[0];
acc[current.name] = value;
return acc;
}, {});
};
var outputParser = function (event) {
return function (output) {
var result = {
event: utils.extractDisplayName(event.name),
number: output.number,
args: {}
};
if (!output.topic) {
return result;
}
var indexedOutputs = filterInputs(event.inputs, true);
var indexedData = "0x" + output.topic.slice(1, output.topic.length).map(function (topic) { return topic.slice(2); }).join("");
var indexedRes = abi.formatOutput(indexedOutputs, indexedData);
var notIndexedOutputs = filterInputs(event.inputs, false);
var notIndexedRes = abi.formatOutput(notIndexedOutputs, output.data);
result.args = getArgumentsObject(event.inputs, indexedRes, notIndexedRes);
return result;
};
};
var getMatchingEvent = function (events, payload) {
for (var i = 0; i < events.length; i++) {
var signature = abi.eventSignatureFromAscii(events[i].name);
if (signature === payload.topic[0]) {
return events[i];
}
}
return undefined;
};
module.exports = {
inputParser: inputParser,
outputParser: outputParser,
getMatchingEvent: getMatchingEvent
};

7
libjsqrc/ethereumjs/lib/filter.js

@ -36,6 +36,8 @@ var Filter = function(options, impl) {
if (options.topics) { if (options.topics) {
console.warn('"topics" is deprecated, use "topic" instead'); console.warn('"topics" is deprecated, use "topic" instead');
} }
this._onWatchResult = options._onWatchEventResult;
// evaluate lazy properties // evaluate lazy properties
options = { options = {
@ -54,7 +56,7 @@ var Filter = function(options, impl) {
this.callbacks = []; this.callbacks = [];
this.id = impl.newFilter(options); this.id = impl.newFilter(options);
web3.provider.startPolling({call: impl.changed, args: [this.id]}, this.id, this.trigger.bind(this)); web3.provider.startPolling({method: impl.changed, params: [this.id]}, this.id, this.trigger.bind(this));
}; };
/// alias for changed* /// alias for changed*
@ -74,7 +76,8 @@ Filter.prototype.changed = function(callback) {
Filter.prototype.trigger = function(messages) { Filter.prototype.trigger = function(messages) {
for (var i = 0; i < this.callbacks.length; i++) { for (var i = 0; i < this.callbacks.length; i++) {
for (var j = 0; j < messages.length; j++) { for (var j = 0; j < messages.length; j++) {
this.callbacks[i].call(this, messages[j]); var message = this._onWatchResult ? this._onWatchResult(messages[j]) : messages[j];
this.callbacks[i].call(this, message);
} }
} }
}; };

32
libjsqrc/ethereumjs/lib/httpsync.js

@ -30,40 +30,16 @@ var HttpSyncProvider = function (host) {
this.host = host || 'http://localhost:8080'; this.host = host || 'http://localhost:8080';
}; };
/// Transforms inner message to proper jsonrpc object
/// @param inner message object
/// @returns jsonrpc object
function formatJsonRpcObject(object) {
return {
jsonrpc: '2.0',
method: object.call,
params: object.args,
id: object._id
};
}
/// Transforms jsonrpc object to inner message
/// @param incoming jsonrpc message
/// @returns inner message object
function formatJsonRpcMessage(message) {
var object = JSON.parse(message);
return {
_id: object.id,
data: object.result,
error: object.error
};
}
HttpSyncProvider.prototype.send = function (payload) { HttpSyncProvider.prototype.send = function (payload) {
var data = formatJsonRpcObject(payload); //var data = formatJsonRpcObject(payload);
var request = new XMLHttpRequest(); var request = new XMLHttpRequest();
request.open('POST', this.host, false); request.open('POST', this.host, false);
request.send(JSON.stringify(data)); request.send(JSON.stringify(payload));
// check request.status // check request.status
return request.responseText; var result = request.responseText;
return JSON.parse(result);
}; };
module.exports = HttpSyncProvider; module.exports = HttpSyncProvider;

65
libjsqrc/ethereumjs/lib/jsonrpc.js

@ -0,0 +1,65 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file jsonrpc.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
var messageId = 1;
/// Should be called to valid json create payload object
/// @param method of jsonrpc call, required
/// @param params, an array of method params, optional
/// @returns valid jsonrpc payload object
var toPayload = function (method, params) {
if (!method)
console.error('jsonrpc method should be specified!');
return {
jsonrpc: '2.0',
method: method,
params: params || [],
id: messageId++
};
};
/// Should be called to check if jsonrpc response is valid
/// @returns true if response is valid, otherwise false
var isValidResponse = function (response) {
return !!response &&
!response.error &&
response.jsonrpc === '2.0' &&
typeof response.id === 'number' &&
response.result !== undefined; // only undefined is not valid json object
};
/// Should be called to create batch payload object
/// @param messages, an array of objects with method (required) and params (optional) fields
var toBatchPayload = function (messages) {
return messages.map(function (message) {
return toPayload(message.method, message.params);
});
};
module.exports = {
toPayload: toPayload,
isValidResponse: isValidResponse,
toBatchPayload: toBatchPayload
};

44
libjsqrc/ethereumjs/lib/providermanager.js

@ -23,7 +23,9 @@
* @date 2014 * @date 2014
*/ */
var web3 = require('./web3'); // jshint ignore:line var web3 = require('./web3');
var jsonrpc = require('./jsonrpc');
/** /**
* Provider manager object prototype * Provider manager object prototype
@ -37,48 +39,38 @@ var web3 = require('./web3'); // jshint ignore:line
var ProviderManager = function() { var ProviderManager = function() {
this.polls = []; this.polls = [];
this.provider = undefined; this.provider = undefined;
this.id = 1;
var self = this; var self = this;
var poll = function () { var poll = function () {
if (self.provider) { self.polls.forEach(function (data) {
self.polls.forEach(function (data) { var result = self.send(data.data);
data.data._id = self.id;
self.id++; if (!(result instanceof Array) || result.length === 0) {
var result = self.provider.send(data.data); return;
}
result = JSON.parse(result);
data.callback(result);
// dont call the callback if result is not an array, or empty one });
if (result.error || !(result.result instanceof Array) || result.result.length === 0) {
return;
}
data.callback(result.result);
});
}
setTimeout(poll, 1000); setTimeout(poll, 1000);
}; };
poll(); poll();
}; };
/// sends outgoing requests /// sends outgoing requests
/// @params data - an object with at least 'method' property
ProviderManager.prototype.send = function(data) { ProviderManager.prototype.send = function(data) {
var payload = jsonrpc.toPayload(data.method, data.params);
data.args = data.args || [];
data._id = this.id++;
if (this.provider === undefined) { if (this.provider === undefined) {
console.error('provider is not set'); console.error('provider is not set');
return null; return null;
} }
//TODO: handle error here? var result = this.provider.send(payload);
var result = this.provider.send(data);
result = JSON.parse(result);
if (result.error) { if (!jsonrpc.isValidResponse(result)) {
console.log(result.error); console.log(result);
return null; return null;
} }

3
libjsqrc/ethereumjs/lib/qtsync.js

@ -25,7 +25,8 @@ var QtSyncProvider = function () {
}; };
QtSyncProvider.prototype.send = function (payload) { QtSyncProvider.prototype.send = function (payload) {
return navigator.qt.callMethod(JSON.stringify(payload)); var result = navigator.qt.callMethod(JSON.stringify(payload));
return JSON.parse(result);
}; };
module.exports = QtSyncProvider; module.exports = QtSyncProvider;

33
libjsqrc/ethereumjs/lib/utils.js

@ -20,6 +20,8 @@
* @date 2015 * @date 2015
*/ */
var c = require('./const');
/// Finds first index of array element matching pattern /// Finds first index of array element matching pattern
/// @param array /// @param array
/// @param callback pattern /// @param callback pattern
@ -82,7 +84,7 @@ var extractDisplayName = function (name) {
var extractTypeName = function (name) { var extractTypeName = function (name) {
/// TODO: make it invulnerable /// TODO: make it invulnerable
var length = name.indexOf('('); var length = name.indexOf('(');
return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)) : ""; return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)).replace(' ', '') : "";
}; };
/// Filters all function from input abi /// Filters all function from input abi
@ -101,6 +103,32 @@ var filterEvents = function (json) {
}); });
}; };
/// used to transform value/string to eth string
/// TODO: use BigNumber.js to parse int
/// TODO: add tests for it!
var toEth = function (str) {
var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;
var unit = 0;
var units = c.ETH_UNITS;
while (val > 3000 && unit < units.length - 1)
{
val /= 1000;
unit++;
}
var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2);
var replaceFunction = function($0, $1, $2) {
return $1 + ',' + $2;
};
while (true) {
var o = s;
s = s.replace(/(\d)(\d\d\d[\.\,])/, replaceFunction);
if (o === s)
break;
}
return s + ' ' + units[unit];
};
module.exports = { module.exports = {
findIndex: findIndex, findIndex: findIndex,
toAscii: toAscii, toAscii: toAscii,
@ -108,6 +136,7 @@ module.exports = {
extractDisplayName: extractDisplayName, extractDisplayName: extractDisplayName,
extractTypeName: extractTypeName, extractTypeName: extractTypeName,
filterFunctions: filterFunctions, filterFunctions: filterFunctions,
filterEvents: filterEvents filterEvents: filterEvents,
toEth: toEth
}; };

62
libjsqrc/ethereumjs/lib/web3.js

@ -29,28 +29,6 @@ if (process.env.NODE_ENV !== 'build') {
var utils = require('./utils'); var utils = require('./utils');
var ETH_UNITS = [
'wei',
'Kwei',
'Mwei',
'Gwei',
'szabo',
'finney',
'ether',
'grand',
'Mether',
'Gether',
'Tether',
'Pether',
'Eether',
'Zether',
'Yether',
'Nether',
'Dether',
'Vether',
'Uether'
];
/// @returns an array of objects describing web3 api methods /// @returns an array of objects describing web3 api methods
var web3Methods = function () { var web3Methods = function () {
return [ return [
@ -158,8 +136,8 @@ var setupMethods = function (obj, methods) {
var args = Array.prototype.slice.call(arguments); var args = Array.prototype.slice.call(arguments);
var call = typeof method.call === 'function' ? method.call(args) : method.call; var call = typeof method.call === 'function' ? method.call(args) : method.call;
return web3.provider.send({ return web3.provider.send({
call: call, method: call,
args: args params: args
}); });
}; };
}); });
@ -172,15 +150,15 @@ var setupProperties = function (obj, properties) {
var proto = {}; var proto = {};
proto.get = function () { proto.get = function () {
return web3.provider.send({ return web3.provider.send({
call: property.getter method: property.getter
}); });
}; };
if (property.setter) { if (property.setter) {
proto.set = function (val) { proto.set = function (val) {
return web3.provider.send({ return web3.provider.send({
call: property.setter, method: property.setter,
args: [val] params: [val]
}); });
}; };
} }
@ -213,29 +191,7 @@ var web3 = {
}, },
/// used to transform value/string to eth string /// used to transform value/string to eth string
/// TODO: use BigNumber.js to parse int toEth: utils.toEth,
toEth: function(str) {
var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;
var unit = 0;
var units = ETH_UNITS;
while (val > 3000 && unit < units.length - 1)
{
val /= 1000;
unit++;
}
var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2);
var replaceFunction = function($0, $1, $2) {
return $1 + ',' + $2;
};
while (true) {
var o = s;
s = s.replace(/(\d)(\d\d\d[\.\,])/, replaceFunction);
if (o === s)
break;
}
return s + ' ' + units[unit];
},
/// eth object prototype /// eth object prototype
eth: { eth: {
@ -271,11 +227,6 @@ var web3 = {
return new web3.filter(filter, shhWatch); return new web3.filter(filter, shhWatch);
} }
}, },
/// @returns true if provider is installed
haveProvider: function() {
return !!web3.provider.provider;
}
}; };
/// setups all api methods /// setups all api methods
@ -298,7 +249,6 @@ var shhWatch = {
setupMethods(shhWatch, shhWatchMethods()); setupMethods(shhWatch, shhWatchMethods());
web3.setProvider = function(provider) { web3.setProvider = function(provider) {
//provider.onmessage = messageHandler; // there will be no async calls, to remove
web3.provider.set(provider); web3.provider.set(provider);
}; };

2
libjsqrc/ethereumjs/package.json

@ -1,7 +1,7 @@
{ {
"name": "ethereum.js", "name": "ethereum.js",
"namespace": "ethereum", "namespace": "ethereum",
"version": "0.0.11", "version": "0.0.13",
"description": "Ethereum Compatible JavaScript API", "description": "Ethereum Compatible JavaScript API",
"main": "./index.js", "main": "./index.js",
"directories": { "directories": {

438
libjsqrc/ethereumjs/test/abi.parsers.js → libjsqrc/ethereumjs/test/abi.inputParser.js

@ -423,443 +423,5 @@ describe('abi', function() {
}); });
}); });
describe('outputParser', function() {
it('should parse output string', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: "string" }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"68656c6c6f000000000000000000000000000000000000000000000000000000")[0],
'hello'
);
assert.equal(
parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"776f726c64000000000000000000000000000000000000000000000000000000")[0],
'world'
);
});
it('should parse output uint', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'uint' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0].toString(10),
new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).toString(10)
);
assert.equal(
parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0].toString(10),
new BigNumber("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0", 16).toString(10)
);
});
it('should parse output uint256', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'uint256' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0].toString(10),
new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).toString(10)
);
assert.equal(
parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0].toString(10),
new BigNumber("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0", 16).toString(10)
);
});
it('should parse output uint128', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'uint128' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0].toString(10),
new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).toString(10)
);
assert.equal(
parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0].toString(10),
new BigNumber("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0", 16).toString(10)
);
});
it('should parse output int', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0], -1);
assert.equal(parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0], -16);
});
it('should parse output int256', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int256' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0], -1);
assert.equal(parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0], -16);
});
it('should parse output int128', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int128' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0], -1);
assert.equal(parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0], -16);
});
it('should parse output hash', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'hash' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
"0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"
);
});
it('should parse output hash256', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'hash256' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
"0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"
);
});
it('should parse output hash160', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'hash160' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
"0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"
);
// TODO shouldnt' the expected hash be shorter?
});
it('should parse output address', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'address' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
"0x407d73d8a49eeb85d32cf465507dd71d507100c1"
);
});
it('should parse output bool', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'bool' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], true);
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000000")[0], false);
});
it('should parse output real', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'real' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000100000000000000000000000000000000")[0], 1);
assert.equal(parser.test("0x0000000000000000000000000000000220000000000000000000000000000000")[0], 2.125);
assert.equal(parser.test("0x0000000000000000000000000000000880000000000000000000000000000000")[0], 8.5);
assert.equal(parser.test("0xffffffffffffffffffffffffffffffff00000000000000000000000000000000")[0], -1);
});
it('should parse output ureal', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'ureal' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000100000000000000000000000000000000")[0], 1);
assert.equal(parser.test("0x0000000000000000000000000000000220000000000000000000000000000000")[0], 2.125);
assert.equal(parser.test("0x0000000000000000000000000000000880000000000000000000000000000000")[0], 8.5);
});
it('should parse multiple output strings', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: "string" },
{ type: "string" }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"68656c6c6f000000000000000000000000000000000000000000000000000000" +
"776f726c64000000000000000000000000000000000000000000000000000000")[0],
'hello'
);
assert.equal(
parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"68656c6c6f000000000000000000000000000000000000000000000000000000" +
"776f726c64000000000000000000000000000000000000000000000000000000")[1],
'world'
);
});
it('should use proper method name', function () {
// given
var d = clone(description);
d[0].name = 'helloworld(int)';
d[0].outputs = [
{ type: "int" }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.helloworld("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.helloworld['int']("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
});
it('should parse multiple methods', function () {
// given
var d = [{
name: "test",
type: "function",
inputs: [{ type: "int" }],
outputs: [{ type: "int" }]
},{
name: "test2",
type: "function",
inputs: [{ type: "string" }],
outputs: [{ type: "string" }]
}];
// when
var parser = abi.outputParser(d);
//then
assert.equal(parser.test("0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test2("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"68656c6c6f000000000000000000000000000000000000000000000000000000")[0],
"hello"
);
});
it('should parse output array', function () {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int[]' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000002" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"0000000000000000000000000000000000000000000000000000000000000006")[0][0],
5
);
assert.equal(parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000002" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"0000000000000000000000000000000000000000000000000000000000000006")[0][1],
6
);
});
it('should parse 0x value', function () {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x")[0], 0);
});
it('should parse 0x value', function () {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'uint' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x")[0], 0);
});
});
}); });

461
libjsqrc/ethereumjs/test/abi.outputParser.js

@ -0,0 +1,461 @@
var assert = require('assert');
var BigNumber = require('bignumber.js');
var abi = require('../lib/abi.js');
var clone = function (object) { return JSON.parse(JSON.stringify(object)); };
var description = [{
"name": "test",
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
describe('abi', function() {
describe('outputParser', function() {
it('should parse output string', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: "string" }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"68656c6c6f000000000000000000000000000000000000000000000000000000")[0],
'hello'
);
assert.equal(
parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"776f726c64000000000000000000000000000000000000000000000000000000")[0],
'world'
);
});
it('should parse output uint', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'uint' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0].toString(10),
new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).toString(10)
);
assert.equal(
parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0].toString(10),
new BigNumber("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0", 16).toString(10)
);
});
it('should parse output uint256', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'uint256' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0].toString(10),
new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).toString(10)
);
assert.equal(
parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0].toString(10),
new BigNumber("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0", 16).toString(10)
);
});
it('should parse output uint128', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'uint128' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0].toString(10),
new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).toString(10)
);
assert.equal(
parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0].toString(10),
new BigNumber("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0", 16).toString(10)
);
});
it('should parse output int', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0], -1);
assert.equal(parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0], -16);
});
it('should parse output int256', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int256' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0], -1);
assert.equal(parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0], -16);
});
it('should parse output int128', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int128' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0], -1);
assert.equal(parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0], -16);
});
it('should parse output hash', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'hash' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
"0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"
);
});
it('should parse output hash256', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'hash256' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
"0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"
);
});
it('should parse output hash160', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'hash160' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
"0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"
);
// TODO shouldnt' the expected hash be shorter?
});
it('should parse output address', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'address' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
"0x407d73d8a49eeb85d32cf465507dd71d507100c1"
);
});
it('should parse output bool', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'bool' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], true);
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000000")[0], false);
});
it('should parse output real', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'real' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000100000000000000000000000000000000")[0], 1);
assert.equal(parser.test("0x0000000000000000000000000000000220000000000000000000000000000000")[0], 2.125);
assert.equal(parser.test("0x0000000000000000000000000000000880000000000000000000000000000000")[0], 8.5);
assert.equal(parser.test("0xffffffffffffffffffffffffffffffff00000000000000000000000000000000")[0], -1);
});
it('should parse output ureal', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'ureal' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000100000000000000000000000000000000")[0], 1);
assert.equal(parser.test("0x0000000000000000000000000000000220000000000000000000000000000000")[0], 2.125);
assert.equal(parser.test("0x0000000000000000000000000000000880000000000000000000000000000000")[0], 8.5);
});
it('should parse multiple output strings', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: "string" },
{ type: "string" }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"68656c6c6f000000000000000000000000000000000000000000000000000000" +
"776f726c64000000000000000000000000000000000000000000000000000000")[0],
'hello'
);
assert.equal(
parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"68656c6c6f000000000000000000000000000000000000000000000000000000" +
"776f726c64000000000000000000000000000000000000000000000000000000")[1],
'world'
);
});
it('should use proper method name', function () {
// given
var d = clone(description);
d[0].name = 'helloworld(int)';
d[0].outputs = [
{ type: "int" }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.helloworld("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.helloworld['int']("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
});
it('should parse multiple methods', function () {
// given
var d = [{
name: "test",
type: "function",
inputs: [{ type: "int" }],
outputs: [{ type: "int" }]
},{
name: "test2",
type: "function",
inputs: [{ type: "string" }],
outputs: [{ type: "string" }]
}];
// when
var parser = abi.outputParser(d);
//then
assert.equal(parser.test("0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test2("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"68656c6c6f000000000000000000000000000000000000000000000000000000")[0],
"hello"
);
});
it('should parse output array', function () {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int[]' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000002" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"0000000000000000000000000000000000000000000000000000000000000006")[0][0],
5
);
assert.equal(parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000002" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"0000000000000000000000000000000000000000000000000000000000000006")[0][1],
6
);
});
it('should parse 0x value', function () {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x")[0], 0);
});
it('should parse 0x value', function () {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'uint' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x")[0], 0);
});
});
});

2
libjsqrc/ethereumjs/test/db.methods.js

@ -1,7 +1,7 @@
var assert = require('assert'); var assert = require('assert');
var web3 = require('../index.js'); var web3 = require('../index.js');
var u = require('./utils.js'); var u = require('./test.utils.js');
describe('web3', function() { describe('web3', function() {
describe('db', function() { describe('db', function() {

2
libjsqrc/ethereumjs/test/eth.methods.js

@ -1,6 +1,6 @@
var assert = require('assert'); var assert = require('assert');
var web3 = require('../index.js'); var web3 = require('../index.js');
var u = require('./utils.js'); var u = require('./test.utils.js');
describe('web3', function() { describe('web3', function() {
describe('eth', function() { describe('eth', function() {

125
libjsqrc/ethereumjs/test/event.inputParser.js

@ -0,0 +1,125 @@
var assert = require('assert');
var event = require('../lib/event.js');
var f = require('../lib/formatters.js');
describe('event', function () {
describe('inputParser', function () {
it('should create basic filter input object', function () {
// given
var address = '0x012345';
var signature = '0x987654';
var e = {
name: 'Event',
inputs: [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}]
};
// when
var impl = event.inputParser(address, signature, e);
var result = impl();
// then
assert.equal(result.address, address);
assert.equal(result.topic.length, 1);
assert.equal(result.topic[0], signature);
});
it('should create filter input object with options', function () {
// given
var address = '0x012345';
var signature = '0x987654';
var options = {
earliest: 1,
latest: 2,
offset: 3,
max: 4
};
var e = {
name: 'Event',
inputs: [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}]
};
// when
var impl = event.inputParser(address, signature, e);
var result = impl({}, options);
// then
assert.equal(result.address, address);
assert.equal(result.topic.length, 1);
assert.equal(result.topic[0], signature);
assert.equal(result.earliest, options.earliest);
assert.equal(result.latest, options.latest);
assert.equal(result.offset, options.offset);
assert.equal(result.max, options.max);
});
it('should create filter input object with indexed params', function () {
// given
var address = '0x012345';
var signature = '0x987654';
var options = {
earliest: 1,
latest: 2,
offset: 3,
max: 4
};
var e = {
name: 'Event',
inputs: [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}]
};
// when
var impl = event.inputParser(address, signature, e);
var result = impl({a: 4}, options);
// then
assert.equal(result.address, address);
assert.equal(result.topic.length, 2);
assert.equal(result.topic[0], signature);
assert.equal(result.topic[1], f.formatInputInt(4));
assert.equal(result.earliest, options.earliest);
assert.equal(result.latest, options.latest);
assert.equal(result.offset, options.offset);
assert.equal(result.max, options.max);
});
it('should create filter input object with an array of indexed params', function () {
// given
var address = '0x012345';
var signature = '0x987654';
var options = {
earliest: 1,
latest: 2,
offset: 3,
max: 4
};
var e = {
name: 'Event',
inputs: [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}]
};
// when
var impl = event.inputParser(address, signature, e);
var result = impl({a: [4, 69]}, options);
// then
assert.equal(result.address, address);
assert.equal(result.topic.length, 2);
assert.equal(result.topic[0], signature);
assert.equal(result.topic[1][0], f.formatInputInt(4));
assert.equal(result.topic[1][1], f.formatInputInt(69));
assert.equal(result.earliest, options.earliest);
assert.equal(result.latest, options.latest);
assert.equal(result.offset, options.offset);
assert.equal(result.max, options.max);
});
});
});

124
libjsqrc/ethereumjs/test/event.js

@ -1,124 +0,0 @@
var assert = require('assert');
var event = require('../lib/event.js');
var f = require('../lib/formatters.js');
describe('event', function () {
it('should create basic filter input object', function () {
// given
var address = '0x012345';
var signature = '0x987654';
var e = {
name: 'Event',
inputs: [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}]
};
// when
var impl = event(address, signature, e);
var result = impl();
// then
assert.equal(result.address, address);
assert.equal(result.topic.length, 1);
assert.equal(result.topic[0], signature);
});
it('should create filter input object with options', function () {
// given
var address = '0x012345';
var signature = '0x987654';
var options = {
earliest: 1,
latest: 2,
offset: 3,
max: 4
};
var e = {
name: 'Event',
inputs: [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}]
};
// when
var impl = event(address, signature, e);
var result = impl({}, options);
// then
assert.equal(result.address, address);
assert.equal(result.topic.length, 1);
assert.equal(result.topic[0], signature);
assert.equal(result.earliest, options.earliest);
assert.equal(result.latest, options.latest);
assert.equal(result.offset, options.offset);
assert.equal(result.max, options.max);
});
it('should create filter input object with indexed params', function () {
// given
var address = '0x012345';
var signature = '0x987654';
var options = {
earliest: 1,
latest: 2,
offset: 3,
max: 4
};
var e = {
name: 'Event',
inputs: [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}]
};
// when
var impl = event(address, signature, e);
var result = impl({a: 4}, options);
// then
assert.equal(result.address, address);
assert.equal(result.topic.length, 2);
assert.equal(result.topic[0], signature);
assert.equal(result.topic[1], f.formatInputInt(4));
assert.equal(result.earliest, options.earliest);
assert.equal(result.latest, options.latest);
assert.equal(result.offset, options.offset);
assert.equal(result.max, options.max);
});
it('should create filter input object with an array of indexed params', function () {
// given
var address = '0x012345';
var signature = '0x987654';
var options = {
earliest: 1,
latest: 2,
offset: 3,
max: 4
};
var e = {
name: 'Event',
inputs: [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}]
};
// when
var impl = event(address, signature, e);
var result = impl({a: [4, 69]}, options);
// then
assert.equal(result.address, address);
assert.equal(result.topic.length, 2);
assert.equal(result.topic[0], signature);
assert.equal(result.topic[1][0], f.formatInputInt(4));
assert.equal(result.topic[1][1], f.formatInputInt(69));
assert.equal(result.earliest, options.earliest);
assert.equal(result.latest, options.latest);
assert.equal(result.offset, options.offset);
assert.equal(result.max, options.max);
});
});

81
libjsqrc/ethereumjs/test/event.outputParser.js

@ -0,0 +1,81 @@
var assert = require('assert');
var event = require('../lib/event.js');
describe('event', function () {
describe('outputParser', function () {
it('should parse basic event output object', function () {
// given
var output = {
"address":"0x78dfc5983baecf65f73e3de3a96cee24e6b7981e",
"data":"0x000000000000000000000000000000000000000000000000000000000000004b",
"number":2,
"topic":[
"0x6e61ef44ac2747ff8b84d353a908eb8bd5c3fb118334d57698c5cfc7041196ad",
"0x0000000000000000000000000000000000000000000000000000000000000001"
]
};
var e = {
name: 'Event',
inputs: [{"name":"a","type":"bool","indexed":true},{"name":"b","type":"uint256","indexed":false}]
};
// when
var impl = event.outputParser(e);
var result = impl(output);
// then
assert.equal(result.event, 'Event');
assert.equal(result.number, 2);
assert.equal(Object.keys(result.args).length, 2);
assert.equal(result.args.a, true);
assert.equal(result.args.b, 75);
});
it('should parse event output object arguments in correct order', function () {
// given
var output = {
"address":"0x78dfc5983baecf65f73e3de3a96cee24e6b7981e",
"data": "0x" +
"000000000000000000000000000000000000000000000000000000000000004b" +
"000000000000000000000000000000000000000000000000000000000000004c" +
"0000000000000000000000000000000000000000000000000000000000000001",
"number":3,
"topic":[
"0x6e61ef44ac2747ff8b84d353a908eb8bd5c3fb118334d57698c5cfc7041196ad",
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x0000000000000000000000000000000000000000000000000000000000000005"
]
};
var e = {
name: 'Event2',
inputs: [
{"name":"a","type":"bool","indexed":true},
{"name":"b","type":"int","indexed":false},
{"name":"c","type":"int","indexed":false},
{"name":"d","type":"int","indexed":true},
{"name":"e","type":"bool","indexed":false}
]
};
// when
var impl = event.outputParser(e);
var result = impl(output);
// then
assert.equal(result.event, 'Event2');
assert.equal(result.number, 3);
assert.equal(Object.keys(result.args).length, 5);
assert.equal(result.args.a, true);
assert.equal(result.args.b, 75);
assert.equal(result.args.c, 76);
assert.equal(result.args.d, 5);
assert.equal(result.args.e, true);
});
});
});

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

@ -0,0 +1,143 @@
var assert = require('assert');
var jsonrpc = require('../lib/jsonrpc');
describe('jsonrpc', function () {
describe('isValidResponse', function () {
it('should validate basic jsonrpc response', function () {
// given
var response = {
jsonrpc: '2.0',
id: 1,
result: []
};
// when
var valid = jsonrpc.isValidResponse(response);
// then
assert.equal(valid, true);
});
it('should validate basic undefined response', function () {
// given
var response = undefined;
// when
var valid = jsonrpc.isValidResponse(response);
// then
assert.equal(valid, false);
});
it('should validate jsonrpc response without jsonrpc field', function () {
// given
var response = {
id: 1,
result: []
};
// when
var valid = jsonrpc.isValidResponse(response);
// then
assert.equal(valid, false);
});
it('should validate jsonrpc response with wrong jsonrpc version', function () {
// given
var response = {
jsonrpc: '1.0',
id: 1,
result: []
};
// when
var valid = jsonrpc.isValidResponse(response);
// then
assert.equal(valid, false);
});
it('should validate jsonrpc response without id number', function () {
// given
var response = {
jsonrpc: '2.0',
result: []
};
// when
var valid = jsonrpc.isValidResponse(response);
// then
assert.equal(valid, false);
});
it('should validate jsonrpc response with wrong id field', function () {
// given
var response = {
jsonrpc: '2.0',
id: 'x',
result: []
};
// when
var valid = jsonrpc.isValidResponse(response);
// then
assert.equal(valid, false);
});
it('should validate jsonrpc response without result field', function () {
// given
var response = {
jsonrpc: '2.0',
id: 1
};
// when
var valid = jsonrpc.isValidResponse(response);
// then
assert.equal(valid, false);
});
it('should validate jsonrpc response with result field === false', function () {
// given
var response = {
jsonrpc: '2.0',
id: 1,
result: false
};
// when
var valid = jsonrpc.isValidResponse(response);
// then
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);
});
});
});

47
libjsqrc/ethereumjs/test/jsonrpc.toBatchPayload.js

@ -0,0 +1,47 @@
var assert = require('assert');
var jsonrpc = require('../lib/jsonrpc');
describe('jsonrpc', function () {
describe('toBatchPayload', function () {
it('should create basic batch payload', function () {
// given
var messages = [{
method: 'helloworld'
}, {
method: 'test2',
params: [1]
}];
// when
var payload = jsonrpc.toBatchPayload(messages);
// then
assert.equal(payload instanceof Array, true);
assert.equal(payload.length, 2);
assert.equal(payload[0].jsonrpc, '2.0');
assert.equal(payload[1].jsonrpc, '2.0');
assert.equal(payload[0].method, 'helloworld');
assert.equal(payload[1].method, 'test2');
assert.equal(payload[0].params instanceof Array, true);
assert.equal(payload[1].params.length, 1);
assert.equal(payload[1].params[0], 1);
assert.equal(typeof payload[0].id, 'number');
assert.equal(typeof payload[1].id, 'number');
assert.equal(payload[0].id + 1, payload[1].id);
});
it('should create batch payload for empty input array', function () {
// given
var messages = [];
// when
var payload = jsonrpc.toBatchPayload(messages);
// then
assert.equal(payload instanceof Array, true);
assert.equal(payload.length, 0);
});
});
});

40
libjsqrc/ethereumjs/test/jsonrpc.toPayload.js

@ -0,0 +1,40 @@
var assert = require('assert');
var jsonrpc = require('../lib/jsonrpc');
describe('jsonrpc', function () {
describe('toPayload', function () {
it('should create basic payload', function () {
// given
var method = 'helloworld';
// when
var payload = jsonrpc.toPayload(method);
// then
assert.equal(payload.jsonrpc, '2.0');
assert.equal(payload.method, method);
assert.equal(payload.params instanceof Array, true);
assert.equal(payload.params.length, 0);
assert.equal(typeof payload.id, 'number');
});
it('should create payload with params', function () {
// given
var method = 'helloworld1';
var params = [123, 'test'];
// when
var payload = jsonrpc.toPayload(method, params);
// then
assert.equal(payload.jsonrpc, '2.0');
assert.equal(payload.method, method);
assert.equal(payload.params.length, 2);
assert.equal(payload.params[0], params[0]);
assert.equal(payload.params[1], params[1]);
assert.equal(typeof payload.id, 'number');
});
});
});

2
libjsqrc/ethereumjs/test/shh.methods.js

@ -1,6 +1,6 @@
var assert = require('assert'); var assert = require('assert');
var web3 = require('../index.js'); var web3 = require('../index.js');
var u = require('./utils.js'); var u = require('./test.utils.js');
describe('web3', function() { describe('web3', function() {
describe('shh', function() { describe('shh', function() {

0
libjsqrc/ethereumjs/test/utils.js → libjsqrc/ethereumjs/test/test.utils.js

42
libjsqrc/ethereumjs/test/utils.extractDisplayName.js

@ -0,0 +1,42 @@
var assert = require('assert');
var utils = require('../lib/utils.js');
describe('utils', function () {
describe('extractDisplayName', function () {
it('should extract display name from method with no params', function () {
// given
var test = 'helloworld()';
// when
var displayName = utils.extractDisplayName(test);
// then
assert.equal(displayName, 'helloworld');
});
it('should extract display name from method with one param' , function () {
// given
var test = 'helloworld1(int)';
// when
var displayName = utils.extractDisplayName(test);
// then
assert.equal(displayName, 'helloworld1');
});
it('should extract display name from method with two params' , function () {
// given
var test = 'helloworld2(int,string)';
// when
var displayName = utils.extractDisplayName(test);
// then
assert.equal(displayName, 'helloworld2');
});
});
});

55
libjsqrc/ethereumjs/test/utils.extractTypeName.js

@ -0,0 +1,55 @@
var assert = require('assert');
var utils = require('../lib/utils.js');
describe('utils', function () {
describe('extractTypeName', function () {
it('should extract type name from method with no params', function () {
// given
var test = 'helloworld()';
// when
var typeName = utils.extractTypeName(test);
// then
assert.equal(typeName, '');
});
it('should extract type name from method with one param', function () {
// given
var test = 'helloworld1(int)';
// when
var typeName = utils.extractTypeName(test);
// then
assert.equal(typeName, 'int');
});
it('should extract type name from method with two params', function () {
// given
var test = 'helloworld2(int,string)';
// when
var typeName = utils.extractTypeName(test);
// then
assert.equal(typeName, 'int,string');
});
it('should extract type name from method with spaces between params', function () {
// given
var test = 'helloworld3(int, string)';
// when
var typeName = utils.extractTypeName(test);
// then
assert.equal(typeName, 'int,string');
});
});
});

2
libjsqrc/ethereumjs/test/web3.methods.js

@ -1,6 +1,6 @@
var assert = require('assert'); var assert = require('assert');
var web3 = require('../index.js'); var web3 = require('../index.js');
var u = require('./utils.js'); var u = require('./test.utils.js');
describe('web3', function() { describe('web3', function() {
u.methodExists(web3, 'sha3'); u.methodExists(web3, 'sha3');

12
libjsqrc/natspec.js

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

2
libp2p/Capability.cpp

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

6
libp2p/Host.cpp

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

17
libp2p/Session.cpp

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

13
libqwebthree/QWebThree.cpp

@ -41,20 +41,9 @@ void QWebThree::clientDieing()
this->disconnect(); this->disconnect();
} }
static QString formatInput(QJsonObject const& _object)
{
QJsonObject res;
res["jsonrpc"] = QString::fromStdString("2.0");
res["method"] = _object["call"];
res["params"] = _object["args"];
res["id"] = _object["_id"];
return QString::fromUtf8(QJsonDocument(res).toJson());
}
QString QWebThree::callMethod(QString _json) QString QWebThree::callMethod(QString _json)
{ {
QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object(); emit processData(_json, ""); // it's synchronous
emit processData(formatInput(f), ""); // it's synchronous
return m_response; return m_response;
} }

92
libserpent/compiler.cpp

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

5
libserpent/compiler.h

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

7
libserpent/util.cpp

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

45
libsolidity/AST.cpp

@ -133,7 +133,7 @@ void ContractDefinition::checkIllegalOverrides() const
FunctionDefinition const*& override = functions[name]; FunctionDefinition const*& override = functions[name];
if (!override) if (!override)
override = function.get(); override = function.get();
else if (override->isPublic() != function->isPublic() || else if (override->getVisibility() != function->getVisibility() ||
override->isDeclaredConst() != function->isDeclaredConst() || override->isDeclaredConst() != function->isDeclaredConst() ||
FunctionType(*override) != FunctionType(*function)) FunctionType(*override) != FunctionType(*function))
BOOST_THROW_EXCEPTION(override->createTypeError("Override changes extended function signature.")); BOOST_THROW_EXCEPTION(override->createTypeError("Override changes extended function signature."));
@ -475,6 +475,8 @@ void FunctionCall::checkTypeRequirements()
// number of non-mapping members // number of non-mapping members
if (m_arguments.size() != 1) if (m_arguments.size() != 1)
BOOST_THROW_EXCEPTION(createTypeError("More than one argument for explicit type conversion.")); BOOST_THROW_EXCEPTION(createTypeError("More than one argument for explicit type conversion."));
if (!m_names.empty())
BOOST_THROW_EXCEPTION(createTypeError("Type conversion can't allow named arguments."));
if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType())) if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType()))
BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed.")); BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed."));
m_type = type.getActualType(); m_type = type.getActualType();
@ -487,9 +489,44 @@ void FunctionCall::checkTypeRequirements()
TypePointers const& parameterTypes = functionType->getParameterTypes(); TypePointers const& parameterTypes = functionType->getParameterTypes();
if (parameterTypes.size() != m_arguments.size()) if (parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call.")); BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i])) if (m_names.empty())
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call.")); {
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call."));
}
else
{
auto const& parameterNames = functionType->getParameterNames();
if (parameterNames.size() != m_names.size())
BOOST_THROW_EXCEPTION(createTypeError("Some argument names are missing."));
// 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])
BOOST_THROW_EXCEPTION(createTypeError("Duplicate named argument."));
}
}
for (size_t i = 0; i < m_names.size(); i++) {
bool found = false;
for (size_t j = 0; j < parameterNames.size(); j++) {
if (parameterNames[j] == *m_names[i]) {
// check type convertible
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[j]))
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call."));
found = true;
break;
}
}
if (!found)
BOOST_THROW_EXCEPTION(createTypeError("Named argument doesn't match function declaration."));
}
}
// @todo actually the return type should be an anonymous struct, // @todo actually the return type should be an anonymous struct,
// but we change it to the type of the first return value until we have structs // but we change it to the type of the first return value until we have structs
if (functionType->getReturnParameterTypes().empty()) if (functionType->getReturnParameterTypes().empty())

41
libsolidity/AST.h

@ -133,12 +133,17 @@ class Declaration: public ASTNode
{ {
public: public:
enum class LValueType { NONE, LOCAL, STORAGE }; enum class LValueType { NONE, LOCAL, STORAGE };
enum class Visibility { DEFAULT, PUBLIC, PROTECTED, PRIVATE };
Declaration(Location const& _location, ASTPointer<ASTString> const& _name): Declaration(Location const& _location, ASTPointer<ASTString> const& _name,
ASTNode(_location), m_name(_name), m_scope(nullptr) {} Visibility _visibility = Visibility::DEFAULT):
ASTNode(_location), m_name(_name), m_visibility(_visibility), m_scope(nullptr) {}
/// @returns the declared name. /// @returns the declared name.
ASTString const& getName() const { return *m_name; } ASTString const& getName() const { return *m_name; }
Visibility getVisibility() const { return m_visibility == Visibility::DEFAULT ? getDefaultVisibility() : m_visibility; }
bool isPublic() const { return getVisibility() == Visibility::PUBLIC; }
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope. /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
/// Available only after name and type resolution step. /// Available only after name and type resolution step.
Declaration const* getScope() const { return m_scope; } Declaration const* getScope() const { return m_scope; }
@ -151,8 +156,12 @@ public:
/// @returns the lvalue type of expressions referencing this declaration /// @returns the lvalue type of expressions referencing this declaration
virtual LValueType getLValueType() const { return LValueType::NONE; } virtual LValueType getLValueType() const { return LValueType::NONE; }
protected:
virtual Visibility getDefaultVisibility() const { return Visibility::PUBLIC; }
private: private:
ASTPointer<ASTString> m_name; ASTPointer<ASTString> m_name;
Visibility m_visibility;
Declaration const* m_scope; Declaration const* m_scope;
}; };
@ -330,16 +339,15 @@ class FunctionDefinition: public Declaration, public VariableScope, public Docum
{ {
public: public:
FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name, FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name,
bool _isPublic, Declaration::Visibility _visibility, bool _isConstructor,
bool _isConstructor,
ASTPointer<ASTString> const& _documentation, ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters, ASTPointer<ParameterList> const& _parameters,
bool _isDeclaredConst, bool _isDeclaredConst,
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers, std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
ASTPointer<ParameterList> const& _returnParameters, ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body): ASTPointer<Block> const& _body):
Declaration(_location, _name), Documented(_documentation), Declaration(_location, _name, _visibility), Documented(_documentation),
m_isPublic(_isPublic), m_isConstructor(_isConstructor), m_isConstructor(_isConstructor),
m_parameters(_parameters), m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst), m_isDeclaredConst(_isDeclaredConst),
m_functionModifiers(_modifiers), m_functionModifiers(_modifiers),
@ -350,7 +358,6 @@ public:
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
bool isPublic() const { return m_isPublic; }
bool isConstructor() const { return m_isConstructor; } bool isConstructor() const { return m_isConstructor; }
bool isDeclaredConst() const { return m_isDeclaredConst; } bool isDeclaredConst() const { return m_isDeclaredConst; }
std::vector<ASTPointer<ModifierInvocation>> const& getModifiers() const { return m_functionModifiers; } std::vector<ASTPointer<ModifierInvocation>> const& getModifiers() const { return m_functionModifiers; }
@ -371,7 +378,6 @@ public:
std::string getCanonicalSignature() const; std::string getCanonicalSignature() const;
private: private:
bool m_isPublic;
bool m_isConstructor; bool m_isConstructor;
ASTPointer<ParameterList> m_parameters; ASTPointer<ParameterList> m_parameters;
bool m_isDeclaredConst; bool m_isDeclaredConst;
@ -388,10 +394,10 @@ class VariableDeclaration: public Declaration
{ {
public: public:
VariableDeclaration(Location const& _location, ASTPointer<TypeName> const& _type, VariableDeclaration(Location const& _location, ASTPointer<TypeName> const& _type,
ASTPointer<ASTString> const& _name, bool _isPublic, bool _isStateVar = false, ASTPointer<ASTString> const& _name, Visibility _visibility,
bool _isIndexed = false): bool _isStateVar = false, bool _isIndexed = false):
Declaration(_location, _name), m_typeName(_type), Declaration(_location, _name, _visibility), m_typeName(_type),
m_isPublic(_isPublic), m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed) {} m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
@ -404,13 +410,14 @@ public:
virtual LValueType getLValueType() const override; virtual LValueType getLValueType() const override;
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); } bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
bool isPublic() const { return m_isPublic; }
bool isStateVariable() const { return m_isStateVariable; } bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; } bool isIndexed() const { return m_isIndexed; }
protected:
Visibility getDefaultVisibility() const override { return Visibility::PROTECTED; }
private: private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var") ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
bool m_isPublic; ///< Whether there is an accessor for it or not
bool m_isStateVariable; ///< Whether or not this is a contract state variable bool m_isStateVariable; ///< Whether or not this is a contract state variable
bool m_isIndexed; ///< Whether this is an indexed variable (used by events). bool m_isIndexed; ///< Whether this is an indexed variable (used by events).
@ -956,14 +963,15 @@ class FunctionCall: public Expression
{ {
public: public:
FunctionCall(Location const& _location, ASTPointer<Expression> const& _expression, FunctionCall(Location const& _location, ASTPointer<Expression> const& _expression,
std::vector<ASTPointer<Expression>> const& _arguments): std::vector<ASTPointer<Expression>> const& _arguments, std::vector<ASTPointer<ASTString>> const& _names):
Expression(_location), m_expression(_expression), m_arguments(_arguments) {} Expression(_location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression const& getExpression() const { return *m_expression; } Expression const& getExpression() const { return *m_expression; }
std::vector<ASTPointer<Expression const>> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; } std::vector<ASTPointer<Expression const>> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; }
std::vector<ASTPointer<ASTString>> const& getNames() const { return m_names; }
/// Returns true if this is not an actual function call, but an explicit type conversion /// Returns true if this is not an actual function call, but an explicit type conversion
/// or constructor call. /// or constructor call.
@ -972,6 +980,7 @@ public:
private: private:
ASTPointer<Expression> m_expression; ASTPointer<Expression> m_expression;
std::vector<ASTPointer<Expression>> m_arguments; std::vector<ASTPointer<Expression>> m_arguments;
std::vector<ASTPointer<ASTString>> m_names;
}; };
/** /**

4
libsolidity/Compiler.cpp

@ -240,10 +240,6 @@ bool Compiler::visit(VariableDeclaration const& _variableDeclaration)
m_context << m_context.getFunctionEntryLabel(_variableDeclaration); m_context << m_context.getFunctionEntryLabel(_variableDeclaration);
ExpressionCompiler::appendStateVariableAccessor(m_context, _variableDeclaration); ExpressionCompiler::appendStateVariableAccessor(m_context, _variableDeclaration);
unsigned sizeOnStack = _variableDeclaration.getType()->getSizeOnStack();
solAssert(sizeOnStack <= 15, "Stack too deep.");
m_context << eth::dupInstruction(sizeOnStack + 1) << eth::Instruction::JUMP;
return false; return false;
} }

72
libsolidity/ExpressionCompiler.cpp

@ -22,6 +22,7 @@
#include <utility> #include <utility>
#include <numeric> #include <numeric>
#include <boost/range/adaptor/reversed.hpp>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcrypto/SHA3.h> #include <libdevcrypto/SHA3.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
@ -194,6 +195,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{ {
//@todo struct construction //@todo struct construction
solAssert(_functionCall.getArguments().size() == 1, ""); solAssert(_functionCall.getArguments().size() == 1, "");
solAssert(_functionCall.getNames().empty(), "");
Expression const& firstArgument = *_functionCall.getArguments().front(); Expression const& firstArgument = *_functionCall.getArguments().front();
firstArgument.accept(*this); firstArgument.accept(*this);
appendTypeConversion(*firstArgument.getType(), *_functionCall.getType()); appendTypeConversion(*firstArgument.getType(), *_functionCall.getType());
@ -201,8 +203,26 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
else else
{ {
FunctionType const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType()); FunctionType const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType());
vector<ASTPointer<Expression const>> arguments = _functionCall.getArguments(); TypePointers const& parameterTypes = function.getParameterTypes();
solAssert(arguments.size() == function.getParameterTypes().size(), ""); vector<ASTPointer<Expression const>> const& callArguments = _functionCall.getArguments();
vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.getNames();
solAssert(callArguments.size() == parameterTypes.size(), "");
vector<ASTPointer<Expression const>> arguments;
if (callArgumentNames.empty())
// normal arguments
arguments = callArguments;
else
// named arguments
for (auto const& parameterName: function.getParameterNames())
{
bool found = false;
for (size_t j = 0; j < callArgumentNames.size() && !found; j++)
if ((found = (parameterName == *callArgumentNames[j])))
// we found the actual parameter position
arguments.push_back(callArguments[j]);
solAssert(found, "");
}
switch (function.getLocation()) switch (function.getLocation())
{ {
@ -823,26 +843,58 @@ unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _typ
return length; return length;
} }
unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, unsigned ExpressionCompiler::appendTypeConversionAndMoveToMemory(Type const& _expectedType, Type const& _type,
Expression const& _expression, unsigned _memoryOffset) Location const& _location, unsigned _memoryOffset)
{ {
_expression.accept(*this); appendTypeConversion(_type, _expectedType, true);
appendTypeConversion(*_expression.getType(), _expectedType, true);
unsigned const c_numBytes = CompilerUtils::getPaddedSize(_expectedType.getCalldataEncodedSize()); unsigned const c_numBytes = CompilerUtils::getPaddedSize(_expectedType.getCalldataEncodedSize());
if (c_numBytes == 0 || c_numBytes > 32) if (c_numBytes == 0 || c_numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError() BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(_expression.getLocation()) << errinfo_sourceLocation(_location)
<< errinfo_comment("Type " + _expectedType.toString() + " not yet supported.")); << errinfo_comment("Type " + _expectedType.toString() + " not yet supported."));
bool const c_leftAligned = _expectedType.getCategory() == Type::Category::STRING; bool const c_leftAligned = _expectedType.getCategory() == Type::Category::STRING;
bool const c_padToWords = true; bool const c_padToWords = true;
return CompilerUtils(m_context).storeInMemory(_memoryOffset, c_numBytes, c_leftAligned, c_padToWords); return CompilerUtils(m_context).storeInMemory(_memoryOffset, c_numBytes, c_leftAligned, c_padToWords);
} }
unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType,
Expression const& _expression,
unsigned _memoryOffset)
{
_expression.accept(*this);
return appendTypeConversionAndMoveToMemory(_expectedType, *_expression.getType(), _expression.getLocation(), _memoryOffset);
}
void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl) void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)
{ {
m_currentLValue.fromStateVariable(_varDecl, _varDecl.getType()); FunctionType thisType(_varDecl);
solAssert(m_currentLValue.isInStorage(), ""); solAssert(thisType.getReturnParameterTypes().size() == 1, "");
m_currentLValue.retrieveValue(_varDecl.getType(), Location(), true); TypePointer const& resultType = thisType.getReturnParameterTypes().front();
unsigned sizeOnStack;
unsigned length = 0;
TypePointers const& params = thisType.getParameterTypes();
// move arguments to memory
for (TypePointer const& param: boost::adaptors::reverse(params))
length += appendTypeConversionAndMoveToMemory(*param, *param, Location(), length);
// retrieve the position of the mapping
m_context << m_context.getStorageLocationOfVariable(_varDecl);
for (TypePointer const& param: params)
{
// move offset to memory
CompilerUtils(m_context).storeInMemory(length);
unsigned argLen = CompilerUtils::getPaddedSize(param->getCalldataEncodedSize());
length -= argLen;
m_context << u256(argLen + 32) << u256(length) << eth::Instruction::SHA3;
}
m_currentLValue = LValue(m_context, LValue::STORAGE, *resultType);
m_currentLValue.retrieveValue(resultType, Location(), true);
sizeOnStack = resultType->getSizeOnStack();
solAssert(sizeOnStack <= 15, "Stack too deep.");
m_context << eth::dupInstruction(sizeOnStack + 1) << eth::Instruction::JUMP;
} }
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,

4
libsolidity/ExpressionCompiler.h

@ -97,6 +97,10 @@ private:
unsigned appendArgumentCopyToMemory(TypePointers const& _types, unsigned appendArgumentCopyToMemory(TypePointers const& _types,
std::vector<ASTPointer<Expression const>> const& _arguments, std::vector<ASTPointer<Expression const>> const& _arguments,
unsigned _memoryOffset = 0); unsigned _memoryOffset = 0);
/// Appends code that copies a type to memory.
/// @returns the number of bytes copied to memory
unsigned appendTypeConversionAndMoveToMemory(Type const& _expectedType, Type const& _type,
Location const& _location, unsigned _memoryOffset = 0);
/// Appends code that evaluates a single expression and copies it to memory (with optional offset). /// Appends code that evaluates a single expression and copies it to memory (with optional offset).
/// @returns the number of bytes copied to memory /// @returns the number of bytes copied to memory
unsigned appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression, unsigned appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression,

89
libsolidity/Parser.cpp

@ -131,27 +131,19 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
} }
while (m_scanner->getCurrentToken() == Token::COMMA); while (m_scanner->getCurrentToken() == Token::COMMA);
expectToken(Token::LBRACE); expectToken(Token::LBRACE);
bool visibilityIsPublic = true;
while (true) while (true)
{ {
Token::Value currentToken = m_scanner->getCurrentToken(); Token::Value currentToken = m_scanner->getCurrentToken();
if (currentToken == Token::RBRACE) if (currentToken == Token::RBRACE)
break; break;
else if (currentToken == Token::PUBLIC || currentToken == Token::PRIVATE)
{
visibilityIsPublic = (m_scanner->getCurrentToken() == Token::PUBLIC);
m_scanner->next();
expectToken(Token::COLON);
}
else if (currentToken == Token::FUNCTION) else if (currentToken == Token::FUNCTION)
functions.push_back(parseFunctionDefinition(visibilityIsPublic, name.get())); functions.push_back(parseFunctionDefinition(name.get()));
else if (currentToken == Token::STRUCT) else if (currentToken == Token::STRUCT)
structs.push_back(parseStructDefinition()); structs.push_back(parseStructDefinition());
else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING ||
Token::isElementaryTypeName(currentToken)) Token::isElementaryTypeName(currentToken))
{ {
VarDeclParserOptions options; VarDeclParserOptions options;
options.isPublic = visibilityIsPublic;
options.isStateVariable = true; options.isStateVariable = true;
stateVariables.push_back(parseVariableDeclaration(options)); stateVariables.push_back(parseVariableDeclaration(options));
expectToken(Token::SEMICOLON); expectToken(Token::SEMICOLON);
@ -177,7 +169,7 @@ ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
if (m_scanner->getCurrentToken() == Token::LPAREN) if (m_scanner->getCurrentToken() == Token::LPAREN)
{ {
m_scanner->next(); m_scanner->next();
arguments = parseFunctionCallArguments(); arguments = parseFunctionCallListArguments();
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RPAREN); expectToken(Token::RPAREN);
} }
@ -186,7 +178,22 @@ ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
return nodeFactory.createNode<InheritanceSpecifier>(name, arguments); return nodeFactory.createNode<InheritanceSpecifier>(name, arguments);
} }
ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic, ASTString const* _contractName) Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token)
{
Declaration::Visibility visibility;
if (_token == Token::PUBLIC)
visibility = Declaration::Visibility::PUBLIC;
else if (_token == Token::PROTECTED)
visibility = Declaration::Visibility::PROTECTED;
else if (_token == Token::PRIVATE)
visibility = Declaration::Visibility::PRIVATE;
else
solAssert(false, "Invalid visibility specifier.");
m_scanner->next();
return visibility;
}
ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const* _contractName)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring; ASTPointer<ASTString> docstring;
@ -201,16 +208,24 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic, A
name = expectIdentifierToken(); name = expectIdentifierToken();
ASTPointer<ParameterList> parameters(parseParameterList()); ASTPointer<ParameterList> parameters(parseParameterList());
bool isDeclaredConst = false; bool isDeclaredConst = false;
Declaration::Visibility visibility(Declaration::Visibility::DEFAULT);
vector<ASTPointer<ModifierInvocation>> modifiers; vector<ASTPointer<ModifierInvocation>> modifiers;
while (true) while (true)
{ {
if (m_scanner->getCurrentToken() == Token::CONST) Token::Value token = m_scanner->getCurrentToken();
if (token == Token::CONST)
{ {
isDeclaredConst = true; isDeclaredConst = true;
m_scanner->next(); m_scanner->next();
} }
else if (m_scanner->getCurrentToken() == Token::IDENTIFIER) else if (token == Token::IDENTIFIER)
modifiers.push_back(parseModifierInvocation()); modifiers.push_back(parseModifierInvocation());
else if (Token::isVisibilitySpecifier(token))
{
if (visibility != Declaration::Visibility::DEFAULT)
BOOST_THROW_EXCEPTION(createParserError("Multiple visibility specifiers."));
visibility = parseVisibilitySpecifier(token);
}
else else
break; break;
} }
@ -226,7 +241,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic, A
ASTPointer<Block> block = parseBlock(); ASTPointer<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block); nodeFactory.setEndPositionFromNode(block);
bool const c_isConstructor = (_contractName && *name == *_contractName); bool const c_isConstructor = (_contractName && *name == *_contractName);
return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, c_isConstructor, docstring, return nodeFactory.createNode<FunctionDefinition>(name, visibility, c_isConstructor, docstring,
parameters, isDeclaredConst, modifiers, parameters, isDeclaredConst, modifiers,
returnParameters, block); returnParameters, block);
} }
@ -253,14 +268,18 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(VarDeclParserOp
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ASTPointer<TypeName> type = parseTypeName(_options.allowVar); ASTPointer<TypeName> type = parseTypeName(_options.allowVar);
bool isIndexed = false; bool isIndexed = false;
if (_options.allowIndexed && m_scanner->getCurrentToken() == Token::INDEXED) Token::Value token = m_scanner->getCurrentToken();
if (_options.allowIndexed && token == Token::INDEXED)
{ {
isIndexed = true; isIndexed = true;
m_scanner->next(); m_scanner->next();
} }
Declaration::Visibility visibility(Declaration::Visibility::DEFAULT);
if (_options.isStateVariable && Token::isVisibilitySpecifier(token))
visibility = parseVisibilitySpecifier(token);
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken(), return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken(),
_options.isPublic, _options.isStateVariable, visibility, _options.isStateVariable,
isIndexed); isIndexed);
} }
@ -313,7 +332,7 @@ ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
if (m_scanner->getCurrentToken() == Token::LPAREN) if (m_scanner->getCurrentToken() == Token::LPAREN)
{ {
m_scanner->next(); m_scanner->next();
arguments = parseFunctionCallArguments(); arguments = parseFunctionCallListArguments();
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RPAREN); expectToken(Token::RPAREN);
} }
@ -573,7 +592,6 @@ ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence)
ASTPointer<Expression> expression = parseUnaryExpression(); ASTPointer<Expression> expression = parseUnaryExpression();
int precedence = Token::precedence(m_scanner->getCurrentToken()); int precedence = Token::precedence(m_scanner->getCurrentToken());
for (; precedence >= _minPrecedence; --precedence) for (; precedence >= _minPrecedence; --precedence)
{
while (Token::precedence(m_scanner->getCurrentToken()) == precedence) while (Token::precedence(m_scanner->getCurrentToken()) == precedence)
{ {
Token::Value op = m_scanner->getCurrentToken(); Token::Value op = m_scanner->getCurrentToken();
@ -582,7 +600,6 @@ ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence)
nodeFactory.setEndPositionFromNode(right); nodeFactory.setEndPositionFromNode(right);
expression = nodeFactory.createNode<BinaryOperation>(expression, op, right); expression = nodeFactory.createNode<BinaryOperation>(expression, op, right);
} }
}
return expression; return expression;
} }
@ -648,10 +665,12 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression()
case Token::LPAREN: case Token::LPAREN:
{ {
m_scanner->next(); m_scanner->next();
vector<ASTPointer<Expression>> arguments = parseFunctionCallArguments(); vector<ASTPointer<Expression>> arguments;
vector<ASTPointer<ASTString>> names;
std::tie(arguments, names) = parseFunctionCallArguments();
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RPAREN); expectToken(Token::RPAREN);
expression = nodeFactory.createNode<FunctionCall>(expression, arguments); expression = nodeFactory.createNode<FunctionCall>(expression, arguments, names);
} }
break; break;
default: default:
@ -704,7 +723,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
return expression; return expression;
} }
vector<ASTPointer<Expression>> Parser::parseFunctionCallArguments() vector<ASTPointer<Expression>> Parser::parseFunctionCallListArguments()
{ {
vector<ASTPointer<Expression>> arguments; vector<ASTPointer<Expression>> arguments;
if (m_scanner->getCurrentToken() != Token::RPAREN) if (m_scanner->getCurrentToken() != Token::RPAREN)
@ -719,6 +738,32 @@ vector<ASTPointer<Expression>> Parser::parseFunctionCallArguments()
return arguments; return arguments;
} }
pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::parseFunctionCallArguments()
{
pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> ret;
Token::Value token = m_scanner->getCurrentToken();
if (token == Token::LBRACE)
{
// call({arg1 : 1, arg2 : 2 })
expectToken(Token::LBRACE);
while (m_scanner->getCurrentToken() != Token::RBRACE)
{
ret.second.push_back(expectIdentifierToken());
expectToken(Token::COLON);
ret.first.push_back(parseExpression());
if (m_scanner->getCurrentToken() == Token::COMMA)
expectToken(Token::COMMA);
else
break;
}
expectToken(Token::RBRACE);
}
else
ret.first = parseFunctionCallListArguments();
return ret;
}
bool Parser::peekVariableDefinition() bool Parser::peekVariableDefinition()
{ {

7
libsolidity/Parser.h

@ -48,7 +48,6 @@ private:
struct VarDeclParserOptions { struct VarDeclParserOptions {
VarDeclParserOptions() {} VarDeclParserOptions() {}
bool allowVar = false; bool allowVar = false;
bool isPublic = false;
bool isStateVariable = false; bool isStateVariable = false;
bool allowIndexed = false; bool allowIndexed = false;
}; };
@ -58,7 +57,8 @@ private:
ASTPointer<ImportDirective> parseImportDirective(); ASTPointer<ImportDirective> parseImportDirective();
ASTPointer<ContractDefinition> parseContractDefinition(); ASTPointer<ContractDefinition> parseContractDefinition();
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier(); ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic, ASTString const* _contractName); Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
ASTPointer<StructDefinition> parseStructDefinition(); ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions()); ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions());
ASTPointer<ModifierDefinition> parseModifierDefinition(); ASTPointer<ModifierDefinition> parseModifierDefinition();
@ -81,7 +81,8 @@ private:
ASTPointer<Expression> parseUnaryExpression(); ASTPointer<Expression> parseUnaryExpression();
ASTPointer<Expression> parseLeftHandSideExpression(); ASTPointer<Expression> parseLeftHandSideExpression();
ASTPointer<Expression> parsePrimaryExpression(); ASTPointer<Expression> parsePrimaryExpression();
std::vector<ASTPointer<Expression>> parseFunctionCallArguments(); std::vector<ASTPointer<Expression>> parseFunctionCallListArguments();
std::pair<std::vector<ASTPointer<Expression>>, std::vector<ASTPointer<ASTString>>> parseFunctionCallArguments();
///@} ///@}
///@{ ///@{

2
libsolidity/Token.h

@ -165,6 +165,7 @@ namespace solidity
K(NEW, "new", 0) \ K(NEW, "new", 0) \
K(PUBLIC, "public", 0) \ K(PUBLIC, "public", 0) \
K(PRIVATE, "private", 0) \ K(PRIVATE, "private", 0) \
K(PROTECTED, "protected", 0) \
K(RETURN, "return", 0) \ K(RETURN, "return", 0) \
K(RETURNS, "returns", 0) \ K(RETURNS, "returns", 0) \
K(STRUCT, "struct", 0) \ K(STRUCT, "struct", 0) \
@ -376,6 +377,7 @@ public:
static bool isUnaryOp(Value op) { return (NOT <= op && op <= DELETE) || op == ADD || op == SUB; } static bool isUnaryOp(Value op) { return (NOT <= op && op <= DELETE) || op == ADD || op == SUB; }
static bool isCountOp(Value op) { return op == INC || op == DEC; } static bool isCountOp(Value op) { return op == INC || op == DEC; }
static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); } static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); }
static bool isVisibilitySpecifier(Value op) { return op == PUBLIC || op == PRIVATE || op == PROTECTED; }
// Returns a string corresponding to the JS token string // Returns a string corresponding to the JS token string
// (.e., "<" for the token LT) or NULL if the token doesn't // (.e., "<" for the token LT) or NULL if the token doesn't

23
libsolidity/Types.cpp

@ -621,11 +621,24 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
FunctionType::FunctionType(VariableDeclaration const& _varDecl): FunctionType::FunctionType(VariableDeclaration const& _varDecl):
m_location(Location::EXTERNAL), m_isConstant(true), m_declaration(&_varDecl) m_location(Location::EXTERNAL), m_isConstant(true), m_declaration(&_varDecl)
{ {
TypePointers params({}); TypePointers params;
vector<string> paramNames({}); vector<string> paramNames;
TypePointers retParams({_varDecl.getType()}); TypePointers retParams;
vector<string> retParamNames({ _varDecl.getName()}); vector<string> retParamNames;
// for now, no input parameters LTODO: change for some things like mapping TypePointer varDeclType = _varDecl.getType();
auto mappingType = dynamic_cast<MappingType const*>(varDeclType.get());
auto returnType = varDeclType;
while (mappingType != nullptr)
{
params.push_back(mappingType->getKeyType());
paramNames.push_back("");
returnType = mappingType->getValueType();
mappingType = dynamic_cast<MappingType const*>(mappingType->getValueType().get());
}
retParams.push_back(returnType);
retParamNames.push_back("");
swap(params, m_parameterTypes); swap(params, m_parameterTypes);
swap(paramNames, m_parameterNames); swap(paramNames, m_parameterNames);

7
libsolidity/grammar.txt

@ -1,14 +1,15 @@
ContractDefinition = 'contract' Identifier ContractDefinition = 'contract' Identifier
( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )? ( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )?
'{' ContractPart* '}' '{' ContractPart* '}'
ContractPart = VariableDeclaration ';' | StructDefinition | ModifierDefinition | ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition
FunctionDefinition | 'public:' | 'private:'
InheritanceSpecifier = Identifier ( '(' Expression ( ',' Expression )* ')' )? InheritanceSpecifier = Identifier ( '(' Expression ( ',' Expression )* ')' )?
StructDefinition = 'struct' Identifier '{' StructDefinition = 'struct' Identifier '{'
( VariableDeclaration (';' VariableDeclaration)* )? '} ( VariableDeclaration (';' VariableDeclaration)* )? '}
StateVariableDeclaration = TypeName ( 'public' | 'protected' | 'private' )? Identifier ';'
ModifierDefinition = 'modifier' Identifier ParameterList? Block ModifierDefinition = 'modifier' Identifier ParameterList? Block
FunctionDefinition = 'function' Identifier ParameterList ( Identifier | 'constant' )* FunctionDefinition = 'function' Identifier ParameterList
( Identifier | 'constant' | 'public' | 'protected' | 'private' )*
( 'returns' ParameterList )? Block ( 'returns' ParameterList )? Block
ParameterList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')' ParameterList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')'
// semantic restriction: mappings and structs (recursively) containing mappings // semantic restriction: mappings and structs (recursively) containing mappings

13
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -28,10 +28,12 @@
#include <liblll/Compiler.h> #include <liblll/Compiler.h>
#include <libethereum/Client.h> #include <libethereum/Client.h>
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
#include <libdevcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <libwhisper/Message.h> #include <libwhisper/Message.h>
#include <libwhisper/WhisperHost.h> #include <libwhisper/WhisperHost.h>
#ifndef _MSC_VER
#include <libserpent/funcs.h> #include <libserpent/funcs.h>
#endif
#include "WebThreeStubServerBase.h" #include "WebThreeStubServerBase.h"
using namespace std; using namespace std;
@ -342,7 +344,10 @@ std::string WebThreeStubServerBase::eth_call(Json::Value const& _json)
Json::Value WebThreeStubServerBase::eth_changed(int const& _id) Json::Value WebThreeStubServerBase::eth_changed(int const& _id)
{ {
return toJson(client()->checkWatch(_id)); auto entries = client()->checkWatch(_id);
if (entries.size())
cnote << "FIRING WATCH" << _id << entries.size();
return toJson(entries);
} }
std::string WebThreeStubServerBase::eth_codeAt(string const& _address) std::string WebThreeStubServerBase::eth_codeAt(string const& _address)
@ -443,7 +448,9 @@ Json::Value WebThreeStubServerBase::eth_compilers()
Json::Value ret(Json::arrayValue); Json::Value ret(Json::arrayValue);
ret.append("lll"); ret.append("lll");
ret.append("solidity"); ret.append("solidity");
#ifndef _MSC_VER
ret.append("serpent"); ret.append("serpent");
#endif
return ret; return ret;
} }
@ -459,6 +466,7 @@ std::string WebThreeStubServerBase::eth_lll(std::string const& _code)
std::string WebThreeStubServerBase::eth_serpent(std::string const& _code) std::string WebThreeStubServerBase::eth_serpent(std::string const& _code)
{ {
string res; string res;
#ifndef _MSC_VER
try try
{ {
res = toJS(dev::asBytes(::compile(_code))); res = toJS(dev::asBytes(::compile(_code)));
@ -471,6 +479,7 @@ std::string WebThreeStubServerBase::eth_serpent(std::string const& _code)
{ {
cwarn << "Uncought serpent compilation exception"; cwarn << "Uncought serpent compilation exception";
} }
#endif
return res; return res;
} }

4
libweb3jsonrpc/WebThreeStubServerBase.h

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

45
macdeployfix.sh

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

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

Loading…
Cancel
Save