diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bab77585..94b9f07c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,11 +131,15 @@ endif() add_subdirectory(libdevcore) add_subdirectory(libevmcore) add_subdirectory(liblll) -add_subdirectory(libserpent) +if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) + add_subdirectory(libserpent) +endif () add_subdirectory(libsolidity) add_subdirectory(lllc) add_subdirectory(solc) +if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) add_subdirectory(sc) +endif() if (JSONRPC) add_subdirectory(libweb3jsonrpc) diff --git a/CodingStandards.txt b/CodingStandards.txt index 6188b9a70..78885ce25 100644 --- a/CodingStandards.txt +++ b/CodingStandards.txt @@ -93,7 +93,7 @@ b. Only one per line. c. Associate */& with type, not variable (at ends with parser, but more readable, and safe if in conjunction with (b)). d. Favour declarations close to use; don't habitually declare at top of scope ala C. e. Always pass non-trivial parameters with a const& suffix. -f. If a function returns multiple values, use std::tuple (std::pair acceptable). Prefer not using */& arguments, except where efficiency requires. +f. If a func tion returns multiple values, use std::tuple (std::pair acceptable). Prefer not using */& arguments, except where efficiency requires. g. Never use a macro where adequate non-preprocessor C++ can be written. h. Prefer "using NewType = OldType" to "typedef OldType NewType". @@ -128,7 +128,7 @@ d. Use override, final and const judiciously. e. No implementations with the class declaration, except: - template or force-inline method (though prefer implementation at bottom of header file). - one-line implementation (in which case include it in same line as declaration). -f. For a method 'foo' +f. For a property 'foo' - Member: m_foo; - Getter: foo() [ also: for booleans, isFoo() ]; - Setter: setFoo(); diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index 219a8cb99..9aa3c4ddd 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -40,7 +40,9 @@ target_link_libraries(${EXECUTABLE} evm) target_link_libraries(${EXECUTABLE} ethcore) target_link_libraries(${EXECUTABLE} devcrypto) target_link_libraries(${EXECUTABLE} secp256k1) +if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) target_link_libraries(${EXECUTABLE} serpent) +endif() target_link_libraries(${EXECUTABLE} lll) target_link_libraries(${EXECUTABLE} solidity) target_link_libraries(${EXECUTABLE} evmcore) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 1921e80cd..568550af2 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -30,10 +30,12 @@ #include #include #include +#ifndef _MSC_VER #include #include +#endif #include -#include +#include #include #include #include @@ -41,7 +43,7 @@ #include #include #include -#include +#include #include #include #include @@ -136,9 +138,9 @@ Main::Main(QWidget *parent) : #endif m_servers.append(QString::fromStdString(Host::pocHost() + ":30303")); - cerr << "State root: " << BlockChain::genesis().stateRoot << endl; - auto block = BlockChain::createGenesisBlock(); - cerr << "Block Hash: " << BlockChain::genesis().hash << endl; + cerr << "State root: " << CanonBlockChain::genesis().stateRoot << endl; + auto block = CanonBlockChain::createGenesisBlock(); + cerr << "Block Hash: " << CanonBlockChain::genesis().hash << endl; cerr << "Block RLP: " << RLP(block) << endl; cerr << "Block Hex: " << toHex(block) << endl; cerr << "Network protocol version: " << c_protocolVersion << endl; @@ -1047,7 +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")); } -static bool blockMatch(string const& _f, BlockDetails const& _b, h256 _h, BlockChain const& _bc) +static bool blockMatch(string const& _f, BlockDetails const& _b, h256 _h, CanonBlockChain const& _bc) { try { @@ -1189,7 +1191,10 @@ void Main::timerEvent(QTimerEvent*) { auto ls = ethereum()->checkWatch(i.first); if (ls.size()) + { + cnote << "FIRING WATCH" << i.first << ls.size(); i.second(ls); + } } } @@ -1202,7 +1207,7 @@ string Main::renderDiff(StateDiff const& _d) const { s << "
"; - AccountDiff const& ad = i.second; + AccountDiff ad = i.second; s << "" << lead(ad.changeType()) << " " << " " << render(i.first).toStdString() << ""; if (!ad.exist.to()) continue; @@ -1210,7 +1215,7 @@ string Main::renderDiff(StateDiff const& _d) const if (ad.balance) { s << "
" << indent << "Balance " << dec << ad.balance.to() << " [=" << formatBalance(ad.balance.to()) << "]"; - auto d = (((dev::bigint)ad.balance.to()) - ((dev::bigint)ad.balance.from())); + bigint d = (dev::bigint)ad.balance.to() - (dev::bigint)ad.balance.from(); s << " " << showpos << dec << d << " [=" << formatBalance(d) << "]" << noshowpos << ""; } if (ad.nonce) @@ -1716,6 +1721,7 @@ void Main::on_data_textChanged() solidity = "

Solidity

Uncaught exception.
"; } } +#ifndef _MSC_VER else if (sourceIsSerpent(src)) { try @@ -1729,6 +1735,7 @@ void Main::on_data_textChanged() errors.push_back("Serpent " + err); } } +#endif else { m_data = compileLLL(src, m_enableOptimizer, &errors); diff --git a/alethzero/OurWebThreeStubServer.h b/alethzero/OurWebThreeStubServer.h index 5223d79bd..16981f9e1 100644 --- a/alethzero/OurWebThreeStubServer.h +++ b/alethzero/OurWebThreeStubServer.h @@ -20,7 +20,7 @@ */ #include -#include +#include #include #include @@ -35,7 +35,7 @@ public: std::vector const& _accounts, Main* main); virtual std::string shh_newIdentity() override; - virtual bool authenticate(dev::TransactionSkeleton const& _t); + virtual bool authenticate(dev::eth::TransactionSkeleton const& _t); signals: void onNewId(QString _s); diff --git a/build.py b/build.py deleted file mode 100755 index 099a8721d..000000000 --- a/build.py +++ /dev/null @@ -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() - diff --git a/cmake/EthExecutableHelper.cmake b/cmake/EthExecutableHelper.cmake index 88e2a3e16..427e0a9c4 100644 --- a/cmake/EthExecutableHelper.cmake +++ b/cmake/EthExecutableHelper.cmake @@ -71,8 +71,9 @@ macro(eth_install_executable EXECUTABLE) if (APPLE) # First have qt5 install plugins and frameworks add_custom_command(TARGET ${EXECUTABLE} POST_BUILD - COMMAND ${MACDEPLOYQT_APP} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app ${eth_qml_dir} + COMMAND ${MACDEPLOYQT_APP} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app -executable=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app/Contents/MacOS/${EXECUTABLE} ${eth_qml_dir} WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + COMMAND sh ${CMAKE_SOURCE_DIR}/macdeployfix.sh ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app/Contents ) # This tool and next will inspect linked libraries in order to determine which dependencies are required @@ -82,19 +83,11 @@ macro(eth_install_executable EXECUTABLE) set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/\$ENV{CONFIGURATION}/${EXECUTABLE}.app") endif () - # TODO check, how fixup_bundle works and if it is required install(CODE " include(BundleUtilities) set(BU_CHMOD_BUNDLE_ITEMS 1) - fixup_bundle(\"${APP_BUNDLE_PATH}\" \"${BUNDLELIBS}\" \"../libqethereum ../libethereum ../secp256k1\") + verify_app(\"${APP_BUNDLE_PATH}\") " COMPONENT RUNTIME ) - # Cleanup duplicate libs from macdeployqt - install(CODE " - file(GLOB LINGER_RM \"${APP_BUNDLE_PATH}/Contents/Frameworks/*.dylib\") - if (LINGER_RM) - file(REMOVE \${LINGER_RM}) - endif () - ") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # copy all dlls to executable directory diff --git a/evmjit/CMakeLists.txt b/evmjit/CMakeLists.txt index 519346007..a449f9a60 100644 --- a/evmjit/CMakeLists.txt +++ b/evmjit/CMakeLists.txt @@ -4,7 +4,7 @@ project(evmjit) set_property(GLOBAL PROPERTY USE_FOLDERS ON) -if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") else() set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -fPIC") endif() @@ -32,9 +32,6 @@ else() link_directories(/usr/lib/llvm-3.5/lib) endif() -# Boost -find_package(Boost REQUIRED) - add_subdirectory(libevmjit) if(EVMJIT_CPP) diff --git a/evmjit/libevmjit-cpp/CMakeLists.txt b/evmjit/libevmjit-cpp/CMakeLists.txt index 25be95177..58375e4ee 100644 --- a/evmjit/libevmjit-cpp/CMakeLists.txt +++ b/evmjit/libevmjit-cpp/CMakeLists.txt @@ -1,5 +1,8 @@ set(TARGET_NAME evmjit-cpp) +# Boost +find_package(Boost REQUIRED) + set(SOURCES Env.cpp JitVM.cpp JitVM.h diff --git a/evmjit/libevmjit-cpp/Env.cpp b/evmjit/libevmjit-cpp/Env.cpp index 90474c7e4..f89897792 100644 --- a/evmjit/libevmjit-cpp/Env.cpp +++ b/evmjit/libevmjit-cpp/Env.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include "Utils.h" extern "C" { @@ -16,7 +16,6 @@ extern "C" using namespace dev; using namespace dev::eth; using jit::i256; - using jit::eth2llvm; EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value) { diff --git a/evmjit/libevmjit-cpp/JitVM.cpp b/evmjit/libevmjit-cpp/JitVM.cpp index dda8133a8..413525e5a 100644 --- a/evmjit/libevmjit-cpp/JitVM.cpp +++ b/evmjit/libevmjit-cpp/JitVM.cpp @@ -2,39 +2,54 @@ #include "JitVM.h" #include #include -#include +#include "Utils.h" namespace dev { namespace eth { +extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below + bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t) { using namespace jit; - m_data.set(RuntimeData::Gas, m_gas); - m_data.set(RuntimeData::Address, fromAddress(_ext.myAddress)); - m_data.set(RuntimeData::Caller, fromAddress(_ext.caller)); - m_data.set(RuntimeData::Origin, fromAddress(_ext.origin)); - m_data.set(RuntimeData::CallValue, _ext.value); - m_data.set(RuntimeData::CallDataSize, _ext.data.size()); - m_data.set(RuntimeData::GasPrice, _ext.gasPrice); - m_data.set(RuntimeData::CoinBase, fromAddress(_ext.currentBlock.coinbaseAddress)); - m_data.set(RuntimeData::TimeStamp, _ext.currentBlock.timestamp); - m_data.set(RuntimeData::Number, _ext.currentBlock.number); - m_data.set(RuntimeData::Difficulty, _ext.currentBlock.difficulty); - m_data.set(RuntimeData::GasLimit, _ext.currentBlock.gasLimit); - m_data.set(RuntimeData::CodeSize, _ext.code.size()); - m_data.callData = _ext.data.data(); - m_data.code = _ext.code.data(); + if (m_gas > std::numeric_limits::max()) + BOOST_THROW_EXCEPTION(OutOfGas()); // Do not accept requests with gas > 2^63 (int64 max) // TODO: Return "not accepted" exception to allow interpreted handle that + + if (_ext.gasPrice > std::numeric_limits::max()) + BOOST_THROW_EXCEPTION(OutOfGas()); + + if (_ext.currentBlock.number > std::numeric_limits::max()) + BOOST_THROW_EXCEPTION(OutOfGas()); + + if (_ext.currentBlock.timestamp > std::numeric_limits::max()) + BOOST_THROW_EXCEPTION(OutOfGas()); + + + m_data.gas = static_cast(m_gas); + m_data.gasPrice = static_cast(_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(_ext.currentBlock.number); + m_data.timestamp = static_cast(_ext.currentBlock.timestamp); + m_data.code = _ext.code.data(); + m_data.codeSize = _ext.code.size(); auto env = reinterpret_cast(&_ext); auto exitCode = m_engine.run(_ext.code, &m_data, env); switch (exitCode) { case ReturnCode::Suicide: - _ext.suicide(right160(m_data.get(RuntimeData::SuicideDestAddress))); + _ext.suicide(right160(llvm2eth(m_data.address))); break; case ReturnCode::BadJumpDestination: @@ -45,24 +60,16 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t) BOOST_THROW_EXCEPTION(StackTooSmall()); case ReturnCode::BadInstruction: BOOST_THROW_EXCEPTION(BadInstruction()); + case ReturnCode::LinkerWorkaround: // never happens + env_sload(); // but forces linker to include env_* JIT callback functions + break; default: break; } - m_gas = llvm2eth(m_data.elems[RuntimeData::Gas]); - return {m_engine.returnData.data(), m_engine.returnData.size()}; // TODO: This all bytesConstRef is problematic, review. + m_gas = m_data.gas; // TODO: Remove m_gas field + return {std::get<0>(m_engine.returnData), std::get<1>(m_engine.returnData)}; } } } - -namespace -{ - // MSVS linker ignores export symbols in Env.cpp if nothing points at least one of them - extern "C" void env_sload(); - void linkerWorkaround() - { - env_sload(); - (void)&linkerWorkaround; // suppress unused function warning from GCC - } -} diff --git a/evmjit/libevmjit-cpp/Utils.h b/evmjit/libevmjit-cpp/Utils.h new file mode 100644 index 000000000..ac796920b --- /dev/null +++ b/evmjit/libevmjit-cpp/Utils.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +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(_u & mask); + _u >>= 64; + i.b = static_cast(_u & mask); + _u >>= 64; + i.c = static_cast(_u & mask); + _u >>= 64; + i.d = static_cast(_u & mask); + return i; +} + +} +} diff --git a/evmjit/libevmjit/Arith256.cpp b/evmjit/libevmjit/Arith256.cpp index b5319bda7..2ec227dcc 100644 --- a/evmjit/libevmjit/Arith256.cpp +++ b/evmjit/libevmjit/Arith256.cpp @@ -1,8 +1,11 @@ #include "Arith256.h" #include "Runtime.h" #include "Type.h" +#include "Endianness.h" #include +#include +#include namespace dev { @@ -24,35 +27,257 @@ Arith256::Arith256(llvm::IRBuilder<>& _builder) : using Linkage = GlobalValue::LinkageTypes; llvm::Type* arg2Types[] = {Type::WordPtr, Type::WordPtr, Type::WordPtr}; - llvm::Type* arg3Types[] = {Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr}; m_mul = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_mul", getModule()); - m_div = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_div", getModule()); - m_mod = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_mod", getModule()); - m_sdiv = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_sdiv", getModule()); - m_smod = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_smod", getModule()); - m_exp = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_exp", getModule()); - m_addmod = Function::Create(FunctionType::get(Type::Void, arg3Types, false), Linkage::ExternalLinkage, "arith_addmod", getModule()); - m_mulmod = Function::Create(FunctionType::get(Type::Void, arg3Types, false), Linkage::ExternalLinkage, "arith_mulmod", getModule()); } -Arith256::~Arith256() -{} +void Arith256::debug(llvm::Value* _value, char _c) +{ + if (!m_debug) + { + llvm::Type* argTypes[] = {Type::Word, m_builder.getInt8Ty()}; + m_debug = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "debug", getModule()); + } + createCall(m_debug, {_value, m_builder.getInt8(_c)}); +} -llvm::Value* Arith256::binaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2) +llvm::Function* Arith256::getDivFunc(llvm::Type* _type) { - m_builder.CreateStore(_arg1, m_arg1); - m_builder.CreateStore(_arg2, m_arg2); - m_builder.CreateCall3(_op, m_arg1, m_arg2, m_result); - return m_builder.CreateLoad(m_result); + auto& func = _type == Type::Word ? m_div : m_div512; + + if (!func) + { + // Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research + // The following algorithm also handles divisor of value 0 returning 0 for both quotient and reminder + + llvm::Type* argTypes[] = {_type, _type}; + auto retType = llvm::StructType::get(m_builder.getContext(), llvm::ArrayRef{argTypes}); + auto funcName = _type == Type::Word ? "div" : "div512"; + func = llvm::Function::Create(llvm::FunctionType::get(retType, argTypes, false), llvm::Function::PrivateLinkage, funcName, getModule()); + + auto zero = llvm::ConstantInt::get(_type, 0); + auto one = llvm::ConstantInt::get(_type, 1); + + auto x = &func->getArgumentList().front(); + x->setName("x"); + auto yArg = x->getNextNode(); + yArg->setName("y"); + + InsertPointGuard guard{m_builder}; + + auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", func); + auto mainBB = llvm::BasicBlock::Create(m_builder.getContext(), "Main", func); + auto loopBB = llvm::BasicBlock::Create(m_builder.getContext(), "Loop", func); + auto continueBB = llvm::BasicBlock::Create(m_builder.getContext(), "Continue", func); + auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func); + + m_builder.SetInsertPoint(entryBB); + auto yNonZero = m_builder.CreateICmpNE(yArg, zero); + auto yLEx = m_builder.CreateICmpULE(yArg, x); + auto r0 = m_builder.CreateSelect(yNonZero, x, zero, "r0"); + m_builder.CreateCondBr(m_builder.CreateAnd(yLEx, yNonZero), mainBB, returnBB); + + m_builder.SetInsertPoint(mainBB); + auto ctlzIntr = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, _type); + // both y and r are non-zero + auto yLz = m_builder.CreateCall2(ctlzIntr, yArg, m_builder.getInt1(true), "y.lz"); + auto rLz = m_builder.CreateCall2(ctlzIntr, r0, m_builder.getInt1(true), "r.lz"); + auto i0 = m_builder.CreateNUWSub(yLz, rLz, "i0"); + auto shlBy0 = m_builder.CreateICmpEQ(i0, zero); + auto y0 = m_builder.CreateShl(yArg, i0); + y0 = m_builder.CreateSelect(shlBy0, yArg, y0, "y0"); // Workaround for LLVM bug: shl by 0 produces wrong result + m_builder.CreateBr(loopBB); + + m_builder.SetInsertPoint(loopBB); + auto yPhi = m_builder.CreatePHI(_type, 2, "y.phi"); + auto rPhi = m_builder.CreatePHI(_type, 2, "r.phi"); + auto iPhi = m_builder.CreatePHI(_type, 2, "i.phi"); + auto qPhi = m_builder.CreatePHI(_type, 2, "q.phi"); + auto rUpdate = m_builder.CreateNUWSub(rPhi, yPhi); + auto qUpdate = m_builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0 + auto rGEy = m_builder.CreateICmpUGE(rPhi, yPhi); + auto r1 = m_builder.CreateSelect(rGEy, rUpdate, rPhi, "r1"); + auto q1 = m_builder.CreateSelect(rGEy, qUpdate, qPhi, "q"); + auto iZero = m_builder.CreateICmpEQ(iPhi, zero); + m_builder.CreateCondBr(iZero, returnBB, continueBB); + + m_builder.SetInsertPoint(continueBB); + auto i2 = m_builder.CreateNUWSub(iPhi, one); + auto q2 = m_builder.CreateShl(q1, one); + auto y2 = m_builder.CreateLShr(yPhi, one); + m_builder.CreateBr(loopBB); + + yPhi->addIncoming(y0, mainBB); + yPhi->addIncoming(y2, continueBB); + rPhi->addIncoming(r0, mainBB); + rPhi->addIncoming(r1, continueBB); + iPhi->addIncoming(i0, mainBB); + iPhi->addIncoming(i2, continueBB); + qPhi->addIncoming(zero, mainBB); + qPhi->addIncoming(q2, continueBB); + + m_builder.SetInsertPoint(returnBB); + auto qRet = m_builder.CreatePHI(_type, 2, "q.ret"); + qRet->addIncoming(zero, entryBB); + qRet->addIncoming(q1, loopBB); + auto rRet = m_builder.CreatePHI(_type, 2, "r.ret"); + rRet->addIncoming(r0, entryBB); + rRet->addIncoming(r1, loopBB); + auto ret = m_builder.CreateInsertValue(llvm::UndefValue::get(retType), qRet, 0, "ret0"); + ret = m_builder.CreateInsertValue(ret, rRet, 1, "ret"); + m_builder.CreateRet(ret); + } + return func; } -llvm::Value* Arith256::ternaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3) +llvm::Function* Arith256::getExpFunc() +{ + if (!m_exp) + { + llvm::Type* argTypes[] = {Type::Word, Type::Word}; + m_exp = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "arith.exp", getModule()); + + auto base = &m_exp->getArgumentList().front(); + base->setName("base"); + auto exponent = base->getNextNode(); + exponent->setName("exponent"); + + InsertPointGuard guard{m_builder}; + + // while (e != 0) { + // if (e % 2 == 1) + // r *= b; + // b *= b; + // e /= 2; + // } + + auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", m_exp); + auto headerBB = llvm::BasicBlock::Create(m_builder.getContext(), "LoopHeader", m_exp); + auto bodyBB = llvm::BasicBlock::Create(m_builder.getContext(), "LoopBody", m_exp); + auto updateBB = llvm::BasicBlock::Create(m_builder.getContext(), "ResultUpdate", m_exp); + auto continueBB = llvm::BasicBlock::Create(m_builder.getContext(), "Continue", m_exp); + auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", m_exp); + + m_builder.SetInsertPoint(entryBB); + auto a1 = m_builder.CreateAlloca(Type::Word, nullptr, "a1"); + auto a2 = m_builder.CreateAlloca(Type::Word, nullptr, "a2"); + auto a3 = m_builder.CreateAlloca(Type::Word, nullptr, "a3"); + m_builder.CreateBr(headerBB); + + m_builder.SetInsertPoint(headerBB); + auto r = m_builder.CreatePHI(Type::Word, 2, "r"); + auto b = m_builder.CreatePHI(Type::Word, 2, "b"); + auto e = m_builder.CreatePHI(Type::Word, 2, "e"); + auto eNonZero = m_builder.CreateICmpNE(e, Constant::get(0), "e.nonzero"); + m_builder.CreateCondBr(eNonZero, bodyBB, returnBB); + + m_builder.SetInsertPoint(bodyBB); + auto eOdd = m_builder.CreateICmpNE(m_builder.CreateAnd(e, Constant::get(1)), Constant::get(0), "e.isodd"); + m_builder.CreateCondBr(eOdd, updateBB, continueBB); + + m_builder.SetInsertPoint(updateBB); + m_builder.CreateStore(r, a1); + m_builder.CreateStore(b, a2); + createCall(m_mul, {a1, a2, a3}); + auto r0 = m_builder.CreateLoad(a3, "r0"); + m_builder.CreateBr(continueBB); + + m_builder.SetInsertPoint(continueBB); + auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1"); + r1->addIncoming(r, bodyBB); + r1->addIncoming(r0, updateBB); + m_builder.CreateStore(b, a1); + m_builder.CreateStore(b, a2); + createCall(m_mul, {a1, a2, a3}); + auto b1 = m_builder.CreateLoad(a3, "b1"); + auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1"); + m_builder.CreateBr(headerBB); + + r->addIncoming(Constant::get(1), entryBB); + r->addIncoming(r1, continueBB); + b->addIncoming(base, entryBB); + b->addIncoming(b1, continueBB); + e->addIncoming(exponent, entryBB); + e->addIncoming(e1, continueBB); + + m_builder.SetInsertPoint(returnBB); + m_builder.CreateRet(r); + } + return m_exp; +} + +llvm::Function* Arith256::getAddModFunc() +{ + if (!m_addmod) + { + auto i512Ty = m_builder.getIntNTy(512); + llvm::Type* argTypes[] = {Type::Word, Type::Word, Type::Word}; + m_addmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "addmod", getModule()); + + auto x = &m_addmod->getArgumentList().front(); + x->setName("x"); + auto y = x->getNextNode(); + y->setName("y"); + auto mod = y->getNextNode(); + mod->setName("m"); + + InsertPointGuard guard{m_builder}; + + auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_addmod); + m_builder.SetInsertPoint(entryBB); + auto x512 = m_builder.CreateZExt(x, i512Ty, "x512"); + auto y512 = m_builder.CreateZExt(y, i512Ty, "y512"); + auto m512 = m_builder.CreateZExt(mod, i512Ty, "m512"); + auto s = m_builder.CreateAdd(x512, y512, "s"); + auto d = createCall(getDivFunc(i512Ty), {s, m512}); + auto r = m_builder.CreateExtractValue(d, 1, "r"); + m_builder.CreateRet(m_builder.CreateTrunc(r, Type::Word)); + } + return m_addmod; +} + +llvm::Function* Arith256::getMulModFunc() +{ + if (!m_mulmod) + { + llvm::Type* argTypes[] = {Type::Word, Type::Word, Type::Word}; + m_mulmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mulmod", getModule()); + + auto i512Ty = m_builder.getIntNTy(512); + llvm::Type* mul512ArgTypes[] = {Type::WordPtr, Type::WordPtr, i512Ty->getPointerTo()}; + auto mul512 = llvm::Function::Create(llvm::FunctionType::get(Type::Void, mul512ArgTypes, false), llvm::Function::ExternalLinkage, "arith_mul512", getModule()); + + auto x = &m_mulmod->getArgumentList().front(); + x->setName("x"); + auto y = x->getNextNode(); + y->setName("y"); + auto mod = y->getNextNode(); + mod->setName("mod"); + + InsertPointGuard guard{m_builder}; + + auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mulmod); + m_builder.SetInsertPoint(entryBB); + auto a1 = m_builder.CreateAlloca(Type::Word); + auto a2 = m_builder.CreateAlloca(Type::Word); + auto a3 = m_builder.CreateAlloca(i512Ty); + m_builder.CreateStore(x, a1); + m_builder.CreateStore(y, a2); + createCall(mul512, {a1, a2, a3}); + auto p = m_builder.CreateLoad(a3, "p"); + auto m = m_builder.CreateZExt(mod, i512Ty, "m"); + auto d = createCall(getDivFunc(i512Ty), {p, m}); + auto r = m_builder.CreateExtractValue(d, 1, "r"); + m_builder.CreateRet(m_builder.CreateTrunc(r, Type::Word)); + } + return m_mulmod; +} + +llvm::Value* Arith256::binaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2) { m_builder.CreateStore(_arg1, m_arg1); m_builder.CreateStore(_arg2, m_arg2); - m_builder.CreateStore(_arg3, m_arg3); - m_builder.CreateCall4(_op, m_arg1, m_arg2, m_arg3, m_result); + m_builder.CreateCall3(_op, m_arg1, m_arg2, m_result); return m_builder.CreateLoad(m_result); } @@ -61,62 +286,200 @@ llvm::Value* Arith256::mul(llvm::Value* _arg1, llvm::Value* _arg2) return binaryOp(m_mul, _arg1, _arg2); } -llvm::Value* Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2) +std::pair 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 Arith256::sdiv(llvm::Value* _x, llvm::Value* _y) { - return binaryOp(m_mod, _arg1, _arg2); -} + auto xIsNeg = m_builder.CreateICmpSLT(_x, Constant::get(0)); + auto xNeg = m_builder.CreateSub(Constant::get(0), _x); + auto xAbs = m_builder.CreateSelect(xIsNeg, xNeg, _x); -llvm::Value* Arith256::sdiv(llvm::Value* _arg1, llvm::Value* _arg2) -{ - return binaryOp(m_sdiv, _arg1, _arg2); -} + auto yIsNeg = m_builder.CreateICmpSLT(_y, Constant::get(0)); + auto yNeg = m_builder.CreateSub(Constant::get(0), _y); + auto yAbs = m_builder.CreateSelect(yIsNeg, yNeg, _y); -llvm::Value* Arith256::smod(llvm::Value* _arg1, llvm::Value* _arg2) -{ - return binaryOp(m_smod, _arg1, _arg2); + auto res = div(xAbs, yAbs); + + // the reminder has the same sign as dividend + auto rNeg = m_builder.CreateSub(Constant::get(0), res.second); + res.second = m_builder.CreateSelect(xIsNeg, rNeg, res.second); + + auto qNeg = m_builder.CreateSub(Constant::get(0), res.first); + auto xyOpposite = m_builder.CreateXor(xIsNeg, yIsNeg); + res.first = m_builder.CreateSelect(xyOpposite, qNeg, res.first); + + return res; } llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2) { - return binaryOp(m_exp, _arg1, _arg2); + return createCall(getExpFunc(), {_arg1, _arg2}); } llvm::Value* Arith256::addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3) { - return ternaryOp(m_addmod, _arg1, _arg2, _arg3); + return createCall(getAddModFunc(), {_arg1, _arg2, _arg3}); } llvm::Value* Arith256::mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3) { - return ternaryOp(m_mulmod, _arg1, _arg2, _arg3); + return createCall(getMulModFunc(), {_arg1, _arg2, _arg3}); } namespace { - using s256 = boost::multiprecision::int256_t; +#ifdef __SIZEOF_INT128__ + using uint128 = __uint128_t; +#else + struct uint128 + { + uint64_t lo = 0; + uint64_t hi = 0; + + uint128(uint64_t lo) : lo(lo) {} + + uint128 operator+(uint128 a) + { + uint128 r = 0; + bool overflow = lo > std::numeric_limits::max() - a.lo; + r.lo = lo + a.lo; + r.hi = hi + a.hi + overflow; + return r; + } + + uint128 operator>>(int s) + { + assert(s == 64); + return hi; + } + + uint128 operator<<(int s) + { + assert(s == 64); + uint128 r = 0; + r.hi = lo; + return r; + } + + explicit operator uint64_t() { return lo; } + + static uint128 mul(uint64_t a, uint64_t b) + { + auto x_lo = 0xFFFFFFFF & a; + auto y_lo = 0xFFFFFFFF & b; + auto x_hi = a >> 32; + auto y_hi = b >> 32; + + auto t1 = x_lo * y_lo; + auto t2 = x_lo * y_hi; + auto t3 = x_hi * y_lo; + auto t4 = x_hi * y_hi; + + auto lo = (uint32_t)t1; + auto mid = (uint64_t)(t1 >> 32) + (uint32_t)t2 + (uint32_t)t3; + auto hi = (uint64_t)(t2 >> 32) + (t3 >> 32) + t4 + (mid >> 32); + + uint128 r = 0; + r.lo = (uint64_t)lo + (mid << 32); + r.hi = hi; + return r; + } + + uint128 operator*(uint128 a) + { + auto t1 = mul(lo, a.lo); + auto t2 = mul(lo, a.hi); + auto t3 = mul(hi, a.lo); + return t1 + (t2 << 64) + (t3 << 64); + } + }; +#endif + + struct uint256 + { + uint64_t lo = 0; + uint64_t mid = 0; + uint128 hi = 0; + + uint256(uint64_t lo, uint64_t mid, uint128 hi): lo(lo), mid(mid), hi(hi) {} + uint256(uint128 n) + { + lo = (uint64_t) n; + mid = (uint64_t) (n >> 64); + } + + explicit operator uint128() + { + uint128 r = lo; + r |= ((uint128) mid) << 64; + return r; + } + + uint256 operator+(uint256 a) + { + auto _lo = (uint128) lo + a.lo; + auto _mid = (uint128) mid + a.mid + (_lo >> 64); + auto _hi = hi + a.hi + (_mid >> 64); + return {(uint64_t)_lo, (uint64_t)_mid, _hi}; + } + + uint256 lo2hi() + { + hi = (uint128)*this; + lo = 0; + mid = 0; + return *this; + } + }; + + struct uint512 + { + uint128 lo; + uint128 mid; + uint256 hi; + }; - inline s256 u2s(u256 _u) + uint256 mul(uint256 x, uint256 y) { - static const bigint c_end = (bigint)1 << 256; - static const u256 c_send = (u256)1 << 255; - if (_u < c_send) - return (s256)_u; - else - return (s256)-(c_end - _u); + auto t1 = (uint128) x.lo * y.lo; + auto t2 = (uint128) x.lo * y.mid; + auto t3 = (uint128) x.lo * y.hi; + auto t4 = (uint128) x.mid * y.lo; + auto t5 = (uint128) x.mid * y.mid; + auto t6 = (uint128) x.mid * y.hi; + auto t7 = x.hi * y.lo; + auto t8 = x.hi * y.mid; + + auto lo = (uint64_t) t1; + auto m1 = (t1 >> 64) + (uint64_t) t2; + auto m2 = (uint64_t) m1; + auto mid = (uint128) m2 + (uint64_t) t4; + auto hi = (t2 >> 64) + t3 + (t4 >> 64) + t5 + (t6 << 64) + t7 + + (t8 << 64) + (m1 >> 64) + (mid >> 64); + + return {lo, (uint64_t)mid, hi}; } - inline u256 s2u(s256 _u) + uint512 mul512(uint256 x, uint256 y) { - static const bigint c_end = (bigint)1 << 256; - if (_u >= 0) - return (u256)_u; - else - return (u256)(c_end + _u); + auto x_lo = (uint128) x; + auto y_lo = (uint128) y; + + auto t1 = mul(x_lo, y_lo); + auto t2 = mul(x_lo, y.hi); + auto t3 = mul(x.hi, y_lo); + auto t4 = mul(x.hi, y.hi); + + auto lo = (uint128) t1; + auto mid = (uint256) t1.hi + (uint128) t2 + (uint128) t3; + auto hi = (uint256)t2.hi + t3.hi + t4 + mid.hi; + + return {lo, (uint128)mid, hi}; } } @@ -124,77 +487,22 @@ namespace } } - extern "C" { - using namespace dev::eth::jit; - EXPORT void arith_mul(i256* _arg1, i256* _arg2, i256* o_result) - { - auto arg1 = llvm2eth(*_arg1); - auto arg2 = llvm2eth(*_arg2); - *o_result = eth2llvm(arg1 * arg2); - } - - EXPORT void arith_div(i256* _arg1, i256* _arg2, i256* o_result) + EXPORT void debug(uint64_t a, uint64_t b, uint64_t c, uint64_t d, char z) { - auto arg1 = llvm2eth(*_arg1); - auto arg2 = llvm2eth(*_arg2); - *o_result = eth2llvm(arg2 == 0 ? arg2 : arg1 / arg2); + std::cerr << "DEBUG " << z << ": " << d << c << b << a << std::endl; } - EXPORT void arith_mod(i256* _arg1, i256* _arg2, i256* o_result) + EXPORT void arith_mul(uint256* _arg1, uint256* _arg2, uint256* o_result) { - auto arg1 = llvm2eth(*_arg1); - auto arg2 = llvm2eth(*_arg2); - *o_result = eth2llvm(arg2 == 0 ? arg2 : arg1 % arg2); + *o_result = mul(*_arg1, *_arg2); } - EXPORT void arith_sdiv(i256* _arg1, i256* _arg2, i256* o_result) + EXPORT void arith_mul512(uint256* _arg1, uint256* _arg2, uint512* o_result) { - auto arg1 = llvm2eth(*_arg1); - auto arg2 = llvm2eth(*_arg2); - *o_result = eth2llvm(arg2 == 0 ? arg2 : s2u(u2s(arg1) / u2s(arg2))); + *o_result = mul512(*_arg1, *_arg2); } - - EXPORT void arith_smod(i256* _arg1, i256* _arg2, i256* o_result) - { - auto arg1 = llvm2eth(*_arg1); - auto arg2 = llvm2eth(*_arg2); - *o_result = eth2llvm(arg2 == 0 ? arg2 : s2u(u2s(arg1) % u2s(arg2))); - } - - EXPORT void arith_exp(i256* _arg1, i256* _arg2, i256* o_result) - { - bigint left = llvm2eth(*_arg1); - bigint right = llvm2eth(*_arg2); - auto ret = static_cast(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 = {}; - } - } - - diff --git a/evmjit/libevmjit/Arith256.h b/evmjit/libevmjit/Arith256.h index 57bc061de..5852137f8 100644 --- a/evmjit/libevmjit/Arith256.h +++ b/evmjit/libevmjit/Arith256.h @@ -13,29 +13,32 @@ class Arith256 : public CompilerHelper { public: Arith256(llvm::IRBuilder<>& _builder); - virtual ~Arith256(); llvm::Value* mul(llvm::Value* _arg1, llvm::Value* _arg2); - llvm::Value* div(llvm::Value* _arg1, llvm::Value* _arg2); - llvm::Value* mod(llvm::Value* _arg1, llvm::Value* _arg2); - llvm::Value* sdiv(llvm::Value* _arg1, llvm::Value* _arg2); - llvm::Value* smod(llvm::Value* _arg1, llvm::Value* _arg2); + std::pair div(llvm::Value* _arg1, llvm::Value* _arg2); + std::pair sdiv(llvm::Value* _arg1, llvm::Value* _arg2); llvm::Value* exp(llvm::Value* _arg1, llvm::Value* _arg2); llvm::Value* mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3); llvm::Value* addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3); + void debug(llvm::Value* _value, char _c); + private: + llvm::Function* getDivFunc(llvm::Type* _type); + llvm::Function* getExpFunc(); + llvm::Function* getAddModFunc(); + llvm::Function* getMulModFunc(); + llvm::Value* binaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2); - llvm::Value* ternaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3); llvm::Function* m_mul; - llvm::Function* m_div; - llvm::Function* m_mod; - llvm::Function* m_sdiv; - llvm::Function* m_smod; - llvm::Function* m_exp; - llvm::Function* m_mulmod; - llvm::Function* m_addmod; + + llvm::Function* m_div = nullptr; + llvm::Function* m_div512 = nullptr; + llvm::Function* m_exp = nullptr; + llvm::Function* m_addmod = nullptr; + llvm::Function* m_mulmod = nullptr; + llvm::Function* m_debug = nullptr; llvm::Value* m_arg1; llvm::Value* m_arg2; diff --git a/evmjit/libevmjit/CMakeLists.txt b/evmjit/libevmjit/CMakeLists.txt index c1acfc409..545e55344 100644 --- a/evmjit/libevmjit/CMakeLists.txt +++ b/evmjit/libevmjit/CMakeLists.txt @@ -11,15 +11,25 @@ if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set_source_files_properties(Cache.cpp PROPERTIES COMPILE_FLAGS -fno-rtti) endif() +find_package(Git) +if(GIT_FOUND) + execute_process(COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} describe --dirty --always + OUTPUT_VARIABLE EVMJIT_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() +if(NOT EVMJIT_VERSION) + set(EVMJIT_VERSION "unknown") +endif() + +message("EVM JIT version: ${EVMJIT_VERSION}") + add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS}) -set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs") +set_target_properties(${TARGET_NAME} PROPERTIES VERSION ${EVMJIT_VERSION} FOLDER "libs") include_directories(${LLVM_INCLUDE_DIRS}) -include_directories(${Boost_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS}) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") -install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib) -install(FILES ${INTERFACE_HEADERS} DESTINATION include/${TARGET_NAME}) +install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin) +#install(FILES ${INTERFACE_HEADERS} DESTINATION include/${TARGET_NAME}) diff --git a/evmjit/libevmjit/Common.h b/evmjit/libevmjit/Common.h index 1d8451c74..d629f25ad 100644 --- a/evmjit/libevmjit/Common.h +++ b/evmjit/libevmjit/Common.h @@ -1,7 +1,14 @@ #pragma once #include -#include +#include +#include + +#ifdef _MSC_VER +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif namespace dev { @@ -12,8 +19,7 @@ namespace jit using byte = uint8_t; using bytes = std::vector; -using u256 = boost::multiprecision::uint256_t; -using bigint = boost::multiprecision::cpp_int; +using bytes_ref = std::tuple; struct NoteChannel {}; // FIXME: Use some log library? @@ -23,18 +29,21 @@ enum class ReturnCode Return = 1, Suicide = 2, - BadJumpDestination = 101, - OutOfGas = 102, - StackTooSmall = 103, - BadInstruction = 104, + OutOfGas = -1, + BadJumpDestination = -2, + StackTooSmall = -3, + BadInstruction = -4, + + LLVMConfigError = -5, + LLVMCompileError = -6, + LLVMLinkError = -7, + + UnexpectedException = -8, - LLVMConfigError = 201, - LLVMCompileError = 202, - LLVMLinkError = 203, + LinkerWorkaround = -299, }; /// Representation of 256-bit value binary compatible with LLVM i256 -// TODO: Replace with h256 struct i256 { uint64_t a = 0; diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index 531ad5526..ad8a14fc7 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -90,6 +91,7 @@ void Compiler::createBasicBlocks(bytes const& _bytecode) } } + // TODO: Create Stop basic block on demand m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc); } @@ -272,7 +274,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode auto lhs = stack.pop(); auto rhs = stack.pop(); auto res = _arith.div(lhs, rhs); - stack.push(res); + stack.push(res.first); break; } @@ -281,7 +283,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode auto lhs = stack.pop(); auto rhs = stack.pop(); auto res = _arith.sdiv(lhs, rhs); - stack.push(res); + stack.push(res.first); break; } @@ -289,8 +291,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode { auto lhs = stack.pop(); auto rhs = stack.pop(); - auto res = _arith.mod(lhs, rhs); - stack.push(res); + auto res = _arith.div(lhs, rhs); + stack.push(res.second); break; } @@ -298,8 +300,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode { auto lhs = stack.pop(); auto rhs = stack.pop(); - auto res = _arith.smod(lhs, rhs); - stack.push(res); + auto res = _arith.sdiv(lhs, rhs); + stack.push(res.second); break; } @@ -455,14 +457,15 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode // test for word >> (k * 8 + 7) auto bitpos = m_builder.CreateAdd(k32x8, m_builder.getInt64(7), "bitpos"); - auto bittester = m_builder.CreateShl(Constant::get(1), bitpos); + auto bitposEx = m_builder.CreateZExt(bitpos, Type::Word); + auto bittester = m_builder.CreateShl(Constant::get(1), bitposEx); auto bitresult = m_builder.CreateAnd(word, bittester); auto bittest = m_builder.CreateICmpUGT(bitresult, Constant::get(0)); // FIXME: The following does not work - LLVM bug, report! //auto bitval = m_builder.CreateLShr(word, bitpos, "bitval"); //auto bittest = m_builder.CreateTrunc(bitval, Type::Bool, "bittest"); - auto mask_ = m_builder.CreateShl(Constant::get(1), bitpos); + auto mask_ = m_builder.CreateShl(Constant::get(1), bitposEx); auto mask = m_builder.CreateSub(mask_, Constant::get(1), "mask"); auto negmask = m_builder.CreateXor(mask, llvm::ConstantInt::getAllOnesValue(Type::Word), "negmask"); @@ -636,20 +639,29 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode case Instruction::CALLER: case Instruction::ORIGIN: case Instruction::CALLVALUE: - case Instruction::CALLDATASIZE: - case Instruction::CODESIZE: case Instruction::GASPRICE: case Instruction::COINBASE: - case Instruction::TIMESTAMP: - case Instruction::NUMBER: case Instruction::DIFFICULTY: case Instruction::GASLIMIT: + case Instruction::NUMBER: + case Instruction::TIMESTAMP: { // Pushes an element of runtime data on stack - stack.push(_runtimeManager.get(inst)); + auto value = _runtimeManager.get(inst); + value = m_builder.CreateZExt(value, Type::Word); + stack.push(value); break; } + case Instruction::CODESIZE: + // TODO: Use constant + stack.push(_runtimeManager.getCodeSize()); + break; + + case Instruction::CALLDATASIZE: + stack.push(_runtimeManager.getCallDataSize()); + break; + case Instruction::BLOCKHASH: { auto number = stack.pop(); @@ -681,7 +693,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode auto reqBytes = stack.pop(); auto srcPtr = _runtimeManager.getCallData(); - auto srcSize = _runtimeManager.get(RuntimeData::CallDataSize); + auto srcSize = _runtimeManager.getCallDataSize(); _memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); break; @@ -694,7 +706,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode auto reqBytes = stack.pop(); auto srcPtr = _runtimeManager.getCode(); // TODO: Code & its size are constants, feature #80814234 - auto srcSize = _runtimeManager.get(RuntimeData::CodeSize); + auto srcSize = _runtimeManager.getCodeSize(); _memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); break; @@ -852,6 +864,18 @@ void Compiler::removeDeadBlocks() } } while (sthErased); + + if (m_jumpTableBlock && llvm::pred_begin(m_jumpTableBlock->llvm()) == llvm::pred_end(m_jumpTableBlock->llvm())) + { + m_jumpTableBlock->llvm()->eraseFromParent(); + m_jumpTableBlock.reset(); + } + + if (m_badJumpBlock && llvm::pred_begin(m_badJumpBlock->llvm()) == llvm::pred_end(m_badJumpBlock->llvm())) + { + m_badJumpBlock->llvm()->eraseFromParent(); + m_badJumpBlock.reset(); + } } void Compiler::dumpCFGifRequired(std::string const& _dotfilePath) diff --git a/evmjit/libevmjit/ExecutionEngine.cpp b/evmjit/libevmjit/ExecutionEngine.cpp index b6b140fd5..2db125c2d 100644 --- a/evmjit/libevmjit/ExecutionEngine.cpp +++ b/evmjit/libevmjit/ExecutionEngine.cpp @@ -1,6 +1,7 @@ #include "ExecutionEngine.h" #include +#include // env options #include #include @@ -19,13 +20,6 @@ #include "Compiler.h" #include "Cache.h" -#if defined(NDEBUG) -#define DEBUG_ENV_OPTION(name) false -#else -#include -#define DEBUG_ENV_OPTION(name) (std::getenv(#name) != nullptr) -#endif - namespace dev { namespace eth @@ -67,13 +61,21 @@ std::string codeHash(bytes const& _code) return std::to_string(hash); } +bool getEnvOption(char const* _name, bool _default) +{ + auto var = std::getenv(_name); + if (!var) + return _default; + return std::strtol(var, nullptr, 10) != 0; +} + } ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _env) { static std::unique_ptr ee; // TODO: Use Managed Objects from LLVM? - static auto debugDumpModule = DEBUG_ENV_OPTION(EVMJIT_DUMP_MODULE); - static bool objectCacheEnabled = !DEBUG_ENV_OPTION(EVMJIT_CACHE_OFF); + static auto debugDumpModule = getEnvOption("EVMJIT_DUMP", false); + static auto objectCacheEnabled = getEnvOption("EVMJIT_CACHE", true); auto mainFuncName = codeHash(_code); EntryFuncPtr entryFuncPtr{}; @@ -136,7 +138,10 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en auto returnCode = runEntryFunc(entryFuncPtr, &runtime); if (returnCode == ReturnCode::Return) - this->returnData = runtime.getReturnData(); + { + returnData = runtime.getReturnData(); // Save reference to return data + std::swap(m_memory, runtime.getMemory()); // Take ownership of memory + } auto executionEndTime = std::chrono::high_resolution_clock::now(); clog(JIT) << " + " << std::chrono::duration_cast(executionEndTime - executionStartTime).count() << " ms\n"; diff --git a/evmjit/libevmjit/ExecutionEngine.h b/evmjit/libevmjit/ExecutionEngine.h index 559701bba..e8d1e1c05 100644 --- a/evmjit/libevmjit/ExecutionEngine.h +++ b/evmjit/libevmjit/ExecutionEngine.h @@ -16,9 +16,15 @@ public: ExecutionEngine(ExecutionEngine const&) = delete; void operator=(ExecutionEngine) = delete; - ReturnCode run(bytes const& _code, RuntimeData* _data, Env* _env); + EXPORT ReturnCode run(bytes const& _code, RuntimeData* _data, Env* _env); - bytes returnData; + /// Reference to returned data (RETURN opcode used) + bytes_ref returnData; + +private: + /// After execution, if RETURN used, memory is moved there + /// to allow client copy the returned data + bytes m_memory; }; } diff --git a/evmjit/libevmjit/Memory.cpp b/evmjit/libevmjit/Memory.cpp index 9f57c7a4b..c6f8a9ec0 100644 --- a/evmjit/libevmjit/Memory.cpp +++ b/evmjit/libevmjit/Memory.cpp @@ -1,237 +1,239 @@ -#include "Memory.h" - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "Type.h" -#include "Runtime.h" -#include "GasMeter.h" -#include "Endianness.h" -#include "RuntimeManager.h" - -namespace dev -{ -namespace eth -{ -namespace jit -{ - -Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter): - RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed - m_gasMeter(_gasMeter) -{ - llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr}; - m_resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule()); - llvm::AttrBuilder attrBuilder; - attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly); - m_resize->setAttributes(llvm::AttributeSet::get(m_resize->getContext(), 1, attrBuilder)); - - m_require = createRequireFunc(_gasMeter); - m_loadWord = createFunc(false, Type::Word, _gasMeter); - m_storeWord = createFunc(true, Type::Word, _gasMeter); - m_storeByte = createFunc(true, Type::Byte, _gasMeter); -} - -llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter) -{ - llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word}; - auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule()); - auto rt = func->arg_begin(); - rt->setName("rt"); - auto offset = rt->getNextNode(); - offset->setName("offset"); - auto size = offset->getNextNode(); - size->setName("size"); - - auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func); - auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); - auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func); - auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func); - - InsertPointGuard guard(m_builder); // Restores insert point at function exit - - // BB "Pre": Ignore checks with size 0 - m_builder.SetInsertPoint(preBB); - auto sizeIsZero = m_builder.CreateICmpEQ(size, Constant::get(0)); - m_builder.CreateCondBr(sizeIsZero, returnBB, checkBB); - - // BB "Check" - m_builder.SetInsertPoint(checkBB); - auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word); - auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res"); - auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq"); - auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1"); - auto rtPtr = getRuntimeManager().getRuntimePtr(); - auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); - auto currSize = m_builder.CreateLoad(sizePtr, "currSize"); - auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall"); - auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded"); - m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights? - - // BB "Resize" - m_builder.SetInsertPoint(resizeBB); - // Check gas first - uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res"); - auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0); - auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2"); - auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow"); - wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired); - wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq"); - sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq"); - auto words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k - auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords"); - _gasMeter.countMemory(newWords); - // Resize - m_builder.CreateStore(sizeRequired, sizePtr); - auto newData = m_builder.CreateCall2(m_resize, rt, sizePtr, "newData"); - auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); - m_builder.CreateStore(newData, dataPtr); - m_builder.CreateBr(returnBB); - - // BB "Return" - m_builder.SetInsertPoint(returnBB); - m_builder.CreateRetVoid(); - return func; -} - -llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&) -{ - auto isWord = _valueType == Type::Word; - - llvm::Type* storeArgs[] = {Type::RuntimePtr, Type::Word, _valueType}; - llvm::Type* loadArgs[] = {Type::RuntimePtr, Type::Word}; - auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload"; - auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::Word, loadArgs, false); - auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule()); - - InsertPointGuard guard(m_builder); // Restores insert point at function exit - - m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func)); - auto rt = func->arg_begin(); - rt->setName("rt"); - auto index = rt->getNextNode(); - index->setName("index"); - - auto valueSize = _valueType->getPrimitiveSizeInBits() / 8; - this->require(index, Constant::get(valueSize)); - auto ptr = getBytePtr(index); - if (isWord) - ptr = m_builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr"); - if (_isStore) - { - llvm::Value* value = index->getNextNode(); - value->setName("value"); - if (isWord) - value = Endianness::toBE(m_builder, value); - m_builder.CreateStore(value, ptr); - m_builder.CreateRetVoid(); - } - else - { - llvm::Value* ret = m_builder.CreateLoad(ptr); - ret = Endianness::toNative(m_builder, ret); - m_builder.CreateRet(ret); - } - - return func; -} - - -llvm::Value* Memory::loadWord(llvm::Value* _addr) -{ - return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr}); -} - -void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) -{ - createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word}); -} - -void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word) -{ - auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); - createCall(m_storeByte, {getRuntimeManager().getRuntimePtr(), _addr, byte}); -} - -llvm::Value* Memory::getData() -{ - auto rtPtr = getRuntimeManager().getRuntimePtr(); - auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); - return m_builder.CreateLoad(dataPtr, "data"); -} - -llvm::Value* Memory::getSize() -{ - auto rtPtr = getRuntimeManager().getRuntimePtr(); - auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); - return m_builder.CreateLoad(sizePtr, "size"); -} - -llvm::Value* Memory::getBytePtr(llvm::Value* _index) -{ - return m_builder.CreateGEP(getData(), _index, "ptr"); -} - -void Memory::require(llvm::Value* _offset, llvm::Value* _size) -{ - createCall(m_require, {getRuntimeManager().getRuntimePtr(), _offset, _size}); -} - -void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, - llvm::Value* _destMemIdx, llvm::Value* _reqBytes) -{ - require(_destMemIdx, _reqBytes); - - // Additional copy cost - // TODO: This round ups to 32 happens in many places - auto copyWords = m_builder.CreateUDiv(m_builder.CreateAdd(_reqBytes, Constant::get(31)), Constant::get(32)); - m_gasMeter.countCopy(copyWords); - - // Algorithm: - // isOutsideData = idx256 >= size256 - // idx64 = trunc idx256 - // size64 = trunc size256 - // dataLeftSize = size64 - idx64 // safe if not isOutsideData - // reqBytes64 = trunc _reqBytes // require() handles large values - // bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min - // bytesToCopy = select(isOutsideData, 0, bytesToCopy0) - - auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize); - auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::lowPrecision); - auto size64 = m_builder.CreateTrunc(_srcSize, Type::lowPrecision); - auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64); - auto reqBytes64 = m_builder.CreateTrunc(_reqBytes, Type::lowPrecision); - auto outOfBound = m_builder.CreateICmpUGT(reqBytes64, dataLeftSize); - auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes64); - auto zero64 = llvm::ConstantInt::get(Type::lowPrecision, 0); // TODO: Cache common constants - auto bytesToCopy = m_builder.CreateSelect(isOutsideData, zero64, bytesToCopyInner); - - auto src = m_builder.CreateGEP(_srcPtr, idx64, "src"); - auto dst = m_builder.CreateGEP(getData(), _destMemIdx, "dst"); - m_builder.CreateMemCpy(dst, src, bytesToCopy, 0); -} - -} -} -} - - -extern "C" -{ - using namespace dev::eth::jit; - - EXPORT byte* mem_resize(Runtime* _rt, i256* _size) // TODO: Use uint64 as size OR use realloc in LLVM IR - { - auto size = _size->a; // Trunc to 64-bit - auto& memory = _rt->getMemory(); - memory.resize(size); - return memory.data(); - } -} +#include "Memory.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "Type.h" +#include "Runtime.h" +#include "GasMeter.h" +#include "Endianness.h" +#include "RuntimeManager.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter): + RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed + m_gasMeter(_gasMeter) +{ + llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr}; + m_resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule()); + llvm::AttrBuilder attrBuilder; + attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly); + m_resize->setAttributes(llvm::AttributeSet::get(m_resize->getContext(), 1, attrBuilder)); + + m_require = createRequireFunc(_gasMeter); + m_loadWord = createFunc(false, Type::Word, _gasMeter); + m_storeWord = createFunc(true, Type::Word, _gasMeter); + m_storeByte = createFunc(true, Type::Byte, _gasMeter); +} + +llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter) +{ + llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word}; + auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule()); + auto rt = func->arg_begin(); + rt->setName("rt"); + auto offset = rt->getNextNode(); + offset->setName("offset"); + auto size = offset->getNextNode(); + size->setName("size"); + + auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func); + auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); + auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func); + auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func); + + InsertPointGuard guard(m_builder); // Restores insert point at function exit + + // BB "Pre": Ignore checks with size 0 + m_builder.SetInsertPoint(preBB); + auto sizeIsZero = m_builder.CreateICmpEQ(size, Constant::get(0)); + m_builder.CreateCondBr(sizeIsZero, returnBB, checkBB); + + // BB "Check" + m_builder.SetInsertPoint(checkBB); + auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word); + auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res"); + auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq"); + auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1"); + auto rtPtr = getRuntimeManager().getRuntimePtr(); + auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); + auto currSize = m_builder.CreateLoad(sizePtr, "currSize"); + auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall"); + auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded"); + m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights? + + // BB "Resize" + m_builder.SetInsertPoint(resizeBB); + // Check gas first + uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res"); + auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0); + auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2"); + auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow"); + wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired); + wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq"); + sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq"); + auto words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k + auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords"); + _gasMeter.countMemory(newWords); + // Resize + m_builder.CreateStore(sizeRequired, sizePtr); + auto newData = m_builder.CreateCall2(m_resize, rt, sizePtr, "newData"); + auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); + m_builder.CreateStore(newData, dataPtr); + m_builder.CreateBr(returnBB); + + // BB "Return" + m_builder.SetInsertPoint(returnBB); + m_builder.CreateRetVoid(); + return func; +} + +llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&) +{ + auto isWord = _valueType == Type::Word; + + llvm::Type* storeArgs[] = {Type::RuntimePtr, Type::Word, _valueType}; + llvm::Type* loadArgs[] = {Type::RuntimePtr, Type::Word}; + auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload"; + auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::Word, loadArgs, false); + auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule()); + + InsertPointGuard guard(m_builder); // Restores insert point at function exit + + m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func)); + auto rt = func->arg_begin(); + rt->setName("rt"); + auto index = rt->getNextNode(); + index->setName("index"); + + auto valueSize = _valueType->getPrimitiveSizeInBits() / 8; + this->require(index, Constant::get(valueSize)); + auto ptr = getBytePtr(index); + if (isWord) + ptr = m_builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr"); + if (_isStore) + { + llvm::Value* value = index->getNextNode(); + value->setName("value"); + if (isWord) + value = Endianness::toBE(m_builder, value); + m_builder.CreateStore(value, ptr); + m_builder.CreateRetVoid(); + } + else + { + llvm::Value* ret = m_builder.CreateLoad(ptr); + ret = Endianness::toNative(m_builder, ret); + m_builder.CreateRet(ret); + } + + return func; +} + + +llvm::Value* Memory::loadWord(llvm::Value* _addr) +{ + return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr}); +} + +void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) +{ + createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word}); +} + +void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word) +{ + auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); + createCall(m_storeByte, {getRuntimeManager().getRuntimePtr(), _addr, byte}); +} + +llvm::Value* Memory::getData() +{ + auto rtPtr = getRuntimeManager().getRuntimePtr(); + auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); + return m_builder.CreateLoad(dataPtr, "data"); +} + +llvm::Value* Memory::getSize() +{ + auto rtPtr = getRuntimeManager().getRuntimePtr(); + auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); + return m_builder.CreateLoad(sizePtr, "size"); +} + +llvm::Value* Memory::getBytePtr(llvm::Value* _index) +{ + auto idx = m_builder.CreateTrunc(_index, Type::Size, "idx"); // Never allow memory index be a type bigger than i64 + return m_builder.CreateGEP(getData(), idx, "ptr"); +} + +void Memory::require(llvm::Value* _offset, llvm::Value* _size) +{ + createCall(m_require, {getRuntimeManager().getRuntimePtr(), _offset, _size}); +} + +void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, + llvm::Value* _destMemIdx, llvm::Value* _reqBytes) +{ + require(_destMemIdx, _reqBytes); + + // Additional copy cost + // TODO: This round ups to 32 happens in many places + auto copyWords = m_builder.CreateUDiv(m_builder.CreateAdd(_reqBytes, Constant::get(31)), Constant::get(32)); + m_gasMeter.countCopy(copyWords); + + // Algorithm: + // isOutsideData = idx256 >= size256 + // idx64 = trunc idx256 + // size64 = trunc size256 + // dataLeftSize = size64 - idx64 // safe if not isOutsideData + // reqBytes64 = trunc _reqBytes // require() handles large values + // bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min + // bytesToCopy = select(isOutsideData, 0, bytesToCopy0) + + auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize); + auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::lowPrecision); + auto size64 = m_builder.CreateTrunc(_srcSize, Type::lowPrecision); + auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64); + auto reqBytes64 = m_builder.CreateTrunc(_reqBytes, Type::lowPrecision); + auto outOfBound = m_builder.CreateICmpUGT(reqBytes64, dataLeftSize); + auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes64); + auto zero64 = llvm::ConstantInt::get(Type::lowPrecision, 0); // TODO: Cache common constants + auto bytesToCopy = m_builder.CreateSelect(isOutsideData, zero64, bytesToCopyInner); + + auto src = m_builder.CreateGEP(_srcPtr, idx64, "src"); + auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); // Never allow memory index be a type bigger than i64 + auto dst = m_builder.CreateGEP(getData(), dstIdx, "dst"); + m_builder.CreateMemCpy(dst, src, bytesToCopy, 0); +} + +} +} +} + + +extern "C" +{ + using namespace dev::eth::jit; + + EXPORT byte* mem_resize(Runtime* _rt, i256* _size) // TODO: Use uint64 as size OR use realloc in LLVM IR + { + auto size = _size->a; // Trunc to 64-bit + auto& memory = _rt->getMemory(); + memory.resize(size); + return memory.data(); + } +} diff --git a/evmjit/libevmjit/Runtime.cpp b/evmjit/libevmjit/Runtime.cpp index 911dc469d..eb70e01d1 100644 --- a/evmjit/libevmjit/Runtime.cpp +++ b/evmjit/libevmjit/Runtime.cpp @@ -18,18 +18,19 @@ Runtime::Runtime(RuntimeData* _data, Env* _env) : m_currJmpBuf(m_jmpBuf) {} -bytes Runtime::getReturnData() const // FIXME: Reconsider returning by copy +bytes_ref Runtime::getReturnData() const { - // TODO: Handle large indexes - auto offset = static_cast(llvm2eth(m_data.elems[RuntimeData::ReturnDataOffset])); - auto size = static_cast(llvm2eth(m_data.elems[RuntimeData::ReturnDataSize])); + auto data = m_data.callData; + auto size = static_cast(m_data.callDataSize); - assert(offset + size <= m_memory.size() || size == 0); - if (offset + size > m_memory.size()) + if (data < m_memory.data() || data >= m_memory.data() + m_memory.size() || size == 0) + { + assert(size == 0); // data can be an invalid pointer only if size is 0 + m_data.callData = nullptr; return {}; + } - auto dataBeg = m_memory.begin() + offset; - return {dataBeg, dataBeg + size}; + return bytes_ref{data, size}; } } diff --git a/evmjit/libevmjit/Runtime.h b/evmjit/libevmjit/Runtime.h index f97efa128..20cf56984 100644 --- a/evmjit/libevmjit/Runtime.h +++ b/evmjit/libevmjit/Runtime.h @@ -1,59 +1,46 @@ - -#pragma once - -#include -#include - -#include "Instruction.h" -#include "CompilerHelper.h" -#include "Utils.h" -#include "Type.h" -#include "RuntimeData.h" - - -#ifdef _MSC_VER - #define EXPORT __declspec(dllexport) -#else - #define EXPORT -#endif - -namespace dev -{ -namespace eth -{ -namespace jit -{ - -using StackImpl = std::vector; -using MemoryImpl = bytes; -using jmp_buf_ref = decltype(&std::jmp_buf{}[0]); - -class Runtime -{ -public: - Runtime(RuntimeData* _data, Env* _env); - - Runtime(const Runtime&) = delete; - Runtime& operator=(const Runtime&) = delete; - - StackImpl& getStack() { return m_stack; } - MemoryImpl& getMemory() { return m_memory; } - Env* getEnvPtr() { return &m_env; } - - bytes getReturnData() const; - jmp_buf_ref getJmpBuf() { return m_jmpBuf; } - -private: - RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract. - Env& m_env; ///< Pointer to environment proxy. Expected by compiled contract. - jmp_buf_ref m_currJmpBuf; ///< Pointer to jump buffer. Expected by compiled contract. - byte* m_memoryData = nullptr; - i256 m_memorySize; - std::jmp_buf m_jmpBuf; - StackImpl m_stack; - MemoryImpl m_memory; -}; - -} -} -} + +#pragma once + +#include +#include "RuntimeData.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +using StackImpl = std::vector; +using MemoryImpl = bytes; +using jmp_buf_ref = decltype(&std::jmp_buf{}[0]); + +class Runtime +{ +public: + Runtime(RuntimeData* _data, Env* _env); + + Runtime(const Runtime&) = delete; + Runtime& operator=(const Runtime&) = delete; + + StackImpl& getStack() { return m_stack; } + MemoryImpl& getMemory() { return m_memory; } + Env* getEnvPtr() { return &m_env; } + + bytes_ref getReturnData() const; + jmp_buf_ref getJmpBuf() { return m_jmpBuf; } + +private: + RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract. + Env& m_env; ///< Pointer to environment proxy. Expected by compiled contract. + jmp_buf_ref m_currJmpBuf; ///< Pointer to jump buffer. Expected by compiled contract. + byte* m_memoryData = nullptr; + i256 m_memorySize; + std::jmp_buf m_jmpBuf; + StackImpl m_stack; + MemoryImpl m_memory; +}; + +} +} +} diff --git a/evmjit/libevmjit/RuntimeData.h b/evmjit/libevmjit/RuntimeData.h index bb52f7864..58d68db8a 100644 --- a/evmjit/libevmjit/RuntimeData.h +++ b/evmjit/libevmjit/RuntimeData.h @@ -15,32 +15,41 @@ struct RuntimeData enum Index { Gas, + GasPrice, + CallData, + CallDataSize, Address, Caller, Origin, CallValue, - CallDataSize, - GasPrice, CoinBase, - TimeStamp, - Number, Difficulty, GasLimit, + Number, + Timestamp, + Code, CodeSize, - _size, - - ReturnDataOffset = CallValue, // Reuse 2 fields for return data reference - ReturnDataSize = CallDataSize, - SuicideDestAddress = Address, ///< Suicide balance destination address + SuicideDestAddress = Address, ///< Suicide balance destination address + ReturnData = CallData, ///< Return data pointer (set only in case of RETURN) + ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN) }; - i256 elems[_size] = {}; + int64_t gas = 0; + int64_t gasPrice = 0; byte const* callData = nullptr; + uint64_t callDataSize = 0; + i256 address; + i256 caller; + i256 origin; + i256 callValue; + i256 coinBase; + i256 difficulty; + i256 gasLimit; + uint64_t number = 0; + int64_t timestamp = 0; byte const* code = nullptr; - - void set(Index _index, u256 _value) { elems[_index] = eth2llvm(_value); } - u256 get(Index _index) { return llvm2eth(elems[_index]); } + uint64_t codeSize = 0; }; /// VM Environment (ExtVM) opaque type diff --git a/evmjit/libevmjit/RuntimeManager.cpp b/evmjit/libevmjit/RuntimeManager.cpp index ea2fe20b5..408f2dee3 100644 --- a/evmjit/libevmjit/RuntimeManager.cpp +++ b/evmjit/libevmjit/RuntimeManager.cpp @@ -22,9 +22,21 @@ llvm::StructType* RuntimeManager::getRuntimeDataType() { llvm::Type* elems[] = { - llvm::ArrayType::get(Type::Word, RuntimeData::_size), // i256[] - Type::BytePtr, // callData - Type::BytePtr // code + Type::Size, // gas + Type::Size, // gasPrice + Type::BytePtr, // callData + Type::Size, // callDataSize + Type::Word, // address + Type::Word, // caller + Type::Word, // origin + Type::Word, // callValue + Type::Word, // coinBase + Type::Word, // difficulty + Type::Word, // gasLimit + Type::Size, // blockNumber + Type::Size, // blockTimestamp + Type::BytePtr, // code + Type::Size, // codeSize }; type = llvm::StructType::create(elems, "RuntimeData"); } @@ -56,19 +68,21 @@ llvm::Twine getName(RuntimeData::Index _index) switch (_index) { default: return "data"; - case RuntimeData::Gas: return "gas"; case RuntimeData::Address: return "address"; case RuntimeData::Caller: return "caller"; case RuntimeData::Origin: return "origin"; case RuntimeData::CallValue: return "callvalue"; - case RuntimeData::CallDataSize: return "calldatasize"; case RuntimeData::GasPrice: return "gasprice"; case RuntimeData::CoinBase: return "coinbase"; - case RuntimeData::TimeStamp: return "timestamp"; - case RuntimeData::Number: return "number"; case RuntimeData::Difficulty: return "difficulty"; case RuntimeData::GasLimit: return "gaslimit"; - case RuntimeData::CodeSize: return "codesize"; + case RuntimeData::CallData: return "callData"; + case RuntimeData::Code: return "code"; + case RuntimeData::CodeSize: return "code"; + case RuntimeData::CallDataSize: return "callDataSize"; + case RuntimeData::Gas: return "gas"; + case RuntimeData::Number: return "number"; + case RuntimeData::Timestamp: return "timestamp"; } } } @@ -100,7 +114,9 @@ llvm::Value* RuntimeManager::getDataPtr() return m_dataPtr; auto rtPtr = getRuntimePtr(); - return m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 0), "data"); + auto dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 0), "data"); + assert(dataPtr->getType() == getRuntimeDataType()->getPointerTo()); + return dataPtr; } llvm::Value* RuntimeManager::getEnvPtr() @@ -111,24 +127,33 @@ llvm::Value* RuntimeManager::getEnvPtr() llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index) { - llvm::Value* idxList[] = {m_builder.getInt32(0), m_builder.getInt32(0), m_builder.getInt32(_index)}; - return m_builder.CreateInBoundsGEP(getDataPtr(), idxList, getName(_index) + "Ptr"); + auto ptr = getBuilder().CreateStructGEP(getDataPtr(), _index); + assert(getRuntimeDataType()->getElementType(_index)->getPointerTo() == ptr->getType()); + return ptr; } llvm::Value* RuntimeManager::get(RuntimeData::Index _index) { - return m_builder.CreateLoad(getPtr(_index), getName(_index)); + return getBuilder().CreateLoad(getPtr(_index), getName(_index)); } void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value) { - m_builder.CreateStore(_value, getPtr(_index)); + auto ptr = getPtr(_index); + assert(ptr->getType() == _value->getType()->getPointerTo()); + getBuilder().CreateStore(_value, ptr); } void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size) { - set(RuntimeData::ReturnDataOffset, _offset); - set(RuntimeData::ReturnDataSize, _size); + auto memPtr = getBuilder().CreateStructGEP(getRuntimePtr(), 3); + auto mem = getBuilder().CreateLoad(memPtr, "memory"); + auto idx = m_builder.CreateTrunc(_offset, Type::Size, "idx"); // Never allow memory index be a type bigger than i64 // TODO: Report bug & fix to LLVM + auto returnDataPtr = getBuilder().CreateGEP(mem, idx); + set(RuntimeData::ReturnData, returnDataPtr); + + auto size64 = getBuilder().CreateTrunc(_size, Type::Size); + set(RuntimeData::ReturnDataSize, size64); } void RuntimeManager::registerSuicide(llvm::Value* _balanceAddress) @@ -146,32 +171,41 @@ llvm::Value* RuntimeManager::get(Instruction _inst) switch (_inst) { default: assert(false); return nullptr; - case Instruction::GAS: return get(RuntimeData::Gas); case Instruction::ADDRESS: return get(RuntimeData::Address); case Instruction::CALLER: return get(RuntimeData::Caller); case Instruction::ORIGIN: return get(RuntimeData::Origin); case Instruction::CALLVALUE: return get(RuntimeData::CallValue); - case Instruction::CALLDATASIZE: return get(RuntimeData::CallDataSize); case Instruction::GASPRICE: return get(RuntimeData::GasPrice); case Instruction::COINBASE: return get(RuntimeData::CoinBase); - case Instruction::TIMESTAMP: return get(RuntimeData::TimeStamp); - case Instruction::NUMBER: return get(RuntimeData::Number); case Instruction::DIFFICULTY: return get(RuntimeData::Difficulty); case Instruction::GASLIMIT: return get(RuntimeData::GasLimit); - case Instruction::CODESIZE: return get(RuntimeData::CodeSize); + case Instruction::NUMBER: return get(RuntimeData::Number); + case Instruction::TIMESTAMP: return get(RuntimeData::Timestamp); } } llvm::Value* RuntimeManager::getCallData() { - auto ptr = getBuilder().CreateStructGEP(getDataPtr(), 1, "calldataPtr"); - return getBuilder().CreateLoad(ptr, "calldata"); + return get(RuntimeData::CallData); } llvm::Value* RuntimeManager::getCode() { - auto ptr = getBuilder().CreateStructGEP(getDataPtr(), 2, "codePtr"); - return getBuilder().CreateLoad(ptr, "code"); + return get(RuntimeData::Code); +} + +llvm::Value* RuntimeManager::getCodeSize() +{ + auto value = get(RuntimeData::CodeSize); + assert(value->getType() == Type::Size); + return getBuilder().CreateZExt(value, Type::Word); +} + +llvm::Value* RuntimeManager::getCallDataSize() +{ + auto value = get(RuntimeData::CallDataSize); + assert(value->getType() == Type::Size); + return getBuilder().CreateZExt(value, Type::Word); } llvm::Value* RuntimeManager::getJmpBuf() @@ -182,14 +216,15 @@ llvm::Value* RuntimeManager::getJmpBuf() llvm::Value* RuntimeManager::getGas() { - return get(RuntimeData::Gas); + auto value = get(RuntimeData::Gas); + assert(value->getType() == Type::Size); + return getBuilder().CreateZExt(value, Type::Word); } void RuntimeManager::setGas(llvm::Value* _gas) { - llvm::Value* idxList[] = {m_builder.getInt32(0), m_builder.getInt32(0), m_builder.getInt32(RuntimeData::Gas)}; - auto ptr = m_builder.CreateInBoundsGEP(getDataPtr(), idxList, "gasPtr"); - m_builder.CreateStore(_gas, ptr); + auto newGas = getBuilder().CreateTrunc(_gas, Type::Size); + set(RuntimeData::Gas, newGas); } } diff --git a/evmjit/libevmjit/RuntimeManager.h b/evmjit/libevmjit/RuntimeManager.h index ce60424ac..b5f3ca657 100644 --- a/evmjit/libevmjit/RuntimeManager.h +++ b/evmjit/libevmjit/RuntimeManager.h @@ -26,6 +26,8 @@ public: llvm::Value* getGas(); // TODO: Remove llvm::Value* getCallData(); llvm::Value* getCode(); + llvm::Value* getCodeSize(); + llvm::Value* getCallDataSize(); void setGas(llvm::Value* _gas); void registerReturnData(llvm::Value* _index, llvm::Value* _size); diff --git a/evmjit/libevmjit/Stack.cpp b/evmjit/libevmjit/Stack.cpp index 52782999a..44f1c21c1 100644 --- a/evmjit/libevmjit/Stack.cpp +++ b/evmjit/libevmjit/Stack.cpp @@ -73,7 +73,7 @@ extern "C" { auto& stack = _rt->getStack(); if (stack.size() < _count) - longjmp(_rt->getJmpBuf(), static_cast(ReturnCode::StackTooSmall)); + longjmp(_rt->getJmpBuf(), static_cast(ReturnCode::StackTooSmall)); stack.erase(stack.end() - _count, stack.end()); } @@ -92,7 +92,7 @@ extern "C" auto& stack = _rt->getStack(); // TODO: encode _index and stack size in the return code if (stack.size() <= _index) - longjmp(_rt->getJmpBuf(), static_cast(ReturnCode::StackTooSmall)); + longjmp(_rt->getJmpBuf(), static_cast(ReturnCode::StackTooSmall)); *o_ret = *(stack.rbegin() + _index); } @@ -102,7 +102,7 @@ extern "C" auto& stack = _rt->getStack(); // TODO: encode _index and stack size in the return code if (stack.size() <= _index) - longjmp(_rt->getJmpBuf(), static_cast(ReturnCode::StackTooSmall)); + longjmp(_rt->getJmpBuf(), static_cast(ReturnCode::StackTooSmall)); *(stack.rbegin() + _index) = *_word; } @@ -116,7 +116,7 @@ extern "C" index = std::numeric_limits::max(); // set max to fill with 0 leter auto data = _rtData->callData; - auto size = _rtData->elems[RuntimeData::CallDataSize].a; + auto size = _rtData->callDataSize; for (auto i = 0; i < 32; ++i) { if (index < size) diff --git a/evmjit/libevmjit/Utils.cpp b/evmjit/libevmjit/Utils.cpp index 0fd9c0e41..5f7f75bfd 100644 --- a/evmjit/libevmjit/Utils.cpp +++ b/evmjit/libevmjit/Utils.cpp @@ -8,32 +8,6 @@ namespace eth namespace jit { -u256 llvm2eth(i256 _i) -{ - u256 u = 0; - u |= _i.d; - u <<= 64; - u |= _i.c; - u <<= 64; - u |= _i.b; - u <<= 64; - u |= _i.a; - return u; -} - -i256 eth2llvm(u256 _u) -{ - i256 i; - u256 mask = 0xFFFFFFFFFFFFFFFF; - i.a = static_cast(_u & mask); - _u >>= 64; - i.b = static_cast(_u & mask); - _u >>= 64; - i.c = static_cast(_u & mask); - _u >>= 64; - i.d = static_cast(_u & mask); - return i; -} } } diff --git a/evmjit/libevmjit/Utils.h b/evmjit/libevmjit/Utils.h index f672365c6..7e6133ced 100644 --- a/evmjit/libevmjit/Utils.h +++ b/evmjit/libevmjit/Utils.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "Common.h" namespace dev @@ -14,9 +16,6 @@ struct JIT: public NoteChannel { static const char* name() { return "JIT"; } }; //#define clog(CHANNEL) std::cerr #define clog(CHANNEL) std::ostream(nullptr) -u256 llvm2eth(i256); -i256 eth2llvm(u256); - } } } diff --git a/evmjit/libevmjit/interface.cpp b/evmjit/libevmjit/interface.cpp index eef92d00f..6b0992dd4 100644 --- a/evmjit/libevmjit/interface.cpp +++ b/evmjit/libevmjit/interface.cpp @@ -1,34 +1,41 @@ -#include "interface.h" -#include #include "ExecutionEngine.h" extern "C" { -evmjit_result evmjit_run(void* _data, void* _env) -{ - using namespace dev::eth::jit; +using namespace dev::eth::jit; - auto data = static_cast(_data); +#ifdef _MSC_VER +#define _ALLOW_KEYWORD_MACROS +#define noexcept throw() +#endif - ExecutionEngine engine; +EXPORT void* evmjit_create() noexcept +{ + return new(std::nothrow) ExecutionEngine; +} + +EXPORT void evmjit_destroy(ExecutionEngine* _engine) noexcept +{ + delete _engine; +} - auto codePtr = data->code; - auto codeSize = data->elems[RuntimeData::CodeSize].a; - bytes bytecode; - bytecode.insert(bytecode.end(), codePtr, codePtr + codeSize); +EXPORT int evmjit_run(ExecutionEngine* _engine, RuntimeData* _data, Env* _env) noexcept +{ + try + { + auto codePtr = _data->code; + auto codeSize = _data->codeSize; + bytes bytecode; + bytecode.insert(bytecode.end(), codePtr, codePtr + codeSize); - auto returnCode = engine.run(bytecode, data, static_cast(_env)); - evmjit_result result = {static_cast(returnCode), 0, nullptr}; - if (returnCode == ReturnCode::Return && !engine.returnData.empty()) + auto returnCode = _engine->run(bytecode, _data, _env); + return static_cast(returnCode); + } + catch(...) { - // TODO: Optimized returning data. Allocating memory on client side by callback function might be a good idea - result.returnDataSize = engine.returnData.size(); - result.returnData = std::malloc(result.returnDataSize); - std::memcpy(result.returnData, engine.returnData.data(), result.returnDataSize); + return static_cast(ReturnCode::UnexpectedException); } - - return result; } } diff --git a/evmjit/libevmjit/interface.h b/evmjit/libevmjit/interface.h index 5d9307ab2..4f4d56610 100644 --- a/evmjit/libevmjit/interface.h +++ b/evmjit/libevmjit/interface.h @@ -1,53 +1,12 @@ -#include -#include #ifdef __cplusplus extern "C" { #endif -typedef struct evmjit_result -{ - int32_t returnCode; - uint64_t returnDataSize; - void* returnData; +void* evmjit_create(); +int evmjit_run(void* _jit, void* _data, void* _env); +void evmjit_destroy(void* _jit); -} evmjit_result; - -evmjit_result evmjit_run(void* _data, void* _env); - -// JIT object opaque type -typedef struct evm_jit evm_jit; - -// Contract execution return code -typedef int evm_jit_return_code; - -// Host-endian 256-bit integer type -typedef struct i256 i256; - -struct i256 -{ - char b[33]; -}; - -// Big-endian right aligned 256-bit hash -typedef struct h256 h256; - -// Runtime data struct - must be provided by external language (Go, C++, Python) -typedef struct evm_jit_rt evm_jit_rt; - -// Runtime callback functions - implementations must be provided by external language (Go, C++, Python) -void evm_jit_rt_sload(evm_jit_rt* _rt, i256* _index, i256* _ret); -void evm_jit_rt_sstore(evm_jit_rt* _rt, i256* _index, i256* _value); -void evm_jit_rt_balance(evm_jit_rt* _rt, h256* _address, i256* _ret); -// And so on... - -evm_jit* evm_jit_create(evm_jit_rt* _runtime_data); - -evm_jit_return_code evm_jit_execute(evm_jit* _jit); - -void evm_jit_get_return_data(evm_jit* _jit, char* _return_data_offset, size_t* _return_data_size); - -void evm_jit_destroy(evm_jit* _jit); #ifdef __cplusplus } diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt index df93361a4..51caee3d3 100644 --- a/libdevcore/CMakeLists.txt +++ b/libdevcore/CMakeLists.txt @@ -26,9 +26,8 @@ else() endif() target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARIES}) -target_link_libraries(${EXECUTABLE} ${Boost_DATE_TIME_LIBRARIES}) +#target_link_libraries(${EXECUTABLE} ${Boost_DATE_TIME_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${Boost_SYSTEM_LIBRARIES}) -target_link_libraries(${EXECUTABLE} ${Boost_CHRONO_LIBRARIES}) if (APPLE) find_package(Threads REQUIRED) diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index 428c86d66..365f65202 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -27,7 +27,7 @@ using namespace dev; namespace dev { -char const* Version = "0.8.0"; +char const* Version = "0.8.1"; } diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index dada9dd5d..883e76dd9 100644 --- a/libdevcore/CommonData.h +++ b/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. std::string toString(string32 const& _s); +template +std::vector keysOf(std::map const& _m) +{ + std::vector ret; + for (auto const& i: _m) + ret.push_back(i.first); + return ret; +} + } diff --git a/libdevcrypto/TrieDB.h b/libdevcrypto/TrieDB.h index b42d67aea..3da63edf4 100644 --- a/libdevcrypto/TrieDB.h +++ b/libdevcrypto/TrieDB.h @@ -49,7 +49,7 @@ extern const h256 EmptyTrie; /** * @brief Merkle Patricia Tree "Trie": a modifed base-16 Radix tree. - * This version uses an database backend. + * This version uses a database backend. * Usage: * @code * GenericTrieDB t(&myDB); diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp index 62017796e..3d1bcaa2a 100644 --- a/libethcore/BlockInfo.cpp +++ b/libethcore/BlockInfo.cpp @@ -208,7 +208,7 @@ void BlockInfo::verifyParent(BlockInfo const& _parent) const if (parentHash != _parent.hash) BOOST_THROW_EXCEPTION(InvalidParentHash()); - if (timestamp < _parent.timestamp) + if (timestamp <= _parent.timestamp) BOOST_THROW_EXCEPTION(InvalidTimestamp()); if (number != _parent.number + 1) diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp index c530beef1..7e3305963 100644 --- a/libethcore/CommonEth.cpp +++ b/libethcore/CommonEth.cpp @@ -32,7 +32,7 @@ namespace dev namespace eth { -const unsigned c_protocolVersion = 52; +const unsigned c_protocolVersion = 53; const unsigned c_databaseVersion = 5; static const vector> g_units = diff --git a/libdevcore/CommonJS.cpp b/libethcore/CommonJS.cpp similarity index 99% rename from libdevcore/CommonJS.cpp rename to libethcore/CommonJS.cpp index 980cb9081..3ed4e2796 100644 --- a/libdevcore/CommonJS.cpp +++ b/libethcore/CommonJS.cpp @@ -72,6 +72,31 @@ bytes unpadLeft(bytes _b) return _b; } +std::string fromRaw(h256 _n, unsigned* _inc) +{ + if (_n) + { + std::string s((char const*)_n.data(), 32); + auto l = s.find_first_of('\0'); + if (!l) + return ""; + if (l != std::string::npos) + { + auto p = s.find_first_not_of('\0', l); + if (!(p == std::string::npos || (_inc && p == 31))) + return ""; + if (_inc) + *_inc = (byte)s[31]; + s.resize(l); + } + for (auto i: s) + if (i < 32) + return ""; + return s; + } + return ""; +} + std::string prettyU256(u256 _n) { unsigned inc = 0; @@ -98,31 +123,6 @@ std::string prettyU256(u256 _n) return s.str(); } -std::string fromRaw(h256 _n, unsigned* _inc) -{ - if (_n) - { - std::string s((char const*)_n.data(), 32); - auto l = s.find_first_of('\0'); - if (!l) - return ""; - if (l != std::string::npos) - { - auto p = s.find_first_not_of('\0', l); - if (!(p == std::string::npos || (_inc && p == 31))) - return ""; - if (_inc) - *_inc = (byte)s[31]; - s.resize(l); - } - for (auto i: s) - if (i < 32) - return ""; - return s; - } - return ""; -} - Address fromString(std::string const& _sn) { if (_sn.size() == 40) @@ -132,3 +132,4 @@ Address fromString(std::string const& _sn) } } + diff --git a/libdevcore/CommonJS.h b/libethcore/CommonJS.h similarity index 94% rename from libdevcore/CommonJS.h rename to libethcore/CommonJS.h index 59e6c1d34..ccc3b3103 100644 --- a/libdevcore/CommonJS.h +++ b/libethcore/CommonJS.h @@ -24,9 +24,11 @@ #pragma once #include -#include -#include "Common.h" -#include "CommonData.h" +#include +#include +#include +#include +#include "CommonEth.h" namespace dev { @@ -94,16 +96,8 @@ template boost::multiprecision::number(_s); } -inline Public jsToPublic(std::string const& _s) { return jsToFixed(_s); } -inline Secret jsToSecret(std::string const& _s) { return jsToFixed(_s); } inline u256 jsToU256(std::string const& _s) { return jsToInt<32>(_s); } -inline std::string jsToBinary(std::string const& _s) -{ - return dev::toString(unpadded(jsToBytes(_s))); -} - inline std::string jsToDecimal(std::string const& _s) { return dev::toString(jsToU256(_s)); @@ -125,6 +119,29 @@ inline double jsFromFixed(std::string const& _s) return (double)jsToU256(_s) / (double)(dev::u256(1) << 128); } +} + +// devcrypto + +#include + +namespace dev +{ + +inline Public jsToPublic(std::string const& _s) { return jsToFixed(_s); } +inline Secret jsToSecret(std::string const& _s) { return jsToFixed(_s); } + +} + + +// ethcore +namespace dev +{ +namespace eth +{ + +inline Address jsToAddress(std::string const& _s) { return jsToFixed(_s); } + struct TransactionSkeleton { Address from; @@ -136,3 +153,4 @@ struct TransactionSkeleton }; } +} diff --git a/libethereum/All.h b/libethereum/All.h index 11e0d55f3..24109cb87 100644 --- a/libethereum/All.h +++ b/libethereum/All.h @@ -1,7 +1,7 @@ #pragma once #include "Account.h" -#include "BlockChain.h" +#include "CanonBlockChain.h" #include "Client.h" #include "Defaults.h" #include "Executive.h" diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 1addcbc14..11ab08ce6 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -53,30 +53,6 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc) return _out; } -std::map const& dev::eth::genesisState() -{ - static std::map s_ret; - if (s_ret.empty()) - { - // Initialise. - for (auto i: vector({ - "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 BlockChain::s_genesis; -boost::shared_mutex BlockChain::x_genesis; - ldb::Slice dev::eth::toSlice(h256 _h, unsigned _sub) { #if ALL_COMPILERS_ARE_CPP11_COMPLIANT @@ -91,31 +67,11 @@ ldb::Slice dev::eth::toSlice(h256 _h, unsigned _sub) #endif } -bytes BlockChain::createGenesisBlock() -{ - RLPStream block(3); - - h256 stateRoot; - { - MemoryDB db; - TrieDB state(&db); - state.init(); - dev::eth::commit(genesisState(), db, state); - stateRoot = state.root(); - } - - block.appendList(14) - << h256() << EmptyListSHA3 << h160() << stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_genesisDifficulty << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42)); - block.appendRaw(RLPEmptyList); - block.appendRaw(RLPEmptyList); - return block.out(); -} - -BlockChain::BlockChain(std::string _path, bool _killExisting) +BlockChain::BlockChain(bytes const& _genesisBlock, std::string _path, bool _killExisting) { // Initialise with the genesis as the last block on the longest chain. - m_genesisHash = BlockChain::genesis().hash; - m_genesisBlock = BlockChain::createGenesisBlock(); + m_genesisBlock = _genesisBlock; + m_genesisHash = sha3(RLP(m_genesisBlock)[0].data()); open(_path, _killExisting); } @@ -353,7 +309,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) } #endif -// cnote << "Parent " << bi.parentHash << " has " << details(bi.parentHash).children.size() << " children."; + // cnote << "Parent " << bi.parentHash << " has " << details(bi.parentHash).children.size() << " children."; h256s ret; // This might be the new best block... @@ -377,7 +333,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db) h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, bool _post) const { -// cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged(); + // cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged(); if (!_from || !_to) { return h256s(); @@ -386,14 +342,14 @@ h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, boo h256s back; unsigned fn = details(_from).number; unsigned tn = details(_to).number; -// cdebug << "treeRoute" << fn << "..." << tn; + // cdebug << "treeRoute" << fn << "..." << tn; while (fn > tn) { if (_pre) ret.push_back(_from); _from = details(_from).parent; fn--; -// cdebug << "from:" << fn << _from.abridged(); + // cdebug << "from:" << fn << _from.abridged(); } while (fn < tn) { @@ -401,7 +357,7 @@ h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, boo back.push_back(_to); _to = details(_to).parent; tn--; -// cdebug << "to:" << tn << _to.abridged(); + // cdebug << "to:" << tn << _to.abridged(); } while (_from != _to) { @@ -415,7 +371,7 @@ h256s BlockChain::treeRoute(h256 _from, h256 _to, h256* o_common, bool _pre, boo back.push_back(_to); fn--; tn--; -// cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged(); + // cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged(); } if (o_common) *o_common = _from; diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 4e5d66c9a..50ad78dac 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -69,8 +69,7 @@ ldb::Slice toSlice(h256 _h, unsigned _sub = 0); class BlockChain { public: - BlockChain(bool _killExisting = false): BlockChain(std::string(), _killExisting) {} - BlockChain(std::string _path, bool _killExisting = false); + BlockChain(bytes const& _genesisBlock, std::string _path, bool _killExisting); ~BlockChain(); void reopen(std::string _path, bool _killExisting = false) { close(); open(_path, _killExisting); } @@ -82,7 +81,7 @@ public: /// Sync the chain with any incoming blocks. All blocks should, if processed in order h256s sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max); - /// Attempt to import the given block directly into the BlockChain and sync with the state DB. + /// Attempt to import the given block directly into the CanonBlockChain and sync with the state DB. /// @returns the block hashes of any blocks that came into/went out of the canonical block chain. h256s attemptImport(bytes const& _block, OverlayDB const& _stateDB) noexcept; @@ -131,13 +130,6 @@ public: /// togther with all their quoted uncles. h256Set allUnclesFrom(h256 _parent) const; - /// @returns the genesis block header. - static BlockInfo const& genesis() { UpgradableGuard l(x_genesis); if (!s_genesis) { auto gb = createGenesisBlock(); UpgradeGuard ul(l); s_genesis.reset(new BlockInfo); s_genesis->populate(&gb); } return *s_genesis; } - - /// @returns the genesis block as its RLP-encoded byte array. - /// @note This is slow as it's constructed anew each call. Consider genesis() instead. - static bytes createGenesisBlock(); - /** @returns the hash of all blocks between @a _from and @a _to, all blocks are ordered first by a number of * blocks that are parent-to-child, then two sibling blocks, then a number of blocks that are child-to-parent. * @@ -171,7 +163,7 @@ private: m_extrasDB->Get(m_readOptions, toSlice(_h, N), &s); if (s.empty()) { - // cout << "Not found in DB: " << _h << endl; +// cout << "Not found in DB: " << _h << endl; return _n; } @@ -208,10 +200,6 @@ private: ldb::WriteOptions m_writeOptions; friend std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc); - - /// Static genesis info and its lock. - static boost::shared_mutex x_genesis; - static std::unique_ptr s_genesis; }; std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc); diff --git a/libethereum/CanonBlockChain.cpp b/libethereum/CanonBlockChain.cpp new file mode 100644 index 000000000..fc7107154 --- /dev/null +++ b/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 . +*/ +/** @file CanonBlockChain.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "CanonBlockChain.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "State.h" +#include "Defaults.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +#define ETH_CATCH 1 + +std::map const& dev::eth::genesisState() +{ + static std::map s_ret; + if (s_ret.empty()) + { + // Initialise. + for (auto i: vector({ + "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 CanonBlockChain::s_genesis; +boost::shared_mutex CanonBlockChain::x_genesis; + +bytes CanonBlockChain::createGenesisBlock() +{ + RLPStream block(3); + + h256 stateRoot; + { + MemoryDB db; + TrieDB 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) +{ +} diff --git a/libethereum/CanonBlockChain.h b/libethereum/CanonBlockChain.h new file mode 100644 index 000000000..d9739097a --- /dev/null +++ b/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 . +*/ +/** @file CanonBlockChain.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#pragma warning(push) +#pragma warning(disable: 4100 4267) +#include +#pragma warning(pop) + +#include +#include +#include +#include +#include +#include +#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 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 s_genesis; +}; + +} +} diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 9cfd18b2d..70b0cf26a 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -182,7 +182,7 @@ unsigned Client::installWatch(h256 _h) Guard l(m_filterLock); ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0; m_watches[ret] = ClientWatch(_h); - cwatch << "+++" << ret << _h; + cwatch << "+++" << ret << _h.abridged(); } auto ch = logs(ret); if (ch.empty()) @@ -200,7 +200,10 @@ unsigned Client::installWatch(LogFilter const& _f) { Guard l(m_filterLock); if (!m_filters.count(h)) + { + cwatch << "FFF" << _f << h.abridged(); m_filters.insert(make_pair(h, _f)); + } } return installWatch(h); } @@ -220,13 +223,17 @@ void Client::uninstallWatch(unsigned _i) auto fit = m_filters.find(id); if (fit != m_filters.end()) if (!--fit->second.refCount) + { + cwatch << "*X*" << fit->first << ":" << fit->second.filter; m_filters.erase(fit); + } } void Client::noteChanged(h256Set const& _filters) { Guard l(m_filterLock); -// cnote << "noteChanged(" << _filters << ")"; + if (_filters.size()) + cnote << "noteChanged(" << _filters << ")"; // accrue all changes left in each filter into the watches. for (auto& i: m_watches) if (_filters.count(i.second.id)) @@ -247,8 +254,11 @@ LocalisedLogEntries Client::peekWatch(unsigned _watchId) const Guard l(m_filterLock); try { - return m_watches.at(_watchId).changes; + auto& w = m_watches.at(_watchId); + w.lastPoll = chrono::system_clock::now(); + return w.changes; } catch (...) {} + return LocalisedLogEntries(); } @@ -258,7 +268,9 @@ LocalisedLogEntries Client::checkWatch(unsigned _watchId) LocalisedLogEntries ret; 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 (...) {} return ret; @@ -553,6 +565,23 @@ void Client::doWork() cworkout << "WORK"; this_thread::sleep_for(chrono::milliseconds(100)); + if (chrono::system_clock::now() - m_lastGarbageCollection > chrono::seconds(5)) + { + // garbage collect on watches + vector 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::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 diff --git a/libethereum/Client.h b/libethereum/Client.h index d53781d90..48fe1fac3 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -34,7 +34,7 @@ #include #include #include -#include "BlockChain.h" +#include "CanonBlockChain.h" #include "TransactionQueue.h" #include "State.h" #include "CommonNet.h" @@ -89,11 +89,12 @@ static const LocalisedLogEntry InitialChange(SpecialLogEntry, 0); struct ClientWatch { - ClientWatch() {} - explicit ClientWatch(h256 _id): id(_id) {} + ClientWatch(): lastPoll(std::chrono::system_clock::now()) {} + explicit ClientWatch(h256 _id): id(_id), lastPoll(std::chrono::system_clock::now()) {} h256 id; 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; }; @@ -228,7 +229,7 @@ public: /// Get the object representing the current state of Ethereum. dev::eth::State postState() const { ReadGuard l(x_stateDB); return m_postMine; } /// Get the object representing the current canonical blockchain. - BlockChain const& blockChain() const { return m_bc; } + CanonBlockChain const& blockChain() const { return m_bc; } // Mining stuff: @@ -308,7 +309,7 @@ private: State asOf(unsigned _h) const; VersionChecker m_vc; ///< Dummy object to check & update the protocol version. - BlockChain m_bc; ///< Maintains block database. + CanonBlockChain m_bc; ///< Maintains block database. TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported). @@ -328,6 +329,8 @@ private: mutable std::mutex m_filterLock; std::map m_filters; std::map m_watches; + + mutable std::chrono::system_clock::time_point m_lastGarbageCollection; }; } diff --git a/libethereum/LogFilter.cpp b/libethereum/LogFilter.cpp index 79f43f2c6..1784094b0 100644 --- a/libethereum/LogFilter.cpp +++ b/libethereum/LogFilter.cpp @@ -27,6 +27,13 @@ using namespace std; using namespace dev; 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 { _s.appendList(6) << m_addresses << m_topics << m_earliest << m_latest << m_max << m_skip; diff --git a/libethereum/LogFilter.h b/libethereum/LogFilter.h index b99ba8ee0..5f68cd5d0 100644 --- a/libethereum/LogFilter.h +++ b/libethereum/LogFilter.h @@ -28,9 +28,18 @@ namespace dev { + +namespace eth +{ +class LogFilter; +} + namespace eth { +/// Simple stream output for the StateDiff. +std::ostream& operator<<(std::ostream& _out, dev::eth::LogFilter const& _s); + class State; class LogFilter @@ -56,14 +65,17 @@ public: LogFilter withEarliest(int _e) { m_earliest = _e; return *this; } LogFilter withLatest(int _e) { m_latest = _e; return *this; } + friend std::ostream& dev::eth::operator<<(std::ostream& _out, dev::eth::LogFilter const& _s); + private: AddressSet m_addresses; std::array m_topics; int m_earliest = 0; int m_latest = -1; - unsigned m_max; - unsigned m_skip; + unsigned m_max = 10; + unsigned m_skip = 0; }; } + } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index ecb5b2606..fd5f9187f 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -35,6 +35,7 @@ #include "ExtVM.h" #include "Executive.h" #include "CachedAddressState.h" +#include "CanonBlockChain.h" using namespace std; using namespace dev; using namespace dev::eth; @@ -74,18 +75,16 @@ State::State(Address _coinbaseAddress, OverlayDB const& _db, BaseState _bs): paranoia("beginning of normal construction.", true); - if (_bs == BaseState::Genesis) + if (_bs == BaseState::CanonGenesis) { dev::eth::commit(genesisState(), m_db, m_state); m_db.commit(); paranoia("after DB commit of normal construction.", true); - m_previousBlock = BlockChain::genesis(); + m_previousBlock = CanonBlockChain::genesis(); } else - { m_previousBlock.setEmpty(); - } resetCurrent(); @@ -304,7 +303,7 @@ bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi) // (Most recent state dump might end up being genesis.) std::vector chain; - while (bi.stateRoot != BlockChain::genesis().hash && m_db.lookup(bi.stateRoot).empty()) // while we don't have the state root of the latest block... + while (bi.number != 0 && m_db.lookup(bi.stateRoot).empty()) // while we don't have the state root of the latest block... { chain.push_back(bi.hash); // push back for later replay. bi.populate(_bc.block(bi.parentHash)); // move to parent. @@ -698,7 +697,7 @@ void State::commitToMine(BlockChain const& _bc) RLPStream unclesData; unsigned unclesCount = 0; - if (m_previousBlock != BlockChain::genesis()) + if (m_previousBlock.number != 0) { // Find great-uncles (or second-cousins or whatever they are) - children of great-grandparents, great-great-grandparents... that were not already uncles in previous generations. // cout << "Checking " << m_previousBlock.hash << ", parent=" << m_previousBlock.parentHash << endl; @@ -800,7 +799,7 @@ void State::completeMine() ret.appendRaw(m_currentTxs); ret.appendRaw(m_currentUncles); 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() << ")"; // Quickly reset the transactions. diff --git a/libethereum/State.h b/libethereum/State.h index 0a288238d..65a333fb4 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -53,7 +53,7 @@ struct StateTrace: public LogChannel { static const char* name() { return "=S="; struct StateDetail: public LogChannel { static const char* name() { return "/S/"; } static const int verbosity = 14; }; struct StateSafeExceptions: public LogChannel { static const char* name() { return "(S)"; } static const int verbosity = 21; }; -enum class BaseState { Empty, Genesis }; +enum class BaseState { Empty, CanonGenesis }; /** * @brief Model of the current state of the ledger. @@ -68,7 +68,7 @@ class State public: /// Construct state object. - State(Address _coinbaseAddress = Address(), OverlayDB const& _db = OverlayDB(), BaseState _bs = BaseState::Genesis); + State(Address _coinbaseAddress = Address(), OverlayDB const& _db = OverlayDB(), BaseState _bs = BaseState::CanonGenesis); /// Construct state object from arbitrary point in blockchain. State(OverlayDB const& _db, BlockChain const& _bc, h256 _hash); diff --git a/libjsqrc/ethereumjs/bower.json b/libjsqrc/ethereumjs/bower.json index 420618390..168f1b39a 100644 --- a/libjsqrc/ethereumjs/bower.json +++ b/libjsqrc/ethereumjs/bower.json @@ -1,7 +1,7 @@ { "name": "ethereum.js", "namespace": "ethereum", - "version": "0.0.11", + "version": "0.0.13", "description": "Ethereum Compatible JavaScript API", "main": ["./dist/ethereum.js", "./dist/ethereum.min.js"], "dependencies": { diff --git a/libjsqrc/ethereumjs/dist/ethereum.js b/libjsqrc/ethereumjs/dist/ethereum.js index 0f30280f9..4c36a7c71 100644 --- a/libjsqrc/ethereumjs/dist/ethereum.js +++ b/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. @@ -238,9 +238,32 @@ if ("build" !== 'build') {/* 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 = { ETH_PADDING: 32, ETH_SIGNATURE_LENGTH: 4, + ETH_UNITS: ETH_UNITS, 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) { 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', { get: function() { @@ -386,8 +414,12 @@ var addEventsToContract = function (contract, desc, address) { var impl = function () { var params = Array.prototype.slice.call(arguments); 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); + o._onWatchEventResult = function (data) { + var parser = eventImpl.outputParser(e); + return parser(data); + }; return web3.eth.watch(o); }; @@ -455,7 +487,7 @@ var contract = function (address, desc) { 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. @@ -481,6 +513,16 @@ module.exports = contract; var abi = require('./abi'); 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 index = utils.findIndex(inputs, function (input) { return input.name === name; @@ -496,7 +538,7 @@ var inputWithName = function (inputs, name) { var indexedParamsToTopics = function (event, indexed) { // sort keys? 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]; 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' 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. @@ -565,6 +663,8 @@ var Filter = function(options, impl) { if (options.topics) { console.warn('"topics" is deprecated, use "topic" instead'); } + + this._onWatchResult = options._onWatchEventResult; // evaluate lazy properties options = { @@ -583,7 +683,7 @@ var Filter = function(options, impl) { this.callbacks = []; 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* @@ -603,7 +703,8 @@ Filter.prototype.changed = function(callback) { Filter.prototype.trigger = function(messages) { for (var i = 0; i < this.callbacks.length; i++) { 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; -},{"./web3":12}],6:[function(require,module,exports){ +},{"./web3":13}],6:[function(require,module,exports){ /* 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. @@ -815,40 +916,16 @@ var HttpSyncProvider = function (host) { 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) { - var data = formatJsonRpcObject(payload); + //var data = formatJsonRpcObject(payload); var request = new XMLHttpRequest(); request.open('POST', this.host, false); - request.send(JSON.stringify(data)); + request.send(JSON.stringify(payload)); // check request.status - return request.responseText; + var result = request.responseText; + return JSON.parse(result); }; module.exports = HttpSyncProvider; @@ -871,6 +948,73 @@ module.exports = HttpSyncProvider; You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ +/** @file jsonrpc.js + * @authors: + * Marek Kotewicz + * @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 . +*/ /** @file providermanager.js * @authors: * Jeffrey Wilcke @@ -880,7 +1024,9 @@ module.exports = HttpSyncProvider; * @date 2014 */ -var web3 = require('./web3'); // jshint ignore:line +var web3 = require('./web3'); +var jsonrpc = require('./jsonrpc'); + /** * Provider manager object prototype @@ -894,48 +1040,38 @@ var web3 = require('./web3'); // jshint ignore:line var ProviderManager = function() { this.polls = []; this.provider = undefined; - this.id = 1; var self = this; var poll = function () { - if (self.provider) { - self.polls.forEach(function (data) { - data.data._id = self.id; - self.id++; - var result = self.provider.send(data.data); - - result = JSON.parse(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; - } + self.polls.forEach(function (data) { + var result = self.send(data.data); + + if (!(result instanceof Array) || result.length === 0) { + return; + } + + data.callback(result); + }); - data.callback(result.result); - }); - } setTimeout(poll, 1000); }; poll(); }; /// sends outgoing requests +/// @params data - an object with at least 'method' property ProviderManager.prototype.send = function(data) { - - data.args = data.args || []; - data._id = this.id++; + var payload = jsonrpc.toPayload(data.method, data.params); if (this.provider === undefined) { console.error('provider is not set'); return null; } - //TODO: handle error here? - var result = this.provider.send(data); - result = JSON.parse(result); + var result = this.provider.send(payload); - if (result.error) { - console.log(result.error); + if (!jsonrpc.isValidResponse(result)) { + console.log(result); return null; } @@ -966,7 +1102,7 @@ ProviderManager.prototype.stopPolling = function (pollId) { 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. @@ -994,13 +1130,14 @@ var QtSyncProvider = function () { }; 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; -},{}],10:[function(require,module,exports){ +},{}],11:[function(require,module,exports){ /* 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. @@ -1104,6 +1241,8 @@ module.exports = { * @date 2015 */ +var c = require('./const'); + /// Finds first index of array element matching pattern /// @param array /// @param callback pattern @@ -1166,7 +1305,7 @@ var extractDisplayName = function (name) { var extractTypeName = function (name) { /// TODO: make it invulnerable 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 @@ -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 = { findIndex: findIndex, toAscii: toAscii, @@ -1192,11 +1357,12 @@ module.exports = { extractDisplayName: extractDisplayName, extractTypeName: extractTypeName, 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. @@ -1228,28 +1394,6 @@ if ("build" !== 'build') {/* 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 var web3Methods = function () { return [ @@ -1357,8 +1501,8 @@ var setupMethods = function (obj, methods) { var args = Array.prototype.slice.call(arguments); var call = typeof method.call === 'function' ? method.call(args) : method.call; return web3.provider.send({ - call: call, - args: args + method: call, + params: args }); }; }); @@ -1371,15 +1515,15 @@ var setupProperties = function (obj, properties) { var proto = {}; proto.get = function () { return web3.provider.send({ - call: property.getter + method: property.getter }); }; if (property.setter) { proto.set = function (val) { return web3.provider.send({ - call: property.setter, - args: [val] + method: property.setter, + params: [val] }); }; } @@ -1412,29 +1556,7 @@ var web3 = { }, /// used to transform value/string to eth string - /// TODO: use BigNumber.js to parse int - 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]; - }, + toEth: utils.toEth, /// eth object prototype eth: { @@ -1470,11 +1592,6 @@ var web3 = { return new web3.filter(filter, shhWatch); } }, - - /// @returns true if provider is installed - haveProvider: function() { - return !!web3.provider.provider; - } }; /// setups all api methods @@ -1497,14 +1614,13 @@ var shhWatch = { setupMethods(shhWatch, shhWatchMethods()); web3.setProvider = function(provider) { - //provider.onmessage = messageHandler; // there will be no async calls, to remove web3.provider.set(provider); }; module.exports = web3; -},{"./utils":11}],"web3":[function(require,module,exports){ +},{"./utils":12}],"web3":[function(require,module,exports){ var web3 = require('./lib/web3'); var ProviderManager = require('./lib/providermanager'); web3.provider = new ProviderManager(); @@ -1517,7 +1633,7 @@ web3.abi = require('./lib/abi'); 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 \ No newline at end of file diff --git a/libjsqrc/ethereumjs/dist/ethereum.js.map b/libjsqrc/ethereumjs/dist/ethereum.js.map index ae0d47200..e441da86c 100644 --- a/libjsqrc/ethereumjs/dist/ethereum.js.map +++ b/libjsqrc/ethereumjs/dist/ethereum.js.map @@ -9,6 +9,7 @@ "lib/filter.js", "lib/formatters.js", "lib/httpsync.js", + "lib/jsonrpc.js", "lib/providermanager.js", "lib/qtsync.js", "lib/types.js", @@ -17,23 +18,24 @@ "index.js" ], "names": [], - "mappings": "AAAA;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC1JA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9GA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/EA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClTA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA", + "mappings": "AAAA;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACvIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC1JA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/EA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9IA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA", "file": "generated.js", "sourceRoot": "", "sourcesContent": [ "(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o.\n*/\n/** @file abi.js\n * @authors:\n * Marek Kotewicz \n * Gav Wood \n * @date 2014\n */\n\nvar web3 = require('./web3'); \nvar utils = require('./utils');\nvar types = require('./types');\nvar c = require('./const');\nvar f = require('./formatters');\n\nvar displayTypeError = function (type) {\n console.error('parser does not support type: ' + type);\n};\n\n/// This method should be called if we want to check if givent type is an array type\n/// @returns true if it is, otherwise false\nvar arrayType = function (type) {\n return type.slice(-2) === '[]';\n};\n\nvar dynamicTypeBytes = function (type, value) {\n // TODO: decide what to do with array of strings\n if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length.\n return f.formatInputInt(value.length); \n return \"\";\n};\n\nvar inputTypes = types.inputTypes(); \n\n/// Formats input params to bytes\n/// @param abi contract method inputs\n/// @param array of params that will be formatted to bytes\n/// @returns bytes representation of input params\nvar formatInput = function (inputs, params) {\n var bytes = \"\";\n var padding = c.ETH_PADDING * 2;\n\n /// first we iterate in search for dynamic \n inputs.forEach(function (input, index) {\n bytes += dynamicTypeBytes(input.type, params[index]);\n });\n\n inputs.forEach(function (input, i) {\n var typeMatch = false;\n for (var j = 0; j < inputTypes.length && !typeMatch; j++) {\n typeMatch = inputTypes[j].type(inputs[i].type, params[i]);\n }\n if (!typeMatch) {\n displayTypeError(inputs[i].type);\n }\n\n var formatter = inputTypes[j - 1].format;\n var toAppend = \"\";\n\n if (arrayType(inputs[i].type))\n toAppend = params[i].reduce(function (acc, curr) {\n return acc + formatter(curr);\n }, \"\");\n else\n toAppend = formatter(params[i]);\n\n bytes += toAppend; \n });\n return bytes;\n};\n\nvar dynamicBytesLength = function (type) {\n if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length.\n return c.ETH_PADDING * 2;\n return 0;\n};\n\nvar outputTypes = types.outputTypes(); \n\n/// Formats output bytes back to param list\n/// @param contract abi method outputs\n/// @param bytes representtion of output \n/// @returns array of output params \nvar formatOutput = function (outs, output) {\n \n output = output.slice(2);\n var result = [];\n var padding = c.ETH_PADDING * 2;\n\n var dynamicPartLength = outs.reduce(function (acc, curr) {\n return acc + dynamicBytesLength(curr.type);\n }, 0);\n \n var dynamicPart = output.slice(0, dynamicPartLength);\n output = output.slice(dynamicPartLength);\n\n outs.forEach(function (out, i) {\n var typeMatch = false;\n for (var j = 0; j < outputTypes.length && !typeMatch; j++) {\n typeMatch = outputTypes[j].type(outs[i].type);\n }\n\n if (!typeMatch) {\n displayTypeError(outs[i].type);\n }\n\n var formatter = outputTypes[j - 1].format;\n if (arrayType(outs[i].type)) {\n var size = f.formatOutputUInt(dynamicPart.slice(0, padding));\n dynamicPart = dynamicPart.slice(padding);\n var array = [];\n for (var k = 0; k < size; k++) {\n array.push(formatter(output.slice(0, padding))); \n output = output.slice(padding);\n }\n result.push(array);\n }\n else if (types.prefixedType('string')(outs[i].type)) {\n dynamicPart = dynamicPart.slice(padding); \n result.push(formatter(output.slice(0, padding)));\n output = output.slice(padding);\n } else {\n result.push(formatter(output.slice(0, padding)));\n output = output.slice(padding);\n }\n });\n\n return result;\n};\n\n/// @param json abi for contract\n/// @returns input parser object for given json abi\n/// TODO: refactor creating the parser, do not double logic from contract\nvar inputParser = function (json) {\n var parser = {};\n json.forEach(function (method) {\n var displayName = utils.extractDisplayName(method.name); \n var typeName = utils.extractTypeName(method.name);\n\n var impl = function () {\n var params = Array.prototype.slice.call(arguments);\n return formatInput(method.inputs, params);\n };\n \n if (parser[displayName] === undefined) {\n parser[displayName] = impl;\n }\n\n parser[displayName][typeName] = impl;\n });\n\n return parser;\n};\n\n/// @param json abi for contract\n/// @returns output parser for given json abi\nvar outputParser = function (json) {\n var parser = {};\n json.forEach(function (method) {\n\n var displayName = utils.extractDisplayName(method.name); \n var typeName = utils.extractTypeName(method.name);\n\n var impl = function (output) {\n return formatOutput(method.outputs, output);\n };\n\n if (parser[displayName] === undefined) {\n parser[displayName] = impl;\n }\n\n parser[displayName][typeName] = impl;\n });\n\n return parser;\n};\n\n/// @param function/event name for which we want to get signature\n/// @returns signature of function/event with given name\nvar signatureFromAscii = function (name) {\n return web3.sha3(web3.fromAscii(name)).slice(0, 2 + c.ETH_SIGNATURE_LENGTH * 2);\n};\n\nvar eventSignatureFromAscii = function (name) {\n return web3.sha3(web3.fromAscii(name));\n};\n\nmodule.exports = {\n inputParser: inputParser,\n outputParser: outputParser,\n formatInput: formatInput,\n formatOutput: formatOutput,\n signatureFromAscii: signatureFromAscii,\n eventSignatureFromAscii: eventSignatureFromAscii\n};\n\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file const.js\n * @authors:\n * Marek Kotewicz \n * @date 2015\n */\n\n/// required to define ETH_BIGNUMBER_ROUNDING_MODE\nif (\"build\" !== 'build') {/*\n var BigNumber = require('bignumber.js'); // jshint ignore:line\n*/}\n\nmodule.exports = {\n ETH_PADDING: 32,\n ETH_SIGNATURE_LENGTH: 4,\n ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }\n};\n\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file contract.js\n * @authors:\n * Marek Kotewicz \n * @date 2014\n */\n\nvar web3 = require('./web3'); \nvar abi = require('./abi');\nvar utils = require('./utils');\nvar eventImpl = require('./event');\n\nvar exportNatspecGlobals = function (vars) {\n // it's used byt natspec.js\n // TODO: figure out better way to solve this\n web3._currentContractAbi = vars.abi;\n web3._currentContractAddress = vars.address;\n web3._currentContractMethodName = vars.method;\n web3._currentContractMethodParams = vars.params;\n};\n\nvar addFunctionRelatedPropertiesToContract = function (contract) {\n \n contract.call = function (options) {\n contract._isTransact = false;\n contract._options = options;\n return contract;\n };\n\n contract.transact = function (options) {\n contract._isTransact = true;\n contract._options = options;\n return contract;\n };\n\n contract._options = {};\n ['gas', 'gasPrice', 'value', 'from'].forEach(function(p) {\n contract[p] = function (v) {\n contract._options[p] = v;\n return contract;\n };\n });\n\n};\n\nvar addFunctionsToContract = function (contract, desc, address) {\n var inputParser = abi.inputParser(desc);\n var outputParser = abi.outputParser(desc);\n\n // create contract functions\n utils.filterFunctions(desc).forEach(function (method) {\n\n var displayName = utils.extractDisplayName(method.name);\n var typeName = utils.extractTypeName(method.name);\n\n var impl = function () {\n var params = Array.prototype.slice.call(arguments);\n var signature = abi.signatureFromAscii(method.name);\n var parsed = inputParser[displayName][typeName].apply(null, params);\n\n var options = contract._options || {};\n options.to = address;\n options.data = signature + parsed;\n \n var isTransact = contract._isTransact === true || (contract._isTransact !== false && !method.constant);\n var collapse = options.collapse !== false;\n \n // reset\n contract._options = {};\n contract._isTransact = null;\n\n if (isTransact) {\n \n exportNatspecGlobals({\n abi: desc,\n address: address,\n method: method.name,\n params: params\n });\n\n // transactions do not have any output, cause we do not know, when they will be processed\n web3.eth.transact(options);\n return;\n }\n \n var output = web3.eth.call(options);\n var ret = outputParser[displayName][typeName](output);\n if (collapse)\n {\n if (ret.length === 1)\n ret = ret[0];\n else if (ret.length === 0)\n ret = null;\n }\n return ret;\n };\n\n if (contract[displayName] === undefined) {\n contract[displayName] = impl;\n }\n\n contract[displayName][typeName] = impl;\n });\n};\n\nvar addEventRelatedPropertiesToContract = function (contract, desc, address) {\n contract.address = address;\n \n Object.defineProperty(contract, 'topic', {\n get: function() {\n return utils.filterEvents(desc).map(function (e) {\n return abi.eventSignatureFromAscii(e.name);\n });\n }\n });\n\n};\n\nvar addEventsToContract = function (contract, desc, address) {\n // create contract events\n utils.filterEvents(desc).forEach(function (e) {\n\n var impl = function () {\n var params = Array.prototype.slice.call(arguments);\n var signature = abi.eventSignatureFromAscii(e.name);\n var event = eventImpl(address, signature, e);\n var o = event.apply(null, params);\n return web3.eth.watch(o); \n };\n \n // this property should be used by eth.filter to check if object is an event\n impl._isEvent = true;\n\n var displayName = utils.extractDisplayName(e.name);\n var typeName = utils.extractTypeName(e.name);\n\n if (contract[displayName] === undefined) {\n contract[displayName] = impl;\n }\n\n contract[displayName][typeName] = impl;\n\n });\n};\n\n\n/**\n * This method should be called when we want to call / transact some solidity method from javascript\n * it returns an object which has same methods available as solidity contract description\n * usage example: \n *\n * var abi = [{\n * name: 'myMethod',\n * inputs: [{ name: 'a', type: 'string' }],\n * outputs: [{name: 'd', type: 'string' }]\n * }]; // contract abi\n *\n * var myContract = web3.eth.contract('0x0123123121', abi); // creation of contract object\n *\n * myContract.myMethod('this is test string param for call'); // myMethod call (implicit, default)\n * myContract.call().myMethod('this is test string param for call'); // myMethod call (explicit)\n * myContract.transact().myMethod('this is test string param for transact'); // myMethod transact\n *\n * @param address - address of the contract, which should be called\n * @param desc - abi json description of the contract, which is being created\n * @returns contract object\n */\n\nvar contract = function (address, desc) {\n\n // workaround for invalid assumption that method.name is the full anonymous prototype of the method.\n // it's not. it's just the name. the rest of the code assumes it's actually the anonymous\n // prototype, so we make it so as a workaround.\n // TODO: we may not want to modify input params, maybe use copy instead?\n desc.forEach(function (method) {\n if (method.name.indexOf('(') === -1) {\n var displayName = method.name;\n var typeName = method.inputs.map(function(i){return i.type; }).join();\n method.name = displayName + '(' + typeName + ')';\n }\n });\n\n var result = {};\n addFunctionRelatedPropertiesToContract(result);\n addFunctionsToContract(result, desc, address);\n addEventRelatedPropertiesToContract(result, desc, address);\n addEventsToContract(result, desc, address);\n\n return result;\n};\n\nmodule.exports = contract;\n\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file event.js\n * @authors:\n * Marek Kotewicz \n * @date 2014\n */\n\nvar abi = require('./abi');\nvar utils = require('./utils');\n\nvar inputWithName = function (inputs, name) {\n var index = utils.findIndex(inputs, function (input) {\n return input.name === name;\n });\n \n if (index === -1) {\n console.error('indexed param with name ' + name + ' not found');\n return undefined;\n }\n return inputs[index];\n};\n\nvar indexedParamsToTopics = function (event, indexed) {\n // sort keys?\n return Object.keys(indexed).map(function (key) {\n var inputs = [inputWithName(event.inputs, key)];\n\n var value = indexed[key];\n if (value instanceof Array) {\n return value.map(function (v) {\n return abi.formatInput(inputs, [v]);\n }); \n }\n return abi.formatInput(inputs, [value]);\n });\n};\n\nvar implementationOfEvent = function (address, signature, event) {\n \n // valid options are 'earliest', 'latest', 'offset' and 'max', as defined for 'eth.watch'\n return function (indexed, options) {\n var o = options || {};\n o.address = address;\n o.topic = [];\n o.topic.push(signature);\n if (indexed) {\n o.topic = o.topic.concat(indexedParamsToTopics(event, indexed));\n }\n return o;\n };\n};\n\nmodule.exports = implementationOfEvent;\n\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file filter.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nvar web3 = require('./web3'); // jshint ignore:line\n\n/// should be used when we want to watch something\n/// it's using inner polling mechanism and is notified about changes\n/// TODO: change 'options' name cause it may be not the best matching one, since we have events\nvar Filter = function(options, impl) {\n\n if (typeof options !== \"string\") {\n\n // topics property is deprecated, warn about it!\n if (options.topics) {\n console.warn('\"topics\" is deprecated, use \"topic\" instead');\n }\n\n // evaluate lazy properties\n options = {\n to: options.to,\n topic: options.topic,\n earliest: options.earliest,\n latest: options.latest,\n max: options.max,\n skip: options.skip,\n address: options.address\n };\n\n }\n \n this.impl = impl;\n this.callbacks = [];\n\n this.id = impl.newFilter(options);\n web3.provider.startPolling({call: impl.changed, args: [this.id]}, this.id, this.trigger.bind(this));\n};\n\n/// alias for changed*\nFilter.prototype.arrived = function(callback) {\n this.changed(callback);\n};\nFilter.prototype.happened = function(callback) {\n this.changed(callback);\n};\n\n/// gets called when there is new eth/shh message\nFilter.prototype.changed = function(callback) {\n this.callbacks.push(callback);\n};\n\n/// trigger calling new message from people\nFilter.prototype.trigger = function(messages) {\n for (var i = 0; i < this.callbacks.length; i++) {\n for (var j = 0; j < messages.length; j++) {\n this.callbacks[i].call(this, messages[j]);\n }\n }\n};\n\n/// should be called to uninstall current filter\nFilter.prototype.uninstall = function() {\n this.impl.uninstallFilter(this.id);\n web3.provider.stopPolling(this.id);\n};\n\n/// should be called to manually trigger getting latest messages from the client\nFilter.prototype.messages = function() {\n return this.impl.getMessages(this.id);\n};\n\n/// alias for messages\nFilter.prototype.logs = function () {\n return this.messages();\n};\n\nmodule.exports = Filter;\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file const.js\n * @authors:\n * Marek Kotewicz \n * @date 2015\n */\n\n/// required to define ETH_BIGNUMBER_ROUNDING_MODE\nif (\"build\" !== 'build') {/*\n var BigNumber = require('bignumber.js'); // jshint ignore:line\n*/}\n\nvar ETH_UNITS = [ \n 'wei', \n 'Kwei', \n 'Mwei', \n 'Gwei', \n 'szabo', \n 'finney', \n 'ether', \n 'grand', \n 'Mether', \n 'Gether', \n 'Tether', \n 'Pether', \n 'Eether', \n 'Zether', \n 'Yether', \n 'Nether', \n 'Dether', \n 'Vether', \n 'Uether' \n];\n\nmodule.exports = {\n ETH_PADDING: 32,\n ETH_SIGNATURE_LENGTH: 4,\n ETH_UNITS: ETH_UNITS,\n ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }\n};\n\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file contract.js\n * @authors:\n * Marek Kotewicz \n * @date 2014\n */\n\nvar web3 = require('./web3'); \nvar abi = require('./abi');\nvar utils = require('./utils');\nvar eventImpl = require('./event');\n\nvar exportNatspecGlobals = function (vars) {\n // it's used byt natspec.js\n // TODO: figure out better way to solve this\n web3._currentContractAbi = vars.abi;\n web3._currentContractAddress = vars.address;\n web3._currentContractMethodName = vars.method;\n web3._currentContractMethodParams = vars.params;\n};\n\nvar addFunctionRelatedPropertiesToContract = function (contract) {\n \n contract.call = function (options) {\n contract._isTransact = false;\n contract._options = options;\n return contract;\n };\n\n contract.transact = function (options) {\n contract._isTransact = true;\n contract._options = options;\n return contract;\n };\n\n contract._options = {};\n ['gas', 'gasPrice', 'value', 'from'].forEach(function(p) {\n contract[p] = function (v) {\n contract._options[p] = v;\n return contract;\n };\n });\n\n};\n\nvar addFunctionsToContract = function (contract, desc, address) {\n var inputParser = abi.inputParser(desc);\n var outputParser = abi.outputParser(desc);\n\n // create contract functions\n utils.filterFunctions(desc).forEach(function (method) {\n\n var displayName = utils.extractDisplayName(method.name);\n var typeName = utils.extractTypeName(method.name);\n\n var impl = function () {\n var params = Array.prototype.slice.call(arguments);\n var signature = abi.signatureFromAscii(method.name);\n var parsed = inputParser[displayName][typeName].apply(null, params);\n\n var options = contract._options || {};\n options.to = address;\n options.data = signature + parsed;\n \n var isTransact = contract._isTransact === true || (contract._isTransact !== false && !method.constant);\n var collapse = options.collapse !== false;\n \n // reset\n contract._options = {};\n contract._isTransact = null;\n\n if (isTransact) {\n \n exportNatspecGlobals({\n abi: desc,\n address: address,\n method: method.name,\n params: params\n });\n\n // transactions do not have any output, cause we do not know, when they will be processed\n web3.eth.transact(options);\n return;\n }\n \n var output = web3.eth.call(options);\n var ret = outputParser[displayName][typeName](output);\n if (collapse)\n {\n if (ret.length === 1)\n ret = ret[0];\n else if (ret.length === 0)\n ret = null;\n }\n return ret;\n };\n\n if (contract[displayName] === undefined) {\n contract[displayName] = impl;\n }\n\n contract[displayName][typeName] = impl;\n });\n};\n\nvar addEventRelatedPropertiesToContract = function (contract, desc, address) {\n contract.address = address;\n contract._onWatchEventResult = function (data) {\n var matchingEvent = event.getMatchingEvent(utils.filterEvents(desc));\n var parser = eventImpl.outputParser(matchingEvent);\n return parser(data);\n };\n \n Object.defineProperty(contract, 'topic', {\n get: function() {\n return utils.filterEvents(desc).map(function (e) {\n return abi.eventSignatureFromAscii(e.name);\n });\n }\n });\n\n};\n\nvar addEventsToContract = function (contract, desc, address) {\n // create contract events\n utils.filterEvents(desc).forEach(function (e) {\n\n var impl = function () {\n var params = Array.prototype.slice.call(arguments);\n var signature = abi.eventSignatureFromAscii(e.name);\n var event = eventImpl.inputParser(address, signature, e);\n var o = event.apply(null, params);\n o._onWatchEventResult = function (data) {\n var parser = eventImpl.outputParser(e);\n return parser(data);\n };\n return web3.eth.watch(o); \n };\n \n // this property should be used by eth.filter to check if object is an event\n impl._isEvent = true;\n\n var displayName = utils.extractDisplayName(e.name);\n var typeName = utils.extractTypeName(e.name);\n\n if (contract[displayName] === undefined) {\n contract[displayName] = impl;\n }\n\n contract[displayName][typeName] = impl;\n\n });\n};\n\n\n/**\n * This method should be called when we want to call / transact some solidity method from javascript\n * it returns an object which has same methods available as solidity contract description\n * usage example: \n *\n * var abi = [{\n * name: 'myMethod',\n * inputs: [{ name: 'a', type: 'string' }],\n * outputs: [{name: 'd', type: 'string' }]\n * }]; // contract abi\n *\n * var myContract = web3.eth.contract('0x0123123121', abi); // creation of contract object\n *\n * myContract.myMethod('this is test string param for call'); // myMethod call (implicit, default)\n * myContract.call().myMethod('this is test string param for call'); // myMethod call (explicit)\n * myContract.transact().myMethod('this is test string param for transact'); // myMethod transact\n *\n * @param address - address of the contract, which should be called\n * @param desc - abi json description of the contract, which is being created\n * @returns contract object\n */\n\nvar contract = function (address, desc) {\n\n // workaround for invalid assumption that method.name is the full anonymous prototype of the method.\n // it's not. it's just the name. the rest of the code assumes it's actually the anonymous\n // prototype, so we make it so as a workaround.\n // TODO: we may not want to modify input params, maybe use copy instead?\n desc.forEach(function (method) {\n if (method.name.indexOf('(') === -1) {\n var displayName = method.name;\n var typeName = method.inputs.map(function(i){return i.type; }).join();\n method.name = displayName + '(' + typeName + ')';\n }\n });\n\n var result = {};\n addFunctionRelatedPropertiesToContract(result);\n addFunctionsToContract(result, desc, address);\n addEventRelatedPropertiesToContract(result, desc, address);\n addEventsToContract(result, desc, address);\n\n return result;\n};\n\nmodule.exports = contract;\n\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file event.js\n * @authors:\n * Marek Kotewicz \n * @date 2014\n */\n\nvar abi = require('./abi');\nvar utils = require('./utils');\n\n/// filter inputs array && returns only indexed (or not) inputs\n/// @param inputs array\n/// @param bool if result should be an array of indexed params on not\n/// @returns array of (not?) indexed params\nvar filterInputs = function (inputs, indexed) {\n return inputs.filter(function (current) {\n return current.indexed === indexed;\n });\n};\n\nvar inputWithName = function (inputs, name) {\n var index = utils.findIndex(inputs, function (input) {\n return input.name === name;\n });\n \n if (index === -1) {\n console.error('indexed param with name ' + name + ' not found');\n return undefined;\n }\n return inputs[index];\n};\n\nvar indexedParamsToTopics = function (event, indexed) {\n // sort keys?\n return Object.keys(indexed).map(function (key) {\n var inputs = [inputWithName(filterInputs(event.inputs, true), key)];\n\n var value = indexed[key];\n if (value instanceof Array) {\n return value.map(function (v) {\n return abi.formatInput(inputs, [v]);\n }); \n }\n return abi.formatInput(inputs, [value]);\n });\n};\n\nvar inputParser = function (address, signature, event) {\n \n // valid options are 'earliest', 'latest', 'offset' and 'max', as defined for 'eth.watch'\n return function (indexed, options) {\n var o = options || {};\n o.address = address;\n o.topic = [];\n o.topic.push(signature);\n if (indexed) {\n o.topic = o.topic.concat(indexedParamsToTopics(event, indexed));\n }\n return o;\n };\n};\n\nvar getArgumentsObject = function (inputs, indexed, notIndexed) {\n var indexedCopy = indexed.slice();\n var notIndexedCopy = notIndexed.slice();\n return inputs.reduce(function (acc, current) {\n var value;\n if (current.indexed)\n value = indexed.splice(0, 1)[0];\n else\n value = notIndexed.splice(0, 1)[0];\n\n acc[current.name] = value;\n return acc;\n }, {}); \n};\n \nvar outputParser = function (event) {\n \n return function (output) {\n var result = {\n event: utils.extractDisplayName(event.name),\n number: output.number,\n args: {}\n };\n\n if (!output.topic) {\n return result;\n }\n \n var indexedOutputs = filterInputs(event.inputs, true);\n var indexedData = \"0x\" + output.topic.slice(1, output.topic.length).map(function (topic) { return topic.slice(2); }).join(\"\");\n var indexedRes = abi.formatOutput(indexedOutputs, indexedData);\n\n var notIndexedOutputs = filterInputs(event.inputs, false);\n var notIndexedRes = abi.formatOutput(notIndexedOutputs, output.data);\n\n result.args = getArgumentsObject(event.inputs, indexedRes, notIndexedRes);\n\n return result;\n };\n};\n\nvar getMatchingEvent = function (events, payload) {\n for (var i = 0; i < events.length; i++) {\n var signature = abi.eventSignatureFromAscii(events[i].name); \n if (signature === payload.topic[0]) {\n return events[i];\n }\n }\n return undefined;\n};\n\n\nmodule.exports = {\n inputParser: inputParser,\n outputParser: outputParser,\n getMatchingEvent: getMatchingEvent\n};\n\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file filter.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nvar web3 = require('./web3'); // jshint ignore:line\n\n/// should be used when we want to watch something\n/// it's using inner polling mechanism and is notified about changes\n/// TODO: change 'options' name cause it may be not the best matching one, since we have events\nvar Filter = function(options, impl) {\n\n if (typeof options !== \"string\") {\n\n // topics property is deprecated, warn about it!\n if (options.topics) {\n console.warn('\"topics\" is deprecated, use \"topic\" instead');\n }\n \n this._onWatchResult = options._onWatchEventResult;\n\n // evaluate lazy properties\n options = {\n to: options.to,\n topic: options.topic,\n earliest: options.earliest,\n latest: options.latest,\n max: options.max,\n skip: options.skip,\n address: options.address\n };\n\n }\n \n this.impl = impl;\n this.callbacks = [];\n\n this.id = impl.newFilter(options);\n web3.provider.startPolling({method: impl.changed, params: [this.id]}, this.id, this.trigger.bind(this));\n};\n\n/// alias for changed*\nFilter.prototype.arrived = function(callback) {\n this.changed(callback);\n};\nFilter.prototype.happened = function(callback) {\n this.changed(callback);\n};\n\n/// gets called when there is new eth/shh message\nFilter.prototype.changed = function(callback) {\n this.callbacks.push(callback);\n};\n\n/// trigger calling new message from people\nFilter.prototype.trigger = function(messages) {\n for (var i = 0; i < this.callbacks.length; i++) {\n for (var j = 0; j < messages.length; j++) {\n var message = this._onWatchResult ? this._onWatchResult(messages[j]) : messages[j];\n this.callbacks[i].call(this, message);\n }\n }\n};\n\n/// should be called to uninstall current filter\nFilter.prototype.uninstall = function() {\n this.impl.uninstallFilter(this.id);\n web3.provider.stopPolling(this.id);\n};\n\n/// should be called to manually trigger getting latest messages from the client\nFilter.prototype.messages = function() {\n return this.impl.getMessages(this.id);\n};\n\n/// alias for messages\nFilter.prototype.logs = function () {\n return this.messages();\n};\n\nmodule.exports = Filter;\n", "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file formatters.js\n * @authors:\n * Marek Kotewicz \n * @date 2015\n */\n\nif (\"build\" !== 'build') {/*\n var BigNumber = require('bignumber.js'); // jshint ignore:line\n*/}\n\nvar utils = require('./utils');\nvar c = require('./const');\n\n/// @param string string to be padded\n/// @param number of characters that result string should have\n/// @param sign, by default 0\n/// @returns right aligned string\nvar padLeft = function (string, chars, sign) {\n return new Array(chars - string.length + 1).join(sign ? sign : \"0\") + string;\n};\n\n/// Formats input value to byte representation of int\n/// If value is negative, return it's two's complement\n/// If the value is floating point, round it down\n/// @returns right-aligned byte representation of int\nvar formatInputInt = function (value) {\n var padding = c.ETH_PADDING * 2;\n if (value instanceof BigNumber || typeof value === 'number') {\n if (typeof value === 'number')\n value = new BigNumber(value);\n BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE);\n value = value.round();\n\n if (value.lessThan(0)) \n value = new BigNumber(\"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\", 16).plus(value).plus(1);\n value = value.toString(16);\n }\n else if (value.indexOf('0x') === 0)\n value = value.substr(2);\n else if (typeof value === 'string')\n value = formatInputInt(new BigNumber(value));\n else\n value = (+value).toString(16);\n return padLeft(value, padding);\n};\n\n/// Formats input value to byte representation of string\n/// @returns left-algined byte representation of string\nvar formatInputString = function (value) {\n return utils.fromAscii(value, c.ETH_PADDING).substr(2);\n};\n\n/// Formats input value to byte representation of bool\n/// @returns right-aligned byte representation bool\nvar formatInputBool = function (value) {\n return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0');\n};\n\n/// Formats input value to byte representation of real\n/// Values are multiplied by 2^m and encoded as integers\n/// @returns byte representation of real\nvar formatInputReal = function (value) {\n return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); \n};\n\n\n/// Check if input value is negative\n/// @param value is hex format\n/// @returns true if it is negative, otherwise false\nvar signedIsNegative = function (value) {\n return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1';\n};\n\n/// Formats input right-aligned input bytes to int\n/// @returns right-aligned input bytes formatted to int\nvar formatOutputInt = function (value) {\n value = value || \"0\";\n // check if it's negative number\n // it it is, return two's complement\n if (signedIsNegative(value)) {\n return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1);\n }\n return new BigNumber(value, 16);\n};\n\n/// Formats big right-aligned input bytes to uint\n/// @returns right-aligned input bytes formatted to uint\nvar formatOutputUInt = function (value) {\n value = value || \"0\";\n return new BigNumber(value, 16);\n};\n\n/// @returns input bytes formatted to real\nvar formatOutputReal = function (value) {\n return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); \n};\n\n/// @returns input bytes formatted to ureal\nvar formatOutputUReal = function (value) {\n return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); \n};\n\n/// @returns right-aligned input bytes formatted to hex\nvar formatOutputHash = function (value) {\n return \"0x\" + value;\n};\n\n/// @returns right-aligned input bytes formatted to bool\nvar formatOutputBool = function (value) {\n return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false;\n};\n\n/// @returns left-aligned input bytes formatted to ascii string\nvar formatOutputString = function (value) {\n return utils.toAscii(value);\n};\n\n/// @returns right-aligned input bytes formatted to address\nvar formatOutputAddress = function (value) {\n return \"0x\" + value.slice(value.length - 40, value.length);\n};\n\n\nmodule.exports = {\n formatInputInt: formatInputInt,\n formatInputString: formatInputString,\n formatInputBool: formatInputBool,\n formatInputReal: formatInputReal,\n formatOutputInt: formatOutputInt,\n formatOutputUInt: formatOutputUInt,\n formatOutputReal: formatOutputReal,\n formatOutputUReal: formatOutputUReal,\n formatOutputHash: formatOutputHash,\n formatOutputBool: formatOutputBool,\n formatOutputString: formatOutputString,\n formatOutputAddress: formatOutputAddress\n};\n\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file httpsync.js\n * @authors:\n * Marek Kotewicz \n * Marian Oancea \n * @date 2014\n */\n\nif (\"build\" !== 'build') {/*\n var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line\n*/}\n\nvar HttpSyncProvider = function (host) {\n this.handlers = [];\n this.host = host || 'http://localhost:8080';\n};\n\n/// Transforms inner message to proper jsonrpc object\n/// @param inner message object\n/// @returns jsonrpc object\nfunction formatJsonRpcObject(object) {\n return {\n jsonrpc: '2.0',\n method: object.call,\n params: object.args,\n id: object._id\n };\n}\n\n/// Transforms jsonrpc object to inner message\n/// @param incoming jsonrpc message \n/// @returns inner message object\nfunction formatJsonRpcMessage(message) {\n var object = JSON.parse(message);\n\n return {\n _id: object.id,\n data: object.result,\n error: object.error\n };\n}\n\nHttpSyncProvider.prototype.send = function (payload) {\n var data = formatJsonRpcObject(payload);\n \n var request = new XMLHttpRequest();\n request.open('POST', this.host, false);\n request.send(JSON.stringify(data));\n \n // check request.status\n return request.responseText;\n};\n\nmodule.exports = HttpSyncProvider;\n\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file providermanager.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nvar web3 = require('./web3'); // jshint ignore:line\n\n/**\n * Provider manager object prototype\n * It's responsible for passing messages to providers\n * If no provider is set it's responsible for queuing requests\n * It's also responsible for polling the ethereum node for incoming messages\n * Default poll timeout is 12 seconds\n * If we are running ethereum.js inside ethereum browser, there are backend based tools responsible for polling,\n * and provider manager polling mechanism is not used\n */\nvar ProviderManager = function() {\n this.polls = [];\n this.provider = undefined;\n this.id = 1;\n\n var self = this;\n var poll = function () {\n if (self.provider) {\n self.polls.forEach(function (data) {\n data.data._id = self.id;\n self.id++;\n var result = self.provider.send(data.data);\n \n result = JSON.parse(result);\n \n // dont call the callback if result is not an array, or empty one\n if (result.error || !(result.result instanceof Array) || result.result.length === 0) {\n return;\n }\n\n data.callback(result.result);\n });\n }\n setTimeout(poll, 1000);\n };\n poll();\n};\n\n/// sends outgoing requests\nProviderManager.prototype.send = function(data) {\n\n data.args = data.args || [];\n data._id = this.id++;\n\n if (this.provider === undefined) {\n console.error('provider is not set');\n return null; \n }\n\n //TODO: handle error here? \n var result = this.provider.send(data);\n result = JSON.parse(result);\n\n if (result.error) {\n console.log(result.error);\n return null;\n }\n\n return result.result;\n};\n\n/// setups provider, which will be used for sending messages\nProviderManager.prototype.set = function(provider) {\n this.provider = provider;\n};\n\n/// this method is only used, when we do not have native qt bindings and have to do polling on our own\n/// should be callled, on start watching for eth/shh changes\nProviderManager.prototype.startPolling = function (data, pollId, callback) {\n this.polls.push({data: data, id: pollId, callback: callback});\n};\n\n/// should be called to stop polling for certain watch changes\nProviderManager.prototype.stopPolling = function (pollId) {\n for (var i = this.polls.length; i--;) {\n var poll = this.polls[i];\n if (poll.id === pollId) {\n this.polls.splice(i, 1);\n }\n }\n};\n\nmodule.exports = ProviderManager;\n\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file qtsync.js\n * @authors:\n * Marek Kotewicz \n * Marian Oancea \n * @date 2014\n */\n\nvar QtSyncProvider = function () {\n};\n\nQtSyncProvider.prototype.send = function (payload) {\n return navigator.qt.callMethod(JSON.stringify(payload));\n};\n\nmodule.exports = QtSyncProvider;\n\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file httpsync.js\n * @authors:\n * Marek Kotewicz \n * Marian Oancea \n * @date 2014\n */\n\nif (\"build\" !== 'build') {/*\n var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line\n*/}\n\nvar HttpSyncProvider = function (host) {\n this.handlers = [];\n this.host = host || 'http://localhost:8080';\n};\n\nHttpSyncProvider.prototype.send = function (payload) {\n //var data = formatJsonRpcObject(payload);\n \n var request = new XMLHttpRequest();\n request.open('POST', this.host, false);\n request.send(JSON.stringify(payload));\n \n // check request.status\n var result = request.responseText;\n return JSON.parse(result);\n};\n\nmodule.exports = HttpSyncProvider;\n\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file jsonrpc.js\n * @authors:\n * Marek Kotewicz \n * @date 2015\n */\n\nvar messageId = 1;\n\n/// Should be called to valid json create payload object\n/// @param method of jsonrpc call, required\n/// @param params, an array of method params, optional\n/// @returns valid jsonrpc payload object\nvar toPayload = function (method, params) {\n if (!method)\n console.error('jsonrpc method should be specified!');\n\n return {\n jsonrpc: '2.0',\n method: method,\n params: params || [],\n id: messageId++\n }; \n};\n\n/// Should be called to check if jsonrpc response is valid\n/// @returns true if response is valid, otherwise false \nvar isValidResponse = function (response) {\n return !!response &&\n !response.error &&\n response.jsonrpc === '2.0' &&\n typeof response.id === 'number' &&\n response.result !== undefined; // only undefined is not valid json object\n};\n\n/// Should be called to create batch payload object\n/// @param messages, an array of objects with method (required) and params (optional) fields\nvar toBatchPayload = function (messages) {\n return messages.map(function (message) {\n return toPayload(message.method, message.params);\n }); \n};\n\nmodule.exports = {\n toPayload: toPayload,\n isValidResponse: isValidResponse,\n toBatchPayload: toBatchPayload\n};\n\n\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file providermanager.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nvar web3 = require('./web3'); \nvar jsonrpc = require('./jsonrpc');\n\n\n/**\n * Provider manager object prototype\n * It's responsible for passing messages to providers\n * If no provider is set it's responsible for queuing requests\n * It's also responsible for polling the ethereum node for incoming messages\n * Default poll timeout is 12 seconds\n * If we are running ethereum.js inside ethereum browser, there are backend based tools responsible for polling,\n * and provider manager polling mechanism is not used\n */\nvar ProviderManager = function() {\n this.polls = [];\n this.provider = undefined;\n\n var self = this;\n var poll = function () {\n self.polls.forEach(function (data) {\n var result = self.send(data.data);\n\n if (!(result instanceof Array) || result.length === 0) {\n return;\n }\n\n data.callback(result);\n });\n\n setTimeout(poll, 1000);\n };\n poll();\n};\n\n/// sends outgoing requests\n/// @params data - an object with at least 'method' property\nProviderManager.prototype.send = function(data) {\n var payload = jsonrpc.toPayload(data.method, data.params);\n\n if (this.provider === undefined) {\n console.error('provider is not set');\n return null; \n }\n\n var result = this.provider.send(payload);\n\n if (!jsonrpc.isValidResponse(result)) {\n console.log(result);\n return null;\n }\n\n return result.result;\n};\n\n/// setups provider, which will be used for sending messages\nProviderManager.prototype.set = function(provider) {\n this.provider = provider;\n};\n\n/// this method is only used, when we do not have native qt bindings and have to do polling on our own\n/// should be callled, on start watching for eth/shh changes\nProviderManager.prototype.startPolling = function (data, pollId, callback) {\n this.polls.push({data: data, id: pollId, callback: callback});\n};\n\n/// should be called to stop polling for certain watch changes\nProviderManager.prototype.stopPolling = function (pollId) {\n for (var i = this.polls.length; i--;) {\n var poll = this.polls[i];\n if (poll.id === pollId) {\n this.polls.splice(i, 1);\n }\n }\n};\n\nmodule.exports = ProviderManager;\n\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file qtsync.js\n * @authors:\n * Marek Kotewicz \n * Marian Oancea \n * @date 2014\n */\n\nvar QtSyncProvider = function () {\n};\n\nQtSyncProvider.prototype.send = function (payload) {\n var result = navigator.qt.callMethod(JSON.stringify(payload));\n return JSON.parse(result);\n};\n\nmodule.exports = QtSyncProvider;\n\n", "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file types.js\n * @authors:\n * Marek Kotewicz \n * @date 2015\n */\n\nvar f = require('./formatters');\n\n/// @param expected type prefix (string)\n/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false\nvar prefixedType = function (prefix) {\n return function (type) {\n return type.indexOf(prefix) === 0;\n };\n};\n\n/// @param expected type name (string)\n/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false\nvar namedType = function (name) {\n return function (type) {\n return name === type;\n };\n};\n\n/// Setups input formatters for solidity types\n/// @returns an array of input formatters \nvar inputTypes = function () {\n \n return [\n { type: prefixedType('uint'), format: f.formatInputInt },\n { type: prefixedType('int'), format: f.formatInputInt },\n { type: prefixedType('hash'), format: f.formatInputInt },\n { type: prefixedType('string'), format: f.formatInputString }, \n { type: prefixedType('real'), format: f.formatInputReal },\n { type: prefixedType('ureal'), format: f.formatInputReal },\n { type: namedType('address'), format: f.formatInputInt },\n { type: namedType('bool'), format: f.formatInputBool }\n ];\n};\n\n/// Setups output formaters for solidity types\n/// @returns an array of output formatters\nvar outputTypes = function () {\n\n return [\n { type: prefixedType('uint'), format: f.formatOutputUInt },\n { type: prefixedType('int'), format: f.formatOutputInt },\n { type: prefixedType('hash'), format: f.formatOutputHash },\n { type: prefixedType('string'), format: f.formatOutputString },\n { type: prefixedType('real'), format: f.formatOutputReal },\n { type: prefixedType('ureal'), format: f.formatOutputUReal },\n { type: namedType('address'), format: f.formatOutputAddress },\n { type: namedType('bool'), format: f.formatOutputBool }\n ];\n};\n\nmodule.exports = {\n prefixedType: prefixedType,\n namedType: namedType,\n inputTypes: inputTypes,\n outputTypes: outputTypes\n};\n\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file utils.js\n * @authors:\n * Marek Kotewicz \n * @date 2015\n */\n\n/// Finds first index of array element matching pattern\n/// @param array\n/// @param callback pattern\n/// @returns index of element\nvar findIndex = function (array, callback) {\n var end = false;\n var i = 0;\n for (; i < array.length && !end; i++) {\n end = callback(array[i]);\n }\n return end ? i - 1 : -1;\n};\n\n/// @returns ascii string representation of hex value prefixed with 0x\nvar toAscii = function(hex) {\n// Find termination\n var str = \"\";\n var i = 0, l = hex.length;\n if (hex.substring(0, 2) === '0x') {\n i = 2;\n }\n for (; i < l; i+=2) {\n var code = parseInt(hex.substr(i, 2), 16);\n if (code === 0) {\n break;\n }\n\n str += String.fromCharCode(code);\n }\n\n return str;\n};\n \nvar toHex = function(str) {\n var hex = \"\";\n for(var i = 0; i < str.length; i++) {\n var n = str.charCodeAt(i).toString(16);\n hex += n.length < 2 ? '0' + n : n;\n }\n\n return hex;\n};\n\n/// @returns hex representation (prefixed by 0x) of ascii string\nvar fromAscii = function(str, pad) {\n pad = pad === undefined ? 0 : pad;\n var hex = toHex(str);\n while (hex.length < pad*2)\n hex += \"00\";\n return \"0x\" + hex;\n};\n\n/// @returns display name for function/event eg. multiply(uint256) -> multiply\nvar extractDisplayName = function (name) {\n var length = name.indexOf('('); \n return length !== -1 ? name.substr(0, length) : name;\n};\n\n/// @returns overloaded part of function/event name\nvar extractTypeName = function (name) {\n /// TODO: make it invulnerable\n var length = name.indexOf('(');\n return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)) : \"\";\n};\n\n/// Filters all function from input abi\n/// @returns abi array with filtered objects of type 'function'\nvar filterFunctions = function (json) {\n return json.filter(function (current) {\n return current.type === 'function'; \n }); \n};\n\n/// Filters all events form input abi\n/// @returns abi array with filtered objects of type 'event'\nvar filterEvents = function (json) {\n return json.filter(function (current) {\n return current.type === 'event';\n });\n};\n\nmodule.exports = {\n findIndex: findIndex,\n toAscii: toAscii,\n fromAscii: fromAscii,\n extractDisplayName: extractDisplayName,\n extractTypeName: extractTypeName,\n filterFunctions: filterFunctions,\n filterEvents: filterEvents\n};\n\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file web3.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nif (\"build\" !== 'build') {/*\n var BigNumber = require('bignumber.js');\n*/}\n\nvar utils = require('./utils');\n\nvar ETH_UNITS = [ \n 'wei', \n 'Kwei', \n 'Mwei', \n 'Gwei', \n 'szabo', \n 'finney', \n 'ether', \n 'grand', \n 'Mether', \n 'Gether', \n 'Tether', \n 'Pether', \n 'Eether', \n 'Zether', \n 'Yether', \n 'Nether', \n 'Dether', \n 'Vether', \n 'Uether' \n];\n\n/// @returns an array of objects describing web3 api methods\nvar web3Methods = function () {\n return [\n { name: 'sha3', call: 'web3_sha3' }\n ];\n};\n\n/// @returns an array of objects describing web3.eth api methods\nvar ethMethods = function () {\n var blockCall = function (args) {\n return typeof args[0] === \"string\" ? \"eth_blockByHash\" : \"eth_blockByNumber\";\n };\n\n var transactionCall = function (args) {\n return typeof args[0] === \"string\" ? 'eth_transactionByHash' : 'eth_transactionByNumber';\n };\n\n var uncleCall = function (args) {\n return typeof args[0] === \"string\" ? 'eth_uncleByHash' : 'eth_uncleByNumber';\n };\n\n var methods = [\n { name: 'balanceAt', call: 'eth_balanceAt' },\n { name: 'stateAt', call: 'eth_stateAt' },\n { name: 'storageAt', call: 'eth_storageAt' },\n { name: 'countAt', call: 'eth_countAt'},\n { name: 'codeAt', call: 'eth_codeAt' },\n { name: 'transact', call: 'eth_transact' },\n { name: 'call', call: 'eth_call' },\n { name: 'block', call: blockCall },\n { name: 'transaction', call: transactionCall },\n { name: 'uncle', call: uncleCall },\n { name: 'compilers', call: 'eth_compilers' },\n { name: 'flush', call: 'eth_flush' },\n { name: 'lll', call: 'eth_lll' },\n { name: 'solidity', call: 'eth_solidity' },\n { name: 'serpent', call: 'eth_serpent' },\n { name: 'logs', call: 'eth_logs' }\n ];\n return methods;\n};\n\n/// @returns an array of objects describing web3.eth api properties\nvar ethProperties = function () {\n return [\n { name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' },\n { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },\n { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },\n { name: 'gasPrice', getter: 'eth_gasPrice' },\n { name: 'accounts', getter: 'eth_accounts' },\n { name: 'peerCount', getter: 'eth_peerCount' },\n { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },\n { name: 'number', getter: 'eth_number'}\n ];\n};\n\n/// @returns an array of objects describing web3.db api methods\nvar dbMethods = function () {\n return [\n { name: 'put', call: 'db_put' },\n { name: 'get', call: 'db_get' },\n { name: 'putString', call: 'db_putString' },\n { name: 'getString', call: 'db_getString' }\n ];\n};\n\n/// @returns an array of objects describing web3.shh api methods\nvar shhMethods = function () {\n return [\n { name: 'post', call: 'shh_post' },\n { name: 'newIdentity', call: 'shh_newIdentity' },\n { name: 'haveIdentity', call: 'shh_haveIdentity' },\n { name: 'newGroup', call: 'shh_newGroup' },\n { name: 'addToGroup', call: 'shh_addToGroup' }\n ];\n};\n\n/// @returns an array of objects describing web3.eth.watch api methods\nvar ethWatchMethods = function () {\n var newFilter = function (args) {\n return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter';\n };\n\n return [\n { name: 'newFilter', call: newFilter },\n { name: 'uninstallFilter', call: 'eth_uninstallFilter' },\n { name: 'getMessages', call: 'eth_filterLogs' }\n ];\n};\n\n/// @returns an array of objects describing web3.shh.watch api methods\nvar shhWatchMethods = function () {\n return [\n { name: 'newFilter', call: 'shh_newFilter' },\n { name: 'uninstallFilter', call: 'shh_uninstallFilter' },\n { name: 'getMessages', call: 'shh_getMessages' }\n ];\n};\n\n/// creates methods in a given object based on method description on input\n/// setups api calls for these methods\nvar setupMethods = function (obj, methods) {\n methods.forEach(function (method) {\n obj[method.name] = function () {\n var args = Array.prototype.slice.call(arguments);\n var call = typeof method.call === 'function' ? method.call(args) : method.call;\n return web3.provider.send({\n call: call,\n args: args\n });\n };\n });\n};\n\n/// creates properties in a given object based on properties description on input\n/// setups api calls for these properties\nvar setupProperties = function (obj, properties) {\n properties.forEach(function (property) {\n var proto = {};\n proto.get = function () {\n return web3.provider.send({\n call: property.getter\n });\n };\n\n if (property.setter) {\n proto.set = function (val) {\n return web3.provider.send({\n call: property.setter,\n args: [val]\n });\n };\n }\n Object.defineProperty(obj, property.name, proto);\n });\n};\n\n/// setups web3 object, and it's in-browser executed methods\nvar web3 = {\n _callbacks: {},\n _events: {},\n providers: {},\n\n /// @returns ascii string representation of hex value prefixed with 0x\n toAscii: utils.toAscii,\n\n /// @returns hex representation (prefixed by 0x) of ascii string\n fromAscii: utils.fromAscii,\n\n /// @returns decimal representaton of hex value prefixed by 0x\n toDecimal: function (val) {\n // remove 0x and place 0, if it's required\n val = val.length > 2 ? val.substring(2) : \"0\";\n return (new BigNumber(val, 16).toString(10));\n },\n\n /// @returns hex representation (prefixed by 0x) of decimal value\n fromDecimal: function (val) {\n return \"0x\" + (new BigNumber(val).toString(16));\n },\n\n /// used to transform value/string to eth string\n /// TODO: use BigNumber.js to parse int\n toEth: function(str) {\n var val = typeof str === \"string\" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;\n var unit = 0;\n var units = ETH_UNITS;\n while (val > 3000 && unit < units.length - 1)\n {\n val /= 1000;\n unit++;\n }\n var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2);\n var replaceFunction = function($0, $1, $2) {\n return $1 + ',' + $2;\n };\n\n while (true) {\n var o = s;\n s = s.replace(/(\\d)(\\d\\d\\d[\\.\\,])/, replaceFunction);\n if (o === s)\n break;\n }\n return s + ' ' + units[unit];\n },\n\n /// eth object prototype\n eth: {\n contractFromAbi: function (abi) {\n return function(addr) {\n // Default to address of Config. TODO: rremove prior to genesis.\n addr = addr || '0xc6d9d2cd449a754c494264e1809c50e34d64562b';\n var ret = web3.eth.contract(addr, abi);\n ret.address = addr;\n return ret;\n };\n },\n\n /// @param filter may be a string, object or event\n /// @param indexed is optional, this is an object with optional event indexed params\n /// @param options is optional, this is an object with optional event options ('max'...)\n watch: function (filter, indexed, options) {\n if (filter._isEvent) {\n return filter(indexed, options);\n }\n return new web3.filter(filter, ethWatch);\n }\n },\n\n /// db object prototype\n db: {},\n\n /// shh object prototype\n shh: {\n \n /// @param filter may be a string, object or event\n watch: function (filter, indexed) {\n return new web3.filter(filter, shhWatch);\n }\n },\n\n /// @returns true if provider is installed\n haveProvider: function() {\n return !!web3.provider.provider;\n }\n};\n\n/// setups all api methods\nsetupMethods(web3, web3Methods());\nsetupMethods(web3.eth, ethMethods());\nsetupProperties(web3.eth, ethProperties());\nsetupMethods(web3.db, dbMethods());\nsetupMethods(web3.shh, shhMethods());\n\nvar ethWatch = {\n changed: 'eth_changed'\n};\n\nsetupMethods(ethWatch, ethWatchMethods());\n\nvar shhWatch = {\n changed: 'shh_changed'\n};\n\nsetupMethods(shhWatch, shhWatchMethods());\n\nweb3.setProvider = function(provider) {\n //provider.onmessage = messageHandler; // there will be no async calls, to remove\n web3.provider.set(provider);\n};\n\nmodule.exports = web3;\n\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file utils.js\n * @authors:\n * Marek Kotewicz \n * @date 2015\n */\n\nvar c = require('./const');\n\n/// Finds first index of array element matching pattern\n/// @param array\n/// @param callback pattern\n/// @returns index of element\nvar findIndex = function (array, callback) {\n var end = false;\n var i = 0;\n for (; i < array.length && !end; i++) {\n end = callback(array[i]);\n }\n return end ? i - 1 : -1;\n};\n\n/// @returns ascii string representation of hex value prefixed with 0x\nvar toAscii = function(hex) {\n// Find termination\n var str = \"\";\n var i = 0, l = hex.length;\n if (hex.substring(0, 2) === '0x') {\n i = 2;\n }\n for (; i < l; i+=2) {\n var code = parseInt(hex.substr(i, 2), 16);\n if (code === 0) {\n break;\n }\n\n str += String.fromCharCode(code);\n }\n\n return str;\n};\n \nvar toHex = function(str) {\n var hex = \"\";\n for(var i = 0; i < str.length; i++) {\n var n = str.charCodeAt(i).toString(16);\n hex += n.length < 2 ? '0' + n : n;\n }\n\n return hex;\n};\n\n/// @returns hex representation (prefixed by 0x) of ascii string\nvar fromAscii = function(str, pad) {\n pad = pad === undefined ? 0 : pad;\n var hex = toHex(str);\n while (hex.length < pad*2)\n hex += \"00\";\n return \"0x\" + hex;\n};\n\n/// @returns display name for function/event eg. multiply(uint256) -> multiply\nvar extractDisplayName = function (name) {\n var length = name.indexOf('('); \n return length !== -1 ? name.substr(0, length) : name;\n};\n\n/// @returns overloaded part of function/event name\nvar extractTypeName = function (name) {\n /// TODO: make it invulnerable\n var length = name.indexOf('(');\n return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)).replace(' ', '') : \"\";\n};\n\n/// Filters all function from input abi\n/// @returns abi array with filtered objects of type 'function'\nvar filterFunctions = function (json) {\n return json.filter(function (current) {\n return current.type === 'function'; \n }); \n};\n\n/// Filters all events form input abi\n/// @returns abi array with filtered objects of type 'event'\nvar filterEvents = function (json) {\n return json.filter(function (current) {\n return current.type === 'event';\n });\n};\n\n/// used to transform value/string to eth string\n/// TODO: use BigNumber.js to parse int\n/// TODO: add tests for it!\nvar toEth = function (str) {\n var val = typeof str === \"string\" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;\n var unit = 0;\n var units = c.ETH_UNITS;\n while (val > 3000 && unit < units.length - 1)\n {\n val /= 1000;\n unit++;\n }\n var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2);\n var replaceFunction = function($0, $1, $2) {\n return $1 + ',' + $2;\n };\n\n while (true) {\n var o = s;\n s = s.replace(/(\\d)(\\d\\d\\d[\\.\\,])/, replaceFunction);\n if (o === s)\n break;\n }\n return s + ' ' + units[unit];\n};\n\nmodule.exports = {\n findIndex: findIndex,\n toAscii: toAscii,\n fromAscii: fromAscii,\n extractDisplayName: extractDisplayName,\n extractTypeName: extractTypeName,\n filterFunctions: filterFunctions,\n filterEvents: filterEvents,\n toEth: toEth\n};\n\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file web3.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nif (\"build\" !== 'build') {/*\n var BigNumber = require('bignumber.js');\n*/}\n\nvar utils = require('./utils');\n\n/// @returns an array of objects describing web3 api methods\nvar web3Methods = function () {\n return [\n { name: 'sha3', call: 'web3_sha3' }\n ];\n};\n\n/// @returns an array of objects describing web3.eth api methods\nvar ethMethods = function () {\n var blockCall = function (args) {\n return typeof args[0] === \"string\" ? \"eth_blockByHash\" : \"eth_blockByNumber\";\n };\n\n var transactionCall = function (args) {\n return typeof args[0] === \"string\" ? 'eth_transactionByHash' : 'eth_transactionByNumber';\n };\n\n var uncleCall = function (args) {\n return typeof args[0] === \"string\" ? 'eth_uncleByHash' : 'eth_uncleByNumber';\n };\n\n var methods = [\n { name: 'balanceAt', call: 'eth_balanceAt' },\n { name: 'stateAt', call: 'eth_stateAt' },\n { name: 'storageAt', call: 'eth_storageAt' },\n { name: 'countAt', call: 'eth_countAt'},\n { name: 'codeAt', call: 'eth_codeAt' },\n { name: 'transact', call: 'eth_transact' },\n { name: 'call', call: 'eth_call' },\n { name: 'block', call: blockCall },\n { name: 'transaction', call: transactionCall },\n { name: 'uncle', call: uncleCall },\n { name: 'compilers', call: 'eth_compilers' },\n { name: 'flush', call: 'eth_flush' },\n { name: 'lll', call: 'eth_lll' },\n { name: 'solidity', call: 'eth_solidity' },\n { name: 'serpent', call: 'eth_serpent' },\n { name: 'logs', call: 'eth_logs' }\n ];\n return methods;\n};\n\n/// @returns an array of objects describing web3.eth api properties\nvar ethProperties = function () {\n return [\n { name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' },\n { name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },\n { name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },\n { name: 'gasPrice', getter: 'eth_gasPrice' },\n { name: 'accounts', getter: 'eth_accounts' },\n { name: 'peerCount', getter: 'eth_peerCount' },\n { name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },\n { name: 'number', getter: 'eth_number'}\n ];\n};\n\n/// @returns an array of objects describing web3.db api methods\nvar dbMethods = function () {\n return [\n { name: 'put', call: 'db_put' },\n { name: 'get', call: 'db_get' },\n { name: 'putString', call: 'db_putString' },\n { name: 'getString', call: 'db_getString' }\n ];\n};\n\n/// @returns an array of objects describing web3.shh api methods\nvar shhMethods = function () {\n return [\n { name: 'post', call: 'shh_post' },\n { name: 'newIdentity', call: 'shh_newIdentity' },\n { name: 'haveIdentity', call: 'shh_haveIdentity' },\n { name: 'newGroup', call: 'shh_newGroup' },\n { name: 'addToGroup', call: 'shh_addToGroup' }\n ];\n};\n\n/// @returns an array of objects describing web3.eth.watch api methods\nvar ethWatchMethods = function () {\n var newFilter = function (args) {\n return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter';\n };\n\n return [\n { name: 'newFilter', call: newFilter },\n { name: 'uninstallFilter', call: 'eth_uninstallFilter' },\n { name: 'getMessages', call: 'eth_filterLogs' }\n ];\n};\n\n/// @returns an array of objects describing web3.shh.watch api methods\nvar shhWatchMethods = function () {\n return [\n { name: 'newFilter', call: 'shh_newFilter' },\n { name: 'uninstallFilter', call: 'shh_uninstallFilter' },\n { name: 'getMessages', call: 'shh_getMessages' }\n ];\n};\n\n/// creates methods in a given object based on method description on input\n/// setups api calls for these methods\nvar setupMethods = function (obj, methods) {\n methods.forEach(function (method) {\n obj[method.name] = function () {\n var args = Array.prototype.slice.call(arguments);\n var call = typeof method.call === 'function' ? method.call(args) : method.call;\n return web3.provider.send({\n method: call,\n params: args\n });\n };\n });\n};\n\n/// creates properties in a given object based on properties description on input\n/// setups api calls for these properties\nvar setupProperties = function (obj, properties) {\n properties.forEach(function (property) {\n var proto = {};\n proto.get = function () {\n return web3.provider.send({\n method: property.getter\n });\n };\n\n if (property.setter) {\n proto.set = function (val) {\n return web3.provider.send({\n method: property.setter,\n params: [val]\n });\n };\n }\n Object.defineProperty(obj, property.name, proto);\n });\n};\n\n/// setups web3 object, and it's in-browser executed methods\nvar web3 = {\n _callbacks: {},\n _events: {},\n providers: {},\n\n /// @returns ascii string representation of hex value prefixed with 0x\n toAscii: utils.toAscii,\n\n /// @returns hex representation (prefixed by 0x) of ascii string\n fromAscii: utils.fromAscii,\n\n /// @returns decimal representaton of hex value prefixed by 0x\n toDecimal: function (val) {\n // remove 0x and place 0, if it's required\n val = val.length > 2 ? val.substring(2) : \"0\";\n return (new BigNumber(val, 16).toString(10));\n },\n\n /// @returns hex representation (prefixed by 0x) of decimal value\n fromDecimal: function (val) {\n return \"0x\" + (new BigNumber(val).toString(16));\n },\n\n /// used to transform value/string to eth string\n toEth: utils.toEth,\n\n /// eth object prototype\n eth: {\n contractFromAbi: function (abi) {\n return function(addr) {\n // Default to address of Config. TODO: rremove prior to genesis.\n addr = addr || '0xc6d9d2cd449a754c494264e1809c50e34d64562b';\n var ret = web3.eth.contract(addr, abi);\n ret.address = addr;\n return ret;\n };\n },\n\n /// @param filter may be a string, object or event\n /// @param indexed is optional, this is an object with optional event indexed params\n /// @param options is optional, this is an object with optional event options ('max'...)\n watch: function (filter, indexed, options) {\n if (filter._isEvent) {\n return filter(indexed, options);\n }\n return new web3.filter(filter, ethWatch);\n }\n },\n\n /// db object prototype\n db: {},\n\n /// shh object prototype\n shh: {\n \n /// @param filter may be a string, object or event\n watch: function (filter, indexed) {\n return new web3.filter(filter, shhWatch);\n }\n },\n};\n\n/// setups all api methods\nsetupMethods(web3, web3Methods());\nsetupMethods(web3.eth, ethMethods());\nsetupProperties(web3.eth, ethProperties());\nsetupMethods(web3.db, dbMethods());\nsetupMethods(web3.shh, shhMethods());\n\nvar ethWatch = {\n changed: 'eth_changed'\n};\n\nsetupMethods(ethWatch, ethWatchMethods());\n\nvar shhWatch = {\n changed: 'shh_changed'\n};\n\nsetupMethods(shhWatch, shhWatchMethods());\n\nweb3.setProvider = function(provider) {\n web3.provider.set(provider);\n};\n\nmodule.exports = web3;\n\n", "var web3 = require('./lib/web3');\nvar ProviderManager = require('./lib/providermanager');\nweb3.provider = new ProviderManager();\nweb3.filter = require('./lib/filter');\nweb3.providers.HttpSyncProvider = require('./lib/httpsync');\nweb3.providers.QtSyncProvider = require('./lib/qtsync');\nweb3.eth.contract = require('./lib/contract');\nweb3.abi = require('./lib/abi');\n\n\nmodule.exports = web3;\n" ] } \ No newline at end of file diff --git a/libjsqrc/ethereumjs/dist/ethereum.min.js b/libjsqrc/ethereumjs/dist/ethereum.min.js index a745a5ed3..df408d3e4 100644 --- a/libjsqrc/ethereumjs/dist/ethereum.min.js +++ b/libjsqrc/ethereumjs/dist/ethereum.min.js @@ -1 +1 @@ -require=function t(e,n,r){function i(a,u){if(!n[a]){if(!e[a]){var f="function"==typeof require&&require;if(!u&&f)return f(a,!0);if(o)return o(a,!0);var s=new Error("Cannot find module '"+a+"'");throw s.code="MODULE_NOT_FOUND",s}var c=n[a]={exports:{}};e[a][0].call(c.exports,function(t){var n=e[a][1][t];return i(n?n:t)},c,c.exports,t,e,n,r)}return n[a].exports}for(var o="function"==typeof require&&require,a=0;av;v++)g.push(m(e.slice(0,r))),e=e.slice(r);n.push(g)}else i.prefixedType("string")(t[s].type)?(c=c.slice(r),n.push(m(e.slice(0,r))),e=e.slice(r)):(n.push(m(e.slice(0,r))),e=e.slice(r))}),n},d=function(t){var e={};return t.forEach(function(t){var n=r.extractDisplayName(t.name),i=r.extractTypeName(t.name),o=function(){var e=Array.prototype.slice.call(arguments);return l(t.inputs,e)};void 0===e[n]&&(e[n]=o),e[n][i]=o}),e},g=function(t){var e={};return t.forEach(function(t){var n=r.extractDisplayName(t.name),i=r.extractTypeName(t.name),o=function(e){return m(t.outputs,e)};void 0===e[n]&&(e[n]=o),e[n][i]=o}),e},v=function(t){return n.sha3(n.fromAscii(t)).slice(0,2+2*o.ETH_SIGNATURE_LENGTH)},y=function(t){return n.sha3(n.fromAscii(t))};e.exports={inputParser:d,outputParser:g,formatInput:l,formatOutput:m,signatureFromAscii:v,eventSignatureFromAscii:y}},{"./const":2,"./formatters":6,"./types":10,"./utils":11,"./web3":12}],2:[function(t,e){e.exports={ETH_PADDING:32,ETH_SIGNATURE_LENGTH:4,ETH_BIGNUMBER_ROUNDING_MODE:{ROUNDING_MODE:BigNumber.ROUND_DOWN}}},{}],3:[function(t,e){var n=t("./web3"),r=t("./abi"),i=t("./utils"),o=t("./event"),a=function(t){n._currentContractAbi=t.abi,n._currentContractAddress=t.address,n._currentContractMethodName=t.method,n._currentContractMethodParams=t.params},u=function(t){t.call=function(e){return t._isTransact=!1,t._options=e,t},t.transact=function(e){return t._isTransact=!0,t._options=e,t},t._options={},["gas","gasPrice","value","from"].forEach(function(e){t[e]=function(n){return t._options[e]=n,t}})},f=function(t,e,o){var u=r.inputParser(e),f=r.outputParser(e);i.filterFunctions(e).forEach(function(s){var c=i.extractDisplayName(s.name),l=i.extractTypeName(s.name),p=function(){var i=Array.prototype.slice.call(arguments),p=r.signatureFromAscii(s.name),h=u[c][l].apply(null,i),m=t._options||{};m.to=o,m.data=p+h;var d=t._isTransact===!0||t._isTransact!==!1&&!s.constant,g=m.collapse!==!1;if(t._options={},t._isTransact=null,d)return a({abi:e,address:o,method:s.name,params:i}),void n.eth.transact(m);var v=n.eth.call(m),y=f[c][l](v);return g&&(1===y.length?y=y[0]:0===y.length&&(y=null)),y};void 0===t[c]&&(t[c]=p),t[c][l]=p})},s=function(t,e,n){t.address=n,Object.defineProperty(t,"topic",{get:function(){return i.filterEvents(e).map(function(t){return r.eventSignatureFromAscii(t.name)})}})},c=function(t,e,a){i.filterEvents(e).forEach(function(e){var u=function(){var t=Array.prototype.slice.call(arguments),i=r.eventSignatureFromAscii(e.name),u=o(a,i,e),f=u.apply(null,t);return n.eth.watch(f)};u._isEvent=!0;var f=i.extractDisplayName(e.name),s=i.extractTypeName(e.name);void 0===t[f]&&(t[f]=u),t[f][s]=u})},l=function(t,e){e.forEach(function(t){if(-1===t.name.indexOf("(")){var e=t.name,n=t.inputs.map(function(t){return t.type}).join();t.name=e+"("+n+")"}});var n={};return u(n),f(n,e,t),s(n,e,t),c(n,e,t),n};e.exports=l},{"./abi":1,"./event":4,"./utils":11,"./web3":12}],4:[function(t,e){var n=t("./abi"),r=t("./utils"),i=function(t,e){var n=r.findIndex(t,function(t){return t.name===e});return-1===n?void console.error("indexed param with name "+e+" not found"):t[n]},o=function(t,e){return Object.keys(e).map(function(r){var o=[i(t.inputs,r)],a=e[r];return a instanceof Array?a.map(function(t){return n.formatInput(o,[t])}):n.formatInput(o,[a])})},a=function(t,e,n){return function(r,i){var a=i||{};return a.address=t,a.topic=[],a.topic.push(e),r&&(a.topic=a.topic.concat(o(n,r))),a}};e.exports=a},{"./abi":1,"./utils":11}],5:[function(t,e){var n=t("./web3"),r=function(t,e){"string"!=typeof t&&(t.topics&&console.warn('"topics" is deprecated, use "topic" instead'),t={to:t.to,topic:t.topic,earliest:t.earliest,latest:t.latest,max:t.max,skip:t.skip,address:t.address}),this.impl=e,this.callbacks=[],this.id=e.newFilter(t),n.provider.startPolling({call:e.changed,args:[this.id]},this.id,this.trigger.bind(this))};r.prototype.arrived=function(t){this.changed(t)},r.prototype.happened=function(t){this.changed(t)},r.prototype.changed=function(t){this.callbacks.push(t)},r.prototype.trigger=function(t){for(var e=0;en;n+=2){var i=parseInt(t.substr(n,2),16);if(0===i)break;e+=String.fromCharCode(i)}return e},i=function(t){for(var e="",n=0;n2?t.substring(2):"0",new BigNumber(t,16).toString(10)},fromDecimal:function(t){return"0x"+new BigNumber(t).toString(16)},toEth:function(t){for(var e="string"==typeof t?0===t.indexOf("0x")?parseInt(t.substr(2),16):parseInt(t):t,n=0,i=r;e>3e3&&nv;v++)g.push(h(e.slice(0,r))),e=e.slice(r);n.push(g)}else i.prefixedType("string")(t[f].type)?(c=c.slice(r),n.push(h(e.slice(0,r))),e=e.slice(r)):(n.push(h(e.slice(0,r))),e=e.slice(r))}),n},d=function(t){var e={};return t.forEach(function(t){var n=r.extractDisplayName(t.name),i=r.extractTypeName(t.name),o=function(){var e=Array.prototype.slice.call(arguments);return l(t.inputs,e)};void 0===e[n]&&(e[n]=o),e[n][i]=o}),e},g=function(t){var e={};return t.forEach(function(t){var n=r.extractDisplayName(t.name),i=r.extractTypeName(t.name),o=function(e){return h(t.outputs,e)};void 0===e[n]&&(e[n]=o),e[n][i]=o}),e},v=function(t){return n.sha3(n.fromAscii(t)).slice(0,2+2*o.ETH_SIGNATURE_LENGTH)},y=function(t){return n.sha3(n.fromAscii(t))};e.exports={inputParser:d,outputParser:g,formatInput:l,formatOutput:h,signatureFromAscii:v,eventSignatureFromAscii:y}},{"./const":2,"./formatters":6,"./types":11,"./utils":12,"./web3":13}],2:[function(t,e){var n=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];e.exports={ETH_PADDING:32,ETH_SIGNATURE_LENGTH:4,ETH_UNITS:n,ETH_BIGNUMBER_ROUNDING_MODE:{ROUNDING_MODE:BigNumber.ROUND_DOWN}}},{}],3:[function(t,e){var n=t("./web3"),r=t("./abi"),i=t("./utils"),o=t("./event"),a=function(t){n._currentContractAbi=t.abi,n._currentContractAddress=t.address,n._currentContractMethodName=t.method,n._currentContractMethodParams=t.params},u=function(t){t.call=function(e){return t._isTransact=!1,t._options=e,t},t.transact=function(e){return t._isTransact=!0,t._options=e,t},t._options={},["gas","gasPrice","value","from"].forEach(function(e){t[e]=function(n){return t._options[e]=n,t}})},s=function(t,e,o){var u=r.inputParser(e),s=r.outputParser(e);i.filterFunctions(e).forEach(function(f){var c=i.extractDisplayName(f.name),l=i.extractTypeName(f.name),p=function(){var i=Array.prototype.slice.call(arguments),p=r.signatureFromAscii(f.name),m=u[c][l].apply(null,i),h=t._options||{};h.to=o,h.data=p+m;var d=t._isTransact===!0||t._isTransact!==!1&&!f.constant,g=h.collapse!==!1;if(t._options={},t._isTransact=null,d)return a({abi:e,address:o,method:f.name,params:i}),void n.eth.transact(h);var v=n.eth.call(h),y=s[c][l](v);return g&&(1===y.length?y=y[0]:0===y.length&&(y=null)),y};void 0===t[c]&&(t[c]=p),t[c][l]=p})},f=function(t,e,n){t.address=n,t._onWatchEventResult=function(t){var n=event.getMatchingEvent(i.filterEvents(e)),r=o.outputParser(n);return r(t)},Object.defineProperty(t,"topic",{get:function(){return i.filterEvents(e).map(function(t){return r.eventSignatureFromAscii(t.name)})}})},c=function(t,e,a){i.filterEvents(e).forEach(function(e){var u=function(){var t=Array.prototype.slice.call(arguments),i=r.eventSignatureFromAscii(e.name),u=o.inputParser(a,i,e),s=u.apply(null,t);return s._onWatchEventResult=function(t){var n=o.outputParser(e);return n(t)},n.eth.watch(s)};u._isEvent=!0;var s=i.extractDisplayName(e.name),f=i.extractTypeName(e.name);void 0===t[s]&&(t[s]=u),t[s][f]=u})},l=function(t,e){e.forEach(function(t){if(-1===t.name.indexOf("(")){var e=t.name,n=t.inputs.map(function(t){return t.type}).join();t.name=e+"("+n+")"}});var n={};return u(n),s(n,e,t),f(n,e,t),c(n,e,t),n};e.exports=l},{"./abi":1,"./event":4,"./utils":12,"./web3":13}],4:[function(t,e){var n=t("./abi"),r=t("./utils"),i=function(t,e){return t.filter(function(t){return t.indexed===e})},o=function(t,e){var n=r.findIndex(t,function(t){return t.name===e});return-1===n?void console.error("indexed param with name "+e+" not found"):t[n]},a=function(t,e){return Object.keys(e).map(function(r){var a=[o(i(t.inputs,!0),r)],u=e[r];return u instanceof Array?u.map(function(t){return n.formatInput(a,[t])}):n.formatInput(a,[u])})},u=function(t,e,n){return function(r,i){var o=i||{};return o.address=t,o.topic=[],o.topic.push(e),r&&(o.topic=o.topic.concat(a(n,r))),o}},s=function(t,e,n){e.slice(),n.slice();return t.reduce(function(t,r){var i;return i=r.indexed?e.splice(0,1)[0]:n.splice(0,1)[0],t[r.name]=i,t},{})},f=function(t){return function(e){var o={event:r.extractDisplayName(t.name),number:e.number,args:{}};if(!e.topic)return o;var a=i(t.inputs,!0),u="0x"+e.topic.slice(1,e.topic.length).map(function(t){return t.slice(2)}).join(""),f=n.formatOutput(a,u),c=i(t.inputs,!1),l=n.formatOutput(c,e.data);return o.args=s(t.inputs,f,l),o}},c=function(t,e){for(var r=0;rn;n+=2){var i=parseInt(t.substr(n,2),16);if(0===i)break;e+=String.fromCharCode(i)}return e},o=function(t){for(var e="",n=0;n3e3&&r2?t.substring(2):"0",new BigNumber(t,16).toString(10)},fromDecimal:function(t){return"0x"+new BigNumber(t).toString(16)},toEth:n.toEth,eth:{contractFromAbi:function(t){return function(e){e=e||"0xc6d9d2cd449a754c494264e1809c50e34d64562b";var n=p.eth.contract(e,t);return n.address=e,n}},watch:function(t,e,n){return t._isEvent?t(e,n):new p.filter(t,m)}},db:{},shh:{watch:function(t){return new p.filter(t,h)}}};c(p,r()),c(p.eth,i()),l(p.eth,o()),c(p.db,a()),c(p.shh,u());var m={changed:"eth_changed"};c(m,s());var h={changed:"shh_changed"};c(h,f()),p.setProvider=function(t){p.provider.set(t)},e.exports=p},{"./utils":12}],web3:[function(t,e){var n=t("./lib/web3"),r=t("./lib/providermanager");n.provider=new r,n.filter=t("./lib/filter"),n.providers.HttpSyncProvider=t("./lib/httpsync"),n.providers.QtSyncProvider=t("./lib/qtsync"),n.eth.contract=t("./lib/contract"),n.abi=t("./lib/abi"),e.exports=n},{"./lib/abi":1,"./lib/contract":3,"./lib/filter":5,"./lib/httpsync":7,"./lib/providermanager":9,"./lib/qtsync":10,"./lib/web3":13}]},{},["web3"]); \ No newline at end of file diff --git a/libjsqrc/ethereumjs/example/event_inc.html b/libjsqrc/ethereumjs/example/event_inc.html new file mode 100644 index 000000000..17df9d681 --- /dev/null +++ b/libjsqrc/ethereumjs/example/event_inc.html @@ -0,0 +1,66 @@ + + + + + + + + + +
+ +
+
+ +
+
+
+ + diff --git a/libjsqrc/ethereumjs/lib/const.js b/libjsqrc/ethereumjs/lib/const.js index 22f6dc690..8a17b794d 100644 --- a/libjsqrc/ethereumjs/lib/const.js +++ b/libjsqrc/ethereumjs/lib/const.js @@ -25,9 +25,32 @@ if (process.env.NODE_ENV !== 'build') { 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 = { ETH_PADDING: 32, ETH_SIGNATURE_LENGTH: 4, + ETH_UNITS: ETH_UNITS, ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN } }; diff --git a/libjsqrc/ethereumjs/lib/contract.js b/libjsqrc/ethereumjs/lib/contract.js index 748844fec..a0525bd9d 100644 --- a/libjsqrc/ethereumjs/lib/contract.js +++ b/libjsqrc/ethereumjs/lib/contract.js @@ -120,6 +120,11 @@ var addFunctionsToContract = function (contract, desc, address) { var addEventRelatedPropertiesToContract = function (contract, desc, 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', { get: function() { @@ -138,8 +143,12 @@ var addEventsToContract = function (contract, desc, address) { var impl = function () { var params = Array.prototype.slice.call(arguments); 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); + o._onWatchEventResult = function (data) { + var parser = eventImpl.outputParser(e); + return parser(data); + }; return web3.eth.watch(o); }; diff --git a/libjsqrc/ethereumjs/lib/event.js b/libjsqrc/ethereumjs/lib/event.js index 812ef9115..0c41e0a39 100644 --- a/libjsqrc/ethereumjs/lib/event.js +++ b/libjsqrc/ethereumjs/lib/event.js @@ -23,6 +23,16 @@ var abi = require('./abi'); 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 index = utils.findIndex(inputs, function (input) { return input.name === name; @@ -38,7 +48,7 @@ var inputWithName = function (inputs, name) { var indexedParamsToTopics = function (event, indexed) { // sort keys? 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]; 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' 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 +}; diff --git a/libjsqrc/ethereumjs/lib/filter.js b/libjsqrc/ethereumjs/lib/filter.js index 25b6d9a4a..6ab2b7edc 100644 --- a/libjsqrc/ethereumjs/lib/filter.js +++ b/libjsqrc/ethereumjs/lib/filter.js @@ -36,6 +36,8 @@ var Filter = function(options, impl) { if (options.topics) { console.warn('"topics" is deprecated, use "topic" instead'); } + + this._onWatchResult = options._onWatchEventResult; // evaluate lazy properties options = { @@ -54,7 +56,7 @@ var Filter = function(options, impl) { this.callbacks = []; 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* @@ -74,7 +76,8 @@ Filter.prototype.changed = function(callback) { Filter.prototype.trigger = function(messages) { for (var i = 0; i < this.callbacks.length; i++) { 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); } } }; diff --git a/libjsqrc/ethereumjs/lib/httpsync.js b/libjsqrc/ethereumjs/lib/httpsync.js index a638cfe94..06e410ca8 100644 --- a/libjsqrc/ethereumjs/lib/httpsync.js +++ b/libjsqrc/ethereumjs/lib/httpsync.js @@ -30,40 +30,16 @@ var HttpSyncProvider = function (host) { 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) { - var data = formatJsonRpcObject(payload); + //var data = formatJsonRpcObject(payload); var request = new XMLHttpRequest(); request.open('POST', this.host, false); - request.send(JSON.stringify(data)); + request.send(JSON.stringify(payload)); // check request.status - return request.responseText; + var result = request.responseText; + return JSON.parse(result); }; module.exports = HttpSyncProvider; diff --git a/libjsqrc/ethereumjs/lib/jsonrpc.js b/libjsqrc/ethereumjs/lib/jsonrpc.js new file mode 100644 index 000000000..b2d6922c0 --- /dev/null +++ b/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 . +*/ +/** @file jsonrpc.js + * @authors: + * Marek Kotewicz + * @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 +}; + + diff --git a/libjsqrc/ethereumjs/lib/providermanager.js b/libjsqrc/ethereumjs/lib/providermanager.js index 25cd14288..55b072634 100644 --- a/libjsqrc/ethereumjs/lib/providermanager.js +++ b/libjsqrc/ethereumjs/lib/providermanager.js @@ -23,7 +23,9 @@ * @date 2014 */ -var web3 = require('./web3'); // jshint ignore:line +var web3 = require('./web3'); +var jsonrpc = require('./jsonrpc'); + /** * Provider manager object prototype @@ -37,48 +39,38 @@ var web3 = require('./web3'); // jshint ignore:line var ProviderManager = function() { this.polls = []; this.provider = undefined; - this.id = 1; var self = this; var poll = function () { - if (self.provider) { - self.polls.forEach(function (data) { - data.data._id = self.id; - self.id++; - var result = self.provider.send(data.data); - - result = JSON.parse(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); - }); - } + self.polls.forEach(function (data) { + var result = self.send(data.data); + + if (!(result instanceof Array) || result.length === 0) { + return; + } + + data.callback(result); + }); + setTimeout(poll, 1000); }; poll(); }; /// sends outgoing requests +/// @params data - an object with at least 'method' property ProviderManager.prototype.send = function(data) { - - data.args = data.args || []; - data._id = this.id++; + var payload = jsonrpc.toPayload(data.method, data.params); if (this.provider === undefined) { console.error('provider is not set'); return null; } - //TODO: handle error here? - var result = this.provider.send(data); - result = JSON.parse(result); + var result = this.provider.send(payload); - if (result.error) { - console.log(result.error); + if (!jsonrpc.isValidResponse(result)) { + console.log(result); return null; } diff --git a/libjsqrc/ethereumjs/lib/qtsync.js b/libjsqrc/ethereumjs/lib/qtsync.js index a287a7172..75dcb43ab 100644 --- a/libjsqrc/ethereumjs/lib/qtsync.js +++ b/libjsqrc/ethereumjs/lib/qtsync.js @@ -25,7 +25,8 @@ var QtSyncProvider = function () { }; 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; diff --git a/libjsqrc/ethereumjs/lib/utils.js b/libjsqrc/ethereumjs/lib/utils.js index 5cd6ec8d6..7cc1917e1 100644 --- a/libjsqrc/ethereumjs/lib/utils.js +++ b/libjsqrc/ethereumjs/lib/utils.js @@ -20,6 +20,8 @@ * @date 2015 */ +var c = require('./const'); + /// Finds first index of array element matching pattern /// @param array /// @param callback pattern @@ -82,7 +84,7 @@ var extractDisplayName = function (name) { var extractTypeName = function (name) { /// TODO: make it invulnerable 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 @@ -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 = { findIndex: findIndex, toAscii: toAscii, @@ -108,6 +136,7 @@ module.exports = { extractDisplayName: extractDisplayName, extractTypeName: extractTypeName, filterFunctions: filterFunctions, - filterEvents: filterEvents + filterEvents: filterEvents, + toEth: toEth }; diff --git a/libjsqrc/ethereumjs/lib/web3.js b/libjsqrc/ethereumjs/lib/web3.js index c3126afc4..41df75051 100644 --- a/libjsqrc/ethereumjs/lib/web3.js +++ b/libjsqrc/ethereumjs/lib/web3.js @@ -29,28 +29,6 @@ if (process.env.NODE_ENV !== 'build') { 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 var web3Methods = function () { return [ @@ -158,8 +136,8 @@ var setupMethods = function (obj, methods) { var args = Array.prototype.slice.call(arguments); var call = typeof method.call === 'function' ? method.call(args) : method.call; return web3.provider.send({ - call: call, - args: args + method: call, + params: args }); }; }); @@ -172,15 +150,15 @@ var setupProperties = function (obj, properties) { var proto = {}; proto.get = function () { return web3.provider.send({ - call: property.getter + method: property.getter }); }; if (property.setter) { proto.set = function (val) { return web3.provider.send({ - call: property.setter, - args: [val] + method: property.setter, + params: [val] }); }; } @@ -213,29 +191,7 @@ var web3 = { }, /// used to transform value/string to eth string - /// TODO: use BigNumber.js to parse int - 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]; - }, + toEth: utils.toEth, /// eth object prototype eth: { @@ -271,11 +227,6 @@ var web3 = { return new web3.filter(filter, shhWatch); } }, - - /// @returns true if provider is installed - haveProvider: function() { - return !!web3.provider.provider; - } }; /// setups all api methods @@ -298,7 +249,6 @@ var shhWatch = { setupMethods(shhWatch, shhWatchMethods()); web3.setProvider = function(provider) { - //provider.onmessage = messageHandler; // there will be no async calls, to remove web3.provider.set(provider); }; diff --git a/libjsqrc/ethereumjs/package.json b/libjsqrc/ethereumjs/package.json index a61ffc76a..8102a2592 100644 --- a/libjsqrc/ethereumjs/package.json +++ b/libjsqrc/ethereumjs/package.json @@ -1,7 +1,7 @@ { "name": "ethereum.js", "namespace": "ethereum", - "version": "0.0.11", + "version": "0.0.13", "description": "Ethereum Compatible JavaScript API", "main": "./index.js", "directories": { diff --git a/libjsqrc/ethereumjs/test/abi.parsers.js b/libjsqrc/ethereumjs/test/abi.inputParser.js similarity index 52% rename from libjsqrc/ethereumjs/test/abi.parsers.js rename to libjsqrc/ethereumjs/test/abi.inputParser.js index 12bccf5a5..12b735153 100644 --- a/libjsqrc/ethereumjs/test/abi.parsers.js +++ b/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); - - }); - - }); }); diff --git a/libjsqrc/ethereumjs/test/abi.outputParser.js b/libjsqrc/ethereumjs/test/abi.outputParser.js new file mode 100644 index 000000000..723c408f0 --- /dev/null +++ b/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); + + }); + + }); +}); + diff --git a/libjsqrc/ethereumjs/test/db.methods.js b/libjsqrc/ethereumjs/test/db.methods.js index 8f8f5409f..2ad384579 100644 --- a/libjsqrc/ethereumjs/test/db.methods.js +++ b/libjsqrc/ethereumjs/test/db.methods.js @@ -1,7 +1,7 @@ var assert = require('assert'); var web3 = require('../index.js'); -var u = require('./utils.js'); +var u = require('./test.utils.js'); describe('web3', function() { describe('db', function() { diff --git a/libjsqrc/ethereumjs/test/eth.methods.js b/libjsqrc/ethereumjs/test/eth.methods.js index 7a031c4c8..8f10b441d 100644 --- a/libjsqrc/ethereumjs/test/eth.methods.js +++ b/libjsqrc/ethereumjs/test/eth.methods.js @@ -1,6 +1,6 @@ var assert = require('assert'); var web3 = require('../index.js'); -var u = require('./utils.js'); +var u = require('./test.utils.js'); describe('web3', function() { describe('eth', function() { diff --git a/libjsqrc/ethereumjs/test/event.inputParser.js b/libjsqrc/ethereumjs/test/event.inputParser.js new file mode 100644 index 000000000..8f9790a2d --- /dev/null +++ b/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); + + }); + }); +}); + diff --git a/libjsqrc/ethereumjs/test/event.js b/libjsqrc/ethereumjs/test/event.js deleted file mode 100644 index 9edd93ae7..000000000 --- a/libjsqrc/ethereumjs/test/event.js +++ /dev/null @@ -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); - - }); - -}); - diff --git a/libjsqrc/ethereumjs/test/event.outputParser.js b/libjsqrc/ethereumjs/test/event.outputParser.js new file mode 100644 index 000000000..22f4ed395 --- /dev/null +++ b/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); + + }); + }); +}); + diff --git a/libjsqrc/ethereumjs/test/jsonrpc.isValidResponse.js b/libjsqrc/ethereumjs/test/jsonrpc.isValidResponse.js new file mode 100644 index 000000000..920b5f3a9 --- /dev/null +++ b/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); + }); + }); +}); diff --git a/libjsqrc/ethereumjs/test/jsonrpc.toBatchPayload.js b/libjsqrc/ethereumjs/test/jsonrpc.toBatchPayload.js new file mode 100644 index 000000000..1c1aafebb --- /dev/null +++ b/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); + }); + }); +}); diff --git a/libjsqrc/ethereumjs/test/jsonrpc.toPayload.js b/libjsqrc/ethereumjs/test/jsonrpc.toPayload.js new file mode 100644 index 000000000..6d6f003bb --- /dev/null +++ b/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'); + }); + }); +}); diff --git a/libjsqrc/ethereumjs/test/shh.methods.js b/libjsqrc/ethereumjs/test/shh.methods.js index fe2dae71d..91ca3caba 100644 --- a/libjsqrc/ethereumjs/test/shh.methods.js +++ b/libjsqrc/ethereumjs/test/shh.methods.js @@ -1,6 +1,6 @@ var assert = require('assert'); var web3 = require('../index.js'); -var u = require('./utils.js'); +var u = require('./test.utils.js'); describe('web3', function() { describe('shh', function() { diff --git a/libjsqrc/ethereumjs/test/utils.js b/libjsqrc/ethereumjs/test/test.utils.js similarity index 100% rename from libjsqrc/ethereumjs/test/utils.js rename to libjsqrc/ethereumjs/test/test.utils.js diff --git a/libjsqrc/ethereumjs/test/utils.extractDisplayName.js b/libjsqrc/ethereumjs/test/utils.extractDisplayName.js new file mode 100644 index 000000000..148653ab2 --- /dev/null +++ b/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'); + }); + }); +}); diff --git a/libjsqrc/ethereumjs/test/utils.extractTypeName.js b/libjsqrc/ethereumjs/test/utils.extractTypeName.js new file mode 100644 index 000000000..2b4bbe767 --- /dev/null +++ b/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'); + }); + + }); +}); diff --git a/libjsqrc/ethereumjs/test/web3.methods.js b/libjsqrc/ethereumjs/test/web3.methods.js index d08495dd9..06de41da4 100644 --- a/libjsqrc/ethereumjs/test/web3.methods.js +++ b/libjsqrc/ethereumjs/test/web3.methods.js @@ -1,6 +1,6 @@ var assert = require('assert'); var web3 = require('../index.js'); -var u = require('./utils.js'); +var u = require('./test.utils.js'); describe('web3', function() { u.methodExists(web3, 'sha3'); diff --git a/libjsqrc/natspec.js b/libjsqrc/natspec.js index d0bbdff17..c8cf07496 100644 --- a/libjsqrc/natspec.js +++ b/libjsqrc/natspec.js @@ -29,10 +29,20 @@ var getContractMethods = function (address, abi) { return web3.eth.contract(address, abi); }; +var getMethodWithName = function(abi, name) { + for (var i = 0; i < abi.length; i++) { + if (abi[i].name === name) { + return abi[i]; + } + } + console.warn('could not find method with name: ' + name); + return undefined; +}; + /// Function called to get all contract method input variables /// @returns hashmap with all contract's method input variables var getContractInputParams = function (abi, methodName, params) { - var method = web3.abi.getMethodWithName(abi, methodName); + var method = getMethodWithName(abi, methodName); return method.inputs.reduce(function (acc, current, index) { acc[current.name] = params[index]; return acc; diff --git a/libp2p/Capability.cpp b/libp2p/Capability.cpp index dfa1ab546..5ef70bbc3 100644 --- a/libp2p/Capability.cpp +++ b/libp2p/Capability.cpp @@ -39,7 +39,7 @@ Capability::Capability(Session* _s, HostCapabilityFace* _h, unsigned _idOffset): void Capability::disable(std::string const& _problem) { - clogS(NetConnect) << "Disabling capability '" << m_host->name() << "'. Reason:" << _problem; + clogS(NetWarn) << "DISABLE: Disabling capability '" << m_host->name() << "'. Reason:" << _problem; m_enabled = false; } diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 4cc985d65..a6267b7e9 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -378,6 +378,8 @@ string Host::pocHost() void Host::connect(std::string const& _addr, unsigned short _port) noexcept { + while (isWorking() && !m_run) + this_thread::sleep_for(chrono::milliseconds(50)); if (!m_run) return; @@ -409,6 +411,8 @@ void Host::connect(std::string const& _addr, unsigned short _port) noexcept void Host::connect(bi::tcp::endpoint const& _ep) { + while (isWorking() && !m_run) + this_thread::sleep_for(chrono::milliseconds(50)); if (!m_run) return; @@ -430,6 +434,8 @@ void Host::connect(bi::tcp::endpoint const& _ep) void Host::connect(std::shared_ptr const& _n) { + while (isWorking() && !m_run) + this_thread::sleep_for(chrono::milliseconds(50)); if (!m_run) return; diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index dc9c50af5..d4342c37a 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -273,24 +273,24 @@ bool Session::interpret(RLP const& _r) } case PingPacket: { - clogS(NetTriviaSummary) << "Ping"; + clogS(NetTriviaSummary) << "Ping"; RLPStream s; sealAndSend(prep(s, PongPacket)); break; } case PongPacket: m_info.lastPing = std::chrono::steady_clock::now() - m_ping; - clogS(NetTriviaSummary) << "Latency: " << chrono::duration_cast(m_info.lastPing).count() << " ms"; + clogS(NetTriviaSummary) << "Latency: " << chrono::duration_cast(m_info.lastPing).count() << " ms"; break; case GetPeersPacket: { - clogS(NetTriviaSummary) << "GetPeers"; + clogS(NetTriviaSummary) << "GetPeers"; m_theyRequestedNodes = true; serviceNodesRequest(); break; } case PeersPacket: - clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)"; + clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)"; m_weRequestedNodes = false; for (unsigned i = 1; i < _r.itemCount(); ++i) { @@ -369,8 +369,13 @@ bool Session::interpret(RLP const& _r) { auto id = _r[0].toInt(); for (auto const& i: m_capabilities) - if (i.second->m_enabled && id >= i.second->m_idOffset && id - i.second->m_idOffset < i.second->hostCapability()->messageCount() && i.second->interpret(id - i.second->m_idOffset, _r)) - return true; + if (id >= i.second->m_idOffset && id - i.second->m_idOffset < i.second->hostCapability()->messageCount()) + { + if (i.second->m_enabled) + return i.second->interpret(id - i.second->m_idOffset, _r); + else + return true; + } return false; } } diff --git a/libqwebthree/QWebThree.cpp b/libqwebthree/QWebThree.cpp index 804766563..31f2f6b92 100644 --- a/libqwebthree/QWebThree.cpp +++ b/libqwebthree/QWebThree.cpp @@ -41,20 +41,9 @@ void QWebThree::clientDieing() 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) { - QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object(); - emit processData(formatInput(f), ""); // it's synchronous + emit processData(_json, ""); // it's synchronous return m_response; } diff --git a/libserpent/compiler.cpp b/libserpent/compiler.cpp index 886eee6df..d19ef4354 100644 --- a/libserpent/compiler.cpp +++ b/libserpent/compiler.cpp @@ -77,43 +77,12 @@ Node popwrap(Node node) { return multiToken(nodelist, 2, node.metadata); } -// Grabs variables -mss getVariables(Node node, mss cur=mss()) { - Metadata m = node.metadata; - // Tokens don't contain any variables - if (node.type == TOKEN) - return cur; - // Don't descend into call fragments - else if (node.val == "lll") - return getVariables(node.args[1], cur); - // At global scope get/set/ref also declare - else if (node.val == "get" || node.val == "set" || node.val == "ref") { - if (node.args[0].type != TOKEN) - err("Variable name must be simple token," - " not complex expression! " + printSimple(node.args[0]), m); - if (!cur.count(node.args[0].val)) { - cur[node.args[0].val] = utd(cur.size() * 32 + 32); - //std::cerr << node.args[0].val << " " << cur[node.args[0].val] << "\n"; - } - } - // Recursively process children - for (unsigned i = 0; i < node.args.size(); i++) { - cur = getVariables(node.args[i], cur); - } - return cur; -} - // Turns LLL tree into tree of code fragments programData opcodeify(Node node, programAux aux=Aux(), programVerticalAux vaux=verticalAux()) { std::string symb = "_"+mkUniqueToken(); Metadata m = node.metadata; - // Get variables - if (!aux.vars.size()) { - aux.vars = getVariables(node); - aux.nextVarMem = aux.vars.size() * 32 + 32; - } // Numbers if (node.type == TOKEN) { return pd(aux, nodeToNumeric(node), 1); @@ -121,6 +90,10 @@ programData opcodeify(Node node, else if (node.val == "ref" || node.val == "get" || node.val == "set") { std::string varname = node.args[0].val; // Determine reference to variable + if (!aux.vars.count(node.args[0].val)) { + aux.vars[node.args[0].val] = utd(aux.nextVarMem); + aux.nextVarMem += 32; + } Node varNode = tkn(aux.vars[varname], m); //std::cerr << varname << " " << printSimple(varNode) << "\n"; // Set variable @@ -173,8 +146,7 @@ programData opcodeify(Node node, } // Comments do nothing else if (node.val == "comment") { - Node* nodelist = nullptr; - return pd(aux, multiToken(nodelist, 0, m), 0); + return pd(aux, astnode("_", m), 0); } // Custom operation sequence // eg. (ops bytez id msize swap1 msize add 0 swap1 mstore) == alloc @@ -371,7 +343,7 @@ Node buildFragmentTree(Node node) { // Builds a dictionary mapping labels to variable names -programAux buildDict(Node program, programAux aux, int labelLength) { +void buildDict(Node program, programAux &aux, int labelLength) { Metadata m = program.metadata; // Token if (program.type == TOKEN) { @@ -388,30 +360,24 @@ programAux buildDict(Node program, programAux aux, int labelLength) { } // A sub-program (ie. LLL) else if (program.val == "____CODE") { - programAux auks = Aux(); + int step = aux.step; + aux.step = 0; for (unsigned i = 0; i < program.args.size(); i++) { - auks = buildDict(program.args[i], auks, labelLength); + buildDict(program.args[i], aux, labelLength); } - for (std::map::iterator it=auks.vars.begin(); - it != auks.vars.end(); - it++) { - aux.vars[(*it).first] = (*it).second; - } - aux.step += auks.step; + aux.step += step; } // Normal sub-block else { for (unsigned i = 0; i < program.args.size(); i++) { - aux = buildDict(program.args[i], aux, labelLength); + buildDict(program.args[i], aux, labelLength); } } - return aux; } // Applies that dictionary -Node substDict(Node program, programAux aux, int labelLength) { +void substDict(Node program, programAux aux, int labelLength, std::vector &out) { Metadata m = program.metadata; - std::vector out; std::vector inner; if (program.type == TOKEN) { if (program.val[0] == '$') { @@ -428,46 +394,32 @@ Node substDict(Node program, programAux aux, int labelLength) { dist = decimalSub(end, start); inner = toByteArr(dist, m, labelLength); } - out.push_back(astnode("_", inner, m)); + for (unsigned i = 0; i < inner.size(); i++) out.push_back(inner[i]); } else if (program.val[0] == '~') { } else if (isNumberLike(program)) { inner = toByteArr(program.val, m); out.push_back(token("PUSH"+unsignedToDecimal(inner.size()))); - out.push_back(astnode("_", inner, m)); + for (unsigned i = 0; i < inner.size(); i++) out.push_back(inner[i]); } - else return program; + else out.push_back(program); } else { for (unsigned i = 0; i < program.args.size(); i++) { - Node n = substDict(program.args[i], aux, labelLength); - if (n.type == TOKEN || n.args.size()) out.push_back(n); + substDict(program.args[i], aux, labelLength, out); } } - return astnode("_", out, m); } // Compiled fragtree -> compiled fragtree without labels -Node dereference(Node program) { +std::vector dereference(Node program) { int sz = treeSize(program) * 4; int labelLength = 1; while (sz >= 256) { labelLength += 1; sz /= 256; } - programAux aux = buildDict(program, Aux(), labelLength); - return substDict(program, aux, labelLength); -} - -// Dereferenced fragtree -> opcodes -std::vector flatten(Node derefed) { + programAux aux = Aux(); + buildDict(program, aux, labelLength); std::vector o; - if (derefed.type == TOKEN) { - o.push_back(derefed); - } - else { - for (unsigned i = 0; i < derefed.args.size(); i++) { - std::vector oprime = flatten(derefed.args[i]); - for (unsigned j = 0; j < oprime.size(); j++) o.push_back(oprime[j]); - } - } + substDict(program, aux, labelLength, o); return o; } @@ -512,12 +464,12 @@ std::vector deserialize(std::string ser) { // Fragtree -> bin std::string assemble(Node fragTree) { - return serialize(flatten(dereference(fragTree))); + return serialize(dereference(fragTree)); } // Fragtree -> tokens std::vector prettyAssemble(Node fragTree) { - return flatten(dereference(fragTree)); + return dereference(fragTree); } // LLL -> bin diff --git a/libserpent/compiler.h b/libserpent/compiler.h index aecaa3718..cae87399d 100644 --- a/libserpent/compiler.h +++ b/libserpent/compiler.h @@ -8,14 +8,11 @@ #include "util.h" // Compiled fragtree -> compiled fragtree without labels -Node dereference(Node program); +std::vector dereference(Node program); // LLL -> fragtree Node buildFragmentTree(Node program); -// Dereferenced fragtree -> opcodes -std::vector flatten(Node derefed); - // opcodes -> bin std::string serialize(std::vector codons); diff --git a/libserpent/util.cpp b/libserpent/util.cpp index 18a8bafc2..1e3187ebc 100755 --- a/libserpent/util.cpp +++ b/libserpent/util.cpp @@ -5,7 +5,6 @@ #include "util.h" #include "bignum.h" #include -#include #include //Token or value node constructor @@ -260,7 +259,7 @@ std::string get_file_contents(std::string filename) { std::string contents; in.seekg(0, std::ios::end); - contents.resize((unsigned)in.tellg()); + contents.resize(in.tellg()); in.seekg(0, std::ios::beg); in.read(&contents[0], contents.size()); in.close(); @@ -274,7 +273,7 @@ void err(std::string errtext, Metadata met) { std::string err = "Error (file \"" + met.file + "\", line " + unsignedToDecimal(met.ln + 1) + ", char " + unsignedToDecimal(met.ch) + "): " + errtext; - std::cerr << err << "\n"; + std::cerr << err << std::endl; throw(err); } @@ -283,7 +282,7 @@ void warn(std::string errtext, Metadata met) { std::string err = "Warning (file \"" + met.file + "\", line " + unsignedToDecimal(met.ln + 1) + ", char " + unsignedToDecimal(met.ch) + "): " + errtext; - std::cerr << err << "\n"; + std::cerr << err << std::endl; } //Bin to hex diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index b07959b39..d1c7d5371 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -475,6 +475,8 @@ void FunctionCall::checkTypeRequirements() // number of non-mapping members if (m_arguments.size() != 1) 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())) BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed.")); m_type = type.getActualType(); @@ -487,9 +489,44 @@ void FunctionCall::checkTypeRequirements() TypePointers const& parameterTypes = functionType->getParameterTypes(); if (parameterTypes.size() != m_arguments.size()) 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])) - BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call.")); + + if (m_names.empty()) + { + 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, // but we change it to the type of the first return value until we have structs if (functionType->getReturnParameterTypes().empty()) @@ -557,6 +594,17 @@ void ElementaryTypeNameExpression::checkTypeRequirements() m_type = make_shared(Type::fromElementaryTypeName(m_typeToken)); } +Literal::Literal(Location const& _location, Token::Value _token, + ASTPointer const& _value, + Token::Value _sub): + PrimaryExpression(_location), m_token(_token), m_value(_value) +{ + if (Token::isEtherSubdenomination(_sub)) + m_subDenomination = static_cast(_sub); + else + m_subDenomination = Literal::SubDenomination::None; +} + void Literal::checkTypeRequirements() { m_type = Type::forLiteral(*this); diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 8e8641c78..bced99f9a 100755 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -963,14 +963,15 @@ class FunctionCall: public Expression { public: FunctionCall(Location const& _location, ASTPointer const& _expression, - std::vector> const& _arguments): - Expression(_location), m_expression(_expression), m_arguments(_arguments) {} + std::vector> const& _arguments, std::vector> const& _names): + Expression(_location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; Expression const& getExpression() const { return *m_expression; } std::vector> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; } + std::vector> const& getNames() const { return m_names; } /// Returns true if this is not an actual function call, but an explicit type conversion /// or constructor call. @@ -979,6 +980,7 @@ public: private: ASTPointer m_expression; std::vector> m_arguments; + std::vector> m_names; }; /** @@ -1110,13 +1112,22 @@ private: }; /** - * A literal string or number. @see Type::literalToBigEndian is used to actually parse its value. + * A literal string or number. @see ExpressionCompiler::endVisit() is used to actually parse its value. */ class Literal: public PrimaryExpression { public: - Literal(Location const& _location, Token::Value _token, ASTPointer const& _value): - PrimaryExpression(_location), m_token(_token), m_value(_value) {} + enum class SubDenomination + { + None = Token::ILLEGAL, + Wei = Token::SubWei, + Szabo = Token::SubSzabo, + Finney = Token::SubFinney, + Ether = Token::SubEther + }; + Literal(Location const& _location, Token::Value _token, + ASTPointer const& _value, + Token::Value _sub = Token::ILLEGAL); virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; @@ -1125,9 +1136,12 @@ public: /// @returns the non-parsed value of the literal ASTString const& getValue() const { return *m_value; } + SubDenomination getSubDenomination() const { return m_subDenomination; } + private: Token::Value m_token; ASTPointer m_value; + SubDenomination m_subDenomination; }; /// @} diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 45e0e80b0..875e00bc2 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -195,6 +195,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { //@todo struct construction solAssert(_functionCall.getArguments().size() == 1, ""); + solAssert(_functionCall.getNames().empty(), ""); Expression const& firstArgument = *_functionCall.getArguments().front(); firstArgument.accept(*this); appendTypeConversion(*firstArgument.getType(), *_functionCall.getType()); @@ -202,8 +203,26 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) else { FunctionType const& function = dynamic_cast(*_functionCall.getExpression().getType()); - vector> arguments = _functionCall.getArguments(); - solAssert(arguments.size() == function.getParameterTypes().size(), ""); + TypePointers const& parameterTypes = function.getParameterTypes(); + vector> const& callArguments = _functionCall.getArguments(); + vector> const& callArgumentNames = _functionCall.getNames(); + solAssert(callArguments.size() == parameterTypes.size(), ""); + + vector> 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()) { diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index cc75fc0a2..741b9aba7 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -169,7 +169,7 @@ ASTPointer Parser::parseInheritanceSpecifier() if (m_scanner->getCurrentToken() == Token::LPAREN) { m_scanner->next(); - arguments = parseFunctionCallArguments(); + arguments = parseFunctionCallListArguments(); nodeFactory.markEndPosition(); expectToken(Token::RPAREN); } @@ -180,7 +180,7 @@ ASTPointer Parser::parseInheritanceSpecifier() Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token) { - Declaration::Visibility visibility; + Declaration::Visibility visibility = Declaration::Visibility::DEFAULT; if (_token == Token::PUBLIC) visibility = Declaration::Visibility::PUBLIC; else if (_token == Token::PROTECTED) @@ -332,7 +332,7 @@ ASTPointer Parser::parseModifierInvocation() if (m_scanner->getCurrentToken() == Token::LPAREN) { m_scanner->next(); - arguments = parseFunctionCallArguments(); + arguments = parseFunctionCallListArguments(); nodeFactory.markEndPosition(); expectToken(Token::RPAREN); } @@ -592,7 +592,6 @@ ASTPointer Parser::parseBinaryExpression(int _minPrecedence) ASTPointer expression = parseUnaryExpression(); int precedence = Token::precedence(m_scanner->getCurrentToken()); for (; precedence >= _minPrecedence; --precedence) - { while (Token::precedence(m_scanner->getCurrentToken()) == precedence) { Token::Value op = m_scanner->getCurrentToken(); @@ -601,7 +600,6 @@ ASTPointer Parser::parseBinaryExpression(int _minPrecedence) nodeFactory.setEndPositionFromNode(right); expression = nodeFactory.createNode(expression, op, right); } - } return expression; } @@ -667,10 +665,12 @@ ASTPointer Parser::parseLeftHandSideExpression() case Token::LPAREN: { m_scanner->next(); - vector> arguments = parseFunctionCallArguments(); + vector> arguments; + vector> names; + std::tie(arguments, names) = parseFunctionCallArguments(); nodeFactory.markEndPosition(); expectToken(Token::RPAREN); - expression = nodeFactory.createNode(expression, arguments); + expression = nodeFactory.createNode(expression, arguments, names); } break; default: @@ -684,6 +684,7 @@ ASTPointer Parser::parsePrimaryExpression() ASTNodeFactory nodeFactory(*this); Token::Value token = m_scanner->getCurrentToken(); ASTPointer expression; + Token::Value nextToken = Token::ILLEGAL; switch (token) { case Token::TRUE_LITERAL: @@ -691,9 +692,12 @@ ASTPointer Parser::parsePrimaryExpression() expression = nodeFactory.createNode(token, getLiteralAndAdvance()); break; case Token::NUMBER: + nextToken = m_scanner->peekNextToken(); case Token::STRING_LITERAL: nodeFactory.markEndPosition(); - expression = nodeFactory.createNode(token, getLiteralAndAdvance()); + expression = nodeFactory.createNode(token, getLiteralAndAdvance(), nextToken); + if (Token::isEtherSubdenomination(nextToken)) + m_scanner->next(); break; case Token::IDENTIFIER: nodeFactory.markEndPosition(); @@ -723,7 +727,7 @@ ASTPointer Parser::parsePrimaryExpression() return expression; } -vector> Parser::parseFunctionCallArguments() +vector> Parser::parseFunctionCallListArguments() { vector> arguments; if (m_scanner->getCurrentToken() != Token::RPAREN) @@ -738,6 +742,32 @@ vector> Parser::parseFunctionCallArguments() return arguments; } +pair>, vector>> Parser::parseFunctionCallArguments() +{ + pair>, vector>> 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() { diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 388fd7a96..19e0af1aa 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -81,7 +81,8 @@ private: ASTPointer parseUnaryExpression(); ASTPointer parseLeftHandSideExpression(); ASTPointer parsePrimaryExpression(); - std::vector> parseFunctionCallArguments(); + std::vector> parseFunctionCallListArguments(); + std::pair>, std::vector>> parseFunctionCallArguments(); ///@} ///@{ diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 76e504499..b07fc46c6 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -174,6 +174,11 @@ namespace solidity K(WHILE, "while", 0) \ \ \ + /* Ether subdenominations */ \ + K(SubWei, "wei", 0) \ + K(SubSzabo, "szabo", 0) \ + K(SubFinney, "finney", 0) \ + K(SubEther, "ether", 0) \ /* type keywords, keep them in this order, keep int as first keyword * the implementation in Types.cpp has to be synced to this here * TODO more to be added */ \ @@ -378,6 +383,7 @@ public: static bool isCountOp(Value op) { return op == INC || op == DEC; } static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); } static bool isVisibilitySpecifier(Value op) { return op == PUBLIC || op == PRIVATE || op == PROTECTED; } + static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == Token::SubEther; } // Returns a string corresponding to the JS token string // (.e., "<" for the token LT) or NULL if the token doesn't diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 54e701c10..648cf9cb2 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -326,15 +326,39 @@ string IntegerConstantType::toString() const return "int_const " + m_value.str(); } -u256 IntegerConstantType::literalValue(Literal const*) const +u256 IntegerConstantType::literalValue(Literal const* _literal) const { + u256 value; // we ignore the literal and hope that the type was correctly determined solAssert(m_value <= u256(-1), "Integer constant too large."); solAssert(m_value >= -(bigint(1) << 255), "Integer constant too small."); + if (m_value >= 0) - return u256(m_value); + value = u256(m_value); else - return s2u(s256(m_value)); + value = s2u(s256(m_value)); + + if (_literal) + { + Literal::SubDenomination sub =_literal->getSubDenomination(); + switch(sub) + { + case Literal::SubDenomination::Wei: + case Literal::SubDenomination::None: + break; + case Literal::SubDenomination::Szabo: + value *= u256(1000000000000); + break; + case Literal::SubDenomination::Finney: + value *= u256(1000000000000000); + break; + case Literal::SubDenomination::Ether: + value *= u256(1000000000000000000); + break; + } + } + + return value; } shared_ptr IntegerConstantType::getIntegerType() const diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index c8aff938f..583645749 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -28,10 +28,12 @@ #include #include #include -#include +#include #include #include +#ifndef _MSC_VER #include +#endif #include "WebThreeStubServerBase.h" using namespace std; @@ -342,7 +344,10 @@ std::string WebThreeStubServerBase::eth_call(Json::Value const& _json) Json::Value WebThreeStubServerBase::eth_changed(int const& _id) { - return toJson(client()->checkWatch(_id)); + auto entries = client()->checkWatch(_id); + if (entries.size()) + cnote << "FIRING WATCH" << _id << entries.size(); + return toJson(entries); } std::string WebThreeStubServerBase::eth_codeAt(string const& _address) @@ -443,7 +448,9 @@ Json::Value WebThreeStubServerBase::eth_compilers() Json::Value ret(Json::arrayValue); ret.append("lll"); ret.append("solidity"); +#ifndef _MSC_VER ret.append("serpent"); +#endif return ret; } @@ -459,6 +466,7 @@ std::string WebThreeStubServerBase::eth_lll(std::string const& _code) std::string WebThreeStubServerBase::eth_serpent(std::string const& _code) { string res; +#ifndef _MSC_VER try { res = toJS(dev::asBytes(::compile(_code))); @@ -471,6 +479,7 @@ std::string WebThreeStubServerBase::eth_serpent(std::string const& _code) { cwarn << "Uncought serpent compilation exception"; } +#endif return res; } diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index ee583efa3..ffb9e1738 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -36,9 +36,9 @@ namespace dev { class WebThreeNetworkFace; class KeyPair; -struct TransactionSkeleton; namespace eth { +struct TransactionSkeleton; class Interface; } namespace shh @@ -122,7 +122,7 @@ public: std::map const& ids() const { return m_ids; } protected: - virtual bool authenticate(dev::TransactionSkeleton const& _t); + virtual bool authenticate(dev::eth::TransactionSkeleton const& _t); protected: virtual dev::eth::Interface* client() = 0; diff --git a/macdeployfix.sh b/macdeployfix.sh new file mode 100755 index 000000000..9a6ce1ebc --- /dev/null +++ b/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 + diff --git a/mix/AppContext.cpp b/mix/AppContext.cpp index e27eac9fd..1aa43a119 100644 --- a/mix/AppContext.cpp +++ b/mix/AppContext.cpp @@ -27,15 +27,16 @@ #include #include #include -#include +#include #include "CodeModel.h" #include "FileIo.h" #include "ClientModel.h" #include "CodeEditorExtensionManager.h" #include "Exceptions.h" -#include "AppContext.h" #include "QEther.h" +#include "QVariableDefinition.h" #include "HttpServer.h" +#include "AppContext.h" using namespace dev; using namespace dev::eth; @@ -49,6 +50,7 @@ AppContext::AppContext(QQmlApplicationEngine* _engine) m_codeModel.reset(new CodeModel(this)); m_clientModel.reset(new ClientModel(this)); m_fileIo.reset(new FileIo()); + connect(QApplication::clipboard(), &QClipboard::dataChanged, [this] { emit clipboardChanged();}); } AppContext::~AppContext() @@ -82,7 +84,7 @@ void AppContext::load() qmlRegisterType("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager"); qmlRegisterType("HttpServer", 1, 0, "HttpServer"); m_applicationEngine->load(QUrl("qrc:/qml/main.qml")); - QQuickWindow *window = qobject_cast(m_applicationEngine->rootObjects().at(0)); + QWindow *window = qobject_cast(m_applicationEngine->rootObjects().at(0)); window->setIcon(QIcon(":/res/mix_256x256x32.png")); appLoaded(); } @@ -105,6 +107,12 @@ void AppContext::displayMessageDialog(QString _title, QString _message) QMetaObject::invokeMethod(dialogWin, "open"); } +QString AppContext::clipboard() const +{ + QClipboard *clipboard = QApplication::clipboard(); + return clipboard->text(); +} + void AppContext::toClipboard(QString _text) { QClipboard *clipboard = QApplication::clipboard(); diff --git a/mix/AppContext.h b/mix/AppContext.h index 31ecd3198..268771207 100644 --- a/mix/AppContext.h +++ b/mix/AppContext.h @@ -48,6 +48,7 @@ class FileIo; class AppContext: public QObject { Q_OBJECT + Q_PROPERTY(QString clipboard READ clipboard WRITE toClipboard NOTIFY clipboardChanged) public: AppContext(QQmlApplicationEngine* _engine); @@ -64,10 +65,13 @@ public: void displayMessageDialog(QString _title, QString _message); /// Copy text to clipboard Q_INVOKABLE void toClipboard(QString _text); + /// Get text from clipboard + QString clipboard() const; signals: /// Triggered once components have been loaded void appLoaded(); + void clipboardChanged(); private: QQmlApplicationEngine* m_applicationEngine; //owned by app diff --git a/mix/CMakeLists.txt b/mix/CMakeLists.txt index d5574667a..db9452061 100644 --- a/mix/CMakeLists.txt +++ b/mix/CMakeLists.txt @@ -46,7 +46,9 @@ target_link_libraries(${EXECUTABLE} evm) target_link_libraries(${EXECUTABLE} ethcore) target_link_libraries(${EXECUTABLE} devcrypto) target_link_libraries(${EXECUTABLE} secp256k1) +if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) target_link_libraries(${EXECUTABLE} serpent) +endif() target_link_libraries(${EXECUTABLE} lll) target_link_libraries(${EXECUTABLE} solidity) target_link_libraries(${EXECUTABLE} evmcore) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 365dce9a9..e18b23ab3 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -22,14 +22,16 @@ #include #include #include +#include #include -#include +#include #include #include "AppContext.h" #include "DebuggingStateWrapper.h" #include "Exceptions.h" #include "QContractDefinition.h" #include "QVariableDeclaration.h" +#include "QVariableDefinition.h" #include "ContractCallDataEncoder.h" #include "CodeModel.h" #include "ClientModel.h" @@ -83,7 +85,7 @@ ClientModel::ClientModel(AppContext* _context): qRegisterMetaType("TransactionLogEntry"); connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection); - m_client.reset(new MixClient()); + m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString())); m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), std::vector { m_client->userAccount() }, m_client.get())); connect(m_web3Server.get(), &Web3Server::newTransaction, this, &ClientModel::onNewTransaction, Qt::DirectConnection); @@ -111,8 +113,28 @@ QString ClientModel::apiCall(QString const& _message) void ClientModel::mine() { - m_client->mine(); - newBlock(); + if (m_running || m_mining) + BOOST_THROW_EXCEPTION(ExecutionStateException()); + m_mining = true; + emit miningStarted(); + emit miningStateChanged(); + QtConcurrent::run([=]() + { + try + { + m_client->mine(); + newBlock(); + m_mining = false; + emit miningComplete(); + } + catch (...) + { + m_mining = false; + std::cerr << boost::current_exception_diagnostic_information(); + emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); + } + emit miningStateChanged(); + }); } QString ClientModel::contractAddress() const @@ -145,7 +167,7 @@ void ClientModel::setupState(QVariantMap _state) TransactionSettings transactionSettings(functionId, transaction.value("url").toString()); transactionSettings.gasPrice = 10000000000000; transactionSettings.gas = 125000; - transactionSettings.value = 100; + transactionSettings.value = 0; transactionSequence.push_back(transactionSettings); } else @@ -242,11 +264,13 @@ void ClientModel::executeSequence(std::vector const& _seque } catch(boost::exception const&) { + std::cerr << boost::current_exception_diagnostic_information(); emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); } catch(std::exception const& e) { + std::cerr << boost::current_exception_diagnostic_information(); emit runFailed(e.what()); } m_running = false; @@ -256,7 +280,7 @@ void ClientModel::executeSequence(std::vector const& _seque void ClientModel::showDebugger() { - ExecutionResult const& last = m_client->record().back().transactions.back(); + ExecutionResult const& last = m_client->lastExecution(); showDebuggerForTransaction(last); } @@ -289,7 +313,7 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) void ClientModel::debugTransaction(unsigned _block, unsigned _index) { - auto const& t = m_client->record().at(_block).transactions.at(_index); + auto const& t = m_client->execution(_block, _index); showDebuggerForTransaction(t); } @@ -320,9 +344,9 @@ void ClientModel::onStateReset() void ClientModel::onNewTransaction() { - unsigned block = m_client->number(); - unsigned index = m_client->record().back().transactions.size() - 1; - ExecutionResult const& tr = m_client->record().back().transactions.back(); + unsigned block = m_client->number() + 1; + unsigned index = m_client->pendingExecutions().size() - 1; + ExecutionResult const& tr = m_client->lastExecution(); QString address = QString::fromStdString(toJS(tr.address)); QString value = QString::fromStdString(dev::toString(tr.value)); QString contract = address; diff --git a/mix/ClientModel.h b/mix/ClientModel.h index 1eb508dac..be264e108 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -26,8 +26,7 @@ #include #include #include -#include "MixClient.h" -#include "QVariableDefinition.h" +#include "MachineStates.h" namespace dev { @@ -39,6 +38,8 @@ class Web3Server; class RpcConnector; class QEther; class QDebugData; +class MixClient; +class QVariableDefinition; /// Backend transaction config class struct TransactionSettings @@ -113,6 +114,8 @@ public: ~ClientModel(); /// @returns true if currently executing contract code Q_PROPERTY(bool running MEMBER m_running NOTIFY runStateChanged) + /// @returns true if currently mining + Q_PROPERTY(bool mining MEMBER m_mining NOTIFY miningStateChanged) /// @returns address of the last executed contract Q_PROPERTY(QString contractAddress READ contractAddress NOTIFY contractAddressChanged) /// ethereum.js RPC request entry point @@ -143,6 +146,12 @@ signals: void runStarted(); /// Transaction execution completed successfully void runComplete(); + /// Mining has started + void miningStarted(); + /// Mined a new block + void miningComplete(); + /// Mining stopped or started + void miningStateChanged(); /// Transaction execution completed with error /// @param _message Error message void runFailed(QString const& _message); @@ -173,6 +182,7 @@ private: AppContext* m_context; std::atomic m_running; + std::atomic m_mining; std::unique_ptr m_client; std::unique_ptr m_rpcConnector; std::unique_ptr m_web3Server; diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index bdec95ca4..29b43bc11 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include "QVariableDeclaration.h" #include "QVariableDefinition.h" diff --git a/mix/DebuggingStateWrapper.cpp b/mix/DebuggingStateWrapper.cpp index 42d0c97ac..d32be4989 100644 --- a/mix/DebuggingStateWrapper.cpp +++ b/mix/DebuggingStateWrapper.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index a6c833532..512847a78 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -23,10 +23,11 @@ #include #include -#include +#include #include #include #include +#include #include #include "Exceptions.h" @@ -34,37 +35,77 @@ using namespace dev; using namespace dev::eth; -using namespace dev::mix; -const Secret c_stdSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074"); +namespace dev +{ +namespace mix +{ -MixClient::MixClient(): - m_userAccount(c_stdSecret) +const Secret c_userAccountSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074"); +const u256 c_mixGenesisDifficulty = (u256) 1 << 4; + +class MixBlockChain: public dev::eth::BlockChain +{ +public: + MixBlockChain(std::string const& _path, h256 _stateRoot): BlockChain(createGenesisBlock(_stateRoot), _path, true) + { + } + + static bytes createGenesisBlock(h256 _stateRoot) + { + RLPStream block(3); + block.appendList(14) + << h256() << EmptyListSHA3 << h160() << _stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_mixGenesisDifficulty << 0 << 1000000 << 0 << (unsigned)0 << std::string() << sha3(bytes(1, 42)); + block.appendRaw(RLPEmptyList); + block.appendRaw(RLPEmptyList); + return block.out(); + } +}; + +MixClient::MixClient(std::string const& _dbPath): + m_userAccount(c_userAccountSecret), m_dbPath(_dbPath), m_minigThreads(0) { resetState(10000000 * ether); } +MixClient::~MixClient() +{ +} + void MixClient::resetState(u256 _balance) { WriteGuard l(x_state); Guard fl(m_filterLock); m_filters.clear(); m_watches.clear(); - m_state = eth::State(m_userAccount.address(), m_stateDB, BaseState::Genesis); - m_state.addBalance(m_userAccount.address(), _balance); - Block genesis; - genesis.state = m_state; - Block open; - m_blocks = Blocks { genesis, open }; //last block contains a list of pending transactions to be finalized -// m_lastHashes.clear(); -// m_lastHashes.resize(256); -// m_lastHashes[0] = genesis.hash; + + m_stateDB = OverlayDB(); + TrieDB accountState(&m_stateDB); + accountState.init(); + std::map genesisState = { std::make_pair(KeyPair(c_userAccountSecret).address(), Account(_balance, Account::NormalCreation)) }; + dev::eth::commit(genesisState, static_cast(m_stateDB), accountState); + h256 stateRoot = accountState.root(); + m_bc.reset(); + m_bc.reset(new MixBlockChain(m_dbPath, stateRoot)); + m_state = eth::State(m_userAccount.address(), m_stateDB, BaseState::Empty); + m_state.sync(bc()); + m_startState = m_state; + m_pendingExecutions.clear(); + m_executions.clear(); } void MixClient::executeTransaction(Transaction const& _t, State& _state) { bytes rlp = _t.rlp(); - Executive execution(_state, LastHashes(), 0); + + // do debugging run first + LastHashes lastHashes(256); + lastHashes[0] = bc().numberHash(bc().number()); + for (unsigned i = 1; i < 256; ++i) + lastHashes[i] = lastHashes[i - 1] ? bc().details(lastHashes[i - 1]).parent : h256(); + + State execState = _state; + Executive execution(execState, lastHashes, 0); execution.setup(&rlp); std::vector machineStates; std::vector levels; @@ -130,21 +171,25 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state) d.value = _t.value(); if (_t.isCreation()) d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce()))); - d.receipt = TransactionReceipt(m_state.rootHash(), execution.gasUsed(), execution.logs()); //TODO: track gas usage - m_blocks.back().transactions.emplace_back(d); + d.receipt = TransactionReceipt(execState.rootHash(), execution.gasUsed(), execution.logs()); //TODO: track gas usage + m_pendingExecutions.emplace_back(std::move(d)); + + // execute on a state + _state.execute(lastHashes, rlp, nullptr, true); + // collect watches h256Set changed; Guard l(m_filterLock); for (std::pair& i: m_filters) - if ((unsigned)i.second.filter.latest() > m_blocks.size() - 1) + if ((unsigned)i.second.filter.latest() > bc().number()) { // acceptable number. - auto m = i.second.filter.matches(d.receipt); + auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1)); if (m.size()) { // filter catches them for (LogEntry const& l: m) - i.second.changes.push_back(LocalisedLogEntry(l, m_blocks.size())); + i.second.changes.push_back(LocalisedLogEntry(l, bc().number() + 1)); changed.insert(i.first); } } @@ -152,38 +197,49 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state) noteChanged(changed); } -void MixClient::validateBlock(int _block) const -{ - if (_block != -1 && _block != 0 && (unsigned)_block >= m_blocks.size() - 1) - BOOST_THROW_EXCEPTION(InvalidBlockException() << BlockIndex(_block)); -} - void MixClient::mine() { WriteGuard l(x_state); - Block& block = m_blocks.back(); - m_state.mine(0, true); + m_state.commitToMine(bc()); + while (!m_state.mine(100, true).completed) {} m_state.completeMine(); - m_state.commitToMine(BlockChain()); - m_state.cleanup(true); - block.state = m_state; - block.info = m_state.info(); - block.hash = block.info.hash; - m_blocks.push_back(Block()); - + bc().import(m_state.blockData(), m_stateDB); + m_state.sync(bc()); + //m_state.cleanup(true); + m_startState = m_state; + m_executions.emplace_back(std::move(m_pendingExecutions)); h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter }; noteChanged(changed); } -State const& MixClient::asOf(int _block) const +ExecutionResult const& MixClient::execution(unsigned _block, unsigned _transaction) const +{ + if (_block == bc().number() + 1) + return m_pendingExecutions.at(_transaction); + return m_executions.at(_block - 1).at(_transaction); +} + +ExecutionResult const& MixClient::lastExecution() const { - validateBlock(_block); + if (m_pendingExecutions.size() > 0) + return m_pendingExecutions.back(); + return m_executions.back().back(); +} + +ExecutionResults const& MixClient::pendingExecutions() const +{ + return m_pendingExecutions; +} + +State MixClient::asOf(int _block) const +{ + ReadGuard l(x_state); if (_block == 0) - return m_blocks[m_blocks.size() - 2].state; - else if (_block == -1) return m_state; + else if (_block == -1) + return m_startState; else - return m_blocks[_block].state; + return State(m_stateDB, bc(), bc().numberHash(_block)); } void MixClient::transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) @@ -228,36 +284,31 @@ bytes MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _ bytes rlp = t.rlp(); WriteGuard lw(x_state); //TODO: lock is required only for last execution state executeTransaction(t, temp); - return m_blocks.back().transactions.back().returnValue; + return m_pendingExecutions.back().returnValue; } u256 MixClient::balanceAt(Address _a, int _block) const { - ReadGuard l(x_state); return asOf(_block).balance(_a); } u256 MixClient::countAt(Address _a, int _block) const { - ReadGuard l(x_state); return asOf(_block).transactionsFrom(_a); } u256 MixClient::stateAt(Address _a, u256 _l, int _block) const { - ReadGuard l(x_state); return asOf(_block).storage(_a, _l); } bytes MixClient::codeAt(Address _a, int _block) const { - ReadGuard l(x_state); return asOf(_block).code(_a); } std::map MixClient::storageAt(Address _a, int _block) const { - ReadGuard l(x_state); return asOf(_block).storage(_a); } @@ -274,23 +325,40 @@ eth::LocalisedLogEntries MixClient::logs(unsigned _watchId) const eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const { LocalisedLogEntries ret; - unsigned lastBlock = m_blocks.size() - 1; //last block contains pending transactions + unsigned lastBlock = bc().number(); unsigned block = std::min(lastBlock, (unsigned)_f.latest()); unsigned end = std::min(lastBlock, std::min(block, (unsigned)_f.earliest())); - for (; ret.size() != _f.max() && block != end; block--) + unsigned skip = _f.skip(); + // Pending transactions + if (block > bc().number()) + { + ReadGuard l(x_state); + for (unsigned i = 0; i < m_state.pending().size(); ++i) + { + // Might have a transaction that contains a matching log. + TransactionReceipt const& tr = m_state.receipt(i); + LogEntries logEntries = _f.matches(tr); + for (unsigned entry = 0; entry < logEntries.size() && ret.size() != _f.max(); ++entry) + ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block)); + skip -= std::min(skip, static_cast(logEntries.size())); + } + block = bc().number(); + } + + // The rest + auto h = bc().numberHash(block); + for (; ret.size() != block && block != end; block--) { - bool pendingBlock = (block == lastBlock); - if (pendingBlock || _f.matches(m_blocks[block].info.logBloom)) - for (ExecutionResult const& t: m_blocks[block].transactions) - if (pendingBlock || _f.matches(t.receipt.bloom())) + if (_f.matches(bc().info(h).logBloom)) + for (TransactionReceipt receipt: bc().receipts(h).receipts) + if (_f.matches(receipt.bloom())) { - LogEntries logEntries = _f.matches(t.receipt); - if (logEntries.size()) - { - for (unsigned entry = _f.skip(); entry < logEntries.size() && ret.size() != _f.max(); ++entry) - ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block)); - } + LogEntries logEntries = _f.matches(receipt); + for (unsigned entry = skip; entry < logEntries.size() && ret.size() != _f.max(); ++entry) + ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block)); + skip -= std::min(skip, static_cast(logEntries.size())); } + h = bc().details(h).parent; } return ret; } @@ -372,66 +440,65 @@ LocalisedLogEntries MixClient::checkWatch(unsigned _watchId) h256 MixClient::hashFromNumber(unsigned _number) const { - validateBlock(_number); - return m_blocks[_number].hash; + return bc().numberHash(_number); } eth::BlockInfo MixClient::blockInfo(h256 _hash) const { - (void)_hash; - BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::blockInfo")); + return BlockInfo(bc().block(_hash)); } eth::BlockDetails MixClient::blockDetails(h256 _hash) const { - (void)_hash; - BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::blockDetails")); + return bc().details(_hash); } eth::Transaction MixClient::transaction(h256 _blockHash, unsigned _i) const { - (void)_blockHash; - (void)_i; - BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::transaction")); + auto bl = bc().block(_blockHash); + RLP b(bl); + if (_i < b[1].itemCount()) + return Transaction(b[1][_i].data(), CheckSignature::Range); + else + return Transaction(); } eth::BlockInfo MixClient::uncle(h256 _blockHash, unsigned _i) const { - (void)_blockHash; - (void)_i; - BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::uncle")); + auto bl = bc().block(_blockHash); + RLP b(bl); + if (_i < b[2].itemCount()) + return BlockInfo::fromHeader(b[2][_i].data()); + else + return BlockInfo(); } unsigned MixClient::number() const { - return m_blocks.size() - 1; + return bc().number(); } eth::Transactions MixClient::pending() const { - return eth::Transactions(); + return m_state.pending(); } eth::StateDiff MixClient::diff(unsigned _txi, h256 _block) const { - (void)_txi; - (void)_block; - BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::diff")); + State st(m_stateDB, bc(), _block); + return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); } eth::StateDiff MixClient::diff(unsigned _txi, int _block) const { - (void)_txi; - (void)_block; - BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::diff")); + State st = asOf(_block); + return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); } Addresses MixClient::addresses(int _block) const { - validateBlock(_block); - ReadGuard l(x_state); Addresses ret; - for (auto const& i: m_state.addresses()) + for (auto const& i: asOf(_block).addresses()) ret.push_back(i.first); return ret; } @@ -456,23 +523,22 @@ Address MixClient::address() const void MixClient::setMiningThreads(unsigned _threads) { - (void)_threads; - BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::setMiningThreads")); + m_minigThreads = _threads; } unsigned MixClient::miningThreads() const { - return 0; + return m_minigThreads; } void MixClient::startMining() { - BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::startMining")); + //no-op } void MixClient::stopMining() { - BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::stopMining")); + //no-op } bool MixClient::isMining() @@ -484,3 +550,6 @@ eth::MineProgress MixClient::miningProgress() const { return eth::MineProgress(); } + +} +} diff --git a/mix/MixClient.h b/mix/MixClient.h index c34fff60b..323a6b7e6 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -24,6 +24,7 @@ #pragma once #include +#include #include #include #include "MachineStates.h" @@ -33,26 +34,20 @@ namespace dev namespace mix { -struct Block -{ - ExecutionResults transactions; - h256 hash; - dev::eth::State state; - dev::eth::BlockInfo info; -}; - -using Blocks = std::vector; - +class MixBlockChain; class MixClient: public dev::eth::Interface { public: - MixClient(); + MixClient(std::string const& _dbPath); + virtual ~MixClient(); /// Reset state to the empty state with given balance. void resetState(u256 _balance); KeyPair const& userAccount() const { return m_userAccount; } void mine(); - Blocks const& record() const { return m_blocks; } + ExecutionResult const& execution(unsigned _block, unsigned _transaction) const; + ExecutionResult const& lastExecution() const; + ExecutionResults const& pendingExecutions() const; //dev::eth::Interface void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override; @@ -94,18 +89,24 @@ public: private: void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state); - void validateBlock(int _block) const; void noteChanged(h256Set const& _filters); - dev::eth::State const& asOf(int _block) const; + dev::eth::State asOf(int _block) const; + MixBlockChain& bc() { return *m_bc; } + MixBlockChain const& bc() const { return *m_bc; } KeyPair m_userAccount; eth::State m_state; + eth::State m_startState; OverlayDB m_stateDB; + std::auto_ptr m_bc; mutable boost::shared_mutex x_state; mutable std::mutex m_filterLock; std::map m_filters; std::map m_watches; - Blocks m_blocks; + std::vector m_executions; + ExecutionResults m_pendingExecutions; + std::string m_dbPath; + unsigned m_minigThreads; }; } diff --git a/mix/QBigInt.cpp b/mix/QBigInt.cpp index 191d20459..21d32a9c3 100644 --- a/mix/QBigInt.cpp +++ b/mix/QBigInt.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include "QBigInt.h" using namespace dev; diff --git a/mix/QBigInt.h b/mix/QBigInt.h index c6ec72305..0712ff984 100644 --- a/mix/QBigInt.h +++ b/mix/QBigInt.h @@ -26,7 +26,7 @@ #include "boost/variant/multivisitors.hpp" #include #include -#include +#include #include using namespace dev; diff --git a/mix/QVariableDefinition.cpp b/mix/QVariableDefinition.cpp index a76388d68..c78273f99 100644 --- a/mix/QVariableDefinition.cpp +++ b/mix/QVariableDefinition.cpp @@ -20,7 +20,7 @@ */ #include -#include +#include #include "QVariableDefinition.h" using namespace dev::mix; diff --git a/mix/qml/DebugInfoList.qml b/mix/qml/DebugInfoList.qml index cad2a4c9e..1e16038e7 100644 --- a/mix/qml/DebugInfoList.qml +++ b/mix/qml/DebugInfoList.qml @@ -4,14 +4,19 @@ import QtQuick.Layouts 1.0 import QtQuick.Controls.Styles 1.1 ColumnLayout { + id: root property string title property variant listModel; property bool collapsible; + property bool enableSelection; + property real storedHeight; property Component itemDelegate + signal rowActivated(int index) spacing: 0 function collapse() { + storedHeight = childrenRect.height; storageContainer.state = "collapsed"; } @@ -32,7 +37,9 @@ ColumnLayout { Image { source: "qrc:/qml/img/opentriangleindicator.png" width: 15 + height: 15 sourceSize.width: 15 + sourceSize.height: 15 id: storageImgArrow } @@ -53,16 +60,20 @@ ColumnLayout { if (storageContainer.state == "collapsed") { storageContainer.state = ""; - storageContainer.parent.parent.height = storageContainer.parent.parent.Layout.maximumHeight; + storageContainer.parent.parent.height = storedHeight; } else + { + storedHeight = root.childrenRect.height; storageContainer.state = "collapsed"; + } } } } } Rectangle { + id: storageContainer border.width: 3 border.color: "#deddd9" Layout.fillWidth: true @@ -80,18 +91,23 @@ ColumnLayout { } } ] - id: storageContainer - ListView { + TableView { clip: true; + alternatingRowColors: false anchors.top: parent.top anchors.left: parent.left anchors.topMargin: 3 anchors.leftMargin: 3 width: parent.width - 3 height: parent.height - 6 - id: storageList model: listModel - delegate: itemDelegate + selectionMode: enableSelection ? SelectionMode.SingleSelection : SelectionMode.NoSelection + headerDelegate: null + itemDelegate: root.itemDelegate + TableViewColumn { + role: "modelData" + width: parent.width + } } } } diff --git a/mix/qml/Debugger.qml b/mix/qml/Debugger.qml index 502f3242e..d7023f4b2 100644 --- a/mix/qml/Debugger.qml +++ b/mix/qml/Debugger.qml @@ -55,6 +55,15 @@ Rectangle { onCompilationComplete: update(null, false); } + Settings { + id: splitSettings + property alias transactionLogHeight: transactionLog.height + property alias callStackHeight: callStackRect.height + property alias storageHeightSettings: storageRect.height + property alias memoryDumpHeightSettings: memoryRect.height + property alias callDataHeightSettings: callDataRect.height + } + Rectangle { visible: false; @@ -104,49 +113,60 @@ Rectangle { } } - Flickable { - property int firstColumnWidth: 180 - property int secondColumnWidth: 250 + ScrollView { id: debugScrollArea - flickableDirection: Flickable.VerticalFlick anchors.fill: parent - contentHeight: 4000 - contentWidth: parent.width - Rectangle + + SplitView { - color: "transparent" - anchors.fill: parent - ColumnLayout - { - property int sideMargin: 10 - id: machineStates - anchors.top: parent.top - anchors.topMargin: 15 - anchors.left: parent.left; - anchors.leftMargin: machineStates.sideMargin - anchors.right: parent.right; - anchors.rightMargin: machineStates.sideMargin - anchors.fill: parent + property int sideMargin: 10 + id: machineStates + anchors.top: parent.top + anchors.topMargin: 15 + anchors.left: parent.left; + anchors.leftMargin: machineStates.sideMargin + width: debugScrollArea.width - machineStates.sideMargin * 2 - 20; + orientation: Qt.Vertical + handleDelegate: Rectangle { + height: machineStates.sideMargin + color: "transparent" + } + + function updateHeight() { + machineStates.height = transactionLog.childrenRect.height + buttonRow.childrenRect.height + assemblyCodeRow.childrenRect.height + + callStackRect.childrenRect.height + storageRect.childrenRect.height + memoryRect.childrenRect.height + callDataRect.childrenRect.height + 120; + } + + Component.onCompleted: updateHeight(); + + + TransactionLog { + id: transactionLog Layout.fillWidth: true - Layout.fillHeight: true + Layout.minimumHeight: 60 + height: 250 + } - TransactionLog { - Layout.fillWidth: true - height: 250 - } + ColumnLayout { + + Layout.fillWidth: true + Layout.fillHeight: true + id: statesLayout + spacing: machineStates.sideMargin - RowLayout { + Rectangle { // step button + slider id: buttonRow - spacing: machineStates.sideMargin height: 27 Layout.fillWidth: true + color: "transparent" - Rectangle - { - height: parent.height + Rectangle { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left color: "transparent" - width: debugScrollArea.firstColumnWidth + width: stateListContainer.width RowLayout { anchors.horizontalCenter: parent.horizontalCenter id: jumpButtons @@ -226,9 +246,11 @@ Rectangle { } Rectangle { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + width: debugInfoContainer.width color: "transparent" - Layout.fillWidth: true - height: parent.height Slider { id: statesSlider anchors.fill: parent @@ -255,83 +277,99 @@ Rectangle { } } - RowLayout { + Rectangle { // Assembly code id: assemblyCodeRow Layout.fillWidth: true height: 405 implicitHeight: 405 - spacing: machineStates.sideMargin + color: "transparent" Rectangle { id: stateListContainer - width: debugScrollArea.firstColumnWidth + anchors.top : parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + width: parent.width * 0.4 height: parent.height border.width: 3 border.color: "#deddd9" color: "white" - anchors.top: parent.top - ListView { + TableView { + id: statesList anchors.fill: parent anchors.leftMargin: 3 anchors.rightMargin: 3 anchors.topMargin: 3 anchors.bottomMargin: 3 clip: true - id: statesList - delegate: renderDelegate - highlight: highlightBar - //highlightFollowsCurrentItem: false + headerDelegate: null + itemDelegate: renderDelegate model: ListModel {} + TableViewColumn { + role: "line" + width: parent.width - 10 + } + } Component { id: highlightBar Rectangle { radius: 4 - height: statesList.currentItem.height - width: statesList.currentItem.width; + anchors.fill: parent y: statesList.currentItem.y color: "#4A90E2" - //Behavior on y { - // PropertyAnimation { properties: "y"; easing.type: Easing.InOutQuad; duration: 50} - //} } } Component { id: renderDelegate - RowLayout { - id: wrapperItem - height: 20 - width: parent.width - spacing: 5 - Text { - anchors.left: parent.left - anchors.leftMargin: 10 - width: 15 - color: "#b2b3ae" - text: line.split(' ')[0] - font.family: "monospace" - font.pointSize: 9 - id: id - wrapMode: Text.NoWrap + Item { + Rectangle { + radius: 4 + anchors.fill: parent + color: "#4A90E2" + visible: styleData.selected; } - Text { - wrapMode: Text.NoWrap - color: parent.ListView.isCurrentItem ? "white" : "black" - font.family: "monospace" - text: line.replace(line.split(' ')[0], '') - anchors.left: id.right - font.pointSize: 9 + + RowLayout { + id: wrapperItem + anchors.fill: parent + spacing: 5 + + + Text { + anchors.left: parent.left + anchors.leftMargin: 10 + width: 15 + color: "#b2b3ae" + text: styleData.value.split(' ')[0] + font.family: "monospace" + font.pointSize: 9 + wrapMode: Text.NoWrap + id: id + } + Text { + anchors.left: id.right; + wrapMode: Text.NoWrap + color: styleData.selected ? "white" : "black" + font.family: "monospace" + text: styleData.value.replace(styleData.value.split(' ')[0], '') + font.pointSize: 9 + } } } } } Rectangle { - Layout.fillWidth: true + id: debugInfoContainer + width: parent.width * 0.6 - machineStates.sideMargin + anchors.top : parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right height: parent.height //- 2 * stateListContainer.border.width color: "transparent" ColumnLayout @@ -372,7 +410,7 @@ Rectangle { title : qsTr("Stack") itemDelegate: Item { id: renderedItem - height: 25 + //height: 25 width: parent.width RowLayout { @@ -391,7 +429,7 @@ Rectangle { anchors.leftMargin: 5 font.family: "monospace" color: "#4a4a4a" - text: model.index; + text: styleData.row; font.pointSize: 9 } } @@ -402,7 +440,6 @@ Rectangle { Layout.fillWidth: true Layout.minimumWidth: 15 Layout.preferredWidth: 15 - Layout.maximumWidth: 60 Layout.minimumHeight: parent.height Text { anchors.left: parent.left @@ -410,7 +447,7 @@ Rectangle { font.family: "monospace" anchors.verticalCenter: parent.verticalCenter color: "#4a4a4a" - text: modelData + text: styleData.value font.pointSize: 9 } } @@ -434,33 +471,87 @@ Rectangle { id: splitInfoList Layout.fillHeight: true Layout.fillWidth: true - - Settings { - id: splitSettings - property alias storageHeightSettings: storageRect.height - property alias memoryDumpHeightSettings: memoryRect.height - property alias callDataHeightSettings: callDataRect.height - } - orientation: Qt.Vertical - width: debugPanel.width - 2 * machineStates.sideMargin - Rectangle { id: callStackRect; color: "transparent" - height: 120 - width: parent.width - Layout.minimumHeight: 120 - Layout.maximumHeight: 400 - CallStack { - anchors.fill: parent + Layout.minimumHeight: 25 + Layout.maximumHeight: 800 + onHeightChanged: machineStates.updateHeight(); + DebugInfoList + { id: callStack - onFrameActivated: Debugger.displayFrame(index); + collapsible: true + anchors.fill: parent + title : qsTr("Call Stack") + enableSelection: true + onRowActivated: Debugger.displayFrame(index); + itemDelegate: + Item { + anchors.fill: parent + + Rectangle { + anchors.fill: parent + color: "#4A90E2" + visible: styleData.selected; + } + + RowLayout + { + id: row + anchors.fill: parent + Rectangle + { + color: "#f7f7f7" + Layout.fillWidth: true + Layout.minimumWidth: 30 + Layout.maximumWidth: 30 + Text { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + font.family: "monospace" + anchors.leftMargin: 5 + color: "#4a4a4a" + text: styleData.row; + font.pointSize: 9 + width: parent.width - 5 + elide: Text.ElideRight + } + } + Rectangle + { + color: "transparent" + Layout.fillWidth: true + Layout.minimumWidth: parent.width - 30 + Layout.maximumWidth: parent.width - 30 + Text { + anchors.leftMargin: 5 + width: parent.width - 5 + wrapMode: Text.Wrap + anchors.left: parent.left + font.family: "monospace" + anchors.verticalCenter: parent.verticalCenter + color: "#4a4a4a" + text: styleData.value; + elide: Text.ElideRight + font.pointSize: 9 + } + } + } + + Rectangle { + anchors.top: row.bottom + width: parent.width; + height: 1; + color: "#cccccc" + anchors.bottom: parent.bottom + } + } } - } + } Rectangle { @@ -468,8 +559,8 @@ Rectangle { color: "transparent" width: parent.width Layout.minimumHeight: 25 - Layout.maximumHeight: 223 - height: 25 + Layout.maximumHeight: 800 + onHeightChanged: machineStates.updateHeight(); DebugInfoList { id: storage @@ -478,29 +569,24 @@ Rectangle { title : qsTr("Storage") itemDelegate: Item { - height: 27 - width: parent.width; + anchors.fill: parent RowLayout { id: row - width: parent.width - height: 26 + anchors.fill: parent Rectangle { color: "#f7f7f7" Layout.fillWidth: true Layout.minimumWidth: parent.width / 2 - Layout.preferredWidth: parent.width / 2 Layout.maximumWidth: parent.width / 2 - Layout.minimumHeight: parent.height - Layout.maximumHeight: parent.height Text { anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left font.family: "monospace" anchors.leftMargin: 5 color: "#4a4a4a" - text: modelData.split('\t')[0]; + text: styleData.value.split('\t')[0]; font.pointSize: 9 width: parent.width - 5 elide: Text.ElideRight @@ -511,10 +597,7 @@ Rectangle { color: "transparent" Layout.fillWidth: true Layout.minimumWidth: parent.width / 2 - Layout.preferredWidth: parent.width / 2 Layout.maximumWidth: parent.width / 2 - Layout.minimumHeight: parent.height - Layout.maximumHeight: parent.height Text { anchors.leftMargin: 5 width: parent.width - 5 @@ -523,7 +606,7 @@ Rectangle { font.family: "monospace" anchors.verticalCenter: parent.verticalCenter color: "#4a4a4a" - text: modelData.split('\t')[1]; + text: styleData.value.split('\t')[1]; elide: Text.ElideRight font.pointSize: 9 } @@ -545,10 +628,10 @@ Rectangle { { id: memoryRect; color: "transparent" - height: 25 width: parent.width Layout.minimumHeight: 25 - Layout.maximumHeight: 223 + Layout.maximumHeight: 800 + onHeightChanged: machineStates.updateHeight(); DebugInfoList { id: memoryDump anchors.fill: parent @@ -567,10 +650,10 @@ Rectangle { { id: callDataRect color: "transparent" - height: 25 width: parent.width Layout.minimumHeight: 25 - Layout.maximumHeight: 223 + Layout.maximumHeight: 800 + onHeightChanged: machineStates.updateHeight(); DebugInfoList { id: callDataDump anchors.fill: parent @@ -586,8 +669,9 @@ Rectangle { } Rectangle { + id: bottomRect; width: parent.width - Layout.minimumHeight: 25 + Layout.minimumHeight: 20 color: "transparent" } } diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index 23cdec975..bd7c44504 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -59,8 +59,7 @@ Rectangle { } function hideRightView() { - if (rightView.visible) - rightView.hide(); + rightView.visible = false; } function toggleWebPreview() { diff --git a/mix/qml/WebCodeEditor.qml b/mix/qml/WebCodeEditor.qml index b5066b1bf..a1c273698 100644 --- a/mix/qml/WebCodeEditor.qml +++ b/mix/qml/WebCodeEditor.qml @@ -4,6 +4,7 @@ import QtQuick.Layouts 1.0 import QtQuick.Controls.Styles 1.1 import CodeEditorExtensionManager 1.0 import QtWebEngine 1.0 +import QtWebEngine.experimental 1.0 Item { signal editorTextChanged @@ -29,6 +30,18 @@ Item { return currentText; } + function syncClipboard() { + if (Qt.platform.os == "osx") { + var text = appContext.clipboard; + editorBrowser.runJavaScript("setClipboardBase64(\"" + Qt.btoa(text) + "\")"); + } + } + + Connections { + target: appContext + onClipboardChanged: syncClipboard() + } + anchors.top: parent.top id: codeEditorView anchors.fill: parent @@ -36,6 +49,7 @@ Item { id: editorBrowser url: "qrc:///qml/html/codeeditor.html" anchors.fill: parent + experimental.settings.javascriptCanAccessClipboard: true onJavaScriptConsoleMessage: { console.log("editor: " + sourceID + ":" + lineNumber + ":" + message); } @@ -47,6 +61,7 @@ Item { setText(currentText, currentMode); runJavaScript("getTextChanged()", function(result) { }); pollTimer.running = true; + syncClipboard(); } } diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml index e95a85fde..f4ddca84e 100644 --- a/mix/qml/WebPreview.qml +++ b/mix/qml/WebPreview.qml @@ -114,7 +114,8 @@ Item { onClientConnected: { //filter polling spam //TODO: do it properly - var log = _request.content.indexOf("eth_changed") < 0; + //var log = _request.content.indexOf("eth_changed") < 0; + var log = true; if (log) console.log(_request.content); var response = clientModel.apiCall(_request.content); diff --git a/mix/qml/html/cm/codemirror.js b/mix/qml/html/cm/codemirror.js index 9c56d7325..38fe63dd4 100644 --- a/mix/qml/html/cm/codemirror.js +++ b/mix/qml/html/cm/codemirror.js @@ -29,7 +29,7 @@ var ie = ie_upto10 || ie_11up; var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]); var webkit = /WebKit\//.test(navigator.userAgent); - var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent); + var qtwebkit = true; var chrome = /Chrome\//.test(navigator.userAgent); var presto = /Opera\//.test(navigator.userAgent); var safari = /Apple Computer/.test(navigator.vendor); diff --git a/mix/qml/html/codeeditor.js b/mix/qml/html/codeeditor.js index 5b3e5450f..ee93d5bf6 100644 --- a/mix/qml/html/codeeditor.js +++ b/mix/qml/html/codeeditor.js @@ -6,6 +6,7 @@ var editor = CodeMirror(document.body, { autofocus: true, }); + editor.setOption("theme", "solarized dark"); editor.setOption("indentUnit", 4); editor.setOption("indentWithTabs", true); @@ -18,11 +19,24 @@ editor.on("change", function(eMirror, object) { }); +var mac = /Mac/.test(navigator.platform); +if (mac === true) { +editor.setOption("extraKeys", { + "Cmd-V": function(cm) { + cm.replaceSelection(clipboard); + }, + "Cmd-X": function(cm) { + window.document.execCommand("cut"); + }, + "Cmd-C": function(cm) { + window.document.execCommand("copy"); + }}); +} + getTextChanged = function() { return editor.changeRegistered; }; - getText = function() { editor.changeRegistered = false; return editor.getValue(); @@ -42,3 +56,7 @@ setText = function(text) { setMode = function(mode) { this.editor.setOption("mode", mode); }; + +setClipboardBase64 = function(text) { + clipboard = window.atob(text); +}; diff --git a/mix/qml/js/Debugger.js b/mix/qml/js/Debugger.js index de1f87256..872d0aa36 100644 --- a/mix/qml/js/Debugger.js +++ b/mix/qml/js/Debugger.js @@ -107,7 +107,7 @@ function select(stateIndex) callStackData.push(address); } callStackData.push(debugData.states[0].address); - callStack.model = callStackData; + callStack.listModel = callStackData; } function codeStr(stateIndex) @@ -118,8 +118,9 @@ function codeStr(stateIndex) function highlightSelection(index) { - statesList.currentIndex = index; - statesList.positionViewAtIndex(index, ListView.Center); + statesList.positionViewAtRow(index, ListView.Center); + statesList.selection.clear(); + statesList.selection.select(index); } function completeCtxInformation(state) diff --git a/mix/qml/js/ProjectModel.js b/mix/qml/js/ProjectModel.js index 7ba02bc90..09daeb79b 100644 --- a/mix/qml/js/ProjectModel.js +++ b/mix/qml/js/ProjectModel.js @@ -155,6 +155,7 @@ function doCloseProject() { console.log("closing project"); projectListModel.clear(); projectPath = ""; + currentDocumentId = ""; projectClosed(); } diff --git a/mix/qml/main.qml b/mix/qml/main.qml index 720d5070d..41b7c50a4 100644 --- a/mix/qml/main.qml +++ b/mix/qml/main.qml @@ -4,6 +4,7 @@ import QtQuick.Controls.Styles 1.1 import QtQuick.Dialogs 1.1 import QtQuick.Layouts 1.1 import QtQuick.Window 2.1 +import QtQuick.PrivateWidgets 1.1 import Qt.labs.settings 1.0 import org.ethereum.qml.QEther 1.0 @@ -87,7 +88,7 @@ ApplicationWindow { text: qsTr("Mine") shortcut: "Ctrl+M" onTriggered: clientModel.mine(); - enabled: codeModel.hasContract && !clientModel.running + enabled: codeModel.hasContract && !clientModel.running &&!clientModel.mining } Connections { diff --git a/pullSerpent.sh b/pullSerpent.sh index c159e3075..5f8bed05d 100755 --- a/pullSerpent.sh +++ b/pullSerpent.sh @@ -2,10 +2,11 @@ opwd="$PWD" cd ../serpent +git stash git pull +git stash pop cp bignum.* compiler.* funcs.* lllparser.* opcodes.h parser.* rewriter.* tokenize.* util.* ../cpp-ethereum/libserpent/ cp cmdline.* "$opwd/sc/" -cp pyserpent.* "$opwd/libpyserpent/" cd "$opwd" -perl -i -p -e 's:include "funcs.h":include :gc' sc/* libpyserpent/* +perl -i -p -e 's:include "funcs.h":include :gc' sc/* diff --git a/pysol/MANIFEST.in b/pysol/MANIFEST.in new file mode 100644 index 000000000..a3edb5ce1 --- /dev/null +++ b/pysol/MANIFEST.in @@ -0,0 +1,13 @@ +include pysol/*.cpp +include *.py +include libdevcore/*cpp +include libdevcore/*h +include libdevcrypto/*cpp +include libdevcrypto/*h +include libethcore/*cpp +include libethcore/*h +include libsolidity/*cpp +include libsolidity/*h +include libevmcore/*cpp +include libevmcore/*h +include pysol/README.md diff --git a/pysol/README.md b/pysol/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/pysol/pysolidity.cpp b/pysol/pysolidity.cpp new file mode 100644 index 000000000..a2214c67e --- /dev/null +++ b/pysol/pysolidity.cpp @@ -0,0 +1,115 @@ +#include +#include "structmember.h" +#include +#include +#include +#include + +#include "../libdevcore/CommonData.h" + + +#include +#include +#include +#include + + +std::string compile(std::string src) { + dev::solidity::CompilerStack compiler; + try + { + std::vector m_data = compiler.compile(src, false); + return std::string(m_data.begin(), m_data.end()); + } + catch (dev::Exception const& exception) + { + std::ostringstream error; + dev::solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); + std::string e = error.str(); + throw(e); + } +} + + +std::string mk_full_signature(std::string src) { + dev::solidity::CompilerStack compiler; + try + { + compiler.compile(src); + return compiler.getInterface(""); + } + catch (dev::Exception const& exception) + { + std::ostringstream error; + dev::solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); + std::string e = error.str(); + throw(e); + } +} + +std::string bob(std::string src) { return src + src; } + +#define PYMETHOD(name, FROM, method, TO) \ + static PyObject * name(PyObject *, PyObject *args) { \ + try { \ + FROM(med) \ + return TO(method(med)); \ + } \ + catch (std::string e) { \ + PyErr_SetString(PyExc_Exception, e.c_str()); \ + return NULL; \ + } \ + } + +#define FROMSTR(v) \ + const char *command; \ + int len; \ + if (!PyArg_ParseTuple(args, "s#", &command, &len)) \ + return NULL; \ + std::string v = std::string(command, len); \ + +// Convert string into python wrapper form +PyObject* pyifyString(std::string s) { + return Py_BuildValue("s#", s.c_str(), s.length()); +} + +// Convert integer into python wrapper form +PyObject* pyifyInteger(unsigned int i) { + return Py_BuildValue("i", i); +} + +// Convert pyobject int into normal form +int cppifyInt(PyObject* o) { + int out; + if (!PyArg_Parse(o, "i", &out)) + throw("Argument should be integer"); + return out; +} + +// Convert pyobject string into normal form +std::string cppifyString(PyObject* o) { + const char *command; + if (!PyArg_Parse(o, "s", &command)) + throw("Argument should be string"); + return std::string(command); +} + +int fh(std::string i) { + return dev::fromHex(i[0]); +} + +PYMETHOD(ps_compile, FROMSTR, compile, pyifyString) +PYMETHOD(ps_mk_full_signature, FROMSTR, mk_full_signature, pyifyString) + +static PyMethodDef PyextMethods[] = { + {"compile", ps_compile, METH_VARARGS, + "Compile code."}, + {"mk_full_signature", ps_mk_full_signature, METH_VARARGS, + "Get the signature of a piece of code."}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +PyMODINIT_FUNC initsolidity(void) +{ + Py_InitModule( "solidity", PyextMethods ); +} diff --git a/pysol/setup.py b/pysol/setup.py new file mode 100755 index 000000000..9aa13c248 --- /dev/null +++ b/pysol/setup.py @@ -0,0 +1,41 @@ +import os +os.chdir('..') + +from setuptools import setup, Extension + +from distutils.sysconfig import get_config_vars + +(opt,) = get_config_vars('OPT') +os.environ['OPT'] = " ".join( + flag for flag in opt.split() if flag != '-Wstrict-prototypes' +) + +setup( + # Name of this package + name="ethereum-solidity", + + # Package version + version='1.8.0', + + description='Solidity compiler python wrapper', + maintainer='Vitalik Buterin', + maintainer_email='v@buterin.com', + license='WTFPL', + url='http://www.ethereum.org/', + + # Describes how to build the actual extension module from C source files. + ext_modules=[ + Extension( + 'solidity', # Python name of the module + sources= ['libdevcore/Common.cpp', 'libdevcore/CommonData.cpp', 'libdevcore/CommonIO.cpp', 'libdevcore/FixedHash.cpp', 'libdevcore/Guards.cpp', 'libdevcore/Log.cpp', 'libdevcore/RangeMask.cpp', 'libdevcore/RLP.cpp', 'libdevcore/Worker.cpp', 'libdevcrypto/AES.cpp', 'libdevcrypto/Common.cpp', 'libdevcrypto/CryptoPP.cpp', 'libdevcrypto/ECDHE.cpp', 'libdevcrypto/FileSystem.cpp', 'libdevcrypto/MemoryDB.cpp', 'libdevcrypto/OverlayDB.cpp', 'libdevcrypto/SHA3.cpp', 'libdevcrypto/TrieCommon.cpp', 'libdevcrypto/TrieDB.cpp', 'libethcore/CommonEth.cpp', 'libethcore/CommonJS.cpp', 'libethcore/Exceptions.cpp', 'libsolidity/AST.cpp', 'libsolidity/ASTJsonConverter.cpp', 'libsolidity/ASTPrinter.cpp', 'libsolidity/CompilerContext.cpp', 'libsolidity/Compiler.cpp', 'libsolidity/CompilerStack.cpp', 'libsolidity/CompilerUtils.cpp', 'libsolidity/DeclarationContainer.cpp', 'libsolidity/ExpressionCompiler.cpp', 'libsolidity/GlobalContext.cpp', 'libsolidity/InterfaceHandler.cpp', 'libsolidity/NameAndTypeResolver.cpp', 'libsolidity/Parser.cpp', 'libsolidity/Scanner.cpp', 'libsolidity/SourceReferenceFormatter.cpp', 'libsolidity/Token.cpp', 'libsolidity/Types.cpp', 'libevmcore/Assembly.cpp', 'libevmcore/Instruction.cpp', 'pysol/pysolidity.cpp'], + libraries=['boost_python', 'boost_filesystem', 'boost_chrono', 'boost_thread', 'cryptopp', 'leveldb', 'jsoncpp'], + include_dirs=['/usr/include/boost', '..', '../..', '.'], + extra_compile_args=['--std=c++11', '-Wno-unknown-pragmas'] + )], + py_modules=[ + ], + scripts=[ + ], + entry_points={ + } + ), diff --git a/sc/CMakeLists.txt b/sc/CMakeLists.txt index 2adde060e..ee638bb52 100644 --- a/sc/CMakeLists.txt +++ b/sc/CMakeLists.txt @@ -8,7 +8,9 @@ set(EXECUTABLE sc) add_executable(${EXECUTABLE} ${SRC_LIST}) +if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) target_link_libraries(${EXECUTABLE} serpent) +endif() target_link_libraries(${EXECUTABLE} lll) target_link_libraries(${EXECUTABLE} evmcore) target_link_libraries(${EXECUTABLE} devcore) diff --git a/sc/cmdline.cpp b/sc/cmdline.cpp index a5fed37d6..04cdeaaf3 100644 --- a/sc/cmdline.cpp +++ b/sc/cmdline.cpp @@ -68,7 +68,7 @@ int main(int argv, char** argc) { std::cout << binToHex(compileLLL(parseLLL(input, true))) << "\n"; } else if (command == "dereference") { - std::cout << printAST(dereference(parseLLL(input, true)), haveSec) <<"\n"; + std::cout << printTokens(dereference(parseLLL(input, true))) <<"\n"; } else if (command == "pretty_assemble") { std::cout << printTokens(prettyAssemble(parseLLL(input, true))) <<"\n"; @@ -88,9 +88,6 @@ int main(int argv, char** argc) { else if (command == "serialize") { std::cout << binToHex(serialize(tokenize(input, Metadata(), false))) << "\n"; } - else if (command == "flatten") { - std::cout << printTokens(flatten(parseLLL(input, true))) << "\n"; - } else if (command == "deserialize") { std::cout << printTokens(deserialize(hexToBin(input))) << "\n"; } diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index ced9785ef..f248a5a07 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -92,6 +92,26 @@ BOOST_AUTO_TEST_CASE(multiple_functions) BOOST_CHECK(callContractFunction("i_am_not_there()", bytes()) == bytes()); } +BOOST_AUTO_TEST_CASE(named_args) +{ + char const* sourceCode = "contract test {\n" + " function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; }\n" + " function b() returns (uint r) { r = a({a: 1, b: 2, c: 3}); }\n" + "}\n"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("b()", bytes()) == toBigEndian(u256(123))); +} + +BOOST_AUTO_TEST_CASE(disorder_named_args) +{ + char const* sourceCode = "contract test {\n" + " function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; }\n" + " function b() returns (uint r) { r = a({c: 3, a: 1, b: 2}); }\n" + "}\n"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("b()", bytes()) == toBigEndian(u256(123))); +} + BOOST_AUTO_TEST_CASE(while_loop) { char const* sourceCode = "contract test {\n" @@ -921,10 +941,10 @@ BOOST_AUTO_TEST_CASE(multiple_elementary_accessors) BOOST_AUTO_TEST_CASE(complex_accessors) { char const* sourceCode = "contract test {\n" - " mapping(uint256 => string4) to_string_map;\n" - " mapping(uint256 => bool) to_bool_map;\n" - " mapping(uint256 => uint256) to_uint_map;\n" - " mapping(uint256 => mapping(uint256 => uint256)) to_multiple_map;\n" + " mapping(uint256 => string4) public to_string_map;\n" + " mapping(uint256 => bool) public to_bool_map;\n" + " mapping(uint256 => uint256) public to_uint_map;\n" + " mapping(uint256 => mapping(uint256 => uint256)) public to_multiple_map;\n" " function test() {\n" " to_string_map[42] = \"24\";\n" " to_bool_map[42] = false;\n" diff --git a/test/SolidityExpressionCompiler.cpp b/test/SolidityExpressionCompiler.cpp index a0cca3a3a..3c3ea1baa 100644 --- a/test/SolidityExpressionCompiler.cpp +++ b/test/SolidityExpressionCompiler.cpp @@ -91,7 +91,15 @@ bytes compileFirstExpression(const string& _sourceCode, vector> _ { Parser parser; ASTPointer sourceUnit; - BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared(CharStream(_sourceCode)))); + try + { + sourceUnit = parser.parse(make_shared(CharStream(_sourceCode))); + } + catch(boost::exception const& _e) + { + auto msg = std::string("Parsing source code failed with: \n") + boost::diagnostic_information(_e); + BOOST_FAIL(msg); + } vector declarations; declarations.reserve(_globalDeclarations.size() + 1); @@ -177,6 +185,66 @@ BOOST_AUTO_TEST_CASE(int_literal) BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } +BOOST_AUTO_TEST_CASE(int_with_wei_ether_subdenomination) +{ + char const* sourceCode = R"( + contract test { + function test () + { + var x = 1 wei; + } + })"; + bytes code = compileFirstExpression(sourceCode); + + bytes expectation({byte(eth::Instruction::PUSH1), 0x1}); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); +} + +BOOST_AUTO_TEST_CASE(int_with_szabo_ether_subdenomination) +{ + char const* sourceCode = R"( + contract test { + function test () + { + var x = 1 szabo; + } + })"; + bytes code = compileFirstExpression(sourceCode); + + bytes expectation({byte(eth::Instruction::PUSH5), 0xe8, 0xd4, 0xa5, 0x10, 0x00}); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); +} + +BOOST_AUTO_TEST_CASE(int_with_finney_ether_subdenomination) +{ + char const* sourceCode = R"( + contract test { + function test () + { + var x = 1 finney; + } + })"; + bytes code = compileFirstExpression(sourceCode); + + bytes expectation({byte(eth::Instruction::PUSH7), 0x3, 0x8d, 0x7e, 0xa4, 0xc6, 0x80, 0x00}); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); +} + +BOOST_AUTO_TEST_CASE(int_with_ether_ether_subdenomination) +{ + char const* sourceCode = R"( + contract test { + function test () + { + var x = 1 ether; + } + })"; + bytes code = compileFirstExpression(sourceCode); + + bytes expectation({byte(eth::Instruction::PUSH8), 0xd, 0xe0, 0xb6, 0xb3, 0xa7, 0x64, 0x00, 0x00}); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); +} + BOOST_AUTO_TEST_CASE(comparison) { char const* sourceCode = "contract test {\n" diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 1a087592c..ae6c374b4 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -868,6 +868,42 @@ BOOST_AUTO_TEST_CASE(access_to_protected_state_variable) BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); } +BOOST_AUTO_TEST_CASE(error_count_in_named_args) +{ + char const* sourceCode = "contract test {\n" + " function a(uint a, uint b) returns (uint r) { r = a + b; }\n" + " function b() returns (uint r) { r = a({a: 1}); }\n" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(empty_in_named_args) +{ + char const* sourceCode = "contract test {\n" + " function a(uint a, uint b) returns (uint r) { r = a + b; }\n" + " function b() returns (uint r) { r = a({}); }\n" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(duplicate_parameter_names_in_named_args) +{ + char const* sourceCode = "contract test {\n" + " function a(uint a, uint b) returns (uint r) { r = a + b; }\n" + " function b() returns (uint r) { r = a({a: 1, a: 2}); }\n" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(invalid_parameter_names_in_named_args) +{ + char const* sourceCode = "contract test {\n" + " function a(uint a, uint b) returns (uint r) { r = a + b; }\n" + " function b() returns (uint r) { r = a({a: 1, c: 2}); }\n" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/SolidityParser.cpp b/test/SolidityParser.cpp index 4ccdcd57a..b6837866c 100644 --- a/test/SolidityParser.cpp +++ b/test/SolidityParser.cpp @@ -124,6 +124,24 @@ BOOST_AUTO_TEST_CASE(single_function_param) BOOST_CHECK_NO_THROW(parseText(text)); } +BOOST_AUTO_TEST_CASE(missing_parameter_name_in_named_args) +{ + char const* text = "contract test {\n" + " function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; }\n" + " function b() returns (uint r) { r = a({: 1, : 2, : 3}); }\n" + "}\n"; + BOOST_CHECK_THROW(parseText(text), ParserError); +} + +BOOST_AUTO_TEST_CASE(missing_argument_in_named_args) +{ + char const* text = "contract test {\n" + " function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; }\n" + " function b() returns (uint r) { r = a({a: , b: , c: }); }\n" + "}\n"; + BOOST_CHECK_THROW(parseText(text), ParserError); +} + BOOST_AUTO_TEST_CASE(function_natspec_documentation) { ASTPointer contract; @@ -642,6 +660,25 @@ BOOST_AUTO_TEST_CASE(multiple_visibility_specifiers) BOOST_CHECK_THROW(parseText(text), ParserError); } +BOOST_AUTO_TEST_CASE(literal_constants_with_ether_subdenominations) +{ + char const* text = R"( + contract c { + function c () + { + a = 1 wei; + b = 2 szabo; + c = 3 finney; + b = 4 ether; + } + uint256 a; + uint256 b; + uint256 c; + uint256 d; + })"; + BOOST_CHECK_NO_THROW(parseTextExplainError(text)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/SolidityScanner.cpp b/test/SolidityScanner.cpp index 7dc9ef482..8088b4d4b 100644 --- a/test/SolidityScanner.cpp +++ b/test/SolidityScanner.cpp @@ -255,6 +255,15 @@ BOOST_AUTO_TEST_CASE(comments_mixed_in_sequence) BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "documentation comment "); } +BOOST_AUTO_TEST_CASE(ether_subdenominations) +{ + Scanner scanner(CharStream("wei szabo finney ether")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::SubWei); + BOOST_CHECK_EQUAL(scanner.next(), Token::SubSzabo); + BOOST_CHECK_EQUAL(scanner.next(), Token::SubFinney); + BOOST_CHECK_EQUAL(scanner.next(), Token::SubEther); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/commonjs.cpp b/test/commonjs.cpp index 860b713dd..041a14f68 100644 --- a/test/commonjs.cpp +++ b/test/commonjs.cpp @@ -20,7 +20,8 @@ */ #include -#include +#include +#include BOOST_AUTO_TEST_SUITE(commonjs) using namespace std; @@ -41,7 +42,7 @@ BOOST_AUTO_TEST_CASE(jsToAddress) cnote << "Testing jsToPublic..."; KeyPair kp = KeyPair::create(); string string = toJS(kp.address()); - Address address = dev::jsToAddress(string); + Address address = dev::eth::jsToAddress(string); BOOST_CHECK_EQUAL(kp.address(), address); } diff --git a/test/fork.cpp b/test/fork.cpp index 1cdb8822c..bc6ed87bc 100644 --- a/test/fork.cpp +++ b/test/fork.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include "TestHelper.h" using namespace std; diff --git a/test/genesis.cpp b/test/genesis.cpp index 8cdb84024..7ca741ee9 100644 --- a/test/genesis.cpp +++ b/test/genesis.cpp @@ -24,7 +24,7 @@ #include #include "JsonSpiritHeaders.h" #include -#include +#include #include #include "TestHelper.h" @@ -58,9 +58,9 @@ BOOST_AUTO_TEST_CASE(genesis_tests) js::mObject o = v.get_obj(); - BOOST_CHECK_EQUAL(BlockChain::genesis().stateRoot, h256(o["genesis_state_root"].get_str())); - BOOST_CHECK_EQUAL(toHex(BlockChain::createGenesisBlock()), toHex(fromHex(o["genesis_rlp_hex"].get_str()))); - BOOST_CHECK_EQUAL(BlockInfo::headerHash(BlockChain::createGenesisBlock()), h256(o["genesis_hash"].get_str())); + BOOST_CHECK_EQUAL(CanonBlockChain::genesis().stateRoot, h256(o["genesis_state_root"].get_str())); + BOOST_CHECK_EQUAL(toHex(CanonBlockChain::createGenesisBlock()), toHex(fromHex(o["genesis_rlp_hex"].get_str()))); + BOOST_CHECK_EQUAL(BlockInfo::headerHash(CanonBlockChain::createGenesisBlock()), h256(o["genesis_hash"].get_str())); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/jsonrpc.cpp b/test/jsonrpc.cpp index 1f0a466b2..eaa9edc45 100644 --- a/test/jsonrpc.cpp +++ b/test/jsonrpc.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/test/state.cpp b/test/state.cpp index 100634be3..fb54a62ae 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -24,7 +24,7 @@ #include #include "JsonSpiritHeaders.h" #include -#include +#include #include #include #include diff --git a/test/stateOriginal.cpp b/test/stateOriginal.cpp index b1a7c0d8e..65ff5084f 100644 --- a/test/stateOriginal.cpp +++ b/test/stateOriginal.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include using namespace std; @@ -40,7 +40,7 @@ int stateTest() Defaults::setDBPath(boost::filesystem::temp_directory_path().string()); OverlayDB stateDB = State::openDB(); - BlockChain bc; + CanonBlockChain bc; State s(myMiner.address(), stateDB); cout << bc; diff --git a/test/txTest.cpp b/test/txTest.cpp index cc78c26a2..8d067f9bb 100644 --- a/test/txTest.cpp +++ b/test/txTest.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include "TestHelper.h" using namespace std; diff --git a/third/CMakeLists.txt b/third/CMakeLists.txt index 4b83b4fa5..71f1e70f2 100644 --- a/third/CMakeLists.txt +++ b/third/CMakeLists.txt @@ -38,7 +38,9 @@ target_link_libraries(${EXECUTABLE} ethereum) target_link_libraries(${EXECUTABLE} evm) target_link_libraries(${EXECUTABLE} ethcore) target_link_libraries(${EXECUTABLE} secp256k1) -target_link_libraries(${EXECUTABLE} serpent) +if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) +target_link_libraries(${EXECUTABLE} serpent) +endif() target_link_libraries(${EXECUTABLE} lll) target_link_libraries(${EXECUTABLE} evmcore) target_link_libraries(${EXECUTABLE} devcore) diff --git a/third/MainWin.cpp b/third/MainWin.cpp index eae421c24..64f90a761 100644 --- a/third/MainWin.cpp +++ b/third/MainWin.cpp @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include #include @@ -97,8 +97,8 @@ Main::Main(QWidget *parent) : setWindowFlags(Qt::Window); ui->setupUi(this); - cerr << "State root: " << BlockChain::genesis().stateRoot << endl; - auto gb = BlockChain::createGenesisBlock(); + cerr << "State root: " << CanonBlockChain::genesis().stateRoot << endl; + auto gb = CanonBlockChain::createGenesisBlock(); cerr << "Block Hash: " << sha3(gb) << endl; cerr << "Block RLP: " << RLP(gb) << endl; cerr << "Block Hex: " << toHex(gb) << endl;